1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Automatically removing stale devices in Homee (#152680)

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
This commit is contained in:
Markus Adrario
2025-10-15 19:36:27 +02:00
committed by GitHub
parent d0cad43a6c
commit ca912906f5
4 changed files with 94 additions and 2 deletions
@@ -3,6 +3,7 @@
import logging
from pyHomee import Homee, HomeeAuthFailedException, HomeeConnectionFailedException
from pyHomee.model import HomeeNode
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
@@ -88,6 +89,40 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomeeConfigEntry) -> boo
sw_version=homee.settings.version,
)
# Remove devices that are no longer present in homee.
devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id)
for device in devices:
# Check if the device is still present in homee
device_identifiers = {identifier[1] for identifier in device.identifiers}
# homee itself uses just the uid, nodes use uid-nodeid
is_homee_hub = homee.settings.uid in device_identifiers
is_node_present = any(
f"{homee.settings.uid}-{node.id}" in device_identifiers
for node in homee.nodes
)
if not is_node_present and not is_homee_hub:
_LOGGER.info("Removing device %s", device.name)
device_registry.async_update_device(
device_id=device.id,
remove_config_entry_id=entry.entry_id,
)
# Remove device at runtime when node is removed in homee
async def _remove_node_callback(node: HomeeNode, add: bool) -> None:
"""Call when a node is removed."""
if not add:
device = device_registry.async_get_device(
identifiers={(DOMAIN, f"{entry.runtime_data.settings.uid}-{node.id}")}
)
if device:
_LOGGER.info("Removing device %s", device.name)
device_registry.async_update_device(
device_id=device.id,
remove_config_entry_id=entry.entry_id,
)
homee.add_nodes_listener(_remove_node_callback)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
@@ -63,7 +63,7 @@ rules:
icon-translations: done
reconfiguration-flow: done
repair-issues: todo
stale-devices: todo
stale-devices: done
# Platinum
async-dependency: todo
+1 -1
View File
@@ -46,7 +46,7 @@ async def test_add_device(
added_node = build_mock_node("add_device.json")
mock_homee.nodes.append(added_node)
mock_homee.get_node_by_id.return_value = mock_homee.nodes[1]
await mock_homee.add_nodes_listener.call_args_list[0][0][0](added_node, True)
await mock_homee.add_nodes_listener.call_args_list[1][0][0](added_node, True)
await hass.async_block_till_done()
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
+57
View File
@@ -143,3 +143,60 @@ async def test_unload_entry(
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
async def test_remove_stale_device_on_startup(
hass: HomeAssistant,
mock_homee: MagicMock,
mock_config_entry: MockConfigEntry,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test removal of stale device on startup."""
mock_homee.nodes = [
build_mock_node("homee.json"),
build_mock_node("light_single.json"), # id 2
build_mock_node("add_device.json"), # id 3
]
mock_homee.get_node_by_id = lambda node_id: mock_homee.nodes[node_id - 1]
await setup_integration(hass, mock_config_entry)
device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")})
assert device is not None
mock_homee.nodes.pop() # Remove node with id 3
# Reload integration
await hass.config_entries.async_reload(mock_config_entry.entry_id)
await hass.async_block_till_done()
# Stale device should be removed
device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")})
assert device is None
async def test_remove_node_callback(
hass: HomeAssistant,
mock_homee: MagicMock,
mock_config_entry: MockConfigEntry,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test removal of device when node is removed in homee."""
mock_homee.nodes = [
build_mock_node("homee.json"),
build_mock_node("light_single.json"), # id 2
build_mock_node("add_device.json"), # id 3
]
mock_homee.get_node_by_id = lambda node_id: mock_homee.nodes[node_id - 1]
await setup_integration(hass, mock_config_entry)
device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")})
assert device is not None
# Simulate removal of node with id 3 in homee
await mock_homee.add_nodes_listener.call_args_list[0][0][0](
mock_homee.nodes[2], add=False
)
await hass.async_block_till_done()
# Device should be removed
device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")})
assert device is None