Adjust device registry for Matter devices (#86108)
* adjust device registry * ignore test unique id * update test * ditch uniqueid + prefix serial * adjust test * add tests * fix switch test * prefix all identifiers * Update homeassistant/components/matter/adapter.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * no underscore in id * fix test Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
5e6ba594aa
commit
4bebf00598
7 changed files with 57 additions and 21 deletions
|
@ -17,7 +17,7 @@ from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DOMAIN, LOGGER
|
from .const import DOMAIN, ID_TYPE_DEVICE_ID, ID_TYPE_SERIAL, LOGGER
|
||||||
from .device_platform import DEVICE_PLATFORM
|
from .device_platform import DEVICE_PLATFORM
|
||||||
from .helpers import get_device_id
|
from .helpers import get_device_id
|
||||||
|
|
||||||
|
@ -91,10 +91,7 @@ class MatterAdapter:
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Create a device registry entry."""
|
"""Create a device registry entry."""
|
||||||
server_info = cast(ServerInfo, self.matter_client.server_info)
|
server_info = cast(ServerInfo, self.matter_client.server_info)
|
||||||
node_unique_id = get_device_id(
|
|
||||||
server_info,
|
|
||||||
node_device,
|
|
||||||
)
|
|
||||||
basic_info = node_device.device_info()
|
basic_info = node_device.device_info()
|
||||||
device_type_instances = node_device.device_type_instances()
|
device_type_instances = node_device.device_type_instances()
|
||||||
|
|
||||||
|
@ -103,16 +100,23 @@ class MatterAdapter:
|
||||||
# fallback name for Bridge
|
# fallback name for Bridge
|
||||||
name = "Hub device"
|
name = "Hub device"
|
||||||
elif not name and device_type_instances:
|
elif not name and device_type_instances:
|
||||||
# fallback name based on device type
|
# use the productName if no node label is present
|
||||||
name = (
|
name = basic_info.productName
|
||||||
f"{device_type_instances[0].device_type.__doc__[:-1]}"
|
|
||||||
f" {node_device.node().node_id}"
|
node_device_id = get_device_id(
|
||||||
)
|
server_info,
|
||||||
|
node_device,
|
||||||
|
)
|
||||||
|
identifiers = {(DOMAIN, f"{ID_TYPE_DEVICE_ID}_{node_device_id}")}
|
||||||
|
# if available, we also add the serialnumber as identifier
|
||||||
|
if basic_info.serialNumber and "test" not in basic_info.serialNumber.lower():
|
||||||
|
# prefix identifier with 'serial_' to be able to filter it
|
||||||
|
identifiers.add((DOMAIN, f"{ID_TYPE_SERIAL}_{basic_info.serialNumber}"))
|
||||||
|
|
||||||
dr.async_get(self.hass).async_get_or_create(
|
dr.async_get(self.hass).async_get_or_create(
|
||||||
name=name,
|
name=name,
|
||||||
config_entry_id=self.config_entry.entry_id,
|
config_entry_id=self.config_entry.entry_id,
|
||||||
identifiers={(DOMAIN, node_unique_id)},
|
identifiers=identifiers,
|
||||||
hw_version=basic_info.hardwareVersionString,
|
hw_version=basic_info.hardwareVersionString,
|
||||||
sw_version=basic_info.softwareVersionString,
|
sw_version=basic_info.softwareVersionString,
|
||||||
manufacturer=basic_info.vendorName,
|
manufacturer=basic_info.vendorName,
|
||||||
|
|
|
@ -8,3 +8,7 @@ CONF_USE_ADDON = "use_addon"
|
||||||
|
|
||||||
DOMAIN = "matter"
|
DOMAIN = "matter"
|
||||||
LOGGER = logging.getLogger(__package__)
|
LOGGER = logging.getLogger(__package__)
|
||||||
|
|
||||||
|
# prefixes to identify device identifier id types
|
||||||
|
ID_TYPE_DEVICE_ID = "deviceid"
|
||||||
|
ID_TYPE_SERIAL = "serial"
|
||||||
|
|
|
@ -15,7 +15,7 @@ from matter_server.common.models.server_information import ServerInfo
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription
|
from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN, ID_TYPE_DEVICE_ID
|
||||||
from .helpers import get_device_id, get_operational_instance_id
|
from .helpers import get_device_id, get_operational_instance_id
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -68,8 +68,9 @@ class MatterEntity(Entity):
|
||||||
f"{device_type_instance.endpoint}-"
|
f"{device_type_instance.endpoint}-"
|
||||||
f"{device_type_instance.device_type.device_type}"
|
f"{device_type_instance.device_type.device_type}"
|
||||||
)
|
)
|
||||||
|
node_device_id = get_device_id(server_info, node_device)
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, get_device_id(server_info, node_device))}
|
identifiers={(DOMAIN, f"{ID_TYPE_DEVICE_ID}_{node_device_id}")}
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
|
|
|
@ -175,7 +175,7 @@
|
||||||
"attribute_id": 5,
|
"attribute_id": 5,
|
||||||
"attribute_type": "chip.clusters.Objects.Basic.Attributes.NodeLabel",
|
"attribute_type": "chip.clusters.Objects.Basic.Attributes.NodeLabel",
|
||||||
"attribute_name": "NodeLabel",
|
"attribute_name": "NodeLabel",
|
||||||
"value": "Mock OnOff Plugin Unit"
|
"value": ""
|
||||||
},
|
},
|
||||||
"0/40/6": {
|
"0/40/6": {
|
||||||
"node_id": 1,
|
"node_id": 1,
|
||||||
|
|
|
@ -469,7 +469,7 @@
|
||||||
"attribute_id": 15,
|
"attribute_id": 15,
|
||||||
"attribute_type": "chip.clusters.Objects.Basic.Attributes.SerialNumber",
|
"attribute_type": "chip.clusters.Objects.Basic.Attributes.SerialNumber",
|
||||||
"attribute_name": "SerialNumber",
|
"attribute_name": "SerialNumber",
|
||||||
"value": "TEST_SN"
|
"value": "12345678"
|
||||||
},
|
},
|
||||||
"0/40/16": {
|
"0/40/16": {
|
||||||
"node_id": 1,
|
"node_id": 1,
|
||||||
|
|
|
@ -28,10 +28,13 @@ async def test_device_registry_single_node_device(
|
||||||
|
|
||||||
dev_reg = dr.async_get(hass)
|
dev_reg = dr.async_get(hass)
|
||||||
entry = dev_reg.async_get_device(
|
entry = dev_reg.async_get_device(
|
||||||
{(DOMAIN, "00000000000004D2-0000000000000001-MatterNodeDevice")}
|
{(DOMAIN, "deviceid_00000000000004D2-0000000000000001-MatterNodeDevice")}
|
||||||
)
|
)
|
||||||
assert entry is not None
|
assert entry is not None
|
||||||
|
|
||||||
|
# test serial id present as additional identifier
|
||||||
|
assert (DOMAIN, "serial_12345678") in entry.identifiers
|
||||||
|
|
||||||
assert entry.name == "Mock OnOff Light"
|
assert entry.name == "Mock OnOff Light"
|
||||||
assert entry.manufacturer == "Nabu Casa"
|
assert entry.manufacturer == "Nabu Casa"
|
||||||
assert entry.model == "Mock Light"
|
assert entry.model == "Mock Light"
|
||||||
|
@ -39,6 +42,30 @@ async def test_device_registry_single_node_device(
|
||||||
assert entry.sw_version == "v1.0"
|
assert entry.sw_version == "v1.0"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_registry_single_node_device_alt(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
matter_client: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test additional device with different attribute values."""
|
||||||
|
await setup_integration_with_node_fixture(
|
||||||
|
hass,
|
||||||
|
"on-off-plugin-unit",
|
||||||
|
matter_client,
|
||||||
|
)
|
||||||
|
|
||||||
|
dev_reg = dr.async_get(hass)
|
||||||
|
entry = dev_reg.async_get_device(
|
||||||
|
{(DOMAIN, "deviceid_00000000000004D2-0000000000000001-MatterNodeDevice")}
|
||||||
|
)
|
||||||
|
assert entry is not None
|
||||||
|
|
||||||
|
# test name is derived from productName (because nodeLabel is absent)
|
||||||
|
assert entry.name == "Mock OnOffPluginUnit (powerplug/switch)"
|
||||||
|
|
||||||
|
# test serial id NOT present as additional identifier
|
||||||
|
assert (DOMAIN, "serial_TEST_SN") not in entry.identifiers
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip("Waiting for a new test fixture")
|
@pytest.mark.skip("Waiting for a new test fixture")
|
||||||
async def test_device_registry_bridge(
|
async def test_device_registry_bridge(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|
|
@ -30,7 +30,7 @@ async def test_turn_on(
|
||||||
switch_node: MatterNode,
|
switch_node: MatterNode,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test turning on a switch."""
|
"""Test turning on a switch."""
|
||||||
state = hass.states.get("switch.mock_onoff_plugin_unit")
|
state = hass.states.get("switch.mock_onoffpluginunit_powerplug_switch")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == "off"
|
assert state.state == "off"
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ async def test_turn_on(
|
||||||
"switch",
|
"switch",
|
||||||
"turn_on",
|
"turn_on",
|
||||||
{
|
{
|
||||||
"entity_id": "switch.mock_onoff_plugin_unit",
|
"entity_id": "switch.mock_onoffpluginunit_powerplug_switch",
|
||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
@ -53,7 +53,7 @@ async def test_turn_on(
|
||||||
set_node_attribute(switch_node, 1, 6, 0, True)
|
set_node_attribute(switch_node, 1, 6, 0, True)
|
||||||
await trigger_subscription_callback(hass, matter_client)
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
|
|
||||||
state = hass.states.get("switch.mock_onoff_plugin_unit")
|
state = hass.states.get("switch.mock_onoffpluginunit_powerplug_switch")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == "on"
|
assert state.state == "on"
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ async def test_turn_off(
|
||||||
switch_node: MatterNode,
|
switch_node: MatterNode,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test turning off a switch."""
|
"""Test turning off a switch."""
|
||||||
state = hass.states.get("switch.mock_onoff_plugin_unit")
|
state = hass.states.get("switch.mock_onoffpluginunit_powerplug_switch")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == "off"
|
assert state.state == "off"
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ async def test_turn_off(
|
||||||
"switch",
|
"switch",
|
||||||
"turn_off",
|
"turn_off",
|
||||||
{
|
{
|
||||||
"entity_id": "switch.mock_onoff_plugin_unit",
|
"entity_id": "switch.mock_onoffpluginunit_powerplug_switch",
|
||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue