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"])
Beispiel #2
0
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
Beispiel #3
0
    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)
Beispiel #4
0
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()
Beispiel #5
0
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()
Beispiel #6
0
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()
Beispiel #7
0
    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,
        }
Beispiel #8
0
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
Beispiel #9
0
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
Beispiel #10
0
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
Beispiel #11
0
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)