Add support for HomeWizard Plug-In Battery and v2 API (#136733)
parent
a8c382566c
commit
cb407bdfc6
|
@ -1,12 +1,18 @@
|
|||
"""The Homewizard integration."""
|
||||
|
||||
from homewizard_energy import HomeWizardEnergy, HomeWizardEnergyV1, HomeWizardEnergyV2
|
||||
from homewizard_energy import (
|
||||
HomeWizardEnergy,
|
||||
HomeWizardEnergyV1,
|
||||
HomeWizardEnergyV2,
|
||||
has_v2_api,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
|
||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
|
||||
from .const import DOMAIN, PLATFORMS
|
||||
from .coordinator import HWEnergyDeviceUpdateCoordinator
|
||||
|
@ -31,6 +37,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomeWizardConfigEntry) -
|
|||
clientsession=async_get_clientsession(hass),
|
||||
)
|
||||
|
||||
await async_check_v2_support_and_create_issue(hass, entry)
|
||||
|
||||
coordinator = HWEnergyDeviceUpdateCoordinator(hass, api)
|
||||
try:
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
@ -63,3 +71,27 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomeWizardConfigEntry) -
|
|||
async def async_unload_entry(hass: HomeAssistant, entry: HomeWizardConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_check_v2_support_and_create_issue(
|
||||
hass: HomeAssistant, entry: HomeWizardConfigEntry
|
||||
) -> None:
|
||||
"""Check if the device supports v2 and create an issue if not."""
|
||||
|
||||
if not await has_v2_api(entry.data[CONF_IP_ADDRESS], async_get_clientsession(hass)):
|
||||
return
|
||||
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"migrate_to_v2_api_{entry.entry_id}",
|
||||
is_fixable=True,
|
||||
is_persistent=False,
|
||||
learn_more_url="https://home-assistant.io/integrations/homewizard/#which-button-do-i-need-to-press-to-configure-the-device",
|
||||
translation_key="migrate_to_v2_api",
|
||||
translation_placeholders={
|
||||
"title": entry.title,
|
||||
},
|
||||
severity=IssueSeverity.WARNING,
|
||||
data={"entry_id": entry.entry_id},
|
||||
)
|
||||
|
|
|
@ -5,28 +5,31 @@ from __future__ import annotations
|
|||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from homewizard_energy import HomeWizardEnergyV1
|
||||
from homewizard_energy.errors import DisabledError, RequestError, UnsupportedError
|
||||
from homewizard_energy import (
|
||||
HomeWizardEnergy,
|
||||
HomeWizardEnergyV1,
|
||||
HomeWizardEnergyV2,
|
||||
has_v2_api,
|
||||
)
|
||||
from homewizard_energy.errors import (
|
||||
DisabledError,
|
||||
RequestError,
|
||||
UnauthorizedError,
|
||||
UnsupportedError,
|
||||
)
|
||||
from homewizard_energy.models import Device
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import onboarding
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_PATH
|
||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN
|
||||
from homeassistant.data_entry_flow import AbortFlow
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.selector import TextSelector
|
||||
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||
|
||||
from .const import (
|
||||
CONF_API_ENABLED,
|
||||
CONF_PRODUCT_NAME,
|
||||
CONF_PRODUCT_TYPE,
|
||||
CONF_SERIAL,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
)
|
||||
from .const import CONF_PRODUCT_NAME, CONF_PRODUCT_TYPE, CONF_SERIAL, DOMAIN, LOGGER
|
||||
|
||||
|
||||
class HomeWizardConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
@ -46,10 +49,14 @@ class HomeWizardConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
errors: dict[str, str] | None = None
|
||||
if user_input is not None:
|
||||
try:
|
||||
device_info = await self._async_try_connect(user_input[CONF_IP_ADDRESS])
|
||||
device_info = await async_try_connect(user_input[CONF_IP_ADDRESS])
|
||||
except RecoverableError as ex:
|
||||
LOGGER.error(ex)
|
||||
errors = {"base": ex.error_code}
|
||||
except UnauthorizedError:
|
||||
# Device responded, so IP is correct. But we have to authorize
|
||||
self.ip_address = user_input[CONF_IP_ADDRESS]
|
||||
return await self.async_step_authorize()
|
||||
else:
|
||||
await self.async_set_unique_id(
|
||||
f"{device_info.product_type}_{device_info.serial}"
|
||||
|
@ -73,22 +80,54 @@ class HomeWizardConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_authorize(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Step where we attempt to get a token."""
|
||||
assert self.ip_address
|
||||
|
||||
# Tell device we want a token, user must now press the button within 30 seconds
|
||||
# The first attempt will always fail, but this opens the window to press the button
|
||||
token = await async_request_token(self.ip_address)
|
||||
errors: dict[str, str] | None = None
|
||||
|
||||
if token is None:
|
||||
if user_input is not None:
|
||||
errors = {"base": "authorization_failed"}
|
||||
|
||||
return self.async_show_form(step_id="authorize", errors=errors)
|
||||
|
||||
# Now we got a token, we can ask for some more info
|
||||
|
||||
async with HomeWizardEnergyV2(self.ip_address, token=token) as api:
|
||||
device_info = await api.device()
|
||||
|
||||
data = {
|
||||
CONF_IP_ADDRESS: self.ip_address,
|
||||
CONF_TOKEN: token,
|
||||
}
|
||||
|
||||
await self.async_set_unique_id(
|
||||
f"{device_info.product_type}_{device_info.serial}"
|
||||
)
|
||||
self._abort_if_unique_id_configured(updates=data)
|
||||
return self.async_create_entry(
|
||||
title=f"{device_info.product_name}",
|
||||
data=data,
|
||||
)
|
||||
|
||||
async def async_step_zeroconf(
|
||||
self, discovery_info: ZeroconfServiceInfo
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle zeroconf discovery."""
|
||||
|
||||
if (
|
||||
CONF_API_ENABLED not in discovery_info.properties
|
||||
or CONF_PATH not in discovery_info.properties
|
||||
or CONF_PRODUCT_NAME not in discovery_info.properties
|
||||
CONF_PRODUCT_NAME not in discovery_info.properties
|
||||
or CONF_PRODUCT_TYPE not in discovery_info.properties
|
||||
or CONF_SERIAL not in discovery_info.properties
|
||||
):
|
||||
return self.async_abort(reason="invalid_discovery_parameters")
|
||||
|
||||
if (discovery_info.properties[CONF_PATH]) != "/api/v1":
|
||||
return self.async_abort(reason="unsupported_api_version")
|
||||
|
||||
self.ip_address = discovery_info.host
|
||||
self.product_type = discovery_info.properties[CONF_PRODUCT_TYPE]
|
||||
self.product_name = discovery_info.properties[CONF_PRODUCT_NAME]
|
||||
|
@ -109,10 +148,12 @@ class HomeWizardConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
This flow is triggered only by DHCP discovery of known devices.
|
||||
"""
|
||||
try:
|
||||
device = await self._async_try_connect(discovery_info.ip)
|
||||
device = await async_try_connect(discovery_info.ip)
|
||||
except RecoverableError as ex:
|
||||
LOGGER.error(ex)
|
||||
return self.async_abort(reason="unknown")
|
||||
except UnauthorizedError:
|
||||
return self.async_abort(reason="unsupported_api_version")
|
||||
|
||||
await self.async_set_unique_id(
|
||||
f"{device.product_type}_{discovery_info.macaddress}"
|
||||
|
@ -139,10 +180,12 @@ class HomeWizardConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
errors: dict[str, str] | None = None
|
||||
if user_input is not None or not onboarding.async_is_onboarded(self.hass):
|
||||
try:
|
||||
await self._async_try_connect(self.ip_address)
|
||||
await async_try_connect(self.ip_address)
|
||||
except RecoverableError as ex:
|
||||
LOGGER.error(ex)
|
||||
errors = {"base": ex.error_code}
|
||||
except UnauthorizedError:
|
||||
return await self.async_step_authorize()
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title=self.product_name,
|
||||
|
@ -172,25 +215,57 @@ class HomeWizardConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
self, entry_data: Mapping[str, Any]
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle re-auth if API was disabled."""
|
||||
return await self.async_step_reauth_confirm()
|
||||
self.ip_address = entry_data[CONF_IP_ADDRESS]
|
||||
|
||||
async def async_step_reauth_confirm(
|
||||
# If token exists, we assume we use the v2 API and that the token has been invalidated
|
||||
if entry_data.get(CONF_TOKEN):
|
||||
return await self.async_step_reauth_confirm_update_token()
|
||||
|
||||
# Else we assume we use the v1 API and that the API has been disabled
|
||||
return await self.async_step_reauth_enable_api()
|
||||
|
||||
async def async_step_reauth_enable_api(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Confirm reauth dialog."""
|
||||
"""Confirm reauth dialog, where user is asked to re-enable the HomeWizard API."""
|
||||
errors: dict[str, str] | None = None
|
||||
if user_input is not None:
|
||||
reauth_entry = self._get_reauth_entry()
|
||||
try:
|
||||
await self._async_try_connect(reauth_entry.data[CONF_IP_ADDRESS])
|
||||
await async_try_connect(reauth_entry.data[CONF_IP_ADDRESS])
|
||||
except RecoverableError as ex:
|
||||
LOGGER.error(ex)
|
||||
errors = {"base": ex.error_code}
|
||||
else:
|
||||
await self.hass.config_entries.async_reload(reauth_entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
return self.async_abort(reason="reauth_enable_api_successful")
|
||||
|
||||
return self.async_show_form(step_id="reauth_confirm", errors=errors)
|
||||
return self.async_show_form(step_id="reauth_enable_api", errors=errors)
|
||||
|
||||
async def async_step_reauth_confirm_update_token(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Confirm reauth dialog."""
|
||||
assert self.ip_address
|
||||
|
||||
errors: dict[str, str] | None = None
|
||||
|
||||
token = await async_request_token(self.ip_address)
|
||||
|
||||
if user_input is not None:
|
||||
if token is None:
|
||||
errors = {"base": "authorization_failed"}
|
||||
else:
|
||||
return self.async_update_reload_and_abort(
|
||||
self._get_reauth_entry(),
|
||||
data_updates={
|
||||
CONF_TOKEN: token,
|
||||
},
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reauth_confirm_update_token", errors=errors
|
||||
)
|
||||
|
||||
async def async_step_reconfigure(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
|
@ -199,7 +274,7 @@ class HomeWizardConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
errors: dict[str, str] = {}
|
||||
if user_input:
|
||||
try:
|
||||
device_info = await self._async_try_connect(user_input[CONF_IP_ADDRESS])
|
||||
device_info = await async_try_connect(user_input[CONF_IP_ADDRESS])
|
||||
|
||||
except RecoverableError as ex:
|
||||
LOGGER.error(ex)
|
||||
|
@ -230,37 +305,65 @@ class HomeWizardConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
errors=errors,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
async def _async_try_connect(ip_address: str) -> Device:
|
||||
"""Try to connect.
|
||||
|
||||
Make connection with device to test the connection
|
||||
and to get info for unique_id.
|
||||
"""
|
||||
async def async_try_connect(ip_address: str) -> Device:
|
||||
"""Try to connect.
|
||||
|
||||
Make connection with device to test the connection
|
||||
and to get info for unique_id.
|
||||
"""
|
||||
|
||||
energy_api: HomeWizardEnergy
|
||||
|
||||
# Determine if device is v1 or v2 capable
|
||||
if await has_v2_api(ip_address):
|
||||
energy_api = HomeWizardEnergyV2(ip_address)
|
||||
else:
|
||||
energy_api = HomeWizardEnergyV1(ip_address)
|
||||
try:
|
||||
return await energy_api.device()
|
||||
|
||||
except DisabledError as ex:
|
||||
raise RecoverableError(
|
||||
"API disabled, API must be enabled in the app", "api_not_enabled"
|
||||
) from ex
|
||||
try:
|
||||
return await energy_api.device()
|
||||
|
||||
except UnsupportedError as ex:
|
||||
LOGGER.error("API version unsuppored")
|
||||
raise AbortFlow("unsupported_api_version") from ex
|
||||
except DisabledError as ex:
|
||||
raise RecoverableError(
|
||||
"API disabled, API must be enabled in the app", "api_not_enabled"
|
||||
) from ex
|
||||
|
||||
except RequestError as ex:
|
||||
raise RecoverableError(
|
||||
"Device unreachable or unexpected response", "network_error"
|
||||
) from ex
|
||||
except UnsupportedError as ex:
|
||||
LOGGER.error("API version unsuppored")
|
||||
raise AbortFlow("unsupported_api_version") from ex
|
||||
|
||||
except Exception as ex:
|
||||
LOGGER.exception("Unexpected exception")
|
||||
raise AbortFlow("unknown_error") from ex
|
||||
except RequestError as ex:
|
||||
raise RecoverableError(
|
||||
"Device unreachable or unexpected response", "network_error"
|
||||
) from ex
|
||||
|
||||
finally:
|
||||
await energy_api.close()
|
||||
except UnauthorizedError as ex:
|
||||
raise UnauthorizedError("Unauthorized") from ex
|
||||
|
||||
except Exception as ex:
|
||||
LOGGER.exception("Unexpected exception")
|
||||
raise AbortFlow("unknown_error") from ex
|
||||
|
||||
finally:
|
||||
await energy_api.close()
|
||||
|
||||
|
||||
async def async_request_token(ip_address: str) -> str | None:
|
||||
"""Try to request a token from the device.
|
||||
|
||||
This method is used to request a token from the device,
|
||||
it will return None if the token request failed.
|
||||
"""
|
||||
|
||||
api = HomeWizardEnergyV2(ip_address)
|
||||
|
||||
try:
|
||||
return await api.get_token("home-assistant")
|
||||
except DisabledError:
|
||||
return None
|
||||
finally:
|
||||
await api.close()
|
||||
|
||||
|
||||
class RecoverableError(HomeAssistantError):
|
||||
|
|
|
@ -13,8 +13,6 @@ PLATFORMS = [Platform.BUTTON, Platform.NUMBER, Platform.SENSOR, Platform.SWITCH]
|
|||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
# Platform config.
|
||||
CONF_API_ENABLED = "api_enabled"
|
||||
CONF_DATA = "data"
|
||||
CONF_PRODUCT_NAME = "product_name"
|
||||
CONF_PRODUCT_TYPE = "product_type"
|
||||
CONF_SERIAL = "serial"
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from homewizard_energy import HomeWizardEnergy
|
||||
from homewizard_energy.errors import DisabledError, RequestError
|
||||
from homewizard_energy.errors import DisabledError, RequestError, UnauthorizedError
|
||||
from homewizard_energy.models import CombinedModels as DeviceResponseEntry
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DOMAIN, LOGGER, UPDATE_INTERVAL
|
||||
|
@ -51,6 +52,9 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]
|
|||
ex, translation_domain=DOMAIN, translation_key="api_disabled"
|
||||
) from ex
|
||||
|
||||
except UnauthorizedError as ex:
|
||||
raise ConfigEntryAuthFailed from ex
|
||||
|
||||
self.api_disabled = False
|
||||
|
||||
self.data = data
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
"any_power_fail_count": {
|
||||
"default": "mdi:transmission-tower-off"
|
||||
},
|
||||
"cycles": {
|
||||
"default": "mdi:battery-sync-outline"
|
||||
},
|
||||
"dsmr_version": {
|
||||
"default": "mdi:counter"
|
||||
},
|
||||
|
|
|
@ -12,6 +12,6 @@
|
|||
"iot_class": "local_polling",
|
||||
"loggers": ["homewizard_energy"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["python-homewizard-energy==v8.1.1"],
|
||||
"zeroconf": ["_hwenergy._tcp.local."]
|
||||
"requirements": ["python-homewizard-energy==v8.2.0"],
|
||||
"zeroconf": ["_hwenergy._tcp.local.", "_homewizard._tcp.local."]
|
||||
}
|
||||
|
|
|
@ -47,7 +47,10 @@ rules:
|
|||
devices: done
|
||||
diagnostics: done
|
||||
discovery-update-info: done
|
||||
discovery: done
|
||||
discovery:
|
||||
status: done
|
||||
comment: |
|
||||
DHCP IP address updates are not supported for the v2 API.
|
||||
docs-data-update: done
|
||||
docs-examples: done
|
||||
docs-known-limitations: done
|
||||
|
@ -66,10 +69,7 @@ rules:
|
|||
exception-translations: done
|
||||
icon-translations: done
|
||||
reconfiguration-flow: done
|
||||
repair-issues:
|
||||
status: exempt
|
||||
comment: |
|
||||
This integration does not raise any repairable issues.
|
||||
repair-issues: done
|
||||
stale-devices:
|
||||
status: exempt
|
||||
comment: |
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
"""Repairs for HomeWizard integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.repairs import RepairsFlow
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from .config_flow import async_request_token
|
||||
|
||||
|
||||
class MigrateToV2ApiRepairFlow(RepairsFlow):
|
||||
"""Handler for an issue fixing flow."""
|
||||
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Create flow."""
|
||||
self.entry = entry
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> data_entry_flow.FlowResult:
|
||||
"""Handle the first step of a fix flow."""
|
||||
|
||||
return await self.async_step_confirm()
|
||||
|
||||
async def async_step_confirm(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the confirm step of a fix flow."""
|
||||
|
||||
if user_input is not None:
|
||||
return await self.async_step_authorize()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="confirm", description_placeholders={"title": self.entry.title}
|
||||
)
|
||||
|
||||
async def async_step_authorize(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the authorize step of a fix flow."""
|
||||
|
||||
ip_address = self.entry.data[CONF_IP_ADDRESS]
|
||||
|
||||
# Tell device we want a token, user must now press the button within 30 seconds
|
||||
# The first attempt will always fail, but this opens the window to press the button
|
||||
token = await async_request_token(ip_address)
|
||||
errors: dict[str, str] | None = None
|
||||
|
||||
if token is None:
|
||||
if user_input is not None:
|
||||
errors = {"base": "authorization_failed"}
|
||||
|
||||
return self.async_show_form(step_id="authorize", errors=errors)
|
||||
|
||||
data = {**self.entry.data, CONF_TOKEN: token}
|
||||
self.hass.config_entries.async_update_entry(self.entry, data=data)
|
||||
await self.hass.config_entries.async_reload(self.entry.entry_id)
|
||||
return self.async_create_entry(data={})
|
||||
|
||||
|
||||
async def async_create_fix_flow(
|
||||
hass: HomeAssistant,
|
||||
issue_id: str,
|
||||
data: dict[str, str | int | float | None] | None,
|
||||
) -> RepairsFlow:
|
||||
"""Create flow."""
|
||||
assert data is not None
|
||||
assert isinstance(data["entry_id"], str)
|
||||
|
||||
if issue_id.startswith("migrate_to_v2_api_") and (
|
||||
entry := hass.config_entries.async_get_entry(data["entry_id"])
|
||||
):
|
||||
return MigrateToV2ApiRepairFlow(entry)
|
||||
|
||||
raise ValueError(f"unknown repair {issue_id}") # pragma: no cover
|
|
@ -6,7 +6,7 @@ from collections.abc import Callable
|
|||
from dataclasses import dataclass
|
||||
from typing import Final
|
||||
|
||||
from homewizard_energy.models import ExternalDevice, Measurement
|
||||
from homewizard_energy.models import CombinedModels, ExternalDevice
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
DEVICE_CLASS_UNITS,
|
||||
|
@ -46,9 +46,9 @@ PARALLEL_UPDATES = 1
|
|||
class HomeWizardSensorEntityDescription(SensorEntityDescription):
|
||||
"""Class describing HomeWizard sensor entities."""
|
||||
|
||||
enabled_fn: Callable[[Measurement], bool] = lambda x: True
|
||||
has_fn: Callable[[Measurement], bool]
|
||||
value_fn: Callable[[Measurement], StateType]
|
||||
enabled_fn: Callable[[CombinedModels], bool] = lambda x: True
|
||||
has_fn: Callable[[CombinedModels], bool]
|
||||
value_fn: Callable[[CombinedModels], StateType]
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
|
@ -69,35 +69,43 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
key="smr_version",
|
||||
translation_key="dsmr_version",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.protocol_version is not None,
|
||||
value_fn=lambda data: data.protocol_version,
|
||||
has_fn=lambda data: data.measurement.protocol_version is not None,
|
||||
value_fn=lambda data: data.measurement.protocol_version,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="meter_model",
|
||||
translation_key="meter_model",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.meter_model is not None,
|
||||
value_fn=lambda data: data.meter_model,
|
||||
has_fn=lambda data: data.measurement.meter_model is not None,
|
||||
value_fn=lambda data: data.measurement.meter_model,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="unique_meter_id",
|
||||
translation_key="unique_meter_id",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.unique_id is not None,
|
||||
value_fn=lambda data: data.unique_id,
|
||||
has_fn=lambda data: data.measurement.unique_id is not None,
|
||||
value_fn=lambda data: data.measurement.unique_id,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="wifi_ssid",
|
||||
translation_key="wifi_ssid",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.wifi_ssid is not None,
|
||||
value_fn=lambda data: data.wifi_ssid,
|
||||
has_fn=(
|
||||
lambda data: data.system is not None and data.system.wifi_ssid is not None
|
||||
),
|
||||
value_fn=(
|
||||
lambda data: data.system.wifi_ssid if data.system is not None else None
|
||||
),
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_tariff",
|
||||
translation_key="active_tariff",
|
||||
has_fn=lambda data: data.tariff is not None,
|
||||
value_fn=lambda data: None if data.tariff is None else str(data.tariff),
|
||||
has_fn=lambda data: data.measurement.tariff is not None,
|
||||
value_fn=(
|
||||
lambda data: None
|
||||
if data.measurement.tariff is None
|
||||
else str(data.measurement.tariff)
|
||||
),
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["1", "2", "3", "4"],
|
||||
),
|
||||
|
@ -108,8 +116,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.wifi_strength is not None,
|
||||
value_fn=lambda data: data.wifi_strength,
|
||||
has_fn=lambda data: data.measurement.wifi_strength is not None,
|
||||
value_fn=lambda data: data.measurement.wifi_strength,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_import_kwh",
|
||||
|
@ -117,8 +125,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.energy_import_kwh is not None,
|
||||
value_fn=lambda data: data.energy_import_kwh,
|
||||
has_fn=lambda data: data.measurement.energy_import_kwh is not None,
|
||||
value_fn=lambda data: data.measurement.energy_import_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_import_t1_kwh",
|
||||
|
@ -129,10 +137,10 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: (
|
||||
# SKT/SDM230/630 provides both total and tariff 1: duplicate.
|
||||
data.energy_import_t1_kwh is not None
|
||||
and data.energy_export_t2_kwh is not None
|
||||
data.measurement.energy_import_t1_kwh is not None
|
||||
and data.measurement.energy_export_t2_kwh is not None
|
||||
),
|
||||
value_fn=lambda data: data.energy_import_t1_kwh,
|
||||
value_fn=lambda data: data.measurement.energy_import_t1_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_import_t2_kwh",
|
||||
|
@ -141,8 +149,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.energy_import_t2_kwh is not None,
|
||||
value_fn=lambda data: data.energy_import_t2_kwh,
|
||||
has_fn=lambda data: data.measurement.energy_import_t2_kwh is not None,
|
||||
value_fn=lambda data: data.measurement.energy_import_t2_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_import_t3_kwh",
|
||||
|
@ -151,8 +159,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.energy_import_t3_kwh is not None,
|
||||
value_fn=lambda data: data.energy_import_t3_kwh,
|
||||
has_fn=lambda data: data.measurement.energy_import_t3_kwh is not None,
|
||||
value_fn=lambda data: data.measurement.energy_import_t3_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_import_t4_kwh",
|
||||
|
@ -161,8 +169,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.energy_import_t4_kwh is not None,
|
||||
value_fn=lambda data: data.energy_import_t4_kwh,
|
||||
has_fn=lambda data: data.measurement.energy_import_t4_kwh is not None,
|
||||
value_fn=lambda data: data.measurement.energy_import_t4_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_export_kwh",
|
||||
|
@ -170,9 +178,9 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.energy_export_kwh is not None,
|
||||
enabled_fn=lambda data: data.energy_export_kwh != 0,
|
||||
value_fn=lambda data: data.energy_export_kwh,
|
||||
has_fn=lambda data: data.measurement.energy_export_kwh is not None,
|
||||
enabled_fn=lambda data: data.measurement.energy_export_kwh != 0,
|
||||
value_fn=lambda data: data.measurement.energy_export_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_export_t1_kwh",
|
||||
|
@ -183,11 +191,11 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: (
|
||||
# SKT/SDM230/630 provides both total and tariff 1: duplicate.
|
||||
data.energy_export_t1_kwh is not None
|
||||
and data.energy_export_t2_kwh is not None
|
||||
data.measurement.energy_export_t1_kwh is not None
|
||||
and data.measurement.energy_export_t2_kwh is not None
|
||||
),
|
||||
enabled_fn=lambda data: data.energy_export_t1_kwh != 0,
|
||||
value_fn=lambda data: data.energy_export_t1_kwh,
|
||||
enabled_fn=lambda data: data.measurement.energy_export_t1_kwh != 0,
|
||||
value_fn=lambda data: data.measurement.energy_export_t1_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_export_t2_kwh",
|
||||
|
@ -196,9 +204,9 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.energy_export_t2_kwh is not None,
|
||||
enabled_fn=lambda data: data.energy_export_t2_kwh != 0,
|
||||
value_fn=lambda data: data.energy_export_t2_kwh,
|
||||
has_fn=lambda data: data.measurement.energy_export_t2_kwh is not None,
|
||||
enabled_fn=lambda data: data.measurement.energy_export_t2_kwh != 0,
|
||||
value_fn=lambda data: data.measurement.energy_export_t2_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_export_t3_kwh",
|
||||
|
@ -207,9 +215,9 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.energy_export_t3_kwh is not None,
|
||||
enabled_fn=lambda data: data.energy_export_t3_kwh != 0,
|
||||
value_fn=lambda data: data.energy_export_t3_kwh,
|
||||
has_fn=lambda data: data.measurement.energy_export_t3_kwh is not None,
|
||||
enabled_fn=lambda data: data.measurement.energy_export_t3_kwh != 0,
|
||||
value_fn=lambda data: data.measurement.energy_export_t3_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_power_export_t4_kwh",
|
||||
|
@ -218,9 +226,9 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.energy_export_t4_kwh is not None,
|
||||
enabled_fn=lambda data: data.energy_export_t4_kwh != 0,
|
||||
value_fn=lambda data: data.energy_export_t4_kwh,
|
||||
has_fn=lambda data: data.measurement.energy_export_t4_kwh is not None,
|
||||
enabled_fn=lambda data: data.measurement.energy_export_t4_kwh != 0,
|
||||
value_fn=lambda data: data.measurement.energy_export_t4_kwh,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_w",
|
||||
|
@ -228,8 +236,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
has_fn=lambda data: data.power_w is not None,
|
||||
value_fn=lambda data: data.power_w,
|
||||
has_fn=lambda data: data.measurement.power_w is not None,
|
||||
value_fn=lambda data: data.measurement.power_w,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_l1_w",
|
||||
|
@ -239,8 +247,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
has_fn=lambda data: data.power_l1_w is not None,
|
||||
value_fn=lambda data: data.power_l1_w,
|
||||
has_fn=lambda data: data.measurement.power_l1_w is not None,
|
||||
value_fn=lambda data: data.measurement.power_l1_w,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_l2_w",
|
||||
|
@ -250,8 +258,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
has_fn=lambda data: data.power_l2_w is not None,
|
||||
value_fn=lambda data: data.power_l2_w,
|
||||
has_fn=lambda data: data.measurement.power_l2_w is not None,
|
||||
value_fn=lambda data: data.measurement.power_l2_w,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_l3_w",
|
||||
|
@ -261,8 +269,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
has_fn=lambda data: data.power_l3_w is not None,
|
||||
value_fn=lambda data: data.power_l3_w,
|
||||
has_fn=lambda data: data.measurement.power_l3_w is not None,
|
||||
value_fn=lambda data: data.measurement.power_l3_w,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_voltage_v",
|
||||
|
@ -270,8 +278,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.voltage_v is not None,
|
||||
value_fn=lambda data: data.voltage_v,
|
||||
has_fn=lambda data: data.measurement.voltage_v is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_v,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_voltage_l1_v",
|
||||
|
@ -281,8 +289,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.voltage_l1_v is not None,
|
||||
value_fn=lambda data: data.voltage_l1_v,
|
||||
has_fn=lambda data: data.measurement.voltage_l1_v is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_l1_v,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_voltage_l2_v",
|
||||
|
@ -292,8 +300,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.voltage_l2_v is not None,
|
||||
value_fn=lambda data: data.voltage_l2_v,
|
||||
has_fn=lambda data: data.measurement.voltage_l2_v is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_l2_v,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_voltage_l3_v",
|
||||
|
@ -303,8 +311,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.voltage_l3_v is not None,
|
||||
value_fn=lambda data: data.voltage_l3_v,
|
||||
has_fn=lambda data: data.measurement.voltage_l3_v is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_l3_v,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_current_a",
|
||||
|
@ -312,8 +320,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.current_a is not None,
|
||||
value_fn=lambda data: data.current_a,
|
||||
has_fn=lambda data: data.measurement.current_a is not None,
|
||||
value_fn=lambda data: data.measurement.current_a,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_current_l1_a",
|
||||
|
@ -323,8 +331,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.current_l1_a is not None,
|
||||
value_fn=lambda data: data.current_l1_a,
|
||||
has_fn=lambda data: data.measurement.current_l1_a is not None,
|
||||
value_fn=lambda data: data.measurement.current_l1_a,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_current_l2_a",
|
||||
|
@ -334,8 +342,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.current_l2_a is not None,
|
||||
value_fn=lambda data: data.current_l2_a,
|
||||
has_fn=lambda data: data.measurement.current_l2_a is not None,
|
||||
value_fn=lambda data: data.measurement.current_l2_a,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_current_l3_a",
|
||||
|
@ -345,8 +353,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.current_l3_a is not None,
|
||||
value_fn=lambda data: data.current_l3_a,
|
||||
has_fn=lambda data: data.measurement.current_l3_a is not None,
|
||||
value_fn=lambda data: data.measurement.current_l3_a,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_frequency_hz",
|
||||
|
@ -354,8 +362,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.frequency_hz is not None,
|
||||
value_fn=lambda data: data.frequency_hz,
|
||||
has_fn=lambda data: data.measurement.frequency_hz is not None,
|
||||
value_fn=lambda data: data.measurement.frequency_hz,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_apparent_power_va",
|
||||
|
@ -363,8 +371,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.apparent_power_va is not None,
|
||||
value_fn=lambda data: data.apparent_power_va,
|
||||
has_fn=lambda data: data.measurement.apparent_power_va is not None,
|
||||
value_fn=lambda data: data.measurement.apparent_power_va,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_apparent_power_l1_va",
|
||||
|
@ -374,8 +382,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.apparent_power_l1_va is not None,
|
||||
value_fn=lambda data: data.apparent_power_l1_va,
|
||||
has_fn=lambda data: data.measurement.apparent_power_l1_va is not None,
|
||||
value_fn=lambda data: data.measurement.apparent_power_l1_va,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_apparent_power_l2_va",
|
||||
|
@ -385,8 +393,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.apparent_power_l2_va is not None,
|
||||
value_fn=lambda data: data.apparent_power_l2_va,
|
||||
has_fn=lambda data: data.measurement.apparent_power_l2_va is not None,
|
||||
value_fn=lambda data: data.measurement.apparent_power_l2_va,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_apparent_power_l3_va",
|
||||
|
@ -396,8 +404,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.apparent_power_l3_va is not None,
|
||||
value_fn=lambda data: data.apparent_power_l3_va,
|
||||
has_fn=lambda data: data.measurement.apparent_power_l3_va is not None,
|
||||
value_fn=lambda data: data.measurement.apparent_power_l3_va,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_reactive_power_var",
|
||||
|
@ -405,8 +413,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.REACTIVE_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.reactive_power_var is not None,
|
||||
value_fn=lambda data: data.reactive_power_var,
|
||||
has_fn=lambda data: data.measurement.reactive_power_var is not None,
|
||||
value_fn=lambda data: data.measurement.reactive_power_var,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_reactive_power_l1_var",
|
||||
|
@ -416,8 +424,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.REACTIVE_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.reactive_power_l1_var is not None,
|
||||
value_fn=lambda data: data.reactive_power_l1_var,
|
||||
has_fn=lambda data: data.measurement.reactive_power_l1_var is not None,
|
||||
value_fn=lambda data: data.measurement.reactive_power_l1_var,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_reactive_power_l2_var",
|
||||
|
@ -427,8 +435,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.REACTIVE_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.reactive_power_l2_var is not None,
|
||||
value_fn=lambda data: data.reactive_power_l2_var,
|
||||
has_fn=lambda data: data.measurement.reactive_power_l2_var is not None,
|
||||
value_fn=lambda data: data.measurement.reactive_power_l2_var,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_reactive_power_l3_var",
|
||||
|
@ -438,8 +446,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.REACTIVE_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.reactive_power_l3_var is not None,
|
||||
value_fn=lambda data: data.reactive_power_l3_var,
|
||||
has_fn=lambda data: data.measurement.reactive_power_l3_var is not None,
|
||||
value_fn=lambda data: data.measurement.reactive_power_l3_var,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_factor",
|
||||
|
@ -447,8 +455,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.power_factor is not None,
|
||||
value_fn=lambda data: to_percentage(data.power_factor),
|
||||
has_fn=lambda data: data.measurement.power_factor is not None,
|
||||
value_fn=lambda data: to_percentage(data.measurement.power_factor),
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_factor_l1",
|
||||
|
@ -458,8 +466,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.power_factor_l1 is not None,
|
||||
value_fn=lambda data: to_percentage(data.power_factor_l1),
|
||||
has_fn=lambda data: data.measurement.power_factor_l1 is not None,
|
||||
value_fn=lambda data: to_percentage(data.measurement.power_factor_l1),
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_factor_l2",
|
||||
|
@ -469,8 +477,8 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.power_factor_l2 is not None,
|
||||
value_fn=lambda data: to_percentage(data.power_factor_l2),
|
||||
has_fn=lambda data: data.measurement.power_factor_l2 is not None,
|
||||
value_fn=lambda data: to_percentage(data.measurement.power_factor_l2),
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_factor_l3",
|
||||
|
@ -480,94 +488,94 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
has_fn=lambda data: data.power_factor_l3 is not None,
|
||||
value_fn=lambda data: to_percentage(data.power_factor_l3),
|
||||
has_fn=lambda data: data.measurement.power_factor_l3 is not None,
|
||||
value_fn=lambda data: to_percentage(data.measurement.power_factor_l3),
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="voltage_sag_l1_count",
|
||||
translation_key="voltage_sag_phase_count",
|
||||
translation_placeholders={"phase": "1"},
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.voltage_sag_l1_count is not None,
|
||||
value_fn=lambda data: data.voltage_sag_l1_count,
|
||||
has_fn=lambda data: data.measurement.voltage_sag_l1_count is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_sag_l1_count,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="voltage_sag_l2_count",
|
||||
translation_key="voltage_sag_phase_count",
|
||||
translation_placeholders={"phase": "2"},
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.voltage_sag_l2_count is not None,
|
||||
value_fn=lambda data: data.voltage_sag_l2_count,
|
||||
has_fn=lambda data: data.measurement.voltage_sag_l2_count is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_sag_l2_count,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="voltage_sag_l3_count",
|
||||
translation_key="voltage_sag_phase_count",
|
||||
translation_placeholders={"phase": "3"},
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.voltage_sag_l3_count is not None,
|
||||
value_fn=lambda data: data.voltage_sag_l3_count,
|
||||
has_fn=lambda data: data.measurement.voltage_sag_l3_count is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_sag_l3_count,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="voltage_swell_l1_count",
|
||||
translation_key="voltage_swell_phase_count",
|
||||
translation_placeholders={"phase": "1"},
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.voltage_swell_l1_count is not None,
|
||||
value_fn=lambda data: data.voltage_swell_l1_count,
|
||||
has_fn=lambda data: data.measurement.voltage_swell_l1_count is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_swell_l1_count,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="voltage_swell_l2_count",
|
||||
translation_key="voltage_swell_phase_count",
|
||||
translation_placeholders={"phase": "2"},
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.voltage_swell_l2_count is not None,
|
||||
value_fn=lambda data: data.voltage_swell_l2_count,
|
||||
has_fn=lambda data: data.measurement.voltage_swell_l2_count is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_swell_l2_count,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="voltage_swell_l3_count",
|
||||
translation_key="voltage_swell_phase_count",
|
||||
translation_placeholders={"phase": "3"},
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.voltage_swell_l3_count is not None,
|
||||
value_fn=lambda data: data.voltage_swell_l3_count,
|
||||
has_fn=lambda data: data.measurement.voltage_swell_l3_count is not None,
|
||||
value_fn=lambda data: data.measurement.voltage_swell_l3_count,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="any_power_fail_count",
|
||||
translation_key="any_power_fail_count",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.any_power_fail_count is not None,
|
||||
value_fn=lambda data: data.any_power_fail_count,
|
||||
has_fn=lambda data: data.measurement.any_power_fail_count is not None,
|
||||
value_fn=lambda data: data.measurement.any_power_fail_count,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="long_power_fail_count",
|
||||
translation_key="long_power_fail_count",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
has_fn=lambda data: data.long_power_fail_count is not None,
|
||||
value_fn=lambda data: data.long_power_fail_count,
|
||||
has_fn=lambda data: data.measurement.long_power_fail_count is not None,
|
||||
value_fn=lambda data: data.measurement.long_power_fail_count,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_power_average_w",
|
||||
translation_key="active_power_average_w",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
has_fn=lambda data: data.average_power_15m_w is not None,
|
||||
value_fn=lambda data: data.average_power_15m_w,
|
||||
has_fn=lambda data: data.measurement.average_power_15m_w is not None,
|
||||
value_fn=lambda data: data.measurement.average_power_15m_w,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="monthly_power_peak_w",
|
||||
translation_key="monthly_power_peak_w",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
has_fn=lambda data: data.monthly_power_peak_w is not None,
|
||||
value_fn=lambda data: data.monthly_power_peak_w,
|
||||
has_fn=lambda data: data.measurement.monthly_power_peak_w is not None,
|
||||
value_fn=lambda data: data.measurement.monthly_power_peak_w,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="active_liter_lpm",
|
||||
translation_key="active_liter_lpm",
|
||||
native_unit_of_measurement=UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
has_fn=lambda data: data.active_liter_lpm is not None,
|
||||
value_fn=lambda data: data.active_liter_lpm,
|
||||
has_fn=lambda data: data.measurement.active_liter_lpm is not None,
|
||||
value_fn=lambda data: data.measurement.active_liter_lpm,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="total_liter_m3",
|
||||
|
@ -575,8 +583,26 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
|
|||
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||
device_class=SensorDeviceClass.WATER,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.total_liter_m3 is not None,
|
||||
value_fn=lambda data: data.total_liter_m3,
|
||||
has_fn=lambda data: data.measurement.total_liter_m3 is not None,
|
||||
value_fn=lambda data: data.measurement.total_liter_m3,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="state_of_charge_pct",
|
||||
translation_key="state_of_charge_pct",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.BATTERY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
has_fn=lambda data: data.measurement.state_of_charge_pct is not None,
|
||||
value_fn=lambda data: data.measurement.state_of_charge_pct,
|
||||
),
|
||||
HomeWizardSensorEntityDescription(
|
||||
key="cycles",
|
||||
translation_key="cycles",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
has_fn=lambda data: data.measurement.cycles is not None,
|
||||
value_fn=lambda data: data.measurement.cycles,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -622,16 +648,15 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Initialize sensors."""
|
||||
|
||||
measurement = entry.runtime_data.data.measurement
|
||||
|
||||
# Initialize default sensors
|
||||
entities: list = [
|
||||
HomeWizardSensorEntity(entry.runtime_data, description)
|
||||
for description in SENSORS
|
||||
if description.has_fn(measurement)
|
||||
if description.has_fn(entry.runtime_data.data)
|
||||
]
|
||||
|
||||
# Initialize external devices
|
||||
measurement = entry.runtime_data.data.measurement
|
||||
if measurement.external_devices is not None:
|
||||
for unique_id, device in measurement.external_devices.items():
|
||||
if device.type is not None and (
|
||||
|
@ -661,13 +686,13 @@ class HomeWizardSensorEntity(HomeWizardEntity, SensorEntity):
|
|||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{coordinator.config_entry.unique_id}_{description.key}"
|
||||
if not description.enabled_fn(self.coordinator.data.measurement):
|
||||
if not description.enabled_fn(self.coordinator.data):
|
||||
self._attr_entity_registry_enabled_default = False
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the sensor value."""
|
||||
return self.entity_description.value_fn(self.coordinator.data.measurement)
|
||||
return self.entity_description.value_fn(self.coordinator.data)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
|
|
|
@ -15,9 +15,17 @@
|
|||
"title": "Confirm",
|
||||
"description": "Do you want to set up {product_type} ({serial}) at {ip_address}?"
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"reauth_enable_api": {
|
||||
"description": "The local API is disabled. Go to the HomeWizard Energy app and enable the API in the device settings."
|
||||
},
|
||||
"reauth_confirm_update_token": {
|
||||
"title": "Re-authenticate",
|
||||
"description": "[%key:component::homewizard::config::step::authorize::description%]"
|
||||
},
|
||||
"authorize": {
|
||||
"title": "Authorize",
|
||||
"description": "Press the button on the HomeWizard Energy device, then select the button below."
|
||||
},
|
||||
"reconfigure": {
|
||||
"description": "Update configuration for {title}.",
|
||||
"data": {
|
||||
|
@ -30,7 +38,8 @@
|
|||
},
|
||||
"error": {
|
||||
"api_not_enabled": "The local API is disabled. Go to the HomeWizard Energy app and enable the API in the device settings.",
|
||||
"network_error": "Device unreachable, make sure that you have entered the correct IP address and that the device is available in your network"
|
||||
"network_error": "Device unreachable, make sure that you have entered the correct IP address and that the device is available in your network",
|
||||
"authorization_failed": "Failed to authorize, make sure to press the button of the device within 30 seconds"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
|
@ -38,7 +47,8 @@
|
|||
"device_not_supported": "This device is not supported",
|
||||
"unknown_error": "[%key:common::config_flow::error::unknown%]",
|
||||
"unsupported_api_version": "Detected unsupported API version",
|
||||
"reauth_successful": "Enabling API was successful",
|
||||
"reauth_enable_api_successful": "Enabling API was successful",
|
||||
"reauth_successful": "Authorization successful",
|
||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
|
||||
"wrong_device": "The configured device is not the same found on this IP address."
|
||||
}
|
||||
|
@ -121,6 +131,12 @@
|
|||
},
|
||||
"total_liter_m3": {
|
||||
"name": "Total water usage"
|
||||
},
|
||||
"cycles": {
|
||||
"name": "Battery cycles"
|
||||
},
|
||||
"state_of_charge_pct": {
|
||||
"name": "State of charge"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
@ -139,5 +155,26 @@
|
|||
"communication_error": {
|
||||
"message": "An error occurred while communicating with HomeWizard device"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"migrate_to_v2_api": {
|
||||
"title": "Update authentication method",
|
||||
"fix_flow": {
|
||||
"step": {
|
||||
"confirm": {
|
||||
"title": "[%key:component::homewizard::issues::migrate_to_v2_api::title%]",
|
||||
"description": "Your {title} now supports a more secure and feature-rich communication method. To take advantage of this, you need to reconfigure the integration.\n\nSelect **Submit** to start the reconfiguration."
|
||||
},
|
||||
"authorize": {
|
||||
"title": "[%key:component::homewizard::config::step::authorize::title%]",
|
||||
"description": "[%key:component::homewizard::config::step::authorize::description%]"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"authorization_failed": "[%key:component::homewizard::config::error::authorization_failed%]",
|
||||
"unknown_error": "[%key:common::config_flow::error::unknown%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ SWITCHES = [
|
|||
key="cloud_connection",
|
||||
translation_key="cloud_connection",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
create_fn=lambda _: True,
|
||||
create_fn=lambda x: x.device.supports_cloud_enable(),
|
||||
available_fn=lambda x: x.system is not None,
|
||||
is_on_fn=lambda x: x.system.cloud_enabled if x.system else None,
|
||||
set_fn=lambda api, active: api.system(cloud_enabled=active),
|
||||
|
|
|
@ -522,6 +522,11 @@ ZEROCONF = {
|
|||
"domain": "homekit",
|
||||
},
|
||||
],
|
||||
"_homewizard._tcp.local.": [
|
||||
{
|
||||
"domain": "homewizard",
|
||||
},
|
||||
],
|
||||
"_hscp._tcp.local.": [
|
||||
{
|
||||
"domain": "apple_tv",
|
||||
|
|
|
@ -2388,7 +2388,7 @@ python-gitlab==1.6.0
|
|||
python-homeassistant-analytics==0.8.1
|
||||
|
||||
# homeassistant.components.homewizard
|
||||
python-homewizard-energy==v8.1.1
|
||||
python-homewizard-energy==v8.2.0
|
||||
|
||||
# homeassistant.components.hp_ilo
|
||||
python-hpilo==4.4.3
|
||||
|
|
|
@ -1933,7 +1933,7 @@ python-fullykiosk==0.0.14
|
|||
python-homeassistant-analytics==0.8.1
|
||||
|
||||
# homeassistant.components.homewizard
|
||||
python-homewizard-energy==v8.1.1
|
||||
python-homewizard-energy==v8.2.0
|
||||
|
||||
# homeassistant.components.izone
|
||||
python-izone==1.2.9
|
||||
|
|
|
@ -3,11 +3,18 @@
|
|||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from homewizard_energy.models import CombinedModels, Device, Measurement, State, System
|
||||
from homewizard_energy.models import (
|
||||
CombinedModels,
|
||||
Device,
|
||||
Measurement,
|
||||
State,
|
||||
System,
|
||||
Token,
|
||||
)
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.homewizard.const import DOMAIN
|
||||
from homeassistant.const import CONF_IP_ADDRESS
|
||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, get_fixture_path, load_json_object_fixture
|
||||
|
@ -65,6 +72,59 @@ def mock_homewizardenergy(
|
|||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_homewizardenergy_v2(
|
||||
device_fixture: str,
|
||||
) -> MagicMock:
|
||||
"""Return a mock bridge."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.homewizard.HomeWizardEnergyV2",
|
||||
autospec=True,
|
||||
) as homewizard,
|
||||
patch(
|
||||
"homeassistant.components.homewizard.config_flow.HomeWizardEnergyV2",
|
||||
new=homewizard,
|
||||
),
|
||||
):
|
||||
client = homewizard.return_value
|
||||
|
||||
client.combined.return_value = CombinedModels(
|
||||
device=Device.from_dict(
|
||||
load_json_object_fixture(f"v2/{device_fixture}/device.json", DOMAIN)
|
||||
),
|
||||
measurement=Measurement.from_dict(
|
||||
load_json_object_fixture(
|
||||
f"v2/{device_fixture}/measurement.json", DOMAIN
|
||||
)
|
||||
),
|
||||
state=(
|
||||
State.from_dict(
|
||||
load_json_object_fixture(f"v2/{device_fixture}/state.json", DOMAIN)
|
||||
)
|
||||
if get_fixture_path(f"v2/{device_fixture}/state.json", DOMAIN).exists()
|
||||
else None
|
||||
),
|
||||
system=(
|
||||
System.from_dict(
|
||||
load_json_object_fixture(f"v2/{device_fixture}/system.json", DOMAIN)
|
||||
)
|
||||
if get_fixture_path(f"v2/{device_fixture}/system.json", DOMAIN).exists()
|
||||
else None
|
||||
),
|
||||
)
|
||||
|
||||
# device() call is used during configuration flow
|
||||
client.device.return_value = client.combined.return_value.device
|
||||
|
||||
# Authorization flow is used during configuration flow
|
||||
client.get_token.return_value = Token.from_dict(
|
||||
load_json_object_fixture("v2/generic/token.json", DOMAIN)
|
||||
).token
|
||||
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Mock setting up a config entry."""
|
||||
|
@ -90,6 +150,20 @@ def mock_config_entry() -> MockConfigEntry:
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry_v2() -> MockConfigEntry:
|
||||
"""Return the default mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
title="Device",
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_IP_ADDRESS: "127.0.0.1",
|
||||
CONF_TOKEN: "00112233445566778899ABCDEFABCDEF",
|
||||
},
|
||||
unique_id="HWE-P1_5c2fafabcdef",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"wifi_ssid": "simulating v1 support",
|
||||
"wifi_strength": 100,
|
||||
"total_power_import_kwh": 123.456,
|
||||
"total_power_export_kwh": 123.456,
|
||||
"active_power_w": 123,
|
||||
"active_voltage_v": 230,
|
||||
"active_current_a": 1.5,
|
||||
"active_frequency_hz": 50,
|
||||
"state_of_charge_pct": 50,
|
||||
"cycles": 123
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"product_type": "HWE-BAT",
|
||||
"product_name": "Plug-In Battery",
|
||||
"serial": "5c2fafabcdef",
|
||||
"firmware_version": "1.00",
|
||||
"api_version": "v1"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"wifi_ssid": "My Wi-Fi",
|
||||
"wifi_rssi_db": -77,
|
||||
"cloud_enabled": false,
|
||||
"uptime_s": 356,
|
||||
"status_led_brightness_pct": 100
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"product_type": "HWE-P1",
|
||||
"product_name": "P1 meter",
|
||||
"serial": "5c2fafabcdef",
|
||||
"firmware_version": "4.19",
|
||||
"api_version": "2.0.0"
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"protocol_version": 50,
|
||||
"meter_model": "ISKRA 2M550T-101",
|
||||
"unique_id": "4E6576657220476F6E6E61204C657420596F7520446F776E",
|
||||
"timestamp": "2024-06-28T14:12:34",
|
||||
"tariff": 2,
|
||||
"energy_import_kwh": 13779.338,
|
||||
"energy_import_t1_kwh": 10830.511,
|
||||
"energy_import_t2_kwh": 2948.827,
|
||||
"energy_export_kwh": 1234.567,
|
||||
"energy_export_t1_kwh": 234.567,
|
||||
"energy_export_t2_kwh": 1000,
|
||||
"power_w": -543,
|
||||
"power_l1_w": -676,
|
||||
"power_l2_w": 133,
|
||||
"power_l3_w": 0,
|
||||
"current_a": 6,
|
||||
"current_l1_a": -4,
|
||||
"current_l2_a": 2,
|
||||
"current_l3_a": 0,
|
||||
"voltage_sag_l1_count": 1,
|
||||
"voltage_sag_l2_count": 1,
|
||||
"voltage_sag_l3_count": 0,
|
||||
"voltage_swell_l1_count": 0,
|
||||
"voltage_swell_l2_count": 0,
|
||||
"voltage_swell_l3_count": 0,
|
||||
"any_power_fail_count": 4,
|
||||
"long_power_fail_count": 5,
|
||||
"average_power_15m_w": 123.0,
|
||||
"monthly_power_peak_w": 1111.0,
|
||||
"monthly_power_peak_timestamp": "2024-06-04T10:11:22",
|
||||
"external": [
|
||||
{
|
||||
"unique_id": "4E6576657220676F6E6E612072756E2061726F756E64",
|
||||
"type": "gas_meter",
|
||||
"timestamp": "2024-06-28T14:00:00",
|
||||
"value": 2569.646,
|
||||
"unit": "m3"
|
||||
},
|
||||
{
|
||||
"unique_id": "616E642064657365727420796F75",
|
||||
"type": "water_meter",
|
||||
"timestamp": "2024-06-28T14:05:00",
|
||||
"value": 123.456,
|
||||
"unit": "m3"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"wifi_ssid": "My Wi-Fi",
|
||||
"wifi_rssi_db": -77,
|
||||
"cloud_enabled": false,
|
||||
"uptime_s": 356,
|
||||
"status_led_brightness_pct": 100,
|
||||
"api_v1_enabled": true
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"token": "00112233445566778899aabbccddeeff",
|
||||
"name": "local/new_user"
|
||||
}
|
|
@ -1,9 +1,99 @@
|
|||
# serializer version: 1
|
||||
# name: test_diagnostics[HWE-BAT]
|
||||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '1.00',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Plug-In Battery',
|
||||
'product_name': 'Plug-In Battery',
|
||||
'product_type': 'HWE-BAT',
|
||||
'serial': '**REDACTED**',
|
||||
}),
|
||||
'measurement': dict({
|
||||
'active_liter_lpm': None,
|
||||
'any_power_fail_count': None,
|
||||
'apparent_power_l1_va': None,
|
||||
'apparent_power_l2_va': None,
|
||||
'apparent_power_l3_va': None,
|
||||
'apparent_power_va': None,
|
||||
'average_power_15m_w': None,
|
||||
'current_a': 1.5,
|
||||
'current_l1_a': None,
|
||||
'current_l2_a': None,
|
||||
'current_l3_a': None,
|
||||
'cycles': 123,
|
||||
'energy_export_kwh': 123.456,
|
||||
'energy_export_t1_kwh': None,
|
||||
'energy_export_t2_kwh': None,
|
||||
'energy_export_t3_kwh': None,
|
||||
'energy_export_t4_kwh': None,
|
||||
'energy_import_kwh': 123.456,
|
||||
'energy_import_t1_kwh': None,
|
||||
'energy_import_t2_kwh': None,
|
||||
'energy_import_t3_kwh': None,
|
||||
'energy_import_t4_kwh': None,
|
||||
'external_devices': None,
|
||||
'frequency_hz': 50.0,
|
||||
'long_power_fail_count': None,
|
||||
'meter_model': None,
|
||||
'monthly_power_peak_timestamp': None,
|
||||
'monthly_power_peak_w': None,
|
||||
'power_factor': None,
|
||||
'power_factor_l1': None,
|
||||
'power_factor_l2': None,
|
||||
'power_factor_l3': None,
|
||||
'power_l1_w': None,
|
||||
'power_l2_w': None,
|
||||
'power_l3_w': None,
|
||||
'power_w': 123.0,
|
||||
'protocol_version': None,
|
||||
'reactive_power_l1_var': None,
|
||||
'reactive_power_l2_var': None,
|
||||
'reactive_power_l3_var': None,
|
||||
'reactive_power_var': None,
|
||||
'state_of_charge_pct': 50.0,
|
||||
'tariff': None,
|
||||
'timestamp': None,
|
||||
'total_liter_m3': None,
|
||||
'unique_id': None,
|
||||
'voltage_l1_v': None,
|
||||
'voltage_l2_v': None,
|
||||
'voltage_l3_v': None,
|
||||
'voltage_sag_l1_count': None,
|
||||
'voltage_sag_l2_count': None,
|
||||
'voltage_sag_l3_count': None,
|
||||
'voltage_swell_l1_count': None,
|
||||
'voltage_swell_l2_count': None,
|
||||
'voltage_swell_l3_count': None,
|
||||
'voltage_v': 230.0,
|
||||
'wifi_ssid': '**REDACTED**',
|
||||
'wifi_strength': 100,
|
||||
}),
|
||||
'state': None,
|
||||
'system': dict({
|
||||
'api_v1_enabled': None,
|
||||
'cloud_enabled': False,
|
||||
'status_led_brightness_pct': 100,
|
||||
'uptime_s': 356,
|
||||
'wifi_rssi_db': -77,
|
||||
'wifi_ssid': '**REDACTED**',
|
||||
}),
|
||||
}),
|
||||
'entry': dict({
|
||||
'ip_address': '**REDACTED**',
|
||||
'product_name': 'P1 Meter',
|
||||
'product_type': 'HWE-P1',
|
||||
'serial': '**REDACTED**',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_diagnostics[HWE-KWH1]
|
||||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': 'v1',
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '3.06',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Wi-Fi kWh Meter 1-phase',
|
||||
|
@ -93,7 +183,7 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': 'v1',
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '3.06',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Wi-Fi kWh Meter 3-phase',
|
||||
|
@ -183,7 +273,7 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': 'v1',
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '4.19',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Wi-Fi P1 Meter',
|
||||
|
@ -309,7 +399,7 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': 'v1',
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '3.03',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Wi-Fi Energy Socket',
|
||||
|
@ -403,7 +493,7 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': 'v1',
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '4.07',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Wi-Fi Energy Socket',
|
||||
|
@ -497,7 +587,7 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': 'v1',
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '2.03',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Wi-Fi Watermeter',
|
||||
|
@ -587,7 +677,7 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': 'v1',
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '3.06',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Wi-Fi kWh Meter 1-phase',
|
||||
|
@ -677,7 +767,7 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'device': dict({
|
||||
'api_version': 'v1',
|
||||
'api_version': '1.0.0',
|
||||
'firmware_version': '3.06',
|
||||
'id': '**REDACTED**',
|
||||
'model_name': 'Wi-Fi kWh Meter 3-phase',
|
||||
|
|
|
@ -1,4 +1,704 @@
|
|||
# serializer version: 1
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_battery_cycles:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
'5c:2f:af:ab:cd:ef',
|
||||
),
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'homewizard',
|
||||
'5c2fafabcdef',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'HomeWizard',
|
||||
'model': 'Plug-In Battery',
|
||||
'model_id': 'HWE-BAT',
|
||||
'name': 'Device',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': '1.00',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_battery_cycles:entity-registry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.device_battery_cycles',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Battery cycles',
|
||||
'platform': 'homewizard',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'cycles',
|
||||
'unique_id': 'HWE-P1_5c2fafabcdef_cycles',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_battery_cycles:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Device Battery cycles',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.device_battery_cycles',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '123',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_current:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
'5c:2f:af:ab:cd:ef',
|
||||
),
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'homewizard',
|
||||
'5c2fafabcdef',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'HomeWizard',
|
||||
'model': 'Plug-In Battery',
|
||||
'model_id': 'HWE-BAT',
|
||||
'name': 'Device',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': '1.00',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_current:entity-registry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.device_current',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.CURRENT: 'current'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Current',
|
||||
'platform': 'homewizard',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'HWE-P1_5c2fafabcdef_active_current_a',
|
||||
'unit_of_measurement': <UnitOfElectricCurrent.AMPERE: 'A'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_current:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'current',
|
||||
'friendly_name': 'Device Current',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfElectricCurrent.AMPERE: 'A'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.device_current',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1.5',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_energy_export:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
'5c:2f:af:ab:cd:ef',
|
||||
),
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'homewizard',
|
||||
'5c2fafabcdef',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'HomeWizard',
|
||||
'model': 'Plug-In Battery',
|
||||
'model_id': 'HWE-BAT',
|
||||
'name': 'Device',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': '1.00',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_energy_export:entity-registry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.device_energy_export',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Energy export',
|
||||
'platform': 'homewizard',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'total_energy_export_kwh',
|
||||
'unique_id': 'HWE-P1_5c2fafabcdef_total_power_export_kwh',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_energy_export:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Device Energy export',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.device_energy_export',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '123.456',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_energy_import:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
'5c:2f:af:ab:cd:ef',
|
||||
),
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'homewizard',
|
||||
'5c2fafabcdef',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'HomeWizard',
|
||||
'model': 'Plug-In Battery',
|
||||
'model_id': 'HWE-BAT',
|
||||
'name': 'Device',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': '1.00',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_energy_import:entity-registry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.device_energy_import',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Energy import',
|
||||
'platform': 'homewizard',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'total_energy_import_kwh',
|
||||
'unique_id': 'HWE-P1_5c2fafabcdef_total_power_import_kwh',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_energy_import:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Device Energy import',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.device_energy_import',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '123.456',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_frequency:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
'5c:2f:af:ab:cd:ef',
|
||||
),
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'homewizard',
|
||||
'5c2fafabcdef',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'HomeWizard',
|
||||
'model': 'Plug-In Battery',
|
||||
'model_id': 'HWE-BAT',
|
||||
'name': 'Device',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': '1.00',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_frequency:entity-registry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.device_frequency',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.FREQUENCY: 'frequency'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Frequency',
|
||||
'platform': 'homewizard',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'HWE-P1_5c2fafabcdef_active_frequency_hz',
|
||||
'unit_of_measurement': <UnitOfFrequency.HERTZ: 'Hz'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_frequency:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'frequency',
|
||||
'friendly_name': 'Device Frequency',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfFrequency.HERTZ: 'Hz'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.device_frequency',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '50.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_power:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
'5c:2f:af:ab:cd:ef',
|
||||
),
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'homewizard',
|
||||
'5c2fafabcdef',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'HomeWizard',
|
||||
'model': 'Plug-In Battery',
|
||||
'model_id': 'HWE-BAT',
|
||||
'name': 'Device',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': '1.00',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_power:entity-registry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.device_power',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 0,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Power',
|
||||
'platform': 'homewizard',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'HWE-P1_5c2fafabcdef_active_power_w',
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_power:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'power',
|
||||
'friendly_name': 'Device Power',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.device_power',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '123.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_state_of_charge:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
'5c:2f:af:ab:cd:ef',
|
||||
),
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'homewizard',
|
||||
'5c2fafabcdef',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'HomeWizard',
|
||||
'model': 'Plug-In Battery',
|
||||
'model_id': 'HWE-BAT',
|
||||
'name': 'Device',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': '1.00',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_state_of_charge:entity-registry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.device_state_of_charge',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 0,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'State of charge',
|
||||
'platform': 'homewizard',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'state_of_charge_pct',
|
||||
'unique_id': 'HWE-P1_5c2fafabcdef_state_of_charge_pct',
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_state_of_charge:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'battery',
|
||||
'friendly_name': 'Device State of charge',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.device_state_of_charge',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '50.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_voltage:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
tuple(
|
||||
'mac',
|
||||
'5c:2f:af:ab:cd:ef',
|
||||
),
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'homewizard',
|
||||
'5c2fafabcdef',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'HomeWizard',
|
||||
'model': 'Plug-In Battery',
|
||||
'model_id': 'HWE-BAT',
|
||||
'name': 'Device',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': '1.00',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_voltage:entity-registry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.device_voltage',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Voltage',
|
||||
'platform': 'homewizard',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'HWE-P1_5c2fafabcdef_active_voltage_v',
|
||||
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_voltage:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'voltage',
|
||||
'friendly_name': 'Device Voltage',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.device_voltage',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '230.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[HWE-KWH1-entity_ids7][sensor.device_apparent_power:device-registry]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
"""Test the homewizard config flow."""
|
||||
|
||||
from ipaddress import ip_address
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from homewizard_energy.errors import DisabledError, RequestError, UnsupportedError
|
||||
from homewizard_energy.errors import (
|
||||
DisabledError,
|
||||
RequestError,
|
||||
UnauthorizedError,
|
||||
UnsupportedError,
|
||||
)
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.homewizard.const import DOMAIN
|
||||
from homeassistant.const import CONF_IP_ADDRESS
|
||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||
|
@ -225,10 +230,10 @@ async def test_discovery_missing_data_in_service_info(hass: HomeAssistant) -> No
|
|||
type="",
|
||||
name="",
|
||||
properties={
|
||||
# "api_enabled": "1", --> removed
|
||||
"api_enabled": "1",
|
||||
"path": "/api/v1",
|
||||
"product_name": "P1 meter",
|
||||
"product_type": "HWE-P1",
|
||||
# "product_type": "HWE-P1", --> removed
|
||||
"serial": "5c2fafabcdef",
|
||||
},
|
||||
),
|
||||
|
@ -238,32 +243,6 @@ async def test_discovery_missing_data_in_service_info(hass: HomeAssistant) -> No
|
|||
assert result["reason"] == "invalid_discovery_parameters"
|
||||
|
||||
|
||||
async def test_discovery_invalid_api(hass: HomeAssistant) -> None:
|
||||
"""Test discovery detecting invalid_api."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
data=ZeroconfServiceInfo(
|
||||
ip_address=ip_address("127.0.0.1"),
|
||||
ip_addresses=[ip_address("127.0.0.1")],
|
||||
port=80,
|
||||
hostname="p1meter-ddeeff.local.",
|
||||
type="",
|
||||
name="",
|
||||
properties={
|
||||
"api_enabled": "1",
|
||||
"path": "/api/not_v1",
|
||||
"product_name": "P1 meter",
|
||||
"product_type": "HWE-P1",
|
||||
"serial": "5c2fafabcdef",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "unsupported_api_version"
|
||||
|
||||
|
||||
async def test_dhcp_discovery_updates_entry(
|
||||
hass: HomeAssistant,
|
||||
mock_homewizardenergy: MagicMock,
|
||||
|
@ -338,6 +317,32 @@ async def test_dhcp_discovery_ignores_unknown(
|
|||
assert result.get("reason") == "unknown"
|
||||
|
||||
|
||||
async def test_dhcp_discovery_aborts_for_v2_api(
|
||||
hass: HomeAssistant,
|
||||
mock_homewizardenergy: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test DHCP discovery aborts when v2 API is detected.
|
||||
|
||||
DHCP discovery requires authorization which is not yet implemented
|
||||
"""
|
||||
mock_homewizardenergy.device.side_effect = UnauthorizedError
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_DHCP},
|
||||
data=DhcpServiceInfo(
|
||||
ip="1.0.0.127",
|
||||
hostname="HW-p1meter-aabbcc",
|
||||
macaddress="5c2fafabcdef",
|
||||
),
|
||||
)
|
||||
|
||||
assert result.get("type") is FlowResultType.ABORT
|
||||
assert result.get("reason") == "unsupported_api_version"
|
||||
|
||||
|
||||
async def test_discovery_flow_updates_new_ip(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
|
@ -455,12 +460,12 @@ async def test_reauth_flow(
|
|||
result = await mock_config_entry.start_reauth_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["step_id"] == "reauth_enable_api"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
assert result["reason"] == "reauth_enable_api_successful"
|
||||
|
||||
|
||||
async def test_reauth_error(
|
||||
|
@ -475,7 +480,7 @@ async def test_reauth_error(
|
|||
result = await mock_config_entry.start_reauth_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["step_id"] == "reauth_enable_api"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
|
@ -609,3 +614,222 @@ async def test_reconfigure_cannot_connect(
|
|||
|
||||
# changed entry
|
||||
assert mock_config_entry.data[CONF_IP_ADDRESS] == "1.0.0.127"
|
||||
|
||||
|
||||
### TESTS FOR V2 IMPLEMENTATION ###
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_manual_flow_works_with_v2_api_support(
|
||||
hass: HomeAssistant,
|
||||
mock_homewizardenergy_v2: MagicMock,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test config flow accepts user configuration and triggers authorization when detected v2 support."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
# Simulate v2 support but not authorized
|
||||
mock_homewizardenergy_v2.device.side_effect = UnauthorizedError
|
||||
mock_homewizardenergy_v2.get_token.side_effect = DisabledError
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homewizard.config_flow.has_v2_api", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "authorize"
|
||||
|
||||
# Simulate user authorizing
|
||||
mock_homewizardenergy_v2.device.side_effect = None
|
||||
mock_homewizardenergy_v2.get_token.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_manual_flow_detects_failed_user_authorization(
|
||||
hass: HomeAssistant,
|
||||
mock_homewizardenergy_v2: MagicMock,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test config flow accepts user configuration and detects failed button press by user."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
# Simulate v2 support but not authorized
|
||||
mock_homewizardenergy_v2.device.side_effect = UnauthorizedError
|
||||
mock_homewizardenergy_v2.get_token.side_effect = DisabledError
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homewizard.config_flow.has_v2_api", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "authorize"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "authorize"
|
||||
assert result["errors"] == {"base": "authorization_failed"}
|
||||
|
||||
# Restore normal functionality
|
||||
mock_homewizardenergy_v2.device.side_effect = None
|
||||
mock_homewizardenergy_v2.get_token.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_reauth_flow_updates_token(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_config_entry_v2: MockConfigEntry,
|
||||
mock_homewizardenergy_v2: MagicMock,
|
||||
) -> None:
|
||||
"""Test reauth flow token is updated."""
|
||||
|
||||
mock_config_entry_v2.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry_v2.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await mock_config_entry_v2.start_reauth_flow(hass)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm_update_token"
|
||||
|
||||
# Simulate user pressing the button and getting a new token
|
||||
mock_homewizardenergy_v2.get_token.return_value = "cool_new_token"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
|
||||
# Verify that the token was updated
|
||||
await hass.async_block_till_done()
|
||||
assert (
|
||||
hass.config_entries.async_entries(DOMAIN)[0].data.get(CONF_TOKEN)
|
||||
== "cool_new_token"
|
||||
)
|
||||
assert len(mock_setup_entry.mock_calls) == 2
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_reauth_flow_handles_user_not_pressing_button(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_config_entry_v2: MockConfigEntry,
|
||||
mock_homewizardenergy_v2: MagicMock,
|
||||
) -> None:
|
||||
"""Test reauth flow token is updated."""
|
||||
|
||||
mock_config_entry_v2.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry_v2.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await mock_config_entry_v2.start_reauth_flow(hass)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm_update_token"
|
||||
assert result["errors"] is None
|
||||
|
||||
# Simulate button not being pressed
|
||||
mock_homewizardenergy_v2.get_token.side_effect = DisabledError
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "authorization_failed"}
|
||||
|
||||
# Simulate user pressing the button and getting a new token
|
||||
mock_homewizardenergy_v2.get_token.side_effect = None
|
||||
mock_homewizardenergy_v2.get_token.return_value = "cool_new_token"
|
||||
|
||||
# Successful reauth
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
|
||||
# Verify that the token was updated
|
||||
await hass.async_block_till_done()
|
||||
assert (
|
||||
hass.config_entries.async_entries(DOMAIN)[0].data.get(CONF_TOKEN)
|
||||
== "cool_new_token"
|
||||
)
|
||||
assert len(mock_setup_entry.mock_calls) == 2
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_discovery_with_v2_api_ask_authorization(
|
||||
hass: HomeAssistant,
|
||||
# mock_setup_entry: AsyncMock,
|
||||
mock_homewizardenergy_v2: MagicMock,
|
||||
) -> None:
|
||||
"""Test discovery detecting missing discovery info."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
data=ZeroconfServiceInfo(
|
||||
ip_address=ip_address("127.0.0.1"),
|
||||
ip_addresses=[ip_address("127.0.0.1")],
|
||||
port=443,
|
||||
hostname="p1meter-abcdef.local.",
|
||||
type="",
|
||||
name="",
|
||||
properties={
|
||||
"api_version": "2.0.0",
|
||||
"id": "appliance/p1dongle/5c2fafabcdef",
|
||||
"product_name": "P1 meter",
|
||||
"product_type": "HWE-P1",
|
||||
"serial": "5c2fafabcdef",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "discovery_confirm"
|
||||
|
||||
mock_homewizardenergy_v2.device.side_effect = UnauthorizedError
|
||||
mock_homewizardenergy_v2.get_token.side_effect = DisabledError
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homewizard.config_flow.has_v2_api", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "authorize"
|
||||
|
||||
mock_homewizardenergy_v2.get_token.side_effect = None
|
||||
mock_homewizardenergy_v2.get_token.return_value = "cool_token"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"][CONF_TOKEN] == "cool_token"
|
||||
|
|
|
@ -21,6 +21,7 @@ from tests.typing import ClientSessionGenerator
|
|||
"SDM630",
|
||||
"HWE-KWH1",
|
||||
"HWE-KWH3",
|
||||
"HWE-BAT",
|
||||
],
|
||||
)
|
||||
async def test_diagnostics(
|
||||
|
|
|
@ -4,7 +4,7 @@ from datetime import timedelta
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from homewizard_energy.errors import DisabledError
|
||||
from homewizard_energy.errors import DisabledError, UnauthorizedError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.homewizard.const import DOMAIN
|
||||
|
@ -14,12 +14,12 @@ from homeassistant.core import HomeAssistant
|
|||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def test_load_unload(
|
||||
async def test_load_unload_v1(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_homewizardenergy: MagicMock,
|
||||
) -> None:
|
||||
"""Test loading and unloading of integration."""
|
||||
"""Test loading and unloading of integration with v1 config."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -33,6 +33,25 @@ async def test_load_unload(
|
|||
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_load_unload_v2(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry_v2: MockConfigEntry,
|
||||
mock_homewizardenergy_v2: MagicMock,
|
||||
) -> None:
|
||||
"""Test loading and unloading of integration with v2 config."""
|
||||
mock_config_entry_v2.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry_v2.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config_entry_v2.state is ConfigEntryState.LOADED
|
||||
assert len(mock_homewizardenergy_v2.combined.mock_calls) == 1
|
||||
|
||||
await hass.config_entries.async_unload(mock_config_entry_v2.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config_entry_v2.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_load_failed_host_unavailable(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
|
@ -64,7 +83,7 @@ async def test_load_detect_api_disabled(
|
|||
assert len(flows) == 1
|
||||
|
||||
flow = flows[0]
|
||||
assert flow.get("step_id") == "reauth_confirm"
|
||||
assert flow.get("step_id") == "reauth_enable_api"
|
||||
assert flow.get("handler") == DOMAIN
|
||||
|
||||
assert "context" in flow
|
||||
|
@ -72,6 +91,31 @@ async def test_load_detect_api_disabled(
|
|||
assert flow["context"].get("entry_id") == mock_config_entry.entry_id
|
||||
|
||||
|
||||
async def test_load_detect_invalid_token(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry_v2: MockConfigEntry,
|
||||
mock_homewizardenergy_v2: MagicMock,
|
||||
) -> None:
|
||||
"""Test setup detects invalid token."""
|
||||
mock_homewizardenergy_v2.combined.side_effect = UnauthorizedError()
|
||||
mock_config_entry_v2.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry_v2.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config_entry_v2.state is ConfigEntryState.SETUP_ERROR
|
||||
|
||||
flows = hass.config_entries.flow.async_progress()
|
||||
assert len(flows) == 1
|
||||
|
||||
flow = flows[0]
|
||||
assert flow.get("step_id") == "reauth_confirm_update_token"
|
||||
assert flow.get("handler") == DOMAIN
|
||||
|
||||
assert "context" in flow
|
||||
assert flow["context"].get("source") == SOURCE_REAUTH
|
||||
assert flow["context"].get("entry_id") == mock_config_entry_v2.entry_id
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_homewizardenergy")
|
||||
async def test_load_removes_reauth_flow(
|
||||
hass: HomeAssistant,
|
||||
|
@ -128,5 +172,5 @@ async def test_disablederror_reloads_integration(
|
|||
assert len(flows) == 1
|
||||
|
||||
flow = flows[0]
|
||||
assert flow.get("step_id") == "reauth_confirm"
|
||||
assert flow.get("step_id") == "reauth_enable_api"
|
||||
assert flow.get("handler") == DOMAIN
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
"""Test the homewizard config flow."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from homewizard_energy.errors import DisabledError
|
||||
|
||||
from homeassistant.components.homewizard.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.repairs import (
|
||||
async_process_repairs_platforms,
|
||||
process_repair_fix_flow,
|
||||
start_repair_fix_flow,
|
||||
)
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
async def test_repair_acquires_token(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_homewizardenergy: MagicMock,
|
||||
mock_homewizardenergy_v2: MagicMock,
|
||||
hass_client: ClientSessionGenerator,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test repair flow is able to obtain and use token."""
|
||||
|
||||
assert await async_setup_component(hass, "repairs", {})
|
||||
await async_process_repairs_platforms(hass)
|
||||
client = await hass_client()
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
with patch("homeassistant.components.homewizard.has_v2_api", return_value=True):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
# Get active repair flow
|
||||
issue_id = f"migrate_to_v2_api_{mock_config_entry.entry_id}"
|
||||
issue = issue_registry.async_get_issue(DOMAIN, issue_id)
|
||||
assert issue is not None
|
||||
|
||||
assert issue.data.get("entry_id") == mock_config_entry.entry_id
|
||||
|
||||
mock_homewizardenergy_v2.get_token.side_effect = DisabledError
|
||||
|
||||
result = await start_repair_fix_flow(client, DOMAIN, issue_id)
|
||||
|
||||
flow_id = result["flow_id"]
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm"
|
||||
|
||||
result = await process_repair_fix_flow(client, flow_id)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "authorize"
|
||||
|
||||
# Simulate user not pressing the button
|
||||
result = await process_repair_fix_flow(client, flow_id, json={})
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "authorize"
|
||||
assert result["errors"] == {"base": "authorization_failed"}
|
||||
|
||||
# Simulate user pressing the button and getting a new token
|
||||
mock_homewizardenergy_v2.get_token.side_effect = None
|
||||
mock_homewizardenergy_v2.get_token.return_value = "cool_token"
|
||||
result = await process_repair_fix_flow(client, flow_id, json={})
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert mock_config_entry.data[CONF_TOKEN] == "cool_token"
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
assert issue_registry.async_get_issue(DOMAIN, issue_id) is None
|
|
@ -291,6 +291,19 @@ pytestmark = [
|
|||
"sensor.water_meter_water",
|
||||
],
|
||||
),
|
||||
(
|
||||
"HWE-BAT",
|
||||
[
|
||||
"sensor.device_battery_cycles",
|
||||
"sensor.device_current",
|
||||
"sensor.device_energy_export",
|
||||
"sensor.device_energy_import",
|
||||
"sensor.device_frequency",
|
||||
"sensor.device_power",
|
||||
"sensor.device_state_of_charge",
|
||||
"sensor.device_voltage",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_sensors(
|
||||
|
@ -431,6 +444,14 @@ async def test_sensors(
|
|||
"sensor.device_wi_fi_strength",
|
||||
],
|
||||
),
|
||||
(
|
||||
"HWE-BAT",
|
||||
[
|
||||
"sensor.device_current",
|
||||
"sensor.device_frequency",
|
||||
"sensor.device_voltage",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_disabled_by_default_sensors(
|
||||
|
@ -492,6 +513,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_apparent_power_phase_3",
|
||||
"sensor.device_apparent_power",
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_battery_cycles",
|
||||
"sensor.device_current_phase_1",
|
||||
"sensor.device_current_phase_2",
|
||||
"sensor.device_current_phase_3",
|
||||
|
@ -521,6 +543,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_reactive_power",
|
||||
"sensor.device_smart_meter_identifier",
|
||||
"sensor.device_smart_meter_model",
|
||||
"sensor.device_state_of_charge",
|
||||
"sensor.device_tariff",
|
||||
"sensor.device_total_water_usage",
|
||||
"sensor.device_voltage_phase_1",
|
||||
|
@ -543,6 +566,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_apparent_power_phase_2",
|
||||
"sensor.device_apparent_power_phase_3",
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_battery_cycles",
|
||||
"sensor.device_current_phase_1",
|
||||
"sensor.device_current_phase_2",
|
||||
"sensor.device_current_phase_3",
|
||||
|
@ -568,6 +592,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_reactive_power_phase_3",
|
||||
"sensor.device_smart_meter_identifier",
|
||||
"sensor.device_smart_meter_model",
|
||||
"sensor.device_state_of_charge",
|
||||
"sensor.device_tariff",
|
||||
"sensor.device_total_water_usage",
|
||||
"sensor.device_voltage_phase_1",
|
||||
|
@ -590,6 +615,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_apparent_power_phase_3",
|
||||
"sensor.device_apparent_power",
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_battery_cycles",
|
||||
"sensor.device_current_phase_1",
|
||||
"sensor.device_current_phase_2",
|
||||
"sensor.device_current_phase_3",
|
||||
|
@ -623,6 +649,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_reactive_power",
|
||||
"sensor.device_smart_meter_identifier",
|
||||
"sensor.device_smart_meter_model",
|
||||
"sensor.device_state_of_charge",
|
||||
"sensor.device_tariff",
|
||||
"sensor.device_voltage_phase_1",
|
||||
"sensor.device_voltage_phase_2",
|
||||
|
@ -644,6 +671,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_apparent_power_phase_3",
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_battery_cycles",
|
||||
"sensor.device_current_phase_1",
|
||||
"sensor.device_current_phase_2",
|
||||
"sensor.device_current_phase_3",
|
||||
|
@ -670,6 +698,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_reactive_power_phase_3",
|
||||
"sensor.device_smart_meter_identifier",
|
||||
"sensor.device_smart_meter_model",
|
||||
"sensor.device_state_of_charge",
|
||||
"sensor.device_tariff",
|
||||
"sensor.device_total_water_usage",
|
||||
"sensor.device_voltage_phase_1",
|
||||
|
@ -688,6 +717,7 @@ async def test_external_sensors_unreachable(
|
|||
"SDM630",
|
||||
[
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_battery_cycles",
|
||||
"sensor.device_current_phase_1",
|
||||
"sensor.device_current_phase_2",
|
||||
"sensor.device_current_phase_3",
|
||||
|
@ -706,6 +736,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_power_failures_detected",
|
||||
"sensor.device_smart_meter_identifier",
|
||||
"sensor.device_smart_meter_model",
|
||||
"sensor.device_state_of_charge",
|
||||
"sensor.device_tariff",
|
||||
"sensor.device_total_water_usage",
|
||||
"sensor.device_voltage_phase_1",
|
||||
|
@ -729,6 +760,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_apparent_power_phase_3",
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_battery_cycles",
|
||||
"sensor.device_current_phase_1",
|
||||
"sensor.device_current_phase_2",
|
||||
"sensor.device_current_phase_3",
|
||||
|
@ -755,6 +787,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_reactive_power_phase_3",
|
||||
"sensor.device_smart_meter_identifier",
|
||||
"sensor.device_smart_meter_model",
|
||||
"sensor.device_state_of_charge",
|
||||
"sensor.device_tariff",
|
||||
"sensor.device_total_water_usage",
|
||||
"sensor.device_voltage_phase_1",
|
||||
|
@ -773,6 +806,7 @@ async def test_external_sensors_unreachable(
|
|||
"HWE-KWH3",
|
||||
[
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_battery_cycles",
|
||||
"sensor.device_current_phase_1",
|
||||
"sensor.device_current_phase_2",
|
||||
"sensor.device_current_phase_3",
|
||||
|
@ -791,6 +825,7 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_power_failures_detected",
|
||||
"sensor.device_smart_meter_identifier",
|
||||
"sensor.device_smart_meter_model",
|
||||
"sensor.device_state_of_charge",
|
||||
"sensor.device_tariff",
|
||||
"sensor.device_total_water_usage",
|
||||
"sensor.device_voltage_phase_1",
|
||||
|
@ -806,6 +841,54 @@ async def test_external_sensors_unreachable(
|
|||
"sensor.device_water_usage",
|
||||
],
|
||||
),
|
||||
(
|
||||
"HWE-BAT",
|
||||
[
|
||||
"sensor.device_apparent_power_phase_1",
|
||||
"sensor.device_apparent_power_phase_2",
|
||||
"sensor.device_apparent_power_phase_3",
|
||||
"sensor.device_apparent_power",
|
||||
"sensor.device_average_demand",
|
||||
"sensor.device_current_phase_1",
|
||||
"sensor.device_current_phase_2",
|
||||
"sensor.device_current_phase_3",
|
||||
"sensor.device_dsmr_version",
|
||||
"sensor.device_energy_export_tariff_1",
|
||||
"sensor.device_energy_export_tariff_2",
|
||||
"sensor.device_energy_export_tariff_4",
|
||||
"sensor.device_energy_import_tariff_1",
|
||||
"sensor.device_energy_import_tariff_2",
|
||||
"sensor.device_energy_import_tariff_3",
|
||||
"sensor.device_energy_import_tariff_4",
|
||||
"sensor.device_long_power_failures_detected",
|
||||
"sensor.device_peak_demand_current_month",
|
||||
"sensor.device_power_factor_phase_1",
|
||||
"sensor.device_power_factor_phase_2",
|
||||
"sensor.device_power_factor_phase_3",
|
||||
"sensor.device_power_factor",
|
||||
"sensor.device_power_failures_detected",
|
||||
"sensor.device_power_phase_1",
|
||||
"sensor.device_power_phase_3",
|
||||
"sensor.device_reactive_power_phase_1",
|
||||
"sensor.device_reactive_power_phase_2",
|
||||
"sensor.device_reactive_power_phase_3",
|
||||
"sensor.device_reactive_power",
|
||||
"sensor.device_smart_meter_identifier",
|
||||
"sensor.device_smart_meter_model",
|
||||
"sensor.device_tariff",
|
||||
"sensor.device_total_water_usage",
|
||||
"sensor.device_voltage_phase_1",
|
||||
"sensor.device_voltage_phase_2",
|
||||
"sensor.device_voltage_phase_3",
|
||||
"sensor.device_voltage_sags_detected_phase_1",
|
||||
"sensor.device_voltage_sags_detected_phase_2",
|
||||
"sensor.device_voltage_sags_detected_phase_3",
|
||||
"sensor.device_voltage_swells_detected_phase_1",
|
||||
"sensor.device_voltage_swells_detected_phase_2",
|
||||
"sensor.device_voltage_swells_detected_phase_3",
|
||||
"sensor.device_water_usage",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_entities_not_created_for_device(
|
||||
|
|
Loading…
Reference in New Issue