From 54459c07d62bf12c6da021102f7e0d9f825f3209 Mon Sep 17 00:00:00 2001 From: Song Guo Date: Thu, 23 May 2024 17:54:31 +0800 Subject: [PATCH] Update --- src/app/icd/client/BUILD.gn | 2 +- src/controller/python/BUILD.gn | 4 +- .../ChipDeviceController-ScriptBinding.cpp | 21 ++++-- ...Controller-ScriptDevicePairingDelegate.cpp | 8 +-- ...ceController-ScriptDevicePairingDelegate.h | 2 +- src/controller/python/chip/ChipDeviceCtrl.py | 64 ++++++++++++++++--- ...Delegate.cpp => PyChipCheckInDelegate.cpp} | 4 +- ...ckInDelegate.h => PyChipCheckInDelegate.h} | 0 src/controller/python/chip/icd/__init__.py | 30 ++++++++- 9 files changed, 107 insertions(+), 28 deletions(-) rename src/controller/python/chip/icd/{CheckInDelegate.cpp => PyChipCheckInDelegate.cpp} (87%) rename src/controller/python/chip/icd/{CheckInDelegate.h => PyChipCheckInDelegate.h} (100%) diff --git a/src/app/icd/client/BUILD.gn b/src/app/icd/client/BUILD.gn index 4d59a627a4bcbb..fefd0f098685db 100644 --- a/src/app/icd/client/BUILD.gn +++ b/src/app/icd/client/BUILD.gn @@ -36,11 +36,11 @@ source_set("manager") { # All sources and configurations used by the CheckInHandler need to go in this source-set source_set("handler") { sources = [ - "CheckInDelegate.h", "CheckInHandler.cpp", "CheckInHandler.h", "DefaultCheckInDelegate.cpp", "DefaultCheckInDelegate.h", + "PyChipCheckInDelegate.h", "RefreshKeySender.cpp", "RefreshKeySender.h", ] diff --git a/src/controller/python/BUILD.gn b/src/controller/python/BUILD.gn index 05a9d5678c622a..0d557f9eef5e32 100644 --- a/src/controller/python/BUILD.gn +++ b/src/controller/python/BUILD.gn @@ -76,8 +76,8 @@ shared_library("ChipDeviceCtrl") { "chip/crypto/p256keypair.cpp", "chip/crypto/p256keypair.h", "chip/discovery/NodeResolution.cpp", - "chip/icd/CheckInDelegate.cpp", - "chip/icd/CheckInDelegate.h", + "chip/icd/PyChipCheckInDelegate.cpp", + "chip/icd/PyChipCheckInDelegate.h", "chip/interaction_model/Delegate.cpp", "chip/interaction_model/Delegate.h", "chip/internal/ChipThreadWork.cpp", diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp index f37c00b29fa480..ef55a1250071c5 100644 --- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp +++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp @@ -58,7 +58,7 @@ #include #include #include -#include +#include #include #include @@ -133,6 +133,7 @@ PyChipError pychip_DeviceController_GetAddressAndPort(chip::Controller::DeviceCo char * outAddress, uint64_t maxAddressLen, uint16_t * outPort); PyChipError pychip_DeviceController_GetCompressedFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId); PyChipError pychip_DeviceController_GetFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId); +PyChipError pychip_DeviceController_GetFabricIndex(chip::Controller::DeviceCommissioner * devCtrl, uint8_t * outFabricIndex); PyChipError pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outNodeId); // Rendezvous @@ -273,12 +274,6 @@ PyChipError pychip_DeviceController_StackInit(Controller::Python::StorageAdapter sICDClientStorage.Init(storageAdapter, &sSessionKeystore); - auto engine = chip::app::InteractionModelEngine::GetInstance(); - PyReturnErrorOnFailure(ToPyChipError(PyChipCheckInDelegate::GetInstance().Init(&sICDClientStorage, engine))); - PyReturnErrorOnFailure( - ToPyChipError(sCheckInHandler.Init(DeviceControllerFactory::GetInstance().GetSystemState()->ExchangeMgr(), - &sICDClientStorage, &PyChipCheckInDelegate::GetInstance(), engine))); - sGroupDataProvider.SetStorageDelegate(storageAdapter); sGroupDataProvider.SetSessionKeystore(factoryParams.sessionKeystore); PyReturnErrorOnFailure(ToPyChipError(sGroupDataProvider.Init())); @@ -309,6 +304,12 @@ PyChipError pychip_DeviceController_StackInit(Controller::Python::StorageAdapter // DeviceControllerFactory::GetInstance().RetainSystemState(); + auto engine = chip::app::InteractionModelEngine::GetInstance(); + PyReturnErrorOnFailure(ToPyChipError(PyChipCheckInDelegate::GetInstance().Init(&sICDClientStorage, engine))); + PyReturnErrorOnFailure( + ToPyChipError(sCheckInHandler.Init(DeviceControllerFactory::GetInstance().GetSystemState()->ExchangeMgr(), + &sICDClientStorage, &PyChipCheckInDelegate::GetInstance(), engine))); + // // Finally, start up the main Matter thread. Any further interactions with the stack // will now need to happen on the Matter thread, OR protected with the stack lock. @@ -361,6 +362,12 @@ PyChipError pychip_DeviceController_GetFabricId(chip::Controller::DeviceCommissi return ToPyChipError(CHIP_NO_ERROR); } +PyChipError pychip_DeviceController_GetFabricIndex(chip::Controller::DeviceCommissioner * devCtrl, uint8_t * outFabricIndex) +{ + *outFabricIndex = devCtrl->GetFabricIndex(); + return ToPyChipError(CHIP_NO_ERROR); +} + PyChipError pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outNodeId) { *outNodeId = devCtrl->GetNodeId(); diff --git a/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.cpp b/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.cpp index ddf9b1de87eb10..850490da01aba0 100644 --- a/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.cpp +++ b/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.cpp @@ -201,13 +201,13 @@ void ScriptDevicePairingDelegate::OnICDRegistrationComplete(NodeId nodeId, uint3 if (err != CHIP_NO_ERROR) { sICDClientStorage.RemoveKey(clientInfo); - ChipLogError(chipTool, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", ChipLogValueX64(nodeId), + ChipLogError(Controller, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", ChipLogValueX64(nodeId), err.AsString()); return; } - ChipLogProgress(Zcl, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(nodeId)); - ChipLogProgress(Zcl, + ChipLogProgress(Controller, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(nodeId)); + ChipLogProgress(Controller, "ICD Registration Complete for device " ChipLogFormatX64 " / Check-In NodeID: " ChipLogFormatX64 " / Monitored Subject: " ChipLogFormatX64 " / ICDCounter %u", ChipLogValueX64(nodeId), ChipLogValueX64(sCommissioningParameters.GetICDCheckInNodeId().Value()), @@ -216,7 +216,7 @@ void ScriptDevicePairingDelegate::OnICDRegistrationComplete(NodeId nodeId, uint3 void ScriptDevicePairingDelegate::OnICDStayActiveComplete(NodeId deviceId, uint32_t promisedActiveDuration) { - ChipLogProgress(Zcl, "ICD Stay Active Complete for device " ChipLogFormatX64 " / promisedActiveDuration: %u", + ChipLogProgress(Controller, "ICD Stay Active Complete for device " ChipLogFormatX64 " / promisedActiveDuration: %u", ChipLogValueX64(deviceId), promisedActiveDuration); } diff --git a/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.h b/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.h index 52c8498dd68f62..f944642a10d5ae 100644 --- a/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.h +++ b/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.h @@ -88,7 +88,7 @@ class ScriptDevicePairingDelegate final : public Controller::DevicePairingDelega Callback::Callback mOpenWindowCallback; Controller::CommissioningWindowOpener * mWindowOpener = nullptr; bool expectingPairingComplete = false; - FabricIndex mFabricIndex; + FabricIndex mFabricIndex = 0; }; } // namespace Controller diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py index d2bee1d75b2d74..e9ad6caf36bdd6 100644 --- a/src/controller/python/chip/ChipDeviceCtrl.py +++ b/src/controller/python/chip/ChipDeviceCtrl.py @@ -46,7 +46,7 @@ from . import FabricAdmin from . import clusters as Clusters -from . import discovery +from . import discovery, icd from .clusters import Attribute as ClusterAttribute from .clusters import ClusterObjects as ClusterObjects from .clusters import Command as ClusterCommand @@ -101,6 +101,14 @@ class NOCChain: adminSubject: int +@dataclass +class ICDRegistrationParameters: + symmetricKey: typing.Optional[bytes] = None + checkInNodeId: typing.Optional[int] = None + monitoredSubject: typing.Optional[int] = None + stayActiveMs: typing.Optional[int] = None + + @_DeviceAvailableCallbackFunct def _DeviceAvailableCallback(closure, device, err): closure.deviceAvailable(device, err) @@ -272,6 +280,7 @@ def HandleCommissioningComplete(nodeid, err): else: logging.warning("Failed to commission: {}".format(err)) + self.DisableIcdRegistration() self.state = DCState.IDLE self._ChipStack.callbackRes = err self._ChipStack.commissioningEventRes = err @@ -350,6 +359,7 @@ def HandlePASEEstablishmentComplete(err: PyChipError): self._isActive = True # Validate FabricID/NodeID followed from NOC Chain self._fabricId = self.GetFabricIdInternal() + self._fabricIndex = self.GetFabricIndexInternal() self._nodeId = self.GetNodeIdInternal() def _finish_init(self): @@ -769,6 +779,19 @@ def GetFabricIdInternal(self): return fabricid.value + def GetFabricIndexInternal(self): + """Get the fabric index from the object. Only used to validate cached value from property.""" + self.CheckIsActive() + + fabricindex = c_uint8(0) + + self._ChipStack.Call( + lambda: self._dmLib.pychip_DeviceController_GetFabricIndex( + self.devCtrl, pointer(fabricindex)) + ).raise_on_error() + + return fabricindex.value + def GetNodeIdInternal(self) -> int: """Get the node ID from the object. Only used to validate cached value from property.""" self.CheckIsActive() @@ -844,6 +867,18 @@ def deviceAvailable(self, device, err): return DeviceProxyWrapper(returnDevice, self._dmLib) + async def WaitForActive(self, nodeid, stayActiveDurationMs=30000): + ''' Waits a LIT ICD device to become active. Will send a StayActive command to the device on active to allow human operations. + + nodeId: Node ID of the LID ICD + stayActiveDurationMs: The duration in the StayActive command, in milliseconds + + Returns: + - StayActiveResponse on success + ''' + await icd.WaitForCheckIn(self._fabricIndex, nodeid) + return await self.SendCommand(nodeid, 0, GeneratedObjects.IcdManagement.Commands.StayActiveRequest(stayActiveDuration=stayActiveDurationMilliseconds)) + async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None): ''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node. @@ -1822,6 +1857,9 @@ def _InitLib(self): self._dmLib.pychip_DeviceController_GetFabricId.argtypes = [c_void_p, POINTER(c_uint64)] self._dmLib.pychip_DeviceController_GetFabricId.restype = PyChipError + self._dmLib.pychip_DeviceController_GetFabricIndex.argtypes = [c_void_p, POINTER(c_uint8)] + self._dmLib.pychip_DeviceController_GetFabricIndex.restype = PyChipError + self._dmLib.pychip_DeviceController_GetLogFilter = [None] self._dmLib.pychip_DeviceController_GetLogFilter = c_uint8 @@ -1984,22 +2022,30 @@ def SetCheckMatchingFabric(self, check: bool): lambda: self._dmLib.pychip_DeviceController_SetCheckMatchingFabric(check) ).raise_on_error() - def SetIcdRegistrationParameters(self, symmetricKey: bytes = None, checkInNodeId: int = 0, monitoredSubject: int = 0, stayActiveMs: int = 0): - if symmetricKey is not None: + def EnableICDRegistration(self, parameters: typing.Optional[ICDRegistrationParameters] = None): + ''' Enables ICD registration for the following commissioning session. + + Args: + parameters: A ICDRegistrationParameters for the parameters used for ICD registration, or None for default arguments. + ''' + if parameters is None: + parameters = ICDRegistrationParameters() + if parameters.symmetricKey is not None: if len(symmetricKey) != 16: raise ValueError("symmetricKey should be 16 bytes") - if not checkInNodeId: - checkInNodeId = self._nodeId - if not monitoredSubject: - monitoredSubject = checkInNodeId + if parameters.checkInNodeId is None: + parameters.checkInNodeId = self._nodeId + if parameters.monitoredSubject is None: + parameters.monitoredSubject = checkInNodeId self.CheckIsActive() self._ChipStack.Call( lambda: self._dmLib.pychip_DeviceController_SetIcdRegistrationParameters( - self.devCtrl, True, symmetricKey, checkInNodeId, monitoredSubject, stayActiveMs) + self.devCtrl, True, parameters.symmetricKey, parameters.checkInNodeId, parameters.monitoredSubject, parameters.stayActiveMs) ).raise_on_error() - def DisableIcdRegistration(self): + def DisableICDRegistration(self): + ''' Disables ICD registration. ''' self.CheckIsActive() self._ChipStack.Call( lambda: self._dmLib.pychip_DeviceController_SetIcdRegistrationParameters(self.devCtrl, False, None, 0, 0, 0) diff --git a/src/controller/python/chip/icd/CheckInDelegate.cpp b/src/controller/python/chip/icd/PyChipCheckInDelegate.cpp similarity index 87% rename from src/controller/python/chip/icd/CheckInDelegate.cpp rename to src/controller/python/chip/icd/PyChipCheckInDelegate.cpp index a6f8f71a78afb3..fd456d39b5dda1 100644 --- a/src/controller/python/chip/icd/CheckInDelegate.cpp +++ b/src/controller/python/chip/icd/PyChipCheckInDelegate.cpp @@ -16,7 +16,7 @@ * limitations under the License. */ -#include "CheckInDelegate.h" +#include "PyChipCheckInDelegate.h" using namespace ::chip; using namespace ::chip::app; @@ -33,7 +33,7 @@ void PyChipCheckInDelegate::OnCheckInComplete(const ICDClientInfo & clientInfo) } } -void pychip_CheckInDelegate_SetOnCheckInCompleteCallback(PyChipCheckInDelegate::OnCheckInCompleteCallback * callback) +extern "C" void pychip_CheckInDelegate_SetOnCheckInCompleteCallback(PyChipCheckInDelegate::OnCheckInCompleteCallback * callback) { PyChipCheckInDelegate::GetInstance().SetOnCheckInCompleteCallback(callback); } diff --git a/src/controller/python/chip/icd/CheckInDelegate.h b/src/controller/python/chip/icd/PyChipCheckInDelegate.h similarity index 100% rename from src/controller/python/chip/icd/CheckInDelegate.h rename to src/controller/python/chip/icd/PyChipCheckInDelegate.h diff --git a/src/controller/python/chip/icd/__init__.py b/src/controller/python/chip/icd/__init__.py index 653461f5ab35d0..2f1918afc767db 100644 --- a/src/controller/python/chip/icd/__init__.py +++ b/src/controller/python/chip/icd/__init__.py @@ -15,6 +15,7 @@ # limitations under the License. # +import asyncio from ctypes import CFUNCTYPE, c_uint8, c_uint64 from dataclasses import dataclass from threading import Condition, Event, Lock @@ -62,13 +63,38 @@ def _ensureInit(): _initialized = True -def AddOnActiveCallback(fabricIndex: int, nodeId: int, callback: Callable[None, [OnCheckInCompleteParams]]): +def RegisterOnActiveCallback(fabricIndex: int, nodeId: int, callback: Callable[None, [OnCheckInCompleteParams]]): + ''' Registers a callback when the device with given (fabric index, node id) becomes active. + + Does nothing if the callback is already registered. + ''' + _ensureInit() with _OnCheckInCompleteWaitListLock: waitList = _OnCheckInCompleteWaitList.get((fabricIndex, nodeId), set()) waitList.add(callback) _OnCheckInCompleteWaitList[(fabricIndex, nodeId)] = waitList -def RemoveOnActiveCallback(fabricIndex: int, nodeId: int, callback: Callable[None, [OnCheckInCompleteParams]]): +def UnregisterOnActiveCallback(fabricIndex: int, nodeId: int, callback: Callable[None, [OnCheckInCompleteParams]]): + ''' Unregisters a callback when the device with given (fabric index, node id) becomes active. + + Does nothing if the callback has not been registered. + ''' with _OnCheckInCompleteWaitListLock: _OnCheckInCompleteWaitList.get((fabricIndex, nodeId), set()).remove(callback) + + +def WaitForCheckIn(fabricIndex: int, nodeId: int): + ''' Waits for a device becomes active. + + Returns: + - A future, completes when the device becomes active. + ''' + eventLoop = asyncio.get_running_loop() + future = eventLoop.create_future() + + def OnCheckInCallback(nodeid): + eventLoop.call_soon_threadsafe(lambda: future.done() or future.set_result(None)) + RemoveOnActiveCallback(fabricIndex, nodeId, OnCheckInCallback) + AddOnActiveCallback(fabricIndex, nodeId, OnCheckInCallback) + return future