diff --git a/.github/dependabot.yml b/.github/dependabot.yml index dcc2e6b..9e0ad98 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,7 +2,7 @@ version: 2 updates: - package-ecosystem: "gitsubmodule" directory: "/" - allow: + ignore: - dependency-name: "connectedhomeip" schedule: interval: "daily" diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9b7d8c7..89dfa90 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -76,10 +76,10 @@ jobs: matrix: arch: - name: x86_64 - container: ghcr.io/project-chip/chip-build:1 + container: ghcr.io/project-chip/chip-build:81 runner: ubuntu-22.04 - name: aarch64 - container: docker.io/agners/aarch64-chip-build:1 + container: docker.io/agners/aarch64-chip-build:81 runner: ARM64 runs-on: ${{ matrix.arch.runner }} @@ -105,7 +105,7 @@ jobs: - name: Extract Matter SDK from tar working-directory: ./ run: | - apt update && apt install zstd + apt-get -qq update && apt-get -qq --yes install zstd rm -rf connectedhomeip/ tar -xaf ./connectedhomeip.tar.zst --use-compress-program=zstdmt . git config --global --add safe.directory "*" @@ -117,9 +117,9 @@ jobs: chip_project_config_include_dirs=[\"//..\"] \ chip_crypto=\"boringssl\" enable_rtti=true \ - enable_pylib=true \ chip_config_memory_debug_checks=false \ chip_config_memory_debug_dmalloc=false \ + chip_exchange_node_id_logging=true \ chip_mdns=\"minimal\" \ chip_minmdns_default_policy=\"libnl\" \ chip_python_version=\"${{ needs.build_prepare.outputs.version }}\" \ @@ -214,7 +214,6 @@ jobs: chip_project_config_include_dirs=[\"//..\"] \ chip_crypto=\"boringssl\" enable_rtti=true \ - enable_pylib=true \ chip_config_memory_debug_checks=false \ chip_config_memory_debug_dmalloc=false \ chip_mdns=\"minimal\" \ diff --git a/.github/workflows/dependency-update.yaml b/.github/workflows/dependency-update.yaml new file mode 100644 index 0000000..8d527e3 --- /dev/null +++ b/.github/workflows/dependency-update.yaml @@ -0,0 +1,59 @@ +name: Update Matter SDK submodule + +on: + schedule: + # Runs at 04:30 UTC every day + - cron: '30 04 * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-connectedhomeip-submodule: + runs-on: ubuntu-latest + name: Update Matter SDK submodule + + steps: + # Checkout the repository + - name: Checkout Repository + uses: actions/checkout@v4 + with: + submodules: false + # Required to push to git repo and trigger push CI run for the PR + ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + + # Update submodule (not recursive) + - name: Update Submodule + id: update-submodule + run: | + git submodule update --init connectedhomeip/ + short_hash_before=$(cd connectedhomeip && git rev-parse --short HEAD) + echo "short_hash_before=${short_hash_before}" >> $GITHUB_OUTPUT + git submodule update --remote connectedhomeip/ + git add connectedhomeip/ + if git diff-index --quiet HEAD; then + echo "empty=true" >> $GITHUB_OUTPUT + exit 0 + fi + short_hash=$(cd connectedhomeip && git rev-parse --short HEAD) + echo "short_hash=${short_hash}" >> $GITHUB_OUTPUT + + # Create Pull Request only if there were changes + - name: Create Pull Request + if: ${{ steps.update-submodule.outputs.empty != true }} + uses: peter-evans/create-pull-request@v6 + with: + commit-message: Update Matter SDK to `${{ steps.update-submodule.outputs.short_hash }}` + title: Update Matter SDK to `${{ steps.update-submodule.outputs.short_hash }}` + body: > + Bumps [connectedhomeip](https://github.com/project-chip/connectedhomeip) + from `${{ steps.update-submodule.outputs.short_hash_before }}` to + `${{ steps.update-submodule.outputs.short_hash }}`. + + + See full diff at + https://github.com/project-chip/connectedhomeip/compare/${{ steps.update-submodule.outputs.short_hash_before }}...${{ steps.update-submodule.outputs.short_hash }}. + branch: update-connectedhomeip-submodule-to-latest-master + diff --git a/.gitmodules b/.gitmodules index c51b07a..eab72d6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,3 +3,4 @@ url = https://github.com/project-chip/connectedhomeip.git branch = master fetchRecurseSubmodules = false + depth = 1 diff --git a/0001-Support-custom-platform-tag.patch b/0001-Support-custom-platform-tag.patch index 2fc8650..7219972 100644 --- a/0001-Support-custom-platform-tag.patch +++ b/0001-Support-custom-platform-tag.patch @@ -1,17 +1,26 @@ -From 44cc7593f34d07501c67f06e5f6d7fcf719aae71 Mon Sep 17 00:00:00 2001 +From a3eb36ccb35c4fa8a4cf816a5acf665dcfbb0484 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 22 Nov 2022 10:51:17 +0100 Subject: [PATCH] Support custom platform tag --- - src/controller/python/BUILD.gn | 20 ++++++++++---------- - 1 file changed, 10 insertions(+), 10 deletions(-) + build/chip/python_wheel.gni | 13 +++++++++++++ + src/controller/python/BUILD.gn | 15 +-------------- + 2 files changed, 14 insertions(+), 14 deletions(-) -diff --git a/src/controller/python/BUILD.gn b/src/controller/python/BUILD.gn -index 5fc2212098..caa33c3a40 100644 ---- a/src/controller/python/BUILD.gn -+++ b/src/controller/python/BUILD.gn -@@ -35,6 +35,15 @@ declare_args() { +diff --git a/build/chip/python_wheel.gni b/build/chip/python_wheel.gni +index ac00fe1404..597a49783d 100644 +--- a/build/chip/python_wheel.gni ++++ b/build/chip/python_wheel.gni +@@ -18,10 +18,23 @@ import("//build_overrides/pigweed.gni") + import("$dir_pw_build/python.gni") + import("${chip_root}/src/system/system.gni") + ++if (current_os == "mac") { ++ import("${chip_root}/build/config/mac/mac_sdk.gni") ++} ++ + declare_args() { chip_python_version = "0.0" chip_python_package_prefix = "chip" chip_python_supports_stack_locking = chip_system_config_locking != "none" @@ -26,8 +35,23 @@ index 5fc2212098..caa33c3a40 100644 + } } - shared_library("ChipDeviceCtrl") { -@@ -344,16 +353,7 @@ chip_python_wheel_action("chip-core") { + template("chip_python_wheel_action") { +diff --git a/src/controller/python/BUILD.gn b/src/controller/python/BUILD.gn +index 58d9cb1f7c..96db7d4dee 100644 +--- a/src/controller/python/BUILD.gn ++++ b/src/controller/python/BUILD.gn +@@ -24,10 +24,6 @@ import("${chip_root}/src/platform/python.gni") + import("${chip_root}/src/system/system.gni") + import("${dir_pw_unit_test}/test.gni") + +-if (current_os == "mac") { +- import("${build_root}/config/mac/mac_sdk.gni") +-} +- + config("controller_wno_deprecate") { + cflags = [ "-Wno-deprecated-declarations" ] + } +@@ -282,16 +278,7 @@ chip_python_wheel_action("chip-core") { cpu_tag = current_cpu } @@ -46,5 +70,5 @@ index 5fc2212098..caa33c3a40 100644 tags = "cp37-abi3-" + py_platform_tag -- -2.45.2 +2.47.0 diff --git a/0002-Use-data-as-platform-storage-location.patch b/0002-Use-data-as-platform-storage-location.patch index 8285c97..c54b086 100644 --- a/0002-Use-data-as-platform-storage-location.patch +++ b/0002-Use-data-as-platform-storage-location.patch @@ -1,4 +1,4 @@ -From 703b75caecae8638ea0caeda0c9f4f4a2bdc1819 Mon Sep 17 00:00:00 2001 +From 0ebf0f8cfea2d4b23b3fa78c681dc6ff6536d699 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 27 May 2022 16:38:14 +0200 Subject: [PATCH] Use /data as platform storage location @@ -8,7 +8,7 @@ Subject: [PATCH] Use /data as platform storage location 1 file changed, 6 insertions(+) diff --git a/src/platform/Linux/BUILD.gn b/src/platform/Linux/BUILD.gn -index d73a2dcb0f..97c397994e 100644 +index 35b5047c9f..4886bc3ebb 100644 --- a/src/platform/Linux/BUILD.gn +++ b/src/platform/Linux/BUILD.gn @@ -38,6 +38,12 @@ if (chip_mdns == "platform") { @@ -25,5 +25,5 @@ index d73a2dcb0f..97c397994e 100644 "../DeviceSafeQueue.cpp", "../DeviceSafeQueue.h", -- -2.45.2 +2.47.0 diff --git a/0006-Add-raw-attribute-callback.patch b/0003-Add-raw-attribute-callback.patch similarity index 78% rename from 0006-Add-raw-attribute-callback.patch rename to 0003-Add-raw-attribute-callback.patch index a9e06aa..ec73933 100644 --- a/0006-Add-raw-attribute-callback.patch +++ b/0003-Add-raw-attribute-callback.patch @@ -1,4 +1,4 @@ -From 749cd076e35127092efa8f680902bebf8967389d Mon Sep 17 00:00:00 2001 +From 0f6dc07cb5c20d89b7e51342a32a73d58dd91b7d Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 23 May 2024 12:48:54 +0200 Subject: [PATCH] Add raw attribute callback @@ -14,18 +14,18 @@ directly from the subscription transaction. 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py -index 9e46eed469..ce522bf452 100644 +index 4d5bc1d17a..cb7bdb0bc4 100644 --- a/src/controller/python/chip/clusters/Attribute.py +++ b/src/controller/python/chip/clusters/Attribute.py -@@ -466,6 +466,7 @@ class SubscriptionTransaction: - def __init__(self, transaction: AsyncReadTransaction, subscriptionId, devCtrl): - self._onResubscriptionAttemptedCb = DefaultResubscriptionAttemptedCallback - self._onAttributeChangeCb = DefaultAttributeChangeCallback -+ self._onRawAttributeChangeCb = None - self._onEventChangeCb = DefaultEventChangeCallback - self._onErrorCb = DefaultErrorCallback +@@ -429,6 +429,7 @@ class SubscriptionTransaction: + self._onResubscriptionAttemptedCb: Callable[[SubscriptionTransaction, + int, int], None] = DefaultResubscriptionAttemptedCallback + self._onAttributeChangeCb: Callable[[TypedAttributePath, SubscriptionTransaction], None] = DefaultAttributeChangeCallback ++ self._onRawAttributeChangeCb: Optional[Callable[[AttributePath, SubscriptionTransaction]]] = None + self._onEventChangeCb: Callable[[EventReadResult, SubscriptionTransaction], None] = DefaultEventChangeCallback + self._onErrorCb: Callable[[int, SubscriptionTransaction], None] = DefaultErrorCallback self._readTransaction = transaction -@@ -491,6 +492,18 @@ class SubscriptionTransaction: +@@ -454,6 +455,18 @@ class SubscriptionTransaction: else: return data[path.Path.EndpointId][path.ClusterType][path.AttributeType] @@ -44,7 +44,7 @@ index 9e46eed469..ce522bf452 100644 def GetEvents(self): return self._readTransaction.GetAllEventValues() -@@ -564,8 +577,14 @@ class SubscriptionTransaction: +@@ -534,8 +547,14 @@ class SubscriptionTransaction: Sets the callback function for the attribute value change event, accepts a Callable accepts an attribute path and the cached data. ''' @@ -61,7 +61,7 @@ index 9e46eed469..ce522bf452 100644 def SetEventUpdateCallback(self, callback: Callable[[EventReadResult, SubscriptionTransaction], None]): if callback is not None: -@@ -583,6 +602,10 @@ class SubscriptionTransaction: +@@ -553,6 +572,10 @@ class SubscriptionTransaction: def OnAttributeChangeCb(self) -> Callable[[TypedAttributePath, SubscriptionTransaction], None]: return self._onAttributeChangeCb @@ -72,15 +72,15 @@ index 9e46eed469..ce522bf452 100644 @property def OnEventChangeCb(self) -> Callable[[EventReadResult, SubscriptionTransaction], None]: return self._onEventChangeCb -@@ -785,14 +808,19 @@ class AsyncReadTransaction: - +@@ -767,14 +790,19 @@ class AsyncReadTransaction: + def _handleReportEnd(self): if (self._subscription_handler is not None): for change in self._changedPathSet: - try: - attribute_path = TypedAttributePath(Path=change) - except (KeyError, ValueError) as err: - # path could not be resolved into a TypedAttributePath -- logging.getLogger(__name__).exception(err) +- LOGGER.exception(err) - continue - self._subscription_handler.OnAttributeChangeCb( - attribute_path, self._subscription_handler) @@ -89,7 +89,7 @@ index 9e46eed469..ce522bf452 100644 + attribute_path = TypedAttributePath(Path=change) + except (KeyError, ValueError) as err: + # path could not be resolved into a TypedAttributePath -+ logging.getLogger(__name__).exception(err) ++ LOGGER.exception(err) + continue + self._subscription_handler.OnAttributeChangeCb( + attribute_path, self._subscription_handler) @@ -101,5 +101,5 @@ index 9e46eed469..ce522bf452 100644 # Clear it out once we've notified of all changes in this transaction. self._changedPathSet = set() -- -2.45.2 +2.47.0 diff --git a/0004-Implement-async-friendly-GetConnectedDevice.patch b/0004-Implement-async-friendly-GetConnectedDevice.patch deleted file mode 100644 index 7ca6a88..0000000 --- a/0004-Implement-async-friendly-GetConnectedDevice.patch +++ /dev/null @@ -1,133 +0,0 @@ -From 005112398921c0b0a2cdf9805390311d5b49f4f3 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Wed, 27 Mar 2024 22:13:19 +0100 -Subject: [PATCH] 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.45.2 - diff --git a/0004-Update-matter-devices.xml.patch b/0004-Update-matter-devices.xml.patch new file mode 100644 index 0000000..bec927a --- /dev/null +++ b/0004-Update-matter-devices.xml.patch @@ -0,0 +1,26 @@ +From 1e4590e6cc935dbadbd95ca339c87517fb16760f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ludovic=20BOU=C3=89?= +Date: Wed, 20 Nov 2024 07:53:48 +0100 +Subject: [PATCH] Update matter-devices.xml + +Fix Carbon Monoxide Concentration Measurement cluster +--- + src/app/zap-templates/zcl/data-model/chip/matter-devices.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/app/zap-templates/zcl/data-model/chip/matter-devices.xml b/src/app/zap-templates/zcl/data-model/chip/matter-devices.xml +index 93fc3915e4..5b28393120 100644 +--- a/src/app/zap-templates/zcl/data-model/chip/matter-devices.xml ++++ b/src/app/zap-templates/zcl/data-model/chip/matter-devices.xml +@@ -2076,7 +2076,7 @@ limitations under the License. + + + +- ++ + + + +-- +2.47.0 + diff --git a/0005-Fix-OnRead-Event-Attribute-DataCallback-for-Arm64-Ap.patch b/0005-Fix-OnRead-Event-Attribute-DataCallback-for-Arm64-Ap.patch deleted file mode 100644 index 0c8f776..0000000 --- a/0005-Fix-OnRead-Event-Attribute-DataCallback-for-Arm64-Ap.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 20422009f55822b8dbd0b4cbc2855b756943ce08 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 25 Apr 2024 15:19:17 +0200 -Subject: [PATCH] Fix OnRead[Event|Attribute]DataCallback for Arm64 Apple - Patform devices - -On M1/Arm64 macOS systems, the OnReadEventDataCallback often returned -an invalid status, e.g.: -ValueError: 16 is not a valid Status - -Use size_t consistently to fix this issue. ---- - src/controller/python/chip/clusters/attribute.cpp | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/src/controller/python/chip/clusters/attribute.cpp b/src/controller/python/chip/clusters/attribute.cpp -index e31f3431b8..b73b4a49b4 100644 ---- a/src/controller/python/chip/clusters/attribute.cpp -+++ b/src/controller/python/chip/clusters/attribute.cpp -@@ -71,10 +71,10 @@ struct __attribute__((packed)) DataVersionFilter - using OnReadAttributeDataCallback = void (*)(PyObject * appContext, chip::DataVersion version, chip::EndpointId endpointId, - chip::ClusterId clusterId, chip::AttributeId attributeId, - std::underlying_type_t imstatus, uint8_t * data, -- uint32_t dataLen); -+ size_t dataLen); - using OnReadEventDataCallback = void (*)(PyObject * appContext, chip::EndpointId endpointId, chip::ClusterId clusterId, - chip::EventId eventId, chip::EventNumber eventNumber, uint8_t priority, uint64_t timestamp, -- uint8_t timestampType, uint8_t * data, uint32_t dataLen, -+ uint8_t timestampType, uint8_t * data, size_t dataLen, - std::underlying_type_t imstatus); - using OnSubscriptionEstablishedCallback = void (*)(PyObject * appContext, SubscriptionId subscriptionId); - using OnResubscriptionAttemptedCallback = void (*)(PyObject * appContext, PyChipError aTerminationCause, -@@ -114,7 +114,7 @@ public: - VerifyOrDie(!aPath.IsListItemOperation()); - size_t bufferLen = (apData == nullptr ? 0 : apData->GetRemainingLength() + apData->GetLengthRead()); - std::unique_ptr buffer = std::unique_ptr(apData == nullptr ? nullptr : new uint8_t[bufferLen]); -- uint32_t size = 0; -+ size_t size = 0; - // When the apData is nullptr, means we did not receive a valid attribute data from server, status will be some error - // status. - if (apData != nullptr) -@@ -166,7 +166,7 @@ public: - void OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus) override - { - uint8_t buffer[CHIP_CONFIG_DEFAULT_UDP_MTU_SIZE]; -- uint32_t size = 0; -+ size_t size = 0; - CHIP_ERROR err = CHIP_NO_ERROR; - // When the apData is nullptr, means we did not receive a valid event data from server, status will be some error - // status. --- -2.45.2 - diff --git a/0007-Python-Eliminate-ZCLSubscribeAttribute-33337.patch b/0007-Python-Eliminate-ZCLSubscribeAttribute-33337.patch deleted file mode 100644 index 37f5b78..0000000 --- a/0007-Python-Eliminate-ZCLSubscribeAttribute-33337.patch +++ /dev/null @@ -1,594 +0,0 @@ -From a1e13f098c1e8a140b016bd10d76b36ebf3d6df2 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Mon, 13 May 2024 15:16:56 +0200 -Subject: [PATCH] [Python] Eliminate ZCLSubscribeAttribute (#33337) - -* Use asyncio sleep to unblock asyncio event loop - -* Avoid fixed sleep in TestCaseEviction - -Use asyncio Event and wait_for to wait for the change and continue -immediately when received. - -* Make TestSubscription an async test - -The current test implementation starves the asyncio event loop by -synchronously waiting for the threading.Condition. This prevents -making the SubscriptionTransaction fully leveraging the async -paradigm. - -It probably would be possible to mix asyncio.sleep() and threading, -but instead embrace the async pradigm for this test. - -* Make TestSubscriptionResumption an async test - -The current test implementation starves the asyncio event loop by -synchronously waiting for the threading.Condition. This prevents -making the SubscriptionTransaction fully leveraging the async -paradigm. - -It probably would be possible to mix asyncio.sleep() and threading, -but instead embrace the async pradigm for this test. - -* Make TestSubscriptionResumptionCapacityStep1 an async test - -Eliminate use of ZCLSubscribeAttribute and embrace asyncio. - -* Make TestSubscriptionResumptionCapacityStep2 an async test - -Eliminate use of ZCLSubscribeAttribute and embrace asyncio. - -* Remove ZCLSubscribeAttribute from subscription_resumption_timeout_test - -Use ReadAttribute with asyncio in subscription_resumption_timeout_test -as well. - -* Rewrite TestWriteBasicAttributes to drop ZCLRead/WriteAttribute - -* Improve wait for end of update task in TestSubscription ---- - .../python/test/test_scripts/base.py | 219 +++++++++--------- - .../test/test_scripts/mobile-device-test.py | 9 +- - ...cription_resumption_capacity_test_ctrl1.py | 5 +- - ...cription_resumption_capacity_test_ctrl2.py | 6 +- - .../subscription_resumption_test.py | 5 +- - .../subscription_resumption_timeout_test.py | 10 +- - 6 files changed, 128 insertions(+), 126 deletions(-) - -diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py -index ed091858d2..5edb78f8e1 100644 ---- a/src/controller/python/test/test_scripts/base.py -+++ b/src/controller/python/test/test_scripts/base.py -@@ -697,13 +697,13 @@ class BaseTestHelper: - # on the sub we established previously. Since it was just marked defunct, it should return back to being - # active and a report should get delivered. - # -- sawValueChange = False -+ sawValueChangeEvent = asyncio.Event() -+ loop = asyncio.get_running_loop() - - def OnValueChange(path: Attribute.TypedAttributePath, transaction: Attribute.SubscriptionTransaction) -> None: -- nonlocal sawValueChange - self.logger.info("Saw value change!") - if (path.AttributeType == Clusters.UnitTesting.Attributes.Int8u and path.Path.EndpointId == 1): -- sawValueChange = True -+ loop.call_soon_threadsafe(sawValueChangeEvent.set) - - self.logger.info("Testing CASE defunct logic") - -@@ -720,14 +720,15 @@ class BaseTestHelper: - # was received. - # - await self.devCtrl2.WriteAttribute(nodeid, [(1, Clusters.UnitTesting.Attributes.Int8u(4))]) -- time.sleep(2) - -- sub.Shutdown() -- -- if sawValueChange is False: -+ try: -+ await asyncio.wait_for(sawValueChangeEvent.wait(), 2) -+ except TimeoutError: - self.logger.error( - "Didn't see value change in time, likely because sub got terminated due to unexpected session eviction!") - return False -+ finally: -+ sub.Shutdown() - - # - # In this test, we're going to setup a subscription on fabric1 through devCtl, then, constantly keep -@@ -739,7 +740,7 @@ class BaseTestHelper: - # - self.logger.info("Testing fabric-isolated CASE eviction") - -- sawValueChange = False -+ sawValueChangeEvent.clear() - sub = await self.devCtrl.ReadAttribute(nodeid, [(Clusters.UnitTesting.Attributes.Int8u)], reportInterval=(0, 1)) - sub.SetAttributeUpdateCallback(OnValueChange) - -@@ -752,20 +753,21 @@ class BaseTestHelper: - # was received. - # - await self.devCtrl2.WriteAttribute(nodeid, [(1, Clusters.UnitTesting.Attributes.Int8u(4))]) -- time.sleep(2) -- -- sub.Shutdown() - -- if sawValueChange is False: -+ try: -+ await asyncio.wait_for(sawValueChangeEvent.wait(), 2) -+ except TimeoutError: - self.logger.error("Didn't see value change in time, likely because sub got terminated due to other fabric (fabric1)") - return False -+ finally: -+ sub.Shutdown() - - # - # Do the same test again, but reversing the roles of fabric1 and fabric2. - # - self.logger.info("Testing fabric-isolated CASE eviction (reverse)") - -- sawValueChange = False -+ sawValueChangeEvent.clear() - sub = await self.devCtrl2.ReadAttribute(nodeid, [(Clusters.UnitTesting.Attributes.Int8u)], reportInterval=(0, 1)) - sub.SetAttributeUpdateCallback(OnValueChange) - -@@ -774,13 +776,13 @@ class BaseTestHelper: - await self.devCtrl.ReadAttribute(nodeid, [(Clusters.BasicInformation.Attributes.ClusterRevision)]) - - await self.devCtrl.WriteAttribute(nodeid, [(1, Clusters.UnitTesting.Attributes.Int8u(4))]) -- time.sleep(2) -- -- sub.Shutdown() -- -- if sawValueChange is False: -+ try: -+ await asyncio.wait_for(sawValueChangeEvent.wait(), 2) -+ except TimeoutError: - self.logger.error("Didn't see value change in time, likely because sub got terminated due to other fabric (fabric2)") - return False -+ finally: -+ sub.Shutdown() - - return True - -@@ -1198,121 +1200,114 @@ class BaseTestHelper: - return False - return True - -- def TestWriteBasicAttributes(self, nodeid: int, endpoint: int, group: int): -+ async def TestWriteBasicAttributes(self, nodeid: int, endpoint: int): - @ dataclass - class AttributeWriteRequest: -- cluster: str -- attribute: str -+ cluster: Clusters.ClusterObjects.Cluster -+ attribute: Clusters.ClusterObjects.ClusterAttributeDescriptor - value: Any - expected_status: IM.Status = IM.Status.Success - - requests = [ -- AttributeWriteRequest("BasicInformation", "NodeLabel", "Test"), -- AttributeWriteRequest("BasicInformation", "Location", -+ AttributeWriteRequest(Clusters.BasicInformation, Clusters.BasicInformation.Attributes.NodeLabel, "Test"), -+ AttributeWriteRequest(Clusters.BasicInformation, Clusters.BasicInformation.Attributes.Location, - "a pretty loooooooooooooog string", IM.Status.ConstraintError), - ] -- failed_zcl = [] -+ failed_attribute_write = [] - for req in requests: - try: - try: -- self.devCtrl.ZCLWriteAttribute(cluster=req.cluster, -- attribute=req.attribute, -- nodeid=nodeid, -- endpoint=endpoint, -- groupid=group, -- value=req.value) -+ await self.devCtrl.WriteAttribute(nodeid, [(endpoint, req.attribute, 0)]) - if req.expected_status != IM.Status.Success: - raise AssertionError( -- f"Write attribute {req.cluster}.{req.attribute} expects failure but got success response") -+ f"Write attribute {req.attribute.__qualname__} expects failure but got success response") - except Exception as ex: - if req.expected_status != IM.Status.Success: - continue - else: - raise ex -- res = self.devCtrl.ZCLReadAttribute( -- cluster=req.cluster, attribute=req.attribute, nodeid=nodeid, endpoint=endpoint, groupid=group) -- TestResult(f"Read attribute {req.cluster}.{req.attribute}", res).assertValueEqual( -- req.value) -+ -+ res = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, req.attribute)]) -+ val = res[endpoint][req.cluster][req.attribute] -+ if val != req.value: -+ raise Exception( -+ f"Read attribute {req.attribute.__qualname__}: expected value {req.value}, got {val}") - except Exception as ex: -- failed_zcl.append(str(ex)) -- if failed_zcl: -- self.logger.exception(f"Following attributes failed: {failed_zcl}") -+ failed_attribute_write.append(str(ex)) -+ if failed_attribute_write: -+ self.logger.exception(f"Following attributes failed: {failed_attribute_write}") - return False - return True - -- def TestSubscription(self, nodeid: int, endpoint: int): -+ async def TestSubscription(self, nodeid: int, endpoint: int): - desiredPath = None - receivedUpdate = 0 -- updateLock = threading.Lock() -- updateCv = threading.Condition(updateLock) -+ updateEvent = asyncio.Event() -+ loop = asyncio.get_running_loop() - - def OnValueChange(path: Attribute.TypedAttributePath, transaction: Attribute.SubscriptionTransaction) -> None: -- nonlocal desiredPath, updateCv, updateLock, receivedUpdate -+ nonlocal desiredPath, updateEvent, receivedUpdate - if path.Path != desiredPath: - return - - data = transaction.GetAttribute(path) - logger.info( - f"Received report from server: path: {path.Path}, value: {data}") -- with updateLock: -- receivedUpdate += 1 -- updateCv.notify_all() -- -- class _conductAttributeChange(threading.Thread): -- def __init__(self, devCtrl: ChipDeviceCtrl.ChipDeviceController, nodeid: int, endpoint: int): -- super(_conductAttributeChange, self).__init__() -- self.nodeid = nodeid -- self.endpoint = endpoint -- self.devCtrl = devCtrl -- -- def run(self): -- for i in range(5): -- time.sleep(3) -- self.devCtrl.ZCLSend( -- "OnOff", "Toggle", self.nodeid, self.endpoint, 0, {}) -+ receivedUpdate += 1 -+ loop.call_soon_threadsafe(updateEvent.set) -+ -+ async def _conductAttributeChange(devCtrl: ChipDeviceCtrl.ChipDeviceController, nodeid: int, endpoint: int): -+ for i in range(5): -+ await asyncio.sleep(3) -+ await self.devCtrl.SendCommand(nodeid, endpoint, Clusters.OnOff.Commands.Toggle()) - - try: - desiredPath = Clusters.Attribute.AttributePath( - EndpointId=1, ClusterId=6, AttributeId=0) - # OnOff Cluster, OnOff Attribute -- subscription = self.devCtrl.ZCLSubscribeAttribute( -- "OnOff", "OnOff", nodeid, endpoint, 1, 10) -+ subscription = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, Clusters.OnOff.Attributes.OnOff)], None, False, reportInterval=(1, 10), -+ keepSubscriptions=False, autoResubscribe=True) - subscription.SetAttributeUpdateCallback(OnValueChange) -- changeThread = _conductAttributeChange( -- self.devCtrl, nodeid, endpoint) - # Reset the number of subscriptions received as subscribing causes a callback. -- changeThread.start() -- with updateCv: -- while receivedUpdate < 5: -- # We should observe 5 attribute changes -- # The changing thread will change the value after 3 seconds. If we're waiting more than 10, assume something -- # is really wrong and bail out here with some information. -- if not updateCv.wait(10.0): -- self.logger.error( -- "Failed to receive subscription update") -- break -- -- # thread changes 5 times, and sleeps for 3 seconds in between. -- # Add an additional 3 seconds of slack. Timeout is in seconds. -- changeThread.join(18.0) -+ taskAttributeChange = loop.create_task(_conductAttributeChange(self.devCtrl, nodeid, endpoint)) - -- # -- # Clean-up by shutting down the sub. Otherwise, we're going to get callbacks through -- # OnValueChange on what will soon become an invalid -- # execution context above. -- # -- subscription.Shutdown() -+ while receivedUpdate < 5: -+ # We should observe 5 attribute changes -+ # The changing thread will change the value after 3 seconds. If we're waiting more than 10, assume something -+ # is really wrong and bail out here with some information. -+ try: -+ await asyncio.wait_for(updateEvent.wait(), 10) -+ updateEvent.clear() -+ except TimeoutError: -+ self.logger.error( -+ "Failed to receive subscription update") -+ break - -- if changeThread.is_alive(): -- # Thread join timed out -- self.logger.error("Failed to join change thread") -- return False -+ # At this point the task should really have done the three attribute, -+ # otherwise something is wrong. Wait for just 1s in case of a race -+ # condition between the last attribute update and the callback. -+ try: -+ await asyncio.wait_for(taskAttributeChange, 1) -+ except asyncio.TimeoutError: -+ # If attribute change task did not finish something is wrong. Cancel -+ # the task. -+ taskAttributeChange.cancel() -+ # This will throw a asyncio.CancelledError and makes sure the test -+ # is declared failed. -+ await taskAttributeChange - - return True if receivedUpdate == 5 else False - - except Exception as ex: - self.logger.exception(f"Failed to finish API test: {ex}") - return False -+ finally: -+ # -+ # Clean-up by shutting down the sub. Otherwise, we're going to get callbacks through -+ # OnValueChange on what will soon become an invalid -+ # execution context above. -+ # -+ subscription.Shutdown() - - return True - -@@ -1346,7 +1341,7 @@ class BaseTestHelper: - - return status == IM.Status.UnsupportedAccess - -- def TestSubscriptionResumption(self, nodeid: int, endpoint: int, remote_ip: str, ssh_port: int, remote_server_app: str): -+ async def TestSubscriptionResumption(self, nodeid: int, endpoint: int, remote_ip: str, ssh_port: int, remote_server_app: str): - ''' - This test validates that the device can resume the subscriptions after restarting. - It is executed in Linux Cirque tests and the steps of this test are: -@@ -1355,42 +1350,40 @@ class BaseTestHelper: - 3. Validate that the controller can receive a report from the remote server app - ''' - desiredPath = None -- receivedUpdate = False -- updateLock = threading.Lock() -- updateCv = threading.Condition(updateLock) -+ updateEvent = asyncio.Event() -+ loop = asyncio.get_running_loop() - - def OnValueReport(path: Attribute.TypedAttributePath, transaction: Attribute.SubscriptionTransaction) -> None: -- nonlocal desiredPath, updateCv, updateLock, receivedUpdate -+ nonlocal desiredPath, updateEvent, receivedUpdate - if path.Path != desiredPath: - return - - data = transaction.GetAttribute(path) - logger.info( - f"Received report from server: path: {path.Path}, value: {data}") -- with updateLock: -- receivedUpdate = True -- updateCv.notify_all() -+ loop.call_soon_threadsafe(updateEvent.set) - - try: - desiredPath = Clusters.Attribute.AttributePath( - EndpointId=0, ClusterId=0x28, AttributeId=5) - # BasicInformation Cluster, NodeLabel Attribute -- subscription = self.devCtrl.ZCLSubscribeAttribute( -- "BasicInformation", "NodeLabel", nodeid, endpoint, 1, 50, keepSubscriptions=True, autoResubscribe=False) -+ subscription = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, Clusters.BasicInformation.Attributes.NodeLabel)], None, False, reportInterval=(1, 50), -+ keepSubscriptions=True, autoResubscribe=False) - subscription.SetAttributeUpdateCallback(OnValueReport) - -- self.logger.info("Restart remote deivce") -+ self.logger.info("Restart remote device") - restartRemoteThread = restartRemoteDevice( - remote_ip, ssh_port, "root", "admin", remote_server_app, "--thread --discriminator 3840") - restartRemoteThread.start() - # After device restarts, the attribute will be set dirty so the subscription can receive - # the update -- with updateCv: -- while receivedUpdate is False: -- if not updateCv.wait(10.0): -- self.logger.error( -- "Failed to receive subscription resumption report") -- break -+ receivedUpdate = False -+ try: -+ await asyncio.wait_for(updateEvent.wait(), 10) -+ receivedUpdate = True -+ except TimeoutError: -+ self.logger.error( -+ "Failed to receive subscription resumption report") - - restartRemoteThread.join(10.0) - -@@ -1437,25 +1430,26 @@ class BaseTestHelper: - controller 1 in container 1 while the Step2 is executed in controller 2 in container 2 - ''' - -- def TestSubscriptionResumptionCapacityStep1(self, nodeid: int, endpoint: int, passcode: int, subscription_capacity: int): -+ async def TestSubscriptionResumptionCapacityStep1(self, nodeid: int, endpoint: int, passcode: int, subscription_capacity: int): - try: - # BasicInformation Cluster, NodeLabel Attribute - for i in range(subscription_capacity): -- self.devCtrl.ZCLSubscribeAttribute( -- "BasicInformation", "NodeLabel", nodeid, endpoint, 1, 50, keepSubscriptions=True, autoResubscribe=False) -+ await self.devCtrl.ReadAttribute(nodeid, [(endpoint, Clusters.BasicInformation.Attributes.NodeLabel)], None, -+ False, reportInterval=(1, 50), -+ keepSubscriptions=True, autoResubscribe=False) - - logger.info("Send OpenCommissioningWindow command on fist controller") - discriminator = 3840 - salt = secrets.token_bytes(16) - iterations = 2000 - verifier = GenerateVerifier(passcode, salt, iterations) -- asyncio.run(self.devCtrl.SendCommand( -+ await self.devCtrl.SendCommand( - nodeid, 0, Clusters.AdministratorCommissioning.Commands.OpenCommissioningWindow( - commissioningTimeout=180, - PAKEPasscodeVerifier=verifier, - discriminator=discriminator, - iterations=iterations, -- salt=salt), timedRequestTimeoutMs=10000)) -+ salt=salt), timedRequestTimeoutMs=10000) - return True - - except Exception as ex: -@@ -1464,8 +1458,8 @@ class BaseTestHelper: - - return True - -- def TestSubscriptionResumptionCapacityStep2(self, nodeid: int, endpoint: int, remote_ip: str, ssh_port: int, -- remote_server_app: str, subscription_capacity: int): -+ async def TestSubscriptionResumptionCapacityStep2(self, nodeid: int, endpoint: int, remote_ip: str, ssh_port: int, -+ remote_server_app: str, subscription_capacity: int): - try: - self.logger.info("Restart remote deivce") - extra_agrs = f"--thread --discriminator 3840 --subscription-capacity {subscription_capacity}" -@@ -1479,8 +1473,9 @@ class BaseTestHelper: - self.logger.info("Send a new subscription request from the second controller") - # Close previous session so that the second controller will res-establish the session with the remote device - self.devCtrl.CloseSession(nodeid) -- self.devCtrl.ZCLSubscribeAttribute( -- "BasicInformation", "NodeLabel", nodeid, endpoint, 1, 50, keepSubscriptions=True, autoResubscribe=False) -+ await self.devCtrl.ReadAttribute(nodeid, [(endpoint, Clusters.BasicInformation.Attributes.NodeLabel)], None, -+ False, reportInterval=(1, 50), -+ keepSubscriptions=True, autoResubscribe=False) - - if restartRemoteThread.is_alive(): - # Thread join timed out -diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py -index 9ceaa35d24..33ae713fe0 100755 ---- a/src/controller/python/test/test_scripts/mobile-device-test.py -+++ b/src/controller/python/test/test_scripts/mobile-device-test.py -@@ -129,9 +129,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): - "Failed to test Read Basic Attributes") - - logger.info("Testing attribute writing") -- FailIfNot(test.TestWriteBasicAttributes(nodeid=device_nodeid, -- endpoint=ENDPOINT_ID, -- group=GROUP_ID), -+ FailIfNot(asyncio.run(test.TestWriteBasicAttributes(nodeid=device_nodeid, -+ endpoint=ENDPOINT_ID)), - "Failed to test Write Basic Attributes") - - logger.info("Testing attribute reading basic again") -@@ -141,11 +140,11 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): - "Failed to test Read Basic Attributes") - - logger.info("Testing subscription") -- FailIfNot(test.TestSubscription(nodeid=device_nodeid, endpoint=LIGHTING_ENDPOINT_ID), -+ FailIfNot(asyncio.run(test.TestSubscription(nodeid=device_nodeid, endpoint=LIGHTING_ENDPOINT_ID)), - "Failed to subscribe attributes.") - - logger.info("Testing another subscription that kills previous subscriptions") -- FailIfNot(test.TestSubscription(nodeid=device_nodeid, endpoint=LIGHTING_ENDPOINT_ID), -+ FailIfNot(asyncio.run(test.TestSubscription(nodeid=device_nodeid, endpoint=LIGHTING_ENDPOINT_ID)), - "Failed to subscribe attributes.") - - logger.info("Testing re-subscription") -diff --git a/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl1.py b/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl1.py -index 19065b8a35..e02564e293 100755 ---- a/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl1.py -+++ b/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl1.py -@@ -19,6 +19,7 @@ - - # Commissioning test. - -+import asyncio - import os - import sys - from optparse import OptionParser -@@ -113,8 +114,8 @@ def main(): - "Failed on on-network commissioing") - - FailIfNot( -- test.TestSubscriptionResumptionCapacityStep1( -- options.nodeid, TEST_ENDPOINT_ID, options.setuppin, options.subscriptionCapacity), -+ asyncio.run(test.TestSubscriptionResumptionCapacityStep1( -+ options.nodeid, TEST_ENDPOINT_ID, options.setuppin, options.subscriptionCapacity)), - "Failed on step 1 of testing subscription resumption capacity") - - timeoutTicker.stop() -diff --git a/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl2.py b/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl2.py -index 2f3058afcd..ac449a9f54 100755 ---- a/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl2.py -+++ b/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl2.py -@@ -19,6 +19,7 @@ - - # Commissioning test. - -+import asyncio - import os - import sys - from optparse import OptionParser -@@ -125,8 +126,9 @@ def main(): - "Failed on on-network commissioing") - - FailIfNot( -- test.TestSubscriptionResumptionCapacityStep2(options.nodeid, TEST_ENDPOINT_ID, options.deviceAddress, -- TEST_SSH_PORT, options.remoteServerApp, options.subscriptionCapacity), -+ asyncio.run( -+ test.TestSubscriptionResumptionCapacityStep2(options.nodeid, TEST_ENDPOINT_ID, options.deviceAddress, -+ TEST_SSH_PORT, options.remoteServerApp, options.subscriptionCapacity)), - "Failed on testing subscription resumption capacity") - - timeoutTicker.stop() -diff --git a/src/controller/python/test/test_scripts/subscription_resumption_test.py b/src/controller/python/test/test_scripts/subscription_resumption_test.py -index 8b2000fb07..79edf6a289 100755 ---- a/src/controller/python/test/test_scripts/subscription_resumption_test.py -+++ b/src/controller/python/test/test_scripts/subscription_resumption_test.py -@@ -19,6 +19,7 @@ - - # Commissioning test. - -+import asyncio - import os - import sys - from optparse import OptionParser -@@ -115,8 +116,8 @@ def main(): - "Failed on on-network commissioing") - - FailIfNot( -- test.TestSubscriptionResumption(options.nodeid, TEST_ENDPOINT_ID, options.deviceAddress, -- TEST_SSH_PORT, options.remoteServerApp), "Failed to resume subscription") -+ asyncio.run(test.TestSubscriptionResumption(options.nodeid, TEST_ENDPOINT_ID, options.deviceAddress, -+ TEST_SSH_PORT, options.remoteServerApp)), "Failed to resume subscription") - - timeoutTicker.stop() - -diff --git a/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py b/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py -index 1f6411f636..4932e5b4cc 100755 ---- a/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py -+++ b/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py -@@ -19,11 +19,13 @@ - - # Commissioning test. - -+import asyncio - import os - import sys - from optparse import OptionParser - - from base import BaseTestHelper, FailIfNot, TestFail, TestTimeout, logger -+from chip import clusters as Clusters - - TEST_DISCRIMINATOR = 3840 - TEST_SETUPPIN = 20202021 -@@ -101,10 +103,12 @@ def main(): - - FailIfNot( - test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), -- "Failed on on-network commissioing") -+ "Failed on on-network commissioning") -+ - try: -- test.devCtrl.ZCLSubscribeAttribute("BasicInformation", "NodeLabel", options.nodeid, TEST_ENDPOINT_ID, 1, 2, -- keepSubscriptions=True, autoResubscribe=False) -+ asyncio.run(test.devCtrl.ReadAttribute(options.nodeid, -+ [(TEST_ENDPOINT_ID, Clusters.BasicInformation.Attributes.NodeLabel)], -+ None, False, reportInterval=(1, 2), keepSubscriptions=True, autoResubscribe=False)) - except Exception as ex: - TestFail(f"Failed to subscribe attribute: {ex}") - --- -2.45.2 - diff --git a/0008-Python-Eliminate-ZCLReadAttribute-ZCLSend-33428.patch b/0008-Python-Eliminate-ZCLReadAttribute-ZCLSend-33428.patch deleted file mode 100644 index dd33643..0000000 --- a/0008-Python-Eliminate-ZCLReadAttribute-ZCLSend-33428.patch +++ /dev/null @@ -1,589 +0,0 @@ -From 18e6fcae6e2dcaa0a39306c19de0246b6d0a5764 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 16 May 2024 09:13:00 +0200 -Subject: [PATCH] [Python] Eliminate ZCLReadAttribute/ZCLSend (#33428) - -* Convert TestLevelControlCluster to asyncio - -Remove ZCLReadAttribute and ZCLSend API use from the level control -test TestLevelControlCluster and convert to asyncio. - -* Convert TestReadBasicAttributes to asyncio - -Remove ZCLReadAttribute API use from basic information cluster test -and convert to use asyncio. - -* Use SendCommand directly in send_zcl_command - -Avoid using ZCLSend API instead use SendCommand directly in the -send_zcl_command helper function. - -* Convert TestFailsafe to use asyncio/SendCommand - -Remove ZCLSend API usage and call SendCommand directly. Also convert -the test to a test using asyncio. - -* Convert TestOnOffCluster to use asyncio/SendCommand - -Remove ZCLSend API usage and call SendCommand directly. Also convert -the test to a test using asyncio. - -* Drop TestResult helper class - -The class is no longer required. Test results are tested directly. - -* Fix send_zcl_command argument formatting - -* Catch exception more specifically - -* Fix TestWriteBasicAttributes for all cases - -It seems TestWriteBasicAttributes did not correctly write -the attributes. The broad exception handling seems to have hidden -this issue even. Make sure the attributes with the correct value -get written, and check for unexpected and expected IM errors -in the per-attribute results specifically. - -* Fix TestFailsafe by catching correct exception - -* Drop unused import ---- - .../python/test/test_scripts/base.py | 189 ++++++++---------- - .../commissioning_failure_test.py | 6 +- - .../test/test_scripts/commissioning_test.py | 6 +- - .../test/test_scripts/failsafe_tests.py | 3 +- - .../test/test_scripts/mobile-device-test.py | 30 ++- - .../test_scripts/split_commissioning_test.py | 11 +- - .../mbed/integration_tests/common/utils.py | 41 ++-- - 7 files changed, 134 insertions(+), 152 deletions(-) - -diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py -index 5edb78f8e1..c9b1881dd7 100644 ---- a/src/controller/python/test/test_scripts/base.py -+++ b/src/controller/python/test/test_scripts/base.py -@@ -178,29 +178,6 @@ class TestTimeout(threading.Thread): - TestFail("Timeout", doCrash=True) - - --class TestResult: -- def __init__(self, operationName, result): -- self.operationName = operationName -- self.result = result -- -- def assertStatusEqual(self, expected): -- if self.result is None: -- raise Exception(f"{self.operationName}: no result got") -- if self.result.status != expected: -- raise Exception( -- f"{self.operationName}: expected status {expected}, got {self.result.status}") -- return self -- -- def assertValueEqual(self, expected): -- self.assertStatusEqual(0) -- if self.result is None: -- raise Exception(f"{self.operationName}: no result got") -- if self.result.value != expected: -- raise Exception( -- f"{self.operationName}: expected value {expected}, got {self.result.value}") -- return self -- -- - class BaseTestHelper: - def __init__(self, nodeid: int, paaTrustStorePath: str, testCommissioner: bool = False, - keypair: p256keypair.P256Keypair = None): -@@ -368,15 +345,16 @@ class BaseTestHelper: - def TestUsedTestCommissioner(self): - return self.devCtrl.GetTestCommissionerUsed() - -- def TestFailsafe(self, nodeid: int): -+ async def TestFailsafe(self, nodeid: int): - self.logger.info("Testing arm failsafe") - - self.logger.info("Setting failsafe on CASE connection") -- err, resp = self.devCtrl.ZCLSend("GeneralCommissioning", "ArmFailSafe", nodeid, -- 0, 0, dict(expiryLengthSeconds=60, breadcrumb=1), blocking=True) -- if err != 0: -+ try: -+ resp = await self.devCtrl.SendCommand(nodeid, 0, -+ Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=60, breadcrumb=1)) -+ except IM.InteractionModelError as ex: - self.logger.error( -- "Failed to send arm failsafe command error is {} with im response{}".format(err, resp)) -+ "Failed to send arm failsafe command error is {}".format(ex.status)) - return False - - if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk: -@@ -387,17 +365,17 @@ class BaseTestHelper: - self.logger.info( - "Attempting to open basic commissioning window - this should fail since the failsafe is armed") - try: -- asyncio.run(self.devCtrl.SendCommand( -+ await self.devCtrl.SendCommand( - nodeid, - 0, - Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180), - timedRequestTimeoutMs=10000 -- )) -+ ) - # we actually want the exception here because we want to see a failure, so return False here - self.logger.error( - 'Incorrectly succeeded in opening basic commissioning window') - return False -- except Exception: -+ except IM.InteractionModelError: - pass - - # TODO: -@@ -413,39 +391,39 @@ class BaseTestHelper: - self.logger.info( - "Attempting to open enhanced commissioning window - this should fail since the failsafe is armed") - try: -- asyncio.run(self.devCtrl.SendCommand( -+ await self.devCtrl.SendCommand( - nodeid, 0, Clusters.AdministratorCommissioning.Commands.OpenCommissioningWindow( - commissioningTimeout=180, - PAKEPasscodeVerifier=verifier, - discriminator=discriminator, - iterations=iterations, -- salt=salt), timedRequestTimeoutMs=10000)) -+ salt=salt), timedRequestTimeoutMs=10000) - - # we actually want the exception here because we want to see a failure, so return False here - self.logger.error( - 'Incorrectly succeeded in opening enhanced commissioning window') - return False -- except Exception: -+ except IM.InteractionModelError: - pass - - self.logger.info("Disarming failsafe on CASE connection") -- err, resp = self.devCtrl.ZCLSend("GeneralCommissioning", "ArmFailSafe", nodeid, -- 0, 0, dict(expiryLengthSeconds=0, breadcrumb=1), blocking=True) -- if err != 0: -+ try: -+ resp = await self.devCtrl.SendCommand(nodeid, 0, -+ Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=0, breadcrumb=1)) -+ except IM.InteractionModelError as ex: - self.logger.error( -- "Failed to send arm failsafe command error is {} with im response{}".format(err, resp)) -+ "Failed to send arm failsafe command error is {}".format(ex.status)) - return False - - self.logger.info( - "Opening Commissioning Window - this should succeed since the failsafe was just disarmed") - try: -- asyncio.run( -- self.devCtrl.SendCommand( -- nodeid, -- 0, -- Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180), -- timedRequestTimeoutMs=10000 -- )) -+ await self.devCtrl.SendCommand( -+ nodeid, -+ 0, -+ Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180), -+ timedRequestTimeoutMs=10000 -+ ) - except Exception: - self.logger.error( - 'Failed to open commissioning window after disarming failsafe') -@@ -453,11 +431,12 @@ class BaseTestHelper: - - self.logger.info( - "Attempting to arm failsafe over CASE - this should fail since the commissioning window is open") -- err, resp = self.devCtrl.ZCLSend("GeneralCommissioning", "ArmFailSafe", nodeid, -- 0, 0, dict(expiryLengthSeconds=60, breadcrumb=1), blocking=True) -- if err != 0: -+ try: -+ resp = await self.devCtrl.SendCommand(nodeid, 0, -+ Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=60, breadcrumb=1)) -+ except IM.InteractionModelError as ex: - self.logger.error( -- "Failed to send arm failsafe command error is {} with im response{}".format(err, resp)) -+ "Failed to send arm failsafe command error is {}".format(ex.status)) - return False - if resp.errorCode is Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kBusyWithOtherAdmin: - return True -@@ -1094,50 +1073,48 @@ class BaseTestHelper: - self.devCtrl.SetThreadOperationalDataset(bytes.fromhex(dataset)) - return True - -- def TestOnOffCluster(self, nodeid: int, endpoint: int, group: int): -+ async def TestOnOffCluster(self, nodeid: int, endpoint: int): - self.logger.info( - "Sending On/Off commands to device {} endpoint {}".format(nodeid, endpoint)) -- err, resp = self.devCtrl.ZCLSend("OnOff", "On", nodeid, -- endpoint, group, {}, blocking=True) -- if err != 0: -+ -+ try: -+ await self.devCtrl.SendCommand(nodeid, endpoint, -+ Clusters.OnOff.Commands.On()) -+ except IM.InteractionModelError as ex: - self.logger.error( -- "failed to send OnOff.On: error is {} with im response{}".format(err, resp)) -+ "failed to send OnOff.On: error is {}".format(ex.status)) - return False -- err, resp = self.devCtrl.ZCLSend("OnOff", "Off", nodeid, -- endpoint, group, {}, blocking=True) -- if err != 0: -+ -+ try: -+ await self.devCtrl.SendCommand(nodeid, endpoint, -+ Clusters.OnOff.Commands.Off()) -+ except IM.InteractionModelError as ex: - self.logger.error( -- "failed to send OnOff.Off: error is {} with im response {}".format(err, resp)) -+ "failed to send OnOff.Off: error is {}".format(ex.status)) - return False - return True - -- def TestLevelControlCluster(self, nodeid: int, endpoint: int, group: int): -+ async def TestLevelControlCluster(self, nodeid: int, endpoint: int): - self.logger.info( - f"Sending MoveToLevel command to device {nodeid} endpoint {endpoint}") -- try: -- commonArgs = dict(transitionTime=0, optionsMask=1, optionsOverride=1) - -+ commonArgs = dict(transitionTime=0, optionsMask=1, optionsOverride=1) -+ -+ async def _moveClusterLevel(setLevel): -+ await self.devCtrl.SendCommand(nodeid, -+ endpoint, -+ Clusters.LevelControl.Commands.MoveToLevel(**commonArgs, level=setLevel)) -+ res = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, Clusters.LevelControl.Attributes.CurrentLevel)]) -+ readVal = res[endpoint][Clusters.LevelControl][Clusters.LevelControl.Attributes.CurrentLevel] -+ if readVal != setLevel: -+ raise Exception(f"Read attribute LevelControl.CurrentLevel: expected value {setLevel}, got {readVal}") -+ -+ try: - # Move to 1 -- self.devCtrl.ZCLSend("LevelControl", "MoveToLevel", nodeid, -- endpoint, group, dict(**commonArgs, level=1), blocking=True) -- res = self.devCtrl.ZCLReadAttribute(cluster="LevelControl", -- attribute="CurrentLevel", -- nodeid=nodeid, -- endpoint=endpoint, -- groupid=group) -- TestResult("Read attribute LevelControl.CurrentLevel", -- res).assertValueEqual(1) -+ await _moveClusterLevel(1) - - # Move to 254 -- self.devCtrl.ZCLSend("LevelControl", "MoveToLevel", nodeid, -- endpoint, group, dict(**commonArgs, level=254), blocking=True) -- res = self.devCtrl.ZCLReadAttribute(cluster="LevelControl", -- attribute="CurrentLevel", -- nodeid=nodeid, -- endpoint=endpoint, -- groupid=group) -- TestResult("Read attribute LevelControl.CurrentLevel", -- res).assertValueEqual(254) -+ await _moveClusterLevel(254) - - return True - except Exception as ex: -@@ -1170,29 +1147,27 @@ class BaseTestHelper: - self.logger.exception("Failed to resolve. {}".format(ex)) - return False - -- def TestReadBasicAttributes(self, nodeid: int, endpoint: int, group: int): -+ async def TestReadBasicAttributes(self, nodeid: int, endpoint: int): -+ attrs = Clusters.BasicInformation.Attributes - basic_cluster_attrs = { -- "VendorName": "TEST_VENDOR", -- "VendorID": 0xFFF1, -- "ProductName": "TEST_PRODUCT", -- "ProductID": 0x8001, -- "NodeLabel": "Test", -- "Location": "XX", -- "HardwareVersion": 0, -- "HardwareVersionString": "TEST_VERSION", -- "SoftwareVersion": 1, -- "SoftwareVersionString": "1.0", -+ attrs.VendorName: "TEST_VENDOR", -+ attrs.VendorID: 0xFFF1, -+ attrs.ProductName: "TEST_PRODUCT", -+ attrs.ProductID: 0x8001, -+ attrs.NodeLabel: "Test", -+ attrs.Location: "XX", -+ attrs.HardwareVersion: 0, -+ attrs.HardwareVersionString: "TEST_VERSION", -+ attrs.SoftwareVersion: 1, -+ attrs.SoftwareVersionString: "1.0", - } - failed_zcl = {} - for basic_attr, expected_value in basic_cluster_attrs.items(): - try: -- res = self.devCtrl.ZCLReadAttribute(cluster="BasicInformation", -- attribute=basic_attr, -- nodeid=nodeid, -- endpoint=endpoint, -- groupid=group) -- TestResult(f"Read attribute {basic_attr}", res).assertValueEqual( -- expected_value) -+ res = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, basic_attr)]) -+ readVal = res[endpoint][Clusters.BasicInformation][basic_attr] -+ if readVal != expected_value: -+ raise Exception(f"Read attribute: expected value {expected_value}, got {readVal}") - except Exception as ex: - failed_zcl[basic_attr] = str(ex) - if failed_zcl: -@@ -1216,16 +1191,16 @@ class BaseTestHelper: - failed_attribute_write = [] - for req in requests: - try: -- try: -- await self.devCtrl.WriteAttribute(nodeid, [(endpoint, req.attribute, 0)]) -- if req.expected_status != IM.Status.Success: -- raise AssertionError( -- f"Write attribute {req.attribute.__qualname__} expects failure but got success response") -- except Exception as ex: -- if req.expected_status != IM.Status.Success: -- continue -- else: -- raise ex -+ # Errors tested here is in the per-attribute result list (type AttributeStatus) -+ write_res = await self.devCtrl.WriteAttribute(nodeid, [(endpoint, req.attribute(req.value))]) -+ status = write_res[0].Status -+ if req.expected_status != status: -+ raise AssertionError( -+ f"Write attribute {req.attribute.__qualname__} expects {req.expected_status} but got {status}") -+ -+ # Only execute read tests where write is successful. -+ if req.expected_status != IM.Status.Success: -+ continue - - res = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, req.attribute)]) - val = res[endpoint][req.cluster][req.attribute] -diff --git a/src/controller/python/test/test_scripts/commissioning_failure_test.py b/src/controller/python/test/test_scripts/commissioning_failure_test.py -index 4681dd108d..a535c8b184 100755 ---- a/src/controller/python/test/test_scripts/commissioning_failure_test.py -+++ b/src/controller/python/test/test_scripts/commissioning_failure_test.py -@@ -19,6 +19,7 @@ - - # Commissioning test. - -+import asyncio - import os - import sys - from optparse import OptionParser -@@ -121,9 +122,8 @@ def main(): - FailIfNot(test.TestCommissionFailure(1, 0), "Failed to commission device") - - logger.info("Testing on off cluster") -- FailIfNot(test.TestOnOffCluster(nodeid=1, -- endpoint=LIGHTING_ENDPOINT_ID, -- group=GROUP_ID), "Failed to test on off cluster") -+ FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=1, -+ endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") - - timeoutTicker.stop() - -diff --git a/src/controller/python/test/test_scripts/commissioning_test.py b/src/controller/python/test/test_scripts/commissioning_test.py -index b6adc0f477..4a7f15d6c3 100755 ---- a/src/controller/python/test/test_scripts/commissioning_test.py -+++ b/src/controller/python/test/test_scripts/commissioning_test.py -@@ -19,6 +19,7 @@ - - # Commissioning test. - -+import asyncio - import os - import sys - from optparse import OptionParser -@@ -146,9 +147,8 @@ def main(): - TestFail("Must provide device address or setup payload to commissioning the device") - - logger.info("Testing on off cluster") -- FailIfNot(test.TestOnOffCluster(nodeid=options.nodeid, -- endpoint=LIGHTING_ENDPOINT_ID, -- group=GROUP_ID), "Failed to test on off cluster") -+ FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=options.nodeid, -+ endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") - - FailIfNot(test.TestUsedTestCommissioner(), - "Test commissioner check failed") -diff --git a/src/controller/python/test/test_scripts/failsafe_tests.py b/src/controller/python/test/test_scripts/failsafe_tests.py -index 4b3838430c..d1a2034e73 100755 ---- a/src/controller/python/test/test_scripts/failsafe_tests.py -+++ b/src/controller/python/test/test_scripts/failsafe_tests.py -@@ -19,6 +19,7 @@ - - # Commissioning test. - -+import asyncio - import os - import sys - from optparse import OptionParser -@@ -99,7 +100,7 @@ def main(): - nodeid=1), - "Failed to finish key exchange") - -- FailIfNot(test.TestFailsafe(nodeid=1), "Failed failsafe test") -+ FailIfNot(asyncio.run(test.TestFailsafe(nodeid=1)), "Failed failsafe test") - - timeoutTicker.stop() - -diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py -index 33ae713fe0..8f6f534dce 100755 ---- a/src/controller/python/test/test_scripts/mobile-device-test.py -+++ b/src/controller/python/test/test_scripts/mobile-device-test.py -@@ -102,20 +102,17 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): - logger.info("Testing datamodel functions") - - logger.info("Testing on off cluster") -- FailIfNot(test.TestOnOffCluster(nodeid=device_nodeid, -- endpoint=LIGHTING_ENDPOINT_ID, -- group=GROUP_ID), "Failed to test on off cluster") -+ FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=device_nodeid, -+ endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") - - logger.info("Testing level control cluster") -- FailIfNot(test.TestLevelControlCluster(nodeid=device_nodeid, -- endpoint=LIGHTING_ENDPOINT_ID, -- group=GROUP_ID), -+ FailIfNot(asyncio.run(test.TestLevelControlCluster(nodeid=device_nodeid, -+ endpoint=LIGHTING_ENDPOINT_ID)), - "Failed to test level control cluster") - - logger.info("Testing sending commands to non exist endpoint") -- FailIfNot(not test.TestOnOffCluster(nodeid=device_nodeid, -- endpoint=233, -- group=GROUP_ID), "Failed to test on off cluster on non-exist endpoint") -+ FailIfNot(not asyncio.run(test.TestOnOffCluster(nodeid=device_nodeid, -+ endpoint=233)), "Failed to test on off cluster on non-exist endpoint") - - # Test experimental Python cluster objects API - logger.info("Testing cluster objects API") -@@ -123,9 +120,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): - "Failed when testing Python Cluster Object APIs") - - logger.info("Testing attribute reading") -- FailIfNot(test.TestReadBasicAttributes(nodeid=device_nodeid, -- endpoint=ENDPOINT_ID, -- group=GROUP_ID), -+ FailIfNot(asyncio.run(test.TestReadBasicAttributes(nodeid=device_nodeid, -+ endpoint=ENDPOINT_ID)), - "Failed to test Read Basic Attributes") - - logger.info("Testing attribute writing") -@@ -134,9 +130,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): - "Failed to test Write Basic Attributes") - - logger.info("Testing attribute reading basic again") -- FailIfNot(test.TestReadBasicAttributes(nodeid=1, -- endpoint=ENDPOINT_ID, -- group=GROUP_ID), -+ FailIfNot(asyncio.run(test.TestReadBasicAttributes(nodeid=1, -+ endpoint=ENDPOINT_ID)), - "Failed to test Read Basic Attributes") - - logger.info("Testing subscription") -@@ -152,9 +147,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): - "Failed to validated re-subscription") - - logger.info("Testing on off cluster over resolved connection") -- FailIfNot(test.TestOnOffCluster(nodeid=device_nodeid, -- endpoint=LIGHTING_ENDPOINT_ID, -- group=GROUP_ID), "Failed to test on off cluster") -+ FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=device_nodeid, -+ endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") - - logger.info("Testing writing/reading fabric sensitive data") - asyncio.run(test.TestFabricSensitive(nodeid=device_nodeid)) -diff --git a/src/controller/python/test/test_scripts/split_commissioning_test.py b/src/controller/python/test/test_scripts/split_commissioning_test.py -index 47fedb3aad..9233d58b90 100755 ---- a/src/controller/python/test/test_scripts/split_commissioning_test.py -+++ b/src/controller/python/test/test_scripts/split_commissioning_test.py -@@ -19,6 +19,7 @@ - - # Commissioning test. - -+import asyncio - import os - import sys - from optparse import OptionParser -@@ -118,14 +119,12 @@ def main(): - "Failed to commission device 2") - - logger.info("Testing on off cluster on device 1") -- FailIfNot(test.TestOnOffCluster(nodeid=1, -- endpoint=LIGHTING_ENDPOINT_ID, -- group=GROUP_ID), "Failed to test on off cluster on device 1") -+ FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=1, -+ endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster on device 1") - - logger.info("Testing on off cluster on device 2") -- FailIfNot(test.TestOnOffCluster(nodeid=2, -- endpoint=LIGHTING_ENDPOINT_ID, -- group=GROUP_ID), "Failed to test on off cluster on device 2") -+ FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=2, -+ endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster on device 2") - - timeoutTicker.stop() - -diff --git a/src/test_driver/mbed/integration_tests/common/utils.py b/src/test_driver/mbed/integration_tests/common/utils.py -index 036b612d7b..2b1db4da30 100644 ---- a/src/test_driver/mbed/integration_tests/common/utils.py -+++ b/src/test_driver/mbed/integration_tests/common/utils.py -@@ -14,6 +14,7 @@ - # limitations under the License. - - -+import asyncio - import logging - import platform - import random -@@ -114,21 +115,33 @@ def send_zcl_command(devCtrl, line): - if len(args) < 5: - raise exceptions.InvalidArgumentCount(5, len(args)) - -- if args[0] not in all_commands: -- raise exceptions.UnknownCluster(args[0]) -- command = all_commands.get(args[0]).get(args[1], None) -+ cluster = args[0] -+ command = args[1] -+ if cluster not in all_commands: -+ raise exceptions.UnknownCluster(cluster) -+ commandObj = all_commands.get(cluster).get(command, None) - # When command takes no arguments, (not command) is True -- if command is None: -- raise exceptions.UnknownCommand(args[0], args[1]) -- err, res = devCtrl.ZCLSend(args[0], args[1], int( -- args[2]), int(args[3]), int(args[4]), FormatZCLArguments(args[5:], command), blocking=True) -- if err != 0: -- log.error("Failed to send ZCL command [{}] {}.".format(err, res)) -- elif res is not None: -- log.info("Success, received command response:") -- log.info(res) -- else: -- log.info("Success, no command response.") -+ if commandObj is None: -+ raise exceptions.UnknownCommand(cluster, command) -+ -+ try: -+ req = commandObj(**FormatZCLArguments(args[5:], commandObj)) -+ except BaseException: -+ raise exceptions.UnknownCommand(cluster, command) -+ -+ nodeid = int(args[2]) -+ endpoint = int(args[3]) -+ try: -+ res = asyncio.run(devCtrl.SendCommand(nodeid, endpoint, req)) -+ logging.debug(f"CommandResponse {res}") -+ if res is not None: -+ log.info("Success, received command response:") -+ log.info(res) -+ else: -+ log.info("Success, no command response.") -+ except exceptions.InteractionModelError as ex: -+ return (int(ex.status), None) -+ log.error("Failed to send ZCL command [{}] {}.".format(int(ex.status), None)) - except exceptions.ChipStackException as ex: - log.error("An exception occurred during processing ZCL command:") - log.error(str(ex)) --- -2.45.2 - diff --git a/0009-Python-Create-pairingDelegate-for-each-DeviceControl.patch b/0009-Python-Create-pairingDelegate-for-each-DeviceControl.patch deleted file mode 100644 index 92b3aa5..0000000 --- a/0009-Python-Create-pairingDelegate-for-each-DeviceControl.patch +++ /dev/null @@ -1,671 +0,0 @@ -From e6f0f69d0d61c60cd7ad3b84f59c50794d95797a Mon Sep 17 00:00:00 2001 -From: "tianfeng.yang" <130436698+tianfeng-yang@users.noreply.github.com> -Date: Wed, 17 Apr 2024 21:53:13 +0800 -Subject: [PATCH] [Python] Create pairingDelegate for each DeviceController - (#32369) - -* Create pairingDelegate for each DeviceController - -* restore the original pairingcomplete callback logic ---- - .../ChipDeviceController-ScriptBinding.cpp | 97 +++++++++++-------- - src/controller/python/OpCredsBinding.cpp | 40 +++++--- - src/controller/python/chip/ChipDeviceCtrl.py | 69 ++++++++----- - 3 files changed, 128 insertions(+), 78 deletions(-) - -diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -index d78c9da8ed..a55d3865bd 100644 ---- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp -+++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -@@ -105,7 +105,6 @@ chip::Controller::CommissioningParameters sCommissioningParameters; - - } // namespace - --chip::Controller::ScriptDevicePairingDelegate sPairingDelegate; - chip::Controller::ScriptPairingDeviceDiscoveryDelegate sPairingDeviceDiscoveryDelegate; - chip::Credentials::GroupDataProviderImpl sGroupDataProvider; - chip::Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore; -@@ -121,9 +120,8 @@ extern "C" { - PyChipError pychip_DeviceController_StackInit(Controller::Python::StorageAdapter * storageAdapter, bool enableServerInteractions); - PyChipError pychip_DeviceController_StackShutdown(); - --PyChipError pychip_DeviceController_NewDeviceController(chip::Controller::DeviceCommissioner ** outDevCtrl, -- chip::NodeId localDeviceId, bool useTestCommissioner); --PyChipError pychip_DeviceController_DeleteDeviceController(chip::Controller::DeviceCommissioner * devCtrl); -+PyChipError pychip_DeviceController_DeleteDeviceController(chip::Controller::DeviceCommissioner * devCtrl, -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate); - PyChipError pychip_DeviceController_GetAddressAndPort(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, - char * outAddress, uint64_t maxAddressLen, uint16_t * outPort); - PyChipError pychip_DeviceController_GetCompressedFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId); -@@ -168,15 +166,17 @@ PyChipError pychip_DeviceController_DiscoverCommissionableNodesDeviceType(chip:: - uint16_t device_type); - PyChipError pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled(chip::Controller::DeviceCommissioner * devCtrl); - --PyChipError pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, uint64_t nodeId, -- uint32_t setupPasscode, const uint8_t filterType, const char * filterParam, -- uint32_t discoveryTimeoutMsec); -+PyChipError pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ uint64_t nodeId, uint32_t setupPasscode, const uint8_t filterType, -+ const char * filterParam, uint32_t discoveryTimeoutMsec); - - PyChipError pychip_DeviceController_PostTaskOnChipThread(ChipThreadTaskRunnerFunct callback, void * pythonContext); - --PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid, -- uint16_t timeout, uint32_t iteration, uint16_t discriminator, -- uint8_t optionInt); -+PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::DeviceCommissioner * devCtrl, -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ chip::NodeId nodeid, uint16_t timeout, uint32_t iteration, -+ uint16_t discriminator, uint8_t optionInt); - - void pychip_DeviceController_PrintDiscoveredDevices(chip::Controller::DeviceCommissioner * devCtrl); - bool pychip_DeviceController_GetIPForDiscoveredDevice(chip::Controller::DeviceCommissioner * devCtrl, int idx, char * addrStr, -@@ -184,19 +184,28 @@ bool pychip_DeviceController_GetIPForDiscoveredDevice(chip::Controller::DeviceCo - - // Pairing Delegate - PyChipError --pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(chip::Controller::DeviceCommissioner * devCtrl, -+pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, - chip::Controller::DevicePairingDelegate_OnPairingCompleteFunct callback); - - PyChipError pychip_ScriptDevicePairingDelegate_SetCommissioningCompleteCallback( -- chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnCommissioningCompleteFunct callback); -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ chip::Controller::DevicePairingDelegate_OnCommissioningCompleteFunct callback); - - PyChipError pychip_ScriptDevicePairingDelegate_SetCommissioningStatusUpdateCallback( -- chip::Controller::DeviceCommissioner * devCtrl, -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, - chip::Controller::DevicePairingDelegate_OnCommissioningStatusUpdateFunct callback); -+ - PyChipError --pychip_ScriptDevicePairingDelegate_SetFabricCheckCallback(chip::Controller::DevicePairingDelegate_OnFabricCheckFunct callback); -+pychip_ScriptDevicePairingDelegate_SetFabricCheckCallback(chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ chip::Controller::DevicePairingDelegate_OnFabricCheckFunct callback); -+ - PyChipError pychip_ScriptDevicePairingDelegate_SetOpenWindowCompleteCallback( -- chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnWindowOpenCompleteFunct callback); -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ chip::Controller::DevicePairingDelegate_OnWindowOpenCompleteFunct callback); -+ -+PyChipError -+pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ bool value); - - // BLE - PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::DeviceCommissioner * devCtrl); -@@ -354,7 +363,6 @@ const char * pychip_DeviceController_StatusReportToString(uint32_t profileId, ui - PyChipError pychip_DeviceController_ConnectBLE(chip::Controller::DeviceCommissioner * devCtrl, uint16_t discriminator, - uint32_t setupPINCode, chip::NodeId nodeid) - { -- sPairingDelegate.SetExpectingPairingComplete(true); - return ToPyChipError(devCtrl->PairDevice(nodeid, - chip::RendezvousParameters() - .SetPeerAddress(Transport::PeerAddress(Transport::Type::kBle)) -@@ -378,14 +386,12 @@ PyChipError pychip_DeviceController_ConnectIP(chip::Controller::DeviceCommission - addr.SetTransportType(chip::Transport::Type::kUdp).SetIPAddress(peerAddr).SetInterface(ifaceOutput); - params.SetPeerAddress(addr).SetDiscriminator(0); - -- sPairingDelegate.SetExpectingPairingComplete(true); - return ToPyChipError(devCtrl->PairDevice(nodeid, params, sCommissioningParameters)); - } - - PyChipError pychip_DeviceController_ConnectWithCode(chip::Controller::DeviceCommissioner * devCtrl, const char * onboardingPayload, - chip::NodeId nodeid, uint8_t discoveryType) - { -- sPairingDelegate.SetExpectingPairingComplete(true); - return ToPyChipError(devCtrl->PairDevice(nodeid, onboardingPayload, sCommissioningParameters, - static_cast(discoveryType))); - } -@@ -430,9 +436,10 @@ PyChipError pychip_DeviceController_UnpairDevice(chip::Controller::DeviceCommiss - return ToPyChipError(err); - } - --PyChipError pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, uint64_t nodeId, -- uint32_t setupPasscode, const uint8_t filterType, const char * filterParam, -- uint32_t discoveryTimeoutMsec) -+PyChipError pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ uint64_t nodeId, uint32_t setupPasscode, const uint8_t filterType, -+ const char * filterParam, uint32_t discoveryTimeoutMsec) - { - Dnssd::DiscoveryFilter filter(static_cast(filterType)); - switch (static_cast(filterType)) -@@ -467,9 +474,8 @@ PyChipError pychip_DeviceController_OnNetworkCommission(chip::Controller::Device - return ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT); - } - -- sPairingDelegate.SetExpectingPairingComplete(true); -- CHIP_ERROR err = sPairingDeviceDiscoveryDelegate.Init(nodeId, setupPasscode, sCommissioningParameters, &sPairingDelegate, -- devCtrl, discoveryTimeoutMsec); -+ CHIP_ERROR err = sPairingDeviceDiscoveryDelegate.Init(nodeId, setupPasscode, sCommissioningParameters, pairingDelegate, devCtrl, -+ discoveryTimeoutMsec); - VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err)); - return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); - } -@@ -587,7 +593,6 @@ PyChipError pychip_DeviceController_EstablishPASESessionIP(chip::Controller::Dev - addr.SetPort(port); - } - params.SetPeerAddress(addr).SetDiscriminator(0); -- sPairingDelegate.SetExpectingPairingComplete(true); - return ToPyChipError(devCtrl->EstablishPASEConnection(nodeid, params)); - } - -@@ -598,14 +603,12 @@ PyChipError pychip_DeviceController_EstablishPASESessionBLE(chip::Controller::De - RendezvousParameters params = chip::RendezvousParameters().SetSetupPINCode(setupPINCode); - addr.SetTransportType(chip::Transport::Type::kBle); - params.SetPeerAddress(addr).SetDiscriminator(discriminator); -- sPairingDelegate.SetExpectingPairingComplete(true); - return ToPyChipError(devCtrl->EstablishPASEConnection(nodeid, params)); - } - - PyChipError pychip_DeviceController_EstablishPASESession(chip::Controller::DeviceCommissioner * devCtrl, const char * setUpCode, - chip::NodeId nodeid) - { -- sPairingDelegate.SetExpectingPairingComplete(true); - return ToPyChipError(devCtrl->EstablishPASEConnection(nodeid, setUpCode)); - } - -@@ -656,15 +659,17 @@ PyChipError pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnab - } - - PyChipError pychip_ScriptDevicePairingDelegate_SetOpenWindowCompleteCallback( -- chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnWindowOpenCompleteFunct callback) -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ chip::Controller::DevicePairingDelegate_OnWindowOpenCompleteFunct callback) - { -- sPairingDelegate.SetCommissioningWindowOpenCallback(callback); -+ pairingDelegate->SetCommissioningWindowOpenCallback(callback); - return ToPyChipError(CHIP_NO_ERROR); - } - --PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid, -- uint16_t timeout, uint32_t iteration, uint16_t discriminator, -- uint8_t optionInt) -+PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::DeviceCommissioner * devCtrl, -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ chip::NodeId nodeid, uint16_t timeout, uint32_t iteration, -+ uint16_t discriminator, uint8_t optionInt) - { - const auto option = static_cast(optionInt); - if (option == Controller::CommissioningWindowOpener::CommissioningWindowOption::kOriginalSetupCode) -@@ -680,7 +685,7 @@ PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::De - Platform::New(static_cast(devCtrl)); - PyChipError err = ToPyChipError(opener->OpenCommissioningWindow(nodeid, System::Clock::Seconds16(timeout), iteration, - discriminator, NullOptional, NullOptional, -- sPairingDelegate.GetOpenWindowCallback(opener), payload)); -+ pairingDelegate->GetOpenWindowCallback(opener), payload)); - return err; - } - -@@ -688,32 +693,42 @@ PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::De - } - - PyChipError --pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(chip::Controller::DeviceCommissioner * devCtrl, -+pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, - chip::Controller::DevicePairingDelegate_OnPairingCompleteFunct callback) - { -- sPairingDelegate.SetKeyExchangeCallback(callback); -+ pairingDelegate->SetKeyExchangeCallback(callback); - return ToPyChipError(CHIP_NO_ERROR); - } - - PyChipError pychip_ScriptDevicePairingDelegate_SetCommissioningCompleteCallback( -- chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnCommissioningCompleteFunct callback) -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ chip::Controller::DevicePairingDelegate_OnCommissioningCompleteFunct callback) - { -- sPairingDelegate.SetCommissioningCompleteCallback(callback); -+ pairingDelegate->SetCommissioningCompleteCallback(callback); - return ToPyChipError(CHIP_NO_ERROR); - } - - PyChipError pychip_ScriptDevicePairingDelegate_SetCommissioningStatusUpdateCallback( -- chip::Controller::DeviceCommissioner * devCtrl, -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, - chip::Controller::DevicePairingDelegate_OnCommissioningStatusUpdateFunct callback) - { -- sPairingDelegate.SetCommissioningStatusUpdateCallback(callback); -+ pairingDelegate->SetCommissioningStatusUpdateCallback(callback); -+ return ToPyChipError(CHIP_NO_ERROR); -+} -+ -+PyChipError -+pychip_ScriptDevicePairingDelegate_SetFabricCheckCallback(chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ chip::Controller::DevicePairingDelegate_OnFabricCheckFunct callback) -+{ -+ pairingDelegate->SetFabricCheckCallback(callback); - return ToPyChipError(CHIP_NO_ERROR); - } - - PyChipError --pychip_ScriptDevicePairingDelegate_SetFabricCheckCallback(chip::Controller::DevicePairingDelegate_OnFabricCheckFunct callback) -+pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, -+ bool value) - { -- sPairingDelegate.SetFabricCheckCallback(callback); -+ pairingDelegate->SetExpectingPairingComplete(value); - return ToPyChipError(CHIP_NO_ERROR); - } - -diff --git a/src/controller/python/OpCredsBinding.cpp b/src/controller/python/OpCredsBinding.cpp -index 79995d133b..66a6c84184 100644 ---- a/src/controller/python/OpCredsBinding.cpp -+++ b/src/controller/python/OpCredsBinding.cpp -@@ -402,12 +402,11 @@ void pychip_OnCommissioningStatusUpdate(chip::PeerId peerId, chip::Controller::C - * TODO(#25214): Need clean up API - * - */ --PyChipError pychip_OpCreds_AllocateControllerForPythonCommissioningFLow(chip::Controller::DeviceCommissioner ** outDevCtrl, -- chip::python::pychip_P256Keypair * operationalKey, -- uint8_t * noc, uint32_t nocLen, uint8_t * icac, -- uint32_t icacLen, uint8_t * rcac, uint32_t rcacLen, -- const uint8_t * ipk, uint32_t ipkLen, -- chip::VendorId adminVendorId, bool enableServerInteractions) -+PyChipError pychip_OpCreds_AllocateControllerForPythonCommissioningFLow( -+ chip::Controller::DeviceCommissioner ** outDevCtrl, chip::Controller::ScriptDevicePairingDelegate ** outPairingDelegate, -+ chip::python::pychip_P256Keypair * operationalKey, uint8_t * noc, uint32_t nocLen, uint8_t * icac, uint32_t icacLen, -+ uint8_t * rcac, uint32_t rcacLen, const uint8_t * ipk, uint32_t ipkLen, chip::VendorId adminVendorId, -+ bool enableServerInteractions) - { - ReturnErrorCodeIf(nocLen > Controller::kMaxCHIPDERCertLength, ToPyChipError(CHIP_ERROR_NO_MEMORY)); - ReturnErrorCodeIf(icacLen > Controller::kMaxCHIPDERCertLength, ToPyChipError(CHIP_ERROR_NO_MEMORY)); -@@ -415,11 +414,13 @@ PyChipError pychip_OpCreds_AllocateControllerForPythonCommissioningFLow(chip::Co - - ChipLogDetail(Controller, "Creating New Device Controller"); - -+ auto pairingDelegate = std::make_unique(); -+ VerifyOrReturnError(pairingDelegate != nullptr, ToPyChipError(CHIP_ERROR_NO_MEMORY)); - auto devCtrl = std::make_unique(); - VerifyOrReturnError(devCtrl != nullptr, ToPyChipError(CHIP_ERROR_NO_MEMORY)); - - Controller::SetupParams initParams; -- initParams.pairingDelegate = &sPairingDelegate; -+ initParams.pairingDelegate = pairingDelegate.get(); - initParams.operationalCredentialsDelegate = &sPlaceholderOperationalCredentialsIssuer; - initParams.operationalKeypair = operationalKey; - initParams.controllerRCAC = ByteSpan(rcac, rcacLen); -@@ -450,13 +451,15 @@ PyChipError pychip_OpCreds_AllocateControllerForPythonCommissioningFLow(chip::Co - chip::Credentials::SetSingleIpkEpochKey(&sGroupDataProvider, devCtrl->GetFabricIndex(), fabricIpk, compressedFabricIdSpan); - VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err)); - -- *outDevCtrl = devCtrl.release(); -+ *outDevCtrl = devCtrl.release(); -+ *outPairingDelegate = pairingDelegate.release(); - - return ToPyChipError(CHIP_NO_ERROR); - } - - // TODO(#25214): Need clean up API - PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Controller::DeviceCommissioner ** outDevCtrl, -+ chip::Controller::ScriptDevicePairingDelegate ** outPairingDelegate, - FabricId fabricId, chip::NodeId nodeId, chip::VendorId adminVendorId, - const char * paaTrustStorePath, bool useTestCommissioner, - bool enableServerInteractions, CASEAuthTag * caseAuthTags, uint32_t caseAuthTagLen, -@@ -468,6 +471,8 @@ PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Co - - VerifyOrReturnError(context != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); - -+ auto pairingDelegate = std::make_unique(); -+ VerifyOrReturnError(pairingDelegate != nullptr, ToPyChipError(CHIP_ERROR_NO_MEMORY)); - auto devCtrl = std::make_unique(); - VerifyOrReturnError(devCtrl != nullptr, ToPyChipError(CHIP_ERROR_NO_MEMORY)); - -@@ -524,7 +529,7 @@ PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Co - VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err)); - - Controller::SetupParams initParams; -- initParams.pairingDelegate = &sPairingDelegate; -+ initParams.pairingDelegate = pairingDelegate.get(); - initParams.operationalCredentialsDelegate = context->mAdapter.get(); - initParams.operationalKeypair = controllerKeyPair; - initParams.controllerRCAC = rcacSpan; -@@ -538,9 +543,9 @@ PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Co - if (useTestCommissioner) - { - initParams.defaultCommissioner = &sTestCommissioner; -- sPairingDelegate.SetCommissioningSuccessCallback(pychip_OnCommissioningSuccess); -- sPairingDelegate.SetCommissioningFailureCallback(pychip_OnCommissioningFailure); -- sPairingDelegate.SetCommissioningStatusUpdateCallback(pychip_OnCommissioningStatusUpdate); -+ pairingDelegate->SetCommissioningSuccessCallback(pychip_OnCommissioningSuccess); -+ pairingDelegate->SetCommissioningFailureCallback(pychip_OnCommissioningFailure); -+ pairingDelegate->SetCommissioningStatusUpdateCallback(pychip_OnCommissioningStatusUpdate); - } - - err = Controller::DeviceControllerFactory::GetInstance().SetupCommissioner(initParams, *devCtrl); -@@ -562,7 +567,8 @@ PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Co - chip::Credentials::SetSingleIpkEpochKey(&sGroupDataProvider, devCtrl->GetFabricIndex(), defaultIpk, compressedFabricIdSpan); - VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err)); - -- *outDevCtrl = devCtrl.release(); -+ *outDevCtrl = devCtrl.release(); -+ *outPairingDelegate = pairingDelegate.release(); - - return ToPyChipError(CHIP_NO_ERROR); - } -@@ -596,7 +602,8 @@ void pychip_OpCreds_FreeDelegate(OpCredsContext * context) - Platform::Delete(context); - } - --PyChipError pychip_DeviceController_DeleteDeviceController(chip::Controller::DeviceCommissioner * devCtrl) -+PyChipError pychip_DeviceController_DeleteDeviceController(chip::Controller::DeviceCommissioner * devCtrl, -+ chip::Controller::ScriptDevicePairingDelegate * pairingDelegate) - { - if (devCtrl != nullptr) - { -@@ -604,6 +611,11 @@ PyChipError pychip_DeviceController_DeleteDeviceController(chip::Controller::Dev - delete devCtrl; - } - -+ if (pairingDelegate != nullptr) -+ { -+ delete pairingDelegate; -+ } -+ - return ToPyChipError(CHIP_NO_ERROR); - } - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index b3d0aa2d7f..9cbd7a32d2 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -253,8 +253,10 @@ class ChipDeviceControllerBase(): - - self._InitLib() - -+ pairingDelegate = c_void_p(None) - devCtrl = c_void_p(None) - -+ self.pairingDelegate = pairingDelegate - self.devCtrl = devCtrl - self.name = name - self.fabricCheckNodeId = -1 -@@ -263,7 +265,7 @@ class ChipDeviceControllerBase(): - self._Cluster = ChipClusters(builtins.chipStack) - self._Cluster.InitLib(self._dmLib) - -- def _set_dev_ctrl(self, devCtrl): -+ def _set_dev_ctrl(self, devCtrl, pairingDelegate): - def HandleCommissioningComplete(nodeid, err): - if err.is_success: - logging.info("Commissioning complete") -@@ -321,25 +323,26 @@ class ChipDeviceControllerBase(): - if not err.is_success: - HandleCommissioningComplete(0, err) - -+ self.pairingDelegate = pairingDelegate - self.devCtrl = devCtrl - - self.cbHandlePASEEstablishmentCompleteFunct = _DevicePairingDelegate_OnPairingCompleteFunct( - HandlePASEEstablishmentComplete) - self._dmLib.pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback( -- self.devCtrl, self.cbHandlePASEEstablishmentCompleteFunct) -+ self.pairingDelegate, self.cbHandlePASEEstablishmentCompleteFunct) - - self.cbHandleCommissioningCompleteFunct = _DevicePairingDelegate_OnCommissioningCompleteFunct( - HandleCommissioningComplete) - self._dmLib.pychip_ScriptDevicePairingDelegate_SetCommissioningCompleteCallback( -- self.devCtrl, self.cbHandleCommissioningCompleteFunct) -+ self.pairingDelegate, self.cbHandleCommissioningCompleteFunct) - - self.cbHandleFabricCheckFunct = _DevicePairingDelegate_OnFabricCheckFunct(HandleFabricCheck) -- self._dmLib.pychip_ScriptDevicePairingDelegate_SetFabricCheckCallback(self.cbHandleFabricCheckFunct) -+ self._dmLib.pychip_ScriptDevicePairingDelegate_SetFabricCheckCallback(self.pairingDelegate, self.cbHandleFabricCheckFunct) - - self.cbHandleOpenWindowCompleteFunct = _DevicePairingDelegate_OnOpenWindowCompleteFunct( - HandleOpenWindowComplete) - self._dmLib.pychip_ScriptDevicePairingDelegate_SetOpenWindowCompleteCallback( -- self.devCtrl, self.cbHandleOpenWindowCompleteFunct) -+ self.pairingDelegate, self.cbHandleOpenWindowCompleteFunct) - - self.cbHandleDeviceUnpairCompleteFunct = _DeviceUnpairingCompleteFunct(HandleUnpairDeviceComplete) - -@@ -355,6 +358,11 @@ class ChipDeviceControllerBase(): - - ChipDeviceController.activeList.add(self) - -+ def _enablePairingCompeleteCallback(self, value: bool): -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(self.pairingDelegate, value) -+ ).raise_on_error() -+ - @property - def fabricAdmin(self) -> FabricAdmin.FabricAdmin: - return self._fabricAdmin -@@ -389,8 +397,9 @@ class ChipDeviceControllerBase(): - if self.devCtrl is not None: - self._ChipStack.Call( - lambda: self._dmLib.pychip_DeviceController_DeleteDeviceController( -- self.devCtrl) -+ self.devCtrl, self.pairingDelegate) - ).raise_on_error() -+ self.pairingDelegate = None - self.devCtrl = None - - ChipDeviceController.activeList.remove(self) -@@ -437,6 +446,7 @@ class ChipDeviceControllerBase(): - self._ChipStack.commissioningCompleteEvent.clear() - - self.state = DCState.COMMISSIONING -+ self._enablePairingCompeleteCallback(True) - self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectBLE( - self.devCtrl, discriminator, setupPinCode, nodeid) -@@ -487,6 +497,7 @@ class ChipDeviceControllerBase(): - self.CheckIsActive() - - self.state = DCState.RENDEZVOUS_ONGOING -+ self._enablePairingCompeleteCallback(True) - return self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionBLE( - self.devCtrl, setupPinCode, discriminator, nodeid) -@@ -496,6 +507,7 @@ class ChipDeviceControllerBase(): - self.CheckIsActive() - - self.state = DCState.RENDEZVOUS_ONGOING -+ self._enablePairingCompeleteCallback(True) - return self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionIP( - self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid, port) -@@ -505,6 +517,7 @@ class ChipDeviceControllerBase(): - self.CheckIsActive() - - self.state = DCState.RENDEZVOUS_ONGOING -+ self._enablePairingCompeleteCallback(True) - return self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_EstablishPASESession( - self.devCtrl, setUpCode.encode("utf-8"), nodeid) -@@ -726,7 +739,7 @@ class ChipDeviceControllerBase(): - self.CheckIsActive() - self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_OpenCommissioningWindow( -- self.devCtrl, nodeid, timeout, iteration, discriminator, option) -+ self.devCtrl, self.pairingDelegate, nodeid, timeout, iteration, discriminator, option) - ).raise_on_error() - self._ChipStack.callbackRes.raise_on_error() - return self._ChipStack.openCommissioningWindowPincode[nodeid] -@@ -1565,16 +1578,13 @@ class ChipDeviceControllerBase(): - self._dmLib = CDLL(self._ChipStack.LocateChipDLL()) - - self._dmLib.pychip_DeviceController_DeleteDeviceController.argtypes = [ -- c_void_p] -+ c_void_p, c_void_p] - self._dmLib.pychip_DeviceController_DeleteDeviceController.restype = PyChipError - - self._dmLib.pychip_DeviceController_ConnectBLE.argtypes = [ - c_void_p, c_uint16, c_uint32, c_uint64] - self._dmLib.pychip_DeviceController_ConnectBLE.restype = PyChipError - -- self._dmLib.pychip_DeviceController_ConnectIP.argtypes = [ -- c_void_p, c_char_p, c_uint32, c_uint64] -- - self._dmLib.pychip_DeviceController_SetThreadOperationalDataset.argtypes = [ - c_char_p, c_uint32] - self._dmLib.pychip_DeviceController_SetThreadOperationalDataset.restype = PyChipError -@@ -1610,7 +1620,7 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_Commission.restype = PyChipError - - self._dmLib.pychip_DeviceController_OnNetworkCommission.argtypes = [ -- c_void_p, c_uint64, c_uint32, c_uint8, c_char_p, c_uint32] -+ c_void_p, c_void_p, c_uint64, c_uint32, c_uint8, c_char_p, c_uint32] - self._dmLib.pychip_DeviceController_OnNetworkCommission.restype = PyChipError - - self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes.argtypes = [ -@@ -1648,6 +1658,7 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_EstablishPASESessionBLE.argtypes = [ - c_void_p, c_uint32, c_uint16, c_uint64] - self._dmLib.pychip_DeviceController_EstablishPASESessionBLE.restype = PyChipError -+ - self._dmLib.pychip_DeviceController_EstablishPASESession.argtypes = [ - c_void_p, c_char_p, c_uint64] - self._dmLib.pychip_DeviceController_EstablishPASESession.restype = PyChipError -@@ -1655,10 +1666,12 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_DiscoverAllCommissionableNodes.argtypes = [ - c_void_p] - self._dmLib.pychip_DeviceController_DiscoverAllCommissionableNodes.restype = PyChipError -+ - self._dmLib.pychip_DeviceController_PrintDiscoveredDevices.argtypes = [ - c_void_p] - self._dmLib.pychip_DeviceController_PrintDiscoveredDevices.argtypes = [ - c_void_p, _ChipDeviceController_IterateDiscoveredCommissionableNodesFunct] -+ - self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode.argtypes = [c_void_p] - self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode.restype = c_bool - -@@ -1703,9 +1716,13 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_ScriptDevicePairingDelegate_SetCommissioningStatusUpdateCallback.restype = PyChipError - - self._dmLib.pychip_ScriptDevicePairingDelegate_SetFabricCheckCallback.argtypes = [ -- _DevicePairingDelegate_OnFabricCheckFunct] -+ c_void_p, _DevicePairingDelegate_OnFabricCheckFunct] - self._dmLib.pychip_ScriptDevicePairingDelegate_SetFabricCheckCallback.restype = PyChipError - -+ self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete.argtypes = [ -+ c_void_p, c_bool] -+ self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete.restype = PyChipError -+ - self._dmLib.pychip_GetConnectedDeviceByNodeId.argtypes = [ - c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct] - self._dmLib.pychip_GetConnectedDeviceByNodeId.restype = PyChipError -@@ -1733,8 +1750,9 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_GetCompressedFabricId.restype = PyChipError - - self._dmLib.pychip_DeviceController_OpenCommissioningWindow.argtypes = [ -- c_void_p, c_uint64, c_uint16, c_uint32, c_uint16, c_uint8] -+ c_void_p, c_void_p, c_uint64, c_uint16, c_uint32, c_uint16, c_uint8] - self._dmLib.pychip_DeviceController_OpenCommissioningWindow.restype = PyChipError -+ - self._dmLib.pychip_TestCommissionerUsed.argtypes = [] - self._dmLib.pychip_TestCommissionerUsed.restype = c_bool - -@@ -1750,6 +1768,7 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_SetTestCommissionerSimulateFailureOnStage.argtypes = [ - c_uint8] - self._dmLib.pychip_SetTestCommissionerSimulateFailureOnStage.restype = c_bool -+ - self._dmLib.pychip_SetTestCommissionerSimulateFailureOnReport.argtypes = [ - c_uint8] - self._dmLib.pychip_SetTestCommissionerSimulateFailureOnReport.restype = c_bool -@@ -1762,8 +1781,7 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_GetCompletionError.restype = PyChipError - - self._dmLib.pychip_DeviceController_IssueNOCChain.argtypes = [ -- c_void_p, py_object, c_char_p, c_size_t, c_uint64 -- ] -+ c_void_p, py_object, c_char_p, c_size_t, c_uint64] - self._dmLib.pychip_DeviceController_IssueNOCChain.restype = PyChipError - - self._dmLib.pychip_OpCreds_InitGroupTestingData.argtypes = [ -@@ -1784,11 +1802,11 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_GetLogFilter = c_uint8 - - self._dmLib.pychip_OpCreds_AllocateController.argtypes = [c_void_p, POINTER( -- c_void_p), c_uint64, c_uint64, c_uint16, c_char_p, c_bool, c_bool, POINTER(c_uint32), c_uint32, c_void_p] -+ c_void_p), POINTER(c_void_p), c_uint64, c_uint64, c_uint16, c_char_p, c_bool, c_bool, POINTER(c_uint32), c_uint32, c_void_p] - self._dmLib.pychip_OpCreds_AllocateController.restype = PyChipError - - self._dmLib.pychip_OpCreds_AllocateControllerForPythonCommissioningFLow.argtypes = [ -- POINTER(c_void_p), c_void_p, POINTER(c_char), c_uint32, POINTER(c_char), c_uint32, POINTER(c_char), c_uint32, POINTER(c_char), c_uint32, c_uint16, c_bool] -+ POINTER(c_void_p), POINTER(c_void_p), c_void_p, POINTER(c_char), c_uint32, POINTER(c_char), c_uint32, POINTER(c_char), c_uint32, POINTER(c_char), c_uint32, c_uint16, c_bool] - self._dmLib.pychip_OpCreds_AllocateControllerForPythonCommissioningFLow.restype = PyChipError - - self._dmLib.pychip_DeviceController_SetIpk.argtypes = [c_void_p, POINTER(c_char), c_size_t] -@@ -1810,6 +1828,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - - self._dmLib.pychip_DeviceController_SetIssueNOCChainCallbackPythonCallback(_IssueNOCChainCallbackPythonCallback) - -+ pairingDelegate = c_void_p(None) - devCtrl = c_void_p(None) - - c_catTags = (c_uint32 * len(catTags))() -@@ -1821,7 +1840,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - self._externalKeyPair = keypair - self._ChipStack.Call( - lambda: self._dmLib.pychip_OpCreds_AllocateController(c_void_p( -- opCredsContext), pointer(devCtrl), fabricId, nodeId, adminVendorId, c_char_p(None if len(paaTrustStorePath) == 0 else str.encode(paaTrustStorePath)), useTestCommissioner, self._ChipStack.enableServerInteractions, c_catTags, len(catTags), None if keypair is None else keypair.native_object) -+ opCredsContext), pointer(devCtrl), pointer(pairingDelegate), fabricId, nodeId, adminVendorId, c_char_p(None if len(paaTrustStorePath) == 0 else str.encode(paaTrustStorePath)), useTestCommissioner, self._ChipStack.enableServerInteractions, c_catTags, len(catTags), None if keypair is None else keypair.native_object) - ).raise_on_error() - - self._fabricAdmin = fabricAdmin -@@ -1829,7 +1848,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - self._nodeId = nodeId - self._caIndex = fabricAdmin.caIndex - -- self._set_dev_ctrl(devCtrl=devCtrl) -+ self._set_dev_ctrl(devCtrl=devCtrl, pairingDelegate=pairingDelegate) - - self._finish_init() - -@@ -1975,9 +1994,10 @@ class ChipDeviceController(ChipDeviceControllerBase): - - self._ChipStack.commissioningCompleteEvent.clear() - -+ self._enablePairingCompeleteCallback(True) - self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_OnNetworkCommission( -- self.devCtrl, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") + b"\x00" if filter is not None else None, discoveryTimeoutMsec) -+ self.devCtrl, self.pairingDelegate, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") + b"\x00" if filter is not None else None, discoveryTimeoutMsec) - ) - if not self._ChipStack.commissioningCompleteEvent.isSet(): - # Error 50 is a timeout -@@ -1998,6 +2018,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - - self._ChipStack.commissioningCompleteEvent.clear() - -+ self._enablePairingCompeleteCallback(True) - self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectWithCode( - self.devCtrl, setupPayload, nodeid, discoveryType.value) -@@ -2017,6 +2038,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - - self._ChipStack.commissioningCompleteEvent.clear() - -+ self._enablePairingCompeleteCallback(True) - self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectIP( - self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid) -@@ -2061,6 +2083,7 @@ class BareChipDeviceController(ChipDeviceControllerBase): - ''' - super().__init__(name or f"ctrl(v/{adminVendorId})") - -+ pairingDelegate = c_void_p(None) - devCtrl = c_void_p(None) - - # Device should hold a reference to the key to avoid it being GC-ed. -@@ -2069,9 +2092,9 @@ class BareChipDeviceController(ChipDeviceControllerBase): - - self._ChipStack.Call( - lambda: self._dmLib.pychip_OpCreds_AllocateControllerForPythonCommissioningFLow( -- c_void_p(devCtrl), nativeKey, noc, len(noc), icac, len(icac) if icac else 0, rcac, len(rcac), ipk, len(ipk) if ipk else 0, adminVendorId, self._ChipStack.enableServerInteractions) -+ c_void_p(devCtrl), c_void_p(pairingDelegate), nativeKey, noc, len(noc), icac, len(icac) if icac else 0, rcac, len(rcac), ipk, len(ipk) if ipk else 0, adminVendorId, self._ChipStack.enableServerInteractions) - ).raise_on_error() - -- self._set_dev_ctrl(devCtrl) -+ self._set_dev_ctrl(devCtrl, pairingDelegate) - - self._finish_init() --- -2.45.2 - diff --git a/0010-Python-Call-SDK-asyncio-friendly-32764.patch b/0010-Python-Call-SDK-asyncio-friendly-32764.patch deleted file mode 100644 index 80b7724..0000000 --- a/0010-Python-Call-SDK-asyncio-friendly-32764.patch +++ /dev/null @@ -1,359 +0,0 @@ -From b079cfb8fabc4927dd59e8913684a2b58fcb4f3c Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 16 May 2024 09:35:25 +0200 -Subject: [PATCH] [Python] Call SDK asyncio friendly (#32764) - -* [Python] Rename CallAsync to CallAsyncWithCallback - -CallAsync continuously calls a callback function during the wait -for the call. Rename the function to reflect that fact. - -This frees up CallAsync for an asyncio friendly implementation. - -* [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. - -* [Python] Use CallAsync where appropriate - -* Rename AsyncSimpleCallableHandle to AsyncioCallableHandle - -* Rename CallAsyncWithCallback to CallAsyncWithCompleteCallback - -Also add a comment that the function needs to be released by registering -a callback and setting the complete event. - -* Add comments about lock ---- - src/controller/python/chip/ChipDeviceCtrl.py | 43 ++++++++------- - src/controller/python/chip/ChipStack.py | 53 ++++++++++++++++++- - .../python/chip/clusters/Command.py | 16 +++--- - 3 files changed, 83 insertions(+), 29 deletions(-) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 9cbd7a32d2..3282ffd191 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -186,7 +186,7 @@ class DeviceProxyWrapper(): - def __del__(self): - if (self._dmLib is not None and hasattr(builtins, 'chipStack') and builtins.chipStack is not None): - # This destructor is called from any threading context, including on the Matter threading context. -- # So, we cannot call chipStack.Call or chipStack.CallAsync which waits for the posted work to -+ # So, we cannot call chipStack.Call or chipStack.CallAsyncWithCompleteCallback which waits for the posted work to - # actually be executed. Instead, we just post/schedule the work and move on. - builtins.chipStack.PostTaskOnChipThread(lambda: self._dmLib.pychip_FreeOperationalDeviceProxy(self._deviceProxy)) - -@@ -447,7 +447,7 @@ class ChipDeviceControllerBase(): - - self.state = DCState.COMMISSIONING - self._enablePairingCompeleteCallback(True) -- self._ChipStack.CallAsync( -+ self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_ConnectBLE( - self.devCtrl, discriminator, setupPinCode, nodeid) - ).raise_on_error() -@@ -459,7 +459,7 @@ class ChipDeviceControllerBase(): - def UnpairDevice(self, nodeid: int): - self.CheckIsActive() - -- return self._ChipStack.CallAsync( -+ return self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_UnpairDevice( - self.devCtrl, nodeid, self.cbHandleDeviceUnpairCompleteFunct) - ).raise_on_error() -@@ -498,7 +498,7 @@ class ChipDeviceControllerBase(): - - self.state = DCState.RENDEZVOUS_ONGOING - self._enablePairingCompeleteCallback(True) -- return self._ChipStack.CallAsync( -+ return self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionBLE( - self.devCtrl, setupPinCode, discriminator, nodeid) - ) -@@ -508,7 +508,7 @@ class ChipDeviceControllerBase(): - - self.state = DCState.RENDEZVOUS_ONGOING - self._enablePairingCompeleteCallback(True) -- return self._ChipStack.CallAsync( -+ return self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionIP( - self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid, port) - ) -@@ -518,7 +518,7 @@ class ChipDeviceControllerBase(): - - self.state = DCState.RENDEZVOUS_ONGOING - self._enablePairingCompeleteCallback(True) -- return self._ChipStack.CallAsync( -+ return self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_EstablishPASESession( - self.devCtrl, setUpCode.encode("utf-8"), nodeid) - ) -@@ -737,7 +737,7 @@ class ChipDeviceControllerBase(): - Returns CommissioningParameters - ''' - self.CheckIsActive() -- self._ChipStack.CallAsync( -+ self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_OpenCommissioningWindow( - self.devCtrl, self.pairingDelegate, nodeid, timeout, iteration, discriminator, option) - ).raise_on_error() -@@ -842,7 +842,7 @@ class ChipDeviceControllerBase(): - - if allowPASE: - returnDevice = c_void_p(None) -- res = self._ChipStack.Call(lambda: self._dmLib.pychip_GetDeviceBeingCommissioned( -+ res = await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetDeviceBeingCommissioned( - self.devCtrl, nodeid, byref(returnDevice)), timeoutMs) - if res.is_success: - logging.info('Using PASE connection') -@@ -872,11 +872,12 @@ class ChipDeviceControllerBase(): - - closure = DeviceAvailableClosure(eventLoop, future) - ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure)) -- self._ChipStack.Call(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId( -+ res = await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId( - self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback), -- timeoutMs).raise_on_error() -+ timeoutMs) -+ res.raise_on_error() - -- # The callback might have been received synchronously (during self._ChipStack.Call()). -+ # The callback might have been received synchronously (during self._ChipStack.CallAsync()). - # In that case the Future has already been set it will return immediately - if (timeoutMs): - timeout = float(timeoutMs) / 1000 -@@ -1004,13 +1005,14 @@ class ChipDeviceControllerBase(): - future = eventLoop.create_future() - - device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs) -- ClusterCommand.SendCommand( -+ res = await ClusterCommand.SendCommand( - future, eventLoop, responseType, device.deviceProxy, ClusterCommand.CommandPath( - EndpointId=endpoint, - ClusterId=payload.cluster_id, - CommandId=payload.command_id, - ), payload, timedRequestTimeoutMs=timedRequestTimeoutMs, -- interactionTimeoutMs=interactionTimeoutMs, busyWaitMs=busyWaitMs, suppressResponse=suppressResponse).raise_on_error() -+ interactionTimeoutMs=interactionTimeoutMs, busyWaitMs=busyWaitMs, suppressResponse=suppressResponse) -+ res.raise_on_error() - return await future - - async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo], -@@ -1046,10 +1048,11 @@ class ChipDeviceControllerBase(): - - device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs) - -- ClusterCommand.SendBatchCommands( -+ res = await ClusterCommand.SendBatchCommands( - future, eventLoop, device.deviceProxy, commands, - timedRequestTimeoutMs=timedRequestTimeoutMs, -- interactionTimeoutMs=interactionTimeoutMs, busyWaitMs=busyWaitMs, suppressResponse=suppressResponse).raise_on_error() -+ interactionTimeoutMs=interactionTimeoutMs, busyWaitMs=busyWaitMs, suppressResponse=suppressResponse) -+ res.raise_on_error() - return await future - - def SendGroupCommand(self, groupid: int, payload: ClusterObjects.ClusterCommand, busyWaitMs: typing.Union[None, int] = None): -@@ -1879,7 +1882,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - self._ChipStack.commissioningCompleteEvent.clear() - self.state = DCState.COMMISSIONING - -- self._ChipStack.CallAsync( -+ self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_Commission( - self.devCtrl, nodeid) - ) -@@ -1995,7 +1998,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - self._ChipStack.commissioningCompleteEvent.clear() - - self._enablePairingCompeleteCallback(True) -- self._ChipStack.CallAsync( -+ self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_OnNetworkCommission( - self.devCtrl, self.pairingDelegate, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") + b"\x00" if filter is not None else None, discoveryTimeoutMsec) - ) -@@ -2019,7 +2022,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - self._ChipStack.commissioningCompleteEvent.clear() - - self._enablePairingCompeleteCallback(True) -- self._ChipStack.CallAsync( -+ self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_ConnectWithCode( - self.devCtrl, setupPayload, nodeid, discoveryType.value) - ) -@@ -2039,7 +2042,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - self._ChipStack.commissioningCompleteEvent.clear() - - self._enablePairingCompeleteCallback(True) -- self._ChipStack.CallAsync( -+ self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_ConnectIP( - self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid) - ) -@@ -2053,7 +2056,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - The NOC chain will be provided in TLV cert format.""" - self.CheckIsActive() - -- return self._ChipStack.CallAsync( -+ return self._ChipStack.CallAsyncWithCompleteCallback( - lambda: self._dmLib.pychip_DeviceController_IssueNOCChain( - self.devCtrl, py_object(self), csr.NOCSRElements, len(csr.NOCSRElements), nodeId) - ) -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index 6df7e41de4..35f9e24ef4 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,35 @@ class AsyncCallableHandle: - return self._res - - -+class AsyncioCallableHandle: -+ """Class which handles Matter SDK Calls asyncio friendly""" -+ -+ def __init__(self, callback): -+ self._callback = callback -+ self._loop = asyncio.get_event_loop() -+ self._future = self._loop.create_future() -+ self._result = None -+ self._exception = None -+ -+ @property -+ def future(self): -+ return self._future -+ -+ 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)) -@@ -178,6 +208,7 @@ class ChipStack(object): - bluetoothAdapter=None, enableServerInteractions=True): - builtins.enableDebugMode = False - -+ # TODO: Probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. - self.networkLock = Lock() - self.completeEvent = Event() - self.commissioningCompleteEvent = Event() -@@ -318,6 +349,7 @@ class ChipStack(object): - logFunct = 0 - if not isinstance(logFunct, _LogMessageFunct): - logFunct = _LogMessageFunct(logFunct) -+ # TODO: Lock probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. - with self.networkLock: - # NOTE: ChipStack must hold a reference to the CFUNCTYPE object while it is - # set. Otherwise it may get garbage collected, and logging calls from the -@@ -360,6 +392,7 @@ class ChipStack(object): - # throw error if op in progress - self.callbackRes = None - self.completeEvent.clear() -+ # TODO: Lock probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. - with self.networkLock: - res = self.PostTaskOnChipThread(callFunct).Wait(timeoutMs) - self.completeEvent.set() -@@ -367,14 +400,32 @@ class ChipStack(object): - return self.callbackRes - return res - -- def CallAsync(self, callFunct): -+ 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. -+ ''' -+ callObj = AsyncioCallableHandle(callFunct) -+ 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(callObj.future, timeoutMs / 1000 if timeoutMs else None) -+ -+ def CallAsyncWithCompleteCallback(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. - Calling this function on CHIP on CHIP mainloop thread will cause deadlock. -+ Make sure to register the necessary callbacks which release the function by setting the completeEvent. - ''' - # throw error if op in progress - self.callbackRes = None - self.completeEvent.clear() -+ # TODO: Lock probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. - with self.networkLock: - res = self.PostTaskOnChipThread(callFunct).Wait() - -diff --git a/src/controller/python/chip/clusters/Command.py b/src/controller/python/chip/clusters/Command.py -index 89aae537c9..6ef25cb211 100644 ---- a/src/controller/python/chip/clusters/Command.py -+++ b/src/controller/python/chip/clusters/Command.py -@@ -291,9 +291,9 @@ def TestOnlySendCommandTimedRequestFlagWithNoTimedInvoke(future: Future, eventLo - )) - - --def SendCommand(future: Future, eventLoop, responseType: Type, device, commandPath: CommandPath, payload: ClusterCommand, -- timedRequestTimeoutMs: Union[None, int] = None, interactionTimeoutMs: Union[None, int] = None, busyWaitMs: Union[None, int] = None, -- suppressResponse: Union[None, bool] = None) -> PyChipError: -+async def SendCommand(future: Future, eventLoop, responseType: Type, device, commandPath: CommandPath, payload: ClusterCommand, -+ timedRequestTimeoutMs: Union[None, int] = None, interactionTimeoutMs: Union[None, int] = None, -+ busyWaitMs: Union[None, int] = None, suppressResponse: Union[None, bool] = None) -> PyChipError: - ''' Send a cluster-object encapsulated command to a device and does the following: - - On receipt of a successful data response, returns the cluster-object equivalent through the provided future. - - None (on a successful response containing no data) -@@ -316,7 +316,7 @@ def SendCommand(future: Future, eventLoop, responseType: Type, device, commandPa - - payloadTLV = payload.ToTLV() - ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) -- return builtins.chipStack.Call( -+ return await builtins.chipStack.CallAsync( - lambda: handle.pychip_CommandSender_SendCommand( - ctypes.py_object(transaction), device, - c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), commandPath.EndpointId, -@@ -353,9 +353,9 @@ def _BuildPyInvokeRequestData(commands: List[InvokeRequestInfo], timedRequestTim - return pyBatchCommandsData - - --def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRequestInfo], -- timedRequestTimeoutMs: Optional[int] = None, interactionTimeoutMs: Optional[int] = None, busyWaitMs: Optional[int] = None, -- suppressResponse: Optional[bool] = None) -> PyChipError: -+async def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRequestInfo], -+ timedRequestTimeoutMs: Optional[int] = None, interactionTimeoutMs: Optional[int] = None, -+ busyWaitMs: Optional[int] = None, suppressResponse: Optional[bool] = None) -> PyChipError: - ''' Initiates an InvokeInteraction with the batch commands provided. - - Arguments: -@@ -388,7 +388,7 @@ def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRe - transaction = AsyncBatchCommandsTransaction(future, eventLoop, responseTypes) - ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) - -- return builtins.chipStack.Call( -+ return await builtins.chipStack.CallAsync( - lambda: handle.pychip_CommandSender_SendBatchCommands( - py_object(transaction), device, - c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), --- -2.45.2 - diff --git a/0011-Python-Make-AttributePath-more-pythonic-33571.patch b/0011-Python-Make-AttributePath-more-pythonic-33571.patch deleted file mode 100644 index f978a7b..0000000 --- a/0011-Python-Make-AttributePath-more-pythonic-33571.patch +++ /dev/null @@ -1,308 +0,0 @@ -From 34be5d25e6038dd22a1e80fa10b548065b6e6215 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Fri, 24 May 2024 10:39:41 +0200 -Subject: [PATCH] [Python] Make AttributePath more pythonic (#33571) - -* [Python] Make AttributePath more pythonic - -Use dataclass default initializer to initialize AttributePath. Use -static method to initialize from Cluster or Attribute. - -Also hash the integer fields directly, this is more efficient than -formatting a string first. - -* Drop AttributePathWithListIndex - -Drop AttributePathWithListIndex as it is unused. - -* Make DataVersionFilter/EventPath pythonic as well - -Use frozen data classes and static initializers similar to -AttributePath. - -* Fix _parseEventPathTuple - -* Fix _parseDataVersionFilterTuple ---- - src/controller/python/chip/ChipDeviceCtrl.py | 47 +++------ - .../python/chip/clusters/Attribute.py | 97 ++++++------------- - .../test/test_scripts/cluster_objects.py | 2 +- - 3 files changed, 48 insertions(+), 98 deletions(-) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 3282ffd191..4d14a42f18 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -1154,33 +1154,26 @@ class ChipDeviceControllerBase(): - # Concrete path - typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]] - ]): -- endpoint = None -- cluster = None -- attribute = None -- - if pathTuple == ('*') or pathTuple == (): - # Wildcard -- pass -+ return ClusterAttribute.AttributePath() - elif not isinstance(pathTuple, tuple): - if isinstance(pathTuple, int): -- endpoint = pathTuple -+ return ClusterAttribute.AttributePath(EndpointId=pathTuple) - elif issubclass(pathTuple, ClusterObjects.Cluster): -- cluster = pathTuple -+ return ClusterAttribute.AttributePath.from_cluster(EndpointId=None, Cluster=pathTuple) - elif issubclass(pathTuple, ClusterObjects.ClusterAttributeDescriptor): -- attribute = pathTuple -+ return ClusterAttribute.AttributePath.from_attribute(EndpointId=None, Attribute=pathTuple) - else: - raise ValueError("Unsupported Attribute Path") - else: - # endpoint + (cluster) attribute / endpoint + cluster -- endpoint = pathTuple[0] - if issubclass(pathTuple[1], ClusterObjects.Cluster): -- cluster = pathTuple[1] -+ return ClusterAttribute.AttributePath.from_cluster(EndpointId=pathTuple[0], Cluster=pathTuple[1]) - elif issubclass(pathTuple[1], ClusterAttribute.ClusterAttributeDescriptor): -- attribute = pathTuple[1] -+ return ClusterAttribute.AttributePath.from_attribute(EndpointId=pathTuple[0], Attribute=pathTuple[1]) - else: - raise ValueError("Unsupported Attribute Path") -- return ClusterAttribute.AttributePath( -- EndpointId=endpoint, Cluster=cluster, Attribute=attribute) - - def _parseDataVersionFilterTuple(self, pathTuple: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]]): - endpoint = None -@@ -1193,7 +1186,7 @@ class ChipDeviceControllerBase(): - else: - raise ValueError("Unsupported Cluster Path") - dataVersion = pathTuple[2] -- return ClusterAttribute.DataVersionFilter( -+ return ClusterAttribute.DataVersionFilter.from_cluster( - EndpointId=endpoint, Cluster=cluster, DataVersion=dataVersion) - - def _parseEventPathTuple(self, pathTuple: typing.Union[ -@@ -1210,39 +1203,31 @@ class ChipDeviceControllerBase(): - typing.Tuple[int, - typing.Type[ClusterObjects.ClusterEvent], int] - ]): -- endpoint = None -- cluster = None -- event = None -- urgent = False - if pathTuple in [('*'), ()]: - # Wildcard -- pass -+ return ClusterAttribute.EventPath() - elif not isinstance(pathTuple, tuple): - logging.debug(type(pathTuple)) - if isinstance(pathTuple, int): -- endpoint = pathTuple -+ return ClusterAttribute.EventPath(EndpointId=pathTuple) - elif issubclass(pathTuple, ClusterObjects.Cluster): -- cluster = pathTuple -+ return ClusterAttribute.EventPath.from_cluster(EndpointId=None, Cluster=pathTuple) - elif issubclass(pathTuple, ClusterObjects.ClusterEvent): -- event = pathTuple -+ return ClusterAttribute.EventPath.from_event(EndpointId=None, Event=pathTuple) - else: - raise ValueError("Unsupported Event Path") - else: - if pathTuple[0] == '*': -- urgent = pathTuple[-1] -- pass -+ return ClusterAttribute.EventPath(Urgent=pathTuple[-1]) - else: -+ urgent = bool(pathTuple[-1]) if len(pathTuple) > 2 else False - # endpoint + (cluster) event / endpoint + cluster -- endpoint = pathTuple[0] - if issubclass(pathTuple[1], ClusterObjects.Cluster): -- cluster = pathTuple[1] -+ return ClusterAttribute.EventPath.from_cluster(EndpointId=pathTuple[0], Cluster=pathTuple[1], Urgent=urgent) - elif issubclass(pathTuple[1], ClusterAttribute.ClusterEvent): -- event = pathTuple[1] -+ return ClusterAttribute.EventPath.from_event(EndpointId=pathTuple[0], Event=pathTuple[1], Urgent=urgent) - else: - raise ValueError("Unsupported Attribute Path") -- urgent = bool(pathTuple[-1]) if len(pathTuple) > 2 else False -- return ClusterAttribute.EventPath( -- EndpointId=endpoint, Cluster=cluster, Event=event, Urgent=urgent) - - async def Read(self, nodeid: int, attributes: typing.List[typing.Union[ - None, # Empty tuple, all wildcard -@@ -1514,7 +1499,7 @@ class ChipDeviceControllerBase(): - - result = asyncio.run(self.ReadAttribute( - nodeid, [(endpoint, attributeType)])) -- path = ClusterAttribute.AttributePath( -+ path = ClusterAttribute.AttributePath.from_attribute( - EndpointId=endpoint, Attribute=attributeType) - return im.AttributeReadResult(path=im.AttributePath(nodeId=nodeid, endpointId=path.EndpointId, clusterId=path.ClusterId, attributeId=path.AttributeId), - status=0, value=result[endpoint][clusterType][attributeType], dataVersion=result[endpoint][clusterType][ClusterAttribute.DataVersion]) -diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py -index ce522bf452..51389e19a1 100644 ---- a/src/controller/python/chip/clusters/Attribute.py -+++ b/src/controller/python/chip/clusters/Attribute.py -@@ -54,62 +54,43 @@ class EventPriority(Enum): - CRITICAL = 2 - - --@dataclass -+@dataclass(frozen=True) - class AttributePath: - EndpointId: int = None - ClusterId: int = None - AttributeId: int = None - -- def __init__(self, EndpointId: int = None, Cluster=None, Attribute=None, ClusterId=None, AttributeId=None): -- self.EndpointId = EndpointId -- if Cluster is not None: -- # Wildcard read for a specific cluster -- if (Attribute is not None) or (ClusterId is not None) or (AttributeId is not None): -- raise Warning( -- "Attribute, ClusterId and AttributeId is ignored when Cluster is specified") -- self.ClusterId = Cluster.id -- return -- if Attribute is not None: -- if (ClusterId is not None) or (AttributeId is not None): -- raise Warning( -- "ClusterId and AttributeId is ignored when Attribute is specified") -- self.ClusterId = Attribute.cluster_id -- self.AttributeId = Attribute.attribute_id -- return -- self.ClusterId = ClusterId -- self.AttributeId = AttributeId -+ @staticmethod -+ def from_cluster(EndpointId: int, Cluster: Cluster) -> AttributePath: -+ if Cluster is None: -+ raise ValueError("Cluster cannot be None") -+ return AttributePath(EndpointId=EndpointId, ClusterId=Cluster.id) -+ -+ @staticmethod -+ def from_attribute(EndpointId: int, Attribute: ClusterAttributeDescriptor) -> AttributePath: -+ if Attribute is None: -+ raise ValueError("Attribute cannot be None") -+ return AttributePath(EndpointId=EndpointId, ClusterId=Attribute.cluster_id, AttributeId=Attribute.attribute_id) - - def __str__(self) -> str: - return f"{self.EndpointId}/{self.ClusterId}/{self.AttributeId}" - -- def __hash__(self): -- return str(self).__hash__() - -- --@dataclass -+@dataclass(frozen=True) - class DataVersionFilter: - EndpointId: int = None - ClusterId: int = None - DataVersion: int = None - -- def __init__(self, EndpointId: int = None, Cluster=None, ClusterId=None, DataVersion=None): -- self.EndpointId = EndpointId -- if Cluster is not None: -- # Wildcard read for a specific cluster -- if (ClusterId is not None): -- raise Warning( -- "Attribute, ClusterId and AttributeId is ignored when Cluster is specified") -- self.ClusterId = Cluster.id -- else: -- self.ClusterId = ClusterId -- self.DataVersion = DataVersion -+ @staticmethod -+ def from_cluster(EndpointId: int, Cluster: Cluster, DataVersion: int = None) -> AttributePath: -+ if Cluster is None: -+ raise ValueError("Cluster cannot be None") -+ return DataVersionFilter(EndpointId=EndpointId, ClusterId=Cluster.id, DataVersion=DataVersion) - - def __str__(self) -> str: - return f"{self.EndpointId}/{self.ClusterId}/{self.DataVersion}" - -- def __hash__(self): -- return str(self).__hash__() -- - - @dataclass - class TypedAttributePath: -@@ -165,44 +146,28 @@ class TypedAttributePath: - self.AttributeId = self.AttributeType.attribute_id - - --@dataclass -+@dataclass(frozen=True) - class EventPath: - EndpointId: int = None - ClusterId: int = None - EventId: int = None - Urgent: int = None - -- def __init__(self, EndpointId: int = None, Cluster=None, Event=None, ClusterId=None, EventId=None, Urgent=None): -- self.EndpointId = EndpointId -- self.Urgent = Urgent -- if Cluster is not None: -- # Wildcard read for a specific cluster -- if (Event is not None) or (ClusterId is not None) or (EventId is not None): -- raise Warning( -- "Event, ClusterId and AttributeId is ignored when Cluster is specified") -- self.ClusterId = Cluster.id -- return -- if Event is not None: -- if (ClusterId is not None) or (EventId is not None): -- raise Warning( -- "ClusterId and EventId is ignored when Event is specified") -- self.ClusterId = Event.cluster_id -- self.EventId = Event.event_id -- return -- self.ClusterId = ClusterId -- self.EventId = EventId -+ @staticmethod -+ def from_cluster(EndpointId: int, Cluster: Cluster, EventId: int = None, Urgent: int = None) -> "EventPath": -+ if Cluster is None: -+ raise ValueError("Cluster cannot be None") -+ return EventPath(EndpointId=EndpointId, ClusterId=Cluster.id, EventId=EventId, Urgent=Urgent) -+ -+ @staticmethod -+ def from_event(EndpointId: int, Event: ClusterEvent, Urgent: int = None) -> "EventPath": -+ if Event is None: -+ raise ValueError("Event cannot be None") -+ return EventPath(EndpointId=EndpointId, ClusterId=Event.cluster_id, EventId=Event.event_id, Urgent=Urgent) - - def __str__(self) -> str: - return f"{self.EndpointId}/{self.ClusterId}/{self.EventId}/{self.Urgent}" - -- def __hash__(self): -- return str(self).__hash__() -- -- --@dataclass --class AttributePathWithListIndex(AttributePath): -- ListIndex: int = None -- - - @dataclass - class EventHeader: -@@ -711,7 +676,7 @@ class AsyncReadTransaction: - def GetAllEventValues(self): - return self._events - -- def handleAttributeData(self, path: AttributePathWithListIndex, dataVersion: int, status: int, data: bytes): -+ def handleAttributeData(self, path: AttributePath, dataVersion: int, status: int, data: bytes): - try: - imStatus = chip.interaction_model.Status(status) - -diff --git a/src/controller/python/test/test_scripts/cluster_objects.py b/src/controller/python/test/test_scripts/cluster_objects.py -index 53516c1dc7..37f6819cbe 100644 ---- a/src/controller/python/test/test_scripts/cluster_objects.py -+++ b/src/controller/python/test/test_scripts/cluster_objects.py -@@ -164,7 +164,7 @@ class ClusterObjectTests: - ] - ) - expectedRes = [ -- AttributeStatus(Path=AttributePath( -+ AttributeStatus(Path=AttributePath.from_attribute( - EndpointId=1, - Attribute=Clusters.UnitTesting.Attributes.ListLongOctetString), Status=chip.interaction_model.Status.Success), - ] --- -2.45.2 - diff --git a/0012-Python-Drop-chip-device-ctrl-33488.patch b/0012-Python-Drop-chip-device-ctrl-33488.patch deleted file mode 100644 index b3d95e5..0000000 --- a/0012-Python-Drop-chip-device-ctrl-33488.patch +++ /dev/null @@ -1,2532 +0,0 @@ -From c6838582c226796198993741aaa8cd1a1022fed3 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Fri, 24 May 2024 16:19:35 +0200 -Subject: [PATCH] [Python] Drop chip-device-ctrl (#33488) - -* Drop chip-device-ctrl and ZCL* APIs - -Drop the deprecated chip-device-ctrl and remove the ZCL* API from the -Python CHIP controller. - -* Update docs to reflect chip-repl commands - -Update the Python CHIP controller docs to reflect the CHIP REPL -instead of the now removed chip-device-ctrl tool. - -* Remove unused imports - -* Replace chip-device-ctrl with chip-repl - -* Update/reword main QUICK_START and READMEs - -* Fix wrong/buggy cross-reference - -* Remove common chip-device-ctrl example - -Remove the outdated chip-device-ctrl example and refer to the Python -controller REPL documentation. - -* Add --ble-adapter support to CHIP REPL and update docs - -Add support to select the Bluetooth adapter using the common ---ble-adapter command line argument. - -Update the advanced docs for the Python Controller. - -* Address review feedback - -* Trim list of commands/add link to official API docs ---- - docs/QUICK_START.md | 2 +- - .../python_chip_controller_advanced_usage.md | 226 ++-- - .../guides/python_chip_controller_building.md | 499 ++----- - .../lighting-app/infineon/cyw30739/README.md | 16 +- - examples/lighting-app/python/README.md | 15 +- - examples/lock-app/infineon/cyw30739/README.md | 16 +- - scripts/tools/linux_ip_namespace_setup.sh | 2 +- - src/controller/README.md | 2 +- - src/controller/python/BUILD.gn | 5 +- - src/controller/python/README.md | 14 +- - src/controller/python/chip-device-ctrl.py | 1202 ----------------- - src/controller/python/chip/ChipDeviceCtrl.py | 82 +- - src/controller/python/chip/ChipReplStartup.py | 4 +- - .../pycontroller/build-chip-wheel.py | 1 - - 14 files changed, 260 insertions(+), 1826 deletions(-) - delete mode 100755 src/controller/python/chip-device-ctrl.py - -diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md -index 97f0f76b3b..10833e0c0c 100644 ---- a/docs/QUICK_START.md -+++ b/docs/QUICK_START.md -@@ -10,7 +10,7 @@ and platforms. - |
Controller / Admin
|
Node
| Description | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | [**chip-tool**](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md) (Linux / Mac)
Includes docs for all the cluster commands supported
| **all-clusters-app**
  • [M5Stack](https://github.com/project-chip/connectedhomeip/blob/master/examples/all-clusters-app/esp32/README.md) (ESP)
  • [Linux](https://github.com/project-chip/connectedhomeip/tree/master/examples/all-clusters-app/linux) simulation | Use the command line tool on a laptop to pair with and control an embedded Wi-Fi platform. This demo supports the “all-clusters-app”, so it provides the basic onoff light test and more. | --| [**chip-device-ctrl.py**](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) | **all-clusters-app**
  • [M5Stack](https://github.com/project-chip/connectedhomeip/blob/master/examples/all-clusters-app/esp32/README.md) (ESP)
  • [Linux](https://github.com/project-chip/connectedhomeip/tree/master/examples/all-clusters-app/linux) simulation | Same as above, but uses the pychip tool as Controller Node. | -+| [**chip-repl**](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) | **all-clusters-app**
  • [M5Stack](https://github.com/project-chip/connectedhomeip/blob/master/examples/all-clusters-app/esp32/README.md) (ESP)
  • [Linux](https://github.com/project-chip/connectedhomeip/tree/master/examples/all-clusters-app/linux) simulation | Same as above, but uses the Python CHIP REPL as Controller Node. | - - ## Thread Nodes - -diff --git a/docs/guides/python_chip_controller_advanced_usage.md b/docs/guides/python_chip_controller_advanced_usage.md -index c3d3f55ddc..2eee5472fd 100644 ---- a/docs/guides/python_chip_controller_advanced_usage.md -+++ b/docs/guides/python_chip_controller_advanced_usage.md -@@ -7,8 +7,9 @@ tool or Matter accessories on Linux. - -
    - --- [Bluetooth LE virtualization on Linux](#bluetooth-le-virtualization-on-linux) --- [Debugging with gdb](#debugging-with-gdb) -+- [Using Python CHIP Controller advanced features](#using-python-chip-controller-advanced-features) -+ - [Bluetooth LE virtualization on Linux](#bluetooth-le-virtualization-on-linux) -+ - [Debugging with gdb](#debugging-with-gdb) - -
    - -@@ -62,38 +63,38 @@ interfaces working as Bluetooth LE central and peripheral, respectively. - TX bytes:3488 acl:95 sco:0 commands:110 errors:0 - ``` - --4. Run the Python CHIP Controller with Bluetooth LE adapter defined from a -+4. Run the Python CHIP Controller REPL with Bluetooth LE adapter defined from a - command line: - -- For example, add `--bluetooth-adapter=hci2` to use the virtual interface -- `hci2` listed above. -+ For example, add `--ble-adapter=2` to use the virtual interface `hci2` -+ listed above. - - ``` -- chip-device-ctrl --bluetooth-adapter=hci2 -+ chip-repl --ble-adapter=2 - ``` - -
    - - ## Debugging with gdb - --You can run the chip-device-ctrl under GDB for debugging, however, since the --Matter core support library is a dynamic library, you cannot read the symbols --unless it is fully loaded. -+You can run the chip-repl under GDB for debugging, however, since the Matter SDK -+library is a dynamic library, you cannot read the symbols unless it is fully -+loaded. - - The following block is a example debug session using GDB: - - ``` - # GDB cannot run scripts directly --# so you need to run Python3 with the path of device controller --# Here, we use the feature from bash to get the path of chip-device-ctrl without typing it. --$ gdb --args python3 `which chip-device-ctrl` --GNU gdb (Ubuntu 10.1-2ubuntu2) 10.1.90.20210411-git --Copyright (C) 2021 Free Software Foundation, Inc. -+# so you need to run Python3 with the path of device controller REPL -+# Here, we use the feature from bash to get the path of chip-repl without typing it. -+$ gdb --args python3 `which chip-repl` -+GNU gdb (GDB) 14.2 -+Copyright (C) 2023 Free Software Foundation, Inc. - License GPLv3+: GNU GPL version 3 or later - This is free software: you are free to change and redistribute it. - There is NO WARRANTY, to the extent permitted by law. - Type "show copying" and "show warranty" for details. --This GDB was configured as "aarch64-linux-gnu". -+This GDB was configured as "x86_64-pc-linux-gnu". - Type "show configuration" for configuration details. - For bug reporting instructions, please see: - . -@@ -103,6 +104,12 @@ Find the GDB manual and other documentation resources online at: - For help, type "help". - Type "apropos word" to search for commands related to "word"... - Reading symbols from python3... -+ -+This GDB supports auto-downloading debuginfo from the following URLs: -+ -+Enable debuginfod for this session? (y or [n]) n -+Debuginfod has been disabled. -+To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit. - (No debugging symbols found in python3) - (gdb) - ``` -@@ -119,38 +126,68 @@ library, let run the Matter device controller first. - - ``` - (gdb) run --Starting program: /usr/bin/python3 /home/ubuntu/.local/bin/chip-device-ctrl -+Starting program: /home/sag/projects/project-chip/connectedhomeip/out/venv/bin/python3 /home/sag/projects/project-chip/connectedhomeip/out/venv/bin/chip-repl - [Thread debugging using libthread_db enabled] --Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1". --CHIP:DIS: Init admin pairing table with server storage. --CHIP:IN: local node id is 0x000000000001b669 --CHIP:DL: MDNS failed to join multicast group on wpan0 for address type IPv4: Inet Error 1016 (0x000003F8): Address not found --CHIP:ZCL: Using ZAP configuration... --CHIP:ZCL: deactivate report event --CHIP:CTL: Getting operational keys --CHIP:CTL: Generating operational certificate for the controller --CHIP:CTL: Getting root certificate for the controller from the issuer --CHIP:CTL: Generating credentials --CHIP:CTL: Loaded credentials successfully --CHIP:DL: Platform main loop started. --Chip Device Controller Shell -+Using host libthread_db library "/usr/lib/libthread_db.so.1". -+Python 3.11.9 (main, Apr 29 2024, 11:59:58) [GCC 13.2.1 20240417] -+Type 'copyright', 'credits' or 'license' for more information -+IPython 8.24.0 -- An enhanced Interactive Python. Type '?' for help. -+[1716395111.775747][364405:364405] CHIP:CTL: Setting attestation nonce to random value -+[1716395111.776196][364405:364405] CHIP:CTL: Setting CSR nonce to random value -+InitBLE 0[1716395111.776809][364405:364405] CHIP:DL: writing settings to file (/tmp/chip_counters.ini-T7hX27) -+[1716395111.776854][364405:364405] CHIP:DL: renamed tmp file to file (/tmp/chip_counters.ini) -+[1716395111.776860][364405:364405] CHIP:DL: NVS set: chip-counters/reboot-count = 9 (0x9) -+[1716395111.777261][364405:364405] CHIP:DL: Got Ethernet interface: eno2 -+[1716395111.777555][364405:364405] CHIP:DL: Found the primary Ethernet interface:eno2 -+[1716395111.777868][364405:364405] CHIP:DL: Got WiFi interface: wlp7s0 -+[1716395111.777877][364405:364405] CHIP:DL: Failed to reset WiFi statistic counts -+────────────────────────────────────────────────────────────────────────────────────────────────────────── Matter REPL ────────────────────────────────────────────────────────────────────────────────────────────────────────── -+ -+ -+ -+ Welcome to the Matter Python REPL! -+ -+ For help, please type matterhelp() -+ -+ To get more information on a particular object/class, you can pass -+ that into matterhelp() as well. -+ -+ -+───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── -+2024-05-22 18:25:11 allenwind PersistentStorage[364405] WARNING Initializing persistent storage from file: /tmp/repl-storage.json -+2024-05-22 18:25:11 allenwind PersistentStorage[364405] WARNING Loading configuration from /tmp/repl-storage.json... -+2024-05-22 18:25:11 allenwind CertificateAuthorityManager[364405] WARNING Loading certificate authorities from storage... -+2024-05-22 18:25:11 allenwind CertificateAuthority[364405] WARNING New CertificateAuthority at index 1 -+2024-05-22 18:25:11 allenwind CertificateAuthority[364405] WARNING Loading fabric admins from storage... -+2024-05-22 18:25:11 allenwind FabricAdmin[364405] WARNING New FabricAdmin: FabricId: 0x0000000000000001, VendorId = 0xFFF1 -+2024-05-22 18:25:11 allenwind FabricAdmin[364405] WARNING Allocating new controller with CaIndex: 1, FabricId: 0x0000000000000001, NodeId: 0x000000000001B669, CatTags: [] -+ -+ -+The following objects have been created: -+ certificateAuthorityManager: Manages a list of CertificateAuthority instances. -+ caList: The list of CertificateAuthority instances. -+ caList: A specific FabricAdmin object at index m for the nth CertificateAuthority instance. - --chip-device-ctrl > -+ -+Default CHIP Device Controller (NodeId: 112233): has been initialized to manage caList[0].adminList[0] (FabricId = 1), and is available as devCtrl -+ -+In [1]: - ``` - --The prompt `chip-device-ctrl >` indicates that the Matter core library is loaded --by Python, you can browse the symbols in the Matter core library, setting --breakpoints on functions and many other functions provided by GDB. -+The prompt `In [1]:` indicates that the Matter SDK library has been loaded and -+initialized by the Python Controller REPL, you can browse the symbols in the -+Matter core library, setting breakpoints on functions and many other functions -+provided by GDB. - --You can use `Ctrl-C` to send SIGINT to the controller anytime you want so you --can set breakpoints. -+You can use `Ctrl-Z` to send `SIGTSTP` to the Python 3 REPL process anytime you -+want so you can set breakpoints (unfortunately Ctrl+C seems to be captured by -+the REPL). - --> (`Ctrl-C` pressed here.) -+In [1]: (`Ctrl-Z` pressed here.) - - ``` --Thread 1 "python3" received signal SIGINT, Interrupt. --0x0000fffff7db79ec in __GI___select (nfds=, readfds=0xffffffffe760, writefds=0x0, exceptfds=0x0, timeout=) at ../sysdeps/unix/sysv/linux/select.c:49 --49 ../sysdeps/unix/sysv/linux/select.c: No such file or directory. -+Thread 1 "python3" received signal SIGTSTP, Stopped (user). -+0x00007ffff7650ceb in kill () from /usr/lib/libc.so.6 - (gdb) - ``` - -@@ -159,40 +196,27 @@ command in GDB (`b` for short) - - ``` - (gdb) b DeviceCommissioner::PairDevice --Breakpoint 1 at 0xfffff5b0f6b4 (2 locations) -+Breakpoint 1 at 0x7fffed453943: DeviceCommissioner::PairDevice. (4 locations) - (gdb) - ``` - --Type `continue` (`c` for short) to continue the device controller, you may need --another hit of `Enter` to see the prompt. -+Type `signal SIGCONT` to continue the device controller after stopping it with -+signal stop, you may need another hit of `Enter` to see the prompt. - - ``` --(gdb) c --Continuing. -- --chip-device-ctrl > -+(gdb) signal SIGCONT -+Continuing with signal SIGCONT. -+In [1]: - ``` - - Let do pairing over IP to see the effect of the breakpoint we just set. - - ``` --chip-device-ctrl > connect -ip 192.168.50.5 20202021 1 --Device is assigned with nodeid = 1 -- --Thread 1 "python3" hit Breakpoint 1, 0x0000fffff5b0f6b4 in chip::Controller::DeviceCommissioner::PairDevice(unsigned long, chip::RendezvousParameters&)@plt () -- from /home/ubuntu/.local/lib/python3.9/site-packages/chip/_ChipDeviceCtrl.so --(gdb) --``` -- --The `@plt` symbol means it is a symbol used by dynamic library loader, type `c` --(for `continue`) and it will break on the real function. -+In [1]: devCtrl.CommissionWithCode("MT:-24J0AFN00KA0648G00", 1234) - --``` --(gdb) c --Continuing. -- --Thread 1 "python3" hit Breakpoint 1, chip::Controller::DeviceCommissioner::PairDevice (this=0xd28540, remoteDeviceId=1, params=...) at ../../src/controller/CHIPDeviceController.cpp:827 --827 { -+Thread 5 "python3" hit Breakpoint 1.1, chip::Controller::DeviceCommissioner::PairDevice (this=0x7fffd8003a90, remoteDeviceId=1234, setUpCode=0x7ffff453d490 "MT:-24J0AFN00KA0648G00", params=..., -+ discoveryType=chip::Controller::DiscoveryType::kAll, resolutionData=...) at ../../src/controller/CHIPDeviceController.cpp:646 -+646 { - (gdb) - ``` - -@@ -201,46 +225,44 @@ then you can use `bt` (for `backtrace`) to see the backtrace of the call stack. - - ``` - (gdb) bt --#0 chip::Controller::DeviceCommissioner::PairDevice(unsigned long, chip::RendezvousParameters&) (this=0xd28540, remoteDeviceId=1, params=...) -- at ../../src/controller/CHIPDeviceController.cpp:827 --#1 0x0000fffff5b3095c in pychip_DeviceController_ConnectIP(chip::Controller::DeviceCommissioner*, char const*, uint32_t, chip::NodeId) -- (devCtrl=0xd28540, peerAddrStr=0xfffff467ace0 "192.168.50.5", setupPINCode=20202021, nodeid=1) at ../../src/controller/python/ChipDeviceController-ScriptBinding.cpp:234 --#2 0x0000fffff7639148 in () at /lib/aarch64-linux-gnu/libffi.so.8 --#3 0x0000fffff7638750 in () at /lib/aarch64-linux-gnu/libffi.so.8 --#4 0x0000fffff7665a44 in () at /usr/lib/python3.9/lib-dynload/_ctypes.cpython-39-aarch64-linux-gnu.so --#5 0x0000fffff7664c7c in () at /usr/lib/python3.9/lib-dynload/_ctypes.cpython-39-aarch64-linux-gnu.so --#6 0x00000000004a54f0 in _PyObject_MakeTpCall () --#7 0x000000000049cb10 in _PyEval_EvalFrameDefault () --#8 0x0000000000496d1c in () --#9 0x00000000004b1eb0 in _PyFunction_Vectorcall () --#10 0x0000000000498264 in _PyEval_EvalFrameDefault () --#11 0x00000000004b1cb8 in _PyFunction_Vectorcall () --#12 0x0000000000498418 in _PyEval_EvalFrameDefault () --#13 0x0000000000496d1c in () --#14 0x00000000004b1eb0 in _PyFunction_Vectorcall () --#15 0x0000000000498418 in _PyEval_EvalFrameDefault () --#16 0x00000000004b1cb8 in _PyFunction_Vectorcall () --#17 0x00000000004c6bc8 in () --#18 0x0000000000498264 in _PyEval_EvalFrameDefault () --#19 0x00000000004b1cb8 in _PyFunction_Vectorcall () --#20 0x0000000000498418 in _PyEval_EvalFrameDefault () --#21 0x00000000004966f8 in () --#22 0x00000000004b1f18 in _PyFunction_Vectorcall () --#23 0x0000000000498418 in _PyEval_EvalFrameDefault () --#24 0x00000000004b1cb8 in _PyFunction_Vectorcall () --#25 0x0000000000498264 in _PyEval_EvalFrameDefault () --#26 0x00000000004966f8 in () --#27 0x0000000000496490 in _PyEval_EvalCodeWithName () --#28 0x0000000000595b7c in PyEval_EvalCode () --#29 0x00000000005c6a5c in () --#30 0x00000000005c0a70 in () --#31 0x00000000005c69a8 in () --#32 0x00000000005c6148 in PyRun_SimpleFileExFlags () --#33 0x00000000005b60bc in Py_RunMain () --#34 0x0000000000585a08 in Py_BytesMain () --#35 0x0000fffff7d0c9d4 in __libc_start_main (main= -- 0x5858fc <_start+60>, argc=2, argv=0xfffffffff498, init=, fini=, rtld_fini=, stack_end=) at ../csu/libc-start.c:332 --#36 0x00000000005858f8 in _start () -+(gdb) bt -+#0 chip::Controller::DeviceCommissioner::PairDevice -+ (this=0x7fffd8003a90, remoteDeviceId=1234, setUpCode=0x7fffef2555d0 "MT:-24J0AFN00KA0648G00", params=..., discoveryType=chip::Controller::DiscoveryType::kAll, resolutionData=...) -+ at ../../src/controller/CHIPDeviceController.cpp:646 -+#1 0x00007fffed040825 in pychip_DeviceController_ConnectWithCode (devCtrl=0x7fffd8003a90, onboardingPayload=0x7fffef2555d0 "MT:-24J0AFN00KA0648G00", nodeid=1234, discoveryType=2 '\002') -+ at ../../src/controller/python/ChipDeviceController-ScriptBinding.cpp:395 -+#2 0x00007ffff6ad5596 in ??? () at /usr/lib/libffi.so.8 -+#3 0x00007ffff6ad200e in ??? () at /usr/lib/libffi.so.8 -+#4 0x00007ffff6ad4bd3 in ffi_call () at /usr/lib/libffi.so.8 -+#5 0x00007ffff6aeaffc in ??? () at /usr/lib/python3.11/lib-dynload/_ctypes.cpython-311-x86_64-linux-gnu.so -+#6 0x00007ffff6aeb4b4 in ??? () at /usr/lib/python3.11/lib-dynload/_ctypes.cpython-311-x86_64-linux-gnu.so -+#7 0x00007ffff794a618 in _PyObject_MakeTpCall () at /usr/lib/libpython3.11.so.1.0 -+#8 0x00007ffff78f3d03 in _PyEval_EvalFrameDefault () at /usr/lib/libpython3.11.so.1.0 -+#9 0x00007ffff7adef90 in ??? () at /usr/lib/libpython3.11.so.1.0 -+#10 0x00007ffff79ebc0b in _PyObject_FastCallDictTstate () at /usr/lib/libpython3.11.so.1.0 -+#11 0x00007ffff79ebe02 in _PyObject_Call_Prepend () at /usr/lib/libpython3.11.so.1.0 -+#12 0x00007ffff79ec114 in ??? () at /usr/lib/libpython3.11.so.1.0 -+#13 0x00007ffff794a618 in _PyObject_MakeTpCall () at /usr/lib/libpython3.11.so.1.0 -+#14 0x00007ffff78f3d03 in _PyEval_EvalFrameDefault () at /usr/lib/libpython3.11.so.1.0 -+#15 0x00007ffff7adef90 in ??? () at /usr/lib/libpython3.11.so.1.0 -+#16 0x00007ffff7955b97 in PyObject_Vectorcall () at /usr/lib/libpython3.11.so.1.0 -+#17 0x00007ffff6aea174 in ??? () at /usr/lib/python3.11/lib-dynload/_ctypes.cpython-311-x86_64-linux-gnu.so -+#18 0x00007ffff6aea28c in ??? () at /usr/lib/python3.11/lib-dynload/_ctypes.cpython-311-x86_64-linux-gnu.so -+#19 0x00007ffff6ad5152 in ??? () at /usr/lib/libffi.so.8 -+#20 0x00007ffff6ad57b8 in ??? () at /usr/lib/libffi.so.8 -+#21 0x00007fffed5de848 in chip::DeviceLayer::Internal::GenericPlatformManagerImpl::_DispatchEvent -+ (this=0x7fffed88dc90 , event=0x7fffe6fffe30) at ../../src/include/platform/internal/GenericPlatformManagerImpl.ipp:304 -+#22 0x00007fffed5dd90d in chip::DeviceLayer::PlatformManager::DispatchEvent (this=0x7fffed88dc80 , event=0x7fffe6fffe30) at ../../src/include/platform/PlatformManager.h:503 -+#23 0x00007fffed5df45b in chip::DeviceLayer::Internal::GenericPlatformManagerImpl_POSIX::ProcessDeviceEvents -+ (this=0x7fffed88dc90 ) at ../../src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp:185 -+#24 0x00007fffed5dee64 in chip::DeviceLayer::Internal::GenericPlatformManagerImpl_POSIX::_RunEventLoop (this=0x7fffed88dc90 ) -+--Type for more, q to quit, c to continue without paging-- -+ at ../../src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp:227 -+#25 0x00007fffed5dd888 in chip::DeviceLayer::PlatformManager::RunEventLoop (this=0x7fffed88dc80 ) at ../../src/include/platform/PlatformManager.h:403 -+#26 0x00007fffed5df3fe in chip::DeviceLayer::Internal::GenericPlatformManagerImpl_POSIX::EventLoopTaskMain (arg=0x7fffed88dc90 ) -+ at ../../src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp:256 -+#27 0x00007ffff76a6ded in ??? () at /usr/lib/libc.so.6 -+#28 0x00007ffff772a0dc in ??? () at /usr/lib/libc.so.6 - (gdb) - ``` - -diff --git a/docs/guides/python_chip_controller_building.md b/docs/guides/python_chip_controller_building.md -index c940f2c925..8a7acc884a 100644 ---- a/docs/guides/python_chip_controller_building.md -+++ b/docs/guides/python_chip_controller_building.md -@@ -1,25 +1,20 @@ --# Deprecation notice -- --chip-device-ctrl is no longer maintained and should not be used. -- --Matter-repl is the current python controller implementation. -- - # Working with Python CHIP Controller - --The Python CHIP Controller is a tool that allows to commission a Matter device --into the network and to communicate with it using the Zigbee Cluster Library --(ZCL) messages. -+The Python CHIP controller is a library that allows to create a Matter fabric -+and commission Matter devices with it. - --> The chip-device-ctrl tool will be deprecated, and will be replaced by --> chip-repl. Continue reading to see how to do the same thing with chip-repl. -+The `chip-repl` is a REPl which sets up a Python CHIP Controller and allows to -+explore the Python CHIP Controller API and communicate with devices from the -+command line. - -
    - - - [Source files](#source-files) --- [Building Android CHIPTool](#building-and-installing) --- [Running the tool](#running-the-tool) --- [Using Python CHIP Controller for Matter accessory testing](#using-python-chip-controller-for-matter-accessory-testing) --- [List of commands](#list-of-commands) -+- [Building Python CHIP Controller](#building-and-installing) -+- [Running the CHIP REPL](#running-the-chip-repl) -+- [Using Python CHIP Controller REPL for Matter accessory testing](#using-python-chip-controller-repl-for-matter-accessory-testing) -+- [Example usage of the Python CHIP Controller REPL](#example-usage-of-the-python-chip-controller-repl) -+- [Explore Clusters, Attributes and Commands](#explore-clusters-attributes-and-commands) - -
    - -@@ -85,35 +80,31 @@ To build and run the Python CHIP controller: - scripts/build_python.sh -m platform -i separate - ``` - -- > Note: To get more details about available build configurations, run the -+ > Note: This builds the Python CHIP Controller along with the CHIP REPL as -+ > Python wheels and installs it into a separate Python virtual environment. -+ > To get more details about available build configurations, run the - > following command: `scripts/build_python.sh --help` - -
    - --## Running the tool -+## Running the CHIP REPL - --1. Activate the Python virtual environment: -+1. Activate the Python virtual environment with the Python CHIP Controller -+ installed: - - ``` - source out/python_env/bin/activate - ``` - --2. Run the Python CHIP controller with root privileges, which is required to -- obtain access to the Bluetooth interface: -- -- ``` -- sudo out/python_env/bin/chip-device-ctrl -- ``` -- -- You can also select the Bluetooth LE interface using command line argument: -+2. Run the CHIP REPL to explore the API of the Python CHIP controller: - - ``` -- sudo out/python_env/bin/chip-device-ctrl --bluetooth-adapter=hci2 -+ chip-repl - ``` - -
    - --## Using Python CHIP Controller for Matter accessory testing -+## Using Python CHIP Controller REPL for Matter accessory testing - - This section describes how to use Python CHIP controller to test the Matter - accessory. Below steps depend on the application clusters that you implemented -@@ -135,13 +126,14 @@ require physical trigger, for example pushing a button. Follow the documentation - of the Matter accessory example to learn how Bluetooth LE advertising is enabled - for the given example. - --### Step 3: Discover Matter accessory device over Bluetooth LE -+### Step 3: Discover commissionable Matter accessory device - --An uncommissioned accessory device advertises over Bluetooth LE. Run the --following command to scan all advertised Matter devices: -+An uncommissioned accessory device advertises over Bluetooth LE or via mDNS if -+already on the network. Run the following command to scan all advertised Matter -+devices: - - ``` --chip-device-ctrl > ble-scan -+devCtrl.DiscoverCommissionableNodes() - ``` - - ### Step 4: Set network pairing credentials -@@ -177,11 +169,12 @@ network interface, such as Thread or Wi-Fi. - datasets directly from the Thread Border Router, you might also use a - different out-of-band method. - --2. Set the previously obtained Active Operational Dataset as a hex-encoded value -- using the following command: -+2. Set the previously obtained Active Operational Dataset as a byte array using -+ the following command: - - ``` -- chip-device-ctrl > set-pairing-thread-credential 0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8 -+ thread_dataset = bytes.fromhex("0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8") -+ devCtrl.SetThreadOperationalDataset(thread_dataset) - ``` - - #### Setting Wi-Fi network credentials -@@ -190,11 +183,9 @@ Assuming your Wi-Fi SSID is _TESTSSID_, and your Wi-Fi password is _P455W4RD_, - set the credentials to the controller by executing the following command: - - ``` --chip-device-ctrl > set-pairing-wifi-credential TESTSSID P455W4RD -+devCtrl.SetWiFiCredentials(, ) - ``` - --**REPL Command**: `devCtrl.SetWiFiCredentials(, )` -- - ### Step 5: Commission the Matter accessory device over Bluetooth LE - - The controller uses a 12-bit value called **discriminator** to discern between -@@ -222,16 +213,26 @@ with the following assumptions for the Matter accessory device: - - The temporary Node ID is _1234_ - - ``` --chip-device-ctrl > connect -ble 3840 20202021 1234 -+devCtrl.ConnectBLE(3840, 20202021, 1234) - ``` - --**REPL Command:** --`devCtrl.ConnectBLE(, , )` -- - You can skip the last parameter, the Node ID, in the command. If you skip it, - the controller will assign it randomly. In that case, note down the Node ID, - because it is required later in the configuration process. - -+It is also possible to use the QR setup code instead. It typically is shown on -+the terminal of the device as well. For example: -+ -+``` -+CHIP:SVR: SetupQRCode: [MT:-24J0AFN00KA0648G00] -+``` -+ -+Use the following command to commission the device with the QR code: -+ -+``` -+devCtrl.CommissionWithCode("MT:-24J0AFN00KA0648G00", 1234) -+``` -+ - After connecting the device over Bluetooth LE, the controller will go through - the following stages: - -@@ -255,429 +256,155 @@ the following stages: - finished and the Python CHIP controller is now using only the IPv6 traffic - to reach the device. - --### Step 6: Control application ZCL clusters. -+### Step 6: Control application clusters. - - For the light bulb example, execute the following command to toggle the LED - state: - - ``` --chip-device-ctrl > zcl OnOff Toggle 1234 1 0 -+await devCtrl.SendCommand(1234, 1, Clusters.OnOff.Commands.Toggle()) - ``` - --**REPL Command:** --`await devCtrl.SendCommand(1234, 1, Clusters.OnOff.Commands.Toggle())` -- - To change the brightness of the LED, use the following command, with the level - value somewhere between 0 and 255. - - ``` --chip-device-ctrl > zcl LevelControl MoveToLevel 1234 1 0 level=50 -+commandToSend = LevelControl.Commands.MoveToLevel(level=50, transitionTime=Null, optionsMask=0, optionsOverride=0) -+await devCtrl.SendCommand(1234, 1, commandToSend) - ``` - --**REPL Command:** --`await devCtrl.SendCommand(1234, 1, LevelControl.Commands.MoveToLevel(level=50, transitionTime=Null, optionsMask=0, optionsOverride=0))` -- - ### Step 7: Read basic information out of the accessory. - - Every Matter accessory device supports a Basic Information Cluster, which - maintains collection of attributes that a controller can obtain from a device, --such as the vendor name, the product name, or software version. Use `zclread` --command to read those values from the device: -+such as the vendor name, the product name, or software version. Use -+`ReadAttribute()` command to read those values from the device: - - ``` --chip-device-ctrl > zclread BasicInformation VendorName 1234 1 0 --chip-device-ctrl > zclread BasicInformation ProductName 1234 1 0 --chip-device-ctrl > zclread BasicInformation SoftwareVersion 1234 1 0 -+attributes = [ -+ (0, Clusters.BasicInformation.Attributes.VendorName), -+ (0, Clusters.BasicInformation.Attributes.ProductName), -+ (0, Clusters.BasicInformation.Attributes.SoftwareVersion), -+] -+await devCtrl.ReadAttribute(1234, attributes) - ``` - --**REPL Command:** --`await devCtrl.ReadAttribute(1234, [(1, Clusters.BasicInformation.Attributes.VendorName)])` -- --> Use the `zcl ? BasicInformation` command to list all available commands for --> Basic Information Cluster. --> - > In REPL, you can type `Clusters.BasicInformation.Attributes.` and then use the - > TAB key. - -
    - --## List of commands -- --### `ble-adapter-print` -+## Example usage of the Python CHIP Controller REPL - --> BLE adapter operations is not yet supported in REPL -+These section covers a few useful commands of the Python CHIP Controller along -+with examples demonstrating how they can be called from the REPL. - --Print the available Bluetooth adapters on device. Takes no arguments: -- --``` --chip-device-ctrl > ble-adapter-print --2021-03-04 16:09:40,930 ChipBLEMgr INFO AdapterName: hci0 AdapterAddress: 00:AA:01:00:00:23 --``` -+The -+[CHIP Device Controller API documentation offer](https://project-chip.github.io/connectedhomeip-doc/testing/ChipDeviceCtrlAPI.html#chip-chipdevicectrl) -+the full list of available commands. - --### `ble-debug-log` -- --> BLE adapter operations is not yet supported in REPL -- --Enable the Bluetooth LE debug logs. -- --``` --chip-device-ctrl > ble-debug-log 1 --``` -- --### `ble-scan [-t ] [identifier]` -- --> BLE adapter operations is not yet supported in REPL -- --Start a scan action to search for valid CHIP devices over Bluetooth LE (for at --most _timeout_ seconds). Stop when the device is matching the identifier or the --counter times out. -- --``` --chip-device-ctrl > ble-scan --2021-05-29 22:28:05,461 ChipBLEMgr INFO scanning started --2021-05-29 22:28:07,206 ChipBLEMgr INFO Name = ChipLight --2021-05-29 22:28:07,206 ChipBLEMgr INFO ID = f016e23d-0d00-35d5-93e7-588acdbc7e54 --2021-05-29 22:28:07,207 ChipBLEMgr INFO RSSI = -79 --2021-05-29 22:28:07,207 ChipBLEMgr INFO Address = E0:4D:84:3C:BB:C3 --2021-05-29 22:28:07,209 ChipBLEMgr INFO Pairing State = 0 --2021-05-29 22:28:07,209 ChipBLEMgr INFO Discriminator = 3840 --2021-05-29 22:28:07,209 ChipBLEMgr INFO Vendor Id = 9050 --2021-05-29 22:28:07,209 ChipBLEMgr INFO Product Id = 20044 --2021-05-29 22:28:07,210 ChipBLEMgr INFO Adv UUID = 0000fff6-0000-1000-8000-00805f9b34fb --2021-05-29 22:28:07,210 ChipBLEMgr INFO Adv Data = 00000f5a234c4e --2021-05-29 22:28:07,210 ChipBLEMgr INFO --2021-05-29 22:28:16,246 ChipBLEMgr INFO scanning stopped --``` -- --### `set-pairing-thread-credential ` -+### `SetThreadOperationalDataset()` - - Provides the controller with Thread network credentials that will be used in the - device commissioning procedure to configure the device with a Thread interface. - - ``` --chip-device-ctrl > set-pairing-thread-credential 0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8 -+thread_dataset = bytes.fromhex("0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8") -+devCtrl.SetThreadOperationalDataset(thread_dataset) - ``` - --**REPL Commands:** --`devCtrl.SetThreadOperationalDataset(bytes.FromHex("0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8"))` -- --### `set-pairing-wifi-credential ` -+### `SetWiFiCredentials(: str, : str)` - - Provides the controller with Wi-Fi network credentials that will be used in the - device commissioning procedure to configure the device with a Wi-Fi interface. - - ``` --chip-device-ctrl > set-pairing-wifi-credential TESTSSID P455W4RD -+devCtrl.SetWiFiCredentials('TESTSSID', 'P455W4RD') - ``` - --**REPL Commands:** `devCtrl.SetWiFiCredentials('TESTSSID', 'P455W4RD')` -- --### `connect -ip
    []` -- --Do key exchange and establish a secure session between controller and device --using IP transport. -- --The Node ID will be used by controller to distinguish multiple devices. This --does not match the spec and will be removed later. The nodeid will not be --persisted by controller / device. -- --If no nodeid given, a random Node ID will be used. -- --**REPL Commands:** --`devCtrl.CommissionIP(b'', , )` -- --### `connect -ble []` -- --Do key exchange and establish a secure session between controller and device --using Bluetooth LE transport. -- --The Node ID will be used by controller to distinguish multiple devices. This --does not match the spec and will be removed later. The nodeid will not be --persisted by controller / device. -- --If no nodeid given, a random Node ID will be used. -- --**REPL Commands:** --`devCtrl.ConnectBLE(, , )` -+### `CommissionWithCode(: str, : int, : DiscoveryType)` - --### `close-session ` -+Commission with the given nodeid from the setupPayload. setupPayload may be a QR -+or the manual setup code. - --If case there exists an open session (PASE or CASE) to the device with a given --Node ID, mark it as expired. -- --**REPL Commands:** `devCtrl.CloseSession()` -- --### `discover` -- --> To be implemented in REPL -- --Discover available Matter accessory devices: -- --``` --chip-device-ctrl > discover -all - ``` -- --### `resolve ` -- --> To be implemented in REPL -- --Resolve DNS-SD name corresponding with the given Node ID and update address of --the node in the device controller: -- --``` --chip-device-ctrl > resolve 1234 -+devCtrl.CommissionWithCode("MT:-24J0AFN00KA0648G00", 1234) - ``` - --### `setup-payload generate [-v ] [-p ] [-cf ] [-dc ] [-dv ] [-ps ]` -- --> To be implemented in REPL -+### `SendCommand(: int, : int, Clusters..Commands.())` - --Print the generated Onboarding Payload Contents in human-readable (Manual --Pairing Code) and machine-readable (QR Code) format: -+Send a Matter command to the device. For example: - -+```python -+commandToSend = Clusters.LevelControl.Commands.MoveWithOnOff(moveMode=1, rate=2, optionsMask=0, optionsOverride=0) -+await devCtrl.SendCommand(1234, 1, commandToSend) - ``` --chip-device-ctrl > setup-payload generate -v 9050 -p 65279 -cf 0 -dc 2 -dv 2976 -ps 34567890 --Manual pairing code: [26318621095] --SetupQRCode: [MT:YNJV7VSC00CMVH7SR00] --``` -- --### `setup-payload parse-manual ` -- --> To be implemented in REPL -- --Print the commissioning information encoded in the Manual Pairing Code: -- --``` --chip-device-ctrl > setup-payload parse-manual 34970112332 --Version: 0 --VendorID: 0 --ProductID: 0 --CommissioningFlow: 0 --RendezvousInformation: 0 --Discriminator: 3840 --SetUpPINCode: 20202021 --``` -- --### `setup-payload parse-qr ` - --> To be implemented in REPL -- --Print the commissioning information encoded in the QR Code payload: -+To see available arguments just create a command object without argument: - - ``` --chip-device-ctrl > setup-payload parse-qr "VP:vendorpayload%MT:W0GU2OTB00KA0648G00" --Version: 0 --VendorID: 9050 --ProductID: 20043 --CommissioningFlow: 0 --RendezvousInformation: 2 [BLE] --Discriminator: 3840 --SetUpPINCode: 20202021 -+Clusters.LevelControl.Commands.MoveWithOnOff() - ``` - --### `zcl [arguments]` -- --Send a ZCL command to the device. For example: -+Shows which arguments are available: - - ``` --chip-device-ctrl > zcl LevelControl MoveWithOnOff 12344321 1 0 moveMode=1 rate=2 -+MoveWithOnOff( -+│ moveMode=0, -+│ rate=Null, -+│ optionsMask=0, -+│ optionsOverride=0 -+) - ``` - --**Format of arguments** -+### `ReadAttribute(: int, [(: int, Clusters..Attributes.)])` - --For any integer and char string (null terminated) types, just use `key=value`, --for example: `rate=2`, `string=123`, `string_2="123 456"` -- --For byte string type, use `key=encoding:value`, currently, we support `str` and --`hex` encoding, the `str` encoding will encode a NULL terminated string. For --example, `networkId=hex:0123456789abcdef` (for --`[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]`), `ssid=str:Test` (for --`['T', 'e', 's', 't', 0x00]`). -- --For boolean type, use `key=True` or `key=False`. -- --**REPL Commands:** -+Read the value of an attribute. For example: - - ```python --# await devCtrl.SendCommand(, , Clusters..Commands.()) --# e.g. --await devCtrl.SendCommand(12344321, 1, Clusters.LevelControl.Commands.MoveWithOnOff(moveMode=1, rate=2, optionsMask=0, optionsOverride=0)) --``` -- --### `zcl ?` -- --List available clusters: -- --``` --chip-device-ctrl > zcl ? --AccountLogin --ApplicationBasic --ApplicationLauncher --AudioOutput --BarrierControl --BasicInformation --Binding --BridgedDeviceBasicInformation --ColorControl --ContentLaunch --Descriptor --DoorLock --EthernetNetworkDiagnostics --FixedLabel --GeneralCommissioning --GeneralDiagnostics --GroupKeyManagement --Groups --Identify --KeypadInput --LevelControl --LowPower --MediaInput --MediaPlayback --NetworkCommissioning --OnOff --OperationalCredentials --PumpConfigurationAndControl --RelativeHumidityMeasurement --ScenesManagement --SoftwareDiagnostics --Switch --Channel --TargetNavigator --TemperatureMeasurement --TestCluster --Thermostat --TrustedRootCertificates --WakeOnLan --WindowCovering --``` -- --**REPL Commands** -- --Type `Clusters.` and hit TAB -- --### `zcl ? ` -- --List available commands in cluster. For example, for _Basic Information_ --cluster: -- --``` --chip-device-ctrl > zcl ? BasicInformation --DataModelRevision --VendorName --VendorID --ProductName --ProductID --UserLabel --Location --HardwareVersion --HardwareVersionString --SoftwareVersion --SoftwareVersionString --ManufacturingDate --PartNumber --ProductURL --ProductLabel --SerialNumber --LocalConfigDisabled --ClusterRevision --``` -- --**REPL Commands** -- --Type `Clusters.(cluster name).Commands.` and hit TAB -- --### `zclread [arguments]` -- --Read the value of ZCL attribute. For example: -- --``` --chip-device-ctrl > zclread BasicInformation VendorName 1234 1 0 --``` -- --**REPL Commands** -- --```python --# devCtrl.ReadAttribute(, [(, Clusters..Attributes.)]) --# e.g. --await devCtrl.ReadAttribute(1234, [(1, Clusters.BasicInformation.Attributes.VendorName)]) --``` -- --### `zclwrite ` -- --Write the value to a ZCL attribute. For example: -- --``` --chip-device-ctrl > zclwrite TestCluster Int8u 1 1 0 1 --chip-device-ctrl > zclwrite TestCluster Boolean 1 1 0 True --chip-device-ctrl > zclwrite TestCluster OctetString 1 1 0 str:123123 --chip-device-ctrl > zclwrite TestCluster CharString 1 1 0 233233 -+await devCtrl.ReadAttribute(1234, [(0, Clusters.BasicInformation.Attributes.VendorName)]) - ``` - --Note: The format of the value is the same as the format of argument values for --ZCL cluster commands. -+### `WriteAttribute(: int, [(: int, Clusters..Attributes.(value=))])` - --**REPL Commands** -+Write a value to an attribute. For example: - - ```python --# devCtrl.WriteAttribute(, [(, Clusters..Attributes.(value=))]) --# e.g. --await devCtrl.WriteAttribute(1, [(1, Clusters.UnitTesting.Attributes.Int8u(value=1))]) --await devCtrl.WriteAttribute(1, [(1, Clusters.UnitTesting.Attributes.Boolean(value=True))]) --await devCtrl.WriteAttribute(1, [(1, Clusters.UnitTesting.Attributes.OctetString(value=b'123123\x00'))]) --await devCtrl.WriteAttribute(1, [(1, Clusters.UnitTesting.Attributes.CharString(value='233233'))]) -+await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.Int8u(value=1))]) -+await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.Boolean(value=True))]) -+await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.OctetString(value=b'123123\x00'))]) -+await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.CharString(value='233233'))]) - ``` - --### `zclsubscribe ` -+### `ReadAttribute(: int, [(: int, Clusters..Attributes.)], reportInterval=(: int, : int))` - --Configure ZCL attribute reporting settings. For example: -- --``` --chip-device-ctrl > zclsubscribe OccupancySensing Occupancy 1234 1 10 20 --``` -- --**REPL Commands** -+Configure Matter attribute reporting settings. For example: - - ```python --# devCtrl.ReadAttribute(, [(, Clusters..Attributes.)], reportInterval=(, )) --# e.g. --await devCtrl.ReadAttribute(1, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20)) -+await devCtrl.ReadAttribute(1234, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20)) - ``` - --### `zclsubscribe -shutdown ` -- --Shutdown an existing attribute subscription. -+To shutdown an existing attribute subscription use the `Shutdown()` function on -+the returned subscription object: - --``` --chip-device-ctrl > zclsubscribe -shutdown 0xdeadbeefcafe -+```python -+sub = await devCtrl.ReadAttribute(1234, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20)) -+sub.Shutdown() - ``` - --The subscription id can be obtained from previous subscription messages: -+## Explore Clusters, Attributes and Commands - --``` --chip-device-ctrl > zclsubscribe OnOff OnOff 1 1 10 20 --(omitted messages) --[1633922898.965587][1117858:1117866] CHIP:DMG: SubscribeResponse = --[1633922898.965599][1117858:1117866] CHIP:DMG: { --[1633922898.965610][1117858:1117866] CHIP:DMG: SubscriptionId = 0xdeadbeefcafe, --[1633922898.965622][1117858:1117866] CHIP:DMG: MinIntervalFloorSeconds = 0xa, --[1633922898.965633][1117858:1117866] CHIP:DMG: MaxIntervalCeilingSeconds = 0x14, --[1633922898.965644][1117858:1117866] CHIP:DMG: } --[1633922898.965662][1117858:1117866] CHIP:ZCL: SubscribeResponse: --[1633922898.965673][1117858:1117866] CHIP:ZCL: SubscriptionId: 0xdeadbeefcafe --[1633922898.965683][1117858:1117866] CHIP:ZCL: ApplicationIdentifier: 0 --[1633922898.965694][1117858:1117866] CHIP:ZCL: status: EMBER_ZCL_STATUS_SUCCESS (0x00) --[1633922898.965709][1117858:1117866] CHIP:ZCL: attributeValue: false --(omitted messages) --``` -+In the Python REPL the Clusters and Attributes are classes. The `Clusters` -+module contains all clusters. Tab completion can be used to explore available -+clusters, attributes and commands. - --The subscription id is `0xdeadbeefcafe` in this case -+For example, to get a list of Clusters, type `Clusters.` and hit tab. Continue -+to hit tab to cycle through the available Clusters. Pressing return will select -+the Cluster. - --**REPL Commands** -+To explore Attributes, use the same technique but with the Attributes sub-class -+of the Clusters class, for example, type `Clusters.(cluster name).Attributes.` -+and hit tab. - --```python --# SubscriptionTransaction.Shutdown() --# e.g. --sub = await devCtrl.ReadAttribute(1, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20)) --sub.Shutdown() --``` -+The same is true for Commands, use the Commands sub-class. type -+`Clusters.(cluster name).Commands.` and hit tab. -diff --git a/examples/lighting-app/infineon/cyw30739/README.md b/examples/lighting-app/infineon/cyw30739/README.md -index f085272b1c..ac11bb3033 100644 ---- a/examples/lighting-app/infineon/cyw30739/README.md -+++ b/examples/lighting-app/infineon/cyw30739/README.md -@@ -104,19 +104,7 @@ Put the CYW30739 in to the recovery mode before running the flash script. - [Openthread_border_router](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/openthread_border_router_pi.md) - for more information on how to setup a border router on a raspberryPi. - --- You can provision and control the Chip device using the python controller, -- Chip tool standalone, Android or iOS app -+- You can provision and control the device using the Python controller REPL, -+ chip-tool standalone, Android or iOS app - - [Python Controller](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) -- -- Here is an example with the Python controller: -- -- ```bash -- $ chip-device-ctrl -- chip-device-ctrl > connect -ble 3840 20202021 1234 -- chip-device-ctrl > zcl NetworkCommissioning AddThreadNetwork 1234 0 0 operationalDataset=hex:0e080000000000000000000300000b35060004001fffe00208dead00beef00cafe0708fddead00beef000005108e11d8ea8ffaa875713699f59e8807e0030a4f70656e5468726561640102c2980410edc641eb63b100b87e90a9980959befc0c0402a0fff8 breadcrumb=0 timeoutMs=1000 -- chip-device-ctrl > zcl NetworkCommissioning EnableNetwork 1234 0 0 networkID=hex:dead00beef00cafe breadcrumb=0 timeoutMs=1000 -- chip-device-ctrl > close-ble -- chip-device-ctrl > resolve 1234 -- chip-device-ctrl > zcl OnOff Toggle 1234 1 0 -- ``` -diff --git a/examples/lighting-app/python/README.md b/examples/lighting-app/python/README.md -index 8e34e59dea..a935a165c9 100644 ---- a/examples/lighting-app/python/README.md -+++ b/examples/lighting-app/python/README.md -@@ -32,17 +32,6 @@ cd examples/lighting-app/python - python lighting.py - ``` - --Control the Python lighting matter device: -+Control the Python lighting matter device using the Python controller REPL: - --```shell --source ./out/python_env/bin/activate -- --chip-device-ctrl -- --chip-device-ctrl > connect -ble 3840 20202021 12344321 --chip-device-ctrl > zcl NetworkCommissioning AddOrUpdateWiFiNetwork 12344321 0 0 ssid=str:YOUR_SSID credentials=str:YOUR_PASSWORD breadcrumb=0 --chip-device-ctrl > zcl NetworkCommissioning ConnectNetwork 12344321 0 0 networkID=str:YOUR_SSID breadcrumb=0 --chip-device-ctrl > close-ble --chip-device-ctrl > resolve 5544332211 1 (pass appropriate fabric ID and node ID, you can get this from get-fabricid) --chip-device-ctrl > zcl OnOff Toggle 12344321 1 0 --``` -+[Python Controller](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) -diff --git a/examples/lock-app/infineon/cyw30739/README.md b/examples/lock-app/infineon/cyw30739/README.md -index b91f4b9157..86c9cf0490 100644 ---- a/examples/lock-app/infineon/cyw30739/README.md -+++ b/examples/lock-app/infineon/cyw30739/README.md -@@ -104,19 +104,7 @@ Put the CYW30739 in to the recovery mode before running the flash script. - [Openthread_border_router](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/openthread_border_router_pi.md) - for more information on how to setup a border router on a raspberryPi. - --- You can provision and control the Chip device using the python controller, -- Chip tool standalone, Android or iOS app -+- You can provision and control the device using the Python controller REPL, -+ chip-tool standalone, Android or iOS app - - [Python Controller](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) -- -- Here is an example with the Python controller: -- -- ```bash -- $ chip-device-ctrl -- chip-device-ctrl > connect -ble 3840 20202021 1234 -- chip-device-ctrl > zcl NetworkCommissioning AddThreadNetwork 1234 0 0 operationalDataset=hex:0e080000000000000000000300000b35060004001fffe00208dead00beef00cafe0708fddead00beef000005108e11d8ea8ffaa875713699f59e8807e0030a4f70656e5468726561640102c2980410edc641eb63b100b87e90a9980959befc0c0402a0fff8 breadcrumb=0 timeoutMs=1000 -- chip-device-ctrl > zcl NetworkCommissioning EnableNetwork 1234 0 0 networkID=hex:dead00beef00cafe breadcrumb=0 timeoutMs=1000 -- chip-device-ctrl > close-ble -- chip-device-ctrl > resolve 1234 -- chip-device-ctrl > zcl OnOff Toggle 1234 1 0 -- ``` -diff --git a/scripts/tools/linux_ip_namespace_setup.sh b/scripts/tools/linux_ip_namespace_setup.sh -index 5d467b4b48..f761eea384 100755 ---- a/scripts/tools/linux_ip_namespace_setup.sh -+++ b/scripts/tools/linux_ip_namespace_setup.sh -@@ -124,7 +124,7 @@ function help() { - echo "sudo /$file_name -r /" - echo "" - echo "Terminal 2:" -- echo "/chip-device-ctrl" -+ echo "/chip-repl" - echo "" - echo "This script requires sudo for setup and requires access to ebtables-legacy" - echo "to set up dual ipv4/ipv6 namespaces. Defaults to ipv6 only." -diff --git a/src/controller/README.md b/src/controller/README.md -index e46c191f55..70a3fa5e8f 100644 ---- a/src/controller/README.md -+++ b/src/controller/README.md -@@ -26,7 +26,7 @@ The POSIX CLI chip-tool is located in - - ### Python - --The Python chip-device-ctrl is located in -+The Python CHIP Controller library is located in - [../controller/python/](../controller/python). - - ## Feature Overview -diff --git a/src/controller/python/BUILD.gn b/src/controller/python/BUILD.gn -index caa33c3a40..e7b06d808e 100644 ---- a/src/controller/python/BUILD.gn -+++ b/src/controller/python/BUILD.gn -@@ -414,10 +414,7 @@ chip_python_wheel_action("chip-clusters") { - } - - chip_python_wheel_action("chip-repl") { -- py_scripts = [ -- "chip-device-ctrl.py", -- "chip-repl.py", -- ] -+ py_scripts = [ "chip-repl.py" ] - - py_manifest_files = [ - { -diff --git a/src/controller/python/README.md b/src/controller/python/README.md -index 34edea4446..e94a95e158 100644 ---- a/src/controller/python/README.md -+++ b/src/controller/python/README.md -@@ -1,10 +1,14 @@ --# Python CHIP Device Controller -+# Python CHIP Controller - --The Python CHIP controller is a tool that allows to commission a Matter device --into the network and to communicate with it using the Zigbee Cluster Library --(ZCL) messages. The tool uses the generic [Chip Device Controller](../) library. -+The Python CHIP controller is a library that allows to create a Matter fabric -+and commission Matter devices with it, as well as communicate with commissioned -+devices by reading/subscribing and writing Attributes and sending Commands. The -+Python CHIP controller is based on the native [Chip Device Controller](../) -+library. - --To learn more about the tool, how to build it and use its commands and advanced -+The Python CHIP Controller comes with a REPL which allows to explore and use the -+Python CHIP controller library from a shell. To learn more about the Python CHIP -+Controller and the REPL, how to build it and use its commands and advanced - features, read the following guides: - - - [Working with Python CHIP Controller](../../../docs/guides/python_chip_controller_building.md) -diff --git a/src/controller/python/chip-device-ctrl.py b/src/controller/python/chip-device-ctrl.py -deleted file mode 100755 -index 469fb2c49f..0000000000 ---- a/src/controller/python/chip-device-ctrl.py -+++ /dev/null -@@ -1,1202 +0,0 @@ --#!/usr/bin/env python -- --# --# Copyright (c) 2020-2021 Project CHIP Authors --# Copyright (c) 2013-2018 Nest Labs, Inc. --# All rights reserved. --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. --# -- --# --# @file --# This file implements the Python-based Chip Device Controller Shell. --# -- --from __future__ import absolute_import, print_function -- --import argparse --import base64 --import ctypes --import logging --import os --import platform --import random --import shlex --import string --import sys --import textwrap --import time --import traceback --import warnings --from cmd import Cmd --from optparse import OptionParser, OptionValueError -- --import chip.logging --import coloredlogs --from chip import ChipCommissionableNodeCtrl, ChipStack, exceptions, native --from chip.setup_payload import SetupPayload --from rich import pretty, print -- --# Extend sys.path with one or more directories, relative to the location of the --# running script, in which the chip package might be found . This makes it --# possible to run the device manager shell from a non-standard install location, --# as well as directly from its location the CHIP source tree. --# --# Note that relative package locations are prepended to sys.path so as to give --# the local version of the package higher priority over any version installed in --# a standard location. --# --scriptDir = os.path.dirname(os.path.abspath(__file__)) --relChipPackageInstallDirs = [ -- ".", -- "../lib/python", -- "../lib/python%s.%s" % (sys.version_info.major, sys.version_info.minor), -- "../lib/Python%s%s" % (sys.version_info.major, sys.version_info.minor), --] --for relInstallDir in relChipPackageInstallDirs: -- absInstallDir = os.path.realpath(os.path.join(scriptDir, relInstallDir)) -- if os.path.isdir(os.path.join(absInstallDir, "chip")): -- sys.path.insert(0, absInstallDir) -- -- --if platform.system() == 'Darwin': -- from chip.ChipCoreBluetoothMgr import CoreBluetoothManager as BleManager --elif sys.platform.startswith('linux'): -- from chip.ChipBluezMgr import BluezManager as BleManager -- --# The exceptions for CHIP Device Controller CLI -- -- --class ChipDevCtrlException(exceptions.ChipStackException): -- pass -- -- --class ParsingError(ChipDevCtrlException): -- def __init__(self, msg=None): -- self.msg = "Parsing Error: " + msg -- -- def __str__(self): -- return self.msg -- -- --def DecodeBase64Option(option, opt, value): -- try: -- return base64.standard_b64decode(value) -- except TypeError: -- raise OptionValueError( -- "option %s: invalid base64 value: %r" % (opt, value)) -- -- --def DecodeHexIntOption(option, opt, value): -- try: -- return int(value, 16) -- except ValueError: -- raise OptionValueError("option %s: invalid value: %r" % (opt, value)) -- -- --def ParseEncodedString(value): -- if value.find(":") < 0: -- raise ParsingError( -- "value should be encoded in encoding:encodedvalue format") -- enc, encValue = value.split(":", 1) -- if enc == "str": -- return encValue.encode("utf-8") + b'\x00' -- elif enc == "hex": -- return bytes.fromhex(encValue) -- raise ParsingError("only str and hex encoding is supported") -- -- --def ParseValueWithType(value, type): -- if type == 'int': -- return int(value) -- elif type == 'str': -- return value -- elif type == 'bytes': -- return ParseEncodedString(value) -- elif type == 'bool': -- return (value.upper() not in ['F', 'FALSE', '0']) -- else: -- raise ParsingError('cannot recognize type: {}'.format(type)) -- -- --def FormatZCLArguments(args, command): -- commandArgs = {} -- for kvPair in args: -- if kvPair.find("=") < 0: -- raise ParsingError("Argument should in key=value format") -- key, value = kvPair.split("=", 1) -- valueType = command.get(key, None) -- commandArgs[key] = ParseValueWithType(value, valueType) -- return commandArgs -- -- --def ShowColoredWarnings(message, category, filename, lineno, file=None, line=None): -- logging.warning(' %s:%s: %s:%s' % -- (filename, lineno, category.__name__, message)) -- return -- -- --class DeviceMgrCmd(Cmd): -- def __init__(self, rendezvousAddr=None, controllerNodeId=1, bluetoothAdapter=None): -- self.lastNetworkId = None -- self.replHint = None -- -- pretty.install(indent_guides=True, expand_all=True) -- -- coloredlogs.install(level='DEBUG') -- chip.logging.RedirectToPythonLogging() -- -- logging.getLogger().setLevel(logging.DEBUG) -- warnings.showwarning = ShowColoredWarnings -- -- Cmd.__init__(self) -- -- Cmd.identchars = string.ascii_letters + string.digits + "-" -- -- if sys.stdin.isatty(): -- self.prompt = "chip-device-ctrl > " -- else: -- self.use_rawinput = 0 -- self.prompt = "" -- -- DeviceMgrCmd.command_names.sort() -- -- self.bleMgr = None -- -- self.chipStack = ChipStack.ChipStack( -- bluetoothAdapter=bluetoothAdapter, persistentStoragePath='/tmp/chip-device-ctrl-storage.json') -- self.certificateAuthorityManager = chip.CertificateAuthority.CertificateAuthorityManager(chipStack=self.chipStack) -- self.certificateAuthority = self.certificateAuthorityManager.NewCertificateAuthority() -- self.fabricAdmin = self.certificateAuthority.NewFabricAdmin(vendorId=0xFFF1, fabricId=1) -- self.devCtrl = self.fabricAdmin.NewController( -- nodeId=controllerNodeId, useTestCommissioner=True) -- -- self.commissionableNodeCtrl = ChipCommissionableNodeCtrl.ChipCommissionableNodeController( -- self.chipStack) -- -- # If we are on Linux and user selects non-default bluetooth adapter. -- if sys.platform.startswith("linux") and (bluetoothAdapter is not None): -- try: -- self.bleMgr = BleManager(self.devCtrl) -- self.bleMgr.ble_adapter_select( -- "hci{}".format(bluetoothAdapter)) -- except Exception as ex: -- traceback.print_exc() -- print( -- "Failed to initialize BLE, if you don't have BLE, run chip-device-ctrl with --no-ble") -- raise ex -- -- self.historyFileName = os.path.expanduser( -- "~/.chip-device-ctrl-history") -- -- try: -- import readline -- -- if "libedit" in readline.__doc__: -- readline.parse_and_bind("bind ^I rl_complete") -- readline.set_completer_delims(" ") -- try: -- readline.read_history_file(self.historyFileName) -- except IOError: -- pass -- except ImportError: -- pass -- -- command_names = [ -- "setup-payload", -- -- "ble-scan", -- "ble-adapter-select", -- "ble-adapter-print", -- "ble-debug-log", -- -- "connect", -- "close-ble", -- "close-session", -- "resolve", -- "paseonly", -- "commission", -- "zcl", -- "zclread", -- "zclsubscribe", -- -- "discover", -- -- "set-pairing-wifi-credential", -- "set-pairing-thread-credential", -- -- "open-commissioning-window", -- -- "get-fabricid", -- ] -- -- def parseline(self, line): -- cmd, arg, line = Cmd.parseline(self, line) -- if cmd: -- cmd = self.shortCommandName(cmd) -- line = cmd + " " + arg -- return cmd, arg, line -- -- def completenames(self, text, *ignored): -- return [ -- name + " " -- for name in DeviceMgrCmd.command_names -- if name.startswith(text) or self.shortCommandName(name).startswith(text) -- ] -- -- def shortCommandName(self, cmd): -- return cmd.replace("-", "") -- -- def precmd(self, line): -- if not self.use_rawinput and line != "EOF" and line != "": -- print(">>> " + line) -- return line -- -- def postcmd(self, stop, line): -- if self.replHint is not None: -- print("Try the following command in repl: ") -- print(self.replHint) -- print("") -- self.replHint = None -- if not stop and self.use_rawinput: -- self.prompt = "chip-device-ctrl > " -- return stop -- -- def postloop(self): -- try: -- import readline -- -- try: -- readline.write_history_file(self.historyFileName) -- except IOError: -- pass -- except ImportError: -- pass -- -- def do_help(self, line): -- if line: -- cmd, arg, unused = self.parseline(line) -- try: -- doc = getattr(self, "do_" + cmd).__doc__ -- except AttributeError: -- doc = None -- if doc: -- self.stdout.write("%s\n" % textwrap.dedent(doc)) -- else: -- self.stdout.write("No help on %s\n" % (line)) -- else: -- self.print_topics( -- "\nAvailable commands (type help for more information):", -- DeviceMgrCmd.command_names, -- 15, -- 80, -- ) -- -- def do_closeble(self, line): -- """ -- close-ble -- -- Close the ble connection to the device. -- """ -- -- warnings.warn( -- "This method is being deprecated. " -- "Please use the DeviceController.CloseBLEConnection method directly in the REPL", DeprecationWarning) -- -- args = shlex.split(line) -- -- if len(args) != 0: -- print("Usage:") -- self.do_help("close") -- return -- -- try: -- self.devCtrl.CloseBLEConnection() -- except exceptions.ChipStackException as ex: -- print(str(ex)) -- -- def do_setlogoutput(self, line): -- """ -- set-log-output [ none | error | progress | detail ] -- -- Set the level of Chip logging output. -- """ -- -- warnings.warn( -- "This method is being deprecated. " -- "Please use the DeviceController.SetLogFilter method directly in the REPL", DeprecationWarning) -- -- args = shlex.split(line) -- -- if len(args) == 0: -- print("Usage:") -- self.do_help("set-log-output") -- return -- if len(args) > 1: -- print("Unexpected argument: " + args[1]) -- return -- -- category = args[0].lower() -- if category == "none": -- category = 0 -- elif category == "error": -- category = 1 -- elif category == "progress": -- category = 2 -- elif category == "detail": -- category = 3 -- else: -- print("Invalid argument: " + args[0]) -- return -- -- try: -- self.devCtrl.SetLogFilter(category) -- except exceptions.ChipStackException as ex: -- print(str(ex)) -- return -- -- def do_setuppayload(self, line): -- """ -- setup-payload generate [options] -- -- Options: -- -vr Version -- -vi Vendor ID -- -pi Product ID -- -cf Custom Flow [Standard = 0, UserActionRequired = 1, Custom = 2] -- -dc Discovery Capabilities [SoftAP = 1 | BLE = 2 | OnNetwork = 4] -- -dv Discriminator Value -- -ps Passcode -- -- setup-payload parse-manual -- setup-payload parse-qr -- """ -- -- warnings.warn( -- "This method is being deprecated. " -- "Please use the SetupPayload function in the chip.setup_payload package directly", DeprecationWarning) -- -- try: -- arglist = shlex.split(line) -- if arglist[0] not in ("generate", "parse-manual", "parse-qr"): -- self.do_help("setup-payload") -- return -- -- if arglist[0] == "generate": -- parser = argparse.ArgumentParser() -- parser.add_argument("-vr", type=int, default=0, dest='version') -- parser.add_argument( -- "-pi", type=int, default=0, dest='productId') -- parser.add_argument( -- "-vi", type=int, default=0, dest='vendorId') -- parser.add_argument( -- '-cf', type=int, default=0, dest='customFlow') -- parser.add_argument( -- "-dc", type=int, default=0, dest='capabilities') -- parser.add_argument( -- "-dv", type=int, default=0, dest='discriminator') -- parser.add_argument("-ps", type=int, dest='passcode') -- args = parser.parse_args(arglist[1:]) -- -- SetupPayload().PrintOnboardingCodes(args.passcode, args.vendorId, args.productId, -- args.discriminator, args.customFlow, args.capabilities, args.version) -- -- if arglist[0] == "parse-manual": -- SetupPayload().ParseManualPairingCode(arglist[1]).Print() -- -- if arglist[0] == "parse-qr": -- SetupPayload().ParseQrCode(arglist[1]).Print() -- -- except exceptions.ChipStackException as ex: -- print(str(ex)) -- return -- -- def do_bleadapterselect(self, line): -- """ -- ble-adapter-select -- -- Start BLE adapter select, deprecated, you can select adapter by command line arguments. -- """ -- if sys.platform.startswith("linux"): -- if not self.bleMgr: -- self.bleMgr = BleManager(self.devCtrl) -- -- self.bleMgr.ble_adapter_select(line) -- print( -- "This change only applies to ble-scan\n" -- "Please run device controller with --bluetooth-adapter= to select adapter\n" + -- "e.g. chip-device-ctrl --bluetooth-adapter hci0" -- ) -- else: -- print( -- "ble-adapter-select only works in Linux, ble-adapter-select mac_address" -- ) -- -- return -- -- def do_bleadapterprint(self, line): -- """ -- ble-adapter-print -- -- Print attached BLE adapter. -- """ -- if sys.platform.startswith("linux"): -- if not self.bleMgr: -- self.bleMgr = BleManager(self.devCtrl) -- -- self.bleMgr.ble_adapter_print() -- else: -- print("ble-adapter-print only works in Linux") -- -- return -- -- def do_bledebuglog(self, line): -- """ -- ble-debug-log 0:1 -- 0: disable BLE debug log -- 1: enable BLE debug log -- """ -- if not self.bleMgr: -- self.bleMgr = BleManager(self.devCtrl) -- -- self.bleMgr.ble_debug_log(line) -- -- return -- -- def do_blescan(self, line): -- """ -- ble-scan -- -- Start BLE scanning operations. -- """ -- -- if not self.bleMgr: -- self.bleMgr = BleManager(self.devCtrl) -- -- self.bleMgr.scan(line) -- -- return -- -- def ConnectFromSetupPayload(self, setupPayload, nodeid): -- # TODO(cecille): Get this from the C++ code? -- ble = 1 << 1 -- # Devices may be uncommissioned, or may already be on the network. Need to check both ways. -- # TODO(cecille): implement soft-ap connection. -- -- # Any device that is already commissioned into a fabric needs to use on-network -- # pairing, so look first on the network regardless of the QR code contents. -- print("Attempting to find device on Network") -- longDiscriminator = ctypes.c_uint16( -- int(setupPayload.attributes['Discriminator'])) -- self.devCtrl.DiscoverCommissionableNodesLongDiscriminator( -- longDiscriminator) -- print("Waiting for device responses...") -- strlen = 100 -- addrStrStorage = ctypes.create_string_buffer(strlen) -- # If this device is on the network and we're looking specifically for 1 device, -- # expect a quick response. -- if self.wait_for_one_discovered_device(): -- self.devCtrl.GetIPForDiscoveredDevice( -- 0, addrStrStorage, strlen) -- addrStr = addrStrStorage.value.decode('utf-8') -- print("Connecting to device at " + addrStr) -- pincode = ctypes.c_uint32( -- int(setupPayload.attributes['SetUpPINCode'])) -- try: -- self.devCtrl.CommissionIP(addrStrStorage, pincode, nodeid) -- print("Connected") -- return 0 -- except Exception as ex: -- print(f"Unable to connect on network: {ex}") -- else: -- print("Unable to locate device on network") -- -- if int(setupPayload.attributes["RendezvousInformation"]) & ble: -- print("Attempting to connect via BLE") -- longDiscriminator = ctypes.c_uint16( -- int(setupPayload.attributes['Discriminator'])) -- pincode = ctypes.c_uint32( -- int(setupPayload.attributes['SetUpPINCode'])) -- try: -- self.devCtrl.ConnectBLE(longDiscriminator, pincode, nodeid) -- print("Connected") -- return 0 -- except Exception as ex: -- print(f"Unable to connect: {ex}") -- return -1 -- -- def do_paseonly(self, line): -- """ -- paseonly -ip [] -- -- TODO: Add more methods to connect to device (like cert for auth, and IP -- for connection) -- """ -- -- try: -- args = shlex.split(line) -- if len(args) <= 1: -- print("Usage:") -- self.do_help("paseonly") -- return -- nodeid = random.randint(1, 1000000) # Just a random number -- if len(args) == 4: -- nodeid = int(args[3]) -- print("Device is assigned with nodeid = {}".format(nodeid)) -- self.replHint = f"devCtrl.EstablishPASESessionIP({repr(args[1])}, {int(args[2])}, {nodeid})" -- if args[0] == "-ip" and len(args) >= 3: -- self.devCtrl.EstablishPASESessionIP(args[1], int(args[2]), nodeid) -- else: -- print("Usage:") -- self.do_help("paseonly") -- return -- print( -- "Device temporary node id (**this does not match spec**): {}".format(nodeid)) -- except Exception as ex: -- print(str(ex)) -- return -- -- def do_commission(self, line): -- """ -- commission nodeid -- -- Runs commissioning on a device that has been connected with paseonly -- """ -- try: -- args = shlex.split(line) -- if len(args) != 1: -- print("Usage:") -- self.do_help("commission") -- return -- nodeid = int(args[0]) -- self.replHint = f"devCtrl.Commission({nodeid})" -- self.devCtrl.Commission(nodeid) -- except Exception as ex: -- print(str(ex)) -- return -- -- def do_connect(self, line): -- """ -- connect -ip [] -- connect -ble [] -- connect -qr [] -- connect -code [] -- -- connect command is used for establishing a rendezvous session to the device. -- currently, only connect using setupPinCode is supported. -- -qr option will connect to the first device with a matching long discriminator. -- -- TODO: Add more methods to connect to device (like cert for auth, and IP -- for connection) -- """ -- -- warnings.warn( -- "This method is being deprecated. " -- "Please use the DeviceController.[ConnectBLE|CommissionIP] methods directly in the REPL", DeprecationWarning) -- -- try: -- args = shlex.split(line) -- if len(args) <= 1: -- print("Usage:") -- self.do_help("connect SetupPinCode") -- return -- -- nodeid = random.randint(1, 1000000) # Just a random number -- if len(args) == 4: -- nodeid = int(args[3]) -- print("Device is assigned with nodeid = {}".format(nodeid)) -- -- if args[0] == "-ip" and len(args) >= 3: -- self.replHint = f"devCtrl.CommissionIP({repr(args[1])}, {int(args[2])}, {nodeid})" -- self.devCtrl.CommissionIP(args[1], int(args[2]), nodeid) -- elif args[0] == "-ble" and len(args) >= 3: -- self.replHint = f"devCtrl.ConnectBLE({int(args[1])}, {int(args[2])}, {nodeid})" -- self.devCtrl.ConnectBLE(int(args[1]), int(args[2]), nodeid) -- elif args[0] in ['-qr', '-code'] and len(args) >= 2: -- if len(args) == 3: -- nodeid = int(args[2]) -- print("Parsing QR code {}".format(args[1])) -- -- setupPayload = None -- if args[0] == '-qr': -- setupPayload = SetupPayload().ParseQrCode(args[1]) -- elif args[0] == '-code': -- setupPayload = SetupPayload( -- ).ParseManualPairingCode(args[1]) -- -- if not int(setupPayload.attributes.get("RendezvousInformation", 0)): -- print("No rendezvous information provided, default to all.") -- setupPayload.attributes["RendezvousInformation"] = 0b111 -- setupPayload.Print() -- self.replHint = f"devCtrl.CommissionWithCode(setupPayload={repr(setupPayload)}, nodeid={nodeid})" -- self.ConnectFromSetupPayload(setupPayload, nodeid) -- else: -- print("Usage:") -- self.do_help("connect SetupPinCode") -- return -- print( -- "Device temporary node id (**this does not match spec**): {}".format(nodeid)) -- except exceptions.ChipStackException as ex: -- print(str(ex)) -- return -- -- def do_closesession(self, line): -- """ -- close-session -- -- Close any session associated with a given node ID. -- """ -- try: -- parser = argparse.ArgumentParser() -- parser.add_argument('nodeid', type=int, help='Peer node ID') -- args = parser.parse_args(shlex.split(line)) -- self.replHint = f"devCtrl.CloseSession({args.nodeid})" -- self.devCtrl.CloseSession(args.nodeid) -- except exceptions.ChipStackException as ex: -- print(str(ex)) -- except Exception: -- self.do_help("close-session") -- -- def do_resolve(self, line): -- """ -- resolve -- -- Resolve DNS-SD name corresponding with the given node ID and -- update address of the node in the device controller. -- """ -- try: -- args = shlex.split(line) -- if len(args) == 1: -- try: -- self.replHint = f"devCtrl.ResolveNode({int(args[0])});devCtrl.GetAddressAndPort({int(args[0])})" -- self.devCtrl.ResolveNode(int(args[0])) -- address = self.devCtrl.GetAddressAndPort(int(args[0])) -- address = "{}:{}".format( -- *address) if address else "unknown" -- print("Current address: " + address) -- except exceptions.ChipStackException as ex: -- print(str(ex)) -- else: -- self.do_help("resolve") -- except exceptions.ChipStackException as ex: -- print(str(ex)) -- return -- -- def wait_for_one_discovered_device(self): -- print("Waiting for device responses...") -- strlen = 100 -- addrStrStorage = ctypes.create_string_buffer(strlen) -- count = 0 -- maxWaitTime = 2 -- while (not self.devCtrl.GetIPForDiscoveredDevice(0, addrStrStorage, strlen) and count < maxWaitTime): -- time.sleep(0.2) -- count = count + 0.2 -- return count < maxWaitTime -- -- def wait_for_many_discovered_devices(self): -- # Discovery happens through mdns, which means we need to wait for responses to come back. -- # TODO(cecille): I suppose we could make this a command line arg. Or Add a callback when -- # x number of responses are received. For now, just 2 seconds. We can all wait that long. -- print("Waiting for device responses...") -- time.sleep(2) -- -- def do_discover(self, line): -- """ -- discover -qr qrcode -- discover -all -- discover -l long_discriminator -- discover -s short_discriminator -- discover -v vendor_id -- discover -t device_type -- discover -c -- -- discover command is used to discover available devices. -- """ -- try: -- arglist = shlex.split(line) -- if len(arglist) < 1: -- print("Usage:") -- self.do_help("discover") -- return -- parser = argparse.ArgumentParser() -- group = parser.add_mutually_exclusive_group() -- group.add_argument( -- '-all', help='discover all commissionable nodes and commissioners', action='store_true') -- group.add_argument( -- '-qr', help='discover commissionable nodes matching provided QR code', type=str) -- group.add_argument( -- '-l', help='discover commissionable nodes with given long discriminator', type=int) -- group.add_argument( -- '-s', help='discover commissionable nodes with given short discriminator', type=int) -- group.add_argument( -- '-v', help='discover commissionable nodes with given vendor ID', type=int) -- group.add_argument( -- '-t', help='discover commissionable nodes with given device type', type=int) -- group.add_argument( -- '-c', help='discover commissionable nodes in commissioning mode', action='store_true') -- args = parser.parse_args(arglist) -- if args.all: -- self.commissionableNodeCtrl.DiscoverCommissioners() -- self.wait_for_many_discovered_devices() -- self.commissionableNodeCtrl.PrintDiscoveredCommissioners() -- self.devCtrl.DiscoverAllCommissioning() -- self.wait_for_many_discovered_devices() -- elif args.qr is not None: -- setupPayload = SetupPayload().ParseQrCode(args.qr) -- longDiscriminator = ctypes.c_uint16( -- int(setupPayload.attributes['Discriminator'])) -- self.devCtrl.DiscoverCommissionableNodesLongDiscriminator( -- longDiscriminator) -- self.wait_for_one_discovered_device() -- elif args.l is not None: -- self.devCtrl.DiscoverCommissionableNodesLongDiscriminator( -- ctypes.c_uint16(args.l)) -- self.wait_for_one_discovered_device() -- elif args.s is not None: -- self.devCtrl.DiscoverCommissionableNodesShortDiscriminator( -- ctypes.c_uint16(args.s)) -- self.wait_for_one_discovered_device() -- elif args.v is not None: -- self.devCtrl.DiscoverCommissionableNodesVendor( -- ctypes.c_uint16(args.v)) -- self.wait_for_many_discovered_devices() -- elif args.t is not None: -- self.devCtrl.DiscoverCommissionableNodesDeviceType( -- ctypes.c_uint16(args.t)) -- self.wait_for_many_discovered_devices() -- elif args.c is not None: -- self.devCtrl.DiscoverCommissionableNodesCommissioningEnabled() -- self.wait_for_many_discovered_devices() -- else: -- self.do_help("discover") -- return -- self.devCtrl.PrintDiscoveredDevices() -- except exceptions.ChipStackException as ex: -- print('exception') -- print(str(ex)) -- return -- except Exception: -- self.do_help("discover") -- return -- -- def do_zcl(self, line): -- """ -- To send ZCL message to device: -- zcl [key=value]... -- To get a list of clusters: -- zcl ? -- To get a list of commands in cluster: -- zcl ? -- -- Send ZCL command to device nodeid -- """ -- try: -- args = shlex.split(line) -- all_commands = self.devCtrl.ZCLCommandList() -- if len(args) == 1 and args[0] == '?': -- print('\n'.join(all_commands.keys())) -- elif len(args) == 2 and args[0] == '?': -- if args[1] not in all_commands: -- raise exceptions.UnknownCluster(args[1]) -- for commands in all_commands.get(args[1]).items(): -- args = ", ".join(["{}: {}".format(argName, argType) -- for argName, argType in commands[1].items()]) -- print(commands[0]) -- if commands[1]: -- print(" ", args) -- else: -- print(" ") -- elif len(args) > 4: -- if args[0] not in all_commands: -- raise exceptions.UnknownCluster(args[0]) -- command = all_commands.get(args[0]).get(args[1], None) -- # When command takes no arguments, (not command) is True -- if command is None: -- raise exceptions.UnknownCommand(args[0], args[1]) -- req = eval(f"Clusters.{args[0]}.Commands.{args[1]}")(**FormatZCLArguments(args[5:], command)) -- self.replHint = f"await devCtrl.SendCommand({int(args[2])}, {int(args[3])}, Clusters.{repr(req)})" -- err, res = self.devCtrl.ZCLSend(args[0], args[1], int( -- args[2]), int(args[3]), int(args[4]), FormatZCLArguments(args[5:], command), blocking=True) -- if err != 0: -- print("Failed to receive command response: {}".format(res)) -- elif res is not None: -- print("Received command status response:") -- print(res) -- else: -- print("Success, no status code is attached with response.") -- else: -- self.do_help("zcl") -- except exceptions.ChipStackException as ex: -- print("An exception occurred during process ZCL command:") -- print(str(ex)) -- except Exception as ex: -- print("An exception occurred during processing input:") -- traceback.print_exc() -- print(str(ex)) -- -- def do_zclread(self, line): -- """ -- To read ZCL attribute: -- zclread -- """ -- try: -- args = shlex.split(line) -- all_attrs = self.devCtrl.ZCLAttributeList() -- if len(args) == 1 and args[0] == '?': -- print('\n'.join(all_attrs.keys())) -- elif len(args) == 2 and args[0] == '?': -- if args[1] not in all_attrs: -- raise exceptions.UnknownCluster(args[1]) -- print('\n'.join(all_attrs.get(args[1]).keys())) -- elif len(args) == 5: -- if args[0] not in all_attrs: -- raise exceptions.UnknownCluster(args[0]) -- self.replHint = (f"await devCtrl.ReadAttribute({int(args[2])}, [({int(args[3])}, " -- f"Clusters.{args[0]}.Attributes.{args[1]})])") -- res = self.devCtrl.ZCLReadAttribute(args[0], args[1], int( -- args[2]), int(args[3]), int(args[4])) -- if res is not None: -- print(repr(res)) -- else: -- self.do_help("zclread") -- except exceptions.ChipStackException as ex: -- print("An exception occurred during reading ZCL attribute:") -- print(str(ex)) -- except Exception as ex: -- print("An exception occurred during processing input:") -- print(str(ex)) -- -- def do_zclwrite(self, line): -- """ -- To write ZCL attribute: -- zclwrite -- """ -- try: -- args = shlex.split(line) -- all_attrs = self.devCtrl.ZCLAttributeList() -- if len(args) == 1 and args[0] == '?': -- print('\n'.join(all_attrs.keys())) -- elif len(args) == 2 and args[0] == '?': -- if args[1] not in all_attrs: -- raise exceptions.UnknownCluster(args[1]) -- cluster_attrs = all_attrs.get(args[1], {}) -- print('\n'.join(["{}: {}".format(key, cluster_attrs[key]["type"]) -- for key in cluster_attrs.keys() if cluster_attrs[key].get("writable", False)])) -- elif len(args) == 6: -- if args[0] not in all_attrs: -- raise exceptions.UnknownCluster(args[0]) -- attribute_type = all_attrs.get(args[0], {}).get( -- args[1], {}).get("type", None) -- self.replHint = ( -- f"await devCtrl.WriteAttribute({int(args[2])}, [({int(args[3])}, " -- f"Clusters.{args[0]}.Attributes.{args[1]}(value={repr(ParseValueWithType(args[5], attribute_type))}))])") -- res = self.devCtrl.ZCLWriteAttribute(args[0], args[1], int( -- args[2]), int(args[3]), int(args[4]), ParseValueWithType(args[5], attribute_type)) -- print(repr(res)) -- else: -- self.do_help("zclwrite") -- except exceptions.ChipStackException as ex: -- print("An exception occurred during writing ZCL attribute:") -- print(str(ex)) -- except Exception as ex: -- print("An exception occurred during processing input:") -- print(str(ex)) -- -- def do_zclsubscribe(self, line): -- """ -- To subscribe ZCL attribute reporting: -- zclsubscribe -- -- To shut down a subscription: -- zclsubscribe -shutdown -- """ -- try: -- args = shlex.split(line) -- all_attrs = self.devCtrl.ZCLAttributeList() -- if len(args) == 1 and args[0] == '?': -- print('\n'.join(all_attrs.keys())) -- elif len(args) == 2 and args[0] == '?': -- if args[1] not in all_attrs: -- raise exceptions.UnknownCluster(args[1]) -- cluster_attrs = all_attrs.get(args[1], {}) -- print('\n'.join([key for key in cluster_attrs.keys( -- ) if cluster_attrs[key].get("reportable", False)])) -- elif len(args) == 6: -- if args[0] not in all_attrs: -- raise exceptions.UnknownCluster(args[0]) -- res = self.devCtrl.ZCLSubscribeAttribute(args[0], args[1], int( -- args[2]), int(args[3]), int(args[4]), int(args[5])) -- self.replHint = (f"sub = await devCtrl.ReadAttribute({int(args[2])}, [({int(args[3])}, " -- f"Clusters.{args[0]}.Attributes.{args[1]})], reportInterval=({int(args[4])}, {int(args[5])}))") -- print(res.GetAllValues()) -- print(f"Subscription Established: {res}") -- elif len(args) == 2 and args[0] == '-shutdown': -- subscriptionId = int(args[1], base=0) -- self.replHint = "You can call sub.Shutdown() (sub is the return value of ReadAttribute() called before)" -- self.devCtrl.ZCLShutdownSubscription(subscriptionId) -- else: -- self.do_help("zclsubscribe") -- except exceptions.ChipStackException as ex: -- print("An exception occurred during configuring reporting of ZCL attribute:") -- print(str(ex)) -- except Exception as ex: -- print("An exception occurred during processing input:") -- print(str(ex)) -- -- def do_setpairingwificredential(self, line): -- """ -- set-pairing-wifi-credential ssid credentials -- """ -- try: -- args = shlex.split(line) -- if len(args) < 2: -- print("Usage:") -- self.do_help("set-pairing-wifi-credential") -- return -- self.devCtrl.SetWiFiCredentials( -- args[0], args[1]) -- self.replHint = f"devCtrl.SetWiFiCredentials({repr(args[0])}, {repr(args[1])})" -- except Exception as ex: -- print(str(ex)) -- return -- -- def do_setpairingthreadcredential(self, line): -- """ -- set-pairing-thread-credential threadOperationalDataset -- """ -- try: -- args = shlex.split(line) -- if len(args) < 1: -- print("Usage:") -- self.do_help("set-pairing-thread-credential") -- return -- self.replHint = f"devCtrl.SetThreadOperationalDataset(bytes.fromhex({repr(args[0])}))" -- self.devCtrl.SetThreadOperationalDataset(bytes.fromhex(args[0])) -- except Exception as ex: -- print(str(ex)) -- return -- -- def do_opencommissioningwindow(self, line): -- """ -- open-commissioning-window [options] -- -- Options: -- -t Timeout (in seconds) -- -o Option [TokenWithRandomPIN = 1, TokenWithProvidedPIN = 2] -- -d Discriminator Value -- -i Iteration -- -- This command is used by a current Administrator to instruct a Node to go into commissioning mode -- """ -- try: -- arglist = shlex.split(line) -- -- if len(arglist) <= 1: -- print("Usage:") -- self.do_help("open-commissioning-window") -- return -- parser = argparse.ArgumentParser() -- parser.add_argument( -- "-t", type=int, default=0, dest='timeout') -- parser.add_argument( -- "-o", type=int, default=1, dest='option') -- parser.add_argument( -- "-i", type=int, default=0, dest='iteration') -- parser.add_argument( -- "-d", type=int, default=0, dest='discriminator') -- args = parser.parse_args(arglist[1:]) -- -- if args.option < 1 or args.option > 2: -- print("Invalid option specified!") -- raise ValueError("Invalid option specified") -- -- self.replHint = (f"devCtrl.OpenCommissioningWindow(nodeid={int(arglist[0])}, timeout={args.timeout}, " -- f"iteration={args.iteration}, discriminator={args.discriminator}, option={args.option})") -- -- self.devCtrl.OpenCommissioningWindow( -- int(arglist[0]), args.timeout, args.iteration, args.discriminator, args.option) -- -- except exceptions.ChipStackException as ex: -- print(str(ex)) -- return -- except Exception: -- self.do_help("open-commissioning-window") -- return -- -- def do_getfabricid(self, line): -- """ -- get-fabricid -- -- Read the current Compressed Fabric Id of the controller device, return 0 if not available. -- """ -- try: -- args = shlex.split(line) -- -- if (len(args) > 0): -- print("Unexpected argument: " + args[1]) -- return -- -- compressed_fabricid = self.devCtrl.GetCompressedFabricId() -- raw_fabricid = self.devCtrl.fabricId -- -- self.replHint = "devCtrl.GetCompressedFabricId(), devCtrl.fabricId" -- except exceptions.ChipStackException as ex: -- print("An exception occurred during reading FabricID:") -- print(str(ex)) -- return -- -- print("Get fabric ID complete") -- -- print("Raw Fabric ID: 0x{:016x}".format(raw_fabricid) -- + " (" + str(raw_fabricid) + ")") -- -- print("Compressed Fabric ID: 0x{:016x}".format(compressed_fabricid) -- + " (" + str(compressed_fabricid) + ")") -- -- def do_history(self, line): -- """ -- history -- -- Show previously executed commands. -- """ -- -- try: -- import readline -- -- h = readline.get_current_history_length() -- for n in range(1, h + 1): -- print(readline.get_history_item(n)) -- except ImportError: -- pass -- -- def do_h(self, line): -- self.do_history(line) -- -- def do_exit(self, line): -- return True -- -- def do_quit(self, line): -- return True -- -- def do_q(self, line): -- return True -- -- def do_EOF(self, line): -- print() -- return True -- -- def emptyline(self): -- pass -- -- --def main(): -- optParser = OptionParser() -- optParser.add_option( -- "-r", -- "--rendezvous-addr", -- action="store", -- dest="rendezvousAddr", -- help="Device rendezvous address", -- metavar="", -- ) -- optParser.add_option( -- "-n", -- "--controller-nodeid", -- action="store", -- dest="controllerNodeId", -- default=1, -- type='int', -- help="Controller node ID", -- metavar="", -- ) -- -- if sys.platform.startswith("linux"): -- optParser.add_option( -- "-b", -- "--bluetooth-adapter", -- action="store", -- dest="bluetoothAdapter", -- default="hci0", -- type="str", -- help="Controller bluetooth adapter ID, use --no-ble to disable bluetooth functions.", -- metavar="", -- ) -- optParser.add_option( -- "--no-ble", -- action="store_true", -- dest="disableBluetooth", -- help="Disable bluetooth, calling BLE related feature with this flag results in undefined behavior.", -- ) -- (options, remainingArgs) = optParser.parse_args(sys.argv[1:]) -- -- if len(remainingArgs) != 0: -- print("Unexpected argument: %s" % remainingArgs[0]) -- sys.exit(-1) -- -- adapterId = None -- if sys.platform.startswith("linux"): -- if options.disableBluetooth: -- adapterId = None -- elif not options.bluetoothAdapter.startswith("hci"): -- print( -- "Invalid bluetooth adapter: {}, adapter name looks like hci0, hci1 etc.") -- sys.exit(-1) -- else: -- try: -- adapterId = int(options.bluetoothAdapter[3:]) -- except ValueError: -- print( -- "Invalid bluetooth adapter: {}, adapter name looks like hci0, hci1 etc.") -- sys.exit(-1) -- native.Init(bluetoothAdapter=adapterId) -- try: -- devMgrCmd = DeviceMgrCmd(rendezvousAddr=options.rendezvousAddr, -- controllerNodeId=options.controllerNodeId, bluetoothAdapter=adapterId) -- except Exception as ex: -- print(ex) -- print("Failed to bringup CHIPDeviceController CLI") -- sys.exit(1) -- -- print("Chip Device Controller Shell") -- if options.rendezvousAddr: -- print("Rendezvous address set to %s" % options.rendezvousAddr) -- -- # Adapter ID will always be 0 -- if adapterId != 0: -- print("Bluetooth adapter set to hci{}".format(adapterId)) -- print() -- -- try: -- devMgrCmd.cmdloop() -- except KeyboardInterrupt: -- print("\nQuitting") -- -- sys.exit(0) -- -- --if __name__ == "__main__": -- print(""" -- chip-device-ctrl will be deprecated and will be removed in the future. Please try chip-repl, which provides a lot of features. -- -- - Multi-fabric support, -- - Better complex type support for sending commands, -- - Native command highlight, -- - Parallel commands with asyncio, -- - Writing complex logic inline. -- -- You can still use chip-device-ctrl as usual for now, and you will learn how to do the same thing in chip-repl. -- -- Feel free to file an issue if some features are not supported by chip-repl yet. -- """) -- main() -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 4d14a42f18..1d1627c46d 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -50,12 +50,9 @@ from . import discovery - from .clusters import Attribute as ClusterAttribute - from .clusters import ClusterObjects as ClusterObjects - from .clusters import Command as ClusterCommand --from .clusters import Objects as GeneratedObjects - from .clusters.CHIPClusters import ChipClusters - from .crypto import p256keypair --from .exceptions import UnknownAttribute, UnknownCommand --from .interaction_model import InteractionModelError, SessionParameters, SessionParametersStruct --from .interaction_model import delegate as im -+from .interaction_model import SessionParameters, SessionParametersStruct - from .native import PyChipError - - __all__ = ["ChipDeviceController", "CommissioningParameters"] -@@ -1464,83 +1461,6 @@ class ChipDeviceControllerBase(): - else: - return res.events - -- def ZCLSend(self, cluster, command, nodeid, endpoint, groupid, args, blocking=False): -- ''' Wrapper over SendCommand that catches the exceptions -- Returns a tuple of (errorCode, CommandResponse) -- ''' -- self.CheckIsActive() -- -- req = None -- try: -- req = eval( -- f"GeneratedObjects.{cluster}.Commands.{command}")(**args) -- except BaseException: -- raise UnknownCommand(cluster, command) -- try: -- res = asyncio.run(self.SendCommand(nodeid, endpoint, req)) -- logging.debug(f"CommandResponse {res}") -- return (0, res) -- except InteractionModelError as ex: -- return (int(ex.status), None) -- -- def ZCLReadAttribute(self, cluster, attribute, nodeid, endpoint, groupid, blocking=True): -- ''' Wrapper over ReadAttribute for a single attribute -- Returns an AttributeReadResult -- ''' -- self.CheckIsActive() -- -- clusterType = getattr(GeneratedObjects, cluster) -- -- try: -- attributeType = eval( -- f"GeneratedObjects.{cluster}.Attributes.{attribute}") -- except BaseException: -- raise UnknownAttribute(cluster, attribute) -- -- result = asyncio.run(self.ReadAttribute( -- nodeid, [(endpoint, attributeType)])) -- path = ClusterAttribute.AttributePath.from_attribute( -- EndpointId=endpoint, Attribute=attributeType) -- return im.AttributeReadResult(path=im.AttributePath(nodeId=nodeid, endpointId=path.EndpointId, clusterId=path.ClusterId, attributeId=path.AttributeId), -- status=0, value=result[endpoint][clusterType][attributeType], dataVersion=result[endpoint][clusterType][ClusterAttribute.DataVersion]) -- -- def ZCLWriteAttribute(self, cluster: str, attribute: str, nodeid, endpoint, groupid, value, dataVersion=0, blocking=True): -- ''' Wrapper over WriteAttribute for a single attribute -- return PyChipError -- ''' -- req = None -- try: -- req = eval( -- f"GeneratedObjects.{cluster}.Attributes.{attribute}")(value) -- except BaseException: -- raise UnknownAttribute(cluster, attribute) -- -- return asyncio.run(self.WriteAttribute(nodeid, [(endpoint, req, dataVersion)])) -- -- def ZCLSubscribeAttribute(self, cluster, attribute, nodeid, endpoint, minInterval, maxInterval, blocking=True, -- keepSubscriptions=False, autoResubscribe=True): -- ''' Wrapper over ReadAttribute for a single attribute -- Returns a SubscriptionTransaction. See ReadAttribute for more information. -- ''' -- self.CheckIsActive() -- -- req = None -- try: -- req = eval(f"GeneratedObjects.{cluster}.Attributes.{attribute}") -- except BaseException: -- raise UnknownAttribute(cluster, attribute) -- return asyncio.run(self.ReadAttribute(nodeid, [(endpoint, req)], None, False, reportInterval=(minInterval, maxInterval), -- keepSubscriptions=keepSubscriptions, autoResubscribe=autoResubscribe)) -- -- def ZCLCommandList(self): -- self.CheckIsActive() -- return self._Cluster.ListClusterCommands() -- -- def ZCLAttributeList(self): -- self.CheckIsActive() -- -- return self._Cluster.ListClusterAttributes() -- - def SetBlockingCB(self, blockingCB): - self.CheckIsActive() - -diff --git a/src/controller/python/chip/ChipReplStartup.py b/src/controller/python/chip/ChipReplStartup.py -index a49638cb99..b75c77efcd 100644 ---- a/src/controller/python/chip/ChipReplStartup.py -+++ b/src/controller/python/chip/ChipReplStartup.py -@@ -94,6 +94,8 @@ def main(): - "-d", "--debug", help="Set default logging level to debug.", action="store_true") - parser.add_argument( - "-t", "--trust-store", help="Path to the PAA trust store.", action="store", default="./credentials/development/paa-root-certs") -+ parser.add_argument( -+ "-b", "--ble-adapter", help="Set the Bluetooth adapter index.", type=int, default=None) - args = parser.parse_args() - - if not os.path.exists(args.trust_store): -@@ -128,7 +130,7 @@ or run `os.chdir` to the root of your CHIP repository checkout. - # nothing we can do ... things will NOT work - return - -- chip.native.Init() -+ chip.native.Init(bluetoothAdapter=args.ble_adapter) - - global certificateAuthorityManager - global chipStack -diff --git a/src/pybindings/pycontroller/build-chip-wheel.py b/src/pybindings/pycontroller/build-chip-wheel.py -index a61b591d5c..61bdf373e8 100644 ---- a/src/pybindings/pycontroller/build-chip-wheel.py -+++ b/src/pybindings/pycontroller/build-chip-wheel.py -@@ -60,7 +60,6 @@ packageName = args.package_name - chipPackageVer = args.build_number - - installScripts = [ -- # InstalledScriptInfo('chip-device-ctrl.py'), - # InstalledScriptInfo('chip-repl.py'), - ] - --- -2.45.2 - diff --git a/0013-Python-Remove-obsolete-callback-handling-33665.patch b/0013-Python-Remove-obsolete-callback-handling-33665.patch deleted file mode 100644 index 87acd99..0000000 --- a/0013-Python-Remove-obsolete-callback-handling-33665.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 3297c2f2240ac4b3dcf740a2604e1f22a42799d5 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 30 May 2024 22:41:35 +0200 -Subject: [PATCH] [Python] Remove obsolete callback handling (#33665) - -The Call() function currently still has some callback handling code -the completeEvent and callbackRes variables. These are only used when -callbacks are in play, like pychip_DeviceController_Commission or -pychip_DeviceController_OpenCommissioningWindow. When calling these -functions CallAsyncWithCompleteCallback() needs to be used (and is -beeing used in all cases). - -In practice, on single threaded applications this is not a problem. -However, when calling the SDK from multiple threads, then another Call() -Might accidentally release a call to CallAsyncWithCompleteCallback() -early. ---- - src/controller/python/chip/ChipStack.py | 22 +--------------------- - 1 file changed, 1 insertion(+), 21 deletions(-) - -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index 35f9e24ef4..3a167bb6bc 100644 ---- a/src/controller/python/chip/ChipStack.py -+++ b/src/controller/python/chip/ChipStack.py -@@ -32,8 +32,7 @@ import logging - import os - import sys - import time --from ctypes import (CFUNCTYPE, POINTER, Structure, c_bool, c_char_p, c_int64, c_uint8, c_uint16, c_uint32, c_ulong, c_void_p, -- py_object, pythonapi) -+from ctypes import CFUNCTYPE, Structure, c_bool, c_char_p, c_int64, c_uint8, c_uint16, c_uint32, c_void_p, py_object, pythonapi - from threading import Condition, Event, Lock - - import chip.native -@@ -194,9 +193,6 @@ class AsyncioCallableHandle: - 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)) - _LogMessageFunct = CFUNCTYPE( - None, c_int64, c_int64, c_char_p, c_uint8, c_char_p) - _ChipThreadTaskRunnerFunct = CFUNCTYPE(None, py_object) -@@ -272,21 +268,11 @@ class ChipStack(object): - self.logger.addHandler(logHandler) - self.logger.setLevel(logging.DEBUG) - -- def HandleComplete(appState, reqState): -- self.callbackRes = True -- self.completeEvent.set() -- -- def HandleError(appState, reqState, err, devStatusPtr): -- self.callbackRes = self.ErrorToException(err, devStatusPtr) -- self.completeEvent.set() -- - @_ChipThreadTaskRunnerFunct - def HandleChipThreadRun(callback): - callback() - - self.cbHandleChipThreadRun = HandleChipThreadRun -- self.cbHandleComplete = _CompleteFunct(HandleComplete) -- self.cbHandleError = _ErrorFunct(HandleError) - # set by other modules(BLE) that require service by thread while thread blocks. - self.blockingCB = None - -@@ -389,15 +375,9 @@ class ChipStack(object): - This function is a wrapper of PostTaskOnChipThread, which includes some handling of application specific logics. - Calling this function on CHIP on CHIP mainloop thread will cause deadlock. - ''' -- # throw error if op in progress -- self.callbackRes = None -- self.completeEvent.clear() - # TODO: Lock probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. - with self.networkLock: - res = self.PostTaskOnChipThread(callFunct).Wait(timeoutMs) -- self.completeEvent.set() -- if res == 0 and self.callbackRes is not None: -- return self.callbackRes - return res - - async def CallAsync(self, callFunct, timeoutMs: int = None): --- -2.45.2 - diff --git a/0014-Python-Add-automation-level-to-log-defines-33670.patch b/0014-Python-Add-automation-level-to-log-defines-33670.patch deleted file mode 100644 index d1693e6..0000000 --- a/0014-Python-Add-automation-level-to-log-defines-33670.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 235286b7c98474afc9434a3eec0c07157b4c3679 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Fri, 31 May 2024 15:24:42 +0200 -Subject: [PATCH] [Python] Add "automation" level to log defines (#33670) - -So far the automation log level was missing. Add it to the log level -defines in the logging module. - -While at it, also rename to LOG_CATEGORY (instead of ERROR_CATEGORY) -and remove duplicated log level definitions in ChipStack. ---- - src/controller/python/chip/ChipStack.py | 16 ++++------------ - src/controller/python/chip/logging/__init__.py | 17 +++++++++-------- - 2 files changed, 13 insertions(+), 20 deletions(-) - -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index 3a167bb6bc..beeaedd6ae 100644 ---- a/src/controller/python/chip/ChipStack.py -+++ b/src/controller/python/chip/ChipStack.py -@@ -36,6 +36,7 @@ from ctypes import CFUNCTYPE, Structure, c_bool, c_char_p, c_int64, c_uint8, c_u - from threading import Condition, Event, Lock - - import chip.native -+from chip.logging import LOG_CATEGORY_AUTOMATION, LOG_CATEGORY_DETAIL, LOG_CATEGORY_ERROR, LOG_CATEGORY_PROGRESS - from chip.native import PyChipError - - from .ChipUtility import ChipUtility -@@ -78,23 +79,14 @@ class DeviceStatusStruct(Structure): - class LogCategory(object): - """Debug logging categories used by chip.""" - -- # NOTE: These values must correspond to those used in the chip C++ code. -- Disabled = 0 -- Error = 1 -- Progress = 2 -- Detail = 3 -- Retain = 4 -- - @staticmethod - def categoryToLogLevel(cat): -- if cat == LogCategory.Error: -+ if cat == LOG_CATEGORY_ERROR: - return logging.ERROR -- elif cat == LogCategory.Progress: -+ elif cat == LOG_CATEGORY_PROGRESS: - return logging.INFO -- elif cat == LogCategory.Detail: -+ elif cat in (LOG_CATEGORY_DETAIL, LOG_CATEGORY_AUTOMATION): - return logging.DEBUG -- elif cat == LogCategory.Retain: -- return logging.CRITICAL - else: - return logging.NOTSET - -diff --git a/src/controller/python/chip/logging/__init__.py b/src/controller/python/chip/logging/__init__.py -index 047d3f4f8e..aca671997d 100644 ---- a/src/controller/python/chip/logging/__init__.py -+++ b/src/controller/python/chip/logging/__init__.py -@@ -19,11 +19,12 @@ import logging - from chip.logging.library_handle import _GetLoggingLibraryHandle - from chip.logging.types import LogRedirectCallback_t - --# Defines match support/logging/Constants.h (LogCategory enum) --ERROR_CATEGORY_NONE = 0 --ERROR_CATEGORY_ERROR = 1 --ERROR_CATEGORY_PROGRESS = 2 --ERROR_CATEGORY_DETAIL = 3 -+# Defines match src/lib/support/logging/Constants.h (LogCategory enum) -+LOG_CATEGORY_NONE = 0 -+LOG_CATEGORY_ERROR = 1 -+LOG_CATEGORY_PROGRESS = 2 -+LOG_CATEGORY_DETAIL = 3 -+LOG_CATEGORY_AUTOMATION = 4 - - - @LogRedirectCallback_t -@@ -34,11 +35,11 @@ def _RedirectToPythonLogging(category, module, message): - - logger = logging.getLogger('chip.native.%s' % module) - -- if category == ERROR_CATEGORY_ERROR: -+ if category == LOG_CATEGORY_ERROR: - logger.error("%s", message) -- elif category == ERROR_CATEGORY_PROGRESS: -+ elif category == LOG_CATEGORY_PROGRESS: - logger.info("%s", message) -- elif category == ERROR_CATEGORY_DETAIL: -+ elif category in (LOG_CATEGORY_DETAIL, LOG_CATEGORY_AUTOMATION): - logger.debug("%s", message) - else: - # All logs are expected to have some reasonable category. This treats --- -2.45.2 - diff --git a/0015-Python-Remove-obsolete-logging-callbacks-33718.patch b/0015-Python-Remove-obsolete-logging-callbacks-33718.patch deleted file mode 100644 index bd5ce9c..0000000 --- a/0015-Python-Remove-obsolete-logging-callbacks-33718.patch +++ /dev/null @@ -1,256 +0,0 @@ -From 4111dc126f04bce71a0f712c59cc7294c2fcad5d Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Tue, 4 Jun 2024 07:14:58 +0200 -Subject: [PATCH] [Python] Remove obsolete logging callbacks (#33718) - -Since #5024 there is a new logging callback working. The old code has -partially been removed in #4690, but never completely. Drop the old -logging code for good. ---- - .../ChipDeviceController-ScriptBinding.cpp | 12 -- - src/controller/python/chip/ChipStack.py | 148 +----------------- - 2 files changed, 2 insertions(+), 158 deletions(-) - -diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -index a55d3865bd..728fd5801f 100644 ---- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp -+++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -@@ -212,7 +212,6 @@ PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::Devic - - const char * pychip_Stack_ErrorToString(ChipError::StorageType err); - const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode); --void pychip_Stack_SetLogFunct(LogMessageFunct logFunct); - - PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, - chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback); -@@ -863,17 +862,6 @@ uint64_t pychip_GetCommandSenderHandle(chip::DeviceProxy * device) - return 0; - } - --void pychip_Stack_SetLogFunct(LogMessageFunct logFunct) --{ -- // TODO: determine if log redirection is supposed to be functioning in CHIP -- // -- // Background: original log baseline supported 'redirect logs to this -- // function' however CHIP does not currently provide this. -- // -- // Ideally log redirection should work so that python code can do things -- // like using the log module. --} -- - PyChipError pychip_DeviceController_PostTaskOnChipThread(ChipThreadTaskRunnerFunct callback, void * pythonContext) - { - if (callback == nullptr || pythonContext == nullptr) -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index beeaedd6ae..06afff3ef3 100644 ---- a/src/controller/python/chip/ChipStack.py -+++ b/src/controller/python/chip/ChipStack.py -@@ -28,15 +28,11 @@ from __future__ import absolute_import, print_function - - import asyncio - import builtins --import logging - import os --import sys --import time --from ctypes import CFUNCTYPE, Structure, c_bool, c_char_p, c_int64, c_uint8, c_uint16, c_uint32, c_void_p, py_object, pythonapi -+from ctypes import CFUNCTYPE, Structure, c_bool, c_char_p, c_uint16, c_uint32, c_void_p, py_object, pythonapi - from threading import Condition, Event, Lock - - import chip.native --from chip.logging import LOG_CATEGORY_AUTOMATION, LOG_CATEGORY_DETAIL, LOG_CATEGORY_ERROR, LOG_CATEGORY_PROGRESS - from chip.native import PyChipError - - from .ChipUtility import ChipUtility -@@ -76,51 +72,6 @@ class DeviceStatusStruct(Structure): - ] - - --class LogCategory(object): -- """Debug logging categories used by chip.""" -- -- @staticmethod -- def categoryToLogLevel(cat): -- if cat == LOG_CATEGORY_ERROR: -- return logging.ERROR -- elif cat == LOG_CATEGORY_PROGRESS: -- return logging.INFO -- elif cat in (LOG_CATEGORY_DETAIL, LOG_CATEGORY_AUTOMATION): -- return logging.DEBUG -- else: -- return logging.NOTSET -- -- --class ChipLogFormatter(logging.Formatter): -- """A custom logging.Formatter for logging chip library messages.""" -- -- def __init__( -- self, -- datefmt=None, -- logModulePrefix=False, -- logLevel=False, -- logTimestamp=False, -- logMSecs=True, -- ): -- fmt = "%(message)s" -- if logModulePrefix: -- fmt = "CHIP:%(chip-module)s: " + fmt -- if logLevel: -- fmt = "%(levelname)s:" + fmt -- if datefmt is not None or logTimestamp: -- fmt = "%(asctime)s " + fmt -- super(ChipLogFormatter, self).__init__(fmt=fmt, datefmt=datefmt) -- self.logMSecs = logMSecs -- -- def formatTime(self, record, datefmt=None): -- if datefmt is None: -- timestampStr = time.strftime("%Y-%m-%d %H:%M:%S%z") -- if self.logMSecs: -- timestampUS = record.__dict__.get("timestamp-usec", 0) -- timestampStr = "%s.%03ld" % (timestampStr, timestampUS / 1000) -- return timestampStr -- -- - class AsyncCallableHandle: - def __init__(self, callback): - self._callback = callback -@@ -185,15 +136,12 @@ class AsyncioCallableHandle: - pythonapi.Py_DecRef(py_object(self)) - - --_LogMessageFunct = CFUNCTYPE( -- None, c_int64, c_int64, c_char_p, c_uint8, c_char_p) - _ChipThreadTaskRunnerFunct = CFUNCTYPE(None, py_object) - - - @_singleton - class ChipStack(object): -- def __init__(self, persistentStoragePath: str, installDefaultLogHandler=True, -- bluetoothAdapter=None, enableServerInteractions=True): -+ def __init__(self, persistentStoragePath: str, enableServerInteractions=True): - builtins.enableDebugMode = False - - # TODO: Probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. -@@ -206,8 +154,6 @@ class ChipStack(object): - self.callbackRes = None - self.commissioningEventRes = None - self.openCommissioningWindowPincode = {} -- self._activeLogFunct = None -- self.addModulePrefixToLogMessage = True - self._enableServerInteractions = enableServerInteractions - - # -@@ -216,50 +162,6 @@ class ChipStack(object): - # - self._loadLib() - -- # Arrange to log output from the chip library to a python logger object with the -- # name 'chip.ChipStack'. If desired, applications can override this behavior by -- # setting self.logger to a different python logger object, or by calling setLogFunct() -- # with their own logging function. -- self.logger = logging.getLogger(__name__) -- self.setLogFunct(self.defaultLogFunct) -- -- # Determine if there are already handlers installed for the logger. Python 3.5+ -- # has a method for this; on older versions the check has to be done manually. -- if hasattr(self.logger, "hasHandlers"): -- hasHandlers = self.logger.hasHandlers() -- else: -- hasHandlers = False -- logger = self.logger -- while logger is not None: -- if len(logger.handlers) > 0: -- hasHandlers = True -- break -- if not logger.propagate: -- break -- logger = logger.parent -- -- # If a logging handler has not already been initialized for 'chip.ChipStack', -- # or any one of its parent loggers, automatically configure a handler to log to -- # stdout. This maintains compatibility with a number of applications which expect -- # chip log output to go to stdout by default. -- # -- # This behavior can be overridden in a variety of ways: -- # - Initialize a different log handler before ChipStack is initialized. -- # - Pass installDefaultLogHandler=False when initializing ChipStack. -- # - Replace the StreamHandler on self.logger with a different handler object. -- # - Set a different Formatter object on the existing StreamHandler object. -- # - Reconfigure the existing ChipLogFormatter object. -- # - Configure chip to call an application-specific logging function by -- # calling self.setLogFunct(). -- # - Call self.setLogFunct(None), which will configure the chip library -- # to log directly to stdout, bypassing python altogether. -- # -- if installDefaultLogHandler and not hasHandlers: -- logHandler = logging.StreamHandler(stream=sys.stdout) -- logHandler.setFormatter(ChipLogFormatter()) -- self.logger.addHandler(logHandler) -- self.logger.setLevel(logging.DEBUG) -- - @_ChipThreadTaskRunnerFunct - def HandleChipThreadRun(callback): - callback() -@@ -292,49 +194,6 @@ class ChipStack(object): - def enableServerInteractions(self): - return self._enableServerInteractions - -- @property -- def defaultLogFunct(self): -- """Returns a python callable which, when called, logs a message to the python logger object -- currently associated with the ChipStack object. -- The returned function is suitable for passing to the setLogFunct() method.""" -- -- def logFunct(timestamp, timestampUSec, moduleName, logCat, message): -- moduleName = ChipUtility.CStringToString(moduleName) -- message = ChipUtility.CStringToString(message) -- if self.addModulePrefixToLogMessage: -- message = "CHIP:%s: %s" % (moduleName, message) -- logLevel = LogCategory.categoryToLogLevel(logCat) -- msgAttrs = { -- "chip-module": moduleName, -- "timestamp": timestamp, -- "timestamp-usec": timestampUSec, -- } -- self.logger.log(logLevel, message, extra=msgAttrs) -- -- return logFunct -- -- def setLogFunct(self, logFunct): -- """Set the function used by the chip library to log messages. -- The supplied object must be a python callable that accepts the following -- arguments: -- timestamp (integer) -- timestampUS (integer) -- module name (encoded UTF-8 string) -- log category (integer) -- message (encoded UTF-8 string) -- Specifying None configures the chip library to log directly to stdout.""" -- if logFunct is None: -- logFunct = 0 -- if not isinstance(logFunct, _LogMessageFunct): -- logFunct = _LogMessageFunct(logFunct) -- # TODO: Lock probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. -- with self.networkLock: -- # NOTE: ChipStack must hold a reference to the CFUNCTYPE object while it is -- # set. Otherwise it may get garbage collected, and logging calls from the -- # chip library will fail. -- self._activeLogFunct = logFunct -- self._ChipStackLib.pychip_Stack_SetLogFunct(logFunct) -- - def Shutdown(self): - # - # Terminate Matter thread and shutdown the stack. -@@ -484,9 +343,6 @@ class ChipStack(object): - self._ChipStackLib.pychip_Stack_StatusReportToString.restype = c_char_p - self._ChipStackLib.pychip_Stack_ErrorToString.argtypes = [c_uint32] - self._ChipStackLib.pychip_Stack_ErrorToString.restype = c_char_p -- self._ChipStackLib.pychip_Stack_SetLogFunct.argtypes = [ -- _LogMessageFunct] -- self._ChipStackLib.pychip_Stack_SetLogFunct.restype = PyChipError - - self._ChipStackLib.pychip_DeviceController_PostTaskOnChipThread.argtypes = [ - _ChipThreadTaskRunnerFunct, py_object] --- -2.45.2 - diff --git a/0016-Python-Drop-network-lock-33720.patch b/0016-Python-Drop-network-lock-33720.patch deleted file mode 100644 index e9179d2..0000000 --- a/0016-Python-Drop-network-lock-33720.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 155d4471de2c988b5089325bb0e9eb59e93d3c0d Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Wed, 5 Jun 2024 16:06:15 +0200 -Subject: [PATCH] [Python] Drop network lock (#33720) - -The network lock is not needed in the Python controller, as all calls -to the SDK are made by posting to the Matter SDK event loop through -ScheduleWork(), hence are guaranteed to be serialized. - -From how I understand ScheduleWork() works, it pushes the work to the -event loop through PostEvent() which at least on POSIX is using the -thread safe device queue (see GenericPlatformManagerImpl_POSIX.cpp). ---- - src/controller/python/chip/ChipStack.py | 12 ++---------- - 1 file changed, 2 insertions(+), 10 deletions(-) - -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index 06afff3ef3..b47c463982 100644 ---- a/src/controller/python/chip/ChipStack.py -+++ b/src/controller/python/chip/ChipStack.py -@@ -144,8 +144,6 @@ class ChipStack(object): - def __init__(self, persistentStoragePath: str, enableServerInteractions=True): - builtins.enableDebugMode = False - -- # TODO: Probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. -- self.networkLock = Lock() - self.completeEvent = Event() - self.commissioningCompleteEvent = Event() - self._ChipStackLib = None -@@ -212,7 +210,6 @@ class ChipStack(object): - # #20437 tracks consolidating these. - # - self._ChipStackLib.pychip_CommonStackShutdown() -- self.networkLock = None - self.completeEvent = None - self._ChipStackLib = None - self._chipDLLPath = None -@@ -226,10 +223,7 @@ class ChipStack(object): - This function is a wrapper of PostTaskOnChipThread, which includes some handling of application specific logics. - Calling this function on CHIP on CHIP mainloop thread will cause deadlock. - ''' -- # TODO: Lock probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. -- with self.networkLock: -- res = self.PostTaskOnChipThread(callFunct).Wait(timeoutMs) -- return res -+ return self.PostTaskOnChipThread(callFunct).Wait(timeoutMs) - - async def CallAsync(self, callFunct, timeoutMs: int = None): - '''Run a Python function on CHIP stack, and wait for the response. -@@ -256,9 +250,7 @@ class ChipStack(object): - # throw error if op in progress - self.callbackRes = None - self.completeEvent.clear() -- # TODO: Lock probably no longer necessary, see https://github.com/project-chip/connectedhomeip/issues/33321. -- with self.networkLock: -- res = self.PostTaskOnChipThread(callFunct).Wait() -+ res = self.PostTaskOnChipThread(callFunct).Wait() - - if not res.is_success: - self.completeEvent.set() --- -2.45.2 - diff --git a/0017-Python-Remove-Python-Bluetooth-and-ChipStack-event-l.patch b/0017-Python-Remove-Python-Bluetooth-and-ChipStack-event-l.patch deleted file mode 100644 index 2401bcf..0000000 --- a/0017-Python-Remove-Python-Bluetooth-and-ChipStack-event-l.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 1edc2acd9f8faeb481e79154c065c2b85eb87fc8 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 6 Jun 2024 17:11:34 +0200 -Subject: [PATCH] [Python] Remove Python Bluetooth and ChipStack event loop - integration (#33775) - -The Python Bluetooth implementation for Linux (`BluezManager` in -ChipBluezMgr.py) and macOS (`CoreBluetoothManager` in -ChipCoreBluetoothMgr.py) integrate with ChipStack to pump their event -loops on long running operations such as commissioning (through -`CallAsyncWithCompleteCallback()`). From what I can tell, the Python -Bluetooth stack is only used for some mbed integration tests. -Specifically through `scan_chip_ble_devices()` -in src/test_driver/mbed/integration_tests/common/utils.py. This -operation doesn't need the event loop integration. - -So as a first step, this PR simply breaks this tie and removes the -event loop integration with the Device ChipStack/ChipDeviceController. ---- - src/controller/python/chip/ChipBluezMgr.py | 1 - - src/controller/python/chip/ChipCoreBluetoothMgr.py | 2 -- - src/controller/python/chip/ChipDeviceCtrl.py | 5 ----- - src/controller/python/chip/ChipStack.py | 8 +------- - 4 files changed, 1 insertion(+), 15 deletions(-) - -diff --git a/src/controller/python/chip/ChipBluezMgr.py b/src/controller/python/chip/ChipBluezMgr.py -index e480750b60..bacf383710 100644 ---- a/src/controller/python/chip/ChipBluezMgr.py -+++ b/src/controller/python/chip/ChipBluezMgr.py -@@ -807,7 +807,6 @@ class BluezManager(ChipBleBase): - self.rx = None - self.setInputHook(self.readlineCB) - self.devMgr = devMgr -- self.devMgr.SetBlockingCB(self.devMgrCB) - - def __del__(self): - self.disconnect() -diff --git a/src/controller/python/chip/ChipCoreBluetoothMgr.py b/src/controller/python/chip/ChipCoreBluetoothMgr.py -index 3f792a5a4d..4a65f1e237 100644 ---- a/src/controller/python/chip/ChipCoreBluetoothMgr.py -+++ b/src/controller/python/chip/ChipCoreBluetoothMgr.py -@@ -184,8 +184,6 @@ class CoreBluetoothManager(ChipBleBase): - def __del__(self): - self.disconnect() - self.setInputHook(self.orig_input_hook) -- self.devCtrl.SetBlockingCB(None) -- self.devCtrl.SetBleEventCB(None) - - def devMgrCB(self): - """A callback used by ChipDeviceCtrl.py to drive the OSX runloop while the -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 1d1627c46d..acbbc88b3e 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -1461,11 +1461,6 @@ class ChipDeviceControllerBase(): - else: - return res.events - -- def SetBlockingCB(self, blockingCB): -- self.CheckIsActive() -- -- self._ChipStack.blockingCB = blockingCB -- - def SetIpk(self, ipk: bytes): - self._ChipStack.Call( - lambda: self._dmLib.pychip_DeviceController_SetIpk(self.devCtrl, ipk, len(ipk)) -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index b47c463982..5fd0601ba2 100644 ---- a/src/controller/python/chip/ChipStack.py -+++ b/src/controller/python/chip/ChipStack.py -@@ -165,8 +165,6 @@ class ChipStack(object): - callback() - - self.cbHandleChipThreadRun = HandleChipThreadRun -- # set by other modules(BLE) that require service by thread while thread blocks. -- self.blockingCB = None - - # - # Storage has to be initialized BEFORE initializing the stack, since the latter -@@ -255,11 +253,7 @@ class ChipStack(object): - if not res.is_success: - self.completeEvent.set() - raise res.to_exception() -- while not self.completeEvent.isSet(): -- if self.blockingCB: -- self.blockingCB() -- -- self.completeEvent.wait(0.05) -+ self.completeEvent.wait() - if isinstance(self.callbackRes, ChipStackException): - raise self.callbackRes - return self.callbackRes --- -2.45.2 - diff --git a/0018-Python-Add-TriggerResubscribeIfScheduled-to-Subscrip.patch b/0018-Python-Add-TriggerResubscribeIfScheduled-to-Subscrip.patch deleted file mode 100644 index bd413bd..0000000 --- a/0018-Python-Add-TriggerResubscribeIfScheduled-to-Subscrip.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 29e6e6b1c0a6925abc33510297f6f49470bcf016 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Fri, 7 Jun 2024 15:50:34 +0200 -Subject: [PATCH] [Python] Add TriggerResubscribeIfScheduled to - SubscriptionTransaction (#33774) - -Add TriggerResubscribeIfScheduled to SubscriptionTransaction. If the -ReadClient currently has a resubscription attempt scheduled, This -function allows to trigger that attempt immediately. This is useful -when the server side is up and communicating, and it's a good time to -try to resubscribe. ---- - src/controller/python/chip/clusters/Attribute.py | 7 +++++++ - src/controller/python/chip/clusters/attribute.cpp | 6 ++++++ - 2 files changed, 13 insertions(+) - -diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py -index 51389e19a1..838936e83b 100644 ---- a/src/controller/python/chip/clusters/Attribute.py -+++ b/src/controller/python/chip/clusters/Attribute.py -@@ -478,6 +478,13 @@ class SubscriptionTransaction: - lambda: handle.pychip_ReadClient_OverrideLivenessTimeout(self._readTransaction._pReadClient, timeoutMs) - ) - -+ async def TriggerResubscribeIfScheduled(self, reason: str): -+ handle = chip.native.GetLibraryHandle() -+ await builtins.chipStack.CallAsync( -+ lambda: handle.pychip_ReadClient_TriggerResubscribeIfScheduled( -+ self._readTransaction._pReadClient, reason.encode("utf-8")) -+ ) -+ - def GetReportingIntervalsSeconds(self) -> Tuple[int, int]: - ''' - Retrieve the reporting intervals associated with an active subscription. -diff --git a/src/controller/python/chip/clusters/attribute.cpp b/src/controller/python/chip/clusters/attribute.cpp -index b73b4a49b4..7c5b2c906a 100644 ---- a/src/controller/python/chip/clusters/attribute.cpp -+++ b/src/controller/python/chip/clusters/attribute.cpp -@@ -464,6 +464,12 @@ void pychip_ReadClient_OverrideLivenessTimeout(ReadClient * pReadClient, uint32_ - pReadClient->OverrideLivenessTimeout(System::Clock::Milliseconds32(livenessTimeoutMs)); - } - -+void pychip_ReadClient_TriggerResubscribeIfScheduled(ReadClient * pReadClient, const char * reason) -+{ -+ VerifyOrDie(pReadClient != nullptr); -+ pReadClient->TriggerResubscribeIfScheduled(reason); -+} -+ - PyChipError pychip_ReadClient_GetReportingIntervals(ReadClient * pReadClient, uint16_t * minIntervalSec, uint16_t * maxIntervalSec) - { - VerifyOrDie(pReadClient != nullptr); --- -2.45.2 - diff --git a/0019-Python-Drop-deprecated-discovery-APIs-33882.patch b/0019-Python-Drop-deprecated-discovery-APIs-33882.patch deleted file mode 100644 index a5e86ef..0000000 --- a/0019-Python-Drop-deprecated-discovery-APIs-33882.patch +++ /dev/null @@ -1,401 +0,0 @@ -From 08d23c879cd47a9ba2d0675ea04a0abb041a8d9e Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Wed, 12 Jun 2024 19:49:51 +0200 -Subject: [PATCH] [Python] Drop deprecated discovery APIs (#33882) - -This change removes the deprecated discovery APIs from the Python -controller. The APIs that are removed are: -- DiscoverCommissionableNodesLongDiscriminator -- DiscoverCommissionableNodesShortDiscriminator -- DiscoverCommissionableNodesVendor -- DiscoverCommissionableNodesDeviceType -- DiscoverCommissionableNodesCommissioningEnabled -- PrintDiscoveredDevices -- DiscoverAllCommissioning - -All these APIs can be replaced with the DiscoverCommissionableNodes -and the appropriate filter flags. ---- - docs/testing/ChipDeviceCtrlAPI.md | 77 --------------- - .../python/ChipDeviceController-Discovery.cpp | 59 ----------- - .../ChipDeviceController-ScriptBinding.cpp | 53 ---------- - src/controller/python/chip/ChipDeviceCtrl.py | 99 ------------------- - 4 files changed, 288 deletions(-) - -diff --git a/docs/testing/ChipDeviceCtrlAPI.md b/docs/testing/ChipDeviceCtrlAPI.md -index 944c787d64..b1111aada5 100644 ---- a/docs/testing/ChipDeviceCtrlAPI.md -+++ b/docs/testing/ChipDeviceCtrlAPI.md -@@ -10,13 +10,6 @@ - - [ShutdownAll](#shutdownall) - - [ExpireSessions](#expiresessions) - - [DiscoverCommissionableNodes](#discovercommissionablenodes) -- - [DiscoverCommissionableNodesLongDiscriminator](#discovercommissionablenodeslongdiscriminator) -- - [DiscoverCommissionableNodesShortDiscriminator](#discovercommissionablenodesshortdiscriminator) -- - [DiscoverCommissionableNodesVendor](#discovercommissionablenodesvendor) -- - [DiscoverCommissionableNodesDeviceType](#discovercommissionablenodesdevicetype) -- - [DiscoverCommissionableNodesCommissioningEnabled](#discovercommissionablenodescommissioningenabled) -- - [PrintDiscoveredDevices](#printdiscovereddevices) -- - [DiscoverAllCommissioning](#discoverallcommissioning) - - [OpenCommissioningWindow](#opencommissioningwindow) - - [GetFabricIdInternal](#getfabricidinternal) - - [GetNodeIdInternal](#getnodeidinternal) -@@ -174,76 +167,6 @@ This function will always return a list of CommissionableDevice. When - stopOnFirst is set, this function will return when at least one device is - discovered or on timeout. - -- -- --#### DiscoverCommissionableNodesLongDiscriminator -- --```python --def DiscoverCommissionableNodesLongDiscriminator(long_discriminator) --``` -- --Deprecated, use DiscoverCommissionableNodes -- -- -- --#### DiscoverCommissionableNodesShortDiscriminator -- --```python --def DiscoverCommissionableNodesShortDiscriminator(short_discriminator) --``` -- --Deprecated, use DiscoverCommissionableNodes -- -- -- --#### DiscoverCommissionableNodesVendor -- --```python --def DiscoverCommissionableNodesVendor(vendor) --``` -- --Deprecated, use DiscoverCommissionableNodes -- -- -- --#### DiscoverCommissionableNodesDeviceType -- --```python --def DiscoverCommissionableNodesDeviceType(device_type) --``` -- --Deprecated, use DiscoverCommissionableNodes -- -- -- --#### DiscoverCommissionableNodesCommissioningEnabled -- --```python --def DiscoverCommissionableNodesCommissioningEnabled() --``` -- --Deprecated, use DiscoverCommissionableNodes -- -- -- --#### PrintDiscoveredDevices -- --```python --def PrintDiscoveredDevices() --``` -- --Deprecated, use GetCommissionableNodes -- -- -- --#### DiscoverAllCommissioning -- --```python --def DiscoverAllCommissioning() --``` -- --Deprecated, use DiscoverCommissionableNodes -- - - - #### OpenCommissioningWindow -diff --git a/src/controller/python/ChipDeviceController-Discovery.cpp b/src/controller/python/ChipDeviceController-Discovery.cpp -index 7b502140a3..5690475567 100644 ---- a/src/controller/python/ChipDeviceController-Discovery.cpp -+++ b/src/controller/python/ChipDeviceController-Discovery.cpp -@@ -164,65 +164,6 @@ void pychip_DeviceController_IterateDiscoveredCommissionableNodes(Controller::De - } - } - --void pychip_DeviceController_PrintDiscoveredDevices(Controller::DeviceCommissioner * devCtrl) --{ -- for (int i = 0; i < devCtrl->GetMaxCommissionableNodesSupported(); ++i) -- { -- const Dnssd::DiscoveredNodeData * dnsSdInfo = devCtrl->GetDiscoveredDevice(i); -- if (dnsSdInfo == nullptr) -- { -- continue; -- } -- char rotatingId[Dnssd::kMaxRotatingIdLen * 2 + 1] = ""; -- Encoding::BytesToUppercaseHexString(dnsSdInfo->commissionData.rotatingId, dnsSdInfo->commissionData.rotatingIdLen, -- rotatingId, sizeof(rotatingId)); -- -- ChipLogProgress(Discovery, "Commissionable Node %d", i); -- ChipLogProgress(Discovery, "\tInstance name:\t\t%s", dnsSdInfo->commissionData.instanceName); -- ChipLogProgress(Discovery, "\tHost name:\t\t%s", dnsSdInfo->resolutionData.hostName); -- ChipLogProgress(Discovery, "\tPort:\t\t\t%u", dnsSdInfo->resolutionData.port); -- ChipLogProgress(Discovery, "\tLong discriminator:\t%u", dnsSdInfo->commissionData.longDiscriminator); -- ChipLogProgress(Discovery, "\tVendor ID:\t\t%u", dnsSdInfo->commissionData.vendorId); -- ChipLogProgress(Discovery, "\tProduct ID:\t\t%u", dnsSdInfo->commissionData.productId); -- ChipLogProgress(Discovery, "\tCommissioning Mode\t%u", dnsSdInfo->commissionData.commissioningMode); -- ChipLogProgress(Discovery, "\tDevice Type\t\t%u", dnsSdInfo->commissionData.deviceType); -- ChipLogProgress(Discovery, "\tDevice Name\t\t%s", dnsSdInfo->commissionData.deviceName); -- ChipLogProgress(Discovery, "\tRotating Id\t\t%s", rotatingId); -- ChipLogProgress(Discovery, "\tPairing Instruction\t%s", dnsSdInfo->commissionData.pairingInstruction); -- ChipLogProgress(Discovery, "\tPairing Hint\t\t%u", dnsSdInfo->commissionData.pairingHint); -- if (dnsSdInfo->resolutionData.GetMrpRetryIntervalIdle().HasValue()) -- { -- ChipLogProgress(Discovery, "\tMrp Interval idle\t%u", -- dnsSdInfo->resolutionData.GetMrpRetryIntervalIdle().Value().count()); -- } -- else -- { -- ChipLogProgress(Discovery, "\tMrp Interval idle\tNot present"); -- } -- if (dnsSdInfo->resolutionData.GetMrpRetryIntervalActive().HasValue()) -- { -- ChipLogProgress(Discovery, "\tMrp Interval active\t%u", -- dnsSdInfo->resolutionData.GetMrpRetryIntervalActive().Value().count()); -- } -- else -- { -- ChipLogProgress(Discovery, "\tMrp Interval active\tNot present"); -- } -- ChipLogProgress(Discovery, "\tSupports TCP\t\t%d", dnsSdInfo->resolutionData.supportsTcp); -- if (dnsSdInfo->resolutionData.isICDOperatingAsLIT.HasValue()) -- { -- ChipLogProgress(Discovery, "\tICD is operating as a\t%s", -- dnsSdInfo->resolutionData.isICDOperatingAsLIT.Value() ? "LIT" : "SIT"); -- } -- for (unsigned j = 0; j < dnsSdInfo->resolutionData.numIPs; ++j) -- { -- char buf[Inet::IPAddress::kMaxStringLength]; -- dnsSdInfo->resolutionData.ipAddress[j].ToString(buf); -- ChipLogProgress(Discovery, "\tAddress %d:\t\t%s", j, buf); -- } -- } --} -- - bool pychip_DeviceController_GetIPForDiscoveredDevice(Controller::DeviceCommissioner * devCtrl, int idx, char * addrStr, - uint32_t len) - { -diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -index 728fd5801f..28bea5384a 100644 ---- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp -+++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -@@ -154,18 +154,6 @@ PyChipError pychip_DeviceController_EstablishPASESession(chip::Controller::Devic - chip::NodeId nodeid); - PyChipError pychip_DeviceController_Commission(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid); - --PyChipError pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator(chip::Controller::DeviceCommissioner * devCtrl, -- uint16_t long_discriminator); --PyChipError pychip_DeviceController_DiscoverAllCommissionableNodes(chip::Controller::DeviceCommissioner * devCtrl); -- --PyChipError pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator(chip::Controller::DeviceCommissioner * devCtrl, -- uint16_t short_discriminator); --PyChipError pychip_DeviceController_DiscoverCommissionableNodesVendor(chip::Controller::DeviceCommissioner * devCtrl, -- uint16_t vendor); --PyChipError pychip_DeviceController_DiscoverCommissionableNodesDeviceType(chip::Controller::DeviceCommissioner * devCtrl, -- uint16_t device_type); --PyChipError pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled(chip::Controller::DeviceCommissioner * devCtrl); -- - PyChipError pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, - chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, - uint64_t nodeId, uint32_t setupPasscode, const uint8_t filterType, -@@ -178,7 +166,6 @@ PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::De - chip::NodeId nodeid, uint16_t timeout, uint32_t iteration, - uint16_t discriminator, uint8_t optionInt); - --void pychip_DeviceController_PrintDiscoveredDevices(chip::Controller::DeviceCommissioner * devCtrl); - bool pychip_DeviceController_GetIPForDiscoveredDevice(chip::Controller::DeviceCommissioner * devCtrl, int idx, char * addrStr, - uint32_t len); - -@@ -617,46 +604,6 @@ PyChipError pychip_DeviceController_Commission(chip::Controller::DeviceCommissio - return ToPyChipError(devCtrl->Commission(nodeid, params)); - } - --PyChipError pychip_DeviceController_DiscoverAllCommissionableNodes(chip::Controller::DeviceCommissioner * devCtrl) --{ -- Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone, static_cast(0)); -- return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); --} -- --PyChipError pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator(chip::Controller::DeviceCommissioner * devCtrl, -- uint16_t long_discriminator) --{ -- Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kLongDiscriminator, long_discriminator); -- return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); --} -- --PyChipError pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator(chip::Controller::DeviceCommissioner * devCtrl, -- uint16_t short_discriminator) --{ -- Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kShortDiscriminator, short_discriminator); -- return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); --} -- --PyChipError pychip_DeviceController_DiscoverCommissionableNodesVendor(chip::Controller::DeviceCommissioner * devCtrl, -- uint16_t vendor) --{ -- Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kVendorId, vendor); -- return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); --} -- --PyChipError pychip_DeviceController_DiscoverCommissionableNodesDeviceType(chip::Controller::DeviceCommissioner * devCtrl, -- uint16_t device_type) --{ -- Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kDeviceType, device_type); -- return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); --} -- --PyChipError pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled(chip::Controller::DeviceCommissioner * devCtrl) --{ -- Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kCommissioningMode); -- return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); --} -- - PyChipError pychip_ScriptDevicePairingDelegate_SetOpenWindowCompleteCallback( - chip::Controller::ScriptDevicePairingDelegate * pairingDelegate, - chip::Controller::DevicePairingDelegate_OnWindowOpenCompleteFunct callback) -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index acbbc88b3e..c2c502ce3a 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -620,66 +620,6 @@ class ChipDeviceControllerBase(): - - return self.GetDiscoveredDevices() - -- def DiscoverCommissionableNodesLongDiscriminator(self, long_discriminator): -- ''' Deprecated, use DiscoverCommissionableNodes -- ''' -- self.CheckIsActive() -- -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator( -- self.devCtrl, long_discriminator) -- ).raise_on_error() -- -- def DiscoverCommissionableNodesShortDiscriminator(self, short_discriminator): -- ''' Deprecated, use DiscoverCommissionableNodes -- ''' -- self.CheckIsActive() -- -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator( -- self.devCtrl, short_discriminator) -- ).raise_on_error() -- -- def DiscoverCommissionableNodesVendor(self, vendor): -- ''' Deprecated, use DiscoverCommissionableNodes -- ''' -- self.CheckIsActive() -- -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesVendor( -- self.devCtrl, vendor) -- ).raise_on_error() -- -- def DiscoverCommissionableNodesDeviceType(self, device_type): -- ''' Deprecated, use DiscoverCommissionableNodes -- ''' -- self.CheckIsActive() -- -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesDeviceType( -- self.devCtrl, device_type) -- ).raise_on_error() -- -- def DiscoverCommissionableNodesCommissioningEnabled(self): -- ''' Deprecated, use DiscoverCommissionableNodes -- ''' -- self.CheckIsActive() -- -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled( -- self.devCtrl) -- ).raise_on_error() -- -- def PrintDiscoveredDevices(self): -- ''' Deprecated, use GetCommissionableNodes -- ''' -- self.CheckIsActive() -- -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_PrintDiscoveredDevices( -- self.devCtrl) -- ) -- - def GetDiscoveredDevices(self): - def GetDevices(devCtrl): - devices = [] -@@ -704,16 +644,6 @@ class ChipDeviceControllerBase(): - self.devCtrl, idx, addrStr, length) - ) - -- def DiscoverAllCommissioning(self): -- ''' Deprecated, use DiscoverCommissionableNodes -- ''' -- self.CheckIsActive() -- -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_DiscoverAllCommissionableNodes( -- self.devCtrl) -- ).raise_on_error() -- - class CommissioningWindowPasscode(enum.IntEnum): - kOriginalSetupCode = 0, - kTokenWithRandomPin = 1, -@@ -1534,26 +1464,6 @@ class ChipDeviceControllerBase(): - c_void_p] - self._dmLib.pychip_DeviceController_StopCommissionableDiscovery.restype = PyChipError - -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator.argtypes = [ -- c_void_p, c_uint16] -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator.restype = PyChipError -- -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator.argtypes = [ -- c_void_p, c_uint16] -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator.restype = PyChipError -- -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesVendor.argtypes = [ -- c_void_p, c_uint16] -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesVendor.restype = PyChipError -- -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesDeviceType.argtypes = [ -- c_void_p, c_uint16] -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesDeviceType.restype = PyChipError -- -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled.argtypes = [ -- c_void_p] -- self._dmLib.pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled.restype = PyChipError -- - self._dmLib.pychip_DeviceController_EstablishPASESessionIP.argtypes = [ - c_void_p, c_char_p, c_uint32, c_uint64, c_uint16] - self._dmLib.pychip_DeviceController_EstablishPASESessionIP.restype = PyChipError -@@ -1566,15 +1476,6 @@ class ChipDeviceControllerBase(): - c_void_p, c_char_p, c_uint64] - self._dmLib.pychip_DeviceController_EstablishPASESession.restype = PyChipError - -- self._dmLib.pychip_DeviceController_DiscoverAllCommissionableNodes.argtypes = [ -- c_void_p] -- self._dmLib.pychip_DeviceController_DiscoverAllCommissionableNodes.restype = PyChipError -- -- self._dmLib.pychip_DeviceController_PrintDiscoveredDevices.argtypes = [ -- c_void_p] -- self._dmLib.pychip_DeviceController_PrintDiscoveredDevices.argtypes = [ -- c_void_p, _ChipDeviceController_IterateDiscoveredCommissionableNodesFunct] -- - self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode.argtypes = [c_void_p] - self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode.restype = c_bool - --- -2.45.2 - diff --git a/0020-Remove-unnecessary-error-log-from-CurrentFabricRemov.patch b/0020-Remove-unnecessary-error-log-from-CurrentFabricRemov.patch deleted file mode 100644 index 5b88cac..0000000 --- a/0020-Remove-unnecessary-error-log-from-CurrentFabricRemov.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 3734bb27d8de27b957698b54d2a03b88619905fc Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 13 Jun 2024 16:13:56 +0200 -Subject: [PATCH] Remove unnecessary error log from CurrentFabricRemover - (#33896) - -* Remove unnecessary error log from CurrentFabricRemover - -Currently, when the CurrentFabricRemover is successful it logs the -following error with err log level: - -Remove Current Fabric Result : src/controller/CurrentFabricRemover.cpp:133: Success - -Get rid of the message if successful, it is confusing. - -* Add braces to if statement ---- - src/controller/CurrentFabricRemover.cpp | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/src/controller/CurrentFabricRemover.cpp b/src/controller/CurrentFabricRemover.cpp -index be89731b24..899860a028 100644 ---- a/src/controller/CurrentFabricRemover.cpp -+++ b/src/controller/CurrentFabricRemover.cpp -@@ -145,7 +145,10 @@ void CurrentFabricRemover::OnCommandFailure(void * context, CHIP_ERROR err) - - void CurrentFabricRemover::FinishRemoveCurrentFabric(void * context, CHIP_ERROR err) - { -- ChipLogError(Controller, "Remove Current Fabric Result : %" CHIP_ERROR_FORMAT, err.Format()); -+ if (err != CHIP_NO_ERROR) -+ { -+ ChipLogError(Controller, "Remove Current Fabric Failed : %" CHIP_ERROR_FORMAT, err.Format()); -+ } - auto * self = static_cast(context); - self->mNextStep = Step::kAcceptRemoveFabricStart; - if (self->mCurrentFabricRemoveCallback != nullptr) --- -2.45.2 - diff --git a/0021-Python-Use-thread-safe-futures-for-concurrent-operat.patch b/0021-Python-Use-thread-safe-futures-for-concurrent-operat.patch deleted file mode 100644 index 540b849..0000000 --- a/0021-Python-Use-thread-safe-futures-for-concurrent-operat.patch +++ /dev/null @@ -1,579 +0,0 @@ -From 0d035c3060a931a7205e64aafb48bf99c82b8301 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 13 Jun 2024 16:44:23 +0200 -Subject: [PATCH] [Python] Use thread-safe futures for concurrent operations - (#33891) - -* [Python] Use thread-safe futures for concurrent operations - -Instead of using quasi-global variables in the ChipStack singleton -use device controller local futures to store results from callbacks. -This has several advantages, namely: -- Avoid unnecessary shared state between device controllers -- Avoid unnecessary shared state between various operations within a - device controller (those who don't share callbacks could be called - from different threads now) -- Explicitly set Futures to None to detect spurious/unexpected callbacks -- Better code readability -- concurrent.futures are thread-safe -- Will make asyncio transition easier - -This change shouldn't change the external API. - -* [Python] Fix EstablishPASESession API compatibility - -* [Python] Make ConnectBLE behave as before ---- - src/controller/python/chip/ChipDeviceCtrl.py | 316 +++++++++++-------- - src/controller/python/chip/ChipStack.py | 27 +- - 2 files changed, 183 insertions(+), 160 deletions(-) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index c2c502ce3a..6805958420 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -30,6 +30,7 @@ from __future__ import absolute_import, annotations, print_function - - import asyncio - import builtins -+import concurrent.futures - import copy - import ctypes - import enum -@@ -138,15 +139,6 @@ def _singleton(cls): - return wrapper - - --class DCState(enum.IntEnum): -- NOT_INITIALIZED = 0 -- IDLE = 1 -- BLE_READY = 2 -- RENDEZVOUS_ONGOING = 3 -- RENDEZVOUS_CONNECTED = 4 -- COMMISSIONING = 5 -- -- - class CommissionableNode(discovery.CommissionableNode): - def SetDeviceController(self, devCtrl: 'ChipDeviceController'): - self._devCtrl = devCtrl -@@ -243,7 +235,6 @@ class ChipDeviceControllerBase(): - activeList = set() - - def __init__(self, name: str = ''): -- self.state = DCState.NOT_INITIALIZED - self.devCtrl = None - self._ChipStack = builtins.chipStack - self._dmLib = None -@@ -261,21 +252,26 @@ class ChipDeviceControllerBase(): - - self._Cluster = ChipClusters(builtins.chipStack) - self._Cluster.InitLib(self._dmLib) -+ self._commissioning_complete_future: typing.Optional[concurrent.futures.Future] = None -+ self._open_window_complete_future: typing.Optional[concurrent.futures.Future] = None -+ self._unpair_device_complete_future: typing.Optional[concurrent.futures.Future] = None -+ self._pase_establishment_complete_future: typing.Optional[concurrent.futures.Future] = None - - def _set_dev_ctrl(self, devCtrl, pairingDelegate): -- def HandleCommissioningComplete(nodeid, err): -+ def HandleCommissioningComplete(nodeId: int, err: PyChipError): - if err.is_success: - logging.info("Commissioning complete") - else: - logging.warning("Failed to commission: {}".format(err)) - -- self.state = DCState.IDLE -- self._ChipStack.callbackRes = err -- self._ChipStack.commissioningEventRes = err - if self._dmLib.pychip_TestCommissionerUsed(): -- self._ChipStack.commissioningEventRes = self._dmLib.pychip_GetCompletionError() -- self._ChipStack.commissioningCompleteEvent.set() -- self._ChipStack.completeEvent.set() -+ err = self._dmLib.pychip_GetCompletionError() -+ -+ if self._commissioning_complete_future is None: -+ logging.exception("HandleCommissioningComplete called unexpectedly") -+ return -+ -+ self._commissioning_complete_future.set_result(err) - - def HandleFabricCheck(nodeId): - self.fabricCheckNodeId = nodeId -@@ -284,13 +280,19 @@ class ChipDeviceControllerBase(): - setupQRCode: str, err: PyChipError) -> None: - if err.is_success: - logging.info("Open Commissioning Window complete setting nodeid {} pincode to {}".format(nodeid, setupPinCode)) -- self._ChipStack.openCommissioningWindowPincode[nodeid] = CommissioningParameters( -+ commissioningParameters = CommissioningParameters( - setupPinCode=setupPinCode, setupManualCode=setupManualCode.decode(), setupQRCode=setupQRCode.decode()) - else: - logging.warning("Failed to open commissioning window: {}".format(err)) - -- self._ChipStack.callbackRes = err -- self._ChipStack.completeEvent.set() -+ if self._open_window_complete_future is None: -+ logging.exception("HandleOpenWindowComplete called unexpectedly") -+ return -+ -+ if err.is_success: -+ self._open_window_complete_future.set_result(commissioningParameters) -+ else: -+ self._open_window_complete_future.set_exception(err.to_exception()) - - def HandleUnpairDeviceComplete(nodeid: int, err: PyChipError): - if err.is_success: -@@ -298,27 +300,33 @@ class ChipDeviceControllerBase(): - else: - logging.warning("Failed to unpair device: {}".format(err)) - -- self._ChipStack.callbackRes = err -- self._ChipStack.completeEvent.set() -+ if self._unpair_device_complete_future is None: -+ logging.exception("HandleUnpairDeviceComplete called unexpectedly") -+ return -+ -+ if err.is_success: -+ self._unpair_device_complete_future.set_result(None) -+ else: -+ self._unpair_device_complete_future.set_exception(err.to_exception()) - - def HandlePASEEstablishmentComplete(err: PyChipError): - if not err.is_success: - logging.warning("Failed to establish secure session to device: {}".format(err)) -- self._ChipStack.callbackRes = err.to_exception() - else: - logging.info("Established secure session with Device") - -- if self.state != DCState.COMMISSIONING: -- # During Commissioning, HandlePASEEstablishmentComplete will also be called, -- # in this case the async operation should be marked as finished by -- # HandleCommissioningComplete instead this function. -- self.state = DCState.IDLE -- self._ChipStack.completeEvent.set() -- else: -- # When commissioning, getting an error during key exhange -- # needs to unblock the entire commissioning flow. -+ if self._commissioning_complete_future is not None: -+ # During Commissioning, HandlePASEEstablishmentComplete will also be called. -+ # Only complete the future if PASE session establishment failed. - if not err.is_success: -- HandleCommissioningComplete(0, err) -+ self._commissioning_complete_future.set_result(err) -+ return -+ -+ if self._pase_establishment_complete_future is None: -+ logging.exception("HandlePASEEstablishmentComplete called unexpectedly") -+ return -+ -+ self._pase_establishment_complete_future.set_result(err) - - self.pairingDelegate = pairingDelegate - self.devCtrl = devCtrl -@@ -343,14 +351,12 @@ class ChipDeviceControllerBase(): - - self.cbHandleDeviceUnpairCompleteFunct = _DeviceUnpairingCompleteFunct(HandleUnpairDeviceComplete) - -- self.state = DCState.IDLE - self._isActive = True - # Validate FabricID/NodeID followed from NOC Chain - self._fabricId = self.GetFabricIdInternal() - self._nodeId = self.GetNodeIdInternal() - - def _finish_init(self): -- self.state = DCState.IDLE - self._isActive = True - - ChipDeviceController.activeList.add(self) -@@ -440,26 +446,35 @@ class ChipDeviceControllerBase(): - def ConnectBLE(self, discriminator, setupPinCode, nodeid) -> PyChipError: - self.CheckIsActive() - -- self._ChipStack.commissioningCompleteEvent.clear() -+ self._commissioning_complete_future = concurrent.futures.Future() - -- self.state = DCState.COMMISSIONING -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_ConnectBLE( -- self.devCtrl, discriminator, setupPinCode, nodeid) -- ).raise_on_error() -- if not self._ChipStack.commissioningCompleteEvent.isSet(): -- # Error 50 is a timeout -- return PyChipError(CHIP_ERROR_TIMEOUT) -- return self._ChipStack.commissioningEventRes -+ try: -+ self._enablePairingCompeleteCallback(True) -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_ConnectBLE( -+ self.devCtrl, discriminator, setupPinCode, nodeid) -+ ).raise_on_error() -+ -+ # TODO: Change return None. Only returning on success is not useful. -+ # but that is what the previous implementation did. -+ res = self._commissioning_complete_future.result() -+ res.raise_on_error() -+ return res -+ finally: -+ self._commissioning_complete_future = None - -- def UnpairDevice(self, nodeid: int): -+ def UnpairDevice(self, nodeid: int) -> None: - self.CheckIsActive() - -- return self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_UnpairDevice( -- self.devCtrl, nodeid, self.cbHandleDeviceUnpairCompleteFunct) -- ).raise_on_error() -+ self._unpair_device_complete_future = concurrent.futures.Future() -+ try: -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_UnpairDevice( -+ self.devCtrl, nodeid, self.cbHandleDeviceUnpairCompleteFunct) -+ ).raise_on_error() -+ self._unpair_device_complete_future.result() -+ finally: -+ self._unpair_device_complete_future = None - - def CloseBLEConnection(self): - self.CheckIsActive() -@@ -493,32 +508,62 @@ class ChipDeviceControllerBase(): - def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid: int): - self.CheckIsActive() - -- self.state = DCState.RENDEZVOUS_ONGOING -- self._enablePairingCompeleteCallback(True) -- return self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionBLE( -- self.devCtrl, setupPinCode, discriminator, nodeid) -- ) -+ self._pase_establishment_complete_future = concurrent.futures.Future() -+ try: -+ self._enablePairingCompeleteCallback(True) -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionBLE( -+ self.devCtrl, setupPinCode, discriminator, nodeid) -+ ).raise_on_error() -+ -+ # TODO: This is a bit funky, but what the API returned with the previous -+ # implementation. We should revisit this. -+ err = self._pase_establishment_complete_future.result() -+ if not err.is_success: -+ return err.to_exception() -+ return None -+ finally: -+ self._pase_establishment_complete_future = None - - def EstablishPASESessionIP(self, ipaddr: str, setupPinCode: int, nodeid: int, port: int = 0): - self.CheckIsActive() - -- self.state = DCState.RENDEZVOUS_ONGOING -- self._enablePairingCompeleteCallback(True) -- return self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionIP( -- self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid, port) -- ) -+ self._pase_establishment_complete_future = concurrent.futures.Future() -+ try: -+ self._enablePairingCompeleteCallback(True) -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionIP( -+ self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid, port) -+ ).raise_on_error() -+ -+ # TODO: This is a bit funky, but what the API returned with the previous -+ # implementation. We should revisit this. -+ err = self._pase_establishment_complete_future.result() -+ if not err.is_success: -+ return err.to_exception() -+ return None -+ finally: -+ self._pase_establishment_complete_future = None - - def EstablishPASESession(self, setUpCode: str, nodeid: int): - self.CheckIsActive() - -- self.state = DCState.RENDEZVOUS_ONGOING -- self._enablePairingCompeleteCallback(True) -- return self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_EstablishPASESession( -- self.devCtrl, setUpCode.encode("utf-8"), nodeid) -- ) -+ self._pase_establishment_complete_future = concurrent.futures.Future() -+ try: -+ self._enablePairingCompeleteCallback(True) -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_EstablishPASESession( -+ self.devCtrl, setUpCode.encode("utf-8"), nodeid) -+ ).raise_on_error() -+ -+ # TODO: This is a bit funky, but what the API returned with the previous -+ # implementation. We should revisit this. -+ err = self._pase_establishment_complete_future.result() -+ if not err.is_success: -+ return err.to_exception() -+ return None -+ finally: -+ self._pase_establishment_complete_future = None - - def GetTestCommissionerUsed(self): - return self._ChipStack.Call( -@@ -553,11 +598,6 @@ class ChipDeviceControllerBase(): - def CheckTestCommissionerPaseConnection(self, nodeid): - return self._dmLib.pychip_TestPaseConnection(nodeid) - -- def NOCChainCallback(self, nocChain): -- self._ChipStack.callbackRes = nocChain -- self._ChipStack.completeEvent.set() -- return -- - def ResolveNode(self, nodeid): - self.CheckIsActive() - -@@ -664,12 +704,16 @@ class ChipDeviceControllerBase(): - Returns CommissioningParameters - ''' - self.CheckIsActive() -- self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_OpenCommissioningWindow( -- self.devCtrl, self.pairingDelegate, nodeid, timeout, iteration, discriminator, option) -- ).raise_on_error() -- self._ChipStack.callbackRes.raise_on_error() -- return self._ChipStack.openCommissioningWindowPincode[nodeid] -+ self._open_window_complete_future = concurrent.futures.Future() -+ try: -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_OpenCommissioningWindow( -+ self.devCtrl, self.pairingDelegate, nodeid, timeout, iteration, discriminator, option) -+ ).raise_on_error() -+ -+ return self._open_window_complete_future.result() -+ finally: -+ self._open_window_complete_future = None - - def GetCompressedFabricId(self): - self.CheckIsActive() -@@ -1630,6 +1674,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - f"caIndex({fabricAdmin.caIndex:x})/fabricId(0x{fabricId:016X})/nodeId(0x{nodeId:016X})" - ) - -+ self._issue_node_chain_complete: typing.Optional[concurrent.futures.Future] = None - self._dmLib.pychip_DeviceController_SetIssueNOCChainCallbackPythonCallback(_IssueNOCChainCallbackPythonCallback) - - pairingDelegate = c_void_p(None) -@@ -1680,17 +1725,18 @@ class ChipDeviceController(ChipDeviceControllerBase): - bool: True if successful, False otherwise. - ''' - self.CheckIsActive() -- self._ChipStack.commissioningCompleteEvent.clear() -- self.state = DCState.COMMISSIONING - -- self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_Commission( -- self.devCtrl, nodeid) -- ) -- if not self._ChipStack.commissioningCompleteEvent.isSet(): -- # Error 50 is a timeout -- return PyChipError(CHIP_ERROR_TIMEOUT) -- return self._ChipStack.commissioningEventRes -+ self._commissioning_complete_future = concurrent.futures.Future() -+ -+ try: -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_Commission( -+ self.devCtrl, nodeid) -+ ).raise_on_error() -+ -+ return self._commissioning_complete_future.result() -+ finally: -+ self._commissioning_complete_future = None - - def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes) -> PyChipError: - ''' Commissions a Thread device over BLE -@@ -1788,25 +1834,21 @@ class ChipDeviceController(ChipDeviceControllerBase): - ''' - self.CheckIsActive() - -- # IP connection will run through full commissioning, so we need to wait -- # for the commissioning complete event, not just any callback. -- self.state = DCState.COMMISSIONING -- - # Convert numerical filters to string for passing down to binding. - if isinstance(filter, int): - filter = str(filter) - -- self._ChipStack.commissioningCompleteEvent.clear() -+ self._commissioning_complete_future = concurrent.futures.Future() -+ try: -+ self._enablePairingCompeleteCallback(True) -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_OnNetworkCommission( -+ self.devCtrl, self.pairingDelegate, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") + b"\x00" if filter is not None else None, discoveryTimeoutMsec) -+ ).raise_on_error() - -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_OnNetworkCommission( -- self.devCtrl, self.pairingDelegate, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") + b"\x00" if filter is not None else None, discoveryTimeoutMsec) -- ) -- if not self._ChipStack.commissioningCompleteEvent.isSet(): -- # Error 50 is a timeout -- return PyChipError(CHIP_ERROR_TIMEOUT) -- return self._ChipStack.commissioningEventRes -+ return self._commissioning_complete_future.result() -+ finally: -+ self._commissioning_complete_future = None - - def CommissionWithCode(self, setupPayload: str, nodeid: int, discoveryType: DiscoveryType = DiscoveryType.DISCOVERY_ALL) -> PyChipError: - ''' Commission with the given nodeid from the setupPayload. -@@ -1816,51 +1858,57 @@ class ChipDeviceController(ChipDeviceControllerBase): - - setupPayload = setupPayload.encode() + b'\0' - -- # IP connection will run through full commissioning, so we need to wait -- # for the commissioning complete event, not just any callback. -- self.state = DCState.COMMISSIONING -+ self._commissioning_complete_future = concurrent.futures.Future() - -- self._ChipStack.commissioningCompleteEvent.clear() -+ try: -+ self._enablePairingCompeleteCallback(True) -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_ConnectWithCode( -+ self.devCtrl, setupPayload, nodeid, discoveryType.value) -+ ).raise_on_error() - -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_ConnectWithCode( -- self.devCtrl, setupPayload, nodeid, discoveryType.value) -- ) -- if not self._ChipStack.commissioningCompleteEvent.isSet(): -- # Error 50 is a timeout -- return PyChipError(CHIP_ERROR_TIMEOUT) -- return self._ChipStack.commissioningEventRes -+ return self._commissioning_complete_future.result() -+ finally: -+ self._commissioning_complete_future = None - - def CommissionIP(self, ipaddr: str, setupPinCode: int, nodeid: int) -> PyChipError: - """ DEPRECATED, DO NOT USE! Use `CommissionOnNetwork` or `CommissionWithCode` """ - self.CheckIsActive() - -- # IP connection will run through full commissioning, so we need to wait -- # for the commissioning complete event, not just any callback. -- self.state = DCState.COMMISSIONING -+ self._commissioning_complete_future = concurrent.futures.Future() - -- self._ChipStack.commissioningCompleteEvent.clear() -+ try: -+ self._enablePairingCompeleteCallback(True) -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_ConnectIP( -+ self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid) -+ ).raise_on_error() - -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_ConnectIP( -- self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid) -- ) -- if not self._ChipStack.commissioningCompleteEvent.isSet(): -- # Error 50 is a timeout -- return PyChipError(CHIP_ERROR_TIMEOUT) -- return self._ChipStack.commissioningEventRes -+ return self._commissioning_complete_future.result() -+ finally: -+ self._commissioning_complete_future = None -+ -+ def NOCChainCallback(self, nocChain): -+ if self._issue_node_chain_complete is None: -+ logging.exception("NOCChainCallback while not expecting a callback") -+ return -+ self._issue_node_chain_complete.set_result(nocChain) -+ return - - def IssueNOCChain(self, csr: Clusters.OperationalCredentials.Commands.CSRResponse, nodeId: int): - """Issue an NOC chain using the associated OperationalCredentialsDelegate. - The NOC chain will be provided in TLV cert format.""" - self.CheckIsActive() - -- return self._ChipStack.CallAsyncWithCompleteCallback( -- lambda: self._dmLib.pychip_DeviceController_IssueNOCChain( -- self.devCtrl, py_object(self), csr.NOCSRElements, len(csr.NOCSRElements), nodeId) -- ) -+ self._issue_node_chain_complete = concurrent.futures.Future() -+ try: -+ self._ChipStack.Call( -+ lambda: self._dmLib.pychip_DeviceController_IssueNOCChain( -+ self.devCtrl, py_object(self), csr.NOCSRElements, len(csr.NOCSRElements), nodeId) -+ ).raise_on_error() -+ return self._issue_node_chain_complete.result() -+ finally: -+ self._issue_node_chain_complete = None - - - class BareChipDeviceController(ChipDeviceControllerBase): -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index 5fd0601ba2..4f19776664 100644 ---- a/src/controller/python/chip/ChipStack.py -+++ b/src/controller/python/chip/ChipStack.py -@@ -30,7 +30,7 @@ import asyncio - import builtins - import os - from ctypes import CFUNCTYPE, Structure, c_bool, c_char_p, c_uint16, c_uint32, c_void_p, py_object, pythonapi --from threading import Condition, Event, Lock -+from threading import Condition, Lock - - import chip.native - from chip.native import PyChipError -@@ -144,14 +144,9 @@ class ChipStack(object): - def __init__(self, persistentStoragePath: str, enableServerInteractions=True): - builtins.enableDebugMode = False - -- self.completeEvent = Event() -- self.commissioningCompleteEvent = Event() - self._ChipStackLib = None - self._chipDLLPath = None - self.devMgr = None -- self.callbackRes = None -- self.commissioningEventRes = None -- self.openCommissioningWindowPincode = {} - self._enableServerInteractions = enableServerInteractions - - # -@@ -212,7 +207,6 @@ class ChipStack(object): - self._ChipStackLib = None - self._chipDLLPath = None - self.devMgr = None -- self.callbackRes = None - - delattr(builtins, "chipStack") - -@@ -239,25 +233,6 @@ class ChipStack(object): - - return await asyncio.wait_for(callObj.future, timeoutMs / 1000 if timeoutMs else None) - -- def CallAsyncWithCompleteCallback(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. -- Calling this function on CHIP on CHIP mainloop thread will cause deadlock. -- Make sure to register the necessary callbacks which release the function by setting the completeEvent. -- ''' -- # throw error if op in progress -- self.callbackRes = None -- self.completeEvent.clear() -- res = self.PostTaskOnChipThread(callFunct).Wait() -- -- if not res.is_success: -- self.completeEvent.set() -- raise res.to_exception() -- self.completeEvent.wait() -- if isinstance(self.callbackRes, ChipStackException): -- raise self.callbackRes -- return self.callbackRes -- - def PostTaskOnChipThread(self, callFunct) -> AsyncCallableHandle: - '''Run a Python function on CHIP stack, and wait for the response. - This function will post a task on CHIP mainloop, and return an object with Wait() method for getting the result. --- -2.45.2 - diff --git a/0022-Avoid-errors-when-using-on-network-commissioning-338.patch b/0022-Avoid-errors-when-using-on-network-commissioning-338.patch deleted file mode 100644 index fdc9c79..0000000 --- a/0022-Avoid-errors-when-using-on-network-commissioning-338.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 69daf2756196568bd80cc3debe6c11cc6beaa222 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 13 Jun 2024 22:29:36 +0200 -Subject: [PATCH] Avoid errors when using on-network commissioning (#33880) - -When on network commissioning is used from Python we currently don't -stop discovery even though we already found a device. Future devices -found won't be processed, so it is fine to stop discovery of -commissionable nodes at this point. - -Also avoid "Unknown filter type; all matches will fail" errors in the -log: If the filter type is set to None, it wasn't the SetUpCodePairer -which set up the discovery. Avoid printing errors in this case. - -A potential alternative to this would be to skip notifying the -SetUpCodePairer in DeviceCommissioner::OnNodeDiscovered in this case. ---- - src/controller/SetUpCodePairer.cpp | 3 +++ - ...ipDeviceController-ScriptPairingDeviceDiscoveryDelegate.cpp | 3 +++ - 2 files changed, 6 insertions(+) - -diff --git a/src/controller/SetUpCodePairer.cpp b/src/controller/SetUpCodePairer.cpp -index db09d91fd7..3a8997703d 100644 ---- a/src/controller/SetUpCodePairer.cpp -+++ b/src/controller/SetUpCodePairer.cpp -@@ -363,6 +363,9 @@ bool SetUpCodePairer::NodeMatchesCurrentFilter(const Dnssd::DiscoveredNodeData & - case Dnssd::DiscoveryFilterType::kLongDiscriminator: - discriminatorMatches = (nodeData.commissionData.longDiscriminator == mCurrentFilter.code); - break; -+ case Dnssd::DiscoveryFilterType::kNone: -+ ChipLogDetail(Controller, "Filter type none; all matches will fail"); -+ return false; - default: - ChipLogError(Controller, "Unknown filter type; all matches will fail"); - return false; -diff --git a/src/controller/python/ChipDeviceController-ScriptPairingDeviceDiscoveryDelegate.cpp b/src/controller/python/ChipDeviceController-ScriptPairingDeviceDiscoveryDelegate.cpp -index 850ec208ac..d211ac9058 100644 ---- a/src/controller/python/ChipDeviceController-ScriptPairingDeviceDiscoveryDelegate.cpp -+++ b/src/controller/python/ChipDeviceController-ScriptPairingDeviceDiscoveryDelegate.cpp -@@ -32,6 +32,9 @@ void ScriptPairingDeviceDiscoveryDelegate::OnDiscoveredDevice(const Dnssd::Disco - nodeData.resolutionData.ipAddress[0].ToString(buf); - ChipLogProgress(chipTool, "Discovered Device: %s:%u", buf, port); - -+ // Stop active discovery. -+ mActiveDeviceCommissioner->StopCommissionableDiscovery(); -+ - // Cancel discovery timer. - chip::DeviceLayer::SystemLayer().CancelTimer(OnDiscoveredTimeout, this); - --- -2.45.2 - diff --git a/0023-Add-success-message-on-removing-current-fabric-33914.patch b/0023-Add-success-message-on-removing-current-fabric-33914.patch deleted file mode 100644 index 2ddb65b..0000000 --- a/0023-Add-success-message-on-removing-current-fabric-33914.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 988b3fbd5db017f49fc6dd0151a6906f4e47844e Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Fri, 14 Jun 2024 01:34:21 +0200 -Subject: [PATCH] Add success message on removing current fabric (#33914) - -Readd a message with progress-level when removing the current fabric -succeeded. ---- - src/controller/CurrentFabricRemover.cpp | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/src/controller/CurrentFabricRemover.cpp b/src/controller/CurrentFabricRemover.cpp -index 899860a028..167fea0bb3 100644 ---- a/src/controller/CurrentFabricRemover.cpp -+++ b/src/controller/CurrentFabricRemover.cpp -@@ -145,7 +145,11 @@ void CurrentFabricRemover::OnCommandFailure(void * context, CHIP_ERROR err) - - void CurrentFabricRemover::FinishRemoveCurrentFabric(void * context, CHIP_ERROR err) - { -- if (err != CHIP_NO_ERROR) -+ if (err == CHIP_NO_ERROR) -+ { -+ ChipLogProgress(Controller, "Remove Current Fabric succeeded."); -+ } -+ else - { - ChipLogError(Controller, "Remove Current Fabric Failed : %" CHIP_ERROR_FORMAT, err.Format()); - } --- -2.45.2 - diff --git a/0024-Python-Drop-unnecessary-null-termination-33915.patch b/0024-Python-Drop-unnecessary-null-termination-33915.patch deleted file mode 100644 index 887d221..0000000 --- a/0024-Python-Drop-unnecessary-null-termination-33915.patch +++ /dev/null @@ -1,54 +0,0 @@ -From e5ecc03b7ae65eda30357af92687b36c23bcf36d Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Fri, 14 Jun 2024 15:43:44 +0200 -Subject: [PATCH] [Python] Drop unnecessary null termination (#33915) - -The ctypes data type `c_char_p` takes care of null-terminating the byte -array provided to it. The additional null termination doesn't hurt in -practice, but it's unnecessary. ---- - src/controller/python/chip/ChipDeviceCtrl.py | 8 +++----- - 1 file changed, 3 insertions(+), 5 deletions(-) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 6805958420..b99d1db018 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -642,7 +642,7 @@ class ChipDeviceControllerBase(): - - self._ChipStack.Call( - lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes( -- self.devCtrl, int(filterType), str(filter).encode("utf-8") + b"\x00")).raise_on_error() -+ self.devCtrl, int(filterType), str(filter).encode("utf-8"))).raise_on_error() - - if timeoutSecond != 0: - if stopOnFirst: -@@ -1843,7 +1843,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - self._enablePairingCompeleteCallback(True) - self._ChipStack.Call( - lambda: self._dmLib.pychip_DeviceController_OnNetworkCommission( -- self.devCtrl, self.pairingDelegate, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") + b"\x00" if filter is not None else None, discoveryTimeoutMsec) -+ self.devCtrl, self.pairingDelegate, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") if filter is not None else None, discoveryTimeoutMsec) - ).raise_on_error() - - return self._commissioning_complete_future.result() -@@ -1856,15 +1856,13 @@ class ChipDeviceController(ChipDeviceControllerBase): - ''' - self.CheckIsActive() - -- setupPayload = setupPayload.encode() + b'\0' -- - self._commissioning_complete_future = concurrent.futures.Future() - - try: - self._enablePairingCompeleteCallback(True) - self._ChipStack.Call( - lambda: self._dmLib.pychip_DeviceController_ConnectWithCode( -- self.devCtrl, setupPayload, nodeid, discoveryType.value) -+ self.devCtrl, setupPayload.encode("utf-8"), nodeid, discoveryType.value) - ).raise_on_error() - - return self._commissioning_complete_future.result() --- -2.45.2 - diff --git a/0025-Fix-python-Wi-Fi-Thread-setup-with-manual-code-33933.patch b/0025-Fix-python-Wi-Fi-Thread-setup-with-manual-code-33933.patch deleted file mode 100644 index f785a46..0000000 --- a/0025-Fix-python-Wi-Fi-Thread-setup-with-manual-code-33933.patch +++ /dev/null @@ -1,416 +0,0 @@ -From bed8252a3ee1cf41510c470514cc86ae4a3a0bf3 Mon Sep 17 00:00:00 2001 -From: Tennessee Carmel-Veilleux -Date: Fri, 14 Jun 2024 18:52:44 -0400 -Subject: [PATCH] Fix python Wi-Fi/Thread setup with manual code (#33933) - -* Fix python Wi-Fi/Thread setup with manual code - -- Plumbing was missing to pass down the short discriminator -- Passing `--manual-code 1234-567-8901` which only has short - discriminator, would always fail to find device over BLE - -Fixes #26907 - -This PR: - -- Adds plumbing to detect short discriminator in Python controller -- Improves code-based setup in CHIPDeviceController to honor the - SetupDiscriminator value, including whether short/long. - -Testing done: -- Ran `python3 src/python_testing/TC_SC_3_6.py --commissioning-method ble-wifi --wifi-ssid MySsid --wifi-passphrase Secret123 --manual-code 2168-374-4904 --storage-path kvs1` - - Before fix, discriminator always mismatched. - - After fix, commissioning succeeds. -- Unit tests and other integration tests still pass - -* Restyled by clang-format - -* Restyled by autopep8 - -* Add warning about GetDiscriminator - -* Improve unit test - -* Fix tests - -* Address review comments - -* Restyled by clang-format - ---------- - -Co-authored-by: Restyled.io ---- - src/controller/CHIPDeviceController.cpp | 7 +-- - .../ChipDeviceController-ScriptBinding.cpp | 18 +++++- - src/controller/python/chip/ChipDeviceCtrl.py | 14 ++--- - src/lib/support/SetupDiscriminator.h | 10 ++-- - .../secure_channel/RendezvousParameters.h | 59 +++++++++++++++++-- - src/python_testing/matter_testing_support.py | 6 +- - src/setup_payload/SetupPayload.h | 2 +- - src/setup_payload/tests/TestManualCode.cpp | 20 ++++--- - 8 files changed, 99 insertions(+), 37 deletions(-) - -diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp -index af0a66ea83..335717664b 100644 ---- a/src/controller/CHIPDeviceController.cpp -+++ b/src/controller/CHIPDeviceController.cpp -@@ -714,10 +714,9 @@ CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, Re - // for later. - mRendezvousParametersForDeviceDiscoveredOverBle = params; - -- SetupDiscriminator discriminator; -- discriminator.SetLongValue(params.GetDiscriminator()); -- SuccessOrExit(err = mSystemState->BleLayer()->NewBleConnectionByDiscriminator( -- discriminator, this, OnDiscoveredDeviceOverBleSuccess, OnDiscoveredDeviceOverBleError)); -+ SuccessOrExit(err = mSystemState->BleLayer()->NewBleConnectionByDiscriminator(params.GetSetupDiscriminator().value(), -+ this, OnDiscoveredDeviceOverBleSuccess, -+ OnDiscoveredDeviceOverBleError)); - ExitNow(CHIP_NO_ERROR); - } - else -diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -index 28bea5384a..4a48434411 100644 ---- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp -+++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -@@ -71,6 +71,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -130,7 +131,7 @@ PyChipError pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommission - - // Rendezvous - PyChipError pychip_DeviceController_ConnectBLE(chip::Controller::DeviceCommissioner * devCtrl, uint16_t discriminator, -- uint32_t setupPINCode, chip::NodeId nodeid); -+ bool isShortDiscriminator, uint32_t setupPINCode, chip::NodeId nodeid); - PyChipError pychip_DeviceController_ConnectIP(chip::Controller::DeviceCommissioner * devCtrl, const char * peerAddrStr, - uint32_t setupPINCode, chip::NodeId nodeid); - PyChipError pychip_DeviceController_ConnectWithCode(chip::Controller::DeviceCommissioner * devCtrl, const char * onboardingPayload, -@@ -347,13 +348,24 @@ const char * pychip_DeviceController_StatusReportToString(uint32_t profileId, ui - } - - PyChipError pychip_DeviceController_ConnectBLE(chip::Controller::DeviceCommissioner * devCtrl, uint16_t discriminator, -- uint32_t setupPINCode, chip::NodeId nodeid) -+ bool isShortDiscriminator, uint32_t setupPINCode, chip::NodeId nodeid) - { -+ SetupDiscriminator setupDiscriminator; -+ -+ if (isShortDiscriminator) -+ { -+ setupDiscriminator.SetShortValue(discriminator & 0xFu); -+ } -+ else -+ { -+ setupDiscriminator.SetLongValue(discriminator); -+ } -+ - return ToPyChipError(devCtrl->PairDevice(nodeid, - chip::RendezvousParameters() - .SetPeerAddress(Transport::PeerAddress(Transport::Type::kBle)) - .SetSetupPINCode(setupPINCode) -- .SetDiscriminator(discriminator), -+ .SetSetupDiscriminator(setupDiscriminator), - sCommissioningParameters)); - } - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index b99d1db018..3cb7e67b7a 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -443,7 +443,7 @@ class ChipDeviceControllerBase(): - self.devCtrl) - ) - -- def ConnectBLE(self, discriminator, setupPinCode, nodeid) -> PyChipError: -+ def ConnectBLE(self, discriminator: int, setupPinCode: int, nodeid: int, isShortDiscriminator: bool = False) -> PyChipError: - self.CheckIsActive() - - self._commissioning_complete_future = concurrent.futures.Future() -@@ -452,7 +452,7 @@ class ChipDeviceControllerBase(): - self._enablePairingCompeleteCallback(True) - self._ChipStack.Call( - lambda: self._dmLib.pychip_DeviceController_ConnectBLE( -- self.devCtrl, discriminator, setupPinCode, nodeid) -+ self.devCtrl, discriminator, isShortDiscriminator, setupPinCode, nodeid) - ).raise_on_error() - - # TODO: Change return None. Only returning on success is not useful. -@@ -1459,7 +1459,7 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_DeleteDeviceController.restype = PyChipError - - self._dmLib.pychip_DeviceController_ConnectBLE.argtypes = [ -- c_void_p, c_uint16, c_uint32, c_uint64] -+ c_void_p, c_uint16, c_bool, c_uint32, c_uint64] - self._dmLib.pychip_DeviceController_ConnectBLE.restype = PyChipError - - self._dmLib.pychip_DeviceController_SetThreadOperationalDataset.argtypes = [ -@@ -1738,17 +1738,17 @@ class ChipDeviceController(ChipDeviceControllerBase): - finally: - self._commissioning_complete_future = None - -- def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes) -> PyChipError: -+ def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes, isShortDiscriminator: bool = False) -> PyChipError: - ''' Commissions a Thread device over BLE - ''' - self.SetThreadOperationalDataset(threadOperationalDataset) -- return self.ConnectBLE(discriminator, setupPinCode, nodeId) -+ return self.ConnectBLE(discriminator, setupPinCode, nodeId, isShortDiscriminator) - -- def CommissionWiFi(self, discriminator, setupPinCode, nodeId, ssid: str, credentials: str) -> PyChipError: -+ def CommissionWiFi(self, discriminator, setupPinCode, nodeId, ssid: str, credentials: str, isShortDiscriminator: bool = False) -> PyChipError: - ''' Commissions a Wi-Fi device over BLE. - ''' - self.SetWiFiCredentials(ssid, credentials) -- return self.ConnectBLE(discriminator, setupPinCode, nodeId) -+ return self.ConnectBLE(discriminator, setupPinCode, nodeId, isShortDiscriminator) - - def SetWiFiCredentials(self, ssid: str, credentials: str): - ''' Set the Wi-Fi credentials to set during commissioning.''' -diff --git a/src/lib/support/SetupDiscriminator.h b/src/lib/support/SetupDiscriminator.h -index 5c1fabae71..2083cdc7cb 100644 ---- a/src/lib/support/SetupDiscriminator.h -+++ b/src/lib/support/SetupDiscriminator.h -@@ -23,16 +23,16 @@ - - #pragma once - --#include -- - #include - -+#include -+ - namespace chip { - - class SetupDiscriminator - { - public: -- constexpr SetupDiscriminator() : mDiscriminator(0), mIsShortDiscriminator(0) {} -+ constexpr SetupDiscriminator() : mDiscriminator(0), mIsShortDiscriminator(false) {} - - // See section 5.1.2. QR Code in the Matter specification - static constexpr int kLongBits = 12; -@@ -104,8 +104,8 @@ private: - // discriminator). - static_assert(kLongBits == 12, "Unexpected field length"); - static_assert(kShortBits <= kLongBits, "Unexpected field length"); -- uint16_t mDiscriminator : 12; -- uint16_t mIsShortDiscriminator : 1; -+ uint16_t mDiscriminator; -+ bool mIsShortDiscriminator; - }; - - } // namespace chip -diff --git a/src/protocols/secure_channel/RendezvousParameters.h b/src/protocols/secure_channel/RendezvousParameters.h -index e5e366d3b3..0e8b793dd0 100644 ---- a/src/protocols/secure_channel/RendezvousParameters.h -+++ b/src/protocols/secure_channel/RendezvousParameters.h -@@ -17,12 +17,15 @@ - - #pragma once - -+#include -+ - #include - #include - #if CONFIG_NETWORK_LAYER_BLE - #include - #endif // CONFIG_NETWORK_LAYER_BLE - -+#include - #include - #include - #include -@@ -58,11 +61,55 @@ public: - - // Discriminators in RendezvousParameters are always long (12-bit) - // discriminators. -- bool HasDiscriminator() const { return mDiscriminator <= kMaxRendezvousDiscriminatorValue; } -- uint16_t GetDiscriminator() const { return mDiscriminator; } -+ bool HasDiscriminator() const { return mSetupDiscriminator.has_value(); } -+ -+ // Obtains the long version of the discriminator, or 0 if short. -+ // WARNING: This is lossy and a bad idea to use. The correct method to use -+ // is GetSetupDiscriminator(). This method exists for public -+ // API backwards compatibility. -+ uint16_t GetDiscriminator() const -+ { -+ if (!mSetupDiscriminator.has_value()) -+ { -+ ChipLogError(Discovery, -+ "Get RendezvousParameters::GetDiscriminator() called without discriminator in params (inconsistent). " -+ "Using value 0 to avoid crash! Ensure discriminator is set!"); -+ return 0; -+ } -+ -+ if (mSetupDiscriminator.value().IsShortDiscriminator()) -+ { -+ ChipLogError(Discovery, -+ "Get RendezvousParameters::GetDiscriminator() called with SHORT discriminator (inconsistent). Using value " -+ "0 to avoid crash! Call GetSetupDiscriminator() to avoid loss."); -+ return 0; -+ } -+ -+ return mSetupDiscriminator.value().GetLongValue(); -+ } -+ -+ std::optional GetSetupDiscriminator() const -+ { -+ if (!mSetupDiscriminator.has_value()) -+ { -+ ChipLogError( -+ Discovery, -+ "Get RendezvousParameters::GetSetupDiscriminator() called without discriminator in params (inconsistent)."); -+ } -+ return mSetupDiscriminator; -+ } -+ -+ RendezvousParameters & SetSetupDiscriminator(SetupDiscriminator discriminator) -+ { -+ mSetupDiscriminator = discriminator; -+ return *this; -+ } -+ - RendezvousParameters & SetDiscriminator(uint16_t discriminator) - { -- mDiscriminator = discriminator; -+ SetupDiscriminator tempDiscriminator; -+ tempDiscriminator.SetLongValue(discriminator); -+ mSetupDiscriminator = tempDiscriminator; - return *this; - } - -@@ -127,9 +174,9 @@ public: - } - - private: -- Transport::PeerAddress mPeerAddress; ///< the peer node address -- uint32_t mSetupPINCode = 0; ///< the target peripheral setup PIN Code -- uint16_t mDiscriminator = UINT16_MAX; ///< the target peripheral discriminator -+ Transport::PeerAddress mPeerAddress; ///< the peer node address -+ uint32_t mSetupPINCode = 0; ///< the target peripheral setup PIN Code -+ std::optional mSetupDiscriminator; - - Spake2pVerifier mPASEVerifier; - bool mHasPASEVerifier = false; -diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py -index aee09dd1b9..f38c116424 100644 ---- a/src/python_testing/matter_testing_support.py -+++ b/src/python_testing/matter_testing_support.py -@@ -1593,14 +1593,16 @@ class CommissionDeviceTest(MatterBaseTest): - info.passcode, - conf.dut_node_ids[i], - conf.wifi_ssid, -- conf.wifi_passphrase -+ conf.wifi_passphrase, -+ isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) - ) - elif conf.commissioning_method == "ble-thread": - return dev_ctrl.CommissionThread( - info.filter_value, - info.passcode, - conf.dut_node_ids[i], -- conf.thread_operational_dataset -+ conf.thread_operational_dataset, -+ isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) - ) - elif conf.commissioning_method == "on-network-ip": - logging.warning("==== USING A DIRECT IP COMMISSIONING METHOD NOT SUPPORTED IN THE LONG TERM ====") -diff --git a/src/setup_payload/SetupPayload.h b/src/setup_payload/SetupPayload.h -index 0bb2169834..1fc50f1508 100644 ---- a/src/setup_payload/SetupPayload.h -+++ b/src/setup_payload/SetupPayload.h -@@ -125,7 +125,7 @@ struct PayloadContents - // payload parsed from a QR code would always have a value for - // rendezvousInformation. - Optional rendezvousInformation; -- SetupDiscriminator discriminator; -+ SetupDiscriminator discriminator{}; - uint32_t setUpPINCode = 0; - - bool isValidQRCodePayload() const; -diff --git a/src/setup_payload/tests/TestManualCode.cpp b/src/setup_payload/tests/TestManualCode.cpp -index cfe7c59c06..37ee76b8af 100644 ---- a/src/setup_payload/tests/TestManualCode.cpp -+++ b/src/setup_payload/tests/TestManualCode.cpp -@@ -25,6 +25,7 @@ - #include - #include - -+#include - #include - #include - #include -@@ -402,8 +403,9 @@ void TestLongCodeReadWrite(nlTestSuite * inSuite, void * context) - } - - void assertEmptyPayloadWithError(nlTestSuite * inSuite, CHIP_ERROR actualError, CHIP_ERROR expectedError, -- const SetupPayload & payload) -+ const SetupPayload & payload, int line) - { -+ ChipLogProgress(Test, "Current check line: %d", line); - NL_TEST_ASSERT(inSuite, actualError == expectedError); - NL_TEST_ASSERT(inSuite, - payload.setUpPINCode == 0 && payload.discriminator.GetLongValue() == 0 && payload.productID == 0 && -@@ -419,46 +421,46 @@ void TestPayloadParser_InvalidEntry(nlTestSuite * inSuite, void * inContext) - decimalString = ""; - decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); - assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), -- CHIP_ERROR_INVALID_STRING_LENGTH, payload); -+ CHIP_ERROR_INVALID_STRING_LENGTH, payload, __LINE__); - - // Invalid character - decimalString = "24184.2196"; - decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); - assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), -- CHIP_ERROR_INVALID_INTEGER_VALUE, payload); -+ CHIP_ERROR_INVALID_INTEGER_VALUE, payload, __LINE__); - - // too short - decimalString = "2456"; - decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); - assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), -- CHIP_ERROR_INVALID_STRING_LENGTH, payload); -+ CHIP_ERROR_INVALID_STRING_LENGTH, payload, __LINE__); - - // too long for long code - decimalString = "123456789123456785671"; - decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); - assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), -- CHIP_ERROR_INVALID_STRING_LENGTH, payload); -+ CHIP_ERROR_INVALID_STRING_LENGTH, payload, __LINE__); - - // too long for short code - decimalString = "12749875380"; - decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); - assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), -- CHIP_ERROR_INVALID_STRING_LENGTH, payload); -+ CHIP_ERROR_INVALID_STRING_LENGTH, payload, __LINE__); - - // bit to indicate short code but long code length - decimalString = "23456789123456785610"; - decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); - assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), -- CHIP_ERROR_INVALID_STRING_LENGTH, payload); -+ CHIP_ERROR_INVALID_STRING_LENGTH, payload, __LINE__); - // no pin code (= 0) - decimalString = "2327680000"; - decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); - assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), -- CHIP_ERROR_INVALID_ARGUMENT, payload); -+ CHIP_ERROR_INVALID_ARGUMENT, payload, __LINE__); - // wrong check digit - decimalString = "02684354589"; - assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), -- CHIP_ERROR_INTEGRITY_CHECK_FAILED, payload); -+ CHIP_ERROR_INTEGRITY_CHECK_FAILED, payload, __LINE__); - } - - void TestCheckDecimalStringValidity(nlTestSuite * inSuite, void * inContext) --- -2.45.2 - diff --git a/0026-Python-Store-original-PyChipError-in-ChipStackExcept.patch b/0026-Python-Store-original-PyChipError-in-ChipStackExcept.patch deleted file mode 100644 index c51cef3..0000000 --- a/0026-Python-Store-original-PyChipError-in-ChipStackExcept.patch +++ /dev/null @@ -1,471 +0,0 @@ -From 65334ba37c6187dc1d3cf2f2a49fa57ee086d277 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Tue, 18 Jun 2024 14:45:11 +0200 -Subject: [PATCH] [Python] Store original PyChipError in ChipStackException - (#33954) - -* [Python] Drop unused ErrorToException function - -Also remove the now unused pychip_Stack_ErrorToString function. This -is handled in pychip_FormatError today. - -* [Python] Cleanup PyChipError return values - -Use PyChipError return value where PyChipErrors are actually returned. -This then also allows us to use the typical .raise_on_error() pattern. - -* [Python] Store original PyChipError in ChipStackException - -This change stores the original PyChipError in ChipStackException so -that details of the original error code can still be retrieved. This -is interesting to use the properties returning processed information -about the original error code. It also preserves the line and code -file which can be helpful. - -* [Python] Fix Command API argument type errors - -NativeLibraryHandleMethodArguments correctly setting the arguments -uncovered some incorrectly set arguments. - -* [Python] Use to_exception() to convert PyChipError to ChipStackError - -* [Python] Fix Cert API argument type errors - -NativeLibraryHandleMethodArguments correctly setting the argument -types causes argument type errors: -ctypes.ArgumentError: argument 1: TypeError: expected LP_c_ubyte instance instead of bytes - -We can safely cast bytes as the native side marks it const. ---- - .../ChipDeviceController-ScriptBinding.cpp | 11 ------ - src/controller/python/chip/ChipDeviceCtrl.py | 13 +++---- - src/controller/python/chip/ChipStack.py | 30 --------------- - .../python/chip/clusters/Attribute.py | 12 +++--- - .../python/chip/clusters/Command.py | 4 +- - .../python/chip/credentials/cert.py | 8 +++- - .../python/chip/discovery/__init__.py | 3 +- - .../python/chip/exceptions/__init__.py | 25 ++++++++++-- - .../python/chip/interaction_model/delegate.py | 8 ++-- - src/controller/python/chip/native/__init__.py | 4 +- - .../chip/setup_payload/setup_payload.py | 38 +++++++------------ - 11 files changed, 61 insertions(+), 95 deletions(-) - -diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -index 4a48434411..a2ab01734b 100644 ---- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp -+++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp -@@ -198,7 +198,6 @@ pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(chip::Controller: - // BLE - PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::DeviceCommissioner * devCtrl); - --const char * pychip_Stack_ErrorToString(ChipError::StorageType err); - const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode); - - PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, -@@ -336,11 +335,6 @@ PyChipError pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommission - return ToPyChipError(CHIP_NO_ERROR); - } - --const char * pychip_DeviceController_ErrorToString(ChipError::StorageType err) --{ -- return chip::ErrorStr(CHIP_ERROR(err)); --} -- - const char * pychip_DeviceController_StatusReportToString(uint32_t profileId, uint16_t statusCode) - { - // return chip::StatusReportStr(profileId, statusCode); -@@ -690,11 +684,6 @@ pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(chip::Controller: - return ToPyChipError(CHIP_NO_ERROR); - } - --const char * pychip_Stack_ErrorToString(ChipError::StorageType err) --{ -- return chip::ErrorStr(CHIP_ERROR(err)); --} -- - const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode) - { - // return chip::StatusReportStr(profileId, statusCode); -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 3cb7e67b7a..3af0facfa5 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -882,12 +882,8 @@ class ChipDeviceControllerBase(): - sessionParametersStruct = SessionParametersStruct.parse(b'\x00' * SessionParametersStruct.sizeof()) - sessionParametersByteArray = SessionParametersStruct.build(sessionParametersStruct) - device = self.GetConnectedDeviceSync(nodeid) -- res = self._ChipStack.Call(lambda: self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters( -- device.deviceProxy, ctypes.c_char_p(sessionParametersByteArray))) -- -- # 0 is CHIP_NO_ERROR -- if res != 0: -- return None -+ self._ChipStack.Call(lambda: self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters( -+ device.deviceProxy, ctypes.c_char_p(sessionParametersByteArray))).raise_on_error() - - sessionParametersStruct = SessionParametersStruct.parse(sessionParametersByteArray) - return SessionParameters( -@@ -899,8 +895,6 @@ class ChipDeviceControllerBase(): - specficiationVersion=sessionParametersStruct.SpecificationVersion if sessionParametersStruct.SpecificationVersion != 0 else None, - maxPathsPerInvoke=sessionParametersStruct.MaxPathsPerInvoke) - -- return res -- - async def TestOnlySendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo], - timedRequestTimeoutMs: typing.Optional[int] = None, - interactionTimeoutMs: typing.Optional[int] = None, busyWaitMs: typing.Optional[int] = None, -@@ -1660,6 +1654,9 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_SetIpk.argtypes = [c_void_p, POINTER(c_char), c_size_t] - self._dmLib.pychip_DeviceController_SetIpk.restype = PyChipError - -+ self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters.restype = PyChipError -+ self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters.argtypes = [c_void_p, c_char_p] -+ - - class ChipDeviceController(ChipDeviceControllerBase): - ''' The ChipDeviceCommissioner binding, named as ChipDeviceController -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index 4f19776664..dc4efc223f 100644 ---- a/src/controller/python/chip/ChipStack.py -+++ b/src/controller/python/chip/ChipStack.py -@@ -35,7 +35,6 @@ from threading import Condition, Lock - import chip.native - from chip.native import PyChipError - --from .ChipUtility import ChipUtility - from .clusters import Attribute as ClusterAttribute - from .clusters import Command as ClusterCommand - from .exceptions import ChipStackError, ChipStackException, DeviceError -@@ -247,33 +246,6 @@ class ChipStack(object): - raise res.to_exception() - return callObj - -- def ErrorToException(self, err, devStatusPtr=None): -- if err == 0x2C and devStatusPtr: -- devStatus = devStatusPtr.contents -- msg = ChipUtility.CStringToString( -- ( -- self._ChipStackLib.pychip_Stack_StatusReportToString( -- devStatus.ProfileId, devStatus.StatusCode -- ) -- ) -- ) -- sysErrorCode = ( -- devStatus.SysErrorCode if ( -- devStatus.SysErrorCode != 0) else None -- ) -- if sysErrorCode is not None: -- msg = msg + " (system err %d)" % (sysErrorCode) -- return DeviceError( -- devStatus.ProfileId, devStatus.StatusCode, sysErrorCode, msg -- ) -- else: -- return ChipStackError( -- err, -- ChipUtility.CStringToString( -- (self._ChipStackLib.pychip_Stack_ErrorToString(err)) -- ), -- ) -- - def LocateChipDLL(self): - self._loadLib() - return self._chipDLLPath -@@ -302,8 +274,6 @@ class ChipStack(object): - c_uint16, - ] - self._ChipStackLib.pychip_Stack_StatusReportToString.restype = c_char_p -- self._ChipStackLib.pychip_Stack_ErrorToString.argtypes = [c_uint32] -- self._ChipStackLib.pychip_Stack_ErrorToString.restype = c_char_p - - self._ChipStackLib.pychip_DeviceController_PostTaskOnChipThread.argtypes = [ - _ChipThreadTaskRunnerFunct, py_object] -diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py -index 838936e83b..16db825c12 100644 ---- a/src/controller/python/chip/clusters/Attribute.py -+++ b/src/controller/python/chip/clusters/Attribute.py -@@ -674,7 +674,7 @@ class AsyncReadTransaction: - self._changedPathSet = set() - self._pReadClient = None - self._pReadCallback = None -- self._resultError = None -+ self._resultError: Optional[PyChipError] = None - - def SetClientObjPointers(self, pReadClient, pReadCallback): - self._pReadClient = pReadClient -@@ -741,7 +741,7 @@ class AsyncReadTransaction: - logging.exception(ex) - - def handleError(self, chipError: PyChipError): -- self._resultError = chipError.code -+ self._resultError = chipError - - def _handleSubscriptionEstablished(self, subscriptionId): - if not self._future.done(): -@@ -805,11 +805,11 @@ class AsyncReadTransaction: - # move on, possibly invalidating the provided _event_loop. - # - if not self._future.done(): -- if self._resultError: -+ if self._resultError is not None: - if self._subscription_handler: -- self._subscription_handler.OnErrorCb(self._resultError, self._subscription_handler) -+ self._subscription_handler.OnErrorCb(self._resultError.code, self._subscription_handler) - else: -- self._future.set_exception(chip.exceptions.ChipStackError(self._resultError)) -+ self._future.set_exception(self._resultError.to_exception()) - else: - self._future.set_result(AsyncReadTransaction.ReadResponse( - attributes=self._cache.attributeCache, events=self._events, tlvAttributes=self._cache.attributeTLVCache)) -@@ -837,7 +837,7 @@ class AsyncWriteTransaction: - self._event_loop = eventLoop - self._future = future - self._resultData = [] -- self._resultError = None -+ self._resultError: Optional[PyChipError] = None - - def handleResponse(self, path: AttributePath, status: int): - try: -diff --git a/src/controller/python/chip/clusters/Command.py b/src/controller/python/chip/clusters/Command.py -index 6ef25cb211..93951338f9 100644 ---- a/src/controller/python/chip/clusters/Command.py -+++ b/src/controller/python/chip/clusters/Command.py -@@ -467,13 +467,13 @@ def Init(): - setter = chip.native.NativeLibraryHandleMethodArguments(handle) - - setter.Set('pychip_CommandSender_SendCommand', -- PyChipError, [py_object, c_void_p, c_uint16, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_bool]) -+ PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_uint16, c_bool]) - setter.Set('pychip_CommandSender_SendBatchCommands', - PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint16, c_bool, POINTER(PyInvokeRequestData), c_size_t]) - setter.Set('pychip_CommandSender_TestOnlySendBatchCommands', - PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint16, c_bool, TestOnlyPyBatchCommandsOverrides, POINTER(PyInvokeRequestData), c_size_t]) - setter.Set('pychip_CommandSender_TestOnlySendCommandTimedRequestNoTimedInvoke', -- PyChipError, [py_object, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_bool]) -+ PyChipError, [py_object, c_void_p, c_uint16, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_uint16, c_bool]) - setter.Set('pychip_CommandSender_SendGroupCommand', - PyChipError, [c_uint16, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16]) - setter.Set('pychip_CommandSender_InitCallbacks', None, [ -diff --git a/src/controller/python/chip/credentials/cert.py b/src/controller/python/chip/credentials/cert.py -index 786c1a4231..df0b28207d 100644 ---- a/src/controller/python/chip/credentials/cert.py -+++ b/src/controller/python/chip/credentials/cert.py -@@ -35,8 +35,10 @@ def convert_x509_cert_to_chip_cert(x509Cert: bytes) -> bytes: - """Converts a x509 certificate to CHIP Certificate.""" - output_buffer = (ctypes.c_uint8 * 1024)() - output_size = ctypes.c_size_t(1024) -+ ptr_type = ctypes.POINTER(ctypes.c_uint8) - -- _handle().pychip_ConvertX509CertToChipCert(x509Cert, len(x509Cert), output_buffer, ctypes.byref(output_size)).raise_on_error() -+ _handle().pychip_ConvertX509CertToChipCert(ctypes.cast(x509Cert, ptr_type), len(x509Cert), -+ ctypes.cast(output_buffer, ptr_type), ctypes.byref(output_size)).raise_on_error() - - return bytes(output_buffer)[:output_size.value] - -@@ -45,7 +47,9 @@ def convert_chip_cert_to_x509_cert(chipCert: bytes) -> bytes: - """Converts a x509 certificate to CHIP Certificate.""" - output_buffer = (ctypes.c_byte * 1024)() - output_size = ctypes.c_size_t(1024) -+ ptr_type = ctypes.POINTER(ctypes.c_uint8) - -- _handle().pychip_ConvertChipCertToX509Cert(chipCert, len(chipCert), output_buffer, ctypes.byref(output_size)).raise_on_error() -+ _handle().pychip_ConvertChipCertToX509Cert(ctypes.cast(chipCert, ptr_type), len(chipCert), -+ ctypes.cast(output_buffer, ptr_type), ctypes.byref(output_size)).raise_on_error() - - return bytes(output_buffer)[:output_size.value] -diff --git a/src/controller/python/chip/discovery/__init__.py b/src/controller/python/chip/discovery/__init__.py -index c400d97542..a25fb49000 100644 ---- a/src/controller/python/chip/discovery/__init__.py -+++ b/src/controller/python/chip/discovery/__init__.py -@@ -236,8 +236,7 @@ def FindAddressAsync(fabricid: int, nodeid: int, callback, timeout_ms=1000): - ) - - res = _GetDiscoveryLibraryHandle().pychip_discovery_resolve(fabricid, nodeid) -- if res != 0: -- raise Exception("Failed to start node resolution") -+ res.raise_on_error() - - - class _SyncAddressFinder: -diff --git a/src/controller/python/chip/exceptions/__init__.py b/src/controller/python/chip/exceptions/__init__.py -index 6b1969f1ef..c7f692e928 100644 ---- a/src/controller/python/chip/exceptions/__init__.py -+++ b/src/controller/python/chip/exceptions/__init__.py -@@ -15,6 +15,8 @@ - # limitations under the License. - # - -+from __future__ import annotations -+ - __all__ = [ - "ChipStackException", - "ChipStackError", -@@ -26,15 +28,32 @@ __all__ = [ - "UnknownCommand", - ] - -+from typing import TYPE_CHECKING -+ -+if TYPE_CHECKING: -+ from chip.native import PyChipError -+ - - class ChipStackException(Exception): - pass - - - class ChipStackError(ChipStackException): -- def __init__(self, err, msg=None): -- self.err = err -- self.msg = msg if msg else "Chip Stack Error %d" % err -+ def __init__(self, chip_error: PyChipError, msg=None): -+ self._chip_error = chip_error -+ self.msg = msg if msg else "Chip Stack Error %d" % chip_error.code -+ -+ @classmethod -+ def from_chip_error(cls, chip_error: PyChipError) -> ChipStackError: -+ return cls(chip_error, str(chip_error)) -+ -+ @property -+ def chip_error(self) -> PyChipError | None: -+ return self._chip_error -+ -+ @property -+ def err(self) -> int: -+ return self._chip_error.code - - def __str__(self): - return self.msg -diff --git a/src/controller/python/chip/interaction_model/delegate.py b/src/controller/python/chip/interaction_model/delegate.py -index 14512000a6..4741e5f2f7 100644 ---- a/src/controller/python/chip/interaction_model/delegate.py -+++ b/src/controller/python/chip/interaction_model/delegate.py -@@ -330,7 +330,7 @@ def InitIMDelegate(): - setter.Set("pychip_InteractionModelDelegate_SetCommandResponseErrorCallback", None, [ - _OnCommandResponseFunct]) - setter.Set("pychip_InteractionModel_GetCommandSenderHandle", -- c_uint32, [ctypes.POINTER(c_uint64)]) -+ chip.native.PyChipError, [ctypes.POINTER(c_uint64)]) - setter.Set("pychip_InteractionModelDelegate_SetOnWriteResponseStatusCallback", None, [ - _OnWriteResponseStatusFunct]) - -@@ -389,10 +389,8 @@ def WaitCommandIndexStatus(commandHandle: int, commandIndex: int): - def GetCommandSenderHandle() -> int: - handle = chip.native.GetLibraryHandle() - resPointer = c_uint64() -- res = handle.pychip_InteractionModel_GetCommandSenderHandle( -- ctypes.pointer(resPointer)) -- if res != 0: -- raise chip.exceptions.ChipStackError(res) -+ handle.pychip_InteractionModel_GetCommandSenderHandle( -+ ctypes.pointer(resPointer)).raise_on_error() - ClearCommandStatus(resPointer.value) - return resPointer.value - -diff --git a/src/controller/python/chip/native/__init__.py b/src/controller/python/chip/native/__init__.py -index ce8b7620f4..9ee94c61dd 100644 ---- a/src/controller/python/chip/native/__init__.py -+++ b/src/controller/python/chip/native/__init__.py -@@ -116,7 +116,7 @@ class PyChipError(ctypes.Structure): - - def to_exception(self) -> typing.Union[None, chip.exceptions.ChipStackError]: - if not self.is_success: -- return chip.exceptions.ChipStackError(self.code, str(self)) -+ return chip.exceptions.ChipStackError.from_chip_error(self) - - def __str__(self): - buf = ctypes.create_string_buffer(256) -@@ -199,7 +199,7 @@ class NativeLibraryHandleMethodArguments: - def Set(self, methodName: str, resultType, argumentTypes: list): - method = getattr(self.handle, methodName) - method.restype = resultType -- method.argtype = argumentTypes -+ method.argtypes = argumentTypes - - - @dataclass -diff --git a/src/controller/python/chip/setup_payload/setup_payload.py b/src/controller/python/chip/setup_payload/setup_payload.py -index 1f70983ad9..702fb319b4 100644 ---- a/src/controller/python/chip/setup_payload/setup_payload.py -+++ b/src/controller/python/chip/setup_payload/setup_payload.py -@@ -14,11 +14,10 @@ - # limitations under the License. - # - --from ctypes import CFUNCTYPE, c_char_p, c_int32, c_uint8, c_uint16, c_uint32 -+from ctypes import CFUNCTYPE, c_char_p, c_uint8, c_uint16, c_uint32 - from typing import Optional - --from chip.exceptions import ChipStackError --from chip.native import GetLibraryHandle, NativeLibraryHandleMethodArguments -+from chip.native import GetLibraryHandle, NativeLibraryHandleMethodArguments, PyChipError - - - class SetupPayload: -@@ -46,34 +45,25 @@ class SetupPayload: - - def ParseQrCode(self, qrCode: str): - self.Clear() -- err = self.chipLib.pychip_SetupPayload_ParseQrCode(qrCode.upper().encode(), -- self.attribute_visitor, -- self.vendor_attribute_visitor) -- -- if err != 0: -- raise ChipStackError(err) -+ self.chipLib.pychip_SetupPayload_ParseQrCode(qrCode.upper().encode(), -+ self.attribute_visitor, -+ self.vendor_attribute_visitor).raise_on_error() - - return self - - def ParseManualPairingCode(self, manualPairingCode: str): - self.Clear() -- err = self.chipLib.pychip_SetupPayload_ParseManualPairingCode(manualPairingCode.encode(), -- self.attribute_visitor, -- self.vendor_attribute_visitor) -- -- if err != 0: -- raise ChipStackError(err) -+ self.chipLib.pychip_SetupPayload_ParseManualPairingCode(manualPairingCode.encode(), -+ self.attribute_visitor, -+ self.vendor_attribute_visitor).raise_on_error() - - return self - - # DEPRECATED - def PrintOnboardingCodes(self, passcode, vendorId, productId, discriminator, customFlow, capabilities, version): - self.Clear() -- err = self.chipLib.pychip_SetupPayload_PrintOnboardingCodes( -- passcode, vendorId, productId, discriminator, customFlow, capabilities, version) -- -- if err != 0: -- raise ChipStackError(err) -+ self.chipLib.pychip_SetupPayload_PrintOnboardingCodes( -+ passcode, vendorId, productId, discriminator, customFlow, capabilities, version).raise_on_error() - - # DEPRECATED - def Print(self): -@@ -106,17 +96,17 @@ class SetupPayload: - return None - - def __InitNativeFunctions(self, chipLib): -- if chipLib.pychip_SetupPayload_ParseQrCode is not None: -+ if chipLib.pychip_SetupPayload_ParseQrCode.argtypes is not None: - return - setter = NativeLibraryHandleMethodArguments(chipLib) - setter.Set("pychip_SetupPayload_ParseQrCode", -- c_int32, -+ PyChipError, - [c_char_p, SetupPayload.AttributeVisitor, SetupPayload.VendorAttributeVisitor]) - setter.Set("pychip_SetupPayload_ParseManualPairingCode", -- c_int32, -+ PyChipError, - [c_char_p, SetupPayload.AttributeVisitor, SetupPayload.VendorAttributeVisitor]) - setter.Set("pychip_SetupPayload_PrintOnboardingCodes", -- c_int32, -+ PyChipError, - [c_uint32, c_uint16, c_uint16, c_uint16, c_uint8, c_uint8, c_uint8]) - - # Getters from parsed contents. --- -2.45.2 - diff --git a/0027-Python-Make-Commissioning-APIs-more-pythonic-and-con.patch b/0027-Python-Make-Commissioning-APIs-more-pythonic-and-con.patch deleted file mode 100644 index 3920b5f..0000000 --- a/0027-Python-Make-Commissioning-APIs-more-pythonic-and-con.patch +++ /dev/null @@ -1,580 +0,0 @@ -From 73f66667b8e34ba483b5131bf0697c1456e53746 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Tue, 18 Jun 2024 23:16:47 +0200 -Subject: [PATCH] [Python] Make Commissioning APIs more pythonic and consistent - (#33905) - -* [Python] Make Commissioning APIs more pythonic and consistent - -This commit makes the commissioning APIs more pythonic and consistent -by not returning PyChipError but simply raising ChipStackError -exceptions on errors instead. - -The return value instead returns the effectively assigned node ID -as defined by the NOC. If the SDK ends up generating that NOC, it -will use the thing passed to PairDevice, so those will match with -what is provided when calling the commissioning API. - -* [Python] Adjust tests to use new commissioning error handling ---- - src/controller/python/chip/ChipDeviceCtrl.py | 95 +++++++++++-------- - src/controller/python/chip/yaml/runner.py | 6 +- - .../python/test/test_scripts/base.py | 28 ++++-- - src/python_testing/TC_ACE_1_5.py | 4 +- - src/python_testing/TC_CGEN_2_4.py | 24 +++-- - src/python_testing/TC_DA_1_5.py | 3 +- - src/python_testing/TC_TIMESYNC_2_13.py | 3 +- - .../TestCommissioningTimeSync.py | 3 +- - src/python_testing/matter_testing_support.py | 72 +++++++++----- - .../integration-tests/common/utils.py | 5 +- - 10 files changed, 143 insertions(+), 100 deletions(-) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 3af0facfa5..5c1790c087 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -143,11 +143,14 @@ class CommissionableNode(discovery.CommissionableNode): - def SetDeviceController(self, devCtrl: 'ChipDeviceController'): - self._devCtrl = devCtrl - -- def Commission(self, nodeId: int, setupPinCode: int) -> PyChipError: -+ def Commission(self, nodeId: int, setupPinCode: int) -> int: - ''' Commission the device using the device controller discovered this device. - - nodeId: The nodeId commissioned to the device - setupPinCode: The setup pin code of the device -+ -+ Returns: -+ - Effective Node ID of the device (as defined by the assigned NOC) - ''' - return self._devCtrl.CommissionOnNetwork( - nodeId, setupPinCode, filterType=discovery.FilterType.INSTANCE_NAME, filter=self.instanceName) -@@ -271,7 +274,10 @@ class ChipDeviceControllerBase(): - logging.exception("HandleCommissioningComplete called unexpectedly") - return - -- self._commissioning_complete_future.set_result(err) -+ if err.is_success: -+ self._commissioning_complete_future.set_result(nodeId) -+ else: -+ self._commissioning_complete_future.set_exception(err.to_exception()) - - def HandleFabricCheck(nodeId): - self.fabricCheckNodeId = nodeId -@@ -319,14 +325,17 @@ class ChipDeviceControllerBase(): - # During Commissioning, HandlePASEEstablishmentComplete will also be called. - # Only complete the future if PASE session establishment failed. - if not err.is_success: -- self._commissioning_complete_future.set_result(err) -+ self._commissioning_complete_future.set_exception(err.to_exception()) - return - - if self._pase_establishment_complete_future is None: - logging.exception("HandlePASEEstablishmentComplete called unexpectedly") - return - -- self._pase_establishment_complete_future.set_result(err) -+ if err.is_success: -+ self._pase_establishment_complete_future.set_result(None) -+ else: -+ self._pase_establishment_complete_future.set_exception(err.to_exception()) - - self.pairingDelegate = pairingDelegate - self.devCtrl = devCtrl -@@ -443,7 +452,12 @@ class ChipDeviceControllerBase(): - self.devCtrl) - ) - -- def ConnectBLE(self, discriminator: int, setupPinCode: int, nodeid: int, isShortDiscriminator: bool = False) -> PyChipError: -+ def ConnectBLE(self, discriminator: int, setupPinCode: int, nodeid: int, isShortDiscriminator: bool = False) -> int: -+ """Connect to a BLE device using the given discriminator and setup pin code. -+ -+ Returns: -+ - Effective Node ID of the device (as defined by the assigned NOC) -+ """ - self.CheckIsActive() - - self._commissioning_complete_future = concurrent.futures.Future() -@@ -455,11 +469,7 @@ class ChipDeviceControllerBase(): - self.devCtrl, discriminator, isShortDiscriminator, setupPinCode, nodeid) - ).raise_on_error() - -- # TODO: Change return None. Only returning on success is not useful. -- # but that is what the previous implementation did. -- res = self._commissioning_complete_future.result() -- res.raise_on_error() -- return res -+ return self._commissioning_complete_future.result() - finally: - self._commissioning_complete_future = None - -@@ -505,7 +515,7 @@ class ChipDeviceControllerBase(): - self.devCtrl, nodeid) - ).raise_on_error() - -- def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid: int): -+ def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid: int) -> None: - self.CheckIsActive() - - self._pase_establishment_complete_future = concurrent.futures.Future() -@@ -516,16 +526,11 @@ class ChipDeviceControllerBase(): - self.devCtrl, setupPinCode, discriminator, nodeid) - ).raise_on_error() - -- # TODO: This is a bit funky, but what the API returned with the previous -- # implementation. We should revisit this. -- err = self._pase_establishment_complete_future.result() -- if not err.is_success: -- return err.to_exception() -- return None -+ self._pase_establishment_complete_future.result() - finally: - self._pase_establishment_complete_future = None - -- def EstablishPASESessionIP(self, ipaddr: str, setupPinCode: int, nodeid: int, port: int = 0): -+ def EstablishPASESessionIP(self, ipaddr: str, setupPinCode: int, nodeid: int, port: int = 0) -> None: - self.CheckIsActive() - - self._pase_establishment_complete_future = concurrent.futures.Future() -@@ -536,16 +541,11 @@ class ChipDeviceControllerBase(): - self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid, port) - ).raise_on_error() - -- # TODO: This is a bit funky, but what the API returned with the previous -- # implementation. We should revisit this. -- err = self._pase_establishment_complete_future.result() -- if not err.is_success: -- return err.to_exception() -- return None -+ self._pase_establishment_complete_future.result() - finally: - self._pase_establishment_complete_future = None - -- def EstablishPASESession(self, setUpCode: str, nodeid: int): -+ def EstablishPASESession(self, setUpCode: str, nodeid: int) -> None: - self.CheckIsActive() - - self._pase_establishment_complete_future = concurrent.futures.Future() -@@ -556,12 +556,7 @@ class ChipDeviceControllerBase(): - self.devCtrl, setUpCode.encode("utf-8"), nodeid) - ).raise_on_error() - -- # TODO: This is a bit funky, but what the API returned with the previous -- # implementation. We should revisit this. -- err = self._pase_establishment_complete_future.result() -- if not err.is_success: -- return err.to_exception() -- return None -+ self._pase_establishment_complete_future.result() - finally: - self._pase_establishment_complete_future = None - -@@ -1709,17 +1704,19 @@ class ChipDeviceController(ChipDeviceControllerBase): - def fabricAdmin(self) -> FabricAdmin: - return self._fabricAdmin - -- def Commission(self, nodeid) -> PyChipError: -+ def Commission(self, nodeid) -> int: - ''' - Start the auto-commissioning process on a node after establishing a PASE connection. - This function is intended to be used in conjunction with `EstablishPASESessionBLE` or - `EstablishPASESessionIP`. It can be called either before or after the DevicePairingDelegate - receives the OnPairingComplete call. Commissioners that want to perform simple -- auto-commissioning should use the supplied "PairDevice" functions above, which will -+ auto-commissioning should use the supplied "CommissionWithCode" function, which will - establish the PASE connection and commission automatically. - -- Return: -- bool: True if successful, False otherwise. -+ Raises a ChipStackError on failure. -+ -+ Returns: -+ - Effective Node ID of the device (as defined by the assigned NOC) - ''' - self.CheckIsActive() - -@@ -1735,13 +1732,13 @@ class ChipDeviceController(ChipDeviceControllerBase): - finally: - self._commissioning_complete_future = None - -- def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes, isShortDiscriminator: bool = False) -> PyChipError: -+ def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes, isShortDiscriminator: bool = False) -> int: - ''' Commissions a Thread device over BLE - ''' - self.SetThreadOperationalDataset(threadOperationalDataset) - return self.ConnectBLE(discriminator, setupPinCode, nodeId, isShortDiscriminator) - -- def CommissionWiFi(self, discriminator, setupPinCode, nodeId, ssid: str, credentials: str, isShortDiscriminator: bool = False) -> PyChipError: -+ def CommissionWiFi(self, discriminator, setupPinCode, nodeId, ssid: str, credentials: str, isShortDiscriminator: bool = False) -> int: - ''' Commissions a Wi-Fi device over BLE. - ''' - self.SetWiFiCredentials(ssid, credentials) -@@ -1812,7 +1809,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - return self.fabricCheckNodeId - - def CommissionOnNetwork(self, nodeId: int, setupPinCode: int, -- filterType: DiscoveryFilterType = DiscoveryFilterType.NONE, filter: typing.Any = None, discoveryTimeoutMsec: int = 30000) -> PyChipError: -+ filterType: DiscoveryFilterType = DiscoveryFilterType.NONE, filter: typing.Any = None, discoveryTimeoutMsec: int = 30000) -> int: - ''' - Does the routine for OnNetworkCommissioning, with a filter for mDNS discovery. - Supported filters are: -@@ -1828,6 +1825,11 @@ class ChipDeviceController(ChipDeviceControllerBase): - DiscoveryFilterType.COMPRESSED_FABRIC_ID - - The filter can be an integer, a string or None depending on the actual type of selected filter. -+ -+ Raises a ChipStackError on failure. -+ -+ Returns: -+ - Effective Node ID of the device (as defined by the assigned NOC) - ''' - self.CheckIsActive() - -@@ -1847,9 +1849,14 @@ class ChipDeviceController(ChipDeviceControllerBase): - finally: - self._commissioning_complete_future = None - -- def CommissionWithCode(self, setupPayload: str, nodeid: int, discoveryType: DiscoveryType = DiscoveryType.DISCOVERY_ALL) -> PyChipError: -+ def CommissionWithCode(self, setupPayload: str, nodeid: int, discoveryType: DiscoveryType = DiscoveryType.DISCOVERY_ALL) -> int: - ''' Commission with the given nodeid from the setupPayload. - setupPayload may be a QR or manual code. -+ -+ Raises a ChipStackError on failure. -+ -+ Returns: -+ - Effective Node ID of the device (as defined by the assigned NOC) - ''' - self.CheckIsActive() - -@@ -1866,8 +1873,14 @@ class ChipDeviceController(ChipDeviceControllerBase): - finally: - self._commissioning_complete_future = None - -- def CommissionIP(self, ipaddr: str, setupPinCode: int, nodeid: int) -> PyChipError: -- """ DEPRECATED, DO NOT USE! Use `CommissionOnNetwork` or `CommissionWithCode` """ -+ def CommissionIP(self, ipaddr: str, setupPinCode: int, nodeid: int) -> int: -+ """ DEPRECATED, DO NOT USE! Use `CommissionOnNetwork` or `CommissionWithCode` -+ -+ Raises a ChipStackError on failure. -+ -+ Returns: -+ - Effective Node ID of the device (as defined by the assigned NOC) -+ """ - self.CheckIsActive() - - self._commissioning_complete_future = concurrent.futures.Future() -diff --git a/src/controller/python/chip/yaml/runner.py b/src/controller/python/chip/yaml/runner.py -index fb7a71bd45..312940f34b 100644 ---- a/src/controller/python/chip/yaml/runner.py -+++ b/src/controller/python/chip/yaml/runner.py -@@ -664,10 +664,10 @@ class CommissionerCommandAction(BaseAction): - if self._command == 'GetCommissionerNodeId': - return _ActionResult(status=_ActionStatus.SUCCESS, response=_GetCommissionerNodeIdResult(dev_ctrl.nodeId)) - -- resp = dev_ctrl.CommissionWithCode(self._setup_payload, self._node_id) -- if resp: -+ try: -+ dev_ctrl.CommissionWithCode(self._setup_payload, self._node_id) - return _ActionResult(status=_ActionStatus.SUCCESS, response=None) -- else: -+ except ChipStackError: - return _ActionResult(status=_ActionStatus.ERROR, response=None) - - -diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py -index c9b1881dd7..83b1441ce1 100644 ---- a/src/controller/python/test/test_scripts/base.py -+++ b/src/controller/python/test/test_scripts/base.py -@@ -41,6 +41,7 @@ import chip.native - from chip import ChipDeviceCtrl - from chip.ChipStack import ChipStack - from chip.crypto import p256keypair -+from chip.exceptions import ChipStackException - from chip.utils import CommissioningBuildingBlocks - from cirque_restart_remote_device import restartRemoteDevice - from ecdsa import NIST256p -@@ -256,8 +257,9 @@ class BaseTestHelper: - devCtrl = self.devCtrl - self.logger.info( - "Attempting to establish PASE session with device id: {} addr: {}".format(str(nodeid), ip)) -- if devCtrl.EstablishPASESessionIP( -- ip, setuppin, nodeid) is not None: -+ try: -+ devCtrl.EstablishPASESessionIP(ip, setuppin, nodeid) -+ except ChipStackException: - self.logger.info( - "Failed to establish PASE session with device id: {} addr: {}".format(str(nodeid), ip)) - return False -@@ -268,7 +270,9 @@ class BaseTestHelper: - def TestCommissionOnly(self, nodeid: int): - self.logger.info( - "Commissioning device with id {}".format(nodeid)) -- if not self.devCtrl.Commission(nodeid): -+ try: -+ self.devCtrl.Commission(nodeid) -+ except ChipStackException: - self.logger.info( - "Failed to commission device with id {}".format(str(nodeid))) - return False -@@ -311,8 +315,10 @@ class BaseTestHelper: - - def TestCommissioning(self, ip: str, setuppin: int, nodeid: int): - self.logger.info("Commissioning device {}".format(ip)) -- if not self.devCtrl.CommissionIP(ip, setuppin, nodeid): -- self.logger.info( -+ try: -+ self.devCtrl.CommissionIP(ip, setuppin, nodeid) -+ except ChipStackException: -+ self.logger.exception( - "Failed to finish commissioning device {}".format(ip)) - return False - self.logger.info("Commissioning finished.") -@@ -320,8 +326,10 @@ class BaseTestHelper: - - def TestCommissioningWithSetupPayload(self, setupPayload: str, nodeid: int, discoveryType: int = 2): - self.logger.info("Commissioning device with setup payload {}".format(setupPayload)) -- if not self.devCtrl.CommissionWithCode(setupPayload, nodeid, chip.discovery.DiscoveryType(discoveryType)): -- self.logger.info( -+ try: -+ self.devCtrl.CommissionWithCode(setupPayload, nodeid, chip.discovery.DiscoveryType(discoveryType)) -+ except ChipStackException: -+ self.logger.exception( - "Failed to finish commissioning device {}".format(setupPayload)) - return False - self.logger.info("Commissioning finished.") -@@ -782,8 +790,10 @@ class BaseTestHelper: - self.devCtrl2 = self.fabricAdmin2.NewController( - self.controllerNodeId, self.paaTrustStorePath) - -- if not self.devCtrl2.CommissionIP(ip, setuppin, nodeid): -- self.logger.info( -+ try: -+ self.devCtrl2.CommissionIP(ip, setuppin, nodeid) -+ except ChipStackException: -+ self.logger.exception( - "Failed to finish key exchange with device {}".format(ip)) - return False - -diff --git a/src/python_testing/TC_ACE_1_5.py b/src/python_testing/TC_ACE_1_5.py -index 93c3fce5c5..bab70260fc 100644 ---- a/src/python_testing/TC_ACE_1_5.py -+++ b/src/python_testing/TC_ACE_1_5.py -@@ -54,10 +54,10 @@ class TC_ACE_1_5(MatterBaseTest): - params = self.openCommissioningWindow(self.th1, self.dut_node_id) - self.print_step(2, "TH1 opens the commissioning window on the DUT") - -- errcode = self.th2.CommissionOnNetwork( -+ self.th2.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.commissioningParameters.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=params.randomDiscriminator) -- logging.info('Commissioning complete done. Successful? {}, errorcode = {}'.format(errcode.is_success, errcode)) -+ logging.info('Commissioning complete done. Successful.') - self.print_step(3, "TH2 commissions DUT using admin node ID N2") - - self.print_step(4, "TH2 reads its fabric index from the Operational Credentials cluster CurrentFabricIndex attribute") -diff --git a/src/python_testing/TC_CGEN_2_4.py b/src/python_testing/TC_CGEN_2_4.py -index 23ab28ef09..a0333e8d9c 100644 ---- a/src/python_testing/TC_CGEN_2_4.py -+++ b/src/python_testing/TC_CGEN_2_4.py -@@ -25,6 +25,7 @@ import chip.clusters.enum - import chip.FabricAdmin - from chip import ChipDeviceCtrl - from chip.ChipDeviceCtrl import CommissioningParameters -+from chip.exceptions import ChipStackError - from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main - from mobly import asserts - -@@ -60,11 +61,12 @@ class TC_CGEN_2_4(MatterBaseTest): - # This will run the commissioning up to the point where stage x is run and the - # response is sent before the test commissioner simulates a failure - self.th2.SetTestCommissionerPrematureCompleteAfter(stage) -- errcode = self.th2.CommissionOnNetwork( -- nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, -- filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator) -- logging.info('Commissioning complete done. Successful? {}, errorcode = {}'.format(errcode.is_success, errcode)) -- asserts.assert_false(errcode.is_success, 'Commissioning complete did not error as expected') -+ ctx = asserts.assert_raises(ChipStackError) -+ with ctx: -+ self.th2.CommissionOnNetwork( -+ nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, -+ filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator) -+ errcode = ctx.exception.chip_error - asserts.assert_true(errcode.sdk_part == expectedErrorPart, 'Unexpected error type returned from CommissioningComplete') - asserts.assert_true(errcode.sdk_code == expectedErrCode, 'Unexpected error code returned from CommissioningComplete') - revokeCmd = Clusters.AdministratorCommissioning.Commands.RevokeCommissioning() -@@ -101,10 +103,14 @@ class TC_CGEN_2_4(MatterBaseTest): - - logging.info('Step 16 - TH2 fully commissions the DUT') - self.th2.ResetTestCommissioner() -- errcode = self.th2.CommissionOnNetwork( -- nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, -- filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator) -- logging.info('Commissioning complete done. Successful? {}, errorcode = {}'.format(errcode.is_success, errcode)) -+ -+ ctx = asserts.assert_raises(ChipStackError) -+ with ctx: -+ self.th2.CommissionOnNetwork( -+ nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, -+ filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator) -+ asserts.assert_true(ctx.exception.chip_error.sdk_code == 0x02, 'Unexpected error code returned from CommissioningComplete') -+ logging.info('Commissioning complete done.') - - logging.info('Step 17 - TH1 sends an arm failsafe') - cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=900, breadcrumb=0) -diff --git a/src/python_testing/TC_DA_1_5.py b/src/python_testing/TC_DA_1_5.py -index 567d757760..17c6e3c16d 100644 ---- a/src/python_testing/TC_DA_1_5.py -+++ b/src/python_testing/TC_DA_1_5.py -@@ -170,10 +170,9 @@ class TC_DA_1_5(MatterBaseTest): - new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=2) - TH2 = new_fabric_admin.NewController(nodeId=112233) - -- errcode = TH2.CommissionOnNetwork( -+ TH2.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234) -- asserts.assert_true(errcode.is_success, 'Commissioning on TH2 did not complete successfully') - - self.print_step(15, "Read NOCs list for TH1") - temp = await self.read_single_attribute_check_success( -diff --git a/src/python_testing/TC_TIMESYNC_2_13.py b/src/python_testing/TC_TIMESYNC_2_13.py -index ceabb23e5d..fa43bbd00c 100644 ---- a/src/python_testing/TC_TIMESYNC_2_13.py -+++ b/src/python_testing/TC_TIMESYNC_2_13.py -@@ -53,10 +53,9 @@ class TC_TIMESYNC_2_13(MatterBaseTest): - new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=2) - TH2 = new_fabric_admin.NewController(nodeId=112233) - -- errcode = TH2.CommissionOnNetwork( -+ TH2.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234) -- asserts.assert_true(errcode.is_success, 'Commissioning on TH2 did not complete successfully') - - self.print_step(3, "TH2 reads the current fabric") - th2_fabric_idx = await self.read_single_attribute_check_success( -diff --git a/src/python_testing/TestCommissioningTimeSync.py b/src/python_testing/TestCommissioningTimeSync.py -index 509aabfc6a..0fca7063fc 100644 ---- a/src/python_testing/TestCommissioningTimeSync.py -+++ b/src/python_testing/TestCommissioningTimeSync.py -@@ -58,10 +58,9 @@ class TestCommissioningTimeSync(MatterBaseTest): - async def commission_and_base_checks(self): - params = self.default_controller.OpenCommissioningWindow( - nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=1234, option=1) -- errcode = self.commissioner.CommissionOnNetwork( -+ self.commissioner.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234) -- asserts.assert_true(errcode.is_success, 'Commissioning did not complete successfully') - self.commissioned = True - - # Check the feature map - if we have a time cluster, we want UTC time to be set -diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py -index f38c116424..cd7fce8f08 100644 ---- a/src/python_testing/matter_testing_support.py -+++ b/src/python_testing/matter_testing_support.py -@@ -1581,35 +1581,55 @@ class CommissionDeviceTest(MatterBaseTest): - info.filter_value = conf.discriminators[i] - - if conf.commissioning_method == "on-network": -- return dev_ctrl.CommissionOnNetwork( -- nodeId=conf.dut_node_ids[i], -- setupPinCode=info.passcode, -- filterType=info.filter_type, -- filter=info.filter_value -- ) -+ try: -+ dev_ctrl.CommissionOnNetwork( -+ nodeId=conf.dut_node_ids[i], -+ setupPinCode=info.passcode, -+ filterType=info.filter_type, -+ filter=info.filter_value -+ ) -+ return True -+ except ChipStackError as e: -+ logging.error("Commissioning failed: %s" % e) -+ return False - elif conf.commissioning_method == "ble-wifi": -- return dev_ctrl.CommissionWiFi( -- info.filter_value, -- info.passcode, -- conf.dut_node_ids[i], -- conf.wifi_ssid, -- conf.wifi_passphrase, -- isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) -- ) -+ try: -+ dev_ctrl.CommissionWiFi( -+ info.filter_value, -+ info.passcode, -+ conf.dut_node_ids[i], -+ conf.wifi_ssid, -+ conf.wifi_passphrase, -+ isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) -+ ) -+ return True -+ except ChipStackError as e: -+ logging.error("Commissioning failed: %s" % e) -+ return False - elif conf.commissioning_method == "ble-thread": -- return dev_ctrl.CommissionThread( -- info.filter_value, -- info.passcode, -- conf.dut_node_ids[i], -- conf.thread_operational_dataset, -- isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) -- ) -+ try: -+ dev_ctrl.CommissionThread( -+ info.filter_value, -+ info.passcode, -+ conf.dut_node_ids[i], -+ conf.thread_operational_dataset, -+ isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) -+ ) -+ return True -+ except ChipStackError as e: -+ logging.error("Commissioning failed: %s" % e) -+ return False - elif conf.commissioning_method == "on-network-ip": -- logging.warning("==== USING A DIRECT IP COMMISSIONING METHOD NOT SUPPORTED IN THE LONG TERM ====") -- return dev_ctrl.CommissionIP( -- ipaddr=conf.commissionee_ip_address_just_for_testing, -- setupPinCode=info.passcode, nodeid=conf.dut_node_ids[i] -- ) -+ try: -+ logging.warning("==== USING A DIRECT IP COMMISSIONING METHOD NOT SUPPORTED IN THE LONG TERM ====") -+ dev_ctrl.CommissionIP( -+ ipaddr=conf.commissionee_ip_address_just_for_testing, -+ setupPinCode=info.passcode, nodeid=conf.dut_node_ids[i] -+ ) -+ return True -+ except ChipStackError as e: -+ logging.error("Commissioning failed: %s" % e) -+ return False - else: - raise ValueError("Invalid commissioning method %s!" % conf.commissioning_method) - -diff --git a/src/test_driver/openiotsdk/integration-tests/common/utils.py b/src/test_driver/openiotsdk/integration-tests/common/utils.py -index 1865cf6274..da2dcff787 100644 ---- a/src/test_driver/openiotsdk/integration-tests/common/utils.py -+++ b/src/test_driver/openiotsdk/integration-tests/common/utils.py -@@ -92,14 +92,11 @@ def connect_device(devCtrl, setupPayload, commissionableDevice, nodeId=None): - - pincode = int(setupPayload.attributes['SetUpPINCode']) - try: -- res = devCtrl.CommissionOnNetwork( -+ devCtrl.CommissionOnNetwork( - nodeId, pincode, filterType=discovery.FilterType.INSTANCE_NAME, filter=commissionableDevice.instanceName) - except exceptions.ChipStackError as ex: - log.error("Commission discovered device failed {}".format(str(ex))) - return None -- if not res: -- log.info("Commission discovered device failed: %r" % res) -- return None - return nodeId - - --- -2.45.2 - diff --git a/0028-Python-Reset-chip-error-in-test-commissioner-34001.patch b/0028-Python-Reset-chip-error-in-test-commissioner-34001.patch deleted file mode 100644 index 17ccbc2..0000000 --- a/0028-Python-Reset-chip-error-in-test-commissioner-34001.patch +++ /dev/null @@ -1,57 +0,0 @@ -From f17afe57391c7346af140fcc8cf7442f6f11f781 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Wed, 19 Jun 2024 16:26:32 +0200 -Subject: [PATCH] [Python] Reset chip error in test commissioner (#34001) - -* [Python] Reset chip error in test commissioner - -Make sure to reset the chip error in test commissioner on reset. This -avoid spurious errors. The Python side reads the error as soon as -mTestCommissionerUsed is set, which happens unconditionally. Hence -clearing the error is necessary. - -* [Python] Remove unexpected exception in TC_CGEN_2_4.py - -With the test commissioner properly resetting the error code the -spurious exception is no longer thrown. Remove the exception handling -from the test. ---- - src/controller/python/OpCredsBinding.cpp | 1 + - src/python_testing/TC_CGEN_2_4.py | 9 +++------ - 2 files changed, 4 insertions(+), 6 deletions(-) - -diff --git a/src/controller/python/OpCredsBinding.cpp b/src/controller/python/OpCredsBinding.cpp -index 66a6c84184..692e5e0ba3 100644 ---- a/src/controller/python/OpCredsBinding.cpp -+++ b/src/controller/python/OpCredsBinding.cpp -@@ -257,6 +257,7 @@ public: - mPrematureCompleteAfter = chip::Controller::CommissioningStage::kError; - mReadCommissioningInfo = chip::Controller::ReadCommissioningInfo(); - mNeedsDST = false; -+ mCompletionError = CHIP_NO_ERROR; - } - bool GetTestCommissionerUsed() { return mTestCommissionerUsed; } - void OnCommissioningSuccess(chip::PeerId peerId) { mReceivedCommissioningSuccess = true; } -diff --git a/src/python_testing/TC_CGEN_2_4.py b/src/python_testing/TC_CGEN_2_4.py -index a0333e8d9c..50bd9c005d 100644 ---- a/src/python_testing/TC_CGEN_2_4.py -+++ b/src/python_testing/TC_CGEN_2_4.py -@@ -104,12 +104,9 @@ class TC_CGEN_2_4(MatterBaseTest): - logging.info('Step 16 - TH2 fully commissions the DUT') - self.th2.ResetTestCommissioner() - -- ctx = asserts.assert_raises(ChipStackError) -- with ctx: -- self.th2.CommissionOnNetwork( -- nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, -- filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator) -- asserts.assert_true(ctx.exception.chip_error.sdk_code == 0x02, 'Unexpected error code returned from CommissioningComplete') -+ self.th2.CommissionOnNetwork( -+ nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, -+ filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator) - logging.info('Commissioning complete done.') - - logging.info('Step 17 - TH1 sends an arm failsafe') --- -2.45.2 - diff --git a/0029-Python-Convert-async-API-functions-to-python-asyncio.patch b/0029-Python-Convert-async-API-functions-to-python-asyncio.patch deleted file mode 100644 index aa8c1f8..0000000 --- a/0029-Python-Convert-async-API-functions-to-python-asyncio.patch +++ /dev/null @@ -1,1765 +0,0 @@ -From a574e6a55ed3fa2dfe14f4f2475b2623f5b10cb0 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 20 Jun 2024 16:14:52 +0200 -Subject: [PATCH] [Python] Convert async API functions to python asyncio - (#33989) - -* [Python] Use context manager for Commissioning - -Use a context manager to handle the commissioning process in the -device controller. This will ensure that the commissioning resources -are properly cleaned up after completion and removes boiler plate -code. - -Also clear fabricCheckNodeId and mark it internal use by adding the -underline prefix. - -Also call pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete -directly on the Python Thread, as this is an atomic operation. This is -will also be more asyncio friendly as it is guaranteed to not block. - -* [Python] Use context manager for all callbacks - -Use context managers for all APIs which wait for callbacks. This -allows to cleanly wrap the future and add additional handling e.g. -locks for asyncio in the future. - -* [Python] Convert commissioning APIs to async functions - -Make all commissioning APIs async functions. This avoids the need -to use run_in_executor() to call them from asyncio code in a non- -blocking way. - -* [Python] Convert UnpairDevice/OpenCommissioningWindow to asyncio - -* [Python] Convert EstablishPASESession to asyncio - -* [Python] Convert IssueNOCChain to asyncio - -* [Python] Add locking to prevent concurrent access with asyncio - -Make sure that different asyncio tasks do not run the same function -concurrently. This is done by adding an asyncio lock to functions -which use callbacks. - -* [Python] Raise an exception if the future did not complete - -* [Python] Convert tests in src/controller/python/ to asyncio - -* [Python] Convert tests in src/python_testing/ to asyncio - -* Adjust yamltest_with_chip_repl_tester to use asyncio - -* [Python] Add documentation to the new context managers - -* [Python] Use asyncio.run() to run async tests ---- - .../yamltest_with_chip_repl_tester.py | 8 +- - src/controller/python/chip/ChipDeviceCtrl.py | 300 +++++++++--------- - .../python/chip/commissioning/pase.py | 6 +- - .../chip/utils/CommissioningBuildingBlocks.py | 4 +- - src/controller/python/chip/yaml/runner.py | 2 +- - .../matter_yamltest_repl_adapter/runner.py | 2 +- - .../python/test/test_scripts/base.py | 48 +-- - .../commissioning_failure_test.py | 32 +- - .../test/test_scripts/commissioning_test.py | 20 +- - .../test_scripts/commissioning_window_test.py | 6 +- - .../example_python_commissioning_flow.py | 4 +- - .../test/test_scripts/failsafe_tests.py | 12 +- - .../test/test_scripts/mobile-device-test.py | 26 +- - .../python_commissioning_flow_test.py | 2 +- - .../test_scripts/split_commissioning_test.py | 30 +- - ...cription_resumption_capacity_test_ctrl1.py | 12 +- - ...cription_resumption_capacity_test_ctrl2.py | 13 +- - .../subscription_resumption_test.py | 12 +- - .../subscription_resumption_timeout_test.py | 12 +- - src/python_testing/TC_ACE_1_5.py | 4 +- - src/python_testing/TC_CGEN_2_4.py | 12 +- - src/python_testing/TC_DA_1_5.py | 4 +- - src/python_testing/TC_IDM_1_2.py | 10 +- - src/python_testing/TC_OPCREDS_3_1.py | 32 +- - src/python_testing/TC_TIMESYNC_2_13.py | 4 +- - .../TestCommissioningTimeSync.py | 4 +- - .../basic_composition_support.py | 2 +- - src/python_testing/matter_testing_support.py | 18 +- - 28 files changed, 325 insertions(+), 316 deletions(-) - -diff --git a/scripts/tests/chiptest/yamltest_with_chip_repl_tester.py b/scripts/tests/chiptest/yamltest_with_chip_repl_tester.py -index 484e21370e..1b301c5728 100644 ---- a/scripts/tests/chiptest/yamltest_with_chip_repl_tester.py -+++ b/scripts/tests/chiptest/yamltest_with_chip_repl_tester.py -@@ -101,7 +101,7 @@ async def execute_test(yaml, runner): - '--pics-file', - default=None, - help='Optional PICS file') --def main(setup_code, yaml_path, node_id, pics_file): -+async def main(setup_code, yaml_path, node_id, pics_file): - # Setting up python environment for running YAML CI tests using python parser. - with tempfile.NamedTemporaryFile() as chip_stack_storage: - chip.native.Init() -@@ -122,7 +122,7 @@ def main(setup_code, yaml_path, node_id, pics_file): - # Creating and commissioning to a single controller to match what is currently done when - # running. - dev_ctrl = ca_list[0].adminList[0].NewController() -- dev_ctrl.CommissionWithCode(setup_code, node_id) -+ await dev_ctrl.CommissionWithCode(setup_code, node_id) - - def _StackShutDown(): - # Tearing down chip stack. If not done in the correct order test will fail. -@@ -143,7 +143,7 @@ def main(setup_code, yaml_path, node_id, pics_file): - runner = ReplTestRunner( - clusters_definitions, certificate_authority_manager, dev_ctrl) - -- asyncio.run(execute_test(yaml, runner)) -+ await execute_test(yaml, runner) - - except Exception: - print(traceback.format_exc()) -@@ -153,4 +153,4 @@ def main(setup_code, yaml_path, node_id, pics_file): - - - if __name__ == '__main__': -- main() -+ asyncio.run(main()) -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 5c1790c087..1c156c22d4 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -139,6 +139,52 @@ def _singleton(cls): - return wrapper - - -+class CallbackContext: -+ """A context manager for handling callbacks that are expected to be called exactly once. -+ -+ The context manager makes sure that no concurrent operations which use the same callback -+ handlers are executed. -+ """ -+ -+ def __init__(self, lock: asyncio.Lock) -> None: -+ self._lock = lock -+ self._future = None -+ -+ async def __aenter__(self): -+ await self._lock.acquire() -+ self._future = concurrent.futures.Future() -+ return self -+ -+ @property -+ def future(self) -> typing.Optional[concurrent.futures.Future]: -+ return self._future -+ -+ async def __aexit__(self, exc_type, exc_value, traceback): -+ if not self._future.done(): -+ raise RuntimeError("CallbackContext future not completed") -+ self._future = None -+ self._lock.release() -+ -+ -+class CommissioningContext(CallbackContext): -+ """A context manager for handling commissioning callbacks that are expected to be called exactly once. -+ -+ This context also resets commissioning related device controller state. -+ """ -+ -+ def __init__(self, devCtrl: ChipDeviceController, lock: asyncio.Lock) -> None: -+ super().__init__(lock) -+ self._devCtrl = devCtrl -+ -+ async def __aenter__(self): -+ await super().__aenter__() -+ self._devCtrl._fabricCheckNodeId = -1 -+ return self -+ -+ async def __aexit__(self, exc_type, exc_value, traceback): -+ await super().__aexit__(exc_type, exc_value, traceback) -+ -+ - class CommissionableNode(discovery.CommissionableNode): - def SetDeviceController(self, devCtrl: 'ChipDeviceController'): - self._devCtrl = devCtrl -@@ -250,15 +296,16 @@ class ChipDeviceControllerBase(): - self.pairingDelegate = pairingDelegate - self.devCtrl = devCtrl - self.name = name -- self.fabricCheckNodeId = -1 -+ self._fabricCheckNodeId = -1 - self._isActive = False - - self._Cluster = ChipClusters(builtins.chipStack) - self._Cluster.InitLib(self._dmLib) -- self._commissioning_complete_future: typing.Optional[concurrent.futures.Future] = None -- self._open_window_complete_future: typing.Optional[concurrent.futures.Future] = None -- self._unpair_device_complete_future: typing.Optional[concurrent.futures.Future] = None -- self._pase_establishment_complete_future: typing.Optional[concurrent.futures.Future] = None -+ self._commissioning_lock: asyncio.Lock = asyncio.Lock() -+ self._commissioning_context: CommissioningContext = CommissioningContext(self, self._commissioning_lock) -+ self._open_window_context: CallbackContext = CallbackContext(asyncio.Lock()) -+ self._unpair_device_context: CallbackContext = CallbackContext(asyncio.Lock()) -+ self._pase_establishment_context: CallbackContext = CallbackContext(self._commissioning_lock) - - def _set_dev_ctrl(self, devCtrl, pairingDelegate): - def HandleCommissioningComplete(nodeId: int, err: PyChipError): -@@ -270,17 +317,17 @@ class ChipDeviceControllerBase(): - if self._dmLib.pychip_TestCommissionerUsed(): - err = self._dmLib.pychip_GetCompletionError() - -- if self._commissioning_complete_future is None: -+ if self._commissioning_context.future is None: - logging.exception("HandleCommissioningComplete called unexpectedly") - return - - if err.is_success: -- self._commissioning_complete_future.set_result(nodeId) -+ self._commissioning_context.future.set_result(nodeId) - else: -- self._commissioning_complete_future.set_exception(err.to_exception()) -+ self._commissioning_context.future.set_exception(err.to_exception()) - - def HandleFabricCheck(nodeId): -- self.fabricCheckNodeId = nodeId -+ self._fabricCheckNodeId = nodeId - - def HandleOpenWindowComplete(nodeid: int, setupPinCode: int, setupManualCode: str, - setupQRCode: str, err: PyChipError) -> None: -@@ -291,14 +338,14 @@ class ChipDeviceControllerBase(): - else: - logging.warning("Failed to open commissioning window: {}".format(err)) - -- if self._open_window_complete_future is None: -+ if self._open_window_context.future is None: - logging.exception("HandleOpenWindowComplete called unexpectedly") - return - - if err.is_success: -- self._open_window_complete_future.set_result(commissioningParameters) -+ self._open_window_context.future.set_result(commissioningParameters) - else: -- self._open_window_complete_future.set_exception(err.to_exception()) -+ self._open_window_context.future.set_exception(err.to_exception()) - - def HandleUnpairDeviceComplete(nodeid: int, err: PyChipError): - if err.is_success: -@@ -306,14 +353,14 @@ class ChipDeviceControllerBase(): - else: - logging.warning("Failed to unpair device: {}".format(err)) - -- if self._unpair_device_complete_future is None: -+ if self._unpair_device_context.future is None: - logging.exception("HandleUnpairDeviceComplete called unexpectedly") - return - - if err.is_success: -- self._unpair_device_complete_future.set_result(None) -+ self._unpair_device_context.future.set_result(None) - else: -- self._unpair_device_complete_future.set_exception(err.to_exception()) -+ self._unpair_device_context.future.set_exception(err.to_exception()) - - def HandlePASEEstablishmentComplete(err: PyChipError): - if not err.is_success: -@@ -321,21 +368,21 @@ class ChipDeviceControllerBase(): - else: - logging.info("Established secure session with Device") - -- if self._commissioning_complete_future is not None: -+ if self._commissioning_context.future is not None: - # During Commissioning, HandlePASEEstablishmentComplete will also be called. - # Only complete the future if PASE session establishment failed. - if not err.is_success: -- self._commissioning_complete_future.set_exception(err.to_exception()) -+ self._commissioning_context.future.set_exception(err.to_exception()) - return - -- if self._pase_establishment_complete_future is None: -+ if self._pase_establishment_context.future is None: - logging.exception("HandlePASEEstablishmentComplete called unexpectedly") - return - - if err.is_success: -- self._pase_establishment_complete_future.set_result(None) -+ self._pase_establishment_context.future.set_result(None) - else: -- self._pase_establishment_complete_future.set_exception(err.to_exception()) -+ self._pase_establishment_context.future.set_exception(err.to_exception()) - - self.pairingDelegate = pairingDelegate - self.devCtrl = devCtrl -@@ -370,10 +417,8 @@ class ChipDeviceControllerBase(): - - ChipDeviceController.activeList.add(self) - -- def _enablePairingCompeleteCallback(self, value: bool): -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(self.pairingDelegate, value) -- ).raise_on_error() -+ def _enablePairingCompleteCallback(self, value: bool): -+ self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(self.pairingDelegate, value) - - @property - def fabricAdmin(self) -> FabricAdmin.FabricAdmin: -@@ -452,7 +497,7 @@ class ChipDeviceControllerBase(): - self.devCtrl) - ) - -- def ConnectBLE(self, discriminator: int, setupPinCode: int, nodeid: int, isShortDiscriminator: bool = False) -> int: -+ async def ConnectBLE(self, discriminator: int, setupPinCode: int, nodeid: int, isShortDiscriminator: bool = False) -> int: - """Connect to a BLE device using the given discriminator and setup pin code. - - Returns: -@@ -460,31 +505,26 @@ class ChipDeviceControllerBase(): - """ - self.CheckIsActive() - -- self._commissioning_complete_future = concurrent.futures.Future() -- -- try: -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.Call( -+ async with self._commissioning_context as ctx: -+ self._enablePairingCompleteCallback(True) -+ res = await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectBLE( - self.devCtrl, discriminator, isShortDiscriminator, setupPinCode, nodeid) -- ).raise_on_error() -+ ) -+ res.raise_on_error() - -- return self._commissioning_complete_future.result() -- finally: -- self._commissioning_complete_future = None -+ return await asyncio.futures.wrap_future(ctx.future) - -- def UnpairDevice(self, nodeid: int) -> None: -+ async def UnpairDevice(self, nodeid: int) -> None: - self.CheckIsActive() - -- self._unpair_device_complete_future = concurrent.futures.Future() -- try: -- self._ChipStack.Call( -+ async with self._unpair_device_context as ctx: -+ res = await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_UnpairDevice( - self.devCtrl, nodeid, self.cbHandleDeviceUnpairCompleteFunct) -- ).raise_on_error() -- self._unpair_device_complete_future.result() -- finally: -- self._unpair_device_complete_future = None -+ ) -+ res.raise_on_error() -+ return await asyncio.futures.wrap_future(ctx.future) - - def CloseBLEConnection(self): - self.CheckIsActive() -@@ -515,50 +555,32 @@ class ChipDeviceControllerBase(): - self.devCtrl, nodeid) - ).raise_on_error() - -- def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid: int) -> None: -+ async def _establishPASESession(self, callFunct): - self.CheckIsActive() - -- self._pase_establishment_complete_future = concurrent.futures.Future() -- try: -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionBLE( -- self.devCtrl, setupPinCode, discriminator, nodeid) -- ).raise_on_error() -- -- self._pase_establishment_complete_future.result() -- finally: -- self._pase_establishment_complete_future = None -- -- def EstablishPASESessionIP(self, ipaddr: str, setupPinCode: int, nodeid: int, port: int = 0) -> None: -- self.CheckIsActive() -+ async with self._pase_establishment_context as ctx: -+ self._enablePairingCompleteCallback(True) -+ res = await self._ChipStack.CallAsync(callFunct) -+ res.raise_on_error() -+ await asyncio.futures.wrap_future(ctx.future) - -- self._pase_establishment_complete_future = concurrent.futures.Future() -- try: -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionIP( -- self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid, port) -- ).raise_on_error() -- -- self._pase_establishment_complete_future.result() -- finally: -- self._pase_establishment_complete_future = None -- -- def EstablishPASESession(self, setUpCode: str, nodeid: int) -> None: -- self.CheckIsActive() -+ async def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid: int) -> None: -+ await self._establishPASESession( -+ lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionBLE( -+ self.devCtrl, setupPinCode, discriminator, nodeid) -+ ) - -- self._pase_establishment_complete_future = concurrent.futures.Future() -- try: -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_EstablishPASESession( -- self.devCtrl, setUpCode.encode("utf-8"), nodeid) -- ).raise_on_error() -+ async def EstablishPASESessionIP(self, ipaddr: str, setupPinCode: int, nodeid: int, port: int = 0) -> None: -+ await self._establishPASESession( -+ lambda: self._dmLib.pychip_DeviceController_EstablishPASESessionIP( -+ self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid, port) -+ ) - -- self._pase_establishment_complete_future.result() -- finally: -- self._pase_establishment_complete_future = None -+ async def EstablishPASESession(self, setUpCode: str, nodeid: int) -> None: -+ await self._establishPASESession( -+ lambda: self._dmLib.pychip_DeviceController_EstablishPASESession( -+ self.devCtrl, setUpCode.encode("utf-8"), nodeid) -+ ) - - def GetTestCommissionerUsed(self): - return self._ChipStack.Call( -@@ -683,8 +705,8 @@ class ChipDeviceControllerBase(): - kOriginalSetupCode = 0, - kTokenWithRandomPin = 1, - -- def OpenCommissioningWindow(self, nodeid: int, timeout: int, iteration: int, -- discriminator: int, option: CommissioningWindowPasscode) -> CommissioningParameters: -+ async def OpenCommissioningWindow(self, nodeid: int, timeout: int, iteration: int, -+ discriminator: int, option: CommissioningWindowPasscode) -> CommissioningParameters: - ''' Opens a commissioning window on the device with the given nodeid. - nodeid: Node id of the device - timeout: Command timeout -@@ -699,16 +721,15 @@ class ChipDeviceControllerBase(): - Returns CommissioningParameters - ''' - self.CheckIsActive() -- self._open_window_complete_future = concurrent.futures.Future() -- try: -- self._ChipStack.Call( -+ -+ async with self._open_window_context as ctx: -+ res = await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_OpenCommissioningWindow( - self.devCtrl, self.pairingDelegate, nodeid, timeout, iteration, discriminator, option) -- ).raise_on_error() -+ ) -+ res.raise_on_error() - -- return self._open_window_complete_future.result() -- finally: -- self._open_window_complete_future = None -+ return await asyncio.futures.wrap_future(ctx.future) - - def GetCompressedFabricId(self): - self.CheckIsActive() -@@ -1666,7 +1687,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - f"caIndex({fabricAdmin.caIndex:x})/fabricId(0x{fabricId:016X})/nodeId(0x{nodeId:016X})" - ) - -- self._issue_node_chain_complete: typing.Optional[concurrent.futures.Future] = None -+ self._issue_node_chain_context: CallbackContext = CallbackContext(asyncio.Lock()) - self._dmLib.pychip_DeviceController_SetIssueNOCChainCallbackPythonCallback(_IssueNOCChainCallbackPythonCallback) - - pairingDelegate = c_void_p(None) -@@ -1704,7 +1725,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - def fabricAdmin(self) -> FabricAdmin: - return self._fabricAdmin - -- def Commission(self, nodeid) -> int: -+ async def Commission(self, nodeid) -> int: - ''' - Start the auto-commissioning process on a node after establishing a PASE connection. - This function is intended to be used in conjunction with `EstablishPASESessionBLE` or -@@ -1720,29 +1741,27 @@ class ChipDeviceController(ChipDeviceControllerBase): - ''' - self.CheckIsActive() - -- self._commissioning_complete_future = concurrent.futures.Future() -- -- try: -- self._ChipStack.Call( -+ async with self._commissioning_context as ctx: -+ self._enablePairingCompleteCallback(False) -+ res = await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_Commission( - self.devCtrl, nodeid) -- ).raise_on_error() -+ ) -+ res.raise_on_error() - -- return self._commissioning_complete_future.result() -- finally: -- self._commissioning_complete_future = None -+ return await asyncio.futures.wrap_future(ctx.future) - -- def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes, isShortDiscriminator: bool = False) -> int: -+ async def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes, isShortDiscriminator: bool = False) -> int: - ''' Commissions a Thread device over BLE - ''' - self.SetThreadOperationalDataset(threadOperationalDataset) -- return self.ConnectBLE(discriminator, setupPinCode, nodeId, isShortDiscriminator) -+ return await self.ConnectBLE(discriminator, setupPinCode, nodeId, isShortDiscriminator) - -- def CommissionWiFi(self, discriminator, setupPinCode, nodeId, ssid: str, credentials: str, isShortDiscriminator: bool = False) -> int: -+ async def CommissionWiFi(self, discriminator, setupPinCode, nodeId, ssid: str, credentials: str, isShortDiscriminator: bool = False) -> int: - ''' Commissions a Wi-Fi device over BLE. - ''' - self.SetWiFiCredentials(ssid, credentials) -- return self.ConnectBLE(discriminator, setupPinCode, nodeId, isShortDiscriminator) -+ return await self.ConnectBLE(discriminator, setupPinCode, nodeId, isShortDiscriminator) - - def SetWiFiCredentials(self, ssid: str, credentials: str): - ''' Set the Wi-Fi credentials to set during commissioning.''' -@@ -1806,10 +1825,11 @@ class ChipDeviceController(ChipDeviceControllerBase): - - def GetFabricCheckResult(self) -> int: - ''' Returns the fabric check result if SetCheckMatchingFabric was used.''' -- return self.fabricCheckNodeId -+ return self._fabricCheckNodeId - -- def CommissionOnNetwork(self, nodeId: int, setupPinCode: int, -- filterType: DiscoveryFilterType = DiscoveryFilterType.NONE, filter: typing.Any = None, discoveryTimeoutMsec: int = 30000) -> int: -+ async def CommissionOnNetwork(self, nodeId: int, setupPinCode: int, -+ filterType: DiscoveryFilterType = DiscoveryFilterType.NONE, filter: typing.Any = None, -+ discoveryTimeoutMsec: int = 30000) -> int: - ''' - Does the routine for OnNetworkCommissioning, with a filter for mDNS discovery. - Supported filters are: -@@ -1837,19 +1857,17 @@ class ChipDeviceController(ChipDeviceControllerBase): - if isinstance(filter, int): - filter = str(filter) - -- self._commissioning_complete_future = concurrent.futures.Future() -- try: -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.Call( -+ async with self._commissioning_context as ctx: -+ self._enablePairingCompleteCallback(True) -+ res = await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_OnNetworkCommission( - self.devCtrl, self.pairingDelegate, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") if filter is not None else None, discoveryTimeoutMsec) -- ).raise_on_error() -+ ) -+ res.raise_on_error() - -- return self._commissioning_complete_future.result() -- finally: -- self._commissioning_complete_future = None -+ return await asyncio.futures.wrap_future(ctx.future) - -- def CommissionWithCode(self, setupPayload: str, nodeid: int, discoveryType: DiscoveryType = DiscoveryType.DISCOVERY_ALL) -> int: -+ async def CommissionWithCode(self, setupPayload: str, nodeid: int, discoveryType: DiscoveryType = DiscoveryType.DISCOVERY_ALL) -> int: - ''' Commission with the given nodeid from the setupPayload. - setupPayload may be a QR or manual code. - -@@ -1860,20 +1878,17 @@ class ChipDeviceController(ChipDeviceControllerBase): - ''' - self.CheckIsActive() - -- self._commissioning_complete_future = concurrent.futures.Future() -- -- try: -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.Call( -+ async with self._commissioning_context as ctx: -+ self._enablePairingCompleteCallback(True) -+ res = await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectWithCode( - self.devCtrl, setupPayload.encode("utf-8"), nodeid, discoveryType.value) -- ).raise_on_error() -+ ) -+ res.raise_on_error() - -- return self._commissioning_complete_future.result() -- finally: -- self._commissioning_complete_future = None -+ return await asyncio.futures.wrap_future(ctx.future) - -- def CommissionIP(self, ipaddr: str, setupPinCode: int, nodeid: int) -> int: -+ async def CommissionIP(self, ipaddr: str, setupPinCode: int, nodeid: int) -> int: - """ DEPRECATED, DO NOT USE! Use `CommissionOnNetwork` or `CommissionWithCode` - - Raises a ChipStackError on failure. -@@ -1883,40 +1898,35 @@ class ChipDeviceController(ChipDeviceControllerBase): - """ - self.CheckIsActive() - -- self._commissioning_complete_future = concurrent.futures.Future() -- -- try: -- self._enablePairingCompeleteCallback(True) -- self._ChipStack.Call( -+ async with self._commissioning_context as ctx: -+ self._enablePairingCompleteCallback(True) -+ res = await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectIP( - self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid) -- ).raise_on_error() -+ ) -+ res.raise_on_error() - -- return self._commissioning_complete_future.result() -- finally: -- self._commissioning_complete_future = None -+ return await asyncio.futures.wrap_future(ctx.future) - - def NOCChainCallback(self, nocChain): -- if self._issue_node_chain_complete is None: -+ if self._issue_node_chain_context.future is None: - logging.exception("NOCChainCallback while not expecting a callback") - return -- self._issue_node_chain_complete.set_result(nocChain) -+ self._issue_node_chain_context.future.set_result(nocChain) - return - -- def IssueNOCChain(self, csr: Clusters.OperationalCredentials.Commands.CSRResponse, nodeId: int): -+ async def IssueNOCChain(self, csr: Clusters.OperationalCredentials.Commands.CSRResponse, nodeId: int): - """Issue an NOC chain using the associated OperationalCredentialsDelegate. - The NOC chain will be provided in TLV cert format.""" - self.CheckIsActive() - -- self._issue_node_chain_complete = concurrent.futures.Future() -- try: -- self._ChipStack.Call( -+ async with self._issue_node_chain_context as ctx: -+ res = await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_IssueNOCChain( - self.devCtrl, py_object(self), csr.NOCSRElements, len(csr.NOCSRElements), nodeId) -- ).raise_on_error() -- return self._issue_node_chain_complete.result() -- finally: -- self._issue_node_chain_complete = None -+ ) -+ res.raise_on_error() -+ return await asyncio.futures.wrap_future(ctx.future) - - - class BareChipDeviceController(ChipDeviceControllerBase): -diff --git a/src/controller/python/chip/commissioning/pase.py b/src/controller/python/chip/commissioning/pase.py -index 9b7e8c5077..c0cfca5ee8 100644 ---- a/src/controller/python/chip/commissioning/pase.py -+++ b/src/controller/python/chip/commissioning/pase.py -@@ -44,9 +44,9 @@ class ContextManager: - self.devCtrl.CloseBLEConnection(self.is_ble) - - --def establish_session(devCtrl: ChipDeviceCtrl.ChipDeviceControllerBase, parameter: commissioning.PaseParameters) -> ContextManager: -+async def establish_session(devCtrl: ChipDeviceCtrl.ChipDeviceControllerBase, parameter: commissioning.PaseParameters) -> ContextManager: - if isinstance(parameter, commissioning.PaseOverBLEParameters): -- devCtrl.EstablishPASESessionBLE(parameter.setup_pin, parameter.discriminator, parameter.temporary_nodeid) -+ await devCtrl.EstablishPASESessionBLE(parameter.setup_pin, parameter.discriminator, parameter.temporary_nodeid) - elif isinstance(parameter, commissioning.PaseOverIPParameters): - device = devCtrl.DiscoverCommissionableNodes(filterType=discovery.FilterType.LONG_DISCRIMINATOR, - filter=parameter.long_discriminator, stopOnFirst=True) -@@ -63,7 +63,7 @@ def establish_session(devCtrl: ChipDeviceCtrl.ChipDeviceControllerBase, paramete - break - if selected_address is None: - raise ValueError("The node for commissioning does not contains routable ip addresses information") -- devCtrl.EstablishPASESessionIP(selected_address, parameter.setup_pin, parameter.temporary_nodeid) -+ await devCtrl.EstablishPASESessionIP(selected_address, parameter.setup_pin, parameter.temporary_nodeid) - else: - raise TypeError("Expect PaseOverBLEParameters or PaseOverIPParameters for establishing PASE session") - return ContextManager( -diff --git a/src/controller/python/chip/utils/CommissioningBuildingBlocks.py b/src/controller/python/chip/utils/CommissioningBuildingBlocks.py -index 8fa80223b1..41d211b2d4 100644 ---- a/src/controller/python/chip/utils/CommissioningBuildingBlocks.py -+++ b/src/controller/python/chip/utils/CommissioningBuildingBlocks.py -@@ -165,7 +165,7 @@ async def AddNOCForNewFabricFromExisting(commissionerDevCtrl, newFabricDevCtrl, - - csrForAddNOC = await commissionerDevCtrl.SendCommand(existingNodeId, 0, opCreds.Commands.CSRRequest(CSRNonce=os.urandom(32))) - -- chainForAddNOC = newFabricDevCtrl.IssueNOCChain(csrForAddNOC, newNodeId) -+ chainForAddNOC = await newFabricDevCtrl.IssueNOCChain(csrForAddNOC, newNodeId) - if (chainForAddNOC.rcacBytes is None or - chainForAddNOC.icacBytes is None or - chainForAddNOC.nocBytes is None or chainForAddNOC.ipkBytes is None): -@@ -219,7 +219,7 @@ async def UpdateNOC(devCtrl, existingNodeId, newNodeId): - return False - csrForUpdateNOC = await devCtrl.SendCommand( - existingNodeId, 0, opCreds.Commands.CSRRequest(CSRNonce=os.urandom(32), isForUpdateNOC=True)) -- chainForUpdateNOC = devCtrl.IssueNOCChain(csrForUpdateNOC, newNodeId) -+ chainForUpdateNOC = await devCtrl.IssueNOCChain(csrForUpdateNOC, newNodeId) - if (chainForUpdateNOC.rcacBytes is None or - chainForUpdateNOC.icacBytes is None or - chainForUpdateNOC.nocBytes is None or chainForUpdateNOC.ipkBytes is None): -diff --git a/src/controller/python/chip/yaml/runner.py b/src/controller/python/chip/yaml/runner.py -index 312940f34b..f0b681fb2b 100644 ---- a/src/controller/python/chip/yaml/runner.py -+++ b/src/controller/python/chip/yaml/runner.py -@@ -665,7 +665,7 @@ class CommissionerCommandAction(BaseAction): - return _ActionResult(status=_ActionStatus.SUCCESS, response=_GetCommissionerNodeIdResult(dev_ctrl.nodeId)) - - try: -- dev_ctrl.CommissionWithCode(self._setup_payload, self._node_id) -+ await dev_ctrl.CommissionWithCode(self._setup_payload, self._node_id) - return _ActionResult(status=_ActionStatus.SUCCESS, response=None) - except ChipStackError: - return _ActionResult(status=_ActionStatus.ERROR, response=None) -diff --git a/src/controller/python/py_matter_yamltest_repl_adapter/matter_yamltest_repl_adapter/runner.py b/src/controller/python/py_matter_yamltest_repl_adapter/matter_yamltest_repl_adapter/runner.py -index aecba0b2c4..5da5035ae0 100644 ---- a/src/controller/python/py_matter_yamltest_repl_adapter/matter_yamltest_repl_adapter/runner.py -+++ b/src/controller/python/py_matter_yamltest_repl_adapter/matter_yamltest_repl_adapter/runner.py -@@ -59,7 +59,7 @@ class Runner(TestRunner): - # device with the provided node id. - if self._node_id_to_commission is not None: - # Magic value is the defaults expected for YAML tests. -- dev_ctrl.CommissionWithCode('MT:-24J0AFN00KA0648G00', self._node_id_to_commission) -+ await dev_ctrl.CommissionWithCode('MT:-24J0AFN00KA0648G00', self._node_id_to_commission) - - self._chip_stack = chip_stack - self._certificate_authority_manager = certificate_authority_manager -diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py -index 83b1441ce1..84a403643d 100644 ---- a/src/controller/python/test/test_scripts/base.py -+++ b/src/controller/python/test/test_scripts/base.py -@@ -234,7 +234,7 @@ class BaseTestHelper: - async def TestRevokeCommissioningWindow(self, ip: str, setuppin: int, nodeid: int): - await self.devCtrl.SendCommand( - nodeid, 0, Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180), timedRequestTimeoutMs=10000) -- if not self.TestPaseOnly(ip=ip, setuppin=setuppin, nodeid=nodeid, devCtrl=self.devCtrl2): -+ if not await self.TestPaseOnly(ip=ip, setuppin=setuppin, nodeid=nodeid, devCtrl=self.devCtrl2): - return False - - await self.devCtrl2.SendCommand( -@@ -248,17 +248,17 @@ class BaseTestHelper: - nodeid, 0, Clusters.AdministratorCommissioning.Commands.RevokeCommissioning(), timedRequestTimeoutMs=10000) - return True - -- def TestEnhancedCommissioningWindow(self, ip: str, nodeid: int): -- params = self.devCtrl.OpenCommissioningWindow(nodeid=nodeid, timeout=600, iteration=10000, discriminator=3840, option=1) -- return self.TestPaseOnly(ip=ip, nodeid=nodeid, setuppin=params.setupPinCode, devCtrl=self.devCtrl2) -+ async def TestEnhancedCommissioningWindow(self, ip: str, nodeid: int): -+ params = await self.devCtrl.OpenCommissioningWindow(nodeid=nodeid, timeout=600, iteration=10000, discriminator=3840, option=1) -+ return await self.TestPaseOnly(ip=ip, nodeid=nodeid, setuppin=params.setupPinCode, devCtrl=self.devCtrl2) - -- def TestPaseOnly(self, ip: str, setuppin: int, nodeid: int, devCtrl=None): -+ async def TestPaseOnly(self, ip: str, setuppin: int, nodeid: int, devCtrl=None): - if devCtrl is None: - devCtrl = self.devCtrl - self.logger.info( - "Attempting to establish PASE session with device id: {} addr: {}".format(str(nodeid), ip)) - try: -- devCtrl.EstablishPASESessionIP(ip, setuppin, nodeid) -+ await devCtrl.EstablishPASESessionIP(ip, setuppin, nodeid) - except ChipStackException: - self.logger.info( - "Failed to establish PASE session with device id: {} addr: {}".format(str(nodeid), ip)) -@@ -267,11 +267,11 @@ class BaseTestHelper: - "Successfully established PASE session with device id: {} addr: {}".format(str(nodeid), ip)) - return True - -- def TestCommissionOnly(self, nodeid: int): -+ async def TestCommissionOnly(self, nodeid: int): - self.logger.info( - "Commissioning device with id {}".format(nodeid)) - try: -- self.devCtrl.Commission(nodeid) -+ await self.devCtrl.Commission(nodeid) - except ChipStackException: - self.logger.info( - "Failed to commission device with id {}".format(str(nodeid))) -@@ -280,17 +280,17 @@ class BaseTestHelper: - "Successfully commissioned device with id {}".format(str(nodeid))) - return True - -- def TestKeyExchangeBLE(self, discriminator: int, setuppin: int, nodeid: int): -+ async def TestKeyExchangeBLE(self, discriminator: int, setuppin: int, nodeid: int): - self.logger.info( - "Conducting key exchange with device {}".format(discriminator)) -- if not self.devCtrl.ConnectBLE(discriminator, setuppin, nodeid): -+ if not await self.devCtrl.ConnectBLE(discriminator, setuppin, nodeid): - self.logger.info( - "Failed to finish key exchange with device {}".format(discriminator)) - return False - self.logger.info("Device finished key exchange.") - return True - -- def TestCommissionFailure(self, nodeid: int, failAfter: int): -+ async def TestCommissionFailure(self, nodeid: int, failAfter: int): - self.devCtrl.ResetTestCommissioner() - a = self.devCtrl.SetTestCommissionerSimulateFailureOnStage(failAfter) - if not a: -@@ -299,10 +299,10 @@ class BaseTestHelper: - - self.logger.info( - "Commissioning device, expecting failure after stage {}".format(failAfter)) -- self.devCtrl.Commission(nodeid) -+ await self.devCtrl.Commission(nodeid) - return self.devCtrl.CheckTestCommissionerCallbacks() and self.devCtrl.CheckTestCommissionerPaseConnection(nodeid) - -- def TestCommissionFailureOnReport(self, nodeid: int, failAfter: int): -+ async def TestCommissionFailureOnReport(self, nodeid: int, failAfter: int): - self.devCtrl.ResetTestCommissioner() - a = self.devCtrl.SetTestCommissionerSimulateFailureOnReport(failAfter) - if not a: -@@ -310,13 +310,13 @@ class BaseTestHelper: - return True - self.logger.info( - "Commissioning device, expecting failure on report for stage {}".format(failAfter)) -- self.devCtrl.Commission(nodeid) -+ await self.devCtrl.Commission(nodeid) - return self.devCtrl.CheckTestCommissionerCallbacks() and self.devCtrl.CheckTestCommissionerPaseConnection(nodeid) - -- def TestCommissioning(self, ip: str, setuppin: int, nodeid: int): -+ async def TestCommissioning(self, ip: str, setuppin: int, nodeid: int): - self.logger.info("Commissioning device {}".format(ip)) - try: -- self.devCtrl.CommissionIP(ip, setuppin, nodeid) -+ await self.devCtrl.CommissionIP(ip, setuppin, nodeid) - except ChipStackException: - self.logger.exception( - "Failed to finish commissioning device {}".format(ip)) -@@ -324,10 +324,10 @@ class BaseTestHelper: - self.logger.info("Commissioning finished.") - return True - -- def TestCommissioningWithSetupPayload(self, setupPayload: str, nodeid: int, discoveryType: int = 2): -+ async def TestCommissioningWithSetupPayload(self, setupPayload: str, nodeid: int, discoveryType: int = 2): - self.logger.info("Commissioning device with setup payload {}".format(setupPayload)) - try: -- self.devCtrl.CommissionWithCode(setupPayload, nodeid, chip.discovery.DiscoveryType(discoveryType)) -+ await self.devCtrl.CommissionWithCode(setupPayload, nodeid, chip.discovery.DiscoveryType(discoveryType)) - except ChipStackException: - self.logger.exception( - "Failed to finish commissioning device {}".format(setupPayload)) -@@ -335,7 +335,7 @@ class BaseTestHelper: - self.logger.info("Commissioning finished.") - return True - -- def TestOnNetworkCommissioning(self, discriminator: int, setuppin: int, nodeid: int, ip_override: str = None): -+ async def TestOnNetworkCommissioning(self, discriminator: int, setuppin: int, nodeid: int, ip_override: str = None): - self.logger.info("Testing discovery") - device = self.TestDiscovery(discriminator=discriminator) - if not device: -@@ -345,7 +345,7 @@ class BaseTestHelper: - if ip_override: - address = ip_override - self.logger.info("Testing commissioning") -- if not self.TestCommissioning(address, setuppin, nodeid): -+ if not await self.TestCommissioning(address, setuppin, nodeid): - self.logger.info("Failed to finish commissioning") - return False - return True -@@ -791,7 +791,7 @@ class BaseTestHelper: - self.controllerNodeId, self.paaTrustStorePath) - - try: -- self.devCtrl2.CommissionIP(ip, setuppin, nodeid) -+ await self.devCtrl2.CommissionIP(ip, setuppin, nodeid) - except ChipStackException: - self.logger.exception( - "Failed to finish key exchange with device {}".format(ip)) -@@ -1312,15 +1312,15 @@ class BaseTestHelper: - return False - return True - -- def TestFabricScopedCommandDuringPase(self, nodeid: int): -+ async def TestFabricScopedCommandDuringPase(self, nodeid: int): - '''Validates that fabric-scoped commands fail during PASE with UNSUPPORTED_ACCESS - - The nodeid is the PASE pseudo-node-ID used during PASE establishment - ''' - status = None - try: -- asyncio.run(self.devCtrl.SendCommand( -- nodeid, 0, Clusters.OperationalCredentials.Commands.UpdateFabricLabel("roboto"))) -+ await self.devCtrl.SendCommand( -+ nodeid, 0, Clusters.OperationalCredentials.Commands.UpdateFabricLabel("roboto")) - except IM.InteractionModelError as ex: - status = ex.status - -diff --git a/src/controller/python/test/test_scripts/commissioning_failure_test.py b/src/controller/python/test/test_scripts/commissioning_failure_test.py -index a535c8b184..13ee0674e5 100755 ---- a/src/controller/python/test/test_scripts/commissioning_failure_test.py -+++ b/src/controller/python/test/test_scripts/commissioning_failure_test.py -@@ -46,7 +46,7 @@ LIGHTING_ENDPOINT_ID = 1 - GROUP_ID = 0 - - --def main(): -+async def main(): - optParser = OptionParser() - optParser.add_option( - "-t", -@@ -98,32 +98,32 @@ def main(): - # TODO: Start at stage 2 once handling for arming failsafe on pase is done. - if options.report: - for testFailureStage in range(3, 20): -- FailIfNot(test.TestPaseOnly(ip=options.deviceAddress1, -- setuppin=20202021, -- nodeid=1), -+ FailIfNot(await test.TestPaseOnly(ip=options.deviceAddress1, -+ setuppin=20202021, -+ nodeid=1), - "Failed to establish PASE connection with device") -- FailIfNot(test.TestCommissionFailureOnReport(1, testFailureStage), -+ FailIfNot(await test.TestCommissionFailureOnReport(1, testFailureStage), - "Commissioning failure tests failed for simulated report failure on stage {}".format(testFailureStage)) - - else: - for testFailureStage in range(3, 20): -- FailIfNot(test.TestPaseOnly(ip=options.deviceAddress1, -- setuppin=20202021, -- nodeid=1), -+ FailIfNot(await test.TestPaseOnly(ip=options.deviceAddress1, -+ setuppin=20202021, -+ nodeid=1), - "Failed to establish PASE connection with device") -- FailIfNot(test.TestCommissionFailure(1, testFailureStage), -+ FailIfNot(await test.TestCommissionFailure(1, testFailureStage), - "Commissioning failure tests failed for simulated stage failure on stage {}".format(testFailureStage)) - - # Ensure we can still commission for real -- FailIfNot(test.TestPaseOnly(ip=options.deviceAddress1, -- setuppin=20202021, -- nodeid=1), -+ FailIfNot(await test.TestPaseOnly(ip=options.deviceAddress1, -+ setuppin=20202021, -+ nodeid=1), - "Failed to establish PASE connection with device") -- FailIfNot(test.TestCommissionFailure(1, 0), "Failed to commission device") -+ FailIfNot(await test.TestCommissionFailure(1, 0), "Failed to commission device") - - logger.info("Testing on off cluster") -- FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=1, -- endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") -+ FailIfNot(await test.TestOnOffCluster(nodeid=1, -+ endpoint=LIGHTING_ENDPOINT_ID), "Failed to test on off cluster") - - timeoutTicker.stop() - -@@ -136,7 +136,7 @@ def main(): - - if __name__ == "__main__": - try: -- main() -+ asyncio.run(main()) - except Exception as ex: - logger.exception(ex) - TestFail("Exception occurred when running tests.") -diff --git a/src/controller/python/test/test_scripts/commissioning_test.py b/src/controller/python/test/test_scripts/commissioning_test.py -index 4a7f15d6c3..c53ab00f33 100755 ---- a/src/controller/python/test/test_scripts/commissioning_test.py -+++ b/src/controller/python/test/test_scripts/commissioning_test.py -@@ -47,7 +47,7 @@ LIGHTING_ENDPOINT_ID = 1 - GROUP_ID = 0 - - --def main(): -+async def main(): - optParser = OptionParser() - optParser.add_option( - "-t", -@@ -133,22 +133,22 @@ def main(): - - if options.deviceAddress: - logger.info("Testing commissioning (IP)") -- FailIfNot(test.TestCommissioning(ip=options.deviceAddress, -- setuppin=20202021, -- nodeid=options.nodeid), -+ FailIfNot(await test.TestCommissioning(ip=options.deviceAddress, -+ setuppin=20202021, -+ nodeid=options.nodeid), - "Failed to finish commissioning") - elif options.setupPayload: - logger.info("Testing commissioning (w/ Setup Payload)") -- FailIfNot(test.TestCommissioningWithSetupPayload(setupPayload=options.setupPayload, -- nodeid=options.nodeid, -- discoveryType=options.discoveryType), -+ FailIfNot(await test.TestCommissioningWithSetupPayload(setupPayload=options.setupPayload, -+ nodeid=options.nodeid, -+ discoveryType=options.discoveryType), - "Failed to finish commissioning") - else: - TestFail("Must provide device address or setup payload to commissioning the device") - - logger.info("Testing on off cluster") -- FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=options.nodeid, -- endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") -+ FailIfNot(await test.TestOnOffCluster(nodeid=options.nodeid, -+ endpoint=LIGHTING_ENDPOINT_ID), "Failed to test on off cluster") - - FailIfNot(test.TestUsedTestCommissioner(), - "Test commissioner check failed") -@@ -164,7 +164,7 @@ def main(): - - if __name__ == "__main__": - try: -- main() -+ asyncio.run(main()) - except Exception as ex: - logger.exception(ex) - TestFail("Exception occurred when running tests.") -diff --git a/src/controller/python/test/test_scripts/commissioning_window_test.py b/src/controller/python/test/test_scripts/commissioning_window_test.py -index 6a113aede2..e146485aaa 100755 ---- a/src/controller/python/test/test_scripts/commissioning_window_test.py -+++ b/src/controller/python/test/test_scripts/commissioning_window_test.py -@@ -89,9 +89,9 @@ async def main(): - "Failed to finish network commissioning") - - logger.info("Commissioning DUT from first commissioner") -- FailIfNot(test.TestPaseOnly(ip=options.deviceAddress, setuppin=20202021, nodeid=1), -+ FailIfNot(await test.TestPaseOnly(ip=options.deviceAddress, setuppin=20202021, nodeid=1), - "Unable to establish PASE connection to device") -- FailIfNot(test.TestCommissionOnly(nodeid=1), "Unable to commission device") -+ FailIfNot(await test.TestCommissionOnly(nodeid=1), "Unable to commission device") - - logger.info("Creating controller on a new fabric") - FailIfNot(test.CreateNewFabricController(), "Unable to create new controller") -@@ -103,7 +103,7 @@ async def main(): - "RevokeCommissioning test failed") - - logger.info("Test Enhanced Commissioning Window") -- FailIfNot(test.TestEnhancedCommissioningWindow(ip=options.deviceAddress, nodeid=1), "EnhancedCommissioningWindow open failed") -+ FailIfNot(await test.TestEnhancedCommissioningWindow(ip=options.deviceAddress, nodeid=1), "EnhancedCommissioningWindow open failed") - - timeoutTicker.stop() - -diff --git a/src/controller/python/test/test_scripts/example_python_commissioning_flow.py b/src/controller/python/test/test_scripts/example_python_commissioning_flow.py -index b10269257f..016825a6f8 100644 ---- a/src/controller/python/test/test_scripts/example_python_commissioning_flow.py -+++ b/src/controller/python/test/test_scripts/example_python_commissioning_flow.py -@@ -32,7 +32,7 @@ class ExampleCustomMatterCommissioningFlow(commissioning_flow_blocks.Commissioni - - async def commission(self, parameter: commissioning.Parameters): - # The example uses PASE, however, the blocks uses a node_id, which supports both PASE and CASE. -- with pase.establish_session(devCtrl=self._devCtrl, parameter=parameter.pase_param) as device: -+ with await pase.establish_session(devCtrl=self._devCtrl, parameter=parameter.pase_param) as device: - node_id = device.node_id - - self._logger.info("Sending ArmFailSafe to device") -@@ -68,7 +68,7 @@ class ExampleCredentialProvider: - - async def get_commissionee_credentials(self, request: commissioning.GetCommissioneeCredentialsRequest) -> commissioning.GetCommissioneeCredentialsResponse: - node_id = random.randint(100000, 999999) -- nocChain = self._devCtrl.IssueNOCChain(Clusters.OperationalCredentials.Commands.CSRResponse( -+ nocChain = await self._devCtrl.IssueNOCChain(Clusters.OperationalCredentials.Commands.CSRResponse( - NOCSRElements=request.csr_elements, attestationSignature=request.attestation_signature), nodeId=node_id) - return commissioning.GetCommissioneeCredentialsResponse( - rcac=nocChain.rcacBytes, -diff --git a/src/controller/python/test/test_scripts/failsafe_tests.py b/src/controller/python/test/test_scripts/failsafe_tests.py -index d1a2034e73..d27111cbf7 100755 ---- a/src/controller/python/test/test_scripts/failsafe_tests.py -+++ b/src/controller/python/test/test_scripts/failsafe_tests.py -@@ -46,7 +46,7 @@ LIGHTING_ENDPOINT_ID = 1 - GROUP_ID = 0 - - --def main(): -+async def main(): - optParser = OptionParser() - optParser.add_option( - "-t", -@@ -95,12 +95,12 @@ def main(): - "Failed to finish network commissioning") - - logger.info("Testing commissioning") -- FailIfNot(test.TestCommissioning(ip=options.deviceAddress, -- setuppin=20202021, -- nodeid=1), -+ FailIfNot(await test.TestCommissioning(ip=options.deviceAddress, -+ setuppin=20202021, -+ nodeid=1), - "Failed to finish key exchange") - -- FailIfNot(asyncio.run(test.TestFailsafe(nodeid=1)), "Failed failsafe test") -+ FailIfNot(await test.TestFailsafe(nodeid=1), "Failed failsafe test") - - timeoutTicker.stop() - -@@ -113,7 +113,7 @@ def main(): - - if __name__ == "__main__": - try: -- main() -+ asyncio.run(main()) - except Exception as ex: - logger.exception(ex) - TestFail("Exception occurred when running tests.") -diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py -index 8f6f534dce..179dfa079a 100755 ---- a/src/controller/python/test/test_scripts/mobile-device-test.py -+++ b/src/controller/python/test/test_scripts/mobile-device-test.py -@@ -57,7 +57,7 @@ TEST_DEVICE_NODE_ID = 1 - ALL_TESTS = ['network_commissioning', 'datamodel'] - - --def ethernet_commissioning(test: BaseTestHelper, discriminator: int, setup_pin: int, address_override: str, device_nodeid: int): -+async def ethernet_commissioning(test: BaseTestHelper, discriminator: int, setup_pin: int, address_override: str, device_nodeid: int): - logger.info("Testing discovery") - device = test.TestDiscovery(discriminator=discriminator) - FailIfNot(device, "Failed to discover any devices.") -@@ -71,27 +71,27 @@ def ethernet_commissioning(test: BaseTestHelper, discriminator: int, setup_pin: - address = address_override - - logger.info("Testing commissioning") -- FailIfNot(test.TestCommissioning(ip=address, -- setuppin=setup_pin, -- nodeid=device_nodeid), -+ FailIfNot(await test.TestCommissioning(ip=address, -+ setuppin=setup_pin, -+ nodeid=device_nodeid), - "Failed to finish key exchange") - - logger.info("Testing multi-controller setup on the same fabric") -- FailIfNot(asyncio.run(test.TestMultiControllerFabric(nodeid=device_nodeid)), "Failed the multi-controller test") -+ FailIfNot(await test.TestMultiControllerFabric(nodeid=device_nodeid), "Failed the multi-controller test") - - logger.info("Testing CATs used on controllers") -- FailIfNot(asyncio.run(test.TestControllerCATValues(nodeid=device_nodeid)), "Failed the controller CAT test") -+ FailIfNot(await test.TestControllerCATValues(nodeid=device_nodeid), "Failed the controller CAT test") - -- ok = asyncio.run(test.TestMultiFabric(ip=address, -- setuppin=20202021, -- nodeid=1)) -+ ok = await test.TestMultiFabric(ip=address, -+ setuppin=20202021, -+ nodeid=1) - FailIfNot(ok, "Failed to commission multi-fabric") - -- FailIfNot(asyncio.run(test.TestAddUpdateRemoveFabric(nodeid=device_nodeid)), -+ FailIfNot(await test.TestAddUpdateRemoveFabric(nodeid=device_nodeid), - "Failed AddUpdateRemoveFabric test") - - logger.info("Testing CASE Eviction") -- FailIfNot(asyncio.run(test.TestCaseEviction(device_nodeid)), "Failed TestCaseEviction") -+ FailIfNot(await test.TestCaseEviction(device_nodeid), "Failed TestCaseEviction") - - logger.info("Testing closing sessions") - FailIfNot(test.TestCloseSession(nodeid=device_nodeid), "Failed to close sessions") -@@ -163,8 +163,8 @@ def do_tests(controller_nodeid, device_nodeid, address, timeout, discriminator, - - chip.logging.RedirectToPythonLogging() - -- ethernet_commissioning(test, discriminator, setup_pin, address, -- device_nodeid) -+ asyncio.run(ethernet_commissioning(test, discriminator, setup_pin, address, -+ device_nodeid)) - - logger.info("Testing resolve") - FailIfNot(test.TestResolve(nodeid=device_nodeid), -diff --git a/src/controller/python/test/test_scripts/python_commissioning_flow_test.py b/src/controller/python/test/test_scripts/python_commissioning_flow_test.py -index 2317a5571e..0f76095a96 100755 ---- a/src/controller/python/test/test_scripts/python_commissioning_flow_test.py -+++ b/src/controller/python/test/test_scripts/python_commissioning_flow_test.py -@@ -126,7 +126,7 @@ def main(): - - async def get_commissionee_credentials(self, request: commissioning.GetCommissioneeCredentialsRequest) -> commissioning.GetCommissioneeCredentialsResponse: - node_id = random.randint(100000, 999999) -- nocChain = self._devCtrl.IssueNOCChain(Clusters.OperationalCredentials.Commands.CSRResponse( -+ nocChain = await self._devCtrl.IssueNOCChain(Clusters.OperationalCredentials.Commands.CSRResponse( - NOCSRElements=request.csr_elements, attestationSignature=request.attestation_signature), nodeId=node_id) - return commissioning.GetCommissioneeCredentialsResponse( - rcac=nocChain.rcacBytes[1:], -diff --git a/src/controller/python/test/test_scripts/split_commissioning_test.py b/src/controller/python/test/test_scripts/split_commissioning_test.py -index 9233d58b90..864a6f357d 100755 ---- a/src/controller/python/test/test_scripts/split_commissioning_test.py -+++ b/src/controller/python/test/test_scripts/split_commissioning_test.py -@@ -46,7 +46,7 @@ LIGHTING_ENDPOINT_ID = 1 - GROUP_ID = 0 - - --def main(): -+async def main(): - optParser = OptionParser() - optParser.add_option( - "-t", -@@ -97,34 +97,34 @@ def main(): - "Failed to finish network commissioning") - - logger.info("Testing PASE connection to device 1") -- FailIfNot(test.TestPaseOnly(ip=options.deviceAddress1, -- setuppin=20202021, -- nodeid=1), -+ FailIfNot(await test.TestPaseOnly(ip=options.deviceAddress1, -+ setuppin=20202021, -+ nodeid=1), - "Failed to establish PASE connection with device 1") - - logger.info("Testing PASE connection to device 2") -- FailIfNot(test.TestPaseOnly(ip=options.deviceAddress2, -- setuppin=20202021, -- nodeid=2), -+ FailIfNot(await test.TestPaseOnly(ip=options.deviceAddress2, -+ setuppin=20202021, -+ nodeid=2), - "Failed to establish PASE connection with device 2") - - logger.info("Attempting to execute a fabric-scoped command during PASE before AddNOC") -- FailIfNot(test.TestFabricScopedCommandDuringPase(nodeid=1), -+ FailIfNot(await test.TestFabricScopedCommandDuringPase(nodeid=1), - "Did not get UNSUPPORTED_ACCESS for fabric-scoped command during PASE") - -- FailIfNot(test.TestCommissionOnly(nodeid=1), -+ FailIfNot(await test.TestCommissionOnly(nodeid=1), - "Failed to commission device 1") - -- FailIfNot(test.TestCommissionOnly(nodeid=2), -+ FailIfNot(await test.TestCommissionOnly(nodeid=2), - "Failed to commission device 2") - - logger.info("Testing on off cluster on device 1") -- FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=1, -- endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster on device 1") -+ FailIfNot(await test.TestOnOffCluster(nodeid=1, -+ endpoint=LIGHTING_ENDPOINT_ID), "Failed to test on off cluster on device 1") - - logger.info("Testing on off cluster on device 2") -- FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=2, -- endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster on device 2") -+ FailIfNot(await test.TestOnOffCluster(nodeid=2, -+ endpoint=LIGHTING_ENDPOINT_ID), "Failed to test on off cluster on device 2") - - timeoutTicker.stop() - -@@ -137,7 +137,7 @@ def main(): - - if __name__ == "__main__": - try: -- main() -+ asyncio.run(main()) - except Exception as ex: - logger.exception(ex) - TestFail("Exception occurred when running tests.") -diff --git a/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl1.py b/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl1.py -index e02564e293..dc126a1d04 100755 ---- a/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl1.py -+++ b/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl1.py -@@ -32,7 +32,7 @@ TEST_SETUPPIN = 20202021 - TEST_ENDPOINT_ID = 0 - - --def main(): -+async def main(): - optParser = OptionParser() - optParser.add_option( - "-t", -@@ -110,12 +110,12 @@ def main(): - nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=True) - - FailIfNot( -- test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), -- "Failed on on-network commissioing") -+ await test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), -+ "Failed on on-network commissioning") - - FailIfNot( -- asyncio.run(test.TestSubscriptionResumptionCapacityStep1( -- options.nodeid, TEST_ENDPOINT_ID, options.setuppin, options.subscriptionCapacity)), -+ await test.TestSubscriptionResumptionCapacityStep1( -+ options.nodeid, TEST_ENDPOINT_ID, options.setuppin, options.subscriptionCapacity), - "Failed on step 1 of testing subscription resumption capacity") - - timeoutTicker.stop() -@@ -129,7 +129,7 @@ def main(): - - if __name__ == "__main__": - try: -- main() -+ asyncio.run(main()) - except Exception as ex: - logger.exception(ex) - TestFail("Exception occurred when running tests.") -diff --git a/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl2.py b/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl2.py -index ac449a9f54..2f50e80bf2 100755 ---- a/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl2.py -+++ b/src/controller/python/test/test_scripts/subscription_resumption_capacity_test_ctrl2.py -@@ -34,7 +34,7 @@ TEST_ENDPOINT_ID = 0 - TEST_SSH_PORT = 2222 - - --def main(): -+async def main(): - optParser = OptionParser() - optParser.add_option( - "-t", -@@ -122,13 +122,12 @@ def main(): - nodeid=112244, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=True) - - FailIfNot( -- test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), -- "Failed on on-network commissioing") -+ await test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), -+ "Failed on on-network commissioning") - - FailIfNot( -- asyncio.run( -- test.TestSubscriptionResumptionCapacityStep2(options.nodeid, TEST_ENDPOINT_ID, options.deviceAddress, -- TEST_SSH_PORT, options.remoteServerApp, options.subscriptionCapacity)), -+ await test.TestSubscriptionResumptionCapacityStep2(options.nodeid, TEST_ENDPOINT_ID, options.deviceAddress, -+ TEST_SSH_PORT, options.remoteServerApp, options.subscriptionCapacity), - "Failed on testing subscription resumption capacity") - - timeoutTicker.stop() -@@ -142,7 +141,7 @@ def main(): - - if __name__ == "__main__": - try: -- main() -+ asyncio.run(main()) - except Exception as ex: - logger.exception(ex) - TestFail("Exception occurred when running tests.") -diff --git a/src/controller/python/test/test_scripts/subscription_resumption_test.py b/src/controller/python/test/test_scripts/subscription_resumption_test.py -index 79edf6a289..b7420c8c17 100755 ---- a/src/controller/python/test/test_scripts/subscription_resumption_test.py -+++ b/src/controller/python/test/test_scripts/subscription_resumption_test.py -@@ -34,7 +34,7 @@ TEST_ENDPOINT_ID = 0 - TEST_SSH_PORT = 2222 - - --def main(): -+async def main(): - optParser = OptionParser() - optParser.add_option( - "-t", -@@ -112,12 +112,12 @@ def main(): - nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=True) - - FailIfNot( -- test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), -- "Failed on on-network commissioing") -+ await test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), -+ "Failed on on-network commissioning") - - FailIfNot( -- asyncio.run(test.TestSubscriptionResumption(options.nodeid, TEST_ENDPOINT_ID, options.deviceAddress, -- TEST_SSH_PORT, options.remoteServerApp)), "Failed to resume subscription") -+ await test.TestSubscriptionResumption(options.nodeid, TEST_ENDPOINT_ID, options.deviceAddress, -+ TEST_SSH_PORT, options.remoteServerApp), "Failed to resume subscription") - - timeoutTicker.stop() - -@@ -130,7 +130,7 @@ def main(): - - if __name__ == "__main__": - try: -- main() -+ asyncio.run(main()) - except Exception as ex: - logger.exception(ex) - TestFail("Exception occurred when running tests.") -diff --git a/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py b/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py -index 4932e5b4cc..f4809a28a8 100755 ---- a/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py -+++ b/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py -@@ -33,7 +33,7 @@ TEST_SETUPPIN = 20202021 - TEST_ENDPOINT_ID = 0 - - --def main(): -+async def main(): - optParser = OptionParser() - optParser.add_option( - "-t", -@@ -102,13 +102,13 @@ def main(): - nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=True) - - FailIfNot( -- test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), -+ await test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress), - "Failed on on-network commissioning") - - try: -- asyncio.run(test.devCtrl.ReadAttribute(options.nodeid, -- [(TEST_ENDPOINT_ID, Clusters.BasicInformation.Attributes.NodeLabel)], -- None, False, reportInterval=(1, 2), keepSubscriptions=True, autoResubscribe=False)) -+ await test.devCtrl.ReadAttribute(options.nodeid, -+ [(TEST_ENDPOINT_ID, Clusters.BasicInformation.Attributes.NodeLabel)], -+ None, False, reportInterval=(1, 2), keepSubscriptions=True, autoResubscribe=False) - except Exception as ex: - TestFail(f"Failed to subscribe attribute: {ex}") - -@@ -123,7 +123,7 @@ def main(): - - if __name__ == "__main__": - try: -- main() -+ asyncio.run(main()) - except Exception as ex: - logger.exception(ex) - TestFail("Exception occurred when running tests.") -diff --git a/src/python_testing/TC_ACE_1_5.py b/src/python_testing/TC_ACE_1_5.py -index bab70260fc..c8ef410954 100644 ---- a/src/python_testing/TC_ACE_1_5.py -+++ b/src/python_testing/TC_ACE_1_5.py -@@ -51,10 +51,10 @@ class TC_ACE_1_5(MatterBaseTest): - self.th2 = new_fabric_admin.NewController(nodeId=TH2_nodeid, - paaTrustStorePath=str(self.matter_test_config.paa_trust_store_path)) - -- params = self.openCommissioningWindow(self.th1, self.dut_node_id) -+ params = await self.openCommissioningWindow(self.th1, self.dut_node_id) - self.print_step(2, "TH1 opens the commissioning window on the DUT") - -- self.th2.CommissionOnNetwork( -+ await self.th2.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.commissioningParameters.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=params.randomDiscriminator) - logging.info('Commissioning complete done. Successful.') -diff --git a/src/python_testing/TC_CGEN_2_4.py b/src/python_testing/TC_CGEN_2_4.py -index 50bd9c005d..5544e26fda 100644 ---- a/src/python_testing/TC_CGEN_2_4.py -+++ b/src/python_testing/TC_CGEN_2_4.py -@@ -42,9 +42,9 @@ kSendNOC = 18 - - class TC_CGEN_2_4(MatterBaseTest): - -- def OpenCommissioningWindow(self) -> CommissioningParameters: -+ async def OpenCommissioningWindow(self) -> CommissioningParameters: - try: -- params = self.th1.OpenCommissioningWindow( -+ params = await self.th1.OpenCommissioningWindow( - nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=self.discriminator, option=1) - return params - -@@ -56,14 +56,14 @@ class TC_CGEN_2_4(MatterBaseTest): - self, stage: int, expectedErrorPart: chip.native.ErrorSDKPart, expectedErrCode: int): - - logging.info("-----------------Fail on step {}-------------------------".format(stage)) -- params = self.OpenCommissioningWindow() -+ params = await self.OpenCommissioningWindow() - self.th2.ResetTestCommissioner() - # This will run the commissioning up to the point where stage x is run and the - # response is sent before the test commissioner simulates a failure - self.th2.SetTestCommissionerPrematureCompleteAfter(stage) - ctx = asserts.assert_raises(ChipStackError) - with ctx: -- self.th2.CommissionOnNetwork( -+ await self.th2.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator) - errcode = ctx.exception.chip_error -@@ -99,12 +99,12 @@ class TC_CGEN_2_4(MatterBaseTest): - await self.CommissionToStageSendCompleteAndCleanup(kSendNOC, chip.native.ErrorSDKPart.IM_CLUSTER_STATUS, 0x02) - - logging.info('Step 15 - TH1 opens a commissioning window') -- params = self.OpenCommissioningWindow() -+ params = await self.OpenCommissioningWindow() - - logging.info('Step 16 - TH2 fully commissions the DUT') - self.th2.ResetTestCommissioner() - -- self.th2.CommissionOnNetwork( -+ await self.th2.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator) - logging.info('Commissioning complete done.') -diff --git a/src/python_testing/TC_DA_1_5.py b/src/python_testing/TC_DA_1_5.py -index 17c6e3c16d..e42b530057 100644 ---- a/src/python_testing/TC_DA_1_5.py -+++ b/src/python_testing/TC_DA_1_5.py -@@ -162,7 +162,7 @@ class TC_DA_1_5(MatterBaseTest): - await self.send_single_cmd(cmd=gcomm.Commands.ArmFailSafe(expiryLengthSeconds=0, breadcrumb=1)) - - self.print_step(13, "Open commissioning window") -- params = self.default_controller.OpenCommissioningWindow( -+ params = await self.default_controller.OpenCommissioningWindow( - nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=1234, option=1) - - self.print_step(14, "Commission to TH2") -@@ -170,7 +170,7 @@ class TC_DA_1_5(MatterBaseTest): - new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=2) - TH2 = new_fabric_admin.NewController(nodeId=112233) - -- TH2.CommissionOnNetwork( -+ await TH2.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234) - -diff --git a/src/python_testing/TC_IDM_1_2.py b/src/python_testing/TC_IDM_1_2.py -index 8f329daca5..18b727d962 100644 ---- a/src/python_testing/TC_IDM_1_2.py -+++ b/src/python_testing/TC_IDM_1_2.py -@@ -187,7 +187,7 @@ class TC_IDM_1_2(MatterBaseTest): - # To get a PASE session, we need an open commissioning window - discriminator = random.randint(0, 4095) - -- params = self.default_controller.OpenCommissioningWindow( -+ params = await self.default_controller.OpenCommissioningWindow( - nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=discriminator, option=1) - - # TH2 = new controller that's not connected over CASE -@@ -201,14 +201,14 @@ class TC_IDM_1_2(MatterBaseTest): - device = next(filter(lambda d: d.commissioningMode == 2 and d.longDiscriminator == discriminator, devices)) - for a in device.addresses: - try: -- TH2.EstablishPASESessionIP(ipaddr=a, setupPinCode=params.setupPinCode, -- nodeid=self.dut_node_id+1, port=device.port) -+ await TH2.EstablishPASESessionIP(ipaddr=a, setupPinCode=params.setupPinCode, -+ nodeid=self.dut_node_id+1, port=device.port) - break - except ChipStackError: - continue - - try: -- TH2.GetConnectedDeviceSync(nodeid=self.dut_node_id+1, allowPASE=True, timeoutMs=1000) -+ await TH2.GetConnectedDevice(nodeid=self.dut_node_id+1, allowPASE=True, timeoutMs=1000) - except TimeoutError: - asserts.fail("Unable to establish a PASE session to the device") - -@@ -260,7 +260,7 @@ class TC_IDM_1_2(MatterBaseTest): - - # Try with RevokeCommissioning - # First open a commissioning window for us to revoke, so we know this command is able to succeed absent this error -- _ = self.default_controller.OpenCommissioningWindow( -+ _ = await self.default_controller.OpenCommissioningWindow( - nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=discriminator, option=1) - cmd = FakeRevokeCommissioning() - try: -diff --git a/src/python_testing/TC_OPCREDS_3_1.py b/src/python_testing/TC_OPCREDS_3_1.py -index 661c3b0b9c..32f3ec7b1f 100644 ---- a/src/python_testing/TC_OPCREDS_3_1.py -+++ b/src/python_testing/TC_OPCREDS_3_1.py -@@ -30,7 +30,7 @@ from mobly import asserts - - - class TC_OPCREDS_3_1(MatterBaseTest): -- def FindAndEstablishPase(self, longDiscriminator: int, setupPinCode: int, nodeid: int, dev_ctrl: ChipDeviceCtrl = None): -+ async def FindAndEstablishPase(self, longDiscriminator: int, setupPinCode: int, nodeid: int, dev_ctrl: ChipDeviceCtrl = None): - if dev_ctrl is None: - dev_ctrl = self.default_controller - -@@ -41,8 +41,8 @@ class TC_OPCREDS_3_1(MatterBaseTest): - Discovery.FilterType.LONG_DISCRIMINATOR and d.longDiscriminator == longDiscriminator, devices)) - for a in device.addresses: - try: -- dev_ctrl.EstablishPASESessionIP(ipaddr=a, setupPinCode=setupPinCode, -- nodeid=nodeid, port=device.port) -+ await dev_ctrl.EstablishPASESessionIP(ipaddr=a, setupPinCode=setupPinCode, -+ nodeid=nodeid, port=device.port) - break - except ChipStackError: - continue -@@ -51,11 +51,11 @@ class TC_OPCREDS_3_1(MatterBaseTest): - except TimeoutError: - asserts.fail("Unable to establish a PASE session to the device") - -- def OpenCommissioningWindow(self, dev_ctrl: ChipDeviceCtrl, node_id: int): -+ async def OpenCommissioningWindow(self, dev_ctrl: ChipDeviceCtrl, node_id: int): - # TODO: abstract this in the base layer? Do we do this a lot? - longDiscriminator = random.randint(0, 4095) - try: -- params = dev_ctrl.OpenCommissioningWindow( -+ params = await dev_ctrl.OpenCommissioningWindow( - nodeid=node_id, timeout=600, iteration=10000, discriminator=longDiscriminator, option=ChipDeviceCtrl.ChipDeviceControllerBase.CommissioningWindowPasscode.kTokenWithRandomPin) - except Exception as e: - logging.exception('Error running OpenCommissioningWindow %s', e) -@@ -85,7 +85,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - "Device fabric table is full - please remove one fabric and retry") - - self.print_step(1, "TH0 opens a commissioning window on the DUT") -- longDiscriminator, params = self.OpenCommissioningWindow(self.default_controller, self.dut_node_id) -+ longDiscriminator, params = await self.OpenCommissioningWindow(self.default_controller, self.dut_node_id) - - self.print_step( - 2, "TH0 reads the BasicCommissioningInfo field from the General commissioning cluster saves MaxCumulativeFailsafeSeconds as `failsafe_max`") -@@ -94,8 +94,8 @@ class TC_OPCREDS_3_1(MatterBaseTest): - - self.print_step(3, "TH1 opens a PASE connection to the DUT") - newNodeId = self.dut_node_id + 1 -- self.FindAndEstablishPase(dev_ctrl=TH1, longDiscriminator=longDiscriminator, -- setupPinCode=params.setupPinCode, nodeid=newNodeId) -+ await self.FindAndEstablishPase(dev_ctrl=TH1, longDiscriminator=longDiscriminator, -+ setupPinCode=params.setupPinCode, nodeid=newNodeId) - - self.print_step(4, "TH1 sends ArmFailSafe command to the DUT with the ExpiryLengthSeconds field set to failsafe_max") - resp = await self.send_single_cmd(dev_ctrl=TH1, node_id=newNodeId, cmd=Clusters.GeneralCommissioning.Commands.ArmFailSafe(failsafe_max)) -@@ -109,7 +109,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - self.print_step(6, "TH1 obtains or generates the NOC, the Root CA Certificate and ICAC using csrResponse and selects an IPK. The certificates shall have their subjects padded with additional data such that they are each the maximum certificate size of 400 bytes when encoded in the MatterCertificateEncoding.") - # Our CA is set up to maximize cert chains already - # Extract the RCAC public key and save as `Root_Public_Key_TH1` -- TH1_certs_real = TH1.IssueNOCChain(csrResponse, newNodeId) -+ TH1_certs_real = await TH1.IssueNOCChain(csrResponse, newNodeId) - if (TH1_certs_real.rcacBytes is None or - TH1_certs_real.icacBytes is None or - TH1_certs_real.nocBytes is None or TH1_certs_real.ipkBytes is None): -@@ -125,7 +125,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - TH1_CA_fake = self.certificate_authority_manager.NewCertificateAuthority() - TH1_fabric_admin_fake = TH1_CA_fake.NewFabricAdmin(vendorId=0xFFF1, fabricId=2) - TH1_fake = TH1_fabric_admin_fake.NewController(nodeId=self.default_controller.nodeId) -- TH1_certs_fake = TH1_fake.IssueNOCChain(csrResponse, newNodeId) -+ TH1_certs_fake = await TH1_fake.IssueNOCChain(csrResponse, newNodeId) - if (TH1_certs_fake.rcacBytes is None or - TH1_certs_fake.icacBytes is None or - TH1_certs_fake.nocBytes is None or TH1_certs_real.ipkBytes is None): -@@ -361,7 +361,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - asserts.assert_equal(len(fabrics), fabrics_original_size, "Fabric list size does not match original") - - self.print_step(37, "TH1 fully commissions DUT onto the fabric using a set of valid certificates") -- TH1.Commission(newNodeId) -+ await TH1.Commission(newNodeId) - - self.print_step( - 38, "TH1 reads the TrustedRootCertificates list from DUT and verify that there are trusted_root_original_size + 1 entries") -@@ -404,7 +404,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - resp.statusCode, opcreds.Enums.NodeOperationalCertStatusEnum.kOk, "Failure on UpdateFabricLabel") - - self.print_step(44, "TH1 sends an OpenCommissioningWindow command to the Administrator Commissioning cluster") -- longDiscriminator, params = self.OpenCommissioningWindow(TH1, newNodeId) -+ longDiscriminator, params = await self.OpenCommissioningWindow(TH1, newNodeId) - - self.print_step(45, "TH2 commissions the DUT") - TH2_CA = self.certificate_authority_manager.NewCertificateAuthority(maximizeCertChains=True) -@@ -413,7 +413,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - TH2_nodeid = self.default_controller.nodeId+2 - TH2 = TH2_fabric_admin.NewController(nodeId=TH2_nodeid) - TH2_dut_nodeid = self.dut_node_id+2 -- TH2.CommissionOnNetwork( -+ await TH2.CommissionOnNetwork( - nodeId=TH2_dut_nodeid, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=longDiscriminator) - -@@ -484,7 +484,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - temp_CA = self.certificate_authority_manager.NewCertificateAuthority() - temp_fabric_admin = temp_CA.NewFabricAdmin(vendorId=0xFFF1, fabricId=3) - temp_controller = temp_fabric_admin.NewController(nodeId=self.default_controller.nodeId) -- temp_certs = temp_controller.IssueNOCChain(csrResponse, newNodeId) -+ temp_certs = await temp_controller.IssueNOCChain(csrResponse, newNodeId) - if (temp_certs.rcacBytes is None or - temp_certs.icacBytes is None or - temp_certs.nocBytes is None or temp_certs.ipkBytes is None): -@@ -521,7 +521,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - - self.print_step(61, "TH1 obtains or generates a NOC and ICAC using the CSR elements from the previous step with a different NodeID, but the same Root CA Certificate and fabric ID as step <>. Save as `Node_Operational_Certificates_TH1_fabric_conflict` and `Intermediate_Certificate_TH1_fabric_conflict`|") - anotherNodeId = newNodeId + 1 -- TH1_certs_fabric_conflict = TH1.IssueNOCChain(csrResponse_new, anotherNodeId) -+ TH1_certs_fabric_conflict = await TH1.IssueNOCChain(csrResponse_new, anotherNodeId) - if (TH1_certs_fabric_conflict.rcacBytes is None or - TH1_certs_fabric_conflict.icacBytes is None or - TH1_certs_fabric_conflict.nocBytes is None or TH1_certs_fabric_conflict.ipkBytes is None): -@@ -565,7 +565,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - "Unexpected response type for UpdateNOC csr request") - - self.print_step(68, "TH1 obtains or generates a NOC, Root CA Certificate, ICAC using the CSR elements from the previous step") -- TH1_certs_3 = TH1.IssueNOCChain(csrResponse, anotherNodeId) -+ TH1_certs_3 = await TH1.IssueNOCChain(csrResponse, anotherNodeId) - if (TH1_certs_3.rcacBytes is None or - TH1_certs_3.icacBytes is None or - TH1_certs_3.nocBytes is None or TH1_certs_3.ipkBytes is None): -diff --git a/src/python_testing/TC_TIMESYNC_2_13.py b/src/python_testing/TC_TIMESYNC_2_13.py -index fa43bbd00c..dea41e505c 100644 ---- a/src/python_testing/TC_TIMESYNC_2_13.py -+++ b/src/python_testing/TC_TIMESYNC_2_13.py -@@ -45,7 +45,7 @@ class TC_TIMESYNC_2_13(MatterBaseTest): - self.print_step(0, "Commissioning, already done") - - self.print_step(1, "TH1 opens a commissioning window") -- params = self.default_controller.OpenCommissioningWindow( -+ params = await self.default_controller.OpenCommissioningWindow( - nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=1234, option=1) - - self.print_step(2, "Commission to TH2") -@@ -53,7 +53,7 @@ class TC_TIMESYNC_2_13(MatterBaseTest): - new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=2) - TH2 = new_fabric_admin.NewController(nodeId=112233) - -- TH2.CommissionOnNetwork( -+ await TH2.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234) - -diff --git a/src/python_testing/TestCommissioningTimeSync.py b/src/python_testing/TestCommissioningTimeSync.py -index 0fca7063fc..dfd03d957d 100644 ---- a/src/python_testing/TestCommissioningTimeSync.py -+++ b/src/python_testing/TestCommissioningTimeSync.py -@@ -56,9 +56,9 @@ class TestCommissioningTimeSync(MatterBaseTest): - return super().teardown_test() - - async def commission_and_base_checks(self): -- params = self.default_controller.OpenCommissioningWindow( -+ params = await self.default_controller.OpenCommissioningWindow( - nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=1234, option=1) -- self.commissioner.CommissionOnNetwork( -+ await self.commissioner.CommissionOnNetwork( - nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, - filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234) - self.commissioned = True -diff --git a/src/python_testing/basic_composition_support.py b/src/python_testing/basic_composition_support.py -index 4a516f67ec..2ea5995960 100644 ---- a/src/python_testing/basic_composition_support.py -+++ b/src/python_testing/basic_composition_support.py -@@ -108,7 +108,7 @@ class BasicCompositionTests: - setupCode = self.matter_test_config.qr_code_content if self.matter_test_config.qr_code_content is not None else self.matter_test_config.manual_code - asserts.assert_true(setupCode, "Require either --qr-code or --manual-code.") - node_id = self.dut_node_id -- dev_ctrl.EstablishPASESession(setupCode, node_id) -+ await dev_ctrl.EstablishPASESession(setupCode, node_id) - else: - # Using the already commissioned node - node_id = self.dut_node_id -diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py -index cd7fce8f08..a3bd5d2837 100644 ---- a/src/python_testing/matter_testing_support.py -+++ b/src/python_testing/matter_testing_support.py -@@ -805,11 +805,11 @@ class MatterBaseTest(base_test.BaseTestClass): - pics_key = pics_key.strip() - return pics_key in picsd and picsd[pics_key] - -- def openCommissioningWindow(self, dev_ctrl: ChipDeviceCtrl, node_id: int) -> CustomCommissioningParameters: -+ async def openCommissioningWindow(self, dev_ctrl: ChipDeviceCtrl, node_id: int) -> CustomCommissioningParameters: - rnd_discriminator = random.randint(0, 4095) - try: -- commissioning_params = dev_ctrl.OpenCommissioningWindow(nodeid=node_id, timeout=900, iteration=1000, -- discriminator=rnd_discriminator, option=1) -+ commissioning_params = await dev_ctrl.OpenCommissioningWindow(nodeid=node_id, timeout=900, iteration=1000, -+ discriminator=rnd_discriminator, option=1) - params = CustomCommissioningParameters(commissioning_params, rnd_discriminator) - return params - -@@ -1563,10 +1563,10 @@ class CommissionDeviceTest(MatterBaseTest): - (conf.root_of_trust_index, conf.fabric_id, node_id)) - logging.info("Commissioning method: %s" % conf.commissioning_method) - -- if not self._commission_device(commission_idx): -+ if not asyncio.run(self._commission_device(commission_idx)): - raise signals.TestAbortAll("Failed to commission node") - -- def _commission_device(self, i) -> bool: -+ async def _commission_device(self, i) -> bool: - dev_ctrl = self.default_controller - conf = self.matter_test_config - -@@ -1582,7 +1582,7 @@ class CommissionDeviceTest(MatterBaseTest): - - if conf.commissioning_method == "on-network": - try: -- dev_ctrl.CommissionOnNetwork( -+ await dev_ctrl.CommissionOnNetwork( - nodeId=conf.dut_node_ids[i], - setupPinCode=info.passcode, - filterType=info.filter_type, -@@ -1594,7 +1594,7 @@ class CommissionDeviceTest(MatterBaseTest): - return False - elif conf.commissioning_method == "ble-wifi": - try: -- dev_ctrl.CommissionWiFi( -+ await dev_ctrl.CommissionWiFi( - info.filter_value, - info.passcode, - conf.dut_node_ids[i], -@@ -1608,7 +1608,7 @@ class CommissionDeviceTest(MatterBaseTest): - return False - elif conf.commissioning_method == "ble-thread": - try: -- dev_ctrl.CommissionThread( -+ await dev_ctrl.CommissionThread( - info.filter_value, - info.passcode, - conf.dut_node_ids[i], -@@ -1622,7 +1622,7 @@ class CommissionDeviceTest(MatterBaseTest): - elif conf.commissioning_method == "on-network-ip": - try: - logging.warning("==== USING A DIRECT IP COMMISSIONING METHOD NOT SUPPORTED IN THE LONG TERM ====") -- dev_ctrl.CommissionIP( -+ await dev_ctrl.CommissionIP( - ipaddr=conf.commissionee_ip_address_just_for_testing, - setupPinCode=info.passcode, nodeid=conf.dut_node_ids[i] - ) --- -2.45.2 - diff --git a/0030-Python-Convert-DiscoverCommissionableNodes-to-asynci.patch b/0030-Python-Convert-DiscoverCommissionableNodes-to-asynci.patch deleted file mode 100644 index 9b0a589..0000000 --- a/0030-Python-Convert-DiscoverCommissionableNodes-to-asynci.patch +++ /dev/null @@ -1,238 +0,0 @@ -From 0b589b17b4c638afb67e4bd6ed91a0c4fabb619d Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Fri, 21 Jun 2024 14:28:03 +0200 -Subject: [PATCH] [Python] Convert DiscoverCommissionableNodes to asyncio - (#34033) - -* [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. - -* [Python] Adjust tests to use new DiscoverCommissionableNodes API ---- - src/controller/python/chip/ChipDeviceCtrl.py | 54 +++++++++++-------- - .../python/chip/commissioning/pase.py | 4 +- - src/controller/python/chip/yaml/runner.py | 2 +- - .../python/test/test_scripts/base.py | 6 +-- - .../test/test_scripts/commissioning_test.py | 2 +- - .../test/test_scripts/failsafe_tests.py | 2 +- - .../test/test_scripts/mobile-device-test.py | 2 +- - src/python_testing/TC_IDM_1_2.py | 2 +- - src/python_testing/TC_OPCREDS_3_1.py | 2 +- - 9 files changed, 42 insertions(+), 34 deletions(-) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 1c156c22d4..208385f305 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -37,7 +37,6 @@ import enum - import json - import logging - import threading --import time - import typing - 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, - c_uint32, c_uint64, c_void_p, create_string_buffer, pointer, py_object, resize, string_at) -@@ -634,8 +633,8 @@ class ChipDeviceControllerBase(): - - return (address.value.decode(), port.value) if error == 0 else None - -- def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None, -- stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]: -+ async def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None, -+ stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]: - ''' Discover commissionable nodes via DNS-SD with specified filters. - Supported filters are: - -@@ -657,27 +656,36 @@ class ChipDeviceControllerBase(): - if isinstance(filter, int): - filter = str(filter) - -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes( -- self.devCtrl, int(filterType), str(filter).encode("utf-8"))).raise_on_error() -- -- if timeoutSecond != 0: -- if stopOnFirst: -- target = time.time() + timeoutSecond -- while time.time() < target: -- if self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)): -- break -- time.sleep(0.1) -- else: -- time.sleep(timeoutSecond) -- -- self._ChipStack.Call( -- lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl)).raise_on_error() -+ # Discovery is also used during commissioning. Make sure this manual discovery -+ # and commissioning attempts do not interfere with each other. -+ async with self._commissioning_lock: -+ res = await self._ChipStack.CallAsync( -+ lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes( -+ self.devCtrl, int(filterType), str(filter).encode("utf-8"))) -+ res.raise_on_error() - -- return self.GetDiscoveredDevices() -+ async def _wait_discovery(): -+ while not await self._ChipStack.CallAsync( -+ lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)): -+ await asyncio.sleep(0.1) -+ return - -- def GetDiscoveredDevices(self): -+ try: -+ if stopOnFirst: -+ await asyncio.wait_for(_wait_discovery(), timeoutSecond) -+ else: -+ await asyncio.sleep(timeoutSecond) -+ except TimeoutError: -+ # Expected timeout, do nothing -+ pass -+ finally: -+ res = await self._ChipStack.CallAsync( -+ lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl)) -+ res.raise_on_error() -+ -+ return await self.GetDiscoveredDevices() -+ -+ async def GetDiscoveredDevices(self): - def GetDevices(devCtrl): - devices = [] - -@@ -691,7 +699,7 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_IterateDiscoveredCommissionableNodes(devCtrl.devCtrl, HandleDevice) - return devices - -- return self._ChipStack.Call(lambda: GetDevices(self)) -+ return await self._ChipStack.CallAsync(lambda: GetDevices(self)) - - def GetIPForDiscoveredDevice(self, idx, addrStr, length): - self.CheckIsActive() -diff --git a/src/controller/python/chip/commissioning/pase.py b/src/controller/python/chip/commissioning/pase.py -index c0cfca5ee8..ee0b96c76b 100644 ---- a/src/controller/python/chip/commissioning/pase.py -+++ b/src/controller/python/chip/commissioning/pase.py -@@ -48,8 +48,8 @@ async def establish_session(devCtrl: ChipDeviceCtrl.ChipDeviceControllerBase, pa - if isinstance(parameter, commissioning.PaseOverBLEParameters): - await devCtrl.EstablishPASESessionBLE(parameter.setup_pin, parameter.discriminator, parameter.temporary_nodeid) - elif isinstance(parameter, commissioning.PaseOverIPParameters): -- device = devCtrl.DiscoverCommissionableNodes(filterType=discovery.FilterType.LONG_DISCRIMINATOR, -- filter=parameter.long_discriminator, stopOnFirst=True) -+ device = await devCtrl.DiscoverCommissionableNodes(filterType=discovery.FilterType.LONG_DISCRIMINATOR, -+ filter=parameter.long_discriminator, stopOnFirst=True) - if not device: - raise ValueError("No commissionable device found") - selected_address = None -diff --git a/src/controller/python/chip/yaml/runner.py b/src/controller/python/chip/yaml/runner.py -index f0b681fb2b..ce1eaf84fc 100644 ---- a/src/controller/python/chip/yaml/runner.py -+++ b/src/controller/python/chip/yaml/runner.py -@@ -712,7 +712,7 @@ class DiscoveryCommandAction(BaseAction): - self.filterType, self.filter = DiscoveryCommandAction._filter_for_step(test_step) - - async def run_action(self, dev_ctrl: ChipDeviceController) -> _ActionResult: -- devices = dev_ctrl.DiscoverCommissionableNodes( -+ devices = await dev_ctrl.DiscoverCommissionableNodes( - filterType=self.filterType, filter=self.filter, stopOnFirst=True, timeoutSecond=5) - - # Devices will be a list: [CommissionableNode(), ...] -diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py -index 84a403643d..5e4def2760 100644 ---- a/src/controller/python/test/test_scripts/base.py -+++ b/src/controller/python/test/test_scripts/base.py -@@ -210,10 +210,10 @@ class BaseTestHelper: - return None - return ctypes.string_at(addrStrStorage).decode("utf-8") - -- def TestDiscovery(self, discriminator: int): -+ async def TestDiscovery(self, discriminator: int): - self.logger.info( - f"Discovering commissionable nodes with discriminator {discriminator}") -- res = self.devCtrl.DiscoverCommissionableNodes( -+ res = await self.devCtrl.DiscoverCommissionableNodes( - chip.discovery.FilterType.LONG_DISCRIMINATOR, discriminator, stopOnFirst=True, timeoutSecond=3) - if not res: - self.logger.info( -@@ -337,7 +337,7 @@ class BaseTestHelper: - - async def TestOnNetworkCommissioning(self, discriminator: int, setuppin: int, nodeid: int, ip_override: str = None): - self.logger.info("Testing discovery") -- device = self.TestDiscovery(discriminator=discriminator) -+ device = await self.TestDiscovery(discriminator=discriminator) - if not device: - self.logger.info("Failed to discover any devices.") - return False -diff --git a/src/controller/python/test/test_scripts/commissioning_test.py b/src/controller/python/test/test_scripts/commissioning_test.py -index c53ab00f33..ac7954595b 100755 ---- a/src/controller/python/test/test_scripts/commissioning_test.py -+++ b/src/controller/python/test/test_scripts/commissioning_test.py -@@ -125,7 +125,7 @@ async def main(): - nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=True) - - logger.info("Testing discovery") -- FailIfNot(test.TestDiscovery(discriminator=options.discriminator), -+ FailIfNot(await test.TestDiscovery(discriminator=options.discriminator), - "Failed to discover any devices.") - - FailIfNot(test.SetNetworkCommissioningParameters(dataset=TEST_THREAD_NETWORK_DATASET_TLV), -diff --git a/src/controller/python/test/test_scripts/failsafe_tests.py b/src/controller/python/test/test_scripts/failsafe_tests.py -index d27111cbf7..9e4d4bb14e 100755 ---- a/src/controller/python/test/test_scripts/failsafe_tests.py -+++ b/src/controller/python/test/test_scripts/failsafe_tests.py -@@ -88,7 +88,7 @@ async def main(): - nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=False) - - logger.info("Testing discovery") -- FailIfNot(test.TestDiscovery(discriminator=TEST_DISCRIMINATOR), -+ FailIfNot(await test.TestDiscovery(discriminator=TEST_DISCRIMINATOR), - "Failed to discover any devices.") - - FailIfNot(test.SetNetworkCommissioningParameters(dataset=TEST_THREAD_NETWORK_DATASET_TLV), -diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py -index 179dfa079a..27f8e98964 100755 ---- a/src/controller/python/test/test_scripts/mobile-device-test.py -+++ b/src/controller/python/test/test_scripts/mobile-device-test.py -@@ -59,7 +59,7 @@ ALL_TESTS = ['network_commissioning', 'datamodel'] - - async def ethernet_commissioning(test: BaseTestHelper, discriminator: int, setup_pin: int, address_override: str, device_nodeid: int): - logger.info("Testing discovery") -- device = test.TestDiscovery(discriminator=discriminator) -+ device = await test.TestDiscovery(discriminator=discriminator) - FailIfNot(device, "Failed to discover any devices.") - - address = device.addresses[0] -diff --git a/src/python_testing/TC_IDM_1_2.py b/src/python_testing/TC_IDM_1_2.py -index 18b727d962..dbf131e362 100644 ---- a/src/python_testing/TC_IDM_1_2.py -+++ b/src/python_testing/TC_IDM_1_2.py -@@ -195,7 +195,7 @@ class TC_IDM_1_2(MatterBaseTest): - new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=self.matter_test_config.fabric_id + 1) - TH2 = new_fabric_admin.NewController(nodeId=112233) - -- devices = TH2.DiscoverCommissionableNodes( -+ devices = await TH2.DiscoverCommissionableNodes( - filterType=Discovery.FilterType.LONG_DISCRIMINATOR, filter=discriminator, stopOnFirst=False) - # For some reason, the devices returned here aren't filtered, so filter ourselves - device = next(filter(lambda d: d.commissioningMode == 2 and d.longDiscriminator == discriminator, devices)) -diff --git a/src/python_testing/TC_OPCREDS_3_1.py b/src/python_testing/TC_OPCREDS_3_1.py -index 32f3ec7b1f..86b6d109be 100644 ---- a/src/python_testing/TC_OPCREDS_3_1.py -+++ b/src/python_testing/TC_OPCREDS_3_1.py -@@ -34,7 +34,7 @@ class TC_OPCREDS_3_1(MatterBaseTest): - if dev_ctrl is None: - dev_ctrl = self.default_controller - -- devices = dev_ctrl.DiscoverCommissionableNodes( -+ devices = await dev_ctrl.DiscoverCommissionableNodes( - filterType=Discovery.FilterType.LONG_DISCRIMINATOR, filter=longDiscriminator, stopOnFirst=False) - # For some reason, the devices returned here aren't filtered, so filter ourselves - device = next(filter(lambda d: d.commissioningMode == --- -2.45.2 - diff --git a/0031-Python-Adjust-logging-levels-in-Python-controller-34.patch b/0031-Python-Adjust-logging-levels-in-Python-controller-34.patch deleted file mode 100644 index 88d6281..0000000 --- a/0031-Python-Adjust-logging-levels-in-Python-controller-34.patch +++ /dev/null @@ -1,422 +0,0 @@ -From 67211f2c41779d428a509004f346eb72fc31ecb2 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Tue, 16 Jul 2024 15:52:08 +0100 -Subject: [PATCH] [Python] Adjust logging levels in Python controller (#34346) - -Use global loggers per-module which is more Pythonic and avoids -unnecessary logger instances. Also use the module name as the logger -name consistently. - -Avoid using the warning level for messages which are really only -informational. Also lower the log level of storage messages to debug, -as they are really not that important. Drop some unnecessary logs -like on every storage commit. ---- - .../python/chip/CertificateAuthority.py | 8 +++-- - src/controller/python/chip/ChipDeviceCtrl.py | 33 ++++++++++--------- - src/controller/python/chip/FabricAdmin.py | 10 +++--- - .../python/chip/clusters/Attribute.py | 19 ++++++----- - .../python/chip/storage/__init__.py | 31 ++++++++--------- - 5 files changed, 50 insertions(+), 51 deletions(-) - -diff --git a/src/controller/python/chip/CertificateAuthority.py b/src/controller/python/chip/CertificateAuthority.py -index bce921cfb7..0fbfcfdbdb 100644 ---- a/src/controller/python/chip/CertificateAuthority.py -+++ b/src/controller/python/chip/CertificateAuthority.py -@@ -28,6 +28,8 @@ from chip import ChipStack, FabricAdmin - from chip.native import PyChipError - from chip.storage import PersistentStorage - -+LOGGER = logging.getLogger(__name__) -+ - - class CertificateAuthority: - ''' This represents an operational Root Certificate Authority (CA) with a root key key pair with associated -@@ -64,7 +66,7 @@ class CertificateAuthority: - persistentStorage: An optional reference to a PersistentStorage object. If one is provided, it will pick that over - the default PersistentStorage object retrieved from the chipStack. - ''' -- self.logger().warning(f"New CertificateAuthority at index {caIndex}") -+ LOGGER.info(f"New CertificateAuthority at index {caIndex}") - - self._chipStack = chipStack - self._caIndex = caIndex -@@ -105,7 +107,7 @@ class CertificateAuthority: - if (not (self._isActive)): - raise RuntimeError("Object isn't active") - -- self.logger().warning("Loading fabric admins from storage...") -+ LOGGER.info("Loading fabric admins from storage...") - - caList = self._persistentStorage.GetReplKey(key='caList') - if (str(self._caIndex) not in caList): -@@ -244,7 +246,7 @@ class CertificateAuthorityManager: - if (not (self._isActive)): - raise RuntimeError("Object is not active") - -- self.logger().warning("Loading certificate authorities from storage...") -+ LOGGER.info("Loading certificate authorities from storage...") - - # - # Persist details to storage (read modify write). -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 208385f305..fc387728ce 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -60,6 +60,8 @@ __all__ = ["ChipDeviceController", "CommissioningParameters"] - # Defined in $CHIP_ROOT/src/lib/core/CHIPError.h - CHIP_ERROR_TIMEOUT: int = 50 - -+LOGGER = logging.getLogger(__name__) -+ - _DevicePairingDelegate_OnPairingCompleteFunct = CFUNCTYPE(None, PyChipError) - _DeviceUnpairingCompleteFunct = CFUNCTYPE(None, c_uint64, PyChipError) - _DevicePairingDelegate_OnCommissioningCompleteFunct = CFUNCTYPE( -@@ -309,15 +311,15 @@ class ChipDeviceControllerBase(): - def _set_dev_ctrl(self, devCtrl, pairingDelegate): - def HandleCommissioningComplete(nodeId: int, err: PyChipError): - if err.is_success: -- logging.info("Commissioning complete") -+ LOGGER.info("Commissioning complete") - else: -- logging.warning("Failed to commission: {}".format(err)) -+ LOGGER.warning("Failed to commission: {}".format(err)) - - if self._dmLib.pychip_TestCommissionerUsed(): - err = self._dmLib.pychip_GetCompletionError() - - if self._commissioning_context.future is None: -- logging.exception("HandleCommissioningComplete called unexpectedly") -+ LOGGER.exception("HandleCommissioningComplete called unexpectedly") - return - - if err.is_success: -@@ -331,14 +333,14 @@ class ChipDeviceControllerBase(): - def HandleOpenWindowComplete(nodeid: int, setupPinCode: int, setupManualCode: str, - setupQRCode: str, err: PyChipError) -> None: - if err.is_success: -- logging.info("Open Commissioning Window complete setting nodeid {} pincode to {}".format(nodeid, setupPinCode)) -+ LOGGER.info("Open Commissioning Window complete setting nodeid {} pincode to {}".format(nodeid, setupPinCode)) - commissioningParameters = CommissioningParameters( - setupPinCode=setupPinCode, setupManualCode=setupManualCode.decode(), setupQRCode=setupQRCode.decode()) - else: -- logging.warning("Failed to open commissioning window: {}".format(err)) -+ LOGGER.warning("Failed to open commissioning window: {}".format(err)) - - if self._open_window_context.future is None: -- logging.exception("HandleOpenWindowComplete called unexpectedly") -+ LOGGER.exception("HandleOpenWindowComplete called unexpectedly") - return - - if err.is_success: -@@ -348,12 +350,12 @@ class ChipDeviceControllerBase(): - - def HandleUnpairDeviceComplete(nodeid: int, err: PyChipError): - if err.is_success: -- logging.info("Succesfully unpaired device with nodeid {}".format(nodeid)) -+ LOGGER.info("Succesfully unpaired device with nodeid {}".format(nodeid)) - else: -- logging.warning("Failed to unpair device: {}".format(err)) -+ LOGGER.warning("Failed to unpair device: {}".format(err)) - - if self._unpair_device_context.future is None: -- logging.exception("HandleUnpairDeviceComplete called unexpectedly") -+ LOGGER.exception("HandleUnpairDeviceComplete called unexpectedly") - return - - if err.is_success: -@@ -363,9 +365,9 @@ class ChipDeviceControllerBase(): - - def HandlePASEEstablishmentComplete(err: PyChipError): - if not err.is_success: -- logging.warning("Failed to establish secure session to device: {}".format(err)) -+ LOGGER.warning("Failed to establish secure session to device: {}".format(err)) - else: -- logging.info("Established secure session with Device") -+ LOGGER.info("Established secure session with Device") - - if self._commissioning_context.future is not None: - # During Commissioning, HandlePASEEstablishmentComplete will also be called. -@@ -375,7 +377,7 @@ class ChipDeviceControllerBase(): - return - - if self._pase_establishment_context.future is None: -- logging.exception("HandlePASEEstablishmentComplete called unexpectedly") -+ LOGGER.exception("HandlePASEEstablishmentComplete called unexpectedly") - return - - if err.is_success: -@@ -794,7 +796,7 @@ class ChipDeviceControllerBase(): - res = self._ChipStack.Call(lambda: self._dmLib.pychip_GetDeviceBeingCommissioned( - self.devCtrl, nodeid, byref(returnDevice)), timeoutMs) - if res.is_success: -- logging.info('Using PASE connection') -+ LOGGER.info('Using PASE connection') - return DeviceProxyWrapper(returnDevice) - - class DeviceAvailableClosure(): -@@ -840,7 +842,7 @@ class ChipDeviceControllerBase(): - res = await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetDeviceBeingCommissioned( - self.devCtrl, nodeid, byref(returnDevice)), timeoutMs) - if res.is_success: -- logging.info('Using PASE connection') -+ LOGGER.info('Using PASE connection') - return DeviceProxyWrapper(returnDevice) - - eventLoop = asyncio.get_running_loop() -@@ -1196,7 +1198,6 @@ class ChipDeviceControllerBase(): - # Wildcard - return ClusterAttribute.EventPath() - elif not isinstance(pathTuple, tuple): -- logging.debug(type(pathTuple)) - if isinstance(pathTuple, int): - return ClusterAttribute.EventPath(EndpointId=pathTuple) - elif issubclass(pathTuple, ClusterObjects.Cluster): -@@ -1918,7 +1919,7 @@ class ChipDeviceController(ChipDeviceControllerBase): - - def NOCChainCallback(self, nocChain): - if self._issue_node_chain_context.future is None: -- logging.exception("NOCChainCallback while not expecting a callback") -+ LOGGER.exception("NOCChainCallback while not expecting a callback") - return - self._issue_node_chain_context.future.set_result(nocChain) - return -diff --git a/src/controller/python/chip/FabricAdmin.py b/src/controller/python/chip/FabricAdmin.py -index fc20327e62..d9e2e35cb2 100644 ---- a/src/controller/python/chip/FabricAdmin.py -+++ b/src/controller/python/chip/FabricAdmin.py -@@ -25,6 +25,8 @@ from chip import CertificateAuthority, ChipDeviceCtrl - from chip.crypto import p256keypair - from chip.native import GetLibraryHandle - -+LOGGER = logging.getLogger(__name__) -+ - - class FabricAdmin: - ''' Administers a fabric associated with a unique FabricID under a given CertificateAuthority -@@ -34,10 +36,6 @@ class FabricAdmin: - def _Handle(cls): - return GetLibraryHandle() - -- @classmethod -- def logger(cls): -- return logging.getLogger('FabricAdmin') -- - def __init__(self, certificateAuthority: CertificateAuthority.CertificateAuthority, vendorId: int, fabricId: int = 1): - ''' Initializes the object. - -@@ -60,7 +58,7 @@ class FabricAdmin: - self._fabricId = fabricId - self._certificateAuthority = certificateAuthority - -- self.logger().warning(f"New FabricAdmin: FabricId: 0x{self._fabricId:016X}, VendorId = 0x{self.vendorId:04X}") -+ LOGGER.info(f"New FabricAdmin: FabricId: 0x{self._fabricId:016X}, VendorId = 0x{self.vendorId:04X}") - - self._isActive = True - self._activeControllers = [] -@@ -94,7 +92,7 @@ class FabricAdmin: - if (nodeId in nodeIdList): - raise RuntimeError(f"Provided NodeId {nodeId} collides with an existing controller instance!") - -- self.logger().warning( -+ LOGGER.info( - f"Allocating new controller with CaIndex: {self._certificateAuthority.caIndex}, " - f"FabricId: 0x{self._fabricId:016X}, NodeId: 0x{nodeId:016X}, CatTags: {catTags}") - -diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py -index 16db825c12..69c64a3ffc 100644 ---- a/src/controller/python/chip/clusters/Attribute.py -+++ b/src/controller/python/chip/clusters/Attribute.py -@@ -40,6 +40,8 @@ from rich.pretty import pprint - - from .ClusterObjects import Cluster, ClusterAttributeDescriptor, ClusterEvent - -+LOGGER = logging.getLogger(__name__) -+ - - @unique - class EventTimestampType(Enum): -@@ -592,7 +594,7 @@ class SubscriptionTransaction: - - def Shutdown(self): - if (self._isDone): -- print("Subscription was already terminated previously!") -+ LOGGER.warning("Subscription 0x%08x was already terminated previously!", self.subscriptionId) - return - - handle = chip.native.GetLibraryHandle() -@@ -698,7 +700,7 @@ class AsyncReadTransaction: - self._changedPathSet.add(path) - - except Exception as ex: -- logging.exception(ex) -+ LOGGER.exception(ex) - - def handleEventData(self, header: EventHeader, path: EventPath, data: bytes, status: int): - try: -@@ -716,12 +718,12 @@ class AsyncReadTransaction: - try: - eventValue = eventType.FromTLV(data) - except Exception as ex: -- logging.error( -+ LOGGER.error( - f"Error convering TLV to Cluster Object for path: Endpoint = {path.EndpointId}/" - f"Cluster = {path.ClusterId}/Event = {path.EventId}") -- logging.error( -+ LOGGER.error( - f"Failed Cluster Object: {str(eventType)}") -- logging.error(ex) -+ LOGGER.error(ex) - eventValue = ValueDecodeFailure( - tlvData, ex) - -@@ -738,7 +740,7 @@ class AsyncReadTransaction: - eventResult, self._subscription_handler) - - except Exception as ex: -- logging.exception(ex) -+ LOGGER.exception(ex) - - def handleError(self, chipError: PyChipError): - self._resultError = chipError -@@ -749,7 +751,6 @@ class AsyncReadTransaction: - self, subscriptionId, self._devCtrl) - self._future.set_result(self._subscription_handler) - else: -- logging.info("Re-subscription succeeded!") - if self._subscription_handler._onResubscriptionSucceededCb is not None: - if (self._subscription_handler._onResubscriptionSucceededCb_isAsync): - self._event_loop.create_task( -@@ -785,7 +786,7 @@ class AsyncReadTransaction: - attribute_path = TypedAttributePath(Path=change) - except (KeyError, ValueError) as err: - # path could not be resolved into a TypedAttributePath -- logging.getLogger(__name__).exception(err) -+ LOGGER.exception(err) - continue - self._subscription_handler.OnAttributeChangeCb( - attribute_path, self._subscription_handler) -@@ -844,7 +845,7 @@ class AsyncWriteTransaction: - imStatus = chip.interaction_model.Status(status) - self._resultData.append(AttributeWriteResult(Path=path, Status=imStatus)) - except ValueError as ex: -- logging.exception(ex) -+ LOGGER.exception(ex) - - def handleError(self, chipError: PyChipError): - self._resultError = chipError -diff --git a/src/controller/python/chip/storage/__init__.py b/src/controller/python/chip/storage/__init__.py -index 385efca6be..20432dcd01 100644 ---- a/src/controller/python/chip/storage/__init__.py -+++ b/src/controller/python/chip/storage/__init__.py -@@ -29,6 +29,8 @@ from typing import Dict - import chip.exceptions - import chip.native - -+LOGGER = logging.getLogger(__name__) -+ - _SyncSetKeyValueCbFunct = CFUNCTYPE( - None, py_object, c_char_p, POINTER(c_char), c_uint16) - _SyncGetKeyValueCbFunct = CFUNCTYPE( -@@ -91,9 +93,6 @@ class PersistentStorage: - - Object must be resident before the Matter stack starts up and last past its shutdown. - ''' -- @classmethod -- def logger(cls): -- return logging.getLogger('PersistentStorage') - - def __init__(self, path: str = None, jsonData: Dict = None): - ''' Initializes the object with either a path to a JSON file that contains the configuration OR -@@ -109,9 +108,9 @@ class PersistentStorage: - raise ValueError("Can't provide both a valid path and jsonData") - - if (path is not None): -- self.logger().warn(f"Initializing persistent storage from file: {path}") -+ LOGGER.info(f"Initializing persistent storage from file: {path}") - else: -- self.logger().warn("Initializing persistent storage from dict") -+ LOGGER.info("Initializing persistent storage from dict") - - self._handle = chip.native.GetLibraryHandle() - self._isActive = True -@@ -125,24 +124,24 @@ class PersistentStorage: - self._file.seek(0) - - if (size != 0): -- self.logger().warn(f"Loading configuration from {path}...") -+ LOGGER.info(f"Loading configuration from {path}...") - self._jsonData = json.load(self._file) - else: - self._jsonData = {} - - except Exception as ex: -- logging.error(ex) -- logging.critical(f"Could not load configuration from {path} - resetting configuration...") -+ LOGGER.error(ex) -+ LOGGER.critical(f"Could not load configuration from {path} - resetting configuration...") - self._jsonData = {} - else: - self._jsonData = jsonData - - if ('sdk-config' not in self._jsonData): -- logging.warn("No valid SDK configuration present - clearing out configuration") -+ LOGGER.warn("No valid SDK configuration present - clearing out configuration") - self._jsonData['sdk-config'] = {} - - if ('repl-config' not in self._jsonData): -- logging.warn("No valid REPL configuration present - clearing out configuration") -+ LOGGER.warn("No valid REPL configuration present - clearing out configuration") - self._jsonData['repl-config'] = {} - - # Clear out the file so that calling 'Commit' will re-open the file at that time in write mode. -@@ -166,7 +165,6 @@ class PersistentStorage: - ''' Commits the cached JSON configuration to file (if one was provided in the constructor). - Otherwise, this is a no-op. - ''' -- self.logger().info("Committing...") - - if (self._path is None): - return -@@ -175,9 +173,8 @@ class PersistentStorage: - try: - self._file = open(self._path, 'w') - except Exception as ex: -- logging.warn( -- f"Could not open {self._path} for writing configuration. Error:") -- logging.warn(ex) -+ LOGGER.error( -+ f"Could not open {self._path} for writing configuration. Error: {ex}") - return - - self._file.seek(0) -@@ -188,7 +185,7 @@ class PersistentStorage: - def SetReplKey(self, key: str, value): - ''' Set a REPL key to a specific value. Creates the key if one doesn't exist already. - ''' -- self.logger().info(f"SetReplKey: {key} = {value}") -+ LOGGER.debug(f"SetReplKey: {key} = {value}") - - if (key is None or key == ''): - raise ValueError("Invalid Key") -@@ -212,7 +209,7 @@ class PersistentStorage: - def SetSdkKey(self, key: str, value: bytes): - ''' Set an SDK key to a specific value. Creates the key if one doesn't exist already. - ''' -- self.logger().info(f"SetSdkKey: {key} = {value}") -+ LOGGER.debug(f"SetSdkKey: {key} = {value}") - - if (key is None or key == ''): - raise ValueError("Invalid Key") -@@ -236,7 +233,7 @@ class PersistentStorage: - def DeleteSdkKey(self, key: str): - ''' Deletes an SDK key if one exists. - ''' -- self.logger().info(f"DeleteSdkKey: {key}") -+ LOGGER.debug(f"DeleteSdkKey: {key}") - - del (self._jsonData['sdk-config'][key]) - self.Commit() --- -2.45.2 - diff --git a/0032-Python-Avoid-RuntimeException-if-APIs-with-future-ra.patch b/0032-Python-Avoid-RuntimeException-if-APIs-with-future-ra.patch deleted file mode 100644 index ad213e0..0000000 --- a/0032-Python-Avoid-RuntimeException-if-APIs-with-future-ra.patch +++ /dev/null @@ -1,288 +0,0 @@ -From 93674dd02f8533ef35afa5c61180507e5ff75fe7 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Wed, 17 Jul 2024 18:26:56 +0100 -Subject: [PATCH] [Python] Avoid RuntimeException if APIs with future raise an - error (#34354) - -Currently, when calling an API which uses a future causes an error -(e.g. CommissionWithCode with an invalid code), then the API call -already returns an error. In this case the call `raise_on_error()` on -the returned PyChipError object make sure that an exception is raised. -However, this also causes the `CallbackContext` context manager to -exit. - -At this point the future is initialized but never completed, which -triggers the previously introduced sanity check in `CallbackContext`: -`RuntimeError("CallbackContext future not completed")`. - -Remove the RuntimeError as existing the context manager early without -completing the future is a use case (when the call setting up the -callback raises an exception). - -Instead, just cancel the future in the context manager if it hasn't -been complete yet, in case someone has a reference to it and expects -it to complete. - -Also, since most API calls return PyChipError, this changes -`CallAsync()` to raise an exception by default instead of returning a -PyChipError object. If the PyChipError object is required or an API -returns something else, the CallAsyncWithResult() method can be used. ---- - src/controller/python/chip/ChipDeviceCtrl.py | 49 ++++++++----------- - src/controller/python/chip/ChipStack.py | 7 ++- - .../python/chip/clusters/Attribute.py | 2 +- - .../python/chip/clusters/Command.py | 4 +- - 4 files changed, 30 insertions(+), 32 deletions(-) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index fc387728ce..3ea996e53c 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -162,7 +162,10 @@ class CallbackContext: - - async def __aexit__(self, exc_type, exc_value, traceback): - if not self._future.done(): -- raise RuntimeError("CallbackContext future not completed") -+ # In case the initial call (which sets up for the callback) fails, -+ # the future will never be used actually. So just cancel it here -+ # for completeness, in case somebody is expecting it to be completed. -+ self._future.cancel() - self._future = None - self._lock.release() - -@@ -508,11 +511,10 @@ class ChipDeviceControllerBase(): - - async with self._commissioning_context as ctx: - self._enablePairingCompleteCallback(True) -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectBLE( - self.devCtrl, discriminator, isShortDiscriminator, setupPinCode, nodeid) - ) -- res.raise_on_error() - - return await asyncio.futures.wrap_future(ctx.future) - -@@ -520,11 +522,11 @@ class ChipDeviceControllerBase(): - self.CheckIsActive() - - async with self._unpair_device_context as ctx: -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_UnpairDevice( - self.devCtrl, nodeid, self.cbHandleDeviceUnpairCompleteFunct) - ) -- res.raise_on_error() -+ - return await asyncio.futures.wrap_future(ctx.future) - - def CloseBLEConnection(self): -@@ -561,8 +563,7 @@ class ChipDeviceControllerBase(): - - async with self._pase_establishment_context as ctx: - self._enablePairingCompleteCallback(True) -- res = await self._ChipStack.CallAsync(callFunct) -- res.raise_on_error() -+ await self._ChipStack.CallAsync(callFunct) - await asyncio.futures.wrap_future(ctx.future) - - async def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid: int) -> None: -@@ -661,13 +662,12 @@ class ChipDeviceControllerBase(): - # Discovery is also used during commissioning. Make sure this manual discovery - # and commissioning attempts do not interfere with each other. - async with self._commissioning_lock: -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes( - self.devCtrl, int(filterType), str(filter).encode("utf-8"))) -- res.raise_on_error() - - async def _wait_discovery(): -- while not await self._ChipStack.CallAsync( -+ while not await self._ChipStack.CallAsyncWithResult( - lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)): - await asyncio.sleep(0.1) - return -@@ -681,9 +681,8 @@ class ChipDeviceControllerBase(): - # Expected timeout, do nothing - pass - finally: -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl)) -- res.raise_on_error() - - return await self.GetDiscoveredDevices() - -@@ -701,7 +700,7 @@ class ChipDeviceControllerBase(): - self._dmLib.pychip_DeviceController_IterateDiscoveredCommissionableNodes(devCtrl.devCtrl, HandleDevice) - return devices - -- return await self._ChipStack.CallAsync(lambda: GetDevices(self)) -+ return await self._ChipStack.CallAsyncWithResult(lambda: GetDevices(self)) - - def GetIPForDiscoveredDevice(self, idx, addrStr, length): - self.CheckIsActive() -@@ -733,11 +732,10 @@ class ChipDeviceControllerBase(): - self.CheckIsActive() - - async with self._open_window_context as ctx: -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_OpenCommissioningWindow( - self.devCtrl, self.pairingDelegate, nodeid, timeout, iteration, discriminator, option) - ) -- res.raise_on_error() - - return await asyncio.futures.wrap_future(ctx.future) - -@@ -839,7 +837,7 @@ class ChipDeviceControllerBase(): - - if allowPASE: - returnDevice = c_void_p(None) -- res = await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetDeviceBeingCommissioned( -+ res = await self._ChipStack.CallAsyncWithResult(lambda: self._dmLib.pychip_GetDeviceBeingCommissioned( - self.devCtrl, nodeid, byref(returnDevice)), timeoutMs) - if res.is_success: - LOGGER.info('Using PASE connection') -@@ -869,10 +867,9 @@ class ChipDeviceControllerBase(): - - closure = DeviceAvailableClosure(eventLoop, future) - ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure)) -- res = await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId( -+ await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId( - self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback), - timeoutMs) -- res.raise_on_error() - - # The callback might have been received synchronously (during self._ChipStack.CallAsync()). - # In that case the Future has already been set it will return immediately -@@ -1752,11 +1749,10 @@ class ChipDeviceController(ChipDeviceControllerBase): - - async with self._commissioning_context as ctx: - self._enablePairingCompleteCallback(False) -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_Commission( - self.devCtrl, nodeid) - ) -- res.raise_on_error() - - return await asyncio.futures.wrap_future(ctx.future) - -@@ -1868,11 +1864,10 @@ class ChipDeviceController(ChipDeviceControllerBase): - - async with self._commissioning_context as ctx: - self._enablePairingCompleteCallback(True) -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_OnNetworkCommission( - self.devCtrl, self.pairingDelegate, nodeId, setupPinCode, int(filterType), str(filter).encode("utf-8") if filter is not None else None, discoveryTimeoutMsec) - ) -- res.raise_on_error() - - return await asyncio.futures.wrap_future(ctx.future) - -@@ -1889,11 +1884,10 @@ class ChipDeviceController(ChipDeviceControllerBase): - - async with self._commissioning_context as ctx: - self._enablePairingCompleteCallback(True) -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectWithCode( - self.devCtrl, setupPayload.encode("utf-8"), nodeid, discoveryType.value) - ) -- res.raise_on_error() - - return await asyncio.futures.wrap_future(ctx.future) - -@@ -1909,11 +1903,10 @@ class ChipDeviceController(ChipDeviceControllerBase): - - async with self._commissioning_context as ctx: - self._enablePairingCompleteCallback(True) -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_ConnectIP( - self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid) - ) -- res.raise_on_error() - - return await asyncio.futures.wrap_future(ctx.future) - -@@ -1930,11 +1923,11 @@ class ChipDeviceController(ChipDeviceControllerBase): - self.CheckIsActive() - - async with self._issue_node_chain_context as ctx: -- res = await self._ChipStack.CallAsync( -+ await self._ChipStack.CallAsync( - lambda: self._dmLib.pychip_DeviceController_IssueNOCChain( - self.devCtrl, py_object(self), csr.NOCSRElements, len(csr.NOCSRElements), nodeId) - ) -- res.raise_on_error() -+ - return await asyncio.futures.wrap_future(ctx.future) - - -diff --git a/src/controller/python/chip/ChipStack.py b/src/controller/python/chip/ChipStack.py -index dc4efc223f..b717859c70 100644 ---- a/src/controller/python/chip/ChipStack.py -+++ b/src/controller/python/chip/ChipStack.py -@@ -216,7 +216,7 @@ class ChipStack(object): - ''' - return self.PostTaskOnChipThread(callFunct).Wait(timeoutMs) - -- async def CallAsync(self, callFunct, timeoutMs: int = None): -+ async def CallAsyncWithResult(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. - ''' -@@ -232,6 +232,11 @@ class ChipStack(object): - - return await asyncio.wait_for(callObj.future, timeoutMs / 1000 if timeoutMs else None) - -+ async def CallAsync(self, callFunct, timeoutMs: int = None) -> None: -+ '''Run a Python function on CHIP stack, and wait for the response.''' -+ res: PyChipError = await self.CallAsyncWithResult(callFunct, timeoutMs) -+ res.raise_on_error() -+ - def PostTaskOnChipThread(self, callFunct) -> AsyncCallableHandle: - '''Run a Python function on CHIP stack, and wait for the response. - This function will post a task on CHIP mainloop, and return an object with Wait() method for getting the result. -diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py -index 69c64a3ffc..31e8444c70 100644 ---- a/src/controller/python/chip/clusters/Attribute.py -+++ b/src/controller/python/chip/clusters/Attribute.py -@@ -482,7 +482,7 @@ class SubscriptionTransaction: - - async def TriggerResubscribeIfScheduled(self, reason: str): - handle = chip.native.GetLibraryHandle() -- await builtins.chipStack.CallAsync( -+ await builtins.chipStack.CallAsyncWithResult( - lambda: handle.pychip_ReadClient_TriggerResubscribeIfScheduled( - self._readTransaction._pReadClient, reason.encode("utf-8")) - ) -diff --git a/src/controller/python/chip/clusters/Command.py b/src/controller/python/chip/clusters/Command.py -index 93951338f9..785bb3d3da 100644 ---- a/src/controller/python/chip/clusters/Command.py -+++ b/src/controller/python/chip/clusters/Command.py -@@ -316,7 +316,7 @@ async def SendCommand(future: Future, eventLoop, responseType: Type, device, com - - payloadTLV = payload.ToTLV() - ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) -- return await builtins.chipStack.CallAsync( -+ return await builtins.chipStack.CallAsyncWithResult( - lambda: handle.pychip_CommandSender_SendCommand( - ctypes.py_object(transaction), device, - c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), commandPath.EndpointId, -@@ -388,7 +388,7 @@ async def SendBatchCommands(future: Future, eventLoop, device, commands: List[In - transaction = AsyncBatchCommandsTransaction(future, eventLoop, responseTypes) - ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) - -- return await builtins.chipStack.CallAsync( -+ return await builtins.chipStack.CallAsyncWithResult( - lambda: handle.pychip_CommandSender_SendBatchCommands( - py_object(transaction), device, - c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), --- -2.45.2 - diff --git a/0033-Python-Fix-build-without-host-unit-test-config-34368.patch b/0033-Python-Fix-build-without-host-unit-test-config-34368.patch deleted file mode 100644 index 7dc1a36..0000000 --- a/0033-Python-Fix-build-without-host-unit-test-config-34368.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 7210d2ded0bcf5c5989cef8af34f84828116ada3 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Wed, 17 Jul 2024 19:10:18 +0100 -Subject: [PATCH] [Python] Fix build without host unit test config (#34368) - -Allow to build the Python controller without host unit test config -enabled. ---- - src/controller/python/chip/clusters/command.cpp | 10 +++++++++- - 1 file changed, 9 insertions(+), 1 deletion(-) - -diff --git a/src/controller/python/chip/clusters/command.cpp b/src/controller/python/chip/clusters/command.cpp -index d65766b798..037c848f96 100644 ---- a/src/controller/python/chip/clusters/command.cpp -+++ b/src/controller/python/chip/clusters/command.cpp -@@ -203,10 +203,13 @@ PyChipError SendBatchCommandsInternal(void * appContext, DeviceProxy * device, u - CHIP_ERROR err = CHIP_NO_ERROR; - - bool testOnlySuppressTimedRequestMessage = false; -- uint16_t * testOnlyCommandRefsOverride = nullptr; -+#if CONFIG_BUILD_FOR_HOST_UNIT_TEST -+ uint16_t * testOnlyCommandRefsOverride = nullptr; -+#endif - - VerifyOrReturnError(device->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); - -+#if CONFIG_BUILD_FOR_HOST_UNIT_TEST - // Test only override validation checks and setup - if (testOnlyOverrides != nullptr) - { -@@ -228,6 +231,7 @@ PyChipError SendBatchCommandsInternal(void * appContext, DeviceProxy * device, u - config.SetRemoteMaxPathsPerInvoke(testOnlyOverrides->overrideRemoteMaxPathsPerInvoke); - } - else -+#endif - { - auto remoteSessionParameters = device->GetSecureSession().Value()->GetRemoteSessionParameters(); - config.SetRemoteMaxPathsPerInvoke(remoteSessionParameters.GetMaxPathsPerInvoke()); -@@ -273,6 +277,7 @@ PyChipError SendBatchCommandsInternal(void * appContext, DeviceProxy * device, u - Optional timedRequestTimeout = - timedRequestTimeoutMs != 0 ? Optional(timedRequestTimeoutMs) : Optional::Missing(); - CommandSender::FinishCommandParameters finishCommandParams(timedRequestTimeout); -+#if CONFIG_BUILD_FOR_HOST_UNIT_TEST - if (testOnlyCommandRefsOverride != nullptr) - { - finishCommandParams.commandRef.SetValue(testOnlyCommandRefsOverride[i]); -@@ -291,6 +296,7 @@ PyChipError SendBatchCommandsInternal(void * appContext, DeviceProxy * device, u - callback->AddCommandRefToIndexLookup(finishCommandParams.commandRef.Value(), i); - } - else -+#endif - { - SuccessOrExit(err = callback->AddCommandRefToIndexLookup(finishCommandParams.commandRef.Value(), i)); - } -@@ -300,12 +306,14 @@ PyChipError SendBatchCommandsInternal(void * appContext, DeviceProxy * device, u - Optional interactionTimeout = interactionTimeoutMs != 0 - ? MakeOptional(System::Clock::Milliseconds32(interactionTimeoutMs)) - : Optional::Missing(); -+#if CONFIG_BUILD_FOR_HOST_UNIT_TEST - if (testOnlySuppressTimedRequestMessage) - { - SuccessOrExit(err = sender->TestOnlyCommandSenderTimedRequestFlagWithNoTimedInvoke(device->GetSecureSession().Value(), - interactionTimeout)); - } - else -+#endif - { - SuccessOrExit(err = sender->SendCommandRequest(device->GetSecureSession().Value(), interactionTimeout)); - } --- -2.45.2 - diff --git a/0034-Python-Fix-subscription-error-handling-and-re-subscr.patch b/0034-Python-Fix-subscription-error-handling-and-re-subscr.patch deleted file mode 100644 index dca247e..0000000 --- a/0034-Python-Fix-subscription-error-handling-and-re-subscr.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 5ab917909f15fec74778cc2e805b254aaccef1c3 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 18 Jul 2024 07:39:38 +0100 -Subject: [PATCH] [Python] Fix subscription error handling and re-subscription - (#34372) - -* [Python] Fix error callback in AsyncReadTransaction - -Currently the error callback is only called when the future is not done -yet and the subscription handler exists. However, the subscription -handler only gets initialized on successful subscription, which is also -where the future gets set to done. So there is no situation where the -error callback is being called, currently. - -Fix this by calling the error callback straight from the Matter SDK -Thread when the subscription handler exists. This makes it independent -of the future. - -* [Python] Update subscription id on re-subscribe - -Make sure we update the subscription ID in the subscription established -callback when the subscription handler already exists. This makes sure -that we have the correct subscription ID stored in the -`SubscriptionTransaction` object after successfully re-subscribe too. ---- - src/controller/python/chip/clusters/Attribute.py | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py -index 31e8444c70..63ca02d8df 100644 ---- a/src/controller/python/chip/clusters/Attribute.py -+++ b/src/controller/python/chip/clusters/Attribute.py -@@ -743,6 +743,8 @@ class AsyncReadTransaction: - LOGGER.exception(ex) - - def handleError(self, chipError: PyChipError): -+ if self._subscription_handler: -+ self._subscription_handler.OnErrorCb(chipError.code, self._subscription_handler) - self._resultError = chipError - - def _handleSubscriptionEstablished(self, subscriptionId): -@@ -751,6 +753,7 @@ class AsyncReadTransaction: - self, subscriptionId, self._devCtrl) - self._future.set_result(self._subscription_handler) - else: -+ self._subscription_handler._subscriptionId = subscriptionId - if self._subscription_handler._onResubscriptionSucceededCb is not None: - if (self._subscription_handler._onResubscriptionSucceededCb_isAsync): - self._event_loop.create_task( -@@ -807,10 +810,7 @@ class AsyncReadTransaction: - # - if not self._future.done(): - if self._resultError is not None: -- if self._subscription_handler: -- self._subscription_handler.OnErrorCb(self._resultError.code, self._subscription_handler) -- else: -- self._future.set_exception(self._resultError.to_exception()) -+ self._future.set_exception(self._resultError.to_exception()) - else: - self._future.set_result(AsyncReadTransaction.ReadResponse( - attributes=self._cache.attributeCache, events=self._events, tlvAttributes=self._cache.attributeTLVCache)) --- -2.45.2 - diff --git a/0035-Python-Only-auto-re-subscribe-after-initial-subscrip.patch b/0035-Python-Only-auto-re-subscribe-after-initial-subscrip.patch deleted file mode 100644 index d2eb1b5..0000000 --- a/0035-Python-Only-auto-re-subscribe-after-initial-subscrip.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 1ef35acf92542d14bf34061f8013c678743c12a4 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Thu, 18 Jul 2024 07:49:05 +0100 -Subject: [PATCH] [Python] Only auto re-subscribe after initial subscription - (#34370) - -The subscription logic waits for the first successful subscription -before the Read() call is being returned (the future is awaited which -is only released on handleSubscriptionEstablished). If the first -subscription attempt fails (e.g. because the CASE session doesn't -establish) the Read() never returns, not with an error but also not -with a subscription transaction. And since the Python side has no -access to the SubscriptionTransaction object at this point yet, -there is also no way to stop this subscription attempt. - -With this change, we only resubscribe if the initial subscription was -successful. This changes semantics slightly, but really allows the -caller to decide if it wants to continue try to establish the -subscription. ---- - src/controller/python/chip/clusters/attribute.cpp | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/src/controller/python/chip/clusters/attribute.cpp b/src/controller/python/chip/clusters/attribute.cpp -index 7c5b2c906a..421284a0ae 100644 ---- a/src/controller/python/chip/clusters/attribute.cpp -+++ b/src/controller/python/chip/clusters/attribute.cpp -@@ -145,18 +145,20 @@ public: - - void OnSubscriptionEstablished(SubscriptionId aSubscriptionId) override - { -+ // Only enable auto resubscribe if the subscription is established successfully. -+ mAutoResubscribeNeeded = mAutoResubscribe; - gOnSubscriptionEstablishedCallback(mAppContext, aSubscriptionId); - } - - CHIP_ERROR OnResubscriptionNeeded(ReadClient * apReadClient, CHIP_ERROR aTerminationCause) override - { -- if (mAutoResubscribe) -+ if (mAutoResubscribeNeeded) - { - ReturnErrorOnFailure(ReadClient::Callback::OnResubscriptionNeeded(apReadClient, aTerminationCause)); - } - gOnResubscriptionAttemptedCallback(mAppContext, ToPyChipError(aTerminationCause), - apReadClient->ComputeTimeTillNextSubscription()); -- if (mAutoResubscribe) -+ if (mAutoResubscribeNeeded) - { - return CHIP_NO_ERROR; - } -@@ -242,7 +244,8 @@ private: - PyObject * mAppContext; - - std::unique_ptr mReadClient; -- bool mAutoResubscribe = true; -+ bool mAutoResubscribe = true; -+ bool mAutoResubscribeNeeded = false; - }; - - extern "C" { --- -2.45.2 - diff --git a/0036-Python-Avoid-InvalidStateError-on-cancel-35380.patch b/0036-Python-Avoid-InvalidStateError-on-cancel-35380.patch deleted file mode 100644 index 75a2b16..0000000 --- a/0036-Python-Avoid-InvalidStateError-on-cancel-35380.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 3f6ba15267c08c8665edafd27043de6a6a3c60ee Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Wed, 4 Sep 2024 14:49:53 +0200 -Subject: [PATCH] [Python] Avoid InvalidStateError on cancel (#35380) - -When the co-routine GetConnectedDevice() gets cancelled, the wait_for -call will cancel the future we are waiting for. However, the SDK still -calls the _DeviceAvailableCallback with an error (CHIP Error 0x00000074: -The operation has been cancelled). However, we can't set the future -result at this point as the co-routine is already cancelled. - -Simply check the future state before setting the result. ---- - src/controller/python/chip/ChipDeviceCtrl.py | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index 3ea996e53c..bb4119f1bc 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -854,6 +854,8 @@ class ChipDeviceControllerBase(): - self._future = future - - def _deviceAvailable(self): -+ if self._future.cancelled(): -+ return - if self._returnDevice.value is not None: - self._future.set_result(self._returnDevice) - else: --- -2.46.0 - diff --git a/0037-python-Add-direct-attribute-paths-to-Read-34833.patch b/0037-python-Add-direct-attribute-paths-to-Read-34833.patch deleted file mode 100644 index 3cb22b6..0000000 --- a/0037-python-Add-direct-attribute-paths-to-Read-34833.patch +++ /dev/null @@ -1,99 +0,0 @@ -From d586d15f83c5fb5fff30b7b7a91b0aced387aa5f Mon Sep 17 00:00:00 2001 -From: C Freeman -Date: Wed, 7 Aug 2024 23:05:32 -0400 -Subject: [PATCH] python: Add direct attribute paths to Read (#34833) - -* python: Add direct attribute paths to Read - -Supports one particular use case: read one or all endpoints, -all clusters, specific (global) attribute. See spec 8.9.2.4. This -is an allowed wildcard construct that is not currently expressable -in the API. - -Test: Used on wildcard read for matter_testing_support. This is - therefore tested on any test using that decorator - switch - and timesync. - -* Restyled by isort - ---------- - -Co-authored-by: Restyled.io ---- - src/controller/python/chip/ChipDeviceCtrl.py | 18 +++++++++++++++--- - src/python_testing/matter_testing_support.py | 1 + - 2 files changed, 16 insertions(+), 3 deletions(-) - -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index bb4119f1bc..e337bb0c5d 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -1142,8 +1142,12 @@ class ChipDeviceControllerBase(): - # Wildcard attribute id - typing.Tuple[int, typing.Type[ClusterObjects.Cluster]], - # Concrete path -- typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]] -+ typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -+ # Directly specified attribute path -+ ClusterAttribute.AttributePath - ]): -+ if isinstance(pathTuple, ClusterAttribute.AttributePath): -+ return pathTuple - if pathTuple == ('*') or pathTuple == (): - # Wildcard - return ClusterAttribute.AttributePath() -@@ -1228,7 +1232,9 @@ class ChipDeviceControllerBase(): - # Wildcard attribute id - typing.Tuple[int, typing.Type[ClusterObjects.Cluster]], - # Concrete path -- typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]] -+ typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -+ # Directly specified attribute path -+ ClusterAttribute.AttributePath - ]] = None, - dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None, events: typing.List[ - typing.Union[ -@@ -1266,6 +1272,8 @@ class ChipDeviceControllerBase(): - ReadAttribute(1, [ Clusters.BasicInformation ] ) -- case 5 above. - ReadAttribute(1, [ (1, Clusters.BasicInformation.Attributes.Location ] ) -- case 1 above. - -+ An AttributePath can also be specified directly by [chip.cluster.Attribute.AttributePath(...)] -+ - dataVersionFilters: A list of tuples of (endpoint, cluster, data version). - - events: A list of tuples of varying types depending on the type of read being requested: -@@ -1326,7 +1334,9 @@ class ChipDeviceControllerBase(): - # Wildcard attribute id - typing.Tuple[int, typing.Type[ClusterObjects.Cluster]], - # Concrete path -- typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]] -+ typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -+ # Directly specified attribute path -+ ClusterAttribute.AttributePath - ]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None, - returnClusterObject: bool = False, - reportInterval: typing.Tuple[int, int] = None, -@@ -1350,6 +1360,8 @@ class ChipDeviceControllerBase(): - ReadAttribute(1, [ Clusters.BasicInformation ] ) -- case 5 above. - ReadAttribute(1, [ (1, Clusters.BasicInformation.Attributes.Location ] ) -- case 1 above. - -+ An AttributePath can also be specified directly by [chip.cluster.Attribute.AttributePath(...)] -+ - returnClusterObject: This returns the data as consolidated cluster objects, with all attributes for a cluster inside - a single cluster-wide cluster object. - -diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py -index a3bd5d2837..93a89192dc 100644 ---- a/src/python_testing/matter_testing_support.py -+++ b/src/python_testing/matter_testing_support.py -@@ -53,6 +53,7 @@ import chip.logging - import chip.native - from chip import discovery - from chip.ChipStack import ChipStack -+from chip.clusters import Attribute - from chip.clusters import ClusterObjects as ClusterObjects - from chip.clusters.Attribute import EventReadResult, SubscriptionTransaction - from chip.exceptions import ChipStackError --- -2.46.0 - diff --git a/0038-Python-Process-attribute-cache-updates-in-Python-thr.patch b/0038-Python-Process-attribute-cache-updates-in-Python-thr.patch deleted file mode 100644 index 89a954e..0000000 --- a/0038-Python-Process-attribute-cache-updates-in-Python-thr.patch +++ /dev/null @@ -1,659 +0,0 @@ -From 6da341868cdd64ac9064395a9bde6e5954e56579 Mon Sep 17 00:00:00 2001 -From: Stefan Agner -Date: Tue, 17 Sep 2024 15:51:16 +0200 -Subject: [PATCH] [Python] Process attribute cache updates in Python thread - (#35557) - -* [Python] Process attribute cache updates in Python thread - -Instead of processing the attribute update in the SDK thread, process -them on request in the Python thread. This avoids acks being sent back -too late to the device after the last DataReport if there are many -attribute updates sent at once. - -Currently still the same data model and processing is done. There is -certainly also room for optimization to make this more efficient. - -* Get updated attribute values - -Make sure to get the attribute values again after each command to get -the updated attribute cache. - -* Reference ReadEvent/ReadAttribute APIs on dev controller object ---- - .../repl/Matter_Basic_Interactions.ipynb | 4 +- - src/controller/python/chip/ChipDeviceCtrl.py | 130 ++++++------ - .../python/chip/clusters/Attribute.py | 76 ++++--- - .../test/test_scripts/cluster_objects.py | 9 +- - src/python_testing/matter_testing_support.py | 185 ++++++++++++++++++ - 5 files changed, 302 insertions(+), 102 deletions(-) - -diff --git a/docs/guides/repl/Matter_Basic_Interactions.ipynb b/docs/guides/repl/Matter_Basic_Interactions.ipynb -index 41c1c78865..bc021aec73 100644 ---- a/docs/guides/repl/Matter_Basic_Interactions.ipynb -+++ b/docs/guides/repl/Matter_Basic_Interactions.ipynb -@@ -3504,7 +3504,7 @@ - "source": [ - "#### Read Events:\n", - "\n", -- "A `ReadEvents` API exists that behaves similarly to the `ReadAttributes` API. It permits the same degrees of wildcard expression as its counterpart and follows the same format for expressing all wildcard permutations." -+ "A `ReadEvent` API exists that behaves similarly to the `ReadAttribute` API. It permits the same degrees of wildcard expression as its counterpart and follows the same format for expressing all wildcard permutations." - ] - }, - { -@@ -3609,7 +3609,7 @@ - "source": [ - "### Subscription Interaction\n", - "\n", -- "To subscribe to a Node, the same `ReadAttributes` API is used to trigger a subscription, with a valid `reportInterval` tuple passed in being used as a way to indicate the request to create a subscription." -+ "To subscribe to a Node, the same `ReadAttribute` API is used to trigger a subscription, with a valid `reportInterval` tuple passed in being used as a way to indicate the request to create a subscription." - ] - }, - { -diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py -index e337bb0c5d..3892580a55 100644 ---- a/src/controller/python/chip/ChipDeviceCtrl.py -+++ b/src/controller/python/chip/ChipDeviceCtrl.py -@@ -1222,22 +1222,25 @@ class ChipDeviceControllerBase(): - else: - raise ValueError("Unsupported Attribute Path") - -- async def Read(self, nodeid: int, attributes: typing.List[typing.Union[ -- None, # Empty tuple, all wildcard -- typing.Tuple[int], # Endpoint -- # Wildcard endpoint, Cluster id present -- typing.Tuple[typing.Type[ClusterObjects.Cluster]], -- # Wildcard endpoint, Cluster + Attribute present -- typing.Tuple[typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -- # Wildcard attribute id -- typing.Tuple[int, typing.Type[ClusterObjects.Cluster]], -- # Concrete path -- typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -- # Directly specified attribute path -- ClusterAttribute.AttributePath -- ]] = None, -+ async def Read( -+ self, -+ nodeid: int, -+ attributes: typing.Optional[typing.List[typing.Union[ -+ None, # Empty tuple, all wildcard -+ typing.Tuple[int], # Endpoint -+ # Wildcard endpoint, Cluster id present -+ typing.Tuple[typing.Type[ClusterObjects.Cluster]], -+ # Wildcard endpoint, Cluster + Attribute present -+ typing.Tuple[typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -+ # Wildcard attribute id -+ typing.Tuple[int, typing.Type[ClusterObjects.Cluster]], -+ # Concrete path -+ typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -+ # Directly specified attribute path -+ ClusterAttribute.AttributePath -+ ]]] = None, - dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None, events: typing.List[ -- typing.Union[ -+ typing.Union[ - None, # Empty tuple, all wildcard - typing.Tuple[str, int], # all wildcard with urgency set - typing.Tuple[int, int], # Endpoint, -@@ -1249,10 +1252,11 @@ class ChipDeviceControllerBase(): - typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int], - # Concrete path - typing.Tuple[int, typing.Type[ClusterObjects.ClusterEvent], int] -- ]] = None, -- eventNumberFilter: typing.Optional[int] = None, -- returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None, -- fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True): -+ ]] = None, -+ eventNumberFilter: typing.Optional[int] = None, -+ returnClusterObject: bool = False, reportInterval: typing.Optional[typing.Tuple[int, int]] = None, -+ fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True -+ ): - ''' - Read a list of attributes and/or events from a target node - -@@ -1315,32 +1319,42 @@ class ChipDeviceControllerBase(): - eventPaths = [self._parseEventPathTuple( - v) for v in events] if events else None - -- ClusterAttribute.Read(future=future, eventLoop=eventLoop, device=device.deviceProxy, devCtrl=self, -+ transaction = ClusterAttribute.AsyncReadTransaction(future, eventLoop, self, returnClusterObject) -+ ClusterAttribute.Read(transaction, device=device.deviceProxy, - attributes=attributePaths, dataVersionFilters=clusterDataVersionFilters, events=eventPaths, -- eventNumberFilter=eventNumberFilter, returnClusterObject=returnClusterObject, -+ eventNumberFilter=eventNumberFilter, - subscriptionParameters=ClusterAttribute.SubscriptionParameters( - reportInterval[0], reportInterval[1]) if reportInterval else None, - fabricFiltered=fabricFiltered, - keepSubscriptions=keepSubscriptions, autoResubscribe=autoResubscribe).raise_on_error() -- return await future -+ await future - -- async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[ -- None, # Empty tuple, all wildcard -- typing.Tuple[int], # Endpoint -- # Wildcard endpoint, Cluster id present -- typing.Tuple[typing.Type[ClusterObjects.Cluster]], -- # Wildcard endpoint, Cluster + Attribute present -- typing.Tuple[typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -- # Wildcard attribute id -- typing.Tuple[int, typing.Type[ClusterObjects.Cluster]], -- # Concrete path -- typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -- # Directly specified attribute path -- ClusterAttribute.AttributePath -- ]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None, -- returnClusterObject: bool = False, -- reportInterval: typing.Tuple[int, int] = None, -- fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True): -+ if result := transaction.GetSubscriptionHandler(): -+ return result -+ else: -+ return transaction.GetReadResponse() -+ -+ async def ReadAttribute( -+ self, -+ nodeid: int, -+ attributes: typing.Optional[typing.List[typing.Union[ -+ None, # Empty tuple, all wildcard -+ typing.Tuple[int], # Endpoint -+ # Wildcard endpoint, Cluster id present -+ typing.Tuple[typing.Type[ClusterObjects.Cluster]], -+ # Wildcard endpoint, Cluster + Attribute present -+ typing.Tuple[typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -+ # Wildcard attribute id -+ typing.Tuple[int, typing.Type[ClusterObjects.Cluster]], -+ # Concrete path -+ typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]], -+ # Directly specified attribute path -+ ClusterAttribute.AttributePath -+ ]]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None, -+ returnClusterObject: bool = False, -+ reportInterval: typing.Optional[typing.Tuple[int, int]] = None, -+ fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True -+ ): - ''' - Read a list of attributes from a target node, this is a wrapper of DeviceController.Read() - -@@ -1401,23 +1415,27 @@ class ChipDeviceControllerBase(): - else: - return res.attributes - -- async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[ -- None, # Empty tuple, all wildcard -- typing.Tuple[str, int], # all wildcard with urgency set -- typing.Tuple[int, int], # Endpoint, -- # Wildcard endpoint, Cluster id present -- typing.Tuple[typing.Type[ClusterObjects.Cluster], int], -- # Wildcard endpoint, Cluster + Event present -- typing.Tuple[typing.Type[ClusterObjects.ClusterEvent], int], -- # Wildcard event id -- typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int], -- # Concrete path -- typing.Tuple[int, typing.Type[ClusterObjects.ClusterEvent], int] -- ]], eventNumberFilter: typing.Optional[int] = None, -- fabricFiltered: bool = True, -- reportInterval: typing.Tuple[int, int] = None, -- keepSubscriptions: bool = False, -- autoResubscribe: bool = True): -+ async def ReadEvent( -+ self, -+ nodeid: int, -+ events: typing.List[typing.Union[ -+ None, # Empty tuple, all wildcard -+ typing.Tuple[str, int], # all wildcard with urgency set -+ typing.Tuple[int, int], # Endpoint, -+ # Wildcard endpoint, Cluster id present -+ typing.Tuple[typing.Type[ClusterObjects.Cluster], int], -+ # Wildcard endpoint, Cluster + Event present -+ typing.Tuple[typing.Type[ClusterObjects.ClusterEvent], int], -+ # Wildcard event id -+ typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int], -+ # Concrete path -+ typing.Tuple[int, typing.Type[ClusterObjects.ClusterEvent], int] -+ ]], eventNumberFilter: typing.Optional[int] = None, -+ fabricFiltered: bool = True, -+ reportInterval: typing.Optional[typing.Tuple[int, int]] = None, -+ keepSubscriptions: bool = False, -+ autoResubscribe: bool = True -+ ): - ''' - Read a list of events from a target node, this is a wrapper of DeviceController.Read() - -diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py -index 63ca02d8df..fcd7d62187 100644 ---- a/src/controller/python/chip/clusters/Attribute.py -+++ b/src/controller/python/chip/clusters/Attribute.py -@@ -327,14 +327,17 @@ class AttributeCache: - returnClusterObject: bool = False - attributeTLVCache: Dict[int, Dict[int, Dict[int, bytes]]] = field( - default_factory=lambda: {}) -- attributeCache: Dict[int, List[Cluster]] = field( -- default_factory=lambda: {}) - versionList: Dict[int, Dict[int, Dict[int, int]]] = field( - default_factory=lambda: {}) - -+ _attributeCacheUpdateNeeded: set[AttributePath] = field( -+ default_factory=lambda: set()) -+ _attributeCache: Dict[int, List[Cluster]] = field( -+ default_factory=lambda: {}) -+ - def UpdateTLV(self, path: AttributePath, dataVersion: int, data: Union[bytes, ValueDecodeFailure]): - ''' Store data in TLV since that makes it easiest to eventually convert to either the -- cluster or attribute view representations (see below in UpdateCachedData). -+ cluster or attribute view representations (see below in GetUpdatedAttributeCache()). - ''' - if (path.EndpointId not in self.attributeTLVCache): - self.attributeTLVCache[path.EndpointId] = {} -@@ -357,7 +360,10 @@ class AttributeCache: - - clusterCache[path.AttributeId] = data - -- def UpdateCachedData(self, changedPathSet: set[AttributePath]): -+ # For this path the attribute cache still requires an update. -+ self._attributeCacheUpdateNeeded.add(path) -+ -+ def GetUpdatedAttributeCache(self) -> Dict[int, List[Cluster]]: - ''' This converts the raw TLV data into a cluster object format. - - Two formats are available: -@@ -394,12 +400,12 @@ class AttributeCache: - except Exception as ex: - return ValueDecodeFailure(value, ex) - -- for attributePath in changedPathSet: -+ for attributePath in self._attributeCacheUpdateNeeded: - endpointId, clusterId, attributeId = attributePath.EndpointId, attributePath.ClusterId, attributePath.AttributeId - -- if endpointId not in self.attributeCache: -- self.attributeCache[endpointId] = {} -- endpointCache = self.attributeCache[endpointId] -+ if endpointId not in self._attributeCache: -+ self._attributeCache[endpointId] = {} -+ endpointCache = self._attributeCache[endpointId] - - if clusterId not in _ClusterIndex: - # -@@ -427,6 +433,8 @@ class AttributeCache: - - attributeType = _AttributeIndex[(clusterId, attributeId)][0] - clusterCache[attributeType] = handle_attribute_view(endpointId, clusterId, attributeId, attributeType) -+ self._attributeCacheUpdateNeeded.clear() -+ return self._attributeCache - - - class SubscriptionTransaction: -@@ -447,12 +455,12 @@ class SubscriptionTransaction: - def GetAttributes(self): - ''' Returns the attribute value cache tracking the latest state on the publisher. - ''' -- return self._readTransaction._cache.attributeCache -+ return self._readTransaction._cache.GetUpdatedAttributeCache() - - def GetAttribute(self, path: TypedAttributePath) -> Any: - ''' Returns a specific attribute given a TypedAttributePath. - ''' -- data = self._readTransaction._cache.attributeCache -+ data = self._readTransaction._cache.GetUpdatedAttributeCache() - - if (self._readTransaction._cache.returnClusterObject): - return eval(f'data[path.Path.EndpointId][path.ClusterType].{path.AttributeName}') -@@ -685,6 +693,18 @@ class AsyncReadTransaction: - def GetAllEventValues(self): - return self._events - -+ def GetReadResponse(self) -> AsyncReadTransaction.ReadResponse: -+ """Prepares and returns the ReadResponse object.""" -+ return self.ReadResponse( -+ attributes=self._cache.GetUpdatedAttributeCache(), -+ events=self._events, -+ tlvAttributes=self._cache.attributeTLVCache -+ ) -+ -+ def GetSubscriptionHandler(self) -> SubscriptionTransaction | None: -+ """Returns subscription transaction.""" -+ return self._subscription_handler -+ - def handleAttributeData(self, path: AttributePath, dataVersion: int, status: int, data: bytes): - try: - imStatus = chip.interaction_model.Status(status) -@@ -751,7 +771,7 @@ class AsyncReadTransaction: - if not self._future.done(): - self._subscription_handler = SubscriptionTransaction( - self, subscriptionId, self._devCtrl) -- self._future.set_result(self._subscription_handler) -+ self._future.set_result(self) - else: - self._subscription_handler._subscriptionId = subscriptionId - if self._subscription_handler._onResubscriptionSucceededCb is not None: -@@ -780,8 +800,6 @@ class AsyncReadTransaction: - pass - - def _handleReportEnd(self): -- self._cache.UpdateCachedData(self._changedPathSet) -- - if (self._subscription_handler is not None): - for change in self._changedPathSet: - if self._subscription_handler.OnAttributeChangeCb: -@@ -812,8 +830,7 @@ class AsyncReadTransaction: - if self._resultError is not None: - self._future.set_exception(self._resultError.to_exception()) - else: -- self._future.set_result(AsyncReadTransaction.ReadResponse( -- attributes=self._cache.attributeCache, events=self._events, tlvAttributes=self._cache.attributeTLVCache)) -+ self._future.set_result(self) - - # - # Decrement the ref on ourselves to match the increment that happened at allocation. -@@ -1041,18 +1058,16 @@ _ReadParams = construct.Struct( - ) - - --def Read(future: Future, eventLoop, device, devCtrl, -- attributes: List[AttributePath] = None, dataVersionFilters: List[DataVersionFilter] = None, -- events: List[EventPath] = None, eventNumberFilter: Optional[int] = None, returnClusterObject: bool = True, -- subscriptionParameters: SubscriptionParameters = None, -+def Read(transaction: AsyncReadTransaction, device, -+ attributes: Optional[List[AttributePath]] = None, dataVersionFilters: Optional[List[DataVersionFilter]] = None, -+ events: Optional[List[EventPath]] = None, eventNumberFilter: Optional[int] = None, -+ subscriptionParameters: Optional[SubscriptionParameters] = None, - fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True) -> PyChipError: - if (not attributes) and dataVersionFilters: - raise ValueError( - "Must provide valid attribute list when data version filters is not null") - - handle = chip.native.GetLibraryHandle() -- transaction = AsyncReadTransaction( -- future, eventLoop, devCtrl, returnClusterObject) - - attributePathsForCffi = None - if attributes is not None: -@@ -1159,25 +1174,6 @@ def Read(future: Future, eventLoop, device, devCtrl, - return res - - --def ReadAttributes(future: Future, eventLoop, device, devCtrl, -- attributes: List[AttributePath], dataVersionFilters: List[DataVersionFilter] = None, -- returnClusterObject: bool = True, -- subscriptionParameters: SubscriptionParameters = None, fabricFiltered: bool = True) -> int: -- return Read(future=future, eventLoop=eventLoop, device=device, -- devCtrl=devCtrl, attributes=attributes, dataVersionFilters=dataVersionFilters, -- events=None, returnClusterObject=returnClusterObject, -- subscriptionParameters=subscriptionParameters, fabricFiltered=fabricFiltered) -- -- --def ReadEvents(future: Future, eventLoop, device, devCtrl, -- events: List[EventPath], eventNumberFilter=None, returnClusterObject: bool = True, -- subscriptionParameters: SubscriptionParameters = None, fabricFiltered: bool = True) -> int: -- return Read(future=future, eventLoop=eventLoop, device=device, devCtrl=devCtrl, attributes=None, -- dataVersionFilters=None, events=events, eventNumberFilter=eventNumberFilter, -- returnClusterObject=returnClusterObject, -- subscriptionParameters=subscriptionParameters, fabricFiltered=fabricFiltered) -- -- - def Init(): - handle = chip.native.GetLibraryHandle() - -diff --git a/src/controller/python/test/test_scripts/cluster_objects.py b/src/controller/python/test/test_scripts/cluster_objects.py -index 37f6819cbe..45c9e99576 100644 ---- a/src/controller/python/test/test_scripts/cluster_objects.py -+++ b/src/controller/python/test/test_scripts/cluster_objects.py -@@ -214,12 +214,12 @@ class ClusterObjectTests: - sub.SetAttributeUpdateCallback(subUpdate) - - try: -- data = sub.GetAttributes() - req = Clusters.OnOff.Commands.On() - await devCtrl.SendCommand(nodeid=NODE_ID, endpoint=1, payload=req) - - await asyncio.wait_for(event.wait(), timeout=11) - -+ data = sub.GetAttributes() - if (data[1][Clusters.OnOff][Clusters.OnOff.Attributes.OnOff] != 1): - raise ValueError("Current On/Off state should be 1") - -@@ -230,6 +230,7 @@ class ClusterObjectTests: - - await asyncio.wait_for(event.wait(), timeout=11) - -+ data = sub.GetAttributes() - if (data[1][Clusters.OnOff][Clusters.OnOff.Attributes.OnOff] != 0): - raise ValueError("Current On/Off state should be 0") - -@@ -252,13 +253,12 @@ class ClusterObjectTests: - sub.SetAttributeUpdateCallback(subUpdate) - - try: -- data = sub.GetAttributes() -- - req = Clusters.OnOff.Commands.On() - await devCtrl.SendCommand(nodeid=NODE_ID, endpoint=1, payload=req) - - await asyncio.wait_for(event.wait(), timeout=11) - -+ data = sub.GetAttributes() - cluster: Clusters.OnOff = data[1][Clusters.OnOff] - if (not cluster.onOff): - raise ValueError("Current On/Off state should be True") -@@ -270,6 +270,7 @@ class ClusterObjectTests: - - await asyncio.wait_for(event.wait(), timeout=11) - -+ data = sub.GetAttributes() - cluster: Clusters.OnOff = data[1][Clusters.OnOff] - if (cluster.onOff): - raise ValueError("Current On/Off state should be False") -@@ -296,7 +297,6 @@ class ClusterObjectTests: - logger.info("Test Subscription With MinInterval of 0") - sub = await devCtrl.ReadAttribute(nodeid=NODE_ID, - attributes=[Clusters.OnOff, Clusters.LevelControl], reportInterval=(0, 60)) -- data = sub.GetAttributes() - - logger.info("Sending off command") - -@@ -313,6 +313,7 @@ class ClusterObjectTests: - - logger.info("Checking read back value is indeed 254") - -+ data = sub.GetAttributes() - if (data[1][Clusters.LevelControl][Clusters.LevelControl.Attributes.CurrentLevel] != 254): - raise ValueError("Current Level should have been 254") - -diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py -index 93a89192dc..6a7b3c4a32 100644 ---- a/src/python_testing/matter_testing_support.py -+++ b/src/python_testing/matter_testing_support.py -@@ -1550,6 +1550,191 @@ def async_test_body(body): - return async_runner - - -+def per_node_test(body): -+ """ Decorator to be used for PICS-free tests that apply to the entire node. -+ -+ Use this decorator when your script needs to be run once to validate the whole node. -+ To use this decorator, the test must NOT have an associated pics_ method. -+ """ -+ -+ def whole_node_runner(self: MatterBaseTest, *args, **kwargs): -+ asserts.assert_false(self.get_test_pics(self.current_test_info.name), "pics_ method supplied for per_node_test.") -+ return _async_runner(body, self, *args, **kwargs) -+ -+ return whole_node_runner -+ -+ -+EndpointCheckFunction = typing.Callable[[Clusters.Attribute.AsyncReadTransaction.ReadResponse, int], bool] -+ -+ -+def _has_cluster(wildcard, endpoint, cluster: ClusterObjects.Cluster) -> bool: -+ try: -+ return cluster in wildcard.attributes[endpoint] -+ except KeyError: -+ return False -+ -+ -+def has_cluster(cluster: ClusterObjects.ClusterObjectDescriptor) -> EndpointCheckFunction: -+ """ EndpointCheckFunction that can be passed as a parameter to the per_endpoint_test decorator. -+ -+ Use this function with the per_endpoint_test decorator to run this test on all endpoints with -+ the specified cluster. For example, given a device with the following conformance -+ -+ EP0: cluster A, B, C -+ EP1: cluster D, E -+ EP2, cluster D -+ EP3, cluster E -+ -+ And the following test specification: -+ @per_endpoint_test(has_cluster(Clusters.D)) -+ test_mytest(self): -+ ... -+ -+ The test would be run on endpoint 1 and on endpoint 2. -+ -+ If the cluster is not found on any endpoint the decorator will call the on_skip function to -+ notify the test harness that the test is not applicable to this node and the test will not be run. -+ """ -+ return partial(_has_cluster, cluster=cluster) -+ -+ -+def _has_attribute(wildcard, endpoint, attribute: ClusterObjects.ClusterAttributeDescriptor) -> bool: -+ cluster = getattr(Clusters, attribute.__qualname__.split('.')[-3]) -+ try: -+ attr_list = wildcard.attributes[endpoint][cluster][cluster.Attributes.AttributeList] -+ return attribute.attribute_id in attr_list -+ except KeyError: -+ return False -+ -+ -+def has_attribute(attribute: ClusterObjects.ClusterAttributeDescriptor) -> EndpointCheckFunction: -+ """ EndpointCheckFunction that can be passed as a parameter to the per_endpoint_test decorator. -+ -+ Use this function with the per_endpoint_test decorator to run this test on all endpoints with -+ the specified attribute. For example, given a device with the following conformance -+ -+ EP0: cluster A, B, C -+ EP1: cluster D with attribute d, E -+ EP2, cluster D with attribute d -+ EP3, cluster D without attribute d -+ -+ And the following test specification: -+ @per_endpoint_test(has_attribute(Clusters.D.Attributes.d)) -+ test_mytest(self): -+ ... -+ -+ The test would be run on endpoint 1 and on endpoint 2. -+ -+ If the cluster is not found on any endpoint the decorator will call the on_skip function to -+ notify the test harness that the test is not applicable to this node and the test will not be run. -+ """ -+ return partial(_has_attribute, attribute=attribute) -+ -+ -+def _has_feature(wildcard, endpoint, cluster: ClusterObjects.ClusterObjectDescriptor, feature: IntFlag) -> bool: -+ try: -+ feature_map = wildcard.attributes[endpoint][cluster][cluster.Attributes.FeatureMap] -+ return (feature & feature_map) != 0 -+ except KeyError: -+ return False -+ -+ -+def has_feature(cluster: ClusterObjects.ClusterObjectDescriptor, feature: IntFlag) -> EndpointCheckFunction: -+ """ EndpointCheckFunction that can be passed as a parameter to the per_endpoint_test decorator. -+ -+ Use this function with the per_endpoint_test decorator to run this test on all endpoints with -+ the specified feature. For example, given a device with the following conformance -+ -+ EP0: cluster A, B, C -+ EP1: cluster D with feature F0 -+ EP2, cluster D with feature F0 -+ EP3, cluster D without feature F0 -+ -+ And the following test specification: -+ @per_endpoint_test(has_feature(Clusters.D.Bitmaps.Feature.F0)) -+ test_mytest(self): -+ ... -+ -+ The test would be run on endpoint 1 and on endpoint 2. -+ -+ If the cluster is not found on any endpoint the decorator will call the on_skip function to -+ notify the test harness that the test is not applicable to this node and the test will not be run. -+ """ -+ return partial(_has_feature, cluster=cluster, feature=feature) -+ -+ -+async def get_accepted_endpoints_for_test(self: MatterBaseTest, accept_function: EndpointCheckFunction) -> list[uint]: -+ """ Helper function for the per_endpoint_test decorator. -+ -+ Returns a list of endpoints on which the test should be run given the accept_function for the test. -+ """ -+ wildcard = await self.default_controller.Read(self.dut_node_id, [(Clusters.Descriptor), Attribute.AttributePath(None, None, GlobalAttributeIds.ATTRIBUTE_LIST_ID), Attribute.AttributePath(None, None, GlobalAttributeIds.FEATURE_MAP_ID), Attribute.AttributePath(None, None, GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID)]) -+ return [e for e in wildcard.attributes.keys() if accept_function(wildcard, e)] -+ -+ -+def per_endpoint_test(accept_function: EndpointCheckFunction): -+ """ Test decorator for a test that needs to be run once per endpoint that meets the accept_function criteria. -+ -+ Place this decorator above the test_ method to have the test framework run this test once per endpoint. -+ This decorator takes an EndpointCheckFunction to assess whether a test needs to be run on a particular -+ endpoint. -+ -+ For example, given the following device conformance: -+ -+ EP0: cluster A, B, C -+ EP1: cluster D, E -+ EP2, cluster D -+ EP3, cluster E -+ -+ And the following test specification: -+ @per_endpoint_test(has_cluster(Clusters.D)) -+ test_mytest(self): -+ ... -+ -+ The test would be run on endpoint 1 and on endpoint 2. -+ -+ If the cluster is not found on any endpoint the decorator will call the on_skip function to -+ notify the test harness that the test is not applicable to this node and the test will not be run. -+ -+ The decorator works by setting the self.matter_test_config.endpoint value and running the test function. -+ Therefore, tests that make use of this decorator should call controller functions against that endpoint. -+ Support functions in this file default to this endpoint. -+ -+ Tests that use this decorator cannot use a pics_ method for test selection and should not reference any -+ PICS values internally. -+ """ -+ def per_endpoint_test_internal(body): -+ def per_endpoint_runner(self: MatterBaseTest, *args, **kwargs): -+ asserts.assert_false(self.get_test_pics(self.current_test_info.name), "pics_ method supplied for per_endpoint_test.") -+ runner_with_timeout = asyncio.wait_for(get_accepted_endpoints_for_test(self, accept_function), timeout=30) -+ endpoints = asyncio.run(runner_with_timeout) -+ if not endpoints: -+ logging.info("No matching endpoints found - skipping test") -+ asserts.skip('No endpoints match requirements') -+ return -+ logging.info(f"Running test on the following endpoints: {endpoints}") -+ # setup_class is meant to be called once, but setup_test is expected to be run before -+ # each iteration. Mobly will run it for us the first time, but since we're running this -+ # more than one time, we want to make sure we reset everything as expected. -+ # Ditto for teardown - we want to tear down after each iteration, and we want to notify the hook that -+ # the test iteration is stopped. test_stop is called by on_pass or on_fail during the last iteration or -+ # on failure. -+ original_ep = self.matter_test_config.endpoint -+ for e in endpoints: -+ logging.info(f'Running test on endpoint {e}') -+ if e != endpoints[0]: -+ self.setup_test() -+ self.matter_test_config.endpoint = e -+ _async_runner(body, self, *args, **kwargs) -+ if e != endpoints[-1] and not self.failed: -+ self.teardown_test() -+ test_duration = (datetime.now(timezone.utc) - self.test_start_time) / timedelta(microseconds=1) -+ self.runner_hook.test_stop(exception=None, duration=test_duration) -+ self.matter_test_config.endpoint = original_ep -+ return per_endpoint_runner -+ return per_endpoint_test_internal -+ -+ - class CommissionDeviceTest(MatterBaseTest): - """Test class auto-injected at the start of test list to commission a device when requested""" - --- -2.46.0 - diff --git a/CHIPProjectConfig.h b/CHIPProjectConfig.h index 373bd17..9cdd42c 100644 --- a/CHIPProjectConfig.h +++ b/CHIPProjectConfig.h @@ -57,7 +57,6 @@ #define CONFIG_BUILD_FOR_HOST_UNIT_TEST 0 // Home Assistant Python Matter server specific configs -#define CHIP_EXCHANGE_NODE_ID_LOGGING 1 #define CHIP_CONFIG_CONTROLLER_MAX_ACTIVE_DEVICES 1024 #define CHIP_CONFIG_CONTROLLER_MAX_ACTIVE_CASE_CLIENTS 32 #define CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS 192 diff --git a/README.md b/README.md index 549bd2d..c832d64 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This repository provides the building infrastructure to build Python wheels of the Matter device controller. It uses the [official project Matter SDK](https://github.com/project-chip/connectedhomeip/) and builds the Python bindings available at `src/controller/python/`. The -bindings use the official Matter implementation wriitten in C++ as a native +bindings use the official Matter implementation written in C++ as a native library using ctypes bindings. The `chip-core` wheel contains this native library. Hence the `chip-core` wheel is a platform specific wheel. diff --git a/connectedhomeip b/connectedhomeip index 648d7bf..dee7d7b 160000 --- a/connectedhomeip +++ b/connectedhomeip @@ -1 +1 @@ -Subproject commit 648d7bf3d2e52ded740c94989965cd8485c85832 +Subproject commit dee7d7bf629ca416ec0fa0b1ef8c9b22debde7c9