Skip to content

Commit da52a2b

Browse files
committed
Add TransportPayloadCapability flag for GetConnectedDevices and bubble
up the flag to the wrapper IM Python APIs. Add python script binding methods for LargePayload tests --to check if session allows large payload. --to close the underlying TCP connection. --to check if the session is active.
1 parent 47cec4e commit da52a2b

File tree

2 files changed

+110
-20
lines changed

2 files changed

+110
-20
lines changed

src/controller/python/ChipDeviceController-ScriptBinding.cpp

+44-3
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::Devic
213213
const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode);
214214

215215
PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
216-
chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback);
216+
chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback,
217+
int transportPayloadCapability);
217218
PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy);
218219
PyChipError pychip_GetLocalSessionId(chip::OperationalDeviceProxy * deviceProxy, uint16_t * localSessionId);
219220
PyChipError pychip_GetNumSessionsToPeer(chip::OperationalDeviceProxy * deviceProxy, uint32_t * numSessions);
@@ -239,6 +240,12 @@ void pychip_Storage_ShutdownAdapter(chip::Controller::Python::StorageAdapter * s
239240
// ICD
240241
//
241242
void pychip_CheckInDelegate_SetOnCheckInCompleteCallback(PyChipCheckInDelegate::OnCheckInCompleteCallback * callback);
243+
244+
//
245+
// LargePayload and TCP
246+
PyChipError pychip_SessionAllowsLargePayload(chip::OperationalDeviceProxy * deviceProxy, bool * allowsLargePayload);
247+
PyChipError pychip_IsSessionActive(chip::OperationalDeviceProxy * deviceProxy, bool * isSessionActive);
248+
PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy);
242249
}
243250

244251
void * pychip_Storage_InitializeStorageAdapter(chip::Controller::Python::PyObject * context,
@@ -805,11 +812,44 @@ struct GetDeviceCallbacks
805812
} // anonymous namespace
806813

807814
PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
808-
chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback)
815+
chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback,
816+
int transportPayloadCapability)
809817
{
810818
VerifyOrReturnError(devCtrl != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
811819
auto * callbacks = new GetDeviceCallbacks(context, callback);
812-
return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure));
820+
return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure,
821+
static_cast<chip::TransportPayloadCapability>(transportPayloadCapability)));
822+
}
823+
824+
PyChipError pychip_SessionAllowsLargePayload(chip::OperationalDeviceProxy * deviceProxy, bool * allowsLargePayload)
825+
{
826+
VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION));
827+
VerifyOrReturnError(allowsLargePayload != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
828+
829+
*allowsLargePayload = deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload();
830+
831+
return ToPyChipError(CHIP_NO_ERROR);
832+
}
833+
834+
PyChipError pychip_IsSessionActive(chip::OperationalDeviceProxy * deviceProxy, bool * isSessionActive)
835+
{
836+
VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION));
837+
VerifyOrReturnError(isSessionActive != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
838+
839+
*isSessionActive = deviceProxy->GetSecureSession().Value()->AsSecureSession()->IsActiveSession();
840+
841+
return ToPyChipError(CHIP_NO_ERROR);
842+
}
843+
844+
PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy)
845+
{
846+
VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION));
847+
VerifyOrReturnError(deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload(),
848+
ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
849+
850+
deviceProxy->GetExchangeManager()->GetSessionManager()->TCPDisconnect(deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection(), /* shouldAbort = */ false);
851+
852+
return ToPyChipError(CHIP_NO_ERROR);
813853
}
814854

815855
PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy)
@@ -855,6 +895,7 @@ PyChipError pychip_GetAttestationChallenge(chip::OperationalDeviceProxy * device
855895
return ToPyChipError(CHIP_NO_ERROR);
856896
}
857897

