tree: 67bc7eb99cffb80b7e5a9f465b26e429754c82de [path history] [tgz]
  1. tests/
  2. utils/
  3. mdns_async_service_info.py
  4. mdns_discovery.py
  5. README.md
src/python_testing/mdns_discovery/README.md

MdnsDiscovery β€” Matter mDNS Service Discovery Tool

The MdnsDiscovery class is a utility designed to discover Matter devices on the local network using mDNS over IPv6. It facilitates service discovery for the four core Matter roles: commissioner, commissionable node, operational node, and Thread border router. Internally, it leverages the python-zeroconf library to perform mDNS lookups.

In addition to high-level service queries, the class also provides direct access to low-level DNS records such as PTR, SRV, TXT, A, and AAAA.

πŸ“ Folder Structure

πŸ“ mdns_discovery/
β”œβ”€β”€ πŸ“data_clases/                # Containers for service info and query results
β”œβ”€β”€ πŸ“enums/                      # Enums for service types and other definitions
β”œβ”€β”€ πŸ“service_listeners/          # Service listeners used during discovery sessions
β”œβ”€β”€ πŸ“tests/                      # Unit tests for assert functions and other methods
β”œβ”€β”€ πŸ“utils/                      # Utility functions: IPv6 filtering and other utils
β”œβ”€β”€ πŸ“„mdns_async_service_info.py  # Supports querying specific mDNS record types
└── πŸ“„mdns_discovery.py           # Main entry point for mDNS discovery operations

πŸ“¦ Features

Service Discovery Methods

These methods perform discovery for Matter and non-Matter services. Each one browses for mDNS advertisements and then resolves the discovered services to retrieve complete service information.

  • get_operational_services(): Discover operational Matter nodes.
  • get_commissioner_services(): Discover Matter commissioners.
  • get_commissionable_services(): Discover commissionable Matter devices.
  • get_border_router_services(): Discover Thread border routers.
  • get_all_services(): Discover services of any kind on the network.
  • discover(): The mDNS discovery engine powering the discovery methods.

Record Query Methods

These methods perform direct queries for specific DNS record types (PTR, SRV, TXT, A, AAAA) for a given service or host and return their detailed information.

  • get_ptr_records(service_type)
  • get_srv_record(service_name)
  • get_txt_record(service_name)
  • get_quada_records(hostname)

Service Type Discovery Methods

These methods identify the types of services advertised on the local network, including both non-Matter services and Matter commissionable services.

  • get_all_service_types(): Discover service types of any kind on the network.
  • get_commissionable_subtypes(): Discovers Matter commissionable subtypes advertised during an open commissioning window.

πŸ§ͺ Example Usage

Below are minimal examples that perform operational node discovery and get a SRV record respectively.

import asyncio
from mdns_discovery.mdns_discovery import MdnsDiscovery

async def main():
    mdns = MdnsDiscovery()

    # Discover operational nodes
    services = await mdns.get_operational_services()

    # Print basic info
    for service in services:
        print(f"Instance: {service.instance_name}")
        print(f"Addresses: {service.addresses}")
        print(f"Port: {service.port}")
        print("---")

asyncio.run(main())
import asyncio
from mdns_discovery.mdns_discovery import MdnsDiscovery

async def main():
    mdns = MdnsDiscovery()
    service_name = 'B7322C948581262F-0000000012344321._matter._tcp.local.'

    # Get SRV record
    srv_record = await mdns.get_srv_record(
        service_name=service_name,
        service_type=MdnsServiceType.OPERATIONAL.value,
        log_output=True
    )

    # Print Hostname
    print(f"Hostname: {srv_record.hostname}")

asyncio.run(main())

🧩 Discovery Logic (Method Flow)

πŸ”„ Service Discovery Flow Mechanics

The following flow illustrates the internal discovery and resolution process shared by all the service discovery methods including get_all_services(). The method get_commissionable_services() is used here as a representative example.

get_commissionable_services()
        β”‚
        β–Ό
   discover(query_service=True)
        β”‚
        β”œβ”€β”€ starts AsyncServiceBrowser(...)
        β”‚       └── invokes _on_service_state_change(...) for each discovered service
        β”‚              └── gathers PTR record info
        β”‚
        └── calls _query_service_info(...) to resolve service info from PTR records
                 β”œβ”€β”€ adds service listener via add_service_listener(...)
                 β”œβ”€β”€ sends query using async_request(...)
                 └── stores results in self._discovered_services
        β–Ό
returns list[MdnsServiceInfo]

πŸ”„ Record Query Flow Mechanics

The following flows illustrate the internal logic behind the record query methods. These methods query specific DNS record types directly.

🎯 get_srv_record and get_txt_record

The method get_srv_record() is used here as a representative example.

get_srv_record()
        β”‚
        β–Ό
   _query_service_info(...)
        β”œβ”€β”€ adds service listener via add_service_listener(...)
        β”œβ”€β”€ sends query using async_request(...)
        └── returns an MdnsServiceInfo object
        β–Ό
returns MdnsServiceInfo

🎯 get_quada_records

get_quada_records()
        β”‚
        β–Ό
   addr_resolver = AddressResolverIPv6(server=hostname)
        β”‚
        β”œβ”€β”€ addr_resolver.async_request(...)
        β”‚        └── performs mDNS query for AAAA records
        β”‚
        └── addr_resolver.ip_addresses_by_version(...)
                 └── extracts the resulting IPv6 addresses
        β–Ό
returns list[QuadaRecord]

🎯 get_ptr_records

get_ptr_records()
        β”‚
        β–Ό
   discover(query_service=False)
        β”‚
        └── starts AsyncServiceBrowser(...)
                └── invokes _on_service_state_change(...) for each discovered service
                       β”œβ”€β”€ gathers PTR record info
                       └── stores results in self._discovered_services
        β–Ό
returns list[PtrRecord]

πŸ”„ Service Types Discovery Flow Mechanics

The following flows illustrate the internal logic behind service type discovery. These flows identify available mDNS service types being advertised on the network, without resolving full service instance details.

🎯 get_all_service_types

get_all_service_types()
        β”‚
        β–Ό
   AsyncZeroconfServiceTypes.async_find(...)
        β”‚
        └── performs a scan for all advertised mDNS service types
        β–Ό
returns List[str]

🎯 get_commissionable_subtypes

get_commissionable_subtypes()
        β”‚
        β–Ό
   get_all_service_types()
        β”‚
        └── discovers all advertised mDNS service types
        β–Ό
filters for commissionable service subtypes
        β–Ό
returns List[str]

πŸ“‘ Zeroconf components used by the MdnsDiscovery class

These three Zeroconf components provide the core functionalities of the MdnsDiscovery class, from discovering service types to resolving a service’s full information.

ComponentFunctionResult Example
AsyncZeroconfServiceTypesReturns all the advertised service-
types on the network.
_matterd._udp.local.
_matter.
_tcp.local.
_V65521._sub._matterd._udp.local.
_IM7322C948581262F._sub._matter._tcp.local.
AsyncServiceBrowserBrowse for services of specific service-
types, returns PTR info, service-name
and service-type.
service_type”: “_matterd._udp.local.”
service_name”: “A6666A3E45CF5655._matterd._udp.local.”
instance_name”: “A6666A3E45CF5655”
async_request
(from ServiceInfo class)
Returns full-service info by service-
name (hostname, txt, addresses, ttl, etc.).
service_name”: “354D34458F15657D._matterd._udp.local.”
service_type”: “_V65521._sub._matterd._udp.local.”
instance_name”: “354D34458F15657D”
hostname”: “00155DD54A04.local.”
port”: 5550
addresses”: [“172.30.139.182”, “fe80::215:5dev:fed5:4a04”]
txt”: {“VP”: "65521+32769}“
priority“: 0
interface_index“: 2
weight“: 0
host_ttl“: 120
other_ttl": 4500

🌐 Asserts for mDNS Values

This module contains validation helpers used by mDNS discovery–related tests. Each function raises a TestFailure on failure and is designed to make the exact failing constraint obvious.

You can import them from the following file: mdns_discovery/utils/asserts.py

from mdns_discovery.utils.asserts import assert_valid_dn_key

assert_valid_dn_key("Kitchen")

βœ… Available assert functions

assert_valid_d_keyassert_valid_commissionable_instance_name
assert_valid_vp_keyassert_valid_operational_instance_name
assert_valid_cm_keyassert_valid_short_discriminator_subtype
assert_valid_dt_keyassert_valid_long_discriminator_subtype
assert_valid_dn_keyassert_is_commissionable_type
assert_valid_ri_keyassert_is_border_router_type
assert_valid_ph_keyassert_valid_devtype_subtype
assert_valid_pi_keyassert_valid_vendor_subtype
assert_valid_jf_keyassert_is_commissioner_type
assert_valid_sii_keyassert_valid_ipv6_addresses
assert_valid_sai_keyassert_is_operational_type
assert_valid_sat_keyassert_valid_product_id
assert_valid_t_keyassert_valid_vendor_id
assert_valid_icd_keyassert_valid_hostname

πŸ“Œ General Information

For a complete overview of available methods, their functionality, and supported parameters, refer to the inline docstrings or inspect the source code directly.