From ba02595cea8d5fb16b51959be1e17ab89ee88516 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Sep 2026 17:59:44 +0200 Subject: [PATCH 1/8] Implement Core PR #178164 --- custom_components/plugwise/entity.py | 9 +++++++-- tests/components/plugwise/test_init.py | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/custom_components/plugwise/entity.py b/custom_components/plugwise/entity.py index 19ff29043..6dacd249e 100644 --- a/custom_components/plugwise/entity.py +++ b/custom_components/plugwise/entity.py @@ -4,11 +4,12 @@ from plugwise.constants import GwEntityData -from homeassistant.const import ATTR_NAME, ATTR_VIA_DEVICE, CONF_HOST +from homeassistant.const import ATTR_NAME, CONF_HOST from homeassistant.helpers.device_registry import ( CONNECTION_NETWORK_MAC, CONNECTION_ZIGBEE, DeviceInfo, + async_get_device_id_by_identifier, ) from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -78,7 +79,11 @@ def __init__( self._attr_device_info.update( { ATTR_NAME: self.device.get(ATTR_NAME), - ATTR_VIA_DEVICE: (DOMAIN, gateway_id), + "via_device_id": async_get_device_id_by_identifier( + coordinator.hass, + (DOMAIN, gateway_id), + config_entry_id=entry.entry_id, + ), } ) diff --git a/tests/components/plugwise/test_init.py b/tests/components/plugwise/test_init.py index 427d0e2de..3c3fb2472 100644 --- a/tests/components/plugwise/test_init.py +++ b/tests/components/plugwise/test_init.py @@ -172,6 +172,28 @@ async def test_device_in_dr( assert device_entry.sw_version == "4.4.2" +@pytest.mark.parametrize("chosen_env", ["m_adam_heating"], indirect=True) +@pytest.mark.parametrize("cooling_present", [False], indirect=True) +async def test_device_via_device_links( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_smile_adam_heat_cool: MagicMock, + device_registry: dr.DeviceRegistry, + init_integration: MockConfigEntry, +) -> None: + """Test that a non-gateway device links to the gateway via via_device_id.""" + gateway_device = device_registry.async_get_device_by_identifier( + (DOMAIN, "da224107914542988a88561b4452b0f6"), mock_config_entry.entry_id + ) + assert gateway_device is not None + + child_device = device_registry.async_get_device_by_identifier( + (DOMAIN, "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6"), mock_config_entry.entry_id + ) + assert child_device is not None + assert child_device.via_device_id == gateway_device.id + + async def check_migration( hass: HomeAssistant, mock_config_entry: MockConfigEntry, From 530c9c0155fcd86a6953dafc90bd845f32967a26 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 11 Aug 2026 19:30:33 +0200 Subject: [PATCH 2/8] Implement Core PR #176905 --- custom_components/plugwise/coordinator.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/custom_components/plugwise/coordinator.py b/custom_components/plugwise/coordinator.py index 4d59282b6..f0e264c76 100644 --- a/custom_components/plugwise/coordinator.py +++ b/custom_components/plugwise/coordinator.py @@ -184,7 +184,11 @@ def _remove_devices(self, removed_devices: set[str]) -> None: """Clean registries when removed devices found.""" device_reg = dr.async_get(self.hass) for device_id in removed_devices: - if (device_entry := device_reg.async_get_device({(DOMAIN, device_id)})) is not None: + if ( + device_entry := device_entry := device_reg.async_get_device_by_identifier( + (DOMAIN, device_id), self.config_entry.entry_id + ) + ) is not None: device_reg.async_update_device( device_entry.id, remove_config_entry_id=self.config_entry.entry_id ) @@ -219,7 +223,11 @@ def _update_device_firmware(self, data: dict[str, GwEntityData]) -> None: def _update_firmware_in_dr(self, device_id: str, firmware: str | None) -> bool: """Update device sw_version in device_registry.""" device_reg = dr.async_get(self.hass) - if (device_entry := device_reg.async_get_device({(DOMAIN, device_id)})) is not None: + if ( + device_entry := device_reg.async_get_device_by_identifier( + (DOMAIN, device_id), self.config_entry.entry_id + ) + ) is not None: device_reg.async_update_device(device_entry.id, sw_version=firmware) LOGGER.debug( "Firmware in device_registry updated for %s %s %s", From 6d29b55137188312a9f034f784360be2571c3549 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 11 Aug 2026 19:32:35 +0200 Subject: [PATCH 3/8] Implement Core PR #177963 --- custom_components/plugwise/coordinator.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/custom_components/plugwise/coordinator.py b/custom_components/plugwise/coordinator.py index f0e264c76..3941f3da1 100644 --- a/custom_components/plugwise/coordinator.py +++ b/custom_components/plugwise/coordinator.py @@ -189,9 +189,7 @@ def _remove_devices(self, removed_devices: set[str]) -> None: (DOMAIN, device_id), self.config_entry.entry_id ) ) is not None: - device_reg.async_update_device( - device_entry.id, remove_config_entry_id=self.config_entry.entry_id - ) + device_reg.async_remove_device(device_entry.id) LOGGER.debug( "%s %s %s removed from device_registry", DOMAIN, From 8faa8b8eadc2a0ae4a38f377bf2e8b3bce8a3d1b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 11 Aug 2026 19:38:00 +0200 Subject: [PATCH 4/8] Implement CORE PR #178334 --- tests/components/plugwise/test_init.py | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/components/plugwise/test_init.py b/tests/components/plugwise/test_init.py index 3c3fb2472..f8583507d 100644 --- a/tests/components/plugwise/test_init.py +++ b/tests/components/plugwise/test_init.py @@ -161,8 +161,8 @@ async def test_device_in_dr( await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done() - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "a455b61e52394b2db5081ce025a430f3")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "a455b61e52394b2db5081ce025a430f3"), mock_config_entry.entry_id ) assert device_entry.hw_version == "AME Smile 2.0 board" assert device_entry.manufacturer == "Plugwise" @@ -370,8 +370,8 @@ async def test_update_device( len(dr.async_entries_for_config_entry(device_registry, mock_config_entry.entry_id)) == 11 ) - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "01234567890abcdefghijklmnopqrstu")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "01234567890abcdefghijklmnopqrstu"), mock_config_entry.entry_id ) assert device_entry is not None @@ -397,8 +397,8 @@ async def test_update_device( len(dr.async_entries_for_config_entry(device_registry, mock_config_entry.entry_id)) == 10 ) - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "1772a4ea304041adb83f357b751341ff")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "1772a4ea304041adb83f357b751341ff"), mock_config_entry.entry_id ) assert device_entry is None @@ -416,8 +416,8 @@ async def test_delete_removed_device( """Test device removal at integration init.""" data = mock_smile_adam_heat_cool.async_update.return_value - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6"), mock_config_entry.entry_id ) assert device_entry is not None @@ -427,8 +427,8 @@ async def test_delete_removed_device( async_fire_time_changed(hass) await hass.async_block_till_done() - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6"), mock_config_entry.entry_id ) assert device_entry is None @@ -446,8 +446,8 @@ async def test_update_device_firmware( """Test device firmware update via coordinator.""" data = mock_smile_adam_heat_cool.async_update.return_value - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "da224107914542988a88561b4452b0f6")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "da224107914542988a88561b4452b0f6"), mock_config_entry.entry_id ) assert device_entry is not None assert str(device_entry.sw_version) == "3.9.0" @@ -458,8 +458,8 @@ async def test_update_device_firmware( async_fire_time_changed(hass) await hass.async_block_till_done() - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "da224107914542988a88561b4452b0f6")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "da224107914542988a88561b4452b0f6"), mock_config_entry.entry_id ) assert device_entry is not None assert str(device_entry.sw_version) == "3.10.13" From 5abd18c072d43d9e8d5e04c32a31547bd68d96b3 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Sep 2026 18:10:46 +0200 Subject: [PATCH 5/8] Corrections --- custom_components/plugwise/coordinator.py | 4 ++-- tests/components/plugwise/test_init.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/custom_components/plugwise/coordinator.py b/custom_components/plugwise/coordinator.py index 3941f3da1..000e9893b 100644 --- a/custom_components/plugwise/coordinator.py +++ b/custom_components/plugwise/coordinator.py @@ -185,7 +185,7 @@ def _remove_devices(self, removed_devices: set[str]) -> None: device_reg = dr.async_get(self.hass) for device_id in removed_devices: if ( - device_entry := device_entry := device_reg.async_get_device_by_identifier( + device_entry := device_reg.async_get_device_by_identifier( (DOMAIN, device_id), self.config_entry.entry_id ) ) is not None: @@ -224,7 +224,7 @@ def _update_firmware_in_dr(self, device_id: str, firmware: str | None) -> bool: if ( device_entry := device_reg.async_get_device_by_identifier( (DOMAIN, device_id), self.config_entry.entry_id - ) + ) ) is not None: device_reg.async_update_device(device_entry.id, sw_version=firmware) LOGGER.debug( diff --git a/tests/components/plugwise/test_init.py b/tests/components/plugwise/test_init.py index f8583507d..0a8cbade8 100644 --- a/tests/components/plugwise/test_init.py +++ b/tests/components/plugwise/test_init.py @@ -403,11 +403,12 @@ async def test_update_device( assert device_entry is None -@pytest.mark.usefixtures("mock_config_entry") @pytest.mark.parametrize("chosen_env", ["m_adam_heating"], indirect=True) @pytest.mark.parametrize("cooling_present", [False], indirect=True) async def test_delete_removed_device( hass: HomeAssistant, + *, + mock_config_entry: MockConfigEntry, mock_smile_adam_heat_cool: MagicMock, device_registry: dr.DeviceRegistry, init_integration: MockConfigEntry, @@ -433,11 +434,12 @@ async def test_delete_removed_device( assert device_entry is None -@pytest.mark.usefixtures("mock_config_entry") @pytest.mark.parametrize("chosen_env", ["m_adam_heating"], indirect=True) @pytest.mark.parametrize("cooling_present", [False], indirect=True) async def test_update_device_firmware( hass: HomeAssistant, + *, + mock_config_entry: MockConfigEntry, mock_smile_adam_heat_cool: MagicMock, device_registry: dr.DeviceRegistry, init_integration: MockConfigEntry, From c8ac78ac3b3c137a96d3faef4a1cc30be7e9e85e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Sep 2026 18:50:37 +0200 Subject: [PATCH 6/8] Implement Core PR #180236 --- tests/components/plugwise/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/components/plugwise/conftest.py b/tests/components/plugwise/conftest.py index 70da35c41..f4473f8c7 100644 --- a/tests/components/plugwise/conftest.py +++ b/tests/components/plugwise/conftest.py @@ -329,6 +329,7 @@ def mock_smile_legacy_anna() -> Generator[MagicMock]: api.async_update.return_value = data api.connect.return_value = Version("1.8.22") + api.cooling_present = False api.gateway_id = "0000aaaa0000aaaa0000aaaa0000aa00" api.heater_id = "04e4cbfe7f4340f090f85ec3b9e6a950" api.reboot = False From 10c69bc168479a3d900512417b4c9e1b9c242329 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Sep 2026 18:55:45 +0200 Subject: [PATCH 7/8] Line up correction with previous update --- tests/components/plugwise/test_init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/plugwise/test_init.py b/tests/components/plugwise/test_init.py index 0a8cbade8..da638e0f8 100644 --- a/tests/components/plugwise/test_init.py +++ b/tests/components/plugwise/test_init.py @@ -407,8 +407,8 @@ async def test_update_device( @pytest.mark.parametrize("cooling_present", [False], indirect=True) async def test_delete_removed_device( hass: HomeAssistant, - *, mock_config_entry: MockConfigEntry, + *, mock_smile_adam_heat_cool: MagicMock, device_registry: dr.DeviceRegistry, init_integration: MockConfigEntry, @@ -438,8 +438,8 @@ async def test_delete_removed_device( @pytest.mark.parametrize("cooling_present", [False], indirect=True) async def test_update_device_firmware( hass: HomeAssistant, - *, mock_config_entry: MockConfigEntry, + *, mock_smile_adam_heat_cool: MagicMock, device_registry: dr.DeviceRegistry, init_integration: MockConfigEntry, From ae4c61a84f7fac460d709bd2e9000485b0d98783 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 4 Sep 2026 19:01:38 +0200 Subject: [PATCH 8/8] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95051eda6..d6a9e0c41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Versions from 0.40 and up ## Ongoing +- Various updates: Core PR's [#176905](https://github.com/home-assistant/core/pull/176905), [#177963](https://github.com/home-assistant/core/pull/177963), [#178164](https://github.com/home-assistant/core/pull/178164), [#178334](https://github.com/home-assistant/core/pull/178334), [#180236](https://github.com/home-assistant/core/pull/180236), and fixes - Correct test-function header with more than 5 arguments via PR [#1118](https://github.com/plugwise/plugwise-beta/pull/1118) - Implement @pytest.mark.usefixtures() via PR [#1117](https://github.com/plugwise/plugwise-beta/pull/1117) - Correct climate and water_heater unique_id's via PR [#1098](https://github.com/plugwise/plugwise-beta/pull/1098)