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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user