1313from datetime import datetime
1414from functools import cached_property , lru_cache
1515import logging
16+ import re
1617import secrets
1718import time
1819from typing import TYPE_CHECKING , Any , cast
128129 0 , Clusters .IcdManagement .Attributes .AttributeList
129130)
130131
132+ RE_MDNS_SERVICE_NAME = re .compile (
133+ rf"^([0-9A-Fa-f]{{16}})-([0-9A-Fa-f]{{16}})\.{ re .escape (MDNS_TYPE_OPERATIONAL_NODE )} $"
134+ )
135+
131136
132137# pylint: disable=too-many-lines,too-many-instance-attributes,too-many-public-methods
133138
@@ -152,7 +157,6 @@ def __init__(
152157 # we keep the last events in memory so we can include them in the diagnostics dump
153158 self .event_history : deque [Attribute .EventReadResult ] = deque (maxlen = 25 )
154159 self ._compressed_fabric_id : int | None = None
155- self ._fabric_id_hex : str | None = None
156160 self ._wifi_credentials_set : bool = False
157161 self ._thread_credentials_set : bool = False
158162 self ._setup_node_tasks = dict [int , asyncio .Task ]()
@@ -179,7 +183,6 @@ async def initialize(self) -> None:
179183 self ._compressed_fabric_id = (
180184 await self ._chip_device_controller .get_compressed_fabric_id ()
181185 )
182- self ._fabric_id_hex = hex (self ._compressed_fabric_id )[2 :]
183186 await load_local_updates (self ._ota_provider_dir )
184187
185188 async def start (self ) -> None :
@@ -245,8 +248,10 @@ async def stop(self) -> None:
245248 LOGGER .debug ("Stopped." )
246249
247250 @property
248- def compressed_fabric_id (self ) -> int | None :
251+ def compressed_fabric_id (self ) -> int :
249252 """Return the compressed fabric id."""
253+ if self ._compressed_fabric_id is None :
254+ raise RuntimeError ("Compressed Fabric ID not set" )
250255 return self ._compressed_fabric_id
251256
252257 @property
@@ -1524,25 +1529,36 @@ def _on_mdns_service_state_change(
15241529 )
15251530 return
15261531
1527- if service_type == MDNS_TYPE_OPERATIONAL_NODE :
1528- if self ._fabric_id_hex is None or self ._fabric_id_hex not in name .lower ():
1529- # filter out messages that are not for our fabric
1530- return
1531- # process the event with a debounce timer
1532+ if service_type != MDNS_TYPE_OPERATIONAL_NODE :
1533+ return
1534+
1535+ if not (match := RE_MDNS_SERVICE_NAME .match (name )):
1536+ LOGGER .getChild ("mdns" ).warning (
1537+ "Service name doesn't match expected operational node pattern: %s" , name
1538+ )
1539+ return
1540+
1541+ fabric_id_hex , node_id_hex = match .groups ()
1542+
1543+ # Filter messages of other fabrics
1544+ if int (fabric_id_hex , 16 ) != self .compressed_fabric_id :
1545+ return
1546+
1547+ # Process the event with a debounce timer
15321548 self ._mdns_event_timer [name ] = self ._loop .call_later (
1533- 0.5 , self ._on_mdns_operational_node_state , name , state_change
1549+ 0.5 ,
1550+ self ._on_mdns_operational_node_state ,
1551+ name ,
1552+ int (node_id_hex , 16 ),
1553+ state_change ,
15341554 )
15351555
15361556 def _on_mdns_operational_node_state (
1537- self , name : str , state_change : ServiceStateChange
1557+ self , name : str , node_id : int , state_change : ServiceStateChange
15381558 ) -> None :
15391559 """Handle a (operational) Matter node MDNS state change."""
15401560 self ._mdns_event_timer .pop (name , None )
1541- logger = LOGGER .getChild ("mdns" )
1542- # the mdns name is constructed as [fabricid]-[nodeid]._matter._tcp.local.
1543- # extract the node id from the name
1544- node_id = int (name .split ("-" )[1 ].split ("." )[0 ], 16 )
1545- node_logger = self .get_node_logger (logger , node_id )
1561+ node_logger = self .get_node_logger (LOGGER .getChild ("mdns" ), node_id )
15461562
15471563 if not (node := self ._nodes .get (node_id )):
15481564 return # this should not happen, but guard just in case
0 commit comments