async def async_get_aiohwenergy_from_entry_data(entry_data): """Create a HomewizardEnergy object from entry data.""" Logger.debug("%s async_get_aiohwenergy_from_entry_data\nentry_data:\n%s" % (__name__, str(entry_data))) return aiohwenergy.HomeWizardEnergy(entry_data["host"])
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Homewizard Energy from a config entry.""" Logger.debug("__init__ async_setup_entry") hass.data[DOMAIN][entry.data["unique_id"]] = {} # Migrate manual config to zeroconf (<0.5.0 to 0.5.x) # Check if unique_id == ipv4 or ipv6 if re.match("(?:[0-9]{1,3}\.){3}[0-9]{1,3}", entry.unique_id) or re.match( "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))", entry.unique_id, ): result = await migrate_old_configuration(hass, entry) if not result: return False hass.data[DOMAIN][entry.data["unique_id"]][ CONF_UNLOAD_CB] = entry.add_update_listener(async_entry_updated) energy_api = aiohwenergy.HomeWizardEnergy(entry.data.get("host")) hass.data[DOMAIN][entry.data["unique_id"]][CONF_API] = energy_api for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) return True
def __init__( self, hass: HomeAssistant, host: str, ) -> None: """Initialize Update Coordinator.""" super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL) self.api = aiohwenergy.HomeWizardEnergy(host)
async def test_device_catches_unsupported_api(mock): async with aiohwenergy.HomeWizardEnergy("1.2.3.4") as api: mock.return_value.__aenter__.side_effect = [ mock_request_response(200, '{"api_version": "v_UNSUPPORTED"}'), ] with raises(aiohwenergy.errors.UnsupportedError): await api.initialize()
async def test_device_catches_unsupported_device(mock): async with aiohwenergy.HomeWizardEnergy("1.2.3.4") as api: mock.return_value.__aenter__.side_effect = [ mock_request_response( 200, '{"product_type": "HWE-INVALID_DEVICE", "api_version": "v1"}' ), ] with raises(aiohwenergy.errors.UnsupportedError): await api.initialize()
async def test_device_catches_invalid_response(mock): async with aiohwenergy.HomeWizardEnergy("1.2.3.4") as api: mock.return_value.__aenter__.side_effect = [ mock_request_response( 404, '{"product_type": "HWE-SKT","product_name": "Energy socket","serial": "aabbccddeeff","firmware_version": "2.13","api_version": "v1"}', ), ] with raises(aiohwenergy.errors.RequestError): await api.initialize()
async def _async_try_connect_and_fetch(ip_address: str) -> dict[str, Any]: """Try to connect.""" _LOGGER.debug("config_flow _async_try_connect_and_fetch") # Make connection with device # This is to test the connection and to get info for unique_id energy_api = aiohwenergy.HomeWizardEnergy(ip_address) try: with async_timeout.timeout(10): await energy_api.initialize() except aiohwenergy.DisabledError as ex: _LOGGER.error("API disabled, API must be enabled in the app") raise AbortFlow("api_not_enabled") from ex except Exception as ex: _LOGGER.exception( "Error connecting with Energy Device at %s", ip_address, ) raise AbortFlow("unknown_error") from ex finally: await energy_api.close() if energy_api.device is None: _LOGGER.error("Initialization failed") raise AbortFlow("unknown_error") # Validate metadata if energy_api.device.api_version != "v1": raise AbortFlow("unsupported_api_version") if energy_api.device.product_type not in SUPPORTED_DEVICES: _LOGGER.error( "Device (%s) not supported by integration", energy_api.device.product_type, ) raise AbortFlow("device_not_supported") return { CONF_PRODUCT_NAME: energy_api.device.product_name, CONF_PRODUCT_TYPE: energy_api.device.product_type, CONF_SERIAL: energy_api.device.serial, }
async def test_device_initialized_p1_data(mock): async with aiohwenergy.HomeWizardEnergy("1.2.3.4") as api: mock.return_value.__aenter__.side_effect = [ mock_request_response( 200, '{"product_type": "HWE-P1","product_name": "P1 meter","serial": "aabbccddeeff","firmware_version": "2.13","api_version": "v1"}', ), mock_request_response( 200, '{"smr_version": 50, "meter_model": "ISKRA 2M550E-1012", "wifi_ssid": "my_wifi", "wifi_strength": 56, "total_power_import_t1_kwh": 123.456, "total_power_import_t2_kwh": 234.567, "total_power_export_t1_kwh": 111.222, "total_power_export_t2_kwh": 222.333, "active_power_w": 600, "active_power_l1_w": 100, "active_power_l2_w": 200, "active_power_l3_w": 300, "total_gas_m3": 40, "gas_timestamp": 211231123456}', ), ] assert api.host == "1.2.3.4" assert api.device is None assert api.data is None assert api.state is None await api.initialize() assert api.device is not None assert api.device.product_type == "HWE-P1" assert api.device.product_name == "P1 meter" assert api.device.serial == "aabbccddeeff" assert api.device.firmware_version == "2.13" assert api.device.api_version == "v1" assert api.data is not None assert api.data.smr_version == 50 assert api.data.meter_model == "ISKRA 2M550E-1012" assert api.data.wifi_ssid == "my_wifi" assert api.data.wifi_strength == 56 assert api.data.total_power_import_t1_kwh == 123.456 assert api.data.total_power_import_t2_kwh == 234.567 assert api.data.total_power_export_t1_kwh == 111.222 assert api.data.total_power_export_t2_kwh == 222.333 assert api.data.active_power_w == 600 assert api.data.active_power_l1_w == 100 assert api.data.active_power_l2_w == 200 assert api.data.active_power_l3_w == 300 assert api.data.total_gas_m3 == 40 assert api.data.gas_timestamp == "2021-12-31T12:34:56" assert len(api.data.available_datapoints) == 14 assert api.state is None
async def test_device_initialized_socket_data_data(mock): async with aiohwenergy.HomeWizardEnergy("1.2.3.4") as api: mock.return_value.__aenter__.side_effect = [ mock_request_response( 200, '{"product_type": "HWE-SKT","product_name": "Energy socket","serial": "aabbccddeeff","firmware_version": "2.13","api_version": "v1"}', ), mock_request_response( 200, '{"wifi_ssid": "my_wifi", "wifi_strength": 56, "total_power_import_t1_kwh": 123.456, "total_power_export_t1_kwh": 111.222, "active_power_w": 600, "active_power_l1_w": 100}', ), mock_request_response( 200, '{"power_on": true, "switch_lock": false, "brightness": 64}' ), ] assert api.host == "1.2.3.4" assert api.device is None assert api.data is None assert api.state is None await api.initialize() assert api.device is not None assert api.device.product_type == "HWE-SKT" assert api.device.product_name == "Energy socket" assert api.device.serial == "aabbccddeeff" assert api.device.firmware_version == "2.13" assert api.device.api_version == "v1" assert api.data is not None assert api.data.meter_model is None assert api.data.wifi_ssid == "my_wifi" assert api.data.wifi_strength == 56 assert api.data.total_power_import_t1_kwh == 123.456 assert api.data.total_power_export_t1_kwh == 111.222 assert api.data.active_power_w == 600 assert api.data.active_power_l1_w == 100 assert len(api.data.available_datapoints) == 6 assert api.state is not None assert api.state.power_on == True assert api.state.switch_lock == False assert api.state.brightness == 64
async def test_device_initialized_kwh_3_data_data(mock): async with aiohwenergy.HomeWizardEnergy("1.2.3.4") as api: mock.return_value.__aenter__.side_effect = [ mock_request_response( 200, '{"product_type": "SDM630-wifi","product_name": "kWh meter","serial": "aabbccddeeff","firmware_version": "2.13","api_version": "v1"}', ), mock_request_response( 200, '{"wifi_ssid": "my_wifi", "wifi_strength": 56, "total_power_import_t1_kwh": 123.456, "total_power_export_t1_kwh": 111.222, "active_power_w": 600, "active_power_l1_w": 100, "active_power_l2_w": 200, "active_power_l3_w": 300}', ), ] assert api.host == "1.2.3.4" assert api.device is None assert api.data is None assert api.state is None await api.initialize() assert api.device is not None assert api.device.product_type == "SDM630-wifi" assert api.device.product_name == "kWh meter" assert api.device.serial == "aabbccddeeff" assert api.device.firmware_version == "2.13" assert api.device.api_version == "v1" assert api.data is not None assert api.data.meter_model is None assert api.data.wifi_ssid == "my_wifi" assert api.data.wifi_strength == 56 assert api.data.total_power_import_t1_kwh == 123.456 assert api.data.total_power_export_t1_kwh == 111.222 assert api.data.active_power_w == 600 assert api.data.active_power_l1_w == 100 assert api.data.active_power_l2_w == 200 assert api.data.active_power_l3_w == 300 assert len(api.data.available_datapoints) == 8 assert api.state is None
async def migrate_old_configuration(hass: HomeAssistant, entry: ConfigEntry): """ Migrates 0.4.x configuration, where unique_id was based on IP to >0.5.0 configuration, where unique_id is based on the serial ID """ Logger.info("Migrating old integration to new one") host_ip = entry.unique_id api = aiohwenergy.HomeWizardEnergy(host_ip) try: with async_timeout.timeout(5): await api.initialize() except (asyncio.TimeoutError, aiohwenergy.RequestError): Logger.error( "(-1) Error connecting to the Energy device at %s", host_ip, ) return False except Exception: # pylint: disable=broad-except Logger.error( "(-2) Error connecting to the Energy device at %s", host_ip, ) return False finally: await api.close() if api.device == None: Logger.error( "Device (%s) API disabled, enable API and restart integration" % host_ip) return False # Update unique_id information unique_id = "%s_%s" % ( api.device.product_type, api.device.serial, ) # Update entities er = await entity_registry.async_get_registry(hass) entities = entity_registry.async_entries_for_config_entry( er, entry.entry_id) old_unique_id_prefix = "p1_meter_%s_" % slugify(host_ip) for entity in entities: new_unique_id_type = entity.unique_id.replace(old_unique_id_prefix, "") new_unique_id = "%s_%s" % (unique_id, new_unique_id_type) Logger.debug("Changing %s to %s" % (entity.unique_id, new_unique_id)) er.async_update_entity(entity.entity_id, new_unique_id=new_unique_id) # Update device information data = entry.data.copy() data["host"] = host_ip data["name"] = api.device.product_name data["custom_name"] = api.device.product_name data["unique_id"] = unique_id data.pop("ip_address") hass.config_entries.async_update_entry(entry, data=data, unique_id=unique_id) return True
async def async_step_user(self, user_input: Optional[ConfigType] = None ) -> Dict[str, Any]: """Handle a flow initiated by the user.""" if user_input is None: return self._show_setup_form() # Check if data is IP (Volup?) # Make connection with device energy_api = aiohwenergy.HomeWizardEnergy(user_input[CONF_IP_ADDRESS]) initialized = False try: with async_timeout.timeout(10): await energy_api.initialize() if energy_api.device != None: initialized = True except (asyncio.TimeoutError, aiohwenergy.RequestError): Logger.error( "Error connecting to the Energy device at %s", energy_api._host, ) return self.async_abort(reason="manual_config_request_error") except aiohwenergy.AioHwEnergyException: Logger.exception("Unknown Energy API error occurred") return self.async_abort(reason="manual_config_unknown_error") except Exception: # pylint: disable=broad-except Logger.exception( "Unknown error connecting with Energy Device at %s", energy_api._host["host"], ) return self.async_abort(reason="manual_config_unknown_error") finally: await energy_api.close() if not initialized: return self.async_abort(reason="manual_config_unknown_error") # Validate metadata if energy_api.device.api_version != "v1": return self.async_abort( reason="manual_config_unsupported_api_version") # Configure device entry_info = { "host": user_input[CONF_IP_ADDRESS], "port": 80, "api_enabled": "1", "path": "/api/v1", "product_name": energy_api.device.product_name, "product_type": energy_api.device.product_type, "serial": energy_api.device.serial, } Logger.debug(entry_info) return await self.async_step_check(entry_info)