Migrate insteon to lookup devices scoped to config entry (#177964)

This commit is contained in:
Erik Montnemery
2026-08-04 14:33:47 +02:00
committed by Bram Kragten
parent 07f5bb8a0a
commit c419da1eb7
6 changed files with 58 additions and 14 deletions
+7 -3
View File
@@ -12,6 +12,7 @@ from homeassistant.helpers import device_registry as dr
from ..const import DEVICE_ADDRESS, ID, INSTEON_DEVICE_NOT_FOUND, TYPE
from ..utils import async_device_name
from .config import get_insteon_config_entry
from .device import notify_device_not_found
ALDB_RECORD = "record"
@@ -32,7 +33,7 @@ ALDB_RECORD_SCHEMA = vol.Schema(
)
async def async_aldb_record_to_dict(dev_registry, record, dirty=False):
async def async_aldb_record_to_dict(dev_registry, record, config_entry_id, dirty=False):
"""Convert an ALDB record to a dict."""
return ALDB_RECORD_SCHEMA(
{
@@ -42,7 +43,9 @@ async def async_aldb_record_to_dict(dev_registry, record, dirty=False):
"highwater": record.is_high_water_mark,
"group": record.group,
"target": str(record.target),
"target_name": await async_device_name(dev_registry, record.target),
"target_name": await async_device_name(
dev_registry, record.target, config_entry_id
),
"data1": record.data1,
"data2": record.data2,
"data3": record.data3,
@@ -88,10 +91,11 @@ async def websocket_get_aldb(
changed_records = list(device.aldb.pending_changes.keys())
dev_registry = dr.async_get(hass)
config_entry_id = get_insteon_config_entry(hass).entry_id
records = [
await async_aldb_record_to_dict(
dev_registry, aldb[mem_addr], mem_addr in changed_records
dev_registry, aldb[mem_addr], config_entry_id, mem_addr in changed_records
)
for mem_addr in aldb
]
@@ -137,12 +137,16 @@ def remove_device_override(hass: HomeAssistant, address: Address):
async def async_link_to_dict(
address: Address, record: ALDBRecord, dev_registry: dr.DeviceRegistry, status=None
address: Address,
record: ALDBRecord,
dev_registry: dr.DeviceRegistry,
config_entry_id: str,
status=None,
) -> dict[str, str | int]:
"""Convert a link to a dictionary."""
link_dict: dict[str, str | int] = {}
device_name = await async_device_name(dev_registry, address)
target_name = await async_device_name(dev_registry, record.target)
device_name = await async_device_name(dev_registry, address, config_entry_id)
target_name = await async_device_name(dev_registry, record.target, config_entry_id)
link_dict["address"] = str(address)
link_dict["device_name"] = device_name or str(address)
link_dict["mem_addr"] = record.mem_addr
@@ -311,8 +315,9 @@ async def websocket_get_broken_links(
"""Get any broken links between devices."""
broken_links = get_broken_links(devices=devices)
dev_registry = dr.async_get(hass)
config_entry_id = get_insteon_config_entry(hass).entry_id
broken_links_list = [
await async_link_to_dict(address, record, dev_registry, status)
await async_link_to_dict(address, record, dev_registry, config_entry_id, status)
for address, record, status in broken_links
if status != LinkStatus.MISSING_TARGET
]
+4 -1
View File
@@ -217,7 +217,10 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
signal = f"{address.id}_{SIGNAL_REMOVE_ENTITY}"
async_dispatcher_send(hass, signal)
dev_registry = dr.async_get(hass)
device = dev_registry.async_get_device(identifiers={(DOMAIN, str(address))})
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
device = dev_registry.async_get_device_by_identifier(
(DOMAIN, str(address)), config_entry.entry_id
)
if device:
dev_registry.async_remove_device(device.id)
+6 -2
View File
@@ -190,9 +190,13 @@ def compute_device_name(ha_device) -> str:
return ha_device.name_by_user or ha_device.name
async def async_device_name(dev_registry: dr.DeviceRegistry, address: Address) -> str:
async def async_device_name(
dev_registry: dr.DeviceRegistry, address: Address, config_entry_id: str
) -> str:
"""Get the Insteon device name from a device registry id."""
ha_device = dev_registry.async_get_device(identifiers={(DOMAIN, str(address))})
ha_device = dev_registry.async_get_device_by_identifier(
(DOMAIN, str(address)), config_entry_id
)
if not ha_device:
if device := devices[address]:
return f"{device.description} ({device.model})"
+10 -1
View File
@@ -20,11 +20,13 @@ from homeassistant.components.insteon.api.aldb import (
TYPE,
)
from homeassistant.components.insteon.api.device import INSTEON_DEVICE_NOT_FOUND
from homeassistant.components.insteon.const import DOMAIN
from homeassistant.core import HomeAssistant
from .const import MOCK_USER_INPUT_PLM
from .mock_devices import MockDevices
from tests.common import load_fixture
from tests.common import MockConfigEntry, load_fixture
from tests.typing import MockHAClientWebSocket, WebSocketGenerator
@@ -38,6 +40,13 @@ async def _setup(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, aldb_data: dict[str, Any]
) -> tuple[MockHAClientWebSocket, MockDevices]:
"""Set up tests."""
config_entry = MockConfigEntry(
domain=DOMAIN,
entry_id="abcde12345",
data=MOCK_USER_INPUT_PLM,
options={},
)
config_entry.add_to_hass(hass)
ws_client = await hass_ws_client(hass)
devices = MockDevices()
await devices.async_load()
+22 -3
View File
@@ -123,14 +123,33 @@ async def test_get_ha_device_name(
"""Test getting the HA device name from an Insteon address."""
_, devices, _, device_reg = await async_mock_setup(hass, hass_ws_client)
config_entry_id = hass.config_entries.async_entries(DOMAIN)[0].entry_id
# Register a colliding device sharing the same identifier but owned by a second
# config entry. The lookup must be scoped to the supplied config entry: an
# unscoped lookup could return either device, so returning the correct name for
# each config entry proves the config entry controls the lookup.
other_config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT_PLM)
other_config_entry.add_to_hass(hass)
device_reg.async_get_or_create(
config_entry_id=other_config_entry.entry_id,
identifiers={(DOMAIN, "11.11.11")},
name="Device 11.11.11 second entry",
)
with patch.object(insteon.api.device, "devices", devices):
# Test a real HA and Insteon device
name = await async_device_name(device_reg, "11.11.11")
# The scoped lookup returns the device owned by the supplied config entry
name = await async_device_name(device_reg, "11.11.11", config_entry_id)
assert name == "Device 11.11.11"
# The same identifier scoped to the second config entry returns its device
name = await async_device_name(
device_reg, "11.11.11", other_config_entry.entry_id
)
assert name == "Device 11.11.11 second entry"
# Test no HA or Insteon device
name = await async_device_name(device_reg, "BB.BB.BB")
name = await async_device_name(device_reg, "BB.BB.BB", config_entry_id)
assert name == ""