mirror of
https://github.com/home-assistant/core.git
synced 2026-08-06 13:26:29 +01:00
Fix via_device race in isy994 (#177722)
This commit is contained in:
@@ -138,12 +138,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
|
||||
) from err
|
||||
|
||||
isy_data = entry.runtime_data = IsyData()
|
||||
_categorize_nodes(isy_data, isy.nodes, ignore_identifier, sensor_identifier)
|
||||
isy_data.root = isy
|
||||
_async_get_or_create_isy_device_in_registry(hass, entry, isy)
|
||||
via_device_id = dr.async_get_device_id_by_identifier(
|
||||
hass, (DOMAIN, isy.uuid), config_entry_id=entry.entry_id
|
||||
)
|
||||
_categorize_nodes(
|
||||
isy_data, isy.nodes, ignore_identifier, sensor_identifier, via_device_id
|
||||
)
|
||||
_categorize_programs(isy_data, isy.programs)
|
||||
# Gather ISY Variables to be added.
|
||||
if isy.variables.children:
|
||||
isy_data.devices[CONF_VARIABLES] = _create_service_device_info(
|
||||
isy, name=CONF_VARIABLES.title(), unique_id=CONF_VARIABLES
|
||||
isy,
|
||||
name=CONF_VARIABLES.title(),
|
||||
unique_id=CONF_VARIABLES,
|
||||
via_device_id=via_device_id,
|
||||
)
|
||||
numbers = isy_data.variables[Platform.NUMBER]
|
||||
for vtype, _, vid in isy.variables.children:
|
||||
@@ -152,7 +162,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
|
||||
isy.conf[CONFIG_NETWORKING] or isy.conf.get(CONFIG_PORTAL)
|
||||
) and isy.networking.nobjs:
|
||||
isy_data.devices[CONF_NETWORK] = _create_service_device_info(
|
||||
isy, name=CONFIG_NETWORKING, unique_id=CONF_NETWORK
|
||||
isy,
|
||||
name=CONFIG_NETWORKING,
|
||||
unique_id=CONF_NETWORK,
|
||||
via_device_id=via_device_id,
|
||||
)
|
||||
for resource in isy.networking.nobjs:
|
||||
isy_data.net_resources.append(resource)
|
||||
@@ -160,9 +173,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
|
||||
# Dump ISY Clock Information. Future: Add ISY as sensor to Hass with attrs
|
||||
LOGGER.debug(repr(isy.clock))
|
||||
|
||||
isy_data.root = isy
|
||||
_async_get_or_create_isy_device_in_registry(hass, entry, isy)
|
||||
|
||||
# Load platforms for the devices in the ISY controller that we support.
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -202,9 +212,11 @@ def _async_get_or_create_isy_device_in_registry(
|
||||
)
|
||||
|
||||
|
||||
def _create_service_device_info(isy: ISY, name: str, unique_id: str) -> DeviceInfo:
|
||||
def _create_service_device_info(
|
||||
isy: ISY, name: str, unique_id: str, via_device_id: str | None
|
||||
) -> DeviceInfo:
|
||||
"""Create device info for ISY service devices."""
|
||||
return DeviceInfo(
|
||||
device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(
|
||||
DOMAIN,
|
||||
@@ -216,9 +228,11 @@ def _create_service_device_info(isy: ISY, name: str, unique_id: str) -> DeviceIn
|
||||
model=isy.conf[ISY_CONF_MODEL],
|
||||
sw_version=isy.conf[ISY_CONF_FIRMWARE],
|
||||
configuration_url=isy.conn.url,
|
||||
via_device=(DOMAIN, isy.uuid),
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
)
|
||||
if via_device_id is not None:
|
||||
device_info["via_device_id"] = via_device_id
|
||||
return device_info
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
|
||||
|
||||
@@ -288,17 +288,18 @@ def _add_backlight_if_supported(isy_data: IsyData, node: Node) -> None:
|
||||
isy_data.aux_properties[Platform.NUMBER].append((node, CMD_BACKLIGHT))
|
||||
|
||||
|
||||
def _generate_device_info(node: Node) -> DeviceInfo:
|
||||
def _generate_device_info(node: Node, via_device_id: str | None) -> DeviceInfo:
|
||||
"""Generate the device info for a root node device."""
|
||||
isy = node.isy
|
||||
device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, f"{isy.uuid}_{node.address}")},
|
||||
manufacturer=node.protocol.title(),
|
||||
name=node.name,
|
||||
via_device=(DOMAIN, isy.uuid),
|
||||
configuration_url=isy.conn.url,
|
||||
suggested_area=node.folder,
|
||||
)
|
||||
if via_device_id is not None:
|
||||
device_info["via_device_id"] = via_device_id
|
||||
|
||||
# ISYv5 Device Types can provide model and manufacturer
|
||||
model: str = str(node.address).rpartition(" ")[0] or node.address
|
||||
@@ -328,7 +329,11 @@ def _generate_device_info(node: Node) -> DeviceInfo:
|
||||
|
||||
|
||||
def _categorize_nodes(
|
||||
isy_data: IsyData, nodes: Nodes, ignore_identifier: str, sensor_identifier: str
|
||||
isy_data: IsyData,
|
||||
nodes: Nodes,
|
||||
ignore_identifier: str,
|
||||
sensor_identifier: str,
|
||||
via_device_id: str | None,
|
||||
) -> None:
|
||||
"""Sort the nodes to their proper platforms."""
|
||||
for path, node in nodes:
|
||||
@@ -339,7 +344,7 @@ def _categorize_nodes(
|
||||
|
||||
if hasattr(node, "parent_node") and node.parent_node is None:
|
||||
# This is a physical device / parent node
|
||||
isy_data.devices[node.address] = _generate_device_info(node)
|
||||
isy_data.devices[node.address] = _generate_device_info(node, via_device_id)
|
||||
isy_data.root_nodes[Platform.BUTTON].append(node)
|
||||
# Any parent node can have communication errors:
|
||||
isy_data.aux_properties[Platform.SENSOR].append((node, PROP_COMMS_ERROR))
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Test the Universal Devices ISY/IoX integration init."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -8,6 +10,7 @@ from homeassistant.components.isy994.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_VERIFY_SSL
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
@@ -71,3 +74,30 @@ async def test_setup_forwards_verify_ssl_to_pyisy(
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert isy_constructor.call_args.kwargs["verify_ssl"] is verify_ssl
|
||||
|
||||
|
||||
async def test_node_device_linked_to_isy_device(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_isy: MagicMock,
|
||||
mock_node: Callable[..., Any],
|
||||
) -> None:
|
||||
"""Test a root node's device is linked to the ISY device via via_device_id."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
node = mock_node(mock_isy, "22 22 22 1", "Test Node", "GenericNode")
|
||||
mock_isy.nodes.__iter__.return_value = [("Test Node", node)]
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
isy_device = device_registry.async_get_device_by_identifier(
|
||||
(DOMAIN, mock_isy.uuid), mock_config_entry.entry_id
|
||||
)
|
||||
node_device = device_registry.async_get_device_by_identifier(
|
||||
(DOMAIN, f"{mock_isy.uuid}_{node.address}"), mock_config_entry.entry_id
|
||||
)
|
||||
assert isy_device is not None
|
||||
assert node_device is not None
|
||||
assert node_device.via_device_id == isy_device.id
|
||||
|
||||
Reference in New Issue
Block a user