Skip to content

Commit 2c1a93d

Browse files
committed
Update Python controller bindings with latest API updates
Specifically, this integrates changes from the following PRs - project-chip/connectedhomeip#34033
1 parent 0aaf8c0 commit 2c1a93d

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
From 47d800809a9979b3162f5f676f308ebd9282c361 Mon Sep 17 00:00:00 2001
2+
From: Stefan Agner <stefan@agner.ch>
3+
Date: Fri, 21 Jun 2024 14:28:03 +0200
4+
Subject: [PATCH] [Python] Convert DiscoverCommissionableNodes to asyncio
5+
(#34033)
6+
7+
* [Python] Convert DiscoverCommissionableNodes to asyncio
8+
9+
Make the discovery of commissionable nodes Python asyncio APIs as well.
10+
This avoids blocking the event loop when using the API.
11+
12+
The implementation is also safe to be used with the Python asyncio
13+
wait_for() function: The discovery process will be cancelled if the
14+
timeout is reached.
15+
16+
* [Python] Adjust tests to use new DiscoverCommissionableNodes API
17+
---
18+
src/controller/python/chip/ChipDeviceCtrl.py | 54 +++++++++++--------
19+
.../python/chip/commissioning/pase.py | 4 +-
20+
src/controller/python/chip/yaml/runner.py | 2 +-
21+
.../python/test/test_scripts/base.py | 6 +--
22+
.../test/test_scripts/commissioning_test.py | 2 +-
23+
.../test/test_scripts/failsafe_tests.py | 2 +-
24+
.../test/test_scripts/mobile-device-test.py | 2 +-
25+
src/python_testing/TC_IDM_1_2.py | 2 +-
26+
src/python_testing/TC_OPCREDS_3_1.py | 2 +-
27+
9 files changed, 42 insertions(+), 34 deletions(-)
28+
29+
diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py
30+
index 1c156c22d4..208385f305 100644
31+
--- a/src/controller/python/chip/ChipDeviceCtrl.py
32+
+++ b/src/controller/python/chip/ChipDeviceCtrl.py
33+
@@ -37,7 +37,6 @@ import enum
34+
import json
35+
import logging
36+
import threading
37+
-import time
38+
import typing
39+
from ctypes import (CDLL, CFUNCTYPE, POINTER, byref, c_bool, c_char, c_char_p, c_int, c_int32, c_size_t, c_uint8, c_uint16,
40+
c_uint32, c_uint64, c_void_p, create_string_buffer, pointer, py_object, resize, string_at)
41+
@@ -634,8 +633,8 @@ class ChipDeviceControllerBase():
42+
43+
return (address.value.decode(), port.value) if error == 0 else None
44+
45+
- def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None,
46+
- stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]:
47+
+ async def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None,
48+
+ stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]:
49+
''' Discover commissionable nodes via DNS-SD with specified filters.
50+
Supported filters are:
51+
52+
@@ -657,27 +656,36 @@ class ChipDeviceControllerBase():
53+
if isinstance(filter, int):
54+
filter = str(filter)
55+
56+
- self._ChipStack.Call(
57+
- lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes(
58+
- self.devCtrl, int(filterType), str(filter).encode("utf-8"))).raise_on_error()
59+
-
60+
- if timeoutSecond != 0:
61+
- if stopOnFirst:
62+
- target = time.time() + timeoutSecond
63+
- while time.time() < target:
64+
- if self._ChipStack.Call(
65+
- lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)):
66+
- break
67+
- time.sleep(0.1)
68+
- else:
69+
- time.sleep(timeoutSecond)
70+
-
71+
- self._ChipStack.Call(
72+
- lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl)).raise_on_error()
73+
+ # Discovery is also used during commissioning. Make sure this manual discovery
74+
+ # and commissioning attempts do not interfere with each other.
75+
+ async with self._commissioning_lock:
76+
+ res = await self._ChipStack.CallAsync(
77+
+ lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes(
78+
+ self.devCtrl, int(filterType), str(filter).encode("utf-8")))
79+
+ res.raise_on_error()
80+
81+
- return self.GetDiscoveredDevices()
82+
+ async def _wait_discovery():
83+
+ while not await self._ChipStack.CallAsync(
84+
+ lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)):
85+
+ await asyncio.sleep(0.1)
86+
+ return
87+
88+
- def GetDiscoveredDevices(self):
89+
+ try:
90+
+ if stopOnFirst:
91+
+ await asyncio.wait_for(_wait_discovery(), timeoutSecond)
92+
+ else:
93+
+ await asyncio.sleep(timeoutSecond)
94+
+ except TimeoutError:
95+
+ # Expected timeout, do nothing
96+
+ pass
97+
+ finally:
98+
+ res = await self._ChipStack.CallAsync(
99+
+ lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl))
100+
+ res.raise_on_error()
101+
+
102+
+ return await self.GetDiscoveredDevices()
103+
+
104+
+ async def GetDiscoveredDevices(self):
105+
def GetDevices(devCtrl):
106+
devices = []
107+
108+
@@ -691,7 +699,7 @@ class ChipDeviceControllerBase():
109+
self._dmLib.pychip_DeviceController_IterateDiscoveredCommissionableNodes(devCtrl.devCtrl, HandleDevice)
110+
return devices
111+
112+
- return self._ChipStack.Call(lambda: GetDevices(self))
113+
+ return await self._ChipStack.CallAsync(lambda: GetDevices(self))
114+
115+
def GetIPForDiscoveredDevice(self, idx, addrStr, length):
116+
self.CheckIsActive()
117+
diff --git a/src/controller/python/chip/commissioning/pase.py b/src/controller/python/chip/commissioning/pase.py
118+
index c0cfca5ee8..ee0b96c76b 100644
119+
--- a/src/controller/python/chip/commissioning/pase.py
120+
+++ b/src/controller/python/chip/commissioning/pase.py
121+
@@ -48,8 +48,8 @@ async def establish_session(devCtrl: ChipDeviceCtrl.ChipDeviceControllerBase, pa
122+
if isinstance(parameter, commissioning.PaseOverBLEParameters):
123+
await devCtrl.EstablishPASESessionBLE(parameter.setup_pin, parameter.discriminator, parameter.temporary_nodeid)
124+
elif isinstance(parameter, commissioning.PaseOverIPParameters):
125+
- device = devCtrl.DiscoverCommissionableNodes(filterType=discovery.FilterType.LONG_DISCRIMINATOR,
126+
- filter=parameter.long_discriminator, stopOnFirst=True)
127+
+ device = await devCtrl.DiscoverCommissionableNodes(filterType=discovery.FilterType.LONG_DISCRIMINATOR,
128+
+ filter=parameter.long_discriminator, stopOnFirst=True)
129+
if not device:
130+
raise ValueError("No commissionable device found")
131+
selected_address = None
132+
diff --git a/src/controller/python/chip/yaml/runner.py b/src/controller/python/chip/yaml/runner.py
133+
index f0b681fb2b..ce1eaf84fc 100644
134+
--- a/src/controller/python/chip/yaml/runner.py
135+
+++ b/src/controller/python/chip/yaml/runner.py
136+
@@ -712,7 +712,7 @@ class DiscoveryCommandAction(BaseAction):
137+
self.filterType, self.filter = DiscoveryCommandAction._filter_for_step(test_step)
138+
139+
async def run_action(self, dev_ctrl: ChipDeviceController) -> _ActionResult:
140+
- devices = dev_ctrl.DiscoverCommissionableNodes(
141+
+ devices = await dev_ctrl.DiscoverCommissionableNodes(
142+
filterType=self.filterType, filter=self.filter, stopOnFirst=True, timeoutSecond=5)
143+
144+
# Devices will be a list: [CommissionableNode(), ...]
145+
diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py
146+
index 84a403643d..5e4def2760 100644
147+
--- a/src/controller/python/test/test_scripts/base.py
148+
+++ b/src/controller/python/test/test_scripts/base.py
149+
@@ -210,10 +210,10 @@ class BaseTestHelper:
150+
return None
151+
return ctypes.string_at(addrStrStorage).decode("utf-8")
152+
153+
- def TestDiscovery(self, discriminator: int):
154+
+ async def TestDiscovery(self, discriminator: int):
155+
self.logger.info(
156+
f"Discovering commissionable nodes with discriminator {discriminator}")
157+
- res = self.devCtrl.DiscoverCommissionableNodes(
158+
+ res = await self.devCtrl.DiscoverCommissionableNodes(
159+
chip.discovery.FilterType.LONG_DISCRIMINATOR, discriminator, stopOnFirst=True, timeoutSecond=3)
160+
if not res:
161+
self.logger.info(
162+
@@ -337,7 +337,7 @@ class BaseTestHelper:
163+
164+
async def TestOnNetworkCommissioning(self, discriminator: int, setuppin: int, nodeid: int, ip_override: str = None):
165+
self.logger.info("Testing discovery")
166+
- device = self.TestDiscovery(discriminator=discriminator)
167+
+ device = await self.TestDiscovery(discriminator=discriminator)
168+
if not device:
169+
self.logger.info("Failed to discover any devices.")
170+
return False
171+
diff --git a/src/controller/python/test/test_scripts/commissioning_test.py b/src/controller/python/test/test_scripts/commissioning_test.py
172+
index c53ab00f33..ac7954595b 100755
173+
--- a/src/controller/python/test/test_scripts/commissioning_test.py
174+
+++ b/src/controller/python/test/test_scripts/commissioning_test.py
175+
@@ -125,7 +125,7 @@ async def main():
176+
nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=True)
177+
178+
logger.info("Testing discovery")
179+
- FailIfNot(test.TestDiscovery(discriminator=options.discriminator),
180+
+ FailIfNot(await test.TestDiscovery(discriminator=options.discriminator),
181+
"Failed to discover any devices.")
182+
183+
FailIfNot(test.SetNetworkCommissioningParameters(dataset=TEST_THREAD_NETWORK_DATASET_TLV),
184+
diff --git a/src/controller/python/test/test_scripts/failsafe_tests.py b/src/controller/python/test/test_scripts/failsafe_tests.py
185+
index d27111cbf7..9e4d4bb14e 100755
186+
--- a/src/controller/python/test/test_scripts/failsafe_tests.py
187+
+++ b/src/controller/python/test/test_scripts/failsafe_tests.py
188+
@@ -88,7 +88,7 @@ async def main():
189+
nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=False)
190+
191+
logger.info("Testing discovery")
192+
- FailIfNot(test.TestDiscovery(discriminator=TEST_DISCRIMINATOR),
193+
+ FailIfNot(await test.TestDiscovery(discriminator=TEST_DISCRIMINATOR),
194+
"Failed to discover any devices.")
195+
196+
FailIfNot(test.SetNetworkCommissioningParameters(dataset=TEST_THREAD_NETWORK_DATASET_TLV),
197+
diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py
198+
index 179dfa079a..27f8e98964 100755
199+
--- a/src/controller/python/test/test_scripts/mobile-device-test.py
200+
+++ b/src/controller/python/test/test_scripts/mobile-device-test.py
201+
@@ -59,7 +59,7 @@ ALL_TESTS = ['network_commissioning', 'datamodel']
202+
203+
async def ethernet_commissioning(test: BaseTestHelper, discriminator: int, setup_pin: int, address_override: str, device_nodeid: int):
204+
logger.info("Testing discovery")
205+
- device = test.TestDiscovery(discriminator=discriminator)
206+
+ device = await test.TestDiscovery(discriminator=discriminator)
207+
FailIfNot(device, "Failed to discover any devices.")
208+
209+
address = device.addresses[0]
210+
diff --git a/src/python_testing/TC_IDM_1_2.py b/src/python_testing/TC_IDM_1_2.py
211+
index 18b727d962..dbf131e362 100644
212+
--- a/src/python_testing/TC_IDM_1_2.py
213+
+++ b/src/python_testing/TC_IDM_1_2.py
214+
@@ -195,7 +195,7 @@ class TC_IDM_1_2(MatterBaseTest):
215+
new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=self.matter_test_config.fabric_id + 1)
216+
TH2 = new_fabric_admin.NewController(nodeId=112233)
217+
218+
- devices = TH2.DiscoverCommissionableNodes(
219+
+ devices = await TH2.DiscoverCommissionableNodes(
220+
filterType=Discovery.FilterType.LONG_DISCRIMINATOR, filter=discriminator, stopOnFirst=False)
221+
# For some reason, the devices returned here aren't filtered, so filter ourselves
222+
device = next(filter(lambda d: d.commissioningMode == 2 and d.longDiscriminator == discriminator, devices))
223+
diff --git a/src/python_testing/TC_OPCREDS_3_1.py b/src/python_testing/TC_OPCREDS_3_1.py
224+
index 32f3ec7b1f..86b6d109be 100644
225+
--- a/src/python_testing/TC_OPCREDS_3_1.py
226+
+++ b/src/python_testing/TC_OPCREDS_3_1.py
227+
@@ -34,7 +34,7 @@ class TC_OPCREDS_3_1(MatterBaseTest):
228+
if dev_ctrl is None:
229+
dev_ctrl = self.default_controller
230+
231+
- devices = dev_ctrl.DiscoverCommissionableNodes(
232+
+ devices = await dev_ctrl.DiscoverCommissionableNodes(
233+
filterType=Discovery.FilterType.LONG_DISCRIMINATOR, filter=longDiscriminator, stopOnFirst=False)
234+
# For some reason, the devices returned here aren't filtered, so filter ourselves
235+
device = next(filter(lambda d: d.commissioningMode ==
236+
--
237+
2.45.2
238+

0 commit comments

Comments
 (0)