mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Add unconfigured flag to thread discovery data (#89230)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
9e1ba8534a
commit
fde205c158
@ -6,6 +6,7 @@ import dataclasses
|
||||
import logging
|
||||
from typing import cast
|
||||
|
||||
from python_otbr_api.mdns import StateBitmap
|
||||
from zeroconf import BadTypeInNameException, DNSPointer, ServiceListener, Zeroconf
|
||||
from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf
|
||||
|
||||
@ -29,14 +30,15 @@ TYPE_PTR = 12
|
||||
class ThreadRouterDiscoveryData:
|
||||
"""Thread router discovery data."""
|
||||
|
||||
addresses: list[str] | None
|
||||
brand: str | None
|
||||
extended_pan_id: str | None
|
||||
model_name: str | None
|
||||
network_name: str | None
|
||||
server: str | None
|
||||
vendor_name: str | None
|
||||
addresses: list[str] | None
|
||||
thread_version: str | None
|
||||
unconfigured: bool | None
|
||||
vendor_name: str | None
|
||||
|
||||
|
||||
def async_discovery_data_from_service(
|
||||
@ -59,15 +61,30 @@ def async_discovery_data_from_service(
|
||||
server = service.server
|
||||
vendor_name = try_decode(service.properties.get(b"vn"))
|
||||
thread_version = try_decode(service.properties.get(b"tv"))
|
||||
unconfigured = None
|
||||
brand = KNOWN_BRANDS.get(vendor_name)
|
||||
if brand == "homeassistant":
|
||||
# Attempt to detect incomplete configuration
|
||||
if (state_bitmap_b := service.properties.get(b"sb")) is not None:
|
||||
try:
|
||||
state_bitmap = StateBitmap.from_bytes(state_bitmap_b)
|
||||
if not state_bitmap.is_active:
|
||||
unconfigured = True
|
||||
except ValueError:
|
||||
_LOGGER.debug("Failed to decode state bitmap in service %s", service)
|
||||
if service.properties.get(b"at") is None:
|
||||
unconfigured = True
|
||||
|
||||
return ThreadRouterDiscoveryData(
|
||||
brand=KNOWN_BRANDS.get(vendor_name),
|
||||
addresses=service.parsed_addresses(),
|
||||
brand=brand,
|
||||
extended_pan_id=ext_pan_id.hex() if ext_pan_id is not None else None,
|
||||
model_name=model_name,
|
||||
network_name=network_name,
|
||||
server=server,
|
||||
vendor_name=vendor_name,
|
||||
addresses=service.parsed_addresses(),
|
||||
thread_version=thread_version,
|
||||
unconfigured=unconfigured,
|
||||
vendor_name=vendor_name,
|
||||
)
|
||||
|
||||
|
||||
|
@ -185,3 +185,109 @@ ROUTER_DISCOVERY_HASS_MISSING_MANDATORY_DATA = {
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
||||
|
||||
ROUTER_DISCOVERY_HASS_NO_ACTIVE_TIMESTAMP = {
|
||||
"type_": "_meshcop._udp.local.",
|
||||
"name": "HomeAssistant OpenThreadBorderRouter #0BBF._meshcop._udp.local.",
|
||||
"addresses": [b"\xc0\xa8\x00s"],
|
||||
"port": 49153,
|
||||
"weight": 0,
|
||||
"priority": 0,
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"properties": {
|
||||
b"rv": b"1",
|
||||
b"vn": b"HomeAssistant",
|
||||
b"mn": b"OpenThreadBorderRouter",
|
||||
b"nn": b"OpenThread HC",
|
||||
b"xp": b"\xe6\x0f\xc7\xc1\x86!,\xe5",
|
||||
b"tv": b"1.3.0",
|
||||
b"xa": b"\xae\xeb/YKW\x0b\xbf",
|
||||
b"sb": b"\x00\x00\x01\xb1",
|
||||
b"pt": b"\x8f\x06Q~",
|
||||
b"sq": b"3",
|
||||
b"bb": b"\xf0\xbf",
|
||||
b"dn": b"DefaultDomain",
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
||||
|
||||
ROUTER_DISCOVERY_HASS_NO_STATE_BITMAP = {
|
||||
"type_": "_meshcop._udp.local.",
|
||||
"name": "HomeAssistant OpenThreadBorderRouter #0BBF._meshcop._udp.local.",
|
||||
"addresses": [b"\xc0\xa8\x00s"],
|
||||
"port": 49153,
|
||||
"weight": 0,
|
||||
"priority": 0,
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"properties": {
|
||||
b"rv": b"1",
|
||||
b"vn": b"HomeAssistant",
|
||||
b"mn": b"OpenThreadBorderRouter",
|
||||
b"nn": b"OpenThread HC",
|
||||
b"xp": b"\xe6\x0f\xc7\xc1\x86!,\xe5",
|
||||
b"tv": b"1.3.0",
|
||||
b"xa": b"\xae\xeb/YKW\x0b\xbf",
|
||||
b"at": b"\x00\x00\x00\x00\x00\x01\x00\x00",
|
||||
b"pt": b"\x8f\x06Q~",
|
||||
b"sq": b"3",
|
||||
b"bb": b"\xf0\xbf",
|
||||
b"dn": b"DefaultDomain",
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
||||
|
||||
ROUTER_DISCOVERY_HASS_BAD_STATE_BITMAP = {
|
||||
"type_": "_meshcop._udp.local.",
|
||||
"name": "HomeAssistant OpenThreadBorderRouter #0BBF._meshcop._udp.local.",
|
||||
"addresses": [b"\xc0\xa8\x00s"],
|
||||
"port": 49153,
|
||||
"weight": 0,
|
||||
"priority": 0,
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"properties": {
|
||||
b"rv": b"1",
|
||||
b"vn": b"HomeAssistant",
|
||||
b"mn": b"OpenThreadBorderRouter",
|
||||
b"nn": b"OpenThread HC",
|
||||
b"xp": b"\xe6\x0f\xc7\xc1\x86!,\xe5",
|
||||
b"tv": b"1.3.0",
|
||||
b"xa": b"\xae\xeb/YKW\x0b\xbf",
|
||||
b"sb": b"\xff\x00\x01\xb1",
|
||||
b"at": b"\x00\x00\x00\x00\x00\x01\x00\x00",
|
||||
b"pt": b"\x8f\x06Q~",
|
||||
b"sq": b"3",
|
||||
b"bb": b"\xf0\xbf",
|
||||
b"dn": b"DefaultDomain",
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
||||
|
||||
ROUTER_DISCOVERY_HASS_STATE_BITMAP_NOT_ACTIVE = {
|
||||
"type_": "_meshcop._udp.local.",
|
||||
"name": "HomeAssistant OpenThreadBorderRouter #0BBF._meshcop._udp.local.",
|
||||
"addresses": [b"\xc0\xa8\x00s"],
|
||||
"port": 49153,
|
||||
"weight": 0,
|
||||
"priority": 0,
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"properties": {
|
||||
b"rv": b"1",
|
||||
b"vn": b"HomeAssistant",
|
||||
b"mn": b"OpenThreadBorderRouter",
|
||||
b"nn": b"OpenThread HC",
|
||||
b"xp": b"\xe6\x0f\xc7\xc1\x86!,\xe5",
|
||||
b"tv": b"1.3.0",
|
||||
b"xa": b"\xae\xeb/YKW\x0b\xbf",
|
||||
b"sb": b"\x00\x00\x01\x31",
|
||||
b"at": b"\x00\x00\x00\x00\x00\x01\x00\x00",
|
||||
b"pt": b"\x8f\x06Q~",
|
||||
b"sq": b"3",
|
||||
b"bb": b"\xf0\xbf",
|
||||
b"dn": b"DefaultDomain",
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
@ -14,8 +14,12 @@ from . import (
|
||||
ROUTER_DISCOVERY_GOOGLE_1,
|
||||
ROUTER_DISCOVERY_HASS,
|
||||
ROUTER_DISCOVERY_HASS_BAD_DATA,
|
||||
ROUTER_DISCOVERY_HASS_BAD_STATE_BITMAP,
|
||||
ROUTER_DISCOVERY_HASS_MISSING_DATA,
|
||||
ROUTER_DISCOVERY_HASS_MISSING_MANDATORY_DATA,
|
||||
ROUTER_DISCOVERY_HASS_NO_ACTIVE_TIMESTAMP,
|
||||
ROUTER_DISCOVERY_HASS_NO_STATE_BITMAP,
|
||||
ROUTER_DISCOVERY_HASS_STATE_BITMAP_NOT_ACTIVE,
|
||||
)
|
||||
|
||||
|
||||
@ -67,14 +71,15 @@ async def test_discover_routers(hass: HomeAssistant, mock_async_zeroconf: None)
|
||||
assert discovered[-1] == (
|
||||
"aeeb2f594b570bbf",
|
||||
discovery.ThreadRouterDiscoveryData(
|
||||
addresses=["192.168.0.115"],
|
||||
brand="homeassistant",
|
||||
extended_pan_id="e60fc7c186212ce5",
|
||||
model_name="OpenThreadBorderRouter",
|
||||
network_name="OpenThread HC",
|
||||
server="core-silabs-multiprotocol.local.",
|
||||
vendor_name="HomeAssistant",
|
||||
thread_version="1.3.0",
|
||||
addresses=["192.168.0.115"],
|
||||
unconfigured=None,
|
||||
vendor_name="HomeAssistant",
|
||||
),
|
||||
)
|
||||
|
||||
@ -91,14 +96,15 @@ async def test_discover_routers(hass: HomeAssistant, mock_async_zeroconf: None)
|
||||
assert discovered[-1] == (
|
||||
"f6a99b425a67abed",
|
||||
discovery.ThreadRouterDiscoveryData(
|
||||
addresses=["192.168.0.124"],
|
||||
brand="google",
|
||||
extended_pan_id="9e75e256f61409a3",
|
||||
model_name="Google Nest Hub",
|
||||
network_name="NEST-PAN-E1AF",
|
||||
server="2d99f293-cd8e-2770-8dd2-6675de9fa000.local.",
|
||||
vendor_name="Google Inc.",
|
||||
thread_version="1.3.0",
|
||||
addresses=["192.168.0.124"],
|
||||
unconfigured=None,
|
||||
vendor_name="Google Inc.",
|
||||
),
|
||||
)
|
||||
|
||||
@ -130,6 +136,56 @@ async def test_discover_routers(hass: HomeAssistant, mock_async_zeroconf: None)
|
||||
mock_async_zeroconf.async_remove_service_listener.assert_called_once_with(listener)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("data", "unconfigured"),
|
||||
[
|
||||
(ROUTER_DISCOVERY_HASS_NO_ACTIVE_TIMESTAMP, True),
|
||||
(ROUTER_DISCOVERY_HASS_BAD_STATE_BITMAP, None),
|
||||
(ROUTER_DISCOVERY_HASS_NO_STATE_BITMAP, None),
|
||||
(ROUTER_DISCOVERY_HASS_STATE_BITMAP_NOT_ACTIVE, True),
|
||||
],
|
||||
)
|
||||
async def test_discover_routers_unconfigured(
|
||||
hass: HomeAssistant, mock_async_zeroconf: None, data, unconfigured
|
||||
) -> None:
|
||||
"""Test discovering thread routers with bad or missing vendor mDNS data."""
|
||||
mock_async_zeroconf.async_add_service_listener = AsyncMock()
|
||||
mock_async_zeroconf.async_remove_service_listener = AsyncMock()
|
||||
mock_async_zeroconf.async_get_service_info = AsyncMock()
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Start Thread router discovery
|
||||
router_discovered_removed = Mock()
|
||||
thread_disovery = discovery.ThreadRouterDiscovery(
|
||||
hass, router_discovered_removed, router_discovered_removed
|
||||
)
|
||||
await thread_disovery.async_start()
|
||||
listener: discovery.ThreadRouterDiscovery.ThreadServiceListener = (
|
||||
mock_async_zeroconf.async_add_service_listener.mock_calls[0][1][1]
|
||||
)
|
||||
|
||||
# Discover a service with bad or missing data
|
||||
mock_async_zeroconf.async_get_service_info.return_value = AsyncServiceInfo(**data)
|
||||
listener.add_service(None, data["type_"], data["name"])
|
||||
await hass.async_block_till_done()
|
||||
router_discovered_removed.assert_called_once_with(
|
||||
"aeeb2f594b570bbf",
|
||||
discovery.ThreadRouterDiscoveryData(
|
||||
addresses=["192.168.0.115"],
|
||||
brand="homeassistant",
|
||||
extended_pan_id="e60fc7c186212ce5",
|
||||
model_name="OpenThreadBorderRouter",
|
||||
network_name="OpenThread HC",
|
||||
server="core-silabs-multiprotocol.local.",
|
||||
thread_version="1.3.0",
|
||||
unconfigured=unconfigured,
|
||||
vendor_name="HomeAssistant",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data", (ROUTER_DISCOVERY_HASS_BAD_DATA, ROUTER_DISCOVERY_HASS_MISSING_DATA)
|
||||
)
|
||||
@ -161,14 +217,15 @@ async def test_discover_routers_bad_data(
|
||||
router_discovered_removed.assert_called_once_with(
|
||||
"aeeb2f594b570bbf",
|
||||
discovery.ThreadRouterDiscoveryData(
|
||||
addresses=["192.168.0.115"],
|
||||
brand=None,
|
||||
extended_pan_id="e60fc7c186212ce5",
|
||||
model_name="OpenThreadBorderRouter",
|
||||
network_name="OpenThread HC",
|
||||
server="core-silabs-multiprotocol.local.",
|
||||
vendor_name=None,
|
||||
thread_version="1.3.0",
|
||||
addresses=["192.168.0.115"],
|
||||
unconfigured=None,
|
||||
vendor_name=None,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -234,14 +234,15 @@ async def test_discover_routers(
|
||||
assert msg == {
|
||||
"event": {
|
||||
"data": {
|
||||
"addresses": ["192.168.0.115"],
|
||||
"brand": "homeassistant",
|
||||
"extended_pan_id": "e60fc7c186212ce5",
|
||||
"model_name": "OpenThreadBorderRouter",
|
||||
"network_name": "OpenThread HC",
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"vendor_name": "HomeAssistant",
|
||||
"addresses": ["192.168.0.115"],
|
||||
"thread_version": "1.3.0",
|
||||
"unconfigured": None,
|
||||
"vendor_name": "HomeAssistant",
|
||||
},
|
||||
"key": "aeeb2f594b570bbf",
|
||||
"type": "router_discovered",
|
||||
@ -261,14 +262,15 @@ async def test_discover_routers(
|
||||
assert msg == {
|
||||
"event": {
|
||||
"data": {
|
||||
"addresses": ["192.168.0.124"],
|
||||
"brand": "google",
|
||||
"extended_pan_id": "9e75e256f61409a3",
|
||||
"model_name": "Google Nest Hub",
|
||||
"network_name": "NEST-PAN-E1AF",
|
||||
"server": "2d99f293-cd8e-2770-8dd2-6675de9fa000.local.",
|
||||
"vendor_name": "Google Inc.",
|
||||
"thread_version": "1.3.0",
|
||||
"addresses": ["192.168.0.124"],
|
||||
"unconfigured": None,
|
||||
"vendor_name": "Google Inc.",
|
||||
},
|
||||
"key": "f6a99b425a67abed",
|
||||
"type": "router_discovered",
|
||||
|
Loading…
x
Reference in New Issue
Block a user