mirror of
https://github.com/home-assistant/core.git
synced 2026-08-03 20:24:55 +02:00
Fix via_device race in tplink
This commit is contained in:
@@ -242,6 +242,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: TPLinkConfigEntry) -> bo
|
|||||||
live_view = entry.data.get(CONF_LIVE_VIEW)
|
live_view = entry.data.get(CONF_LIVE_VIEW)
|
||||||
|
|
||||||
entry.runtime_data = TPLinkData(parent_coordinator, camera_creds, live_view)
|
entry.runtime_data = TPLinkData(parent_coordinator, camera_creds, live_view)
|
||||||
|
|
||||||
|
# Register the parent device before forwarding platforms so child device
|
||||||
|
# entities can resolve their via_device_id from it.
|
||||||
|
device_registry = dr.async_get(hass)
|
||||||
|
device_registry.async_get_or_create(
|
||||||
|
config_entry_id=entry.entry_id,
|
||||||
|
identifiers={(DOMAIN, device.device_id)},
|
||||||
|
)
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -236,7 +236,13 @@ class CoordinatedTPLinkEntity(CoordinatorEntity[TPLinkDataUpdateCoordinator], AB
|
|||||||
and parent != registry_device
|
and parent != registry_device
|
||||||
and parent.device_type is not Device.Type.WallSwitch
|
and parent.device_type is not Device.Type.WallSwitch
|
||||||
):
|
):
|
||||||
self._attr_device_info["via_device"] = (DOMAIN, parent.device_id)
|
self._attr_device_info["via_device_id"] = (
|
||||||
|
dr.async_get_device_id_by_identifier(
|
||||||
|
self.coordinator.hass,
|
||||||
|
(DOMAIN, parent.device_id),
|
||||||
|
config_entry_id=self.coordinator.config_entry.entry_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self._attr_device_info["connections"] = {
|
self._attr_device_info["connections"] = {
|
||||||
(dr.CONNECTION_NETWORK_MAC, device.mac)
|
(dr.CONNECTION_NETWORK_MAC, device.mac)
|
||||||
@@ -642,7 +648,6 @@ class CoordinatedTPLinkModuleEntity(CoordinatedTPLinkEntity, ABC):
|
|||||||
platform_domain=platform_domain,
|
platform_domain=platform_domain,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
has_parent_entities = bool(entities)
|
|
||||||
|
|
||||||
children = _get_new_children(
|
children = _get_new_children(
|
||||||
device, coordinator, known_child_device_ids, entity_class.__name__
|
device, coordinator, known_child_device_ids, entity_class.__name__
|
||||||
@@ -678,16 +683,6 @@ class CoordinatedTPLinkModuleEntity(CoordinatedTPLinkEntity, ABC):
|
|||||||
)
|
)
|
||||||
entities.extend(child_entities)
|
entities.extend(child_entities)
|
||||||
|
|
||||||
if first_check and entities and not has_parent_entities:
|
|
||||||
# Get or create the parent device for via_device.
|
|
||||||
# This is a timing factor in case this platform is loaded before
|
|
||||||
# other platforms that will have entities on the parent. Eventually
|
|
||||||
# those other platforms will update the parent with full DeviceInfo
|
|
||||||
device_registry = dr.async_get(hass)
|
|
||||||
device_registry.async_get_or_create(
|
|
||||||
config_entry_id=coordinator.config_entry.entry_id,
|
|
||||||
identifiers={(DOMAIN, device.device_id)},
|
|
||||||
)
|
|
||||||
return entities
|
return entities
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ from homeassistant.util import dt as dt_util
|
|||||||
from . import (
|
from . import (
|
||||||
_mocked_device,
|
_mocked_device,
|
||||||
_mocked_feature,
|
_mocked_feature,
|
||||||
|
_mocked_strip_children,
|
||||||
_patch_connect,
|
_patch_connect,
|
||||||
_patch_discovery,
|
_patch_discovery,
|
||||||
_patch_single_discovery,
|
_patch_single_discovery,
|
||||||
@@ -1245,3 +1246,84 @@ async def test_automatic_device_addition_does_not_remove_disabled_default(
|
|||||||
check_entities("hub")
|
check_entities("hub")
|
||||||
for child_id in (1, 2, 3):
|
for child_id in (1, 2, 3):
|
||||||
check_entities(f"child_{child_id}")
|
check_entities(f"child_{child_id}")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_via_device_links(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test that strip child devices link to the parent via via_device_id."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=MAC_ADDRESS
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
feature = _mocked_feature(
|
||||||
|
"consumption_this_month",
|
||||||
|
value=5.2,
|
||||||
|
type_=Feature.Type.Sensor,
|
||||||
|
category=Feature.Category.Primary,
|
||||||
|
)
|
||||||
|
parent = _mocked_device(
|
||||||
|
alias="my_plug",
|
||||||
|
features=[feature],
|
||||||
|
children=_mocked_strip_children(features=[feature]),
|
||||||
|
device_type=DeviceType.Strip,
|
||||||
|
)
|
||||||
|
with _patch_discovery(device=parent), _patch_connect(device=parent):
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
parent_device = device_registry.async_get_device_by_identifier(
|
||||||
|
(DOMAIN, parent.device_id), config_entry.entry_id
|
||||||
|
)
|
||||||
|
assert parent_device is not None
|
||||||
|
|
||||||
|
assert parent.children
|
||||||
|
for child in parent.children:
|
||||||
|
child_device = device_registry.async_get_device_by_identifier(
|
||||||
|
(DOMAIN, child.device_id), config_entry.entry_id
|
||||||
|
)
|
||||||
|
assert child_device is not None
|
||||||
|
assert child_device.id != parent_device.id
|
||||||
|
assert child_device.via_device_id == parent_device.id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_wall_switch_child_uses_connections(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test that WallSwitch child devices merge with the parent via connections."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=MAC_ADDRESS
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
feature = _mocked_feature(
|
||||||
|
"consumption_this_month",
|
||||||
|
value=5.2,
|
||||||
|
type_=Feature.Type.Sensor,
|
||||||
|
category=Feature.Category.Primary,
|
||||||
|
)
|
||||||
|
parent = _mocked_device(
|
||||||
|
alias="my_plug",
|
||||||
|
features=[feature],
|
||||||
|
children=_mocked_strip_children(features=[feature]),
|
||||||
|
device_type=DeviceType.WallSwitch,
|
||||||
|
)
|
||||||
|
with _patch_discovery(device=parent), _patch_connect(device=parent):
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
parent_device = device_registry.async_get_device_by_identifier(
|
||||||
|
(DOMAIN, parent.device_id), config_entry.entry_id
|
||||||
|
)
|
||||||
|
assert parent_device is not None
|
||||||
|
assert parent_device.via_device_id is None
|
||||||
|
|
||||||
|
assert parent.children
|
||||||
|
for child in parent.children:
|
||||||
|
# WallSwitch children merge into the parent device via the mac connection
|
||||||
|
child_device = device_registry.async_get_device_by_identifier(
|
||||||
|
(DOMAIN, child.device_id), config_entry.entry_id
|
||||||
|
)
|
||||||
|
assert child_device is not None
|
||||||
|
assert child_device.id == parent_device.id
|
||||||
|
|||||||
Reference in New Issue
Block a user