Add helper method to get matter device info (#103765)

* Add helper method to get matter device info

* Cleanup async

* Guard

* get node from device id instead of node id

* Fix test

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Marcel van der Veldt 2023-11-10 19:43:54 +01:00 committed by GitHub
parent e157206eeb
commit ebdd2daab6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 18 deletions

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
from contextlib import suppress
from functools import cache
from matter_server.client import MatterClient
from matter_server.client.exceptions import CannotConnect, InvalidServerVersion
@ -28,12 +29,34 @@ from .addon import get_addon_manager
from .api import async_register_api
from .const import CONF_INTEGRATION_CREATED_ADDON, CONF_USE_ADDON, DOMAIN, LOGGER
from .discovery import SUPPORTED_PLATFORMS
from .helpers import MatterEntryData, get_matter, get_node_from_device_entry
from .helpers import (
MatterEntryData,
get_matter,
get_node_from_device_entry,
node_from_ha_device_id,
)
from .models import MatterDeviceInfo
CONNECT_TIMEOUT = 10
LISTEN_READY_TIMEOUT = 30
@callback
@cache
def get_matter_device_info(
hass: HomeAssistant, device_id: str
) -> MatterDeviceInfo | None:
"""Return Matter device info or None if device does not exist."""
if not (node := node_from_ha_device_id(hass, device_id)):
return None
return MatterDeviceInfo(
unique_id=node.device_info.uniqueID,
vendor_id=hex(node.device_info.vendorID),
product_id=hex(node.device_info.productID),
)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Matter from a config entry."""
if use_addon := entry.data.get(CONF_USE_ADDON):
@ -190,7 +213,7 @@ async def async_remove_config_entry_device(
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry
) -> bool:
"""Remove a config entry from a device."""
node = await get_node_from_device_entry(hass, device_entry)
node = get_node_from_device_entry(hass, device_entry)
if node is None:
return True
@ -218,21 +241,11 @@ async def async_remove_config_entry_device(
def _async_init_services(hass: HomeAssistant) -> None:
"""Init services."""
async def _node_id_from_ha_device_id(ha_device_id: str) -> int | None:
"""Get node id from ha device id."""
dev_reg = dr.async_get(hass)
device = dev_reg.async_get(ha_device_id)
if device is None:
return None
if node := await get_node_from_device_entry(hass, device):
return node.node_id
return None
async def open_commissioning_window(call: ServiceCall) -> None:
"""Open commissioning window on specific node."""
node_id = await _node_id_from_ha_device_id(call.data["device_id"])
node = node_from_ha_device_id(hass, call.data["device_id"])
if node_id is None:
if node is None:
raise HomeAssistantError("This is not a Matter device")
matter_client = get_matter(hass).matter_client
@ -240,7 +253,7 @@ def _async_init_services(hass: HomeAssistant) -> None:
# We are sending device ID .
try:
await matter_client.open_commissioning_window(node_id)
await matter_client.open_commissioning_window(node.node_id)
except NodeCommissionFailed as err:
raise HomeAssistantError(str(err)) from err

View File

@ -58,7 +58,7 @@ async def async_get_device_diagnostics(
"""Return diagnostics for a device."""
matter = get_matter(hass)
server_diagnostics = await matter.matter_client.get_diagnostics()
node = await get_node_from_device_entry(hass, device)
node = get_node_from_device_entry(hass, device)
return {
"server_info": dataclass_to_dict(server_diagnostics.info),

View File

@ -66,7 +66,18 @@ def get_device_id(
return f"{operational_instance_id}-{postfix}"
async def get_node_from_device_entry(
@callback
def node_from_ha_device_id(hass: HomeAssistant, ha_device_id: str) -> MatterNode | None:
"""Get node id from ha device id."""
dev_reg = dr.async_get(hass)
device = dev_reg.async_get(ha_device_id)
if device is None:
raise ValueError("Invalid device ID")
return get_node_from_device_entry(hass, device)
@callback
def get_node_from_device_entry(
hass: HomeAssistant, device: dr.DeviceEntry
) -> MatterNode | None:
"""Return MatterNode from device entry."""

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import TypedDict
from chip.clusters import Objects as clusters
from chip.clusters.Objects import ClusterAttributeDescriptor
@ -16,6 +17,20 @@ SensorValueTypes = type[
]
class MatterDeviceInfo(TypedDict):
"""Dictionary with Matter Device info.
Used to send to other Matter controllers,
such as Google Home to prevent duplicated devices.
Reference: https://developers.home.google.com/matter/device-deduplication
"""
unique_id: str
vendor_id: str # vendorId hex string
product_id: str # productId hex string
@dataclass
class MatterEntityInfo:
"""Info discovered from (primary) Matter Attribute to create entity."""

View File

@ -56,7 +56,7 @@ async def test_get_node_from_device_entry(
device_registry, config_entry.entry_id
)[0]
assert device_entry
node_from_device_entry = await get_node_from_device_entry(hass, device_entry)
node_from_device_entry = get_node_from_device_entry(hass, device_entry)
assert node_from_device_entry is node