From 1124706ae43429d8798bf2d1aa010e9b8fe5ef49 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 28 Mar 2024 00:18:16 +0100 Subject: [PATCH] [Python] Implement asyncio variant of CallAsync Call Matter SDK in a asyncio friendly way. During posting of the task onto the CHIP mainloop, it makes sure that the asyncio loop is not blocked. --- src/controller/python/chip/ChipStack.py | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py index 6c0eb4dc1409ea..b6d29257f08162 100644 --- a/src/controller/python/chip/ChipStack.py +++ b/src/controller/python/chip/ChipStack.py @@ -26,6 +26,7 @@ from __future__ import absolute_import, print_function +import asyncio import builtins import logging import os @@ -164,6 +165,31 @@ def Wait(self, timeoutMs: int = None): return self._res +class AsyncSimpleCallableHandle: + """Class which handles Matter SDK Calls asyncio friendly""" + + def __init__(self, callback, loop, future): + self._callback = callback + self._loop = loop + self._future = future + self._result = None + self._exception = None + + def _done(self): + if self._exception: + self._future.set_exception(self._exception) + else: + self._future.set_result(self._result) + + def __call__(self): + try: + self._result = self._callback() + except Exception as ex: + self._exception = ex + self._loop.call_soon_threadsafe(self._done) + pythonapi.Py_DecRef(py_object(self)) + + _CompleteFunct = CFUNCTYPE(None, c_void_p, c_void_p) _ErrorFunct = CFUNCTYPE(None, c_void_p, c_void_p, c_ulong, POINTER(DeviceStatusStruct)) @@ -367,6 +393,24 @@ def Call(self, callFunct, timeoutMs: int = None): return self.callbackRes return res + async def CallAsync(self, callFunct, timeoutMs: int = None): + '''Run a Python function on CHIP stack, and wait for the response. + This function will post a task on CHIP mainloop and waits for the call response in a asyncio friendly manner. + ''' + loop = asyncio.get_event_loop() + future = loop.create_future() + callObj = AsyncSimpleCallableHandle(callFunct, loop, future) + pythonapi.Py_IncRef(py_object(callObj)) + + res = self._ChipStackLib.pychip_DeviceController_PostTaskOnChipThread( + self.cbHandleChipThreadRun, py_object(callObj)) + + if not res.is_success: + pythonapi.Py_DecRef(py_object(callObj)) + raise res.to_exception() + + return await asyncio.wait_for(future, timeoutMs / 1000 if timeoutMs else None) + def CallAsyncWithCallback(self, callFunct): '''Run a Python function on CHIP stack, and wait for the application specific response. This function is a wrapper of PostTaskOnChipThread, which includes some handling of application specific logics.