898+
858899
PyChipError pychip_GetDeviceBeingCommissioned(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
859900
CommissioneeDeviceProxy ** proxy)
860901
{

src/controller/python/chip/ChipDeviceCtrl.py

+66-17
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@
8484

8585
_ChipDeviceController_IterateDiscoveredCommissionableNodesFunct = CFUNCTYPE(None, c_char_p, c_size_t)
8686

87+
# Defines for the transport payload types to use to select the suitable
88+
# underlying transport of the session.
89+
#class TransportPayloadCapability(ctypes.c_int):
90+
class TransportPayloadCapability(ctypes.c_int):
91+
MRP_PAYLOAD = 0
92+
LARGE_PAYLOAD = 1
93+
MRP_OR_TCP_PAYLOAD = 2
94+
8795

8896
@dataclass
8997
class CommissioningParameters:
@@ -371,6 +379,39 @@ def attestationChallenge(self) -> bytes:
371379

372380
return bytes(buf)
373381

382+
@property
383+
def sessionAllowsLargePayload(self) -> bool:
384+
self._dmLib.pychip_SessionAllowsLargePayload.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)]
385+
self._dmLib.pychip_SessionAllowsLargePayload.restype = PyChipError
386+
387+
supportsLargePayload = ctypes.c_bool(False)
388+
389+
builtins.chipStack.Call(
390+
lambda: self._dmLib.pychip_SessionAllowsLargePayload(self._deviceProxy, pointer(supportsLargePayload))
391+
).raise_on_error()
392+
393+
return supportsLargePayload.value
394+
395+
@property
396+
def isSessionActive(self) -> bool:
397+
self._dmLib.pychip_IsSessionActive.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)]
398+
self._dmLib.pychip_IsSessionActive.restype = PyChipError
399+
400+
isSessionActive = ctypes.c_bool(False)
401+
402+
builtins.chipStack.Call(
403+
lambda: self._dmLib.pychip_IsSessionActive(self._deviceProxy, pointer(isSessionActive))
404+
).raise_on_error()
405+
406+
return isSessionActive.value
407+
408+
def closeTCPConnectionWithPeer(self):
409+
self._dmLib.pychip_CloseTCPConnectionWithPeer.argtypes = [ctypes.c_void_p]
410+
self._dmLib.pychip_CloseTCPConnectionWithPeer.restype = PyChipError
411+
412+
builtins.chipStack.Call(
413+
lambda: self._dmLib.pychip_CloseTCPConnectionWithPeer(self._deviceProxy)
414+
).raise_on_error()
374415

375416
DiscoveryFilterType = discovery.FilterType
376417
DiscoveryType = discovery.DiscoveryType
@@ -906,7 +947,7 @@ async def FindOrEstablishPASESession(self, setupCode: str, nodeid: int, timeoutM
906947
if res.is_success:
907948
return DeviceProxyWrapper(returnDevice, DeviceProxyWrapper.DeviceProxyType.COMMISSIONEE, self._dmLib)
908949

909-
def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None):
950+
def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None, payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
910951
''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node.
911952
912953
nodeId: Target's Node ID
@@ -943,7 +984,7 @@ def deviceAvailable(self, device, err):
943984
closure = DeviceAvailableClosure()
944985
ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure))
945986
self._ChipStack.Call(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId(
946-
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback),
987+
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability),
947988
timeoutMs).raise_on_error()
948989

949990
# The callback might have been received synchronously (during self._ChipStack.Call()).
@@ -975,7 +1016,8 @@ async def WaitForActive(self, nodeid, *, timeoutSeconds=30.0, stayActiveDuration
9751016
await WaitForCheckIn(ScopedNodeId(nodeid, self._fabricIndex), timeoutSeconds=timeoutSeconds)
9761017
return await self.SendCommand(nodeid, 0, Clusters.IcdManagement.Commands.StayActiveRequest(stayActiveDuration=stayActiveDurationMs))
9771018

978-
async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None):
1019+
async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None,
1020+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
9791021
''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node.
9801022
9811023
nodeId: Target's Node ID
@@ -1020,7 +1062,7 @@ def deviceAvailable(self, device, err):
10201062
closure = DeviceAvailableClosure(eventLoop, future)
10211063
ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure))
10221064
await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId(
1023-
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback),
1065+
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability),
10241066
timeoutMs)
10251067

10261068
# The callback might have been received synchronously (during self._ChipStack.CallAsync()).
@@ -1124,7 +1166,8 @@ async def TestOnlySendCommandTimedRequestFlagWithNoTimedInvoke(self, nodeid: int
11241166
async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.ClusterCommand, responseType=None,
11251167
timedRequestTimeoutMs: typing.Union[None, int] = None,
11261168
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None,
1127-
suppressResponse: typing.Union[None, bool] = None):
1169+
suppressResponse: typing.Union[None, bool] = None,
1170+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
11281171
'''
11291172
Send a cluster-object encapsulated command to a node and get returned a future that can be awaited upon to receive
11301173
the response. If a valid responseType is passed in, that will be used to de-serialize the object. If not,
@@ -1144,7 +1187,7 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.
11441187
eventLoop = asyncio.get_running_loop()
11451188
future = eventLoop.create_future()
11461189

1147-
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
1190+
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability)
11481191
res = await ClusterCommand.SendCommand(
11491192
future, eventLoop, responseType, device.deviceProxy, ClusterCommand.CommandPath(
11501193
EndpointId=endpoint,
@@ -1158,7 +1201,8 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.
11581201
async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo],
11591202
timedRequestTimeoutMs: typing.Optional[int] = None,
11601203
interactionTimeoutMs: typing.Optional[int] = None, busyWaitMs: typing.Optional[int] = None,
1161-
suppressResponse: typing.Optional[bool] = None):
1204+
suppressResponse: typing.Optional[bool] = None,
1205+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
11621206
'''
11631207
Send a batch of cluster-object encapsulated commands to a node and get returned a future that can be awaited upon to receive
11641208
the responses. If a valid responseType is passed in, that will be used to de-serialize the object. If not,
@@ -1186,7 +1230,7 @@ async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterComm
11861230
eventLoop = asyncio.get_running_loop()
11871231
future = eventLoop.create_future()
11881232

1189-
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
1233+
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability)
11901234

11911235
res = await ClusterCommand.SendBatchCommands(
11921236
future, eventLoop, device.deviceProxy, commands,
@@ -1215,7 +1259,8 @@ def SendGroupCommand(self, groupid: int, payload: ClusterObjects.ClusterCommand,
12151259
async def WriteAttribute(self, nodeid: int,
12161260
attributes: typing.List[typing.Tuple[int, ClusterObjects.ClusterAttributeDescriptor]],
12171261
timedRequestTimeoutMs: typing.Union[None, int] = None,
1218-
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None):
1262+
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None,
1263+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
12191264
'''
12201265
Write a list of attributes on a target node.
12211266
@@ -1237,7 +1282,7 @@ async def WriteAttribute(self, nodeid: int,
12371282
eventLoop = asyncio.get_running_loop()
12381283
future = eventLoop.create_future()
12391284

1240-
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
1285+
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability)
12411286

12421287
attrs = []
12431288
for v in attributes:
@@ -1396,7 +1441,8 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[
13961441
]] = None,
13971442
eventNumberFilter: typing.Optional[int] = None,
13981443
returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None,
1399-
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True):
1444+
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True,
1445+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
14001446
'''
14011447
Read a list of attributes and/or events from a target node
14021448
@@ -1456,7 +1502,7 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[
14561502
eventLoop = asyncio.get_running_loop()
14571503
future = eventLoop.create_future()
14581504

1459-
device = await self.GetConnectedDevice(nodeid)
1505+
device = await self.GetConnectedDevice(nodeid, payloadCapability=payloadCapability)
14601506
attributePaths = [self._parseAttributePathTuple(
14611507
v) for v in attributes] if attributes else None
14621508
clusterDataVersionFilters = [self._parseDataVersionFilterTuple(
@@ -1487,7 +1533,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[
14871533
]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None,
14881534
returnClusterObject: bool = False,
14891535
reportInterval: typing.Tuple[int, int] = None,
1490-
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True):
1536+
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True,
1537+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
14911538
'''
14921539
Read a list of attributes from a target node, this is a wrapper of DeviceController.Read()
14931540
@@ -1547,7 +1594,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[
15471594
reportInterval=reportInterval,
15481595
fabricFiltered=fabricFiltered,
15491596
keepSubscriptions=keepSubscriptions,
1550-
autoResubscribe=autoResubscribe)
1597+
autoResubscribe=autoResubscribe,
1598+
payloadCapability=payloadCapability)
15511599
if isinstance(res, ClusterAttribute.SubscriptionTransaction):
15521600
return res
15531601
else:
@@ -1569,7 +1617,8 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[
15691617
fabricFiltered: bool = True,
15701618
reportInterval: typing.Tuple[int, int] = None,
15711619
keepSubscriptions: bool = False,
1572-
autoResubscribe: bool = True):
1620+
autoResubscribe: bool = True,
1621+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
15731622
'''
15741623
Read a list of events from a target node, this is a wrapper of DeviceController.Read()
15751624
@@ -1616,7 +1665,7 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[
16161665
'''
16171666
res = await self.Read(nodeid=nodeid, events=events, eventNumberFilter=eventNumberFilter,
16181667
fabricFiltered=fabricFiltered, reportInterval=reportInterval, keepSubscriptions=keepSubscriptions,
1619-
autoResubscribe=autoResubscribe)
1668+
autoResubscribe=autoResubscribe, payloadCapability=payloadCapability)
16201669
if isinstance(res, ClusterAttribute.SubscriptionTransaction):
16211670
return res
16221671
else:
@@ -1764,7 +1813,7 @@ def _InitLib(self):
17641813
self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete.restype = PyChipError
17651814

17661815
self._dmLib.pychip_GetConnectedDeviceByNodeId.argtypes = [
1767-
c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct]
1816+
c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct, c_int]
17681817
self._dmLib.pychip_GetConnectedDeviceByNodeId.restype = PyChipError
17691818

17701819
self._dmLib.pychip_FreeOperationalDeviceProxy.argtypes = [

0 commit comments

Comments
 (0)