Skip to content

Commit e3655d1

Browse files
committed
[Python] Convert DiscoverCommissionableNodes to asyncio
Make the discovery of commissionable nodes Python asyncio APIs as well. This avoids blocking the event loop when using the API. The implementation is also safe to be used with the Python asyncio wait_for() function: The discovery process will be cancelled if the timeout is reached.
1 parent e407d40 commit e3655d1

File tree

1 file changed

+31
-23
lines changed

1 file changed

+31
-23
lines changed

src/controller/python/chip/ChipDeviceCtrl.py

+31-23
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import logging
3939
import secrets
4040
import threading
41-
import time
4241
import typing
4342
from ctypes import (CDLL, CFUNCTYPE, POINTER, Structure, byref, c_bool, c_char, c_char_p, c_int, c_int32, c_size_t, c_uint8,
4443
c_uint16, c_uint32, c_uint64, c_void_p, create_string_buffer, pointer, py_object, resize, string_at)
@@ -729,8 +728,8 @@ def GetAddressAndPort(self, nodeid):
729728

730729
return (address.value.decode(), port.value) if error == 0 else None
731730

732-
def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None,
733-
stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]:
731+
async def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None,
732+
stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]:
734733
''' Discover commissionable nodes via DNS-SD with specified filters.
735734
Supported filters are:
736735

@@ -752,27 +751,36 @@ def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discove
752751
if isinstance(filter, int):
753752
filter = str(filter)
754753

755-
self._ChipStack.Call(
756-
lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes(
757-
self.devCtrl, int(filterType), str(filter).encode("utf-8"))).raise_on_error()
758-
759-
if timeoutSecond != 0:
760-
if stopOnFirst:
761-
target = time.time() + timeoutSecond
762-
while time.time() < target:
763-
if self._ChipStack.Call(
764-
lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)):
765-
break
766-
time.sleep(0.1)
767-
else:
768-
time.sleep(timeoutSecond)
769-
770-
self._ChipStack.Call(
771-
lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl)).raise_on_error()
754+
# Discovery is also used during commissioning. Make sure this manual discovery
755+
# and commissioning attempts do not interfere with each other.
756+
async with self._commissioning_lock:
757+
res = await self._ChipStack.CallAsync(
758+
lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes(
759+
self.devCtrl, int(filterType), str(filter).encode("utf-8")))
760+
res.raise_on_error()
772761

773-
return self.GetDiscoveredDevices()
762+
async def _wait_discovery():
763+
while not await self._ChipStack.CallAsync(
764+
lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)):
765+
await asyncio.sleep(0.1)
766+
return
774767

775-
def GetDiscoveredDevices(self):
768+
try:
769+
if stopOnFirst:
770+
await asyncio.wait_for(_wait_discovery(), timeoutSecond)
771+
else:
772+
await asyncio.sleep(timeoutSecond)
773+
except TimeoutError:
774+
# Expected timeout, do nothing
775+
pass
776+
finally:
777+
res = await self._ChipStack.CallAsync(
778+
lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl))
779+
res.raise_on_error()
780+
781+
return await self.GetDiscoveredDevices()
782+
783+
async def GetDiscoveredDevices(self):
776784
def GetDevices(devCtrl):
777785
devices = []
778786

@@ -786,7 +794,7 @@ def HandleDevice(deviceJson, deviceJsonLen):
786794
self._dmLib.pychip_DeviceController_IterateDiscoveredCommissionableNodes(devCtrl.devCtrl, HandleDevice)
787795
return devices
788796

789-
return self._ChipStack.Call(lambda: GetDevices(self))
797+
return await self._ChipStack.CallAsync(lambda: GetDevices(self))
790798

791799
def GetIPForDiscoveredDevice(self, idx, addrStr, length):
792800
self.CheckIsActive()

0 commit comments

Comments
 (0)