Skip to content

Commit cea4978

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 cea4978

File tree

2 files changed

+113
-20
lines changed

2 files changed

+113
-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,45 @@ 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(
851+
deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection(), /* shouldAbort = */ false);
852+
853+
return ToPyChipError(CHIP_NO_ERROR);
813854
}
814855

815856
PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy)

src/controller/python/chip/ChipDeviceCtrl.py

+69-17
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@
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+
91+
92+
class TransportPayloadCapability(ctypes.c_int):
93+
MRP_PAYLOAD = 0
94+
LARGE_PAYLOAD = 1
95+
MRP_OR_TCP_PAYLOAD = 2
96+
8797

8898
@dataclass
8999
class CommissioningParameters:
@@ -371,6 +381,40 @@ def attestationChallenge(self) -> bytes:
371381

372382
return bytes(buf)
373383

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

375419
DiscoveryFilterType = discovery.FilterType
376420
DiscoveryType = discovery.DiscoveryType
@@ -906,7 +950,7 @@ async def FindOrEstablishPASESession(self, setupCode: str, nodeid: int, timeoutM
906950
if res.is_success:
907951
return DeviceProxyWrapper(returnDevice, DeviceProxyWrapper.DeviceProxyType.COMMISSIONEE, self._dmLib)
908952

909-
def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None):
953+
def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None, payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
910954
''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node.
911955
912956
nodeId: Target's Node ID
@@ -943,7 +987,7 @@ def deviceAvailable(self, device, err):
943987
closure = DeviceAvailableClosure()
944988
ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure))
945989
self._ChipStack.Call(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId(
946-
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback),
990+
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability),
947991
timeoutMs).raise_on_error()
948992

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

978-
async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None):
1022+
async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None,
1023+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
9791024
''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node.
9801025
9811026
nodeId: Target's Node ID
@@ -1020,7 +1065,7 @@ def deviceAvailable(self, device, err):
10201065
closure = DeviceAvailableClosure(eventLoop, future)
10211066
ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure))
10221067
await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId(
1023-
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback),
1068+
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability),
10241069
timeoutMs)
10251070

10261071
# The callback might have been received synchronously (during self._ChipStack.CallAsync()).
@@ -1124,7 +1169,8 @@ async def TestOnlySendCommandTimedRequestFlagWithNoTimedInvoke(self, nodeid: int
11241169
async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.ClusterCommand, responseType=None,
11251170
timedRequestTimeoutMs: typing.Union[None, int] = None,
11261171
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None,
1127-
suppressResponse: typing.Union[None, bool] = None):
1172+
suppressResponse: typing.Union[None, bool] = None,
1173+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
11281174
'''
11291175
Send a cluster-object encapsulated command to a node and get returned a future that can be awaited upon to receive
11301176
the response. If a valid responseType is passed in, that will be used to de-serialize the object. If not,
@@ -1144,7 +1190,7 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.
11441190
eventLoop = asyncio.get_running_loop()
11451191
future = eventLoop.create_future()
11461192

1147-
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
1193+
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability)
11481194
res = await ClusterCommand.SendCommand(
11491195
future, eventLoop, responseType, device.deviceProxy, ClusterCommand.CommandPath(
11501196
EndpointId=endpoint,
@@ -1158,7 +1204,8 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.
11581204
async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo],
11591205
timedRequestTimeoutMs: typing.Optional[int] = None,
11601206
interactionTimeoutMs: typing.Optional[int] = None, busyWaitMs: typing.Optional[int] = None,
1161-
suppressResponse: typing.Optional[bool] = None):
1207+
suppressResponse: typing.Optional[bool] = None,
1208+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
11621209
'''
11631210
Send a batch of cluster-object encapsulated commands to a node and get returned a future that can be awaited upon to receive
11641211
the responses. If a valid responseType is passed in, that will be used to de-serialize the object. If not,
@@ -1186,7 +1233,7 @@ async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterComm
11861233
eventLoop = asyncio.get_running_loop()
11871234
future = eventLoop.create_future()
11881235

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

11911238
res = await ClusterCommand.SendBatchCommands(
11921239
future, eventLoop, device.deviceProxy, commands,
@@ -1215,7 +1262,8 @@ def SendGroupCommand(self, groupid: int, payload: ClusterObjects.ClusterCommand,
12151262
async def WriteAttribute(self, nodeid: int,
12161263
attributes: typing.List[typing.Tuple[int, ClusterObjects.ClusterAttributeDescriptor]],
12171264
timedRequestTimeoutMs: typing.Union[None, int] = None,
1218-
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None):
1265+
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None,
1266+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
12191267
'''
12201268
Write a list of attributes on a target node.
12211269
@@ -1237,7 +1285,7 @@ async def WriteAttribute(self, nodeid: int,
12371285
eventLoop = asyncio.get_running_loop()
12381286
future = eventLoop.create_future()
12391287

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

12421290
attrs = []
12431291
for v in attributes:
@@ -1396,7 +1444,8 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[
13961444
]] = None,
13971445
eventNumberFilter: typing.Optional[int] = None,
13981446
returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None,
1399-
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True):
1447+
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True,
1448+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
14001449
'''
14011450
Read a list of attributes and/or events from a target node
14021451
@@ -1456,7 +1505,7 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[
14561505
eventLoop = asyncio.get_running_loop()
14571506
future = eventLoop.create_future()
14581507

1459-
device = await self.GetConnectedDevice(nodeid)
1508+
device = await self.GetConnectedDevice(nodeid, payloadCapability=payloadCapability)
14601509
attributePaths = [self._parseAttributePathTuple(
14611510
v) for v in attributes] if attributes else None
14621511
clusterDataVersionFilters = [self._parseDataVersionFilterTuple(
@@ -1487,7 +1536,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[
14871536
]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None,
14881537
returnClusterObject: bool = False,
14891538
reportInterval: typing.Tuple[int, int] = None,
1490-
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True):
1539+
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True,
1540+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
14911541
'''
14921542
Read a list of attributes from a target node, this is a wrapper of DeviceController.Read()
14931543
@@ -1547,7 +1597,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[
15471597
reportInterval=reportInterval,
15481598
fabricFiltered=fabricFiltered,
15491599
keepSubscriptions=keepSubscriptions,
1550-
autoResubscribe=autoResubscribe)
1600+
autoResubscribe=autoResubscribe,
1601+
payloadCapability=payloadCapability)
15511602
if isinstance(res, ClusterAttribute.SubscriptionTransaction):
15521603
return res
15531604
else:
@@ -1569,7 +1620,8 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[
15691620
fabricFiltered: bool = True,
15701621
reportInterval: typing.Tuple[int, int] = None,
15711622
keepSubscriptions: bool = False,
1572-
autoResubscribe: bool = True):
1623+
autoResubscribe: bool = True,
1624+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
15731625
'''
15741626
Read a list of events from a target node, this is a wrapper of DeviceController.Read()
15751627
@@ -1616,7 +1668,7 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[
16161668
'''
16171669
res = await self.Read(nodeid=nodeid, events=events, eventNumberFilter=eventNumberFilter,
16181670
fabricFiltered=fabricFiltered, reportInterval=reportInterval, keepSubscriptions=keepSubscriptions,
1619-
autoResubscribe=autoResubscribe)
1671+
autoResubscribe=autoResubscribe, payloadCapability=payloadCapability)
16201672
if isinstance(res, ClusterAttribute.SubscriptionTransaction):
16211673
return res
16221674
else:
@@ -1764,7 +1816,7 @@ def _InitLib(self):
17641816
self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete.restype = PyChipError
17651817

17661818
self._dmLib.pychip_GetConnectedDeviceByNodeId.argtypes = [
1767-
c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct]
1819+
c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct, c_int]
17681820
self._dmLib.pychip_GetConnectedDeviceByNodeId.restype = PyChipError
17691821

17701822
self._dmLib.pychip_FreeOperationalDeviceProxy.argtypes = [

0 commit comments

Comments
 (0)