Fix for deCONZ issue - Detected that integration 'deconz' calls device_registry.async_get_or_create referencing a non existing via_device - #134539 (#150355)

pull/151624/head
mattreim 2025-09-03 14:28:52 +02:00 committed by GitHub
parent 0e1dd04083
commit 1369a98fa3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 27 additions and 42 deletions

View File

@ -177,7 +177,7 @@ class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
"""Return a device description for device registry."""
return DeviceInfo(
identifiers={(DOMAIN, self._group_identifier)},
manufacturer="Dresden Elektronik",
manufacturer="dresden elektronik",
model="deCONZ group",
name=self.group.name,
via_device=(DOMAIN, self.hub.api.config.bridge_id),

View File

@ -14,7 +14,6 @@ from pydeconz.models.event import EventType
from homeassistant.config_entries import SOURCE_HASSIO
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.dispatcher import async_dispatcher_send
from ..const import CONF_MASTER_GATEWAY, DOMAIN, HASSIO_CONFIGURATION_URL, PLATFORMS
@ -169,17 +168,8 @@ class DeconzHub:
async def async_update_device_registry(self) -> None:
"""Update device registry."""
if self.api.config.mac is None:
return
device_registry = dr.async_get(self.hass)
# Host device
device_registry.async_get_or_create(
config_entry_id=self.config_entry.entry_id,
connections={(CONNECTION_NETWORK_MAC, self.api.config.mac)},
)
# Gateway service
configuration_url = f"http://{self.config.host}:{self.config.port}"
if self.config_entry.source == SOURCE_HASSIO:
@ -189,11 +179,10 @@ class DeconzHub:
configuration_url=configuration_url,
entry_type=dr.DeviceEntryType.SERVICE,
identifiers={(DOMAIN, self.api.config.bridge_id)},
manufacturer="Dresden Elektronik",
manufacturer="dresden elektronik",
model=self.api.config.model_id,
name=self.api.config.name,
sw_version=self.api.config.software_version,
via_device=(CONNECTION_NETWORK_MAC, self.api.config.mac),
)
@staticmethod

View File

@ -396,7 +396,7 @@ class DeconzGroup(DeconzBaseLight[Group]):
"""Return a device description for device registry."""
return DeviceInfo(
identifiers={(DOMAIN, self.unique_id)},
manufacturer="Dresden Elektronik",
manufacturer="dresden elektronik",
model="deCONZ group",
name=self._device.name,
via_device=(DOMAIN, self.hub.api.config.bridge_id),

View File

@ -11,7 +11,6 @@ from homeassistant.helpers import (
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.util.read_only_dict import ReadOnlyDict
from .const import CONF_BRIDGE_ID, DOMAIN, LOGGER
@ -120,8 +119,8 @@ async def async_configure_service(hub: DeconzHub, data: ReadOnlyDict) -> None:
"field": "/lights/1/state",
"data": {"on": true}
}
See Dresden Elektroniks REST API documentation for details:
http://dresden-elektronik.github.io/deconz-rest-doc/rest/
See deCONZ REST-API documentation for details:
https://dresden-elektronik.github.io/deconz-rest-doc/
"""
field = data.get(SERVICE_FIELD, "")
entity_id = data.get(SERVICE_ENTITY)
@ -162,14 +161,6 @@ async def async_remove_orphaned_entries_service(hub: DeconzHub) -> None:
)
]
# Don't remove the Gateway host entry
if hub.api.config.mac:
hub_host = device_registry.async_get_device(
connections={(CONNECTION_NETWORK_MAC, hub.api.config.mac)},
)
if hub_host and hub_host.id in devices_to_be_removed:
devices_to_be_removed.remove(hub_host.id)
# Don't remove the Gateway service entry
hub_service = device_registry.async_get_device(
identifiers={(DOMAIN, hub.api.config.bridge_id)}

View File

@ -19,7 +19,7 @@
}),
'labels': set({
}),
'manufacturer': 'Dresden Elektronik',
'manufacturer': 'dresden elektronik',
'model': 'deCONZ',
'model_id': None,
'name': 'deCONZ mock gateway',

View File

@ -76,14 +76,14 @@ async def test_deconz_events(
) -> None:
"""Test successful creation of deconz events."""
assert len(hass.states.async_all()) == 3
# 5 switches + 2 additional devices for deconz service and host
# 5 switches + 1 additional device for deconz gateway
assert (
len(
dr.async_entries_for_config_entry(
device_registry, config_entry_setup.entry_id
)
)
== 7
== 6
)
assert hass.states.get("sensor.switch_2_battery").state == "100"
assert hass.states.get("sensor.switch_3_battery").state == "100"
@ -233,14 +233,14 @@ async def test_deconz_alarm_events(
) -> None:
"""Test successful creation of deconz alarm events."""
assert len(hass.states.async_all()) == 4
# 1 alarm control device + 2 additional devices for deconz service and host
# 1 alarm control device + 1 additional device for deconz gateway
assert (
len(
dr.async_entries_for_config_entry(
device_registry, config_entry_setup.entry_id
)
)
== 3
== 2
)
captured_events = async_capture_events(hass, CONF_DECONZ_ALARM_EVENT)
@ -362,7 +362,7 @@ async def test_deconz_presence_events(
device_registry, config_entry_setup.entry_id
)
)
== 3
== 2
)
device = device_registry.async_get_device(
@ -439,7 +439,7 @@ async def test_deconz_relative_rotary_events(
device_registry, config_entry_setup.entry_id
)
)
== 3
== 2
)
device = device_registry.async_get_device(
@ -508,5 +508,5 @@ async def test_deconz_events_bad_unique_id(
device_registry, config_entry_setup.entry_id
)
)
== 2
== 1
)

View File

@ -56,7 +56,7 @@ async def test_configure_service_with_field(
{
"name": "Test",
"state": {"reachable": True},
"type": "Light",
"type": "Dimmable light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
],
@ -85,7 +85,7 @@ async def test_configure_service_with_entity(
{
"name": "Test",
"state": {"reachable": True},
"type": "Light",
"type": "Dimmable light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
],
@ -204,7 +204,7 @@ async def test_service_refresh_devices(
"1": {
"name": "Light 1 name",
"state": {"reachable": True},
"type": "Light",
"type": "Dimmable light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
},
@ -270,7 +270,7 @@ async def test_service_refresh_devices_trigger_no_state_update(
"1": {
"name": "Light 1 name",
"state": {"reachable": True},
"type": "Light",
"type": "Dimmable light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
},
@ -301,7 +301,7 @@ async def test_service_refresh_devices_trigger_no_state_update(
{
"name": "Light 0 name",
"state": {"reachable": True},
"type": "Light",
"type": "Dimmable light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
],
@ -327,7 +327,12 @@ async def test_remove_orphaned_entries_service(
"""Test service works and also don't remove more than expected."""
device = device_registry.async_get_or_create(
config_entry_id=config_entry_setup.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, "123")},
identifiers={(DOMAIN, BRIDGE_ID)},
)
device_registry.async_get_or_create(
config_entry_id=config_entry_setup.entry_id,
identifiers={(DOMAIN, "orphaned")},
)
assert (
@ -338,7 +343,7 @@ async def test_remove_orphaned_entries_service(
if config_entry_setup.entry_id in entry.config_entries
]
)
== 5 # Host, gateway, light, switch and orphan
== 4 # Gateway, light, switch and orphan
)
entity_registry.async_get_or_create(
@ -374,7 +379,7 @@ async def test_remove_orphaned_entries_service(
if config_entry_setup.entry_id in entry.config_entries
]
)
== 4 # Host, gateway, light and switch
== 3 # Gateway, light and switch
)
assert (

View File

@ -1,4 +1,4 @@
"""deCONZ service tests."""
"""UniFi service tests."""
from typing import Any
from unittest.mock import PropertyMock, patch