-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy path0004-Python-Implement-async-friendly-GetConnectedDevice.patch
133 lines (118 loc) · 6.13 KB
/
0004-Python-Implement-async-friendly-GetConnectedDevice.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
From f9fc067ad51d3989a2045f19fc5641971ce1ee20 Mon Sep 17 00:00:00 2001
From: Stefan Agner <stefan@agner.ch>
Date: Wed, 27 Mar 2024 22:13:19 +0100
Subject: [PATCH] [Python] Implement async friendly GetConnectedDevice
Currently GetConnectedDeviceSync() is blocking e.g. when a new session
needs to be created. This is not asyncio friendly as it blocks the
whole event loop.
Implement a asyncio friendly variant GetConnectedDevice() which is
a co-routine function which can be awaited.
---
src/controller/python/chip/ChipDeviceCtrl.py | 62 ++++++++++++++++++--
1 file changed, 56 insertions(+), 6 deletions(-)
diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py
index 369260787d..b3d0aa2d7f 100644
--- a/src/controller/python/chip/ChipDeviceCtrl.py
+++ b/src/controller/python/chip/ChipDeviceCtrl.py
@@ -823,6 +823,56 @@ class ChipDeviceControllerBase():
return DeviceProxyWrapper(returnDevice, self._dmLib)
+ async def GetConnectedDevice(self, nodeid, allowPASE=True, timeoutMs: int = None):
+ ''' Returns DeviceProxyWrapper upon success.'''
+ self.CheckIsActive()
+
+ if allowPASE:
+ returnDevice = c_void_p(None)
+ res = self._ChipStack.Call(lambda: self._dmLib.pychip_GetDeviceBeingCommissioned(
+ self.devCtrl, nodeid, byref(returnDevice)), timeoutMs)
+ if res.is_success:
+ logging.info('Using PASE connection')
+ return DeviceProxyWrapper(returnDevice)
+
+ eventLoop = asyncio.get_running_loop()
+ future = eventLoop.create_future()
+
+ class DeviceAvailableClosure():
+ def __init__(self, loop, future: asyncio.Future):
+ self._returnDevice = c_void_p(None)
+ self._returnErr = None
+ self._event_loop = loop
+ self._future = future
+
+ def _deviceAvailable(self):
+ if self._returnDevice.value is not None:
+ self._future.set_result(self._returnDevice)
+ else:
+ self._future.set_exception(self._returnErr.to_exception())
+
+ def deviceAvailable(self, device, err):
+ self._returnDevice = c_void_p(device)
+ self._returnErr = err
+ self._event_loop.call_soon_threadsafe(self._deviceAvailable)
+ ctypes.pythonapi.Py_DecRef(ctypes.py_object(self))
+
+ closure = DeviceAvailableClosure(eventLoop, future)
+ ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure))
+ self._ChipStack.Call(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId(
+ self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback),
+ timeoutMs).raise_on_error()
+
+ # The callback might have been received synchronously (during self._ChipStack.Call()).
+ # In that case the Future has already been set it will return immediately
+ if (timeoutMs):
+ timeout = float(timeoutMs) / 1000
+ await asyncio.wait_for(future, timeout=timeout)
+ else:
+ await future
+
+ return DeviceProxyWrapper(future.result(), self._dmLib)
+
def ComputeRoundTripTimeout(self, nodeid, upperLayerProcessingTimeoutMs: int = 0):
''' Returns a computed timeout value based on the round-trip time it takes for the peer at the other end of the session to
receive a message, process it and send it back. This is computed based on the session type, the type of transport,
@@ -887,7 +937,7 @@ class ChipDeviceControllerBase():
eventLoop = asyncio.get_running_loop()
future = eventLoop.create_future()
- device = self.GetConnectedDeviceSync(nodeid, timeoutMs=interactionTimeoutMs)
+ device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
ClusterCommand.TestOnlySendBatchCommands(
future, eventLoop, device.deviceProxy, commands,
@@ -908,7 +958,7 @@ class ChipDeviceControllerBase():
eventLoop = asyncio.get_running_loop()
future = eventLoop.create_future()
- device = self.GetConnectedDeviceSync(nodeid, timeoutMs=None)
+ device = await self.GetConnectedDevice(nodeid, timeoutMs=None)
ClusterCommand.TestOnlySendCommandTimedRequestFlagWithNoTimedInvoke(
future, eventLoop, responseType, device.deviceProxy, ClusterCommand.CommandPath(
EndpointId=endpoint,
@@ -940,7 +990,7 @@ class ChipDeviceControllerBase():
eventLoop = asyncio.get_running_loop()
future = eventLoop.create_future()
- device = self.GetConnectedDeviceSync(nodeid, timeoutMs=interactionTimeoutMs)
+ device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
ClusterCommand.SendCommand(
future, eventLoop, responseType, device.deviceProxy, ClusterCommand.CommandPath(
EndpointId=endpoint,
@@ -981,7 +1031,7 @@ class ChipDeviceControllerBase():
eventLoop = asyncio.get_running_loop()
future = eventLoop.create_future()
- device = self.GetConnectedDeviceSync(nodeid, timeoutMs=interactionTimeoutMs)
+ device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
ClusterCommand.SendBatchCommands(
future, eventLoop, device.deviceProxy, commands,
@@ -1031,7 +1081,7 @@ class ChipDeviceControllerBase():
eventLoop = asyncio.get_running_loop()
future = eventLoop.create_future()
- device = self.GetConnectedDeviceSync(nodeid, timeoutMs=interactionTimeoutMs)
+ device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
attrs = []
for v in attributes:
@@ -1259,7 +1309,7 @@ class ChipDeviceControllerBase():
eventLoop = asyncio.get_running_loop()
future = eventLoop.create_future()
- device = self.GetConnectedDeviceSync(nodeid)
+ device = await self.GetConnectedDevice(nodeid)
attributePaths = [self._parseAttributePathTuple(
v) for v in attributes] if attributes else None
clusterDataVersionFilters = [self._parseDataVersionFilterTuple(
--
2.44.0