From a53ad33f9d3f8bf1b531acfbbe76cc3152794f05 Mon Sep 17 00:00:00 2001 From: ihidchaos Date: Mon, 28 Oct 2024 10:33:17 +0800 Subject: [PATCH 1/3] replace Avahi with ZeroConf for cross platform;add fake LED for testing --- README.md | 9 ++--- circuitmatter/__init__.py | 4 +-- circuitmatter/utility/mdns/zeroconf.py | 47 ++++++++++++++++++++++++++ examples/fake_onoff_led.py | 32 ++++++++++++++++++ examples/replay.py | 4 +-- examples/replay_rgb_light.py | 4 +-- pyproject.toml | 1 + 7 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 circuitmatter/utility/mdns/zeroconf.py create mode 100644 examples/fake_onoff_led.py diff --git a/README.md b/README.md index f76107a..fb0501d 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,7 @@ CircuitMatter is currently developed in CPython 3.12, the de facto implementatio ### Running on a Raspberry Pi SBC -CircuitMatter uses [avahi tools](https://avahi.org) to manage MDNS on Linux. It must therefore be installed for it to work properly. -```shell -sudo apt-get install avahi-utils -``` - -Now, install CircuitMatter: +Install CircuitMatter: ```shell pip install circuitmatter @@ -203,7 +198,7 @@ To run CircuitMatter against a live Matter commissioner run: python examples/replay.py ``` -This will start up MDNS via avahi for discovery by the commissioner and then reply to received UDP packets. CircuitMatter currently doesn't fully commission so it can't act as any specific type of device yet. When it can, there will be examples. +This will start up MDNS for discovery by the commissioner and then reply to received UDP packets. CircuitMatter currently doesn't fully commission so it can't act as any specific type of device yet. When it can, there will be examples. ## Running a Matter commissioner diff --git a/circuitmatter/__init__.py b/circuitmatter/__init__.py index 3cd743f..d2c517f 100644 --- a/circuitmatter/__init__.py +++ b/circuitmatter/__init__.py @@ -37,9 +37,9 @@ def __init__( self.socketpool = socketpool if mdns_server is None: - from circuitmatter.utility.mdns.avahi import Avahi + from circuitmatter.utility.mdns.zeroconf import ZeroConf - mdns_server = Avahi() + mdns_server = ZeroConf() self.mdns_server = mdns_server if random_source is None: diff --git a/circuitmatter/utility/mdns/zeroconf.py b/circuitmatter/utility/mdns/zeroconf.py new file mode 100644 index 0000000..0a7b978 --- /dev/null +++ b/circuitmatter/utility/mdns/zeroconf.py @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import socket +from zeroconf import IPVersion, ServiceInfo, Zeroconf + +class ZeroConf: + def __init__(self): + self.zeroconf = Zeroconf(ip_version=IPVersion.All) + self.service_infos = {} + + def advertise_service( + self, + service_type, + protocol, + port, + txt_records={}, + subtypes=[], + instance_name="", + ): + txt_records = [f"{key}={value}" for key, value in txt_records.items()] + main_info = ServiceInfo( + f"{service_type}.{protocol}.local", + instance_name, + addresses=[socket.inet_aton("0.0.0.0")], + port=port, + properties=txt_records, + ) + + sub_info = ServiceInfo( + subtypes, + instance_name, + addresses=[socket.inet_aton("0.0.0.0")], + port=port, + properties=txt_records, + ) + + self.zeroconf.register_service(main_info) + self.zeroconf.register_service(sub_info) + self.service_infos[service_type + instance_name] = main_info + self.service_infos[subtypes + instance_name] = sub_info + + def __del__(self): + for service_info in self.service_infos.values(): + self.zeroconf.unregister_service(service_info) + self.zeroconf.close() diff --git a/examples/fake_onoff_led.py b/examples/fake_onoff_led.py new file mode 100644 index 0000000..a777994 --- /dev/null +++ b/examples/fake_onoff_led.py @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +"""Simple fake LED on and off as a light.""" + +import circuitmatter as cm +from circuitmatter.device_types.lighting import on_off + + +class LED(on_off.OnOffLight): + def __init__(self, name, led): + super().__init__(name) + self._name = name + self._led = led + + def on(self): + self._led.value = True + print("Led %s is On", self._name) + + def off(self): + self._led.value = False + print("Led %s is Off", self._name) + + +matter = cm.CircuitMatter() +led = LED("led1") +matter.add_device(led) +led = LED("led2") +matter.add_device(led) +while True: + matter.process_packets() diff --git a/examples/replay.py b/examples/replay.py index 7b736c5..c521319 100644 --- a/examples/replay.py +++ b/examples/replay.py @@ -13,7 +13,7 @@ from circuitmatter.device_types.lighting import on_off from circuitmatter.utility import random from circuitmatter.utility.mdns import DummyMDNS -from circuitmatter.utility.mdns.avahi import Avahi +from circuitmatter.utility.mdns.zeroconf import ZeroConf from circuitmatter.utility.recording import RecordingRandom, RecordingSocketPool from circuitmatter.utility.replay import ReplayRandom, ReplaySocketPool @@ -51,7 +51,7 @@ def run(replay_file=None): # No starting state. record_file.write("none\n") socketpool = RecordingSocketPool(record_file, socket) - mdns_server = Avahi() + mdns_server = ZeroConf() random_source = RecordingRandom(record_file, random) matter = cm.CircuitMatter(socketpool, mdns_server, random_source, device_state) diff --git a/examples/replay_rgb_light.py b/examples/replay_rgb_light.py index da1c932..6e05f07 100644 --- a/examples/replay_rgb_light.py +++ b/examples/replay_rgb_light.py @@ -13,7 +13,7 @@ from circuitmatter.device_types.lighting import extended_color from circuitmatter.utility import random from circuitmatter.utility.mdns import DummyMDNS -from circuitmatter.utility.mdns.avahi import Avahi +from circuitmatter.utility.mdns.zeroconf import ZeroConf from circuitmatter.utility.recording import RecordingRandom, RecordingSocketPool from circuitmatter.utility.replay import ReplayRandom, ReplaySocketPool @@ -73,7 +73,7 @@ def run(replay_file=None): # No starting state. record_file.write("none\n") socketpool = RecordingSocketPool(record_file, socket) - mdns_server = Avahi() + mdns_server = ZeroConf() random_source = RecordingRandom(record_file, random) matter = cm.CircuitMatter(socketpool, mdns_server, random_source, device_state) diff --git a/pyproject.toml b/pyproject.toml index 0e96bc8..fc8697b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ requires = [ "setuptools", "wheel", "setuptools-scm", + "zeroconf", ] [project] From 1e712f666f91efa0789ddcf744707709da4f7f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=AD=E7=8E=89=E8=B6=85?= Date: Thu, 28 Nov 2024 09:43:55 +0800 Subject: [PATCH 2/3] re-format --- circuitmatter/utility/mdns/zeroconf.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/circuitmatter/utility/mdns/zeroconf.py b/circuitmatter/utility/mdns/zeroconf.py index 0a7b978..54abfaf 100644 --- a/circuitmatter/utility/mdns/zeroconf.py +++ b/circuitmatter/utility/mdns/zeroconf.py @@ -3,8 +3,10 @@ # SPDX-License-Identifier: MIT import socket + from zeroconf import IPVersion, ServiceInfo, Zeroconf + class ZeroConf: def __init__(self): self.zeroconf = Zeroconf(ip_version=IPVersion.All) @@ -21,19 +23,19 @@ def advertise_service( ): txt_records = [f"{key}={value}" for key, value in txt_records.items()] main_info = ServiceInfo( - f"{service_type}.{protocol}.local", - instance_name, - addresses=[socket.inet_aton("0.0.0.0")], - port=port, - properties=txt_records, + f"{service_type}.{protocol}.local", + instance_name, + addresses=[socket.inet_aton("0.0.0.0")], + port=port, + properties=txt_records, ) sub_info = ServiceInfo( - subtypes, - instance_name, - addresses=[socket.inet_aton("0.0.0.0")], - port=port, - properties=txt_records, + subtypes, + instance_name, + addresses=[socket.inet_aton("0.0.0.0")], + port=port, + properties=txt_records, ) self.zeroconf.register_service(main_info) From f28a95dc933e1cac339699295b8badb51f84ac79 Mon Sep 17 00:00:00 2001 From: ihidchaos Date: Fri, 29 Nov 2024 18:51:27 +0800 Subject: [PATCH 3/3] fix service --- circuitmatter/utility/mdns/zeroconf.py | 28 +++++++++---------- .../fake_onoff_led.py => fake_onoff_led.py | 16 +++++------ 2 files changed, 22 insertions(+), 22 deletions(-) rename examples/fake_onoff_led.py => fake_onoff_led.py (72%) diff --git a/circuitmatter/utility/mdns/zeroconf.py b/circuitmatter/utility/mdns/zeroconf.py index 54abfaf..68af468 100644 --- a/circuitmatter/utility/mdns/zeroconf.py +++ b/circuitmatter/utility/mdns/zeroconf.py @@ -21,27 +21,27 @@ def advertise_service( subtypes=[], instance_name="", ): - txt_records = [f"{key}={value}" for key, value in txt_records.items()] + txt_records = {key: value for key, value in txt_records.items()} main_info = ServiceInfo( - f"{service_type}.{protocol}.local", - instance_name, + f"{service_type}.{protocol}.local.", + f"{service_type}.{protocol}.local.", addresses=[socket.inet_aton("0.0.0.0")], port=port, properties=txt_records, ) - - sub_info = ServiceInfo( - subtypes, - instance_name, - addresses=[socket.inet_aton("0.0.0.0")], - port=port, - properties=txt_records, - ) - self.zeroconf.register_service(main_info) - self.zeroconf.register_service(sub_info) self.service_infos[service_type + instance_name] = main_info - self.service_infos[subtypes + instance_name] = sub_info + + for subtype in subtypes: + sub_info = ServiceInfo( + f"{subtype}.local.", + f"{subtype}.local.", + addresses=[socket.inet_aton("0.0.0.0")], + port=port, + properties=txt_records, + ) + self.service_infos[subtype] = sub_info + self.zeroconf.register_service(sub_info) def __del__(self): for service_info in self.service_infos.values(): diff --git a/examples/fake_onoff_led.py b/fake_onoff_led.py similarity index 72% rename from examples/fake_onoff_led.py rename to fake_onoff_led.py index a777994..b40d304 100644 --- a/examples/fake_onoff_led.py +++ b/fake_onoff_led.py @@ -9,24 +9,24 @@ class LED(on_off.OnOffLight): - def __init__(self, name, led): + def __init__(self, name): super().__init__(name) self._name = name - self._led = led + self.state = False def on(self): - self._led.value = True + self.state = True print("Led %s is On", self._name) def off(self): - self._led.value = False + self.state = False print("Led %s is Off", self._name) matter = cm.CircuitMatter() -led = LED("led1") -matter.add_device(led) -led = LED("led2") -matter.add_device(led) +led1 = LED("led1") +matter.add_device(led1) +led2 = LED("led2") +matter.add_device(led2) while True: matter.process_packets()