From 6c69cff738b99a689a6c0f8dc22528ef8c832a47 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Sat, 1 Aug 2026 07:13:35 +0200 Subject: [PATCH 01/10] Improve entity class in Midea (#177900) --- homeassistant/components/midea/climate.py | 9 --------- homeassistant/components/midea/entity.py | 9 ++++++--- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/midea/climate.py b/homeassistant/components/midea/climate.py index 0da5a824fe336..bc0c00cfb975a 100644 --- a/homeassistant/components/midea/climate.py +++ b/homeassistant/components/midea/climate.py @@ -166,15 +166,6 @@ class MideaClimate(MideaEntity, ClimateEntity): _attr_temperature_unit = UnitOfTemperature.CELSIUS _zone: int | None = None - def __init__( - self, - device: MideaClimateDevice, - description: MideaClimateEntityDescription, - ) -> None: - """Midea Climate entity init.""" - super().__init__(device, description.key) - self.entity_description = description - def _float_attribute(self, attr: str) -> float | None: """Return a device attribute as float, if convertible.""" value = self._device.get_attribute(attr) diff --git a/homeassistant/components/midea/entity.py b/homeassistant/components/midea/entity.py index e5182872a2495..db59109622ca4 100644 --- a/homeassistant/components/midea/entity.py +++ b/homeassistant/components/midea/entity.py @@ -6,7 +6,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.device_registry import DeviceInfo -from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity import Entity, EntityDescription from .const import DOMAIN, LOGGER from .device_catalog import MIDEA_DEVICE_NAMES @@ -20,11 +20,14 @@ class MideaEntity(Entity): _attr_has_entity_name = True _attr_should_poll = False - def __init__(self, device: MideaDevice, entity_key: str) -> None: + def __init__( + self, device: MideaDevice, entity_description: EntityDescription + ) -> None: """Initialize Midea base entity.""" self._device = device - self._unique_id = f"{self._device.device_id}_{entity_key}" + self._unique_id = f"{self._device.device_id}_{entity_description.key}" self._device_name = self._device.name + self.entity_description = entity_description @override async def async_added_to_hass(self) -> None: From a771220ea812325c81c5443e6865219330845aa6 Mon Sep 17 00:00:00 2001 From: Erwin Douna Date: Sat, 1 Aug 2026 07:14:08 +0200 Subject: [PATCH 02/10] Remove error placeholders in Firefly III (#177882) --- homeassistant/components/firefly_iii/coordinator.py | 6 ------ homeassistant/components/firefly_iii/strings.json | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/firefly_iii/coordinator.py b/homeassistant/components/firefly_iii/coordinator.py index a2e2d2a1bf6f9..22c8676a9b62f 100644 --- a/homeassistant/components/firefly_iii/coordinator.py +++ b/homeassistant/components/firefly_iii/coordinator.py @@ -76,19 +76,16 @@ async def _async_setup(self) -> None: raise ConfigEntryAuthFailed( translation_domain=DOMAIN, translation_key="invalid_auth", - translation_placeholders={"error": repr(err)}, ) from err except FireflyConnectionError as err: raise UpdateFailed( translation_domain=DOMAIN, translation_key="cannot_connect", - translation_placeholders={"error": repr(err)}, ) from err except FireflyTimeoutError as err: raise UpdateFailed( translation_domain=DOMAIN, translation_key="timeout_connect", - translation_placeholders={"error": repr(err)}, ) from err @override @@ -127,19 +124,16 @@ async def _async_update_data(self) -> FireflyCoordinatorData: raise ConfigEntryAuthFailed( translation_domain=DOMAIN, translation_key="invalid_auth", - translation_placeholders={"error": repr(err)}, ) from err except FireflyConnectionError as err: raise UpdateFailed( translation_domain=DOMAIN, translation_key="cannot_connect", - translation_placeholders={"error": repr(err)}, ) from err except FireflyTimeoutError as err: raise UpdateFailed( translation_domain=DOMAIN, translation_key="timeout_connect", - translation_placeholders={"error": repr(err)}, ) from err return FireflyCoordinatorData( diff --git a/homeassistant/components/firefly_iii/strings.json b/homeassistant/components/firefly_iii/strings.json index d367a68699363..9c595e36a89f5 100644 --- a/homeassistant/components/firefly_iii/strings.json +++ b/homeassistant/components/firefly_iii/strings.json @@ -89,13 +89,13 @@ }, "exceptions": { "cannot_connect": { - "message": "An error occurred while trying to connect to the Firefly III instance: {error}" + "message": "An error occurred while trying to connect to the Firefly III instance" }, "invalid_auth": { - "message": "An error occurred while trying to authenticate: {error}" + "message": "An error occurred while trying to authenticate" }, "timeout_connect": { - "message": "A timeout occurred while trying to connect to the Firefly III instance: {error}" + "message": "A timeout occurred while trying to connect to the Firefly III instance" } } } From b7a6b502f930c0cdd6bd9c7a21f34825383012e9 Mon Sep 17 00:00:00 2001 From: Jeroen de Jong Date: Sat, 1 Aug 2026 09:37:58 +0200 Subject: [PATCH 03/10] Add timer number to ToGrill (#177359) Co-authored-by: Claude Opus 4.8 (1M context) --- homeassistant/components/togrill/number.py | 37 ++ homeassistant/components/togrill/strings.json | 3 + .../togrill/snapshots/test_number.ambr | 620 ++++++++++++++++++ tests/components/togrill/test_number.py | 53 ++ 4 files changed, 713 insertions(+) diff --git a/homeassistant/components/togrill/number.py b/homeassistant/components/togrill/number.py index 0162c93d164b9..0bfdc47aa1ff3 100644 --- a/homeassistant/components/togrill/number.py +++ b/homeassistant/components/togrill/number.py @@ -2,12 +2,14 @@ from collections.abc import Callable, Generator, Mapping from dataclasses import dataclass +from datetime import timedelta from typing import Any, override from togrill_bluetooth.packets import ( AlarmType, PacketA0Notify, PacketA6Write, + PacketA7Write, PacketA8Notify, PacketA300Write, PacketA301Write, @@ -121,6 +123,37 @@ def _set_maximum( ) +def _get_timer_description(probe_number: int) -> ToGrillNumberEntityDescription: + def _get_timer(coordinator: ToGrillCoordinator) -> float | None: + if not (packet := coordinator.get_packet(PacketA8Notify, probe_number)): + return None + return packet.time.total_seconds() / 60 + + def _set_timer(coordinator: ToGrillCoordinator, value: float) -> PacketWrite: + return PacketA7Write( + probe=probe_number, + time=timedelta(minutes=value), + unknown=1 if value else 0, + ) + + return ToGrillNumberEntityDescription( + key=f"timer_{probe_number}", + translation_key="timer", + translation_placeholders={"probe_number": f"{probe_number}"}, + device_class=NumberDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.MINUTES, + native_min_value=0, + native_max_value=720, + native_step=1, + mode=NumberMode.BOX, + icon="mdi:timer-outline", + set_packet=_set_timer, + get_value=_get_timer, + entity_supported=lambda x: probe_number <= x[CONF_PROBE_COUNT], + probe_number=probe_number, + ) + + def _get_ambient_temperatures( coordinator: ToGrillCoordinator, alarm_type: AlarmType ) -> tuple[float | None, float | None]: @@ -137,6 +170,10 @@ def _get_ambient_temperatures( for probe_number in range(1, MAX_PROBE_COUNT + 1) for description in _get_temperature_descriptions(probe_number) ], + *[ + _get_timer_description(probe_number) + for probe_number in range(1, MAX_PROBE_COUNT + 1) + ], ToGrillNumberEntityDescription( key="ambient_temperature_minimum", translation_key="ambient_temperature_minimum", diff --git a/homeassistant/components/togrill/strings.json b/homeassistant/components/togrill/strings.json index 41b5c036b0ec0..4f3b404c4e0d7 100644 --- a/homeassistant/components/togrill/strings.json +++ b/homeassistant/components/togrill/strings.json @@ -69,6 +69,9 @@ }, "temperature_target": { "name": "Target temperature" + }, + "timer": { + "name": "Timer" } }, "select": { diff --git a/tests/components/togrill/snapshots/test_number.ambr b/tests/components/togrill/snapshots/test_number.ambr index 0972818cbc350..4004cb711c864 100644 --- a/tests/components/togrill/snapshots/test_number.ambr +++ b/tests/components/togrill/snapshots/test_number.ambr @@ -246,6 +246,68 @@ 'state': 'unknown', }) # --- +# name: test_setup[no_data][number.probe_1_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_1_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[no_data][number.probe_1_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 1 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_1_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup[no_data][number.probe_2_maximum_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -432,6 +494,68 @@ 'state': 'unknown', }) # --- +# name: test_setup[no_data][number.probe_2_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_2_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[no_data][number.probe_2_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 2 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_2_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup[one_probe_with_target_alarm][number.pro_05_alarm_interval-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -679,6 +803,68 @@ 'state': '50.0', }) # --- +# name: test_setup[one_probe_with_target_alarm][number.probe_1_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_1_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[one_probe_with_target_alarm][number.probe_1_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 1 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_1_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- # name: test_setup[one_probe_with_target_alarm][number.probe_2_maximum_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -865,6 +1051,68 @@ 'state': 'unknown', }) # --- +# name: test_setup[one_probe_with_target_alarm][number.probe_2_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_2_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[one_probe_with_target_alarm][number.probe_2_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 2 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_2_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- # name: test_setup_with_ambient[ambient_with_range][number.pro_05_alarm_interval-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -1236,6 +1484,68 @@ 'state': 'unknown', }) # --- +# name: test_setup_with_ambient[ambient_with_range][number.probe_1_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_1_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_with_range][number.probe_1_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 1 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_1_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup_with_ambient[ambient_with_range][number.probe_2_maximum_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -1422,6 +1732,68 @@ 'state': 'unknown', }) # --- +# name: test_setup_with_ambient[ambient_with_range][number.probe_2_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_2_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_with_range][number.probe_2_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 2 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_2_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup_with_ambient[ambient_wrong_alarm_type][number.pro_05_alarm_interval-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -1793,6 +2165,68 @@ 'state': 'unknown', }) # --- +# name: test_setup_with_ambient[ambient_wrong_alarm_type][number.probe_1_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_1_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_wrong_alarm_type][number.probe_1_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 1 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_1_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup_with_ambient[ambient_wrong_alarm_type][number.probe_2_maximum_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -1979,6 +2413,68 @@ 'state': 'unknown', }) # --- +# name: test_setup_with_ambient[ambient_wrong_alarm_type][number.probe_2_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_2_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_wrong_alarm_type][number.probe_2_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 2 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_2_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup_with_ambient[no_data][number.pro_05_alarm_interval-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -2350,6 +2846,68 @@ 'state': 'unknown', }) # --- +# name: test_setup_with_ambient[no_data][number.probe_1_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_1_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[no_data][number.probe_1_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 1 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_1_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup_with_ambient[no_data][number.probe_2_maximum_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -2536,3 +3094,65 @@ 'state': 'unknown', }) # --- +# name: test_setup_with_ambient[no_data][number.probe_2_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 720, + : 0, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.probe_2_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Timer', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:timer-outline', + 'original_name': 'Timer', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'timer', + 'unique_id': '00000000-0000-0000-0000-000000000001_timer_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[no_data][number.probe_2_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Probe 2 Timer', + : 'mdi:timer-outline', + : 720, + : 0, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.probe_2_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/togrill/test_number.py b/tests/components/togrill/test_number.py index f32720219bcfc..1c6282a080928 100644 --- a/tests/components/togrill/test_number.py +++ b/tests/components/togrill/test_number.py @@ -1,5 +1,6 @@ """Test numbers for ToGrill integration.""" +from datetime import timedelta from unittest.mock import Mock from bleak.exc import BleakError @@ -9,6 +10,7 @@ from togrill_bluetooth.packets import ( PacketA0Notify, PacketA6Write, + PacketA7Write, PacketA8Notify, PacketA300Write, PacketA301Write, @@ -324,6 +326,32 @@ async def test_set_ambient_number( PacketA6Write(temperature_unit=None, alarm_interval=15), id="alarm_interval", ), + pytest.param( + [ + PacketA8Notify( + probe=1, + alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, + temperature_1=50.0, + ), + ], + "number.probe_1_timer", + 10.0, + PacketA7Write(probe=1, time=timedelta(minutes=10), unknown=1), + id="timer", + ), + pytest.param( + [ + PacketA8Notify( + probe=1, + alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, + temperature_1=50.0, + ), + ], + "number.probe_1_timer", + 0.0, + PacketA7Write(probe=1, time=timedelta(0), unknown=0), + id="timer_stop", + ), ], ) async def test_set_number( @@ -442,3 +470,28 @@ async def test_set_number_disconnected( }, blocking=True, ) + + +async def test_timer_readback( + hass: HomeAssistant, + mock_entry: MockConfigEntry, + mock_client: Mock, +) -> None: + """Test that a running timer is reported back in minutes.""" + + inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) + + await setup_entry(hass, mock_entry, [Platform.NUMBER]) + + mock_client.mocked_notify( + PacketA8Notify( + probe=1, + alarm_type=None, + time=timedelta(minutes=30), + ) + ) + await hass.async_block_till_done() + + state = hass.states.get("number.probe_1_timer") + assert state is not None + assert float(state.state) == 30.0 From 1e5df2cfcc9bc5119ea5778c44ea5fb549c30b22 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:55:48 +0200 Subject: [PATCH 04/10] Bump python-homewizard-energy to 10.2.0 (#172311) --- .../components/homewizard/manifest.json | 2 +- requirements_all.txt | 2 +- .../homewizard/snapshots/test_button.ambr | 2 +- .../snapshots/test_diagnostics.ambr | 16 +- .../homewizard/snapshots/test_number.ambr | 4 +- .../homewizard/snapshots/test_select.ambr | 2 +- .../homewizard/snapshots/test_sensor.ambr | 548 +++++++++--------- .../homewizard/snapshots/test_switch.ambr | 22 +- 8 files changed, 299 insertions(+), 299 deletions(-) diff --git a/homeassistant/components/homewizard/manifest.json b/homeassistant/components/homewizard/manifest.json index f9a56ea3db9d7..962bc818bf2d3 100644 --- a/homeassistant/components/homewizard/manifest.json +++ b/homeassistant/components/homewizard/manifest.json @@ -13,6 +13,6 @@ "iot_class": "local_polling", "loggers": ["homewizard_energy"], "quality_scale": "platinum", - "requirements": ["python-homewizard-energy==10.1.0"], + "requirements": ["python-homewizard-energy==10.2.0"], "zeroconf": ["_hwenergy._tcp.local.", "_homewizard._tcp.local."] } diff --git a/requirements_all.txt b/requirements_all.txt index a3e2b03983f4a..ce69ca683661f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2689,7 +2689,7 @@ python-google-weather-api==0.0.6 python-homeassistant-analytics==0.9.0 # homeassistant.components.homewizard -python-homewizard-energy==10.1.0 +python-homewizard-energy==10.2.0 # homeassistant.components.hp_ilo python-hpilo==4.4.3 diff --git a/tests/components/homewizard/snapshots/test_button.ambr b/tests/components/homewizard/snapshots/test_button.ambr index 6d717631cbe24..8256f2fa6d1f9 100644 --- a/tests/components/homewizard/snapshots/test_button.ambr +++ b/tests/components/homewizard/snapshots/test_button.ambr @@ -75,7 +75,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, diff --git a/tests/components/homewizard/snapshots/test_diagnostics.ambr b/tests/components/homewizard/snapshots/test_diagnostics.ambr index c465608be87dc..eed458518a057 100644 --- a/tests/components/homewizard/snapshots/test_diagnostics.ambr +++ b/tests/components/homewizard/snapshots/test_diagnostics.ambr @@ -99,7 +99,7 @@ 'api_version': '1.0.0', 'firmware_version': '3.06', 'id': '**REDACTED**', - 'model_name': 'Wi-Fi kWh Meter 1-phase', + 'model_name': 'kWh Meter 1-phase', 'product_name': 'kWh meter', 'product_type': 'HWE-KWH1', 'serial': '**REDACTED**', @@ -191,7 +191,7 @@ 'api_version': '1.0.0', 'firmware_version': '3.06', 'id': '**REDACTED**', - 'model_name': 'Wi-Fi kWh Meter 3-phase', + 'model_name': 'kWh Meter 3-phase', 'product_name': 'KWh meter 3-phase', 'product_type': 'HWE-KWH3', 'serial': '**REDACTED**', @@ -294,7 +294,7 @@ 'api_version': '1.0.0', 'firmware_version': '4.19', 'id': '**REDACTED**', - 'model_name': 'Wi-Fi P1 Meter', + 'model_name': 'P1 Meter', 'product_name': 'P1 meter', 'product_type': 'HWE-P1', 'serial': '**REDACTED**', @@ -422,7 +422,7 @@ 'api_version': '1.0.0', 'firmware_version': '3.03', 'id': '**REDACTED**', - 'model_name': 'Wi-Fi Energy Socket', + 'model_name': 'Energy Socket', 'product_name': 'Energy Socket', 'product_type': 'HWE-SKT', 'serial': '**REDACTED**', @@ -518,7 +518,7 @@ 'api_version': '1.0.0', 'firmware_version': '4.07', 'id': '**REDACTED**', - 'model_name': 'Wi-Fi Energy Socket', + 'model_name': 'Energy Socket', 'product_name': 'Energy Socket', 'product_type': 'HWE-SKT', 'serial': '**REDACTED**', @@ -614,7 +614,7 @@ 'api_version': '1.0.0', 'firmware_version': '2.03', 'id': '**REDACTED**', - 'model_name': 'Wi-Fi Watermeter', + 'model_name': 'Watermeter', 'product_name': 'Watermeter', 'product_type': 'HWE-WTR', 'serial': '**REDACTED**', @@ -706,7 +706,7 @@ 'api_version': '1.0.0', 'firmware_version': '3.06', 'id': '**REDACTED**', - 'model_name': 'Wi-Fi kWh Meter 1-phase', + 'model_name': 'kWh Meter 1-phase', 'product_name': 'kWh meter', 'product_type': 'SDM230-wifi', 'serial': '**REDACTED**', @@ -798,7 +798,7 @@ 'api_version': '1.0.0', 'firmware_version': '3.06', 'id': '**REDACTED**', - 'model_name': 'Wi-Fi kWh Meter 3-phase', + 'model_name': 'kWh Meter 3-phase', 'product_name': 'KWh meter 3-phase', 'product_type': 'SDM630-wifi', 'serial': '**REDACTED**', diff --git a/tests/components/homewizard/snapshots/test_number.ambr b/tests/components/homewizard/snapshots/test_number.ambr index e06105aa381df..67b9797f65e36 100644 --- a/tests/components/homewizard/snapshots/test_number.ambr +++ b/tests/components/homewizard/snapshots/test_number.ambr @@ -84,7 +84,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -178,7 +178,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, diff --git a/tests/components/homewizard/snapshots/test_select.ambr b/tests/components/homewizard/snapshots/test_select.ambr index 7c1138e33c036..f7972b40b6675 100644 --- a/tests/components/homewizard/snapshots/test_select.ambr +++ b/tests/components/homewizard/snapshots/test_select.ambr @@ -85,7 +85,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, diff --git a/tests/components/homewizard/snapshots/test_sensor.ambr b/tests/components/homewizard/snapshots/test_sensor.ambr index a54dc0cc69b36..330cb6bc7c342 100644 --- a/tests/components/homewizard/snapshots/test_sensor.ambr +++ b/tests/components/homewizard/snapshots/test_sensor.ambr @@ -1104,7 +1104,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1196,7 +1196,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1288,7 +1288,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1380,7 +1380,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1472,7 +1472,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1564,7 +1564,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1656,7 +1656,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1745,7 +1745,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1837,7 +1837,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -1929,7 +1929,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -2021,7 +2021,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -2105,7 +2105,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -2193,7 +2193,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -2285,7 +2285,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -2377,7 +2377,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -2469,7 +2469,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -2561,7 +2561,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -2653,7 +2653,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -2745,7 +2745,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -2837,7 +2837,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -2929,7 +2929,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3021,7 +3021,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3113,7 +3113,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3205,7 +3205,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3297,7 +3297,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3386,7 +3386,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3475,7 +3475,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3564,7 +3564,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3656,7 +3656,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3748,7 +3748,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3840,7 +3840,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -3932,7 +3932,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4024,7 +4024,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4116,7 +4116,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4208,7 +4208,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4300,7 +4300,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4392,7 +4392,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4484,7 +4484,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4576,7 +4576,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4660,7 +4660,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -4748,7 +4748,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -4837,7 +4837,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -4929,7 +4929,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5021,7 +5021,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5113,7 +5113,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5205,7 +5205,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5297,7 +5297,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5381,7 +5381,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5473,7 +5473,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5565,7 +5565,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5657,7 +5657,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5749,7 +5749,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5841,7 +5841,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -5933,7 +5933,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6025,7 +6025,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6117,7 +6117,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6209,7 +6209,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6301,7 +6301,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6393,7 +6393,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6477,7 +6477,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6566,7 +6566,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6658,7 +6658,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6742,7 +6742,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6834,7 +6834,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -6926,7 +6926,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7018,7 +7018,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7102,7 +7102,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7186,7 +7186,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7284,7 +7284,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7376,7 +7376,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7468,7 +7468,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7560,7 +7560,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7652,7 +7652,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7736,7 +7736,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7820,7 +7820,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7904,7 +7904,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -7988,7 +7988,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -8072,7 +8072,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -8156,7 +8156,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -8248,7 +8248,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -8332,7 +8332,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -8416,7 +8416,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Gas meter', 'name_by_user': None, @@ -8504,7 +8504,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Heat meter', 'name_by_user': None, @@ -8592,7 +8592,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Inlet heat meter', 'name_by_user': None, @@ -8676,7 +8676,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Warm water meter', 'name_by_user': None, @@ -8764,7 +8764,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Water meter', 'name_by_user': None, @@ -8856,7 +8856,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -8945,7 +8945,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9037,7 +9037,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9129,7 +9129,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9221,7 +9221,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9305,7 +9305,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9397,7 +9397,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9489,7 +9489,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9581,7 +9581,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9673,7 +9673,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9765,7 +9765,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9857,7 +9857,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -9949,7 +9949,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10041,7 +10041,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10133,7 +10133,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10225,7 +10225,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10317,7 +10317,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10401,7 +10401,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10490,7 +10490,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10582,7 +10582,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10666,7 +10666,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10758,7 +10758,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10850,7 +10850,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -10942,7 +10942,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11026,7 +11026,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11110,7 +11110,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11208,7 +11208,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11300,7 +11300,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11392,7 +11392,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11484,7 +11484,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11576,7 +11576,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11660,7 +11660,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11744,7 +11744,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11828,7 +11828,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11912,7 +11912,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -11996,7 +11996,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -12080,7 +12080,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -12172,7 +12172,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -12256,7 +12256,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -12340,7 +12340,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Gas meter', 'name_by_user': None, @@ -12428,7 +12428,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Heat meter', 'name_by_user': None, @@ -12516,7 +12516,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Inlet heat meter', 'name_by_user': None, @@ -12600,7 +12600,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Warm water meter', 'name_by_user': None, @@ -12688,7 +12688,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Water meter', 'name_by_user': None, @@ -12780,7 +12780,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -12869,7 +12869,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -12961,7 +12961,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13053,7 +13053,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13145,7 +13145,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13237,7 +13237,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13329,7 +13329,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13413,7 +13413,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13505,7 +13505,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13597,7 +13597,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13689,7 +13689,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13781,7 +13781,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13873,7 +13873,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -13965,7 +13965,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14057,7 +14057,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14149,7 +14149,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14241,7 +14241,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14333,7 +14333,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14425,7 +14425,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14509,7 +14509,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14598,7 +14598,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14690,7 +14690,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14774,7 +14774,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14866,7 +14866,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -14958,7 +14958,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15050,7 +15050,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15134,7 +15134,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15218,7 +15218,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15316,7 +15316,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15408,7 +15408,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15500,7 +15500,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15592,7 +15592,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15684,7 +15684,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15768,7 +15768,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15852,7 +15852,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -15936,7 +15936,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -16020,7 +16020,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -16104,7 +16104,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -16188,7 +16188,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -16280,7 +16280,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -16364,7 +16364,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -16448,7 +16448,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Gas meter', 'name_by_user': None, @@ -16536,7 +16536,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Heat meter', 'name_by_user': None, @@ -16624,7 +16624,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Inlet heat meter', 'name_by_user': None, @@ -16708,7 +16708,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Warm water meter', 'name_by_user': None, @@ -16796,7 +16796,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Water meter', 'name_by_user': None, @@ -16888,7 +16888,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -16977,7 +16977,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17069,7 +17069,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17161,7 +17161,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17253,7 +17253,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17345,7 +17345,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17437,7 +17437,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17529,7 +17529,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17621,7 +17621,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17713,7 +17713,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17805,7 +17805,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17897,7 +17897,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -17989,7 +17989,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18081,7 +18081,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18173,7 +18173,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18265,7 +18265,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18349,7 +18349,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18441,7 +18441,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18525,7 +18525,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18617,7 +18617,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18709,7 +18709,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18801,7 +18801,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18893,7 +18893,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -18985,7 +18985,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19077,7 +19077,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19169,7 +19169,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19253,7 +19253,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19337,7 +19337,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19421,7 +19421,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19505,7 +19505,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19589,7 +19589,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19673,7 +19673,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19765,7 +19765,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19849,7 +19849,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi P1 Meter', + 'model': 'P1 Meter', 'model_id': 'HWE-P1', 'name': 'Device', 'name_by_user': None, @@ -19937,7 +19937,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20029,7 +20029,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20121,7 +20121,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20213,7 +20213,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20305,7 +20305,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20397,7 +20397,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20481,7 +20481,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20569,7 +20569,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20661,7 +20661,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20753,7 +20753,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20845,7 +20845,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -20937,7 +20937,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21029,7 +21029,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21121,7 +21121,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21210,7 +21210,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21302,7 +21302,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21394,7 +21394,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21486,7 +21486,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21578,7 +21578,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21662,7 +21662,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -21750,7 +21750,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Watermeter', + 'model': 'Watermeter', 'model_id': 'HWE-WTR', 'name': 'Device', 'name_by_user': None, @@ -21842,7 +21842,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Watermeter', + 'model': 'Watermeter', 'model_id': 'HWE-WTR', 'name': 'Device', 'name_by_user': None, @@ -21934,7 +21934,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Watermeter', + 'model': 'Watermeter', 'model_id': 'HWE-WTR', 'name': 'Device', 'name_by_user': None, @@ -22018,7 +22018,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Watermeter', + 'model': 'Watermeter', 'model_id': 'HWE-WTR', 'name': 'Device', 'name_by_user': None, @@ -22106,7 +22106,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22198,7 +22198,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22290,7 +22290,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22382,7 +22382,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22474,7 +22474,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22566,7 +22566,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22658,7 +22658,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22747,7 +22747,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22839,7 +22839,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -22931,7 +22931,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -23023,7 +23023,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -23107,7 +23107,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -23195,7 +23195,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -23287,7 +23287,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -23379,7 +23379,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -23471,7 +23471,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -23563,7 +23563,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -23655,7 +23655,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -23747,7 +23747,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -23839,7 +23839,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -23931,7 +23931,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24023,7 +24023,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24115,7 +24115,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24207,7 +24207,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24299,7 +24299,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24388,7 +24388,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24477,7 +24477,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24566,7 +24566,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24658,7 +24658,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24750,7 +24750,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24842,7 +24842,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -24934,7 +24934,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -25026,7 +25026,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -25118,7 +25118,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -25210,7 +25210,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -25302,7 +25302,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -25394,7 +25394,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -25486,7 +25486,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -25578,7 +25578,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, @@ -25662,7 +25662,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, diff --git a/tests/components/homewizard/snapshots/test_switch.ambr b/tests/components/homewizard/snapshots/test_switch.ambr index eb2a6c0e54834..86491faa87a89 100644 --- a/tests/components/homewizard/snapshots/test_switch.ambr +++ b/tests/components/homewizard/snapshots/test_switch.ambr @@ -74,7 +74,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'HWE-KWH1', 'name': 'Device', 'name_by_user': None, @@ -158,7 +158,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'HWE-KWH3', 'name': 'Device', 'name_by_user': None, @@ -243,7 +243,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -327,7 +327,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -411,7 +411,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -496,7 +496,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -580,7 +580,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -664,7 +664,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Energy Socket', + 'model': 'Energy Socket', 'model_id': 'HWE-SKT', 'name': 'Device', 'name_by_user': None, @@ -748,7 +748,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi Watermeter', + 'model': 'Watermeter', 'model_id': 'HWE-WTR', 'name': 'Device', 'name_by_user': None, @@ -832,7 +832,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 1-phase', + 'model': 'kWh Meter 1-phase', 'model_id': 'SDM230-wifi', 'name': 'Device', 'name_by_user': None, @@ -916,7 +916,7 @@ 'labels': set({ }), 'manufacturer': 'HomeWizard', - 'model': 'Wi-Fi kWh Meter 3-phase', + 'model': 'kWh Meter 3-phase', 'model_id': 'SDM630-wifi', 'name': 'Device', 'name_by_user': None, From 59514201171177dfc180991d14fefbf75413a59d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 1 Aug 2026 10:55:12 +0200 Subject: [PATCH 05/10] Fix via_device race in advantage_air (#177705) --- .../components/advantage_air/__init__.py | 14 +++++++- .../components/advantage_air/entity.py | 13 ++++++-- .../components/advantage_air/light.py | 7 +++- tests/components/advantage_air/test_init.py | 33 +++++++++++++++++++ 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/advantage_air/__init__.py b/homeassistant/components/advantage_air/__init__.py index 4114f612fe959..7d2a5515f794e 100644 --- a/homeassistant/components/advantage_air/__init__.py +++ b/homeassistant/components/advantage_air/__init__.py @@ -4,7 +4,7 @@ from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.typing import ConfigType @@ -50,6 +50,18 @@ async def async_setup_entry( entry.runtime_data = coordinator + # Register the system device so child devices can resolve it as their + # via_device parent regardless of platform setup order. + system = coordinator.data["system"] + dr.async_get(hass).async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, system["rid"])}, + manufacturer="Advantage Air", + model=system["sysType"], + name=system["name"], + sw_version=system["myAppRev"], + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True diff --git a/homeassistant/components/advantage_air/entity.py b/homeassistant/components/advantage_air/entity.py index c0f4cd5512c24..975cc901871cf 100644 --- a/homeassistant/components/advantage_air/entity.py +++ b/homeassistant/components/advantage_air/entity.py @@ -5,6 +5,7 @@ from advantage_air import ApiError from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -49,7 +50,11 @@ def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None: self._attr_unique_id += f"-{ac_key}" self._attr_device_info = DeviceInfo( - via_device=(DOMAIN, self.coordinator.data["system"]["rid"]), + via_device_id=dr.async_get_device_id_by_identifier( + self.coordinator.hass, + (DOMAIN, self.coordinator.data["system"]["rid"]), + config_entry_id=self.coordinator.config_entry.entry_id, + ), identifiers={(DOMAIN, self._attr_unique_id)}, manufacturer="Advantage Air", model=self.coordinator.data["system"]["sysType"], @@ -105,7 +110,11 @@ def __init__( self._attr_unique_id += f"-{self._id}" self._attr_device_info = DeviceInfo( - via_device=(DOMAIN, self.coordinator.data["system"]["rid"]), + via_device_id=dr.async_get_device_id_by_identifier( + self.coordinator.hass, + (DOMAIN, self.coordinator.data["system"]["rid"]), + config_entry_id=self.coordinator.config_entry.entry_id, + ), identifiers={(DOMAIN, self._attr_unique_id)}, manufacturer="Advantage Air", model="MyPlace", diff --git a/homeassistant/components/advantage_air/light.py b/homeassistant/components/advantage_air/light.py index d3c94da3fd1a7..a76f30a33e9c5 100644 --- a/homeassistant/components/advantage_air/light.py +++ b/homeassistant/components/advantage_air/light.py @@ -4,6 +4,7 @@ from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -55,7 +56,11 @@ def __init__( self._attr_unique_id += f"-{self._id}" self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, self._attr_unique_id)}, - via_device=(DOMAIN, self.coordinator.data["system"]["rid"]), + via_device_id=dr.async_get_device_id_by_identifier( + self.coordinator.hass, + (DOMAIN, self.coordinator.data["system"]["rid"]), + config_entry_id=self.coordinator.config_entry.entry_id, + ), manufacturer="Advantage Air", model=light.get("moduleType"), name=light["name"], diff --git a/tests/components/advantage_air/test_init.py b/tests/components/advantage_air/test_init.py index e700485c75a1d..ff5c8ebc506bd 100644 --- a/tests/components/advantage_air/test_init.py +++ b/tests/components/advantage_air/test_init.py @@ -3,9 +3,12 @@ from unittest.mock import AsyncMock from advantage_air import ApiError +import pytest +from homeassistant.components.advantage_air.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from . import add_mock_config, patch_get @@ -21,6 +24,36 @@ async def test_async_setup_entry(hass: HomeAssistant, mock_get: AsyncMock) -> No assert entry.state is ConfigEntryState.NOT_LOADED +@pytest.mark.usefixtures("mock_get") +@pytest.mark.parametrize( + "child_identifier", + [ + "uniqueid-ac1", # AC device + "uniqueid-100", # myLights light device + "uniqueid-203", # myThings device + ], +) +async def test_child_devices_via_device( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + child_identifier: str, +) -> None: + """Test child devices link to the system device via via_device_id.""" + + entry = await add_mock_config(hass) + + parent = device_registry.async_get_device_by_identifier( + (DOMAIN, "uniqueid"), entry.entry_id + ) + assert parent is not None + + child = device_registry.async_get_device_by_identifier( + (DOMAIN, child_identifier), entry.entry_id + ) + assert child is not None + assert child.via_device_id == parent.id + + async def test_async_setup_entry_failure(hass: HomeAssistant) -> None: """Test a unsuccessful setup entry.""" From 8c6bc8a68f26e2770dd551974e3964ecaeec2a40 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 1 Aug 2026 11:03:49 +0200 Subject: [PATCH 06/10] Fix via_device race in directv (#177712) --- homeassistant/components/directv/__init__.py | 20 +++++++++++++ homeassistant/components/directv/entity.py | 27 +++++++++++++++--- .../components/directv/media_player.py | 14 +++++++++- homeassistant/components/directv/remote.py | 14 +++++++++- tests/components/directv/test_media_player.py | 28 +++++++++++++++++-- 5 files changed, 95 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/directv/__init__.py b/homeassistant/components/directv/__init__.py index a6aa9ea9745eb..8e895602caed5 100644 --- a/homeassistant/components/directv/__init__.py +++ b/homeassistant/components/directv/__init__.py @@ -8,8 +8,11 @@ from homeassistant.const import CONF_HOST, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession +from .const import DOMAIN + PLATFORMS = [Platform.MEDIA_PLAYER, Platform.REMOTE] SCAN_INTERVAL = timedelta(seconds=30) @@ -28,6 +31,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: DirecTVConfigEntry) -> b entry.runtime_data = dtv + # Register the receiver device so client entities can link to it via_device_id. + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, dtv.device.info.receiver_id)}, + manufacturer=dtv.device.info.brand, + name=next( + ( + str.title(location.name) + for location in dtv.device.locations + if not location.client + ), + None, + ), + sw_version=dtv.device.info.version, + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True diff --git a/homeassistant/components/directv/entity.py b/homeassistant/components/directv/entity.py index 4f0126dcc2491..0031fc3e149c0 100644 --- a/homeassistant/components/directv/entity.py +++ b/homeassistant/components/directv/entity.py @@ -2,9 +2,12 @@ from directv import DIRECTV +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity import Entity +from . import DirecTVConfigEntry from .const import DOMAIN @@ -14,16 +17,32 @@ class DIRECTVEntity(Entity): _attr_has_entity_name = True _attr_name = None - def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None: + def __init__( + self, + *, + hass: HomeAssistant, + dtv: DIRECTV, + entry: DirecTVConfigEntry, + name: str, + address: str = "0", + ) -> None: """Initialize the DirecTV entity.""" self._address = address self._device_id = address if address != "0" else dtv.device.info.receiver_id self._is_client = address != "0" self.dtv = dtv + via_device_id: str | None = None + if self._is_client: + via_device_id = dr.async_get_device_id_by_identifier( + hass, + (DOMAIN, dtv.device.info.receiver_id), + config_entry_id=entry.entry_id, + ) self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, self._device_id)}, - manufacturer=self.dtv.device.info.brand, + manufacturer=dtv.device.info.brand, name=name, - sw_version=self.dtv.device.info.version, - via_device=(DOMAIN, self.dtv.device.info.receiver_id), + sw_version=dtv.device.info.version, ) + if via_device_id is not None: + self._attr_device_info["via_device_id"] = via_device_id diff --git a/homeassistant/components/directv/media_player.py b/homeassistant/components/directv/media_player.py index bf21c0955fc1c..bebaedfd41b6d 100644 --- a/homeassistant/components/directv/media_player.py +++ b/homeassistant/components/directv/media_player.py @@ -61,7 +61,9 @@ async def async_setup_entry( async_add_entities( ( DIRECTVMediaPlayer( + hass=hass, dtv=dtv, + entry=entry, name=str.title(location.name), address=location.address, ) @@ -74,10 +76,20 @@ async def async_setup_entry( class DIRECTVMediaPlayer(DIRECTVEntity, MediaPlayerEntity): """Representation of a DirecTV receiver on the network.""" - def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None: + def __init__( + self, + *, + hass: HomeAssistant, + dtv: DIRECTV, + entry: DirecTVConfigEntry, + name: str, + address: str = "0", + ) -> None: """Initialize DirecTV media player.""" super().__init__( + hass=hass, dtv=dtv, + entry=entry, name=name, address=address, ) diff --git a/homeassistant/components/directv/remote.py b/homeassistant/components/directv/remote.py index 3484949eb1397..ecd293dc84dbc 100644 --- a/homeassistant/components/directv/remote.py +++ b/homeassistant/components/directv/remote.py @@ -29,7 +29,9 @@ async def async_setup_entry( async_add_entities( ( DIRECTVRemote( + hass=hass, dtv=dtv, + entry=entry, name=str.title(location.name), address=location.address, ) @@ -42,10 +44,20 @@ async def async_setup_entry( class DIRECTVRemote(DIRECTVEntity, RemoteEntity): """Device that sends commands to a DirecTV receiver.""" - def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None: + def __init__( + self, + *, + hass: HomeAssistant, + dtv: DIRECTV, + entry: DirecTVConfigEntry, + name: str, + address: str = "0", + ) -> None: """Initialize DirecTV remote.""" super().__init__( + hass=hass, dtv=dtv, + entry=entry, name=name, address=address, ) diff --git a/tests/components/directv/test_media_player.py b/tests/components/directv/test_media_player.py index 91a63f48be444..eefbe5b391538 100644 --- a/tests/components/directv/test_media_player.py +++ b/tests/components/directv/test_media_player.py @@ -6,6 +6,7 @@ from freezegun.api import FrozenDateTimeFactory import pytest +from homeassistant.components.directv.const import DOMAIN from homeassistant.components.directv.media_player import ( ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, @@ -46,10 +47,10 @@ STATE_UNAVAILABLE, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.util import dt as dt_util -from . import setup_integration +from . import RECEIVER_ID, setup_integration from tests.test_util.aiohttp import AiohttpClientMocker @@ -162,6 +163,29 @@ async def test_unique_id( assert unavailable_client.unique_id == "9XXXXXXXXXX9" +async def test_client_device_via_device_id( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + entity_registry: er.EntityRegistry, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test a client's device links to the receiver device via via_device_id.""" + entry = await setup_integration(hass, aioclient_mock) + + receiver_device = device_registry.async_get_device_by_identifier( + (DOMAIN, RECEIVER_ID), entry.entry_id + ) + assert receiver_device is not None + + client_entity = entity_registry.async_get(CLIENT_ENTITY_ID) + assert client_entity is not None + assert client_entity.device_id is not None + + client_device = device_registry.async_get(client_entity.device_id) + assert client_device is not None + assert client_device.via_device_id == receiver_device.id + + async def test_supported_features( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: From a1642e6f5b35c425a6d29fb0ebc0df93cfaa5cc8 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 1 Aug 2026 11:13:28 +0200 Subject: [PATCH 07/10] Fix via_device race in airzone_cloud (#177707) --- .../components/airzone_cloud/__init__.py | 55 ++++++++++++++++++- .../components/airzone_cloud/entity.py | 24 ++++++-- tests/components/airzone_cloud/test_init.py | 48 +++++++++++++++- 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/airzone_cloud/__init__.py b/homeassistant/components/airzone_cloud/__init__.py index a1da7e0216aec..50f1b1a71fe2e 100644 --- a/homeassistant/components/airzone_cloud/__init__.py +++ b/homeassistant/components/airzone_cloud/__init__.py @@ -2,11 +2,20 @@ from aioairzone_cloud.cloudapi import AirzoneCloudApi from aioairzone_cloud.common import ConnectionOptions +from aioairzone_cloud.const import ( + AZD_FIRMWARE, + AZD_MODEL, + AZD_NAME, + AZD_SYSTEMS, + AZD_WEBSERVER, + AZD_WEBSERVERS, +) from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_USERNAME, Platform -from homeassistant.core import HomeAssistant -from homeassistant.helpers import aiohttp_client +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import aiohttp_client, device_registry as dr +from .const import DOMAIN, MANUFACTURER from .coordinator import AirzoneCloudConfigEntry, AirzoneUpdateCoordinator PLATFORMS: list[Platform] = [ @@ -42,11 +51,53 @@ async def async_setup_entry( entry.runtime_data = coordinator + _async_register_devices(hass, entry, coordinator) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True +@callback +def _async_register_devices( + hass: HomeAssistant, + entry: AirzoneCloudConfigEntry, + coordinator: AirzoneUpdateCoordinator, +) -> None: + """Register WebServer and System devices referenced as via_device parents. + + Child devices resolve their via_device_id at add time, so the parents must + already exist regardless of which platform creates their own entities. + """ + device_registry = dr.async_get(hass) + + for ws_id, ws_data in coordinator.data.get(AZD_WEBSERVERS, {}).items(): + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + connections={(dr.CONNECTION_NETWORK_MAC, ws_id)}, + identifiers={(DOMAIN, ws_id)}, + manufacturer=MANUFACTURER, + model="WebServer", + name=ws_data[AZD_NAME], + sw_version=ws_data[AZD_FIRMWARE], + ) + + for system_id, system_data in coordinator.data.get(AZD_SYSTEMS, {}).items(): + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, system_id)}, + manufacturer=MANUFACTURER, + model=system_data.get(AZD_MODEL), + name=system_data[AZD_NAME], + sw_version=system_data.get(AZD_FIRMWARE), + via_device_id=dr.async_get_device_id_by_identifier( + hass, + (DOMAIN, system_data[AZD_WEBSERVER]), + config_entry_id=entry.entry_id, + ), + ) + + async def async_unload_entry( hass: HomeAssistant, entry: AirzoneCloudConfigEntry ) -> bool: diff --git a/homeassistant/components/airzone_cloud/entity.py b/homeassistant/components/airzone_cloud/entity.py index d0765ea5688be..e025838ef4a8c 100644 --- a/homeassistant/components/airzone_cloud/entity.py +++ b/homeassistant/components/airzone_cloud/entity.py @@ -73,7 +73,11 @@ def __init__( manufacturer=MANUFACTURER, model=aidoo_data[AZD_MODEL], name=aidoo_data[AZD_NAME], - via_device=(DOMAIN, aidoo_data[AZD_WEBSERVER]), + via_device_id=dr.async_get_device_id_by_identifier( + coordinator.hass, + (DOMAIN, aidoo_data[AZD_WEBSERVER]), + config_entry_id=coordinator.config_entry.entry_id, + ), ) @override @@ -164,7 +168,11 @@ def __init__( manufacturer=MANUFACTURER, model="Hot Water", name=dhw_data[AZD_NAME], - via_device=(DOMAIN, dhw_data[AZD_WEBSERVER]), + via_device_id=dr.async_get_device_id_by_identifier( + coordinator.hass, + (DOMAIN, dhw_data[AZD_WEBSERVER]), + config_entry_id=coordinator.config_entry.entry_id, + ), ) @override @@ -257,7 +265,11 @@ def __init__( model=system_data.get(AZD_MODEL), manufacturer=MANUFACTURER, name=system_data[AZD_NAME], - via_device=(DOMAIN, system_data[AZD_WEBSERVER]), + via_device_id=dr.async_get_device_id_by_identifier( + coordinator.hass, + (DOMAIN, system_data[AZD_WEBSERVER]), + config_entry_id=coordinator.config_entry.entry_id, + ), sw_version=system_data.get(AZD_FIRMWARE), ) @@ -322,7 +334,11 @@ def __init__( model=zone_data.get(AZD_THERMOSTAT_MODEL), manufacturer=MANUFACTURER, name=zone_data[AZD_NAME], - via_device=(DOMAIN, self.system_id), + via_device_id=dr.async_get_device_id_by_identifier( + coordinator.hass, + (DOMAIN, self.system_id), + config_entry_id=coordinator.config_entry.entry_id, + ), sw_version=zone_data.get(AZD_THERMOSTAT_FW), ) diff --git a/tests/components/airzone_cloud/test_init.py b/tests/components/airzone_cloud/test_init.py index 6cab0be6e7c46..bee4a2bc2de00 100644 --- a/tests/components/airzone_cloud/test_init.py +++ b/tests/components/airzone_cloud/test_init.py @@ -7,8 +7,9 @@ from homeassistant.components.airzone_cloud.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr -from .util import CONFIG +from .util import CONFIG, WS_ID, WS_ID_AIDOO, async_init_integration from tests.common import MockConfigEntry @@ -54,6 +55,51 @@ async def test_unload_entry(hass: HomeAssistant) -> None: assert config_entry.state is ConfigEntryState.NOT_LOADED +async def test_device_via_device( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, +) -> None: + """Test that child devices are linked to their via_device parents.""" + await async_init_integration(hass) + + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + + ws_device = device_registry.async_get_device_by_identifier( + (DOMAIN, WS_ID), config_entry.entry_id + ) + assert ws_device is not None + assert ws_device.via_device_id is None + + ws_aidoo_device = device_registry.async_get_device_by_identifier( + (DOMAIN, WS_ID_AIDOO), config_entry.entry_id + ) + assert ws_aidoo_device is not None + + system_device = device_registry.async_get_device_by_identifier( + (DOMAIN, "system1"), config_entry.entry_id + ) + assert system_device is not None + assert system_device.via_device_id == ws_device.id + + zone_device = device_registry.async_get_device_by_identifier( + (DOMAIN, "zone1"), config_entry.entry_id + ) + assert zone_device is not None + assert zone_device.via_device_id == system_device.id + + dhw_device = device_registry.async_get_device_by_identifier( + (DOMAIN, "dhw1"), config_entry.entry_id + ) + assert dhw_device is not None + assert dhw_device.via_device_id == ws_device.id + + aidoo_device = device_registry.async_get_device_by_identifier( + (DOMAIN, "aidoo1"), config_entry.entry_id + ) + assert aidoo_device is not None + assert aidoo_device.via_device_id == ws_aidoo_device.id + + async def test_init_api_timeout(hass: HomeAssistant) -> None: """Test API timeouts when loading the Airzone Cloud integration.""" From a8e0db0b414553d04369c329261c69d72ce6e418 Mon Sep 17 00:00:00 2001 From: Lucas Vilela Date: Sat, 1 Aug 2026 06:18:30 -0300 Subject: [PATCH 08/10] Add floodlight schedule time entities to Reolink integration (#173140) Co-authored-by: starkillerOG --- homeassistant/components/reolink/__init__.py | 1 + homeassistant/components/reolink/icons.json | 8 + homeassistant/components/reolink/strings.json | 8 + homeassistant/components/reolink/time.py | 138 ++++++++++++++++++ tests/components/reolink/test_time.py | 105 +++++++++++++ 5 files changed, 260 insertions(+) create mode 100644 homeassistant/components/reolink/time.py create mode 100644 tests/components/reolink/test_time.py diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index 7a4d32bea96fd..f1894b00a09cb 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -58,6 +58,7 @@ Platform.SENSOR, Platform.SIREN, Platform.SWITCH, + Platform.TIME, Platform.UPDATE, ] FIRMWARE_UPDATE_INTERVAL = timedelta(hours=24) diff --git a/homeassistant/components/reolink/icons.json b/homeassistant/components/reolink/icons.json index 6e1943fafe5a1..efcf8aa7267d7 100644 --- a/homeassistant/components/reolink/icons.json +++ b/homeassistant/components/reolink/icons.json @@ -629,6 +629,14 @@ "siren_on_event": { "default": "mdi:alarm-light" } + }, + "time": { + "floodlight_schedule_end": { + "default": "mdi:clock-end" + }, + "floodlight_schedule_start": { + "default": "mdi:clock-start" + } } }, "services": { diff --git a/homeassistant/components/reolink/strings.json b/homeassistant/components/reolink/strings.json index f695251a1528c..c5fa83c37f39f 100644 --- a/homeassistant/components/reolink/strings.json +++ b/homeassistant/components/reolink/strings.json @@ -880,6 +880,14 @@ "siren_on_event": { "name": "Siren on event" } + }, + "time": { + "floodlight_schedule_end": { + "name": "Floodlight schedule end" + }, + "floodlight_schedule_start": { + "name": "Floodlight schedule start" + } } }, "exceptions": { diff --git a/homeassistant/components/reolink/time.py b/homeassistant/components/reolink/time.py new file mode 100644 index 0000000000000..7fa39e57f1fd4 --- /dev/null +++ b/homeassistant/components/reolink/time.py @@ -0,0 +1,138 @@ +"""Component providing support for Reolink time entities.""" + +from collections.abc import Callable +from dataclasses import dataclass +from datetime import time +from typing import Any, override + +from reolink_aio.api import Host +from reolink_aio.enums import SpotlightModeEnum + +from homeassistant.components.time import TimeEntity, TimeEntityDescription +from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription +from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error + +PARALLEL_UPDATES = 0 + + +@dataclass(frozen=True, kw_only=True) +class ReolinkTimeEntityDescription( + TimeEntityDescription, + ReolinkChannelEntityDescription, +): + """A class that describes time entities.""" + + method: Callable[[Host, int, time], Any] + value: Callable[[Host, int], time | None] + + +def _schedule_time(api: Host, ch: int, prefix: str) -> time | None: + """Return the start or end time of the floodlight schedule.""" + schedule = api.whiteled_schedule(ch) + if not schedule: + return None + return time(hour=schedule[f"{prefix}Hour"], minute=schedule[f"{prefix}Min"]) + + +def _set_start(api: Host, ch: int, value: time) -> Any: + """Set the start time of the floodlight schedule.""" + schedule = api.whiteled_schedule(ch) or {} + return api.set_spotlight_lighting_schedule( + ch, + schedule.get("EndHour", 0), + schedule.get("EndMin", 0), + value.hour, + value.minute, + ) + + +def _set_end(api: Host, ch: int, value: time) -> Any: + """Set the end time of the floodlight schedule.""" + schedule = api.whiteled_schedule(ch) or {} + return api.set_spotlight_lighting_schedule( + ch, + value.hour, + value.minute, + schedule.get("StartHour", 0), + schedule.get("StartMin", 0), + ) + + +TIME_ENTITIES = ( + ReolinkTimeEntityDescription( + key="floodlight_schedule_start", + cmd_key="GetWhiteLed", + cmd_id=[289, 438], + translation_key="floodlight_schedule_start", + entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=False, + supported=lambda api, ch: ( + SpotlightModeEnum.schedule.name in api.whiteled_mode_list(ch) + ), + value=lambda api, ch: _schedule_time(api, ch, "Start"), + method=_set_start, + ), + ReolinkTimeEntityDescription( + key="floodlight_schedule_end", + cmd_key="GetWhiteLed", + cmd_id=[289, 438], + translation_key="floodlight_schedule_end", + entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=False, + supported=lambda api, ch: ( + SpotlightModeEnum.schedule.name in api.whiteled_mode_list(ch) + ), + value=lambda api, ch: _schedule_time(api, ch, "End"), + method=_set_end, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ReolinkConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up Reolink time entities.""" + reolink_data = config_entry.runtime_data + api = reolink_data.host.api + + async_add_entities( + ReolinkTimeEntity(reolink_data, channel, entity_description) + for entity_description in TIME_ENTITIES + for channel in api.channels + if entity_description.supported(api, channel) + ) + + +class ReolinkTimeEntity(ReolinkChannelCoordinatorEntity, TimeEntity): + """Base time entity class for Reolink IP cameras.""" + + entity_description: ReolinkTimeEntityDescription + + def __init__( + self, + reolink_data: ReolinkData, + channel: int, + entity_description: ReolinkTimeEntityDescription, + ) -> None: + """Initialize Reolink time entity.""" + self.entity_description = entity_description + super().__init__(reolink_data, channel) + + @property + @override + def native_value(self) -> time | None: + """Return the current value.""" + return self.entity_description.value(self._host.api, self._channel) + + @raise_translated_error + @override + async def async_set_value(self, value: time) -> None: + """Update the current value.""" + await self.entity_description.method(self._host.api, self._channel, value) + self.async_write_ha_state() diff --git a/tests/components/reolink/test_time.py b/tests/components/reolink/test_time.py new file mode 100644 index 0000000000000..6fa340126d7a2 --- /dev/null +++ b/tests/components/reolink/test_time.py @@ -0,0 +1,105 @@ +"""Test the Reolink time platform.""" + +from datetime import time +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from reolink_aio.enums import SpotlightModeEnum +from reolink_aio.exceptions import InvalidParameterError, ReolinkError + +from homeassistant.components.time import DOMAIN as TIME_DOMAIN, SERVICE_SET_VALUE +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import ATTR_ENTITY_ID, ATTR_TIME, STATE_UNKNOWN, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError + +from .conftest import TEST_CAM_NAME + +from tests.common import MockConfigEntry + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_floodlight_schedule( + hass: HomeAssistant, + config_entry: MockConfigEntry, + reolink_host: MagicMock, +) -> None: + """Test the floodlight schedule start and end time entities.""" + reolink_host.whiteled_schedule.return_value = { + "StartHour": 18, + "StartMin": 0, + "EndHour": 6, + "EndMin": 30, + } + reolink_host.whiteled_mode_list.return_value = [SpotlightModeEnum.schedule.name] + reolink_host.set_spotlight_lighting_schedule = AsyncMock() + + with patch("homeassistant.components.reolink.PLATFORMS", [Platform.TIME]): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state is ConfigEntryState.LOADED + + start_id = f"{Platform.TIME}.{TEST_CAM_NAME}_floodlight_schedule_start" + end_id = f"{Platform.TIME}.{TEST_CAM_NAME}_floodlight_schedule_end" + + assert hass.states.get(start_id).state == "18:00:00" + assert hass.states.get(end_id).state == "06:30:00" + + # Setting the start time keeps the existing end time (6:30) + await hass.services.async_call( + TIME_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: start_id, ATTR_TIME: time(20, 15)}, + blocking=True, + ) + reolink_host.set_spotlight_lighting_schedule.assert_called_with(0, 6, 30, 20, 15) + + # Setting the end time keeps the existing start time (18:00) + await hass.services.async_call( + TIME_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: end_id, ATTR_TIME: time(7, 0)}, + blocking=True, + ) + reolink_host.set_spotlight_lighting_schedule.assert_called_with(0, 7, 0, 18, 0) + + reolink_host.set_spotlight_lighting_schedule.side_effect = ReolinkError( + "Test error" + ) + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + TIME_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: start_id, ATTR_TIME: time(20, 15)}, + blocking=True, + ) + + reolink_host.set_spotlight_lighting_schedule.side_effect = InvalidParameterError( + "Test error" + ) + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + TIME_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: start_id, ATTR_TIME: time(20, 15)}, + blocking=True, + ) + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_floodlight_schedule_unknown( + hass: HomeAssistant, + config_entry: MockConfigEntry, + reolink_host: MagicMock, +) -> None: + """Test the floodlight schedule entities when no schedule is available.""" + reolink_host.whiteled_mode_list.return_value = [SpotlightModeEnum.schedule.name] + reolink_host.whiteled_schedule.return_value = None + + with patch("homeassistant.components.reolink.PLATFORMS", [Platform.TIME]): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state is ConfigEntryState.LOADED + + start_id = f"{Platform.TIME}.{TEST_CAM_NAME}_floodlight_schedule_start" + assert hass.states.get(start_id).state == STATE_UNKNOWN From 142fe44650abea86f64c758a9c6c8fcf13d789ac Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 1 Aug 2026 11:22:23 +0200 Subject: [PATCH 09/10] Fix via_device race in actron_air (#177704) --- .../components/actron_air/__init__.py | 15 +++++++++++++ .../components/actron_air/coordinator.py | 2 ++ homeassistant/components/actron_air/entity.py | 7 +++++- tests/components/actron_air/test_init.py | 22 +++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/actron_air/__init__.py b/homeassistant/components/actron_air/__init__.py index 6691c6fd510b7..04d595a3ac2f7 100644 --- a/homeassistant/components/actron_air/__init__.py +++ b/homeassistant/components/actron_air/__init__.py @@ -6,6 +6,7 @@ from homeassistant.const import CONF_API_TOKEN, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr from .const import DOMAIN, LOGGER from .coordinator import ( @@ -37,6 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) -> translation_key="setup_connection_error", ) from err + device_registry = dr.async_get(hass) system_coordinators: dict[str, ActronAirSystemCoordinator] = {} for system in systems: coordinator = ActronAirSystemCoordinator(hass, entry, api, system) @@ -44,6 +46,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) -> await coordinator.async_config_entry_first_refresh() system_coordinators[system.serial] = coordinator + # Register the AC system device so zone entities can link to it as their + # via device when they are set up. + ac_system = coordinator.data.ac_system + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, system.serial)}, + name=ac_system.system_name, + manufacturer="Actron Air", + model_id=ac_system.master_wc_model, + sw_version=ac_system.master_wc_firmware_version, + serial_number=system.serial, + ) + entry.runtime_data = ActronAirRuntimeData( api=api, system_coordinators=system_coordinators, diff --git a/homeassistant/components/actron_air/coordinator.py b/homeassistant/components/actron_air/coordinator.py index b3c2383197d92..09c17824448a3 100644 --- a/homeassistant/components/actron_air/coordinator.py +++ b/homeassistant/components/actron_air/coordinator.py @@ -40,6 +40,8 @@ class ActronAirRuntimeData: class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirStatus]): """System coordinator for Actron Air integration.""" + config_entry: ActronAirConfigEntry + def __init__( self, hass: HomeAssistant, diff --git a/homeassistant/components/actron_air/entity.py b/homeassistant/components/actron_air/entity.py index 0b1bb5e759dd3..f0c2abe7142c7 100644 --- a/homeassistant/components/actron_air/entity.py +++ b/homeassistant/components/actron_air/entity.py @@ -7,6 +7,7 @@ from actron_neo_api import ActronAirAPIError, ActronAirZone from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -90,5 +91,9 @@ def __init__( manufacturer="Actron Air", model="Zone", suggested_area=zone.title, - via_device=(DOMAIN, self._serial_number), + via_device_id=dr.async_get_device_id_by_identifier( + coordinator.hass, + (DOMAIN, self._serial_number), + config_entry_id=coordinator.config_entry.entry_id, + ), ) diff --git a/tests/components/actron_air/test_init.py b/tests/components/actron_air/test_init.py index 5a13dd6185317..b4af9a47cffbc 100644 --- a/tests/components/actron_air/test_init.py +++ b/tests/components/actron_air/test_init.py @@ -3,9 +3,12 @@ from unittest.mock import AsyncMock from actron_neo_api import ActronAirAPIError, ActronAirAuthError +import pytest +from homeassistant.components.actron_air.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from . import setup_integration @@ -36,3 +39,22 @@ async def test_setup_entry_api_error( await setup_integration(hass, mock_config_entry) assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + +@pytest.mark.usefixtures("init_integration_with_zone") +async def test_zone_device_via_device_id( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + mock_config_entry: MockConfigEntry, +) -> None: + """Test the zone device links to the AC system device via via_device_id.""" + system_device = device_registry.async_get_device_by_identifier( + (DOMAIN, "123456"), mock_config_entry.entry_id + ) + assert system_device is not None + + zone_device = device_registry.async_get_device_by_identifier( + (DOMAIN, "123456_zone_0"), mock_config_entry.entry_id + ) + assert zone_device is not None + assert zone_device.via_device_id == system_device.id From 919fe3191fc3e0eda1efd90b178754bf2dc0bfee Mon Sep 17 00:00:00 2001 From: Luis Miranda <161006+luuuis@users.noreply.github.com> Date: Sat, 1 Aug 2026 10:22:56 +0100 Subject: [PATCH 10/10] fix(omie): remove naive datetime.now() usage (#177921) --- tests/components/omie/conftest.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/components/omie/conftest.py b/tests/components/omie/conftest.py index d13ececb629e5..2e9a512939135 100644 --- a/tests/components/omie/conftest.py +++ b/tests/components/omie/conftest.py @@ -10,6 +10,7 @@ from homeassistant.components.omie.const import DOMAIN from homeassistant.core import HomeAssistant +from homeassistant.util import dt as dt_util from . import price_enc, spot_price_fetcher @@ -85,7 +86,7 @@ def mock_omie_results_jan15() -> OMIEResults: ], ) return OMIEResults( - updated_at=dt.datetime.now(), # pylint: disable=home-assistant-enforce-naive-now + updated_at=dt_util.now(), market_date=test_date, contents=spot_data, raw=json.dumps(spot_data), @@ -120,7 +121,7 @@ def mock_omie_results_oct15() -> OMIEResults: ], ) return OMIEResults( - updated_at=dt.datetime.now(), # pylint: disable=home-assistant-enforce-naive-now + updated_at=dt_util.now(), market_date=test_date, contents=spot_data, raw=json.dumps(spot_data), @@ -155,7 +156,7 @@ def mock_omie_results_oct26_dst() -> OMIEResults: ], ) return OMIEResults( - updated_at=dt.datetime.now(), # pylint: disable=home-assistant-enforce-naive-now + updated_at=dt_util.now(), market_date=test_date, contents=spot_data, raw=json.dumps(spot_data), @@ -190,7 +191,7 @@ def mock_omie_results_jan16() -> OMIEResults: ], ) return OMIEResults( - updated_at=dt.datetime.now(), # pylint: disable=home-assistant-enforce-naive-now + updated_at=dt_util.now(), market_date=test_date, contents=spot_data, raw=json.dumps(spot_data),