Skip to content

Commit bec5fc3

Browse files
authored
[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
1 parent e22266b commit bec5fc3

File tree

10 files changed

+143
-100
lines changed

10 files changed

+143
-100
lines changed

src/controller/python/chip/ChipDeviceCtrl.py

+54-41
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,14 @@ class CommissionableNode(discovery.CommissionableNode):
230230
def SetDeviceController(self, devCtrl: 'ChipDeviceController'):
231231
self._devCtrl = devCtrl
232232

233-
def Commission(self, nodeId: int, setupPinCode: int) -> PyChipError:
233+
def Commission(self, nodeId: int, setupPinCode: int) -> int:
234234
''' Commission the device using the device controller discovered this device.
235235
236236
nodeId: The nodeId commissioned to the device
237237
setupPinCode: The setup pin code of the device
238+
239+
Returns:
240+
- Effective Node ID of the device (as defined by the assigned NOC)
238241
'''
239242
return self._devCtrl.CommissionOnNetwork(
240243
nodeId, setupPinCode, filterType=discovery.FilterType.INSTANCE_NAME, filter=self.instanceName)
@@ -365,7 +368,10 @@ def HandleCommissioningComplete(nodeId: int, err: PyChipError):
365368
logging.exception("HandleCommissioningComplete called unexpectedly")
366369
return
367370

368-
self._commissioning_complete_future.set_result(err)
371+
if err.is_success:
372+
self._commissioning_complete_future.set_result(nodeId)
373+
else:
374+
self._commissioning_complete_future.set_exception(err.to_exception())
369375

370376
def HandleFabricCheck(nodeId):
371377
self.fabricCheckNodeId = nodeId
@@ -413,14 +419,17 @@ def HandlePASEEstablishmentComplete(err: PyChipError):
413419
# During Commissioning, HandlePASEEstablishmentComplete will also be called.
414420
# Only complete the future if PASE session establishment failed.
415421
if not err.is_success:
416-
self._commissioning_complete_future.set_result(err)
422+
self._commissioning_complete_future.set_exception(err.to_exception())
417423
return
418424

419425
if self._pase_establishment_complete_future is None:
420426
logging.exception("HandlePASEEstablishmentComplete called unexpectedly")
421427
return
422428

423-
self._pase_establishment_complete_future.set_result(err)
429+
if err.is_success:
430+
self._pase_establishment_complete_future.set_result(None)
431+
else:
432+
self._pase_establishment_complete_future.set_exception(err.to_exception())
424433

425434
self.pairingDelegate = pairingDelegate
426435
self.devCtrl = devCtrl
@@ -538,7 +547,12 @@ def IsConnected(self):
538547
self.devCtrl)
539548
)
540549

541-
def ConnectBLE(self, discriminator: int, setupPinCode: int, nodeid: int, isShortDiscriminator: bool = False) -> PyChipError:
550+
def ConnectBLE(self, discriminator: int, setupPinCode: int, nodeid: int, isShortDiscriminator: bool = False) -> int:
551+
"""Connect to a BLE device using the given discriminator and setup pin code.
552+
553+
Returns:
554+
- Effective Node ID of the device (as defined by the assigned NOC)
555+
"""
542556
self.CheckIsActive()
543557

544558
self._commissioning_complete_future = concurrent.futures.Future()
@@ -550,11 +564,7 @@ def ConnectBLE(self, discriminator: int, setupPinCode: int, nodeid: int, isShort
550564
self.devCtrl, discriminator, isShortDiscriminator, setupPinCode, nodeid)
551565
).raise_on_error()
552566

553-
# TODO: Change return None. Only returning on success is not useful.
554-
# but that is what the previous implementation did.
555-
res = self._commissioning_complete_future.result()
556-
res.raise_on_error()
557-
return res
567+
return self._commissioning_complete_future.result()
558568
finally:
559569
self._commissioning_complete_future = None
560570

@@ -600,7 +610,7 @@ def CloseSession(self, nodeid):
600610
self.devCtrl, nodeid)
601611
).raise_on_error()
602612

603-
def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid: int):
613+
def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid: int) -> None:
604614
self.CheckIsActive()
605615

606616
self._pase_establishment_complete_future = concurrent.futures.Future()
@@ -611,16 +621,11 @@ def EstablishPASESessionBLE(self, setupPinCode: int, discriminator: int, nodeid:
611621
self.devCtrl, setupPinCode, discriminator, nodeid)
612622
).raise_on_error()
613623

614-
# TODO: This is a bit funky, but what the API returned with the previous
615-
# implementation. We should revisit this.
616-
err = self._pase_establishment_complete_future.result()
617-
if not err.is_success:
618-
return err.to_exception()
619-
return None
624+
self._pase_establishment_complete_future.result()
620625
finally:
621626
self._pase_establishment_complete_future = None
622627

623-
def EstablishPASESessionIP(self, ipaddr: str, setupPinCode: int, nodeid: int, port: int = 0):
628+
def EstablishPASESessionIP(self, ipaddr: str, setupPinCode: int, nodeid: int, port: int = 0) -> None:
624629
self.CheckIsActive()
625630

626631
self._pase_establishment_complete_future = concurrent.futures.Future()
@@ -631,16 +636,11 @@ def EstablishPASESessionIP(self, ipaddr: str, setupPinCode: int, nodeid: int, po
631636
self.devCtrl, ipaddr.encode("utf-8"), setupPinCode, nodeid, port)
632637
).raise_on_error()
633638

634-
# TODO: This is a bit funky, but what the API returned with the previous
635-
# implementation. We should revisit this.
636-
err = self._pase_establishment_complete_future.result()
637-
if not err.is_success:
638-
return err.to_exception()
639-
return None
639+
self._pase_establishment_complete_future.result()
640640
finally:
641641
self._pase_establishment_complete_future = None
642642

643-
def EstablishPASESession(self, setUpCode: str, nodeid: int):
643+
def EstablishPASESession(self, setUpCode: str, nodeid: int) -> None:
644644
self.CheckIsActive()
645645

646646
self._pase_establishment_complete_future = concurrent.futures.Future()
@@ -651,12 +651,7 @@ def EstablishPASESession(self, setUpCode: str, nodeid: int):
651651
self.devCtrl, setUpCode.encode("utf-8"), nodeid)
652652
).raise_on_error()
653653

654-
# TODO: This is a bit funky, but what the API returned with the previous
655-
# implementation. We should revisit this.
656-
err = self._pase_establishment_complete_future.result()
657-
if not err.is_success:
658-
return err.to_exception()
659-
return None
654+
self._pase_establishment_complete_future.result()
660655
finally:
661656
self._pase_establishment_complete_future = None
662657

@@ -1874,17 +1869,19 @@ def caIndex(self) -> int:
18741869
def fabricAdmin(self) -> FabricAdmin:
18751870
return self._fabricAdmin
18761871

1877-
def Commission(self, nodeid) -> PyChipError:
1872+
def Commission(self, nodeid) -> int:
18781873
'''
18791874
Start the auto-commissioning process on a node after establishing a PASE connection.
18801875
This function is intended to be used in conjunction with `EstablishPASESessionBLE` or
18811876
`EstablishPASESessionIP`. It can be called either before or after the DevicePairingDelegate
18821877
receives the OnPairingComplete call. Commissioners that want to perform simple
1883-
auto-commissioning should use the supplied "PairDevice" functions above, which will
1878+
auto-commissioning should use the supplied "CommissionWithCode" function, which will
18841879
establish the PASE connection and commission automatically.
18851880
1886-
Return:
1887-
bool: True if successful, False otherwise.
1881+
Raises a ChipStackError on failure.
1882+
1883+
Returns:
1884+
- Effective Node ID of the device (as defined by the assigned NOC)
18881885
'''
18891886
self.CheckIsActive()
18901887

@@ -1900,13 +1897,13 @@ def Commission(self, nodeid) -> PyChipError:
19001897
finally:
19011898
self._commissioning_complete_future = None
19021899

1903-
def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes, isShortDiscriminator: bool = False) -> PyChipError:
1900+
def CommissionThread(self, discriminator, setupPinCode, nodeId, threadOperationalDataset: bytes, isShortDiscriminator: bool = False) -> int:
19041901
''' Commissions a Thread device over BLE
19051902
'''
19061903
self.SetThreadOperationalDataset(threadOperationalDataset)
19071904
return self.ConnectBLE(discriminator, setupPinCode, nodeId, isShortDiscriminator)
19081905

1909-
def CommissionWiFi(self, discriminator, setupPinCode, nodeId, ssid: str, credentials: str, isShortDiscriminator: bool = False) -> PyChipError:
1906+
def CommissionWiFi(self, discriminator, setupPinCode, nodeId, ssid: str, credentials: str, isShortDiscriminator: bool = False) -> int:
19101907
''' Commissions a Wi-Fi device over BLE.
19111908
'''
19121909
self.SetWiFiCredentials(ssid, credentials)
@@ -2009,7 +2006,7 @@ def GetFabricCheckResult(self) -> int:
20092006
return self.fabricCheckNodeId
20102007

20112008
def CommissionOnNetwork(self, nodeId: int, setupPinCode: int,
2012-
filterType: DiscoveryFilterType = DiscoveryFilterType.NONE, filter: typing.Any = None, discoveryTimeoutMsec: int = 30000) -> PyChipError:
2009+
filterType: DiscoveryFilterType = DiscoveryFilterType.NONE, filter: typing.Any = None, discoveryTimeoutMsec: int = 30000) -> int:
20132010
'''
20142011
Does the routine for OnNetworkCommissioning, with a filter for mDNS discovery.
20152012
Supported filters are:
@@ -2025,6 +2022,11 @@ def CommissionOnNetwork(self, nodeId: int, setupPinCode: int,
20252022
DiscoveryFilterType.COMPRESSED_FABRIC_ID
20262023
20272024
The filter can be an integer, a string or None depending on the actual type of selected filter.
2025+
2026+
Raises a ChipStackError on failure.
2027+
2028+
Returns:
2029+
- Effective Node ID of the device (as defined by the assigned NOC)
20282030
'''
20292031
self.CheckIsActive()
20302032

@@ -2044,9 +2046,14 @@ def CommissionOnNetwork(self, nodeId: int, setupPinCode: int,
20442046
finally:
20452047
self._commissioning_complete_future = None
20462048

2047-
def CommissionWithCode(self, setupPayload: str, nodeid: int, discoveryType: DiscoveryType = DiscoveryType.DISCOVERY_ALL) -> PyChipError:
2049+
def CommissionWithCode(self, setupPayload: str, nodeid: int, discoveryType: DiscoveryType = DiscoveryType.DISCOVERY_ALL) -> int:
20482050
''' Commission with the given nodeid from the setupPayload.
20492051
setupPayload may be a QR or manual code.
2052+
2053+
Raises a ChipStackError on failure.
2054+
2055+
Returns:
2056+
- Effective Node ID of the device (as defined by the assigned NOC)
20502057
'''
20512058
self.CheckIsActive()
20522059

@@ -2063,8 +2070,14 @@ def CommissionWithCode(self, setupPayload: str, nodeid: int, discoveryType: Disc
20632070
finally:
20642071
self._commissioning_complete_future = None
20652072

2066-
def CommissionIP(self, ipaddr: str, setupPinCode: int, nodeid: int) -> PyChipError:
2067-
""" DEPRECATED, DO NOT USE! Use `CommissionOnNetwork` or `CommissionWithCode` """
2073+
def CommissionIP(self, ipaddr: str, setupPinCode: int, nodeid: int) -> int:
2074+
""" DEPRECATED, DO NOT USE! Use `CommissionOnNetwork` or `CommissionWithCode`
2075+
2076+
Raises a ChipStackError on failure.
2077+
2078+
Returns:
2079+
- Effective Node ID of the device (as defined by the assigned NOC)
2080+
"""
20682081
self.CheckIsActive()
20692082

20702083
self._commissioning_complete_future = concurrent.futures.Future()

src/controller/python/chip/yaml/runner.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,10 @@ async def run_action(self, dev_ctrl: ChipDeviceController) -> _ActionResult:
664664
if self._command == 'GetCommissionerNodeId':
665665
return _ActionResult(status=_ActionStatus.SUCCESS, response=_GetCommissionerNodeIdResult(dev_ctrl.nodeId))
666666

667-
resp = dev_ctrl.CommissionWithCode(self._setup_payload, self._node_id)
668-
if resp:
667+
try:
668+
dev_ctrl.CommissionWithCode(self._setup_payload, self._node_id)
669669
return _ActionResult(status=_ActionStatus.SUCCESS, response=None)
670-
else:
670+
except ChipStackError:
671671
return _ActionResult(status=_ActionStatus.ERROR, response=None)
672672

673673

src/controller/python/test/test_scripts/base.py

+19-9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from chip import ChipDeviceCtrl
4242
from chip.ChipStack import ChipStack
4343
from chip.crypto import p256keypair
44+
from chip.exceptions import ChipStackException
4445
from chip.utils import CommissioningBuildingBlocks
4546
from cirque_restart_remote_device import restartRemoteDevice
4647
from ecdsa import NIST256p
@@ -256,8 +257,9 @@ def TestPaseOnly(self, ip: str, setuppin: int, nodeid: int, devCtrl=None):
256257
devCtrl = self.devCtrl
257258
self.logger.info(
258259
"Attempting to establish PASE session with device id: {} addr: {}".format(str(nodeid), ip))
259-
if devCtrl.EstablishPASESessionIP(
260-
ip, setuppin, nodeid) is not None:
260+
try:
261+
devCtrl.EstablishPASESessionIP(ip, setuppin, nodeid)
262+
except ChipStackException:
261263
self.logger.info(
262264
"Failed to establish PASE session with device id: {} addr: {}".format(str(nodeid), ip))
263265
return False
@@ -268,7 +270,9 @@ def TestPaseOnly(self, ip: str, setuppin: int, nodeid: int, devCtrl=None):
268270
def TestCommissionOnly(self, nodeid: int):
269271
self.logger.info(
270272
"Commissioning device with id {}".format(nodeid))
271-
if not self.devCtrl.Commission(nodeid):
273+
try:
274+
self.devCtrl.Commission(nodeid)
275+
except ChipStackException:
272276
self.logger.info(
273277
"Failed to commission device with id {}".format(str(nodeid)))
274278
return False
@@ -311,17 +315,21 @@ def TestCommissionFailureOnReport(self, nodeid: int, failAfter: int):
311315

312316
def TestCommissioning(self, ip: str, setuppin: int, nodeid: int):
313317
self.logger.info("Commissioning device {}".format(ip))
314-
if not self.devCtrl.CommissionIP(ip, setuppin, nodeid):
315-
self.logger.info(
318+
try:
319+
self.devCtrl.CommissionIP(ip, setuppin, nodeid)
320+
except ChipStackException:
321+
self.logger.exception(
316322
"Failed to finish commissioning device {}".format(ip))
317323
return False
318324
self.logger.info("Commissioning finished.")
319325
return True
320326

321327
def TestCommissioningWithSetupPayload(self, setupPayload: str, nodeid: int, discoveryType: int = 2):
322328
self.logger.info("Commissioning device with setup payload {}".format(setupPayload))
323-
if not self.devCtrl.CommissionWithCode(setupPayload, nodeid, chip.discovery.DiscoveryType(discoveryType)):
324-
self.logger.info(
329+
try:
330+
self.devCtrl.CommissionWithCode(setupPayload, nodeid, chip.discovery.DiscoveryType(discoveryType))
331+
except ChipStackException:
332+
self.logger.exception(
325333
"Failed to finish commissioning device {}".format(setupPayload))
326334
return False
327335
self.logger.info("Commissioning finished.")
@@ -783,8 +791,10 @@ async def TestMultiFabric(self, ip: str, setuppin: int, nodeid: int):
783791
self.devCtrl2 = self.fabricAdmin2.NewController(
784792
self.controllerNodeId, self.paaTrustStorePath)
785793

786-
if not self.devCtrl2.CommissionIP(ip, setuppin, nodeid):
787-
self.logger.info(
794+
try:
795+
self.devCtrl2.CommissionIP(ip, setuppin, nodeid)
796+
except ChipStackException:
797+
self.logger.exception(
788798
"Failed to finish key exchange with device {}".format(ip))
789799
return False
790800

src/python_testing/TC_ACE_1_5.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ async def test_TC_ACE_1_5(self):
5454
params = self.openCommissioningWindow(self.th1, self.dut_node_id)
5555
self.print_step(2, "TH1 opens the commissioning window on the DUT")
5656

57-
errcode = self.th2.CommissionOnNetwork(
57+
self.th2.CommissionOnNetwork(
5858
nodeId=self.dut_node_id, setupPinCode=params.commissioningParameters.setupPinCode,
5959
filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=params.randomDiscriminator)
60-
logging.info('Commissioning complete done. Successful? {}, errorcode = {}'.format(errcode.is_success, errcode))
60+
logging.info('Commissioning complete done. Successful.')
6161
self.print_step(3, "TH2 commissions DUT using admin node ID N2")
6262

6363
self.print_step(4, "TH2 reads its fabric index from the Operational Credentials cluster CurrentFabricIndex attribute")

src/python_testing/TC_CGEN_2_4.py

+15-9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import chip.FabricAdmin
2626
from chip import ChipDeviceCtrl
2727
from chip.ChipDeviceCtrl import CommissioningParameters
28+
from chip.exceptions import ChipStackError
2829
from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main
2930
from mobly import asserts
3031

@@ -60,11 +61,12 @@ async def CommissionToStageSendCompleteAndCleanup(
6061
# This will run the commissioning up to the point where stage x is run and the
6162
# response is sent before the test commissioner simulates a failure
6263
self.th2.SetTestCommissionerPrematureCompleteAfter(stage)
63-
errcode = self.th2.CommissionOnNetwork(
64-
nodeId=self.dut_node_id, setupPinCode=params.setupPinCode,
65-
filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator)
66-
logging.info('Commissioning complete done. Successful? {}, errorcode = {}'.format(errcode.is_success, errcode))
67-
asserts.assert_false(errcode.is_success, 'Commissioning complete did not error as expected')
64+
ctx = asserts.assert_raises(ChipStackError)
65+
with ctx:
66+
self.th2.CommissionOnNetwork(
67+
nodeId=self.dut_node_id, setupPinCode=params.setupPinCode,
68+
filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator)
69+
errcode = ctx.exception.chip_error
6870
asserts.assert_true(errcode.sdk_part == expectedErrorPart, 'Unexpected error type returned from CommissioningComplete')
6971
asserts.assert_true(errcode.sdk_code == expectedErrCode, 'Unexpected error code returned from CommissioningComplete')
7072
revokeCmd = Clusters.AdministratorCommissioning.Commands.RevokeCommissioning()
@@ -101,10 +103,14 @@ async def test_TC_CGEN_2_4(self):
101103

102104
logging.info('Step 16 - TH2 fully commissions the DUT')
103105
self.th2.ResetTestCommissioner()
104-
errcode = self.th2.CommissionOnNetwork(
105-
nodeId=self.dut_node_id, setupPinCode=params.setupPinCode,
106-
filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator)
107-
logging.info('Commissioning complete done. Successful? {}, errorcode = {}'.format(errcode.is_success, errcode))
106+
107+
ctx = asserts.assert_raises(ChipStackError)
108+
with ctx:
109+
self.th2.CommissionOnNetwork(
110+
nodeId=self.dut_node_id, setupPinCode=params.setupPinCode,
111+
filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=self.discriminator)
112+
asserts.assert_true(ctx.exception.chip_error.sdk_code == 0x02, 'Unexpected error code returned from CommissioningComplete')
113+
logging.info('Commissioning complete done.')
108114

109115
logging.info('Step 17 - TH1 sends an arm failsafe')
110116
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=900, breadcrumb=0)

src/python_testing/TC_DA_1_5.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,9 @@ async def test_TC_DA_1_5(self):
170170
new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=2)
171171
TH2 = new_fabric_admin.NewController(nodeId=112233)
172172

173-
errcode = TH2.CommissionOnNetwork(
173+
TH2.CommissionOnNetwork(
174174
nodeId=self.dut_node_id, setupPinCode=params.setupPinCode,
175175
filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234)
176-
asserts.assert_true(errcode.is_success, 'Commissioning on TH2 did not complete successfully')
177176

178177
self.print_step(15, "Read NOCs list for TH1")
179178
temp = await self.read_single_attribute_check_success(

0 commit comments

Comments
 (0)