From b9eee2802ecfe6c9be96c2b9ef3e6c845c7954e9 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Tue, 28 May 2024 15:16:41 +0200 Subject: [PATCH 1/7] [Tizen] Add libatomic.so to QEMU Docker image (#33631) --- integrations/docker/images/base/chip-build/version | 2 +- .../docker/images/stage-3/chip-build-tizen-qemu/Dockerfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index 9a59e96c18355c..05ab2a03dbdb4f 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -52 : [NXP] Update K32W0 SDK to 2.6.14 \ No newline at end of file +53 : [Tizen] Add libatomic.so to QEMU Docker image diff --git a/integrations/docker/images/stage-3/chip-build-tizen-qemu/Dockerfile b/integrations/docker/images/stage-3/chip-build-tizen-qemu/Dockerfile index ff63e5c1940a01..8c38d19c1b3ed8 100644 --- a/integrations/docker/images/stage-3/chip-build-tizen-qemu/Dockerfile +++ b/integrations/docker/images/stage-3/chip-build-tizen-qemu/Dockerfile @@ -84,6 +84,7 @@ RUN set -x \ # Add extra libraries to the root image && guestfish --rw -a $TIZEN_IOT_IMAGE_ROOT -m /dev/sda copy-in \ $TIZEN_SDK_TOOLCHAIN/arm-tizen-linux-gnueabi/lib/libasan.so.* \ + $TIZEN_SDK_TOOLCHAIN/arm-tizen-linux-gnueabi/lib/libatomic.so.* \ $TIZEN_SDK_TOOLCHAIN/arm-tizen-linux-gnueabi/lib/libubsan.so.* \ $TIZEN_SDK_SYSROOT/usr/lib/libbluetooth-api.so.* \ $TIZEN_SDK_SYSROOT/usr/lib/libcapi-network-bluetooth.so.* \ From 4dac5367c3f20c0c3d6ea1f428b375bdfec0e0d7 Mon Sep 17 00:00:00 2001 From: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com> Date: Wed, 29 May 2024 01:44:55 +1200 Subject: [PATCH 2/7] Linux: Guard mallinfo() use behind check for __GLIBC__ (#33626) * Linux: Guard mallinfo() use behind check for __GLIBC__ * Use positive checks for __GLIBC__ rather than ifndef --- .../Linux/DiagnosticDataProviderImpl.cpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/platform/Linux/DiagnosticDataProviderImpl.cpp b/src/platform/Linux/DiagnosticDataProviderImpl.cpp index 5ee5558064afeb..bebe5827163fa9 100644 --- a/src/platform/Linux/DiagnosticDataProviderImpl.cpp +++ b/src/platform/Linux/DiagnosticDataProviderImpl.cpp @@ -74,8 +74,10 @@ enum class WiFiStatsCountType kWiFiOverrunCount }; +#if defined(__GLIBC__) // Static variable to store the maximum heap size static size_t maxHeapHighWatermark = 0; +#endif CHIP_ERROR GetEthernetStatsCount(EthernetStatsCountType type, uint64_t & count) { @@ -223,9 +225,7 @@ DiagnosticDataProviderImpl & DiagnosticDataProviderImpl::GetDefaultInstance() CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapFree(uint64_t & currentHeapFree) { -#ifndef __GLIBC__ - return CHIP_ERROR_NOT_IMPLEMENTED; -#else +#if defined(__GLIBC__) struct mallinfo mallocInfo = mallinfo(); // Get the current amount of heap memory, in bytes, that are not being utilized @@ -233,14 +233,14 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapFree(uint64_t & currentHeap currentHeapFree = mallocInfo.fordblks; return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; #endif } CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapUsed(uint64_t & currentHeapUsed) { -#ifndef __GLIBC__ - return CHIP_ERROR_NOT_IMPLEMENTED; -#else +#if defined(__GLIBC__) struct mallinfo mallocInfo = mallinfo(); // Get the current amount of heap memory, in bytes, that are being used by @@ -253,14 +253,14 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapUsed(uint64_t & currentHeap maxHeapHighWatermark = currentHeapUsed; } return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; #endif } CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapHighWatermark(uint64_t & currentHeapHighWatermark) { -#ifndef __GLIBC__ - return CHIP_ERROR_NOT_IMPLEMENTED; -#else +#if defined(__GLIBC__) struct mallinfo mallocInfo = mallinfo(); // The usecase of this function is embedded devices,on which we would need to intercept @@ -279,6 +279,8 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapHighWatermark(uint64_t & cu currentHeapHighWatermark = maxHeapHighWatermark; return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; #endif } @@ -287,10 +289,12 @@ CHIP_ERROR DiagnosticDataProviderImpl::ResetWatermarks() // If implemented, the server SHALL set the value of the CurrentHeapHighWatermark attribute to the // value of the CurrentHeapUsed. +#if defined(__GLIBC__) // Get the current amount of heap memory, in bytes, that are being used by // the current running program and reset the max heap high watermark to current heap amount. struct mallinfo mallocInfo = mallinfo(); maxHeapHighWatermark = mallocInfo.uordblks; +#endif return CHIP_NO_ERROR; } From c7b4965356abe49439348548259199a8daec3e05 Mon Sep 17 00:00:00 2001 From: jamesharrow <93921463+jamesharrow@users.noreply.github.com> Date: Tue, 28 May 2024 14:45:33 +0100 Subject: [PATCH 3/7] Remove provisional from evse, evse mode, epm, eem, power topology (#33558) * XML file changes to remove apiMaturity="provisional" for clusters and features which are approved in Matter 1.3 * Removed stray text. * Updated after regen_all --- .../all-clusters-app.matter | 10 ++-- .../energy-management-app.matter | 10 ++-- .../electrical-energy-measurement-cluster.xml | 10 ++-- .../electrical-power-measurement-cluster.xml | 10 ++-- .../data-model/chip/energy-evse-cluster.xml | 60 +++++++++---------- .../chip/energy-evse-mode-cluster.xml | 2 +- .../chip/power-topology-cluster.xml | 2 +- .../data_model/controller-clusters.matter | 10 ++-- 8 files changed, 57 insertions(+), 57 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 03e3cc6ca3fe20..719293ab5f877a 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -3876,7 +3876,7 @@ cluster ValveConfigurationAndControl = 129 { } /** This cluster provides a mechanism for querying data about electrical power as measured by the server. */ -provisional cluster ElectricalPowerMeasurement = 144 { +cluster ElectricalPowerMeasurement = 144 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -3981,7 +3981,7 @@ provisional cluster ElectricalPowerMeasurement = 144 { } /** This cluster provides a mechanism for querying data about the electrical energy imported or provided by the server. */ -provisional cluster ElectricalEnergyMeasurement = 145 { +cluster ElectricalEnergyMeasurement = 145 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -4278,7 +4278,7 @@ provisional cluster DeviceEnergyManagement = 152 { } /** Electric Vehicle Supply Equipment (EVSE) is equipment used to charge an Electric Vehicle (EV) or Plug-In Hybrid Electric Vehicle. This cluster provides an interface to the functionality of Electric Vehicle Supply Equipment (EVSE) management. */ -provisional cluster EnergyEvse = 153 { +cluster EnergyEvse = 153 { revision 2; enum EnergyTransferStoppedReasonEnum : enum8 { @@ -4490,7 +4490,7 @@ provisional cluster EnergyPreference = 155 { } /** The Power Topology Cluster provides a mechanism for expressing how power is flowing between endpoints. */ -provisional cluster PowerTopology = 156 { +cluster PowerTopology = 156 { revision 1; bitmap Feature : bitmap32 { @@ -4511,7 +4511,7 @@ provisional cluster PowerTopology = 156 { } /** Attributes and commands for selecting a mode from a list of supported options. */ -provisional cluster EnergyEvseMode = 157 { +cluster EnergyEvseMode = 157 { revision 1; enum ModeTag : enum16 { diff --git a/examples/energy-management-app/energy-management-common/energy-management-app.matter b/examples/energy-management-app/energy-management-common/energy-management-app.matter index ea4e5db21a0b18..68db2eea271ec3 100644 --- a/examples/energy-management-app/energy-management-common/energy-management-app.matter +++ b/examples/energy-management-app/energy-management-common/energy-management-app.matter @@ -1237,7 +1237,7 @@ cluster GroupKeyManagement = 63 { } /** This cluster provides a mechanism for querying data about electrical power as measured by the server. */ -provisional cluster ElectricalPowerMeasurement = 144 { +cluster ElectricalPowerMeasurement = 144 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -1342,7 +1342,7 @@ provisional cluster ElectricalPowerMeasurement = 144 { } /** This cluster provides a mechanism for querying data about the electrical energy imported or provided by the server. */ -provisional cluster ElectricalEnergyMeasurement = 145 { +cluster ElectricalEnergyMeasurement = 145 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -1639,7 +1639,7 @@ provisional cluster DeviceEnergyManagement = 152 { } /** Electric Vehicle Supply Equipment (EVSE) is equipment used to charge an Electric Vehicle (EV) or Plug-In Hybrid Electric Vehicle. This cluster provides an interface to the functionality of Electric Vehicle Supply Equipment (EVSE) management. */ -provisional cluster EnergyEvse = 153 { +cluster EnergyEvse = 153 { revision 2; enum EnergyTransferStoppedReasonEnum : enum8 { @@ -1817,7 +1817,7 @@ provisional cluster EnergyEvse = 153 { } /** The Power Topology Cluster provides a mechanism for expressing how power is flowing between endpoints. */ -provisional cluster PowerTopology = 156 { +cluster PowerTopology = 156 { revision 1; bitmap Feature : bitmap32 { @@ -1838,7 +1838,7 @@ provisional cluster PowerTopology = 156 { } /** Attributes and commands for selecting a mode from a list of supported options. */ -provisional cluster EnergyEvseMode = 157 { +cluster EnergyEvseMode = 157 { revision 1; enum ModeTag : enum16 { diff --git a/src/app/zap-templates/zcl/data-model/chip/electrical-energy-measurement-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/electrical-energy-measurement-cluster.xml index 0a4039badf7c97..eb05052538d2de 100644 --- a/src/app/zap-templates/zcl/data-model/chip/electrical-energy-measurement-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/electrical-energy-measurement-cluster.xml @@ -16,7 +16,7 @@ limitations under the License. --> - + Electrical Energy Measurement Measurement & Sensing 0x0091 @@ -52,25 +52,25 @@ limitations under the License. PeriodicEnergyExported CumulativeEnergyReset - + CumulativeEnergyMeasured - + PeriodicEnergyMeasured - + - + diff --git a/src/app/zap-templates/zcl/data-model/chip/electrical-power-measurement-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/electrical-power-measurement-cluster.xml index 69be8e08570f39..7f49a643d1b1f9 100644 --- a/src/app/zap-templates/zcl/data-model/chip/electrical-power-measurement-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/electrical-power-measurement-cluster.xml @@ -16,7 +16,7 @@ limitations under the License. --> - + Electrical Power Measurement Measurement & Sensing 0x0090 @@ -82,18 +82,18 @@ limitations under the License. PowerFactor NeutralCurrent - + MeasurementPeriodRanges - + - + @@ -107,7 +107,7 @@ limitations under the License. - + diff --git a/src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml index cbc0882ac1b3ac..5c22b8e93be8da 100644 --- a/src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml @@ -14,7 +14,7 @@ limitations under the License. - + @@ -25,7 +25,7 @@ limitations under the License. - + @@ -34,7 +34,7 @@ limitations under the License. - + @@ -55,7 +55,7 @@ limitations under the License. - + @@ -86,7 +86,7 @@ limitations under the License. - + Energy EVSE Energy Management 0x0099 @@ -177,7 +177,7 @@ limitations under the License. Allows a client to disable the EVSE from charging and discharging. - + @@ -188,7 +188,7 @@ limitations under the License. Allows a client to enable the EVSE to discharge an EV. - + Allows a client to put the EVSE into a self-diagnostics mode. @@ -205,41 +205,41 @@ limitations under the License. The GetTargetsResponse is sent in response to the GetTargets Command. - + EVConnected - + - + EVNotDetected - - - - + + + + - + EnergyTransferStarted - - - + + + - + EnergyTransferStopped - - - - + + + + - + Fault - - - - + + + + - + RFID - + diff --git a/src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml index 22e5562ed33f7f..c4b702b701d174 100644 --- a/src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml @@ -24,7 +24,7 @@ limitations under the License. - + General Energy EVSE Mode 0x009D diff --git a/src/app/zap-templates/zcl/data-model/chip/power-topology-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/power-topology-cluster.xml index 2fdddcbe3560da..3bca0bd43ee8af 100644 --- a/src/app/zap-templates/zcl/data-model/chip/power-topology-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/power-topology-cluster.xml @@ -17,7 +17,7 @@ limitations under the License. - + Measurement & Sensing Power Topology 0x009C diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index dcc22df982b9e5..cd310219ea88d2 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -4112,7 +4112,7 @@ cluster ValveConfigurationAndControl = 129 { } /** This cluster provides a mechanism for querying data about electrical power as measured by the server. */ -provisional cluster ElectricalPowerMeasurement = 144 { +cluster ElectricalPowerMeasurement = 144 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -4217,7 +4217,7 @@ provisional cluster ElectricalPowerMeasurement = 144 { } /** This cluster provides a mechanism for querying data about the electrical energy imported or provided by the server. */ -provisional cluster ElectricalEnergyMeasurement = 145 { +cluster ElectricalEnergyMeasurement = 145 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -4803,7 +4803,7 @@ provisional cluster DeviceEnergyManagement = 152 { } /** Electric Vehicle Supply Equipment (EVSE) is equipment used to charge an Electric Vehicle (EV) or Plug-In Hybrid Electric Vehicle. This cluster provides an interface to the functionality of Electric Vehicle Supply Equipment (EVSE) management. */ -provisional cluster EnergyEvse = 153 { +cluster EnergyEvse = 153 { revision 2; enum EnergyTransferStoppedReasonEnum : enum8 { @@ -5015,7 +5015,7 @@ provisional cluster EnergyPreference = 155 { } /** The Power Topology Cluster provides a mechanism for expressing how power is flowing between endpoints. */ -provisional cluster PowerTopology = 156 { +cluster PowerTopology = 156 { revision 1; bitmap Feature : bitmap32 { @@ -5036,7 +5036,7 @@ provisional cluster PowerTopology = 156 { } /** Attributes and commands for selecting a mode from a list of supported options. */ -provisional cluster EnergyEvseMode = 157 { +cluster EnergyEvseMode = 157 { revision 1; enum ModeTag : enum16 { From 533df0c7ab1b9d7b7bb41e4ab119985c0bfe43ca Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 28 May 2024 10:19:41 -0400 Subject: [PATCH 4/7] Fix test by making darwin-framework-tool use the same command timeout as chip-tool. (#33633) The commands now take a bit longer to time out on the exchange level, so we were hitting the test timeout. --- .../commands/clusters/ModelCommandBridge.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.h b/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.h index 447d2c2ae5ccf6..2deb7cfb4cac5e 100644 --- a/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.h +++ b/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.h @@ -38,7 +38,7 @@ class ModelCommand : public CHIPCommandBridge /////////// CHIPCommand Interface ///////// CHIP_ERROR RunCommand() override; - chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(10); } + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(20); } virtual CHIP_ERROR SendCommand(MTRBaseDevice * _Nonnull device, chip::EndpointId endPointId) = 0; From 5656870a131ae7611c838a3c82ececc7796c8235 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 28 May 2024 13:26:49 -0400 Subject: [PATCH 5/7] Start the helper apps for the MTRDevice pool test as part of the test. (#33604) We're actually hitting random failures where the apps get started, but over 15 minutes pass before the test tries to commission them, which causes them to close their commissioning windows, which makes the test fail. The fix is to just start the apps right when we're about to need them. --- .github/workflows/darwin.yaml | 13 +++---- .../CHIPTests/MTRCommissionableBrowserTests.m | 9 ++--- .../CHIPTests/MTRPerControllerStorageTests.m | 34 +++++++++++++------ .../TestHelpers/MTRTestServerAppRunner.m | 2 +- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/.github/workflows/darwin.yaml b/.github/workflows/darwin.yaml index ad2a513209cc65..ba9c0f1d9f02c3 100644 --- a/.github/workflows/darwin.yaml +++ b/.github/workflows/darwin.yaml @@ -97,10 +97,10 @@ jobs: bootstrap-logs-framework-${{ matrix.options.flavor }} - name: Build example All Clusters Server run: | - scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug chip_config_network_layer_ble=false + scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug/all-clusters-app chip_config_network_layer_ble=false - name: Build example OTA Provider run: | - scripts/examples/gn_build_example.sh examples/ota-provider-app/linux out/debug chip_config_network_layer_ble=false + scripts/examples/gn_build_example.sh examples/ota-provider-app/linux out/debug/ota-provider-app chip_config_network_layer_ble=false - name: Build example OTA Requestor run: | scripts/examples/gn_build_example.sh examples/ota-requestor-app/linux out/debug/ota-requestor-app chip_config_network_layer_ble=false non_spec_compliant_ota_action_delay_floor=0 @@ -113,13 +113,8 @@ jobs: run: | mkdir -p /tmp/darwin/framework-tests echo "This is a simple log" > /tmp/darwin/framework-tests/end_user_support_log.txt - ../../../out/debug/chip-all-clusters-app --interface-id -1 --end_user_support_log /tmp/darwin/framework-tests/end_user_support_log.txt > >(tee /tmp/darwin/framework-tests/all-cluster-app.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-err.log >&2) & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --dac_provider ../../../credentials/development/commissioner_dut/struct_cd_origin_pid_vid_correct/test_case_vector.json --product-id 32768 --discriminator 3839 --secured-device-port 5539 --KVS /tmp/chip-all-clusters-app-kvs2 > >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid-err.log >&2) & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 101 --passcode 1001 --KVS /tmp/chip-all-clusters-app-kvs101 --secured-device-port 5531 & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 102 --passcode 1002 --KVS /tmp/chip-all-clusters-app-kvs102 --secured-device-port 5532 & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 103 --passcode 1003 --KVS /tmp/chip-all-clusters-app-kvs103 --secured-device-port 5533 & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 104 --passcode 1004 --KVS /tmp/chip-all-clusters-app-kvs104 --secured-device-port 5534 & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 105 --passcode 1005 --KVS /tmp/chip-all-clusters-app-kvs105 --secured-device-port 5535 & + ../../../out/debug/all-clusters-app/chip-all-clusters-app --interface-id -1 --end_user_support_log /tmp/darwin/framework-tests/end_user_support_log.txt > >(tee /tmp/darwin/framework-tests/all-cluster-app.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-err.log >&2) & + ../../../out/debug/all-clusters-app/chip-all-clusters-app --interface-id -1 --dac_provider ../../../credentials/development/commissioner_dut/struct_cd_origin_pid_vid_correct/test_case_vector.json --product-id 32768 --discriminator 3839 --secured-device-port 5539 --KVS /tmp/chip-all-clusters-app-kvs2 > >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid-err.log >&2) & export TEST_RUNNER_ASAN_OPTIONS=__CURRENT_VALUE__:detect_stack_use_after_return=1 diff --git a/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m b/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m index 4253625af80ff7..047ce4f8588816 100644 --- a/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m +++ b/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m @@ -34,13 +34,8 @@ static const uint16_t kTestProductId2 = 0x8001u; static const uint16_t kTestDiscriminator1 = 3840u; static const uint16_t kTestDiscriminator2 = 3839u; -static const uint16_t kTestDiscriminator3 = 101u; -static const uint16_t kTestDiscriminator4 = 102u; -static const uint16_t kTestDiscriminator5 = 103u; -static const uint16_t kTestDiscriminator6 = 104u; -static const uint16_t kTestDiscriminator7 = 105u; static const uint16_t kDiscoverDeviceTimeoutInSeconds = 10; -static const uint16_t kExpectedDiscoveredDevicesCount = 7; +static const uint16_t kExpectedDiscoveredDevicesCount = 2; // Singleton controller we use. static MTRDeviceController * sController = nil; @@ -102,7 +97,7 @@ - (void)controller:(MTRDeviceController *)controller didFindCommissionableDevice XCTAssertEqual(instanceName.length, 16); // The instance name is random, so just ensure the len is right. XCTAssertEqualObjects(vendorId, @(kTestVendorId)); XCTAssertTrue([productId isEqual:@(kTestProductId1)] || [productId isEqual:@(kTestProductId2)]); - XCTAssertTrue([discriminator isEqual:@(kTestDiscriminator1)] || [discriminator isEqual:@(kTestDiscriminator2)] || [discriminator isEqual:@(kTestDiscriminator3)] || [discriminator isEqual:@(kTestDiscriminator4)] || [discriminator isEqual:@(kTestDiscriminator5)] || [discriminator isEqual:@(kTestDiscriminator6)] || [discriminator isEqual:@(kTestDiscriminator7)]); + XCTAssertTrue([discriminator isEqual:@(kTestDiscriminator1)] || [discriminator isEqual:@(kTestDiscriminator2)]); XCTAssertEqual(commissioningMode, YES); NSLog(@"Found Device (%@) with discriminator: %@ (vendor: %@, product: %@)", instanceName, discriminator, vendorId, productId); diff --git a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m index c5e39276323891..601cbcc9125536 100644 --- a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m @@ -29,6 +29,7 @@ #import "MTRTestKeys.h" #import "MTRTestPerControllerStorage.h" #import "MTRTestResetCommissioneeHelper.h" +#import "MTRTestServerAppRunner.h" static const uint16_t kPairingTimeoutInSeconds = 10; static const uint16_t kTimeoutInSeconds = 3; @@ -2147,7 +2148,7 @@ - (void)testSetMRPParametersWithRunningController } // TODO: This might also want to go in a separate test file, with some shared setup for commissioning devices per test -- (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize +- (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize deviceOnboardingPayloads:(NSDictionary *)deviceOnboardingPayloads { __auto_type * factory = [MTRDeviceControllerFactory sharedInstance]; XCTAssertNotNil(factory); @@ -2183,15 +2184,7 @@ - (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize XCTAssertEqualObjects(controller.controllerNodeID, nodeID); - // QRCodes generated for discriminators 101~105 and passcodes 1001~1005 NSArray * orderedDeviceIDs = @[ @(101), @(102), @(103), @(104), @(105) ]; - NSDictionary * deviceOnboardingPayloads = @{ - @(101) : @"MT:00000EBQ15IZC900000", - @(102) : @"MT:00000MNY16-AD900000", - @(103) : @"MT:00000UZ427GOD900000", - @(104) : @"MT:00000CQM00Z.D900000", - @(105) : @"MT:00000K0V01FDE900000", - }; // Commission 5 devices for (NSNumber * deviceID in orderedDeviceIDs) { @@ -2272,8 +2265,27 @@ - (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize - (void)testSubscriptionPool { - [self doTestSubscriptionPoolWithSize:1]; - [self doTestSubscriptionPoolWithSize:2]; + // QRCodes generated for discriminators 1111~1115 and passcodes 1001~1005 + NSDictionary * deviceOnboardingPayloads = @{ + @(101) : @"MT:00000UZ427U0D900000", + @(102) : @"MT:00000CQM00BED900000", + @(103) : @"MT:00000K0V01TRD900000", + @(104) : @"MT:00000SC11293E900000", + @(105) : @"MT:00000-O913RGE900000", + }; + + // Start our helper apps. + __auto_type * sortedKeys = [[deviceOnboardingPayloads allKeys] sortedArrayUsingSelector:@selector(compare:)]; + for (NSNumber * deviceID in sortedKeys) { + __auto_type * appRunner = [[MTRTestServerAppRunner alloc] initWithAppName:@"all-clusters" + arguments:@[] + payload:deviceOnboardingPayloads[deviceID] + testcase:self]; + XCTAssertNotNil(appRunner); + } + + [self doTestSubscriptionPoolWithSize:1 deviceOnboardingPayloads:deviceOnboardingPayloads]; + [self doTestSubscriptionPoolWithSize:2 deviceOnboardingPayloads:deviceOnboardingPayloads]; } @end diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m index 83912e9eb2dc0b..18424ed332a6af 100644 --- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m +++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m @@ -90,7 +90,7 @@ - (instancetype)initWithAppName:(NSString *)name arguments:(NSArray [testcase launchTask:_appTask]; - NSLog(@"Started %@ with arguments %@ stdout=%@ and stderr=%@", name, allArguments, outFile, errorFile); + NSLog(@"Started chip-%@-app with arguments %@ stdout=%@ and stderr=%@", name, allArguments, outFile, errorFile); return self; #endif // HAVE_NSTASK From af25f563b285d11ebf7899f9e7a4f9b514b887d3 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Tue, 28 May 2024 14:09:20 -0400 Subject: [PATCH 6/7] Decouple `CommandHandler.h/cpp` from the BUILD.gn build rules of app/interaction-model (#33595) * Initial version of splitting out command handler dependencies * Restyle * move privilege-storage based on dynamic server build rules...this is somewhat messy * More dynamic server fixes * Another gn fix for android/dynamic-server builds --------- Co-authored-by: Andrei Litvin --- .github/workflows/lint.yml | 1 - src/app/BUILD.gn | 65 +++++++++++++++++++++++++++++++++----- src/app/CommandHandler.cpp | 14 ++------ src/app/CommandHandler.h | 3 +- 4 files changed, 61 insertions(+), 22 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4685948fd7bd12..bd5385580b9c22 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -98,7 +98,6 @@ jobs: --known-failure controller/ExamplePersistentStorage.cpp \ --known-failure controller/ExamplePersistentStorage.h \ --known-failure app/AttributeAccessToken.h \ - --known-failure app/CommandHandler.h \ --known-failure app/CommandHandlerInterface.h \ --known-failure app/CommandResponseSender.h \ --known-failure app/CommandSenderLegacyCallback.h \ diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 4bb4a05a555d39..f49a40f398842a 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -179,9 +179,6 @@ static_library("interaction-model") { "ReadClient.h", # TODO: cpp is only included conditionally. Needs logic # fixing "ReadPrepareParams.h", - "RequiredPrivilege.h", - "StatusResponse.cpp", - "StatusResponse.h", "SubscriptionResumptionStorage.h", "TimedHandler.cpp", "TimedHandler.h", @@ -210,6 +207,7 @@ static_library("interaction-model") { public_deps = [ ":app_config", + ":command-handler", ":constants", ":paths", ":subscription-info-provider", @@ -248,8 +246,6 @@ static_library("interaction-model") { "dynamic_server/AccessControl.cpp", "dynamic_server/AccessControl.h", "dynamic_server/DynamicDispatcher.cpp", - "util/privilege-storage.cpp", - "util/privilege-storage.h", ] public_deps += [ @@ -301,6 +297,62 @@ static_library("attribute-access") { ] } +source_set("required-privileges") { + sources = [ "RequiredPrivilege.h" ] + + public_deps = [ + ":paths", + "${chip_root}/src/access:types", + ] + + if (chip_build_controller_dynamic_server) { + sources += [ + "util/privilege-storage.cpp", + "util/privilege-storage.h", + ] + + public_deps += [ + ":global-attributes", + "${chip_root}/src/access", + "${chip_root}/src/app/dynamic_server:mock-codegen-includes", + ] + + public_configs = [ ":config-controller-dynamic-server" ] + } +} + +source_set("status-response") { + sources = [ + "StatusResponse.cpp", + "StatusResponse.h", + ] + public_deps = [ + ":constants", + "${chip_root}/src/app/MessageDef", + "${chip_root}/src/messaging", + ] +} + +source_set("command-handler") { + sources = [ + "CommandHandler.cpp", + "CommandHandler.h", + "CommandHandlerExchangeInterface.h", + ] + + public_deps = [ + ":paths", + ":required-privileges", + ":status-response", + "${chip_root}/src/access:types", + "${chip_root}/src/app/MessageDef", + "${chip_root}/src/app/data-model", + "${chip_root}/src/app/util:callbacks", + "${chip_root}/src/lib/support", + "${chip_root}/src/messaging", + ] +} + # Note to developpers, instead of continuously adding files in the app librabry, it is recommand to create smaller source_sets that app can depend on. # This way, we can have a better understanding of dependencies and other componenets can depend on the different source_sets without needing to depend on the entire app library. static_library("app") { @@ -312,8 +364,6 @@ static_library("app") { "AttributePersistenceProvider.h", "ChunkedWriteCallback.cpp", "ChunkedWriteCallback.h", - "CommandHandler.cpp", - "CommandHandlerExchangeInterface.h", "CommandResponseHelper.h", "CommandResponseSender.cpp", "DefaultAttributePersistenceProvider.cpp", @@ -338,7 +388,6 @@ static_library("app") { # (app depending on im and im including these headers): # Name with _ so that linter does not recognize it # "CommandResponseSender._h" - # "CommandHandler._h" # "ReadHandler._h", # "WriteHandler._h" ] diff --git a/src/app/CommandHandler.cpp b/src/app/CommandHandler.cpp index 86ef22ab886212..6077e0934721f7 100644 --- a/src/app/CommandHandler.cpp +++ b/src/app/CommandHandler.cpp @@ -15,21 +15,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/** - * @file - * This file defines object for a CHIP IM Invoke Command Handler - * - */ - -#include "CommandHandler.h" -#include "InteractionModelEngine.h" -#include "RequiredPrivilege.h" -#include "messaging/ExchangeContext.h" +#include #include #include #include +#include #include #include #include @@ -37,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/src/app/CommandHandler.h b/src/app/CommandHandler.h index 96b7af873554ed..b06064c46a69be 100644 --- a/src/app/CommandHandler.h +++ b/src/app/CommandHandler.h @@ -30,9 +30,8 @@ #pragma once -#include "CommandPathRegistry.h" - #include +#include #include #include #include From 22a9dc3a9c0628f19e0edc0cafc89ddcecd82b18 Mon Sep 17 00:00:00 2001 From: Shubham Patil Date: Tue, 28 May 2024 23:53:08 +0530 Subject: [PATCH 7/7] setup_payload: Add support for parsing setup payloads in python impl (#32516) * setup_payload: Add support for parsing setup payloads in python impl - Base38 decode impl in python - use construct to generate/parse setup payload in python - Add cli to parse and generate using click - unit tests for parsing and verification using chip-tool - removed the older script which only generated the codes - replaced the usage of older utility with newer one * Restyled by isort * fix the requirements * Added some test dataset * always use latest bitarray --------- Co-authored-by: Restyled.io --- .github/workflows/build.yaml | 2 +- scripts/tools/bouffalolab/factory_qrcode.py | 4 +- .../tools/generate_esp32_chip_factory_bin.py | 2 +- .../generate_nrfconnect_chip_factory_data.py | 4 +- src/setup_payload/python/Base38.py | 23 ++ src/setup_payload/python/README.md | 17 +- src/setup_payload/python/SetupPayload.py | 220 ++++++++++++++++++ .../python/generate_setup_payload.py | 170 -------------- src/setup_payload/python/requirements.txt | 4 +- .../run_python_setup_payload_gen_test.py | 136 ----------- .../tests/run_python_setup_payload_test.py | 213 +++++++++++++++++ 11 files changed, 475 insertions(+), 320 deletions(-) create mode 100755 src/setup_payload/python/SetupPayload.py delete mode 100755 src/setup_payload/python/generate_setup_payload.py delete mode 100644 src/setup_payload/tests/run_python_setup_payload_gen_test.py create mode 100644 src/setup_payload/tests/run_python_setup_payload_test.py diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 03e3c76c625b7d..88588363d03ad4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -333,7 +333,7 @@ jobs: scripts/run_in_build_env.sh 'virtualenv pyenv' source pyenv/bin/activate pip3 install -r src/setup_payload/python/requirements.txt - python3 src/setup_payload/tests/run_python_setup_payload_gen_test.py out/chip-tool + python3 src/setup_payload/tests/run_python_setup_payload_test.py out/chip-tool build_linux_python_lighting_device: name: Build on Linux (python lighting-app) diff --git a/scripts/tools/bouffalolab/factory_qrcode.py b/scripts/tools/bouffalolab/factory_qrcode.py index 1a7f6303495775..0e12d8349d4c91 100644 --- a/scripts/tools/bouffalolab/factory_qrcode.py +++ b/scripts/tools/bouffalolab/factory_qrcode.py @@ -20,13 +20,13 @@ try: import qrcode - from generate_setup_payload import CommissioningFlow, SetupPayload + from SetupPayload import CommissioningFlow, SetupPayload except ImportError: SDK_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) sys.path.append(os.path.join(SDK_ROOT, "src/setup_payload/python")) try: import qrcode - from generate_setup_payload import CommissioningFlow, SetupPayload + from SetupPayload import CommissioningFlow, SetupPayload except ModuleNotFoundError or ImportError: no_onboarding_modules = True else: diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py index 262be540a9bfed..eab2ec6b43c229 100755 --- a/scripts/tools/generate_esp32_chip_factory_bin.py +++ b/scripts/tools/generate_esp32_chip_factory_bin.py @@ -30,7 +30,7 @@ sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'scripts', 'tools', 'spake2p')) from spake2p import generate_verifier # noqa: E402 isort:skip sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'src', 'setup_payload', 'python')) -from generate_setup_payload import CommissioningFlow, SetupPayload # noqa: E402 isort:skip +from SetupPayload import CommissioningFlow, SetupPayload # noqa: E402 isort:skip if os.getenv('IDF_PATH'): sys.path.insert(0, os.path.join(os.getenv('IDF_PATH'), diff --git a/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py b/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py index 78212cefd81e6e..ab70c69d7f9d53 100644 --- a/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py +++ b/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py @@ -32,13 +32,13 @@ try: import qrcode - from generate_setup_payload import CommissioningFlow, SetupPayload + from SetupPayload import CommissioningFlow, SetupPayload except ImportError: SDK_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) sys.path.append(os.path.join(SDK_ROOT, "src/setup_payload/python")) try: import qrcode - from generate_setup_payload import CommissioningFlow, SetupPayload + from SetupPayload import CommissioningFlow, SetupPayload except ModuleNotFoundError or ImportError: no_onboarding_modules = True else: diff --git a/src/setup_payload/python/Base38.py b/src/setup_payload/python/Base38.py index 23113f7ed25c8d..834ae9e1d520e7 100644 --- a/src/setup_payload/python/Base38.py +++ b/src/setup_payload/python/Base38.py @@ -24,6 +24,7 @@ RADIX = len(CODES) BASE38_CHARS_NEEDED_IN_CHUNK = [2, 4, 5] MAX_BYTES_IN_CHUNK = 3 +MAX_ENCODED_BYTES_IN_CHUNK = 5 def encode(bytes): @@ -47,3 +48,25 @@ def encode(bytes): base38_chars_needed -= 1 return qrcode + + +def decode(qrcode): + total_chars = len(qrcode) + decoded_bytes = bytearray() + + for i in range(0, total_chars, MAX_ENCODED_BYTES_IN_CHUNK): + if (i + MAX_ENCODED_BYTES_IN_CHUNK) > total_chars: + chars_in_chunk = total_chars - i + else: + chars_in_chunk = MAX_ENCODED_BYTES_IN_CHUNK + + value = 0 + for j in range(i + chars_in_chunk - 1, i - 1, -1): + value = value * RADIX + CODES.index(qrcode[j]) + + bytes_in_chunk = BASE38_CHARS_NEEDED_IN_CHUNK.index(chars_in_chunk) + 1 + for k in range(0, bytes_in_chunk): + decoded_bytes.append(value & 0xFF) + value = value >> 8 + + return decoded_bytes diff --git a/src/setup_payload/python/README.md b/src/setup_payload/python/README.md index 068bf553fb7e6b..a39496104300d1 100644 --- a/src/setup_payload/python/README.md +++ b/src/setup_payload/python/README.md @@ -1,19 +1,22 @@ -## Python tool to generate Matter onboarding codes +## Python tool to generate and parse Matter onboarding codes -Generates Manual Pairing Code and QR Code +Generates and parses Manual Pairing Code and QR Code #### example usage: +- Parse + ``` -./generate_setup_payload.py -h -./generate_setup_payload.py -d 3840 -p 20202021 -cf 0 -dm 2 -vid 65521 -pid 32768 +./SetupPayload.py parse MT:U9VJ0OMV172PX813210 +./SetupPayload.py parse 34970112332 ``` -- Output +- Generate ``` -Manualcode : 34970112332 -QRCode : MT:Y.K9042C00KA0648G00 +./SetupPayload.py generate --help +./SetupPayload.py generate -d 3840 -p 20202021 +./SetupPayload.py generate -d 3840 -p 20202021 --vendor-id 65521 --product-id 32768 -cf 0 -dm 2 ``` For more details please refer Matter Specification diff --git a/src/setup_payload/python/SetupPayload.py b/src/setup_payload/python/SetupPayload.py new file mode 100755 index 00000000000000..82a58d957eff08 --- /dev/null +++ b/src/setup_payload/python/SetupPayload.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024 Project CHIP Authors +# 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. + +import enum + +import Base38 +import click +from bitarray import bitarray +from bitarray.util import int2ba, zeros +from construct import BitsInteger, BitStruct, Enum +from stdnum.verhoeff import calc_check_digit + +# Format for constructing manualcode +manualcode_format = BitStruct( + 'version' / BitsInteger(1), + 'vid_pid_present' / BitsInteger(1), + 'discriminator' / BitsInteger(4), + 'pincode_lsb' / BitsInteger(14), + 'pincode_msb' / BitsInteger(13), + 'vid' / BitsInteger(16), + 'pid' / BitsInteger(16), + 'padding' / BitsInteger(7), # this is intentional as BitStruct only takes 8-bit aligned data +) + +# Format for constructing qrcode +# qrcode bytes are packed as lsb....msb, hence the order is reversed +qrcode_format = BitStruct( + 'padding' / BitsInteger(4), + 'pincode' / BitsInteger(27), + 'discriminator' / BitsInteger(12), + 'discovery' / BitsInteger(8), + 'flow' / Enum(BitsInteger(2), + Standard=0, UserIntent=1, Custom=2), + 'pid' / BitsInteger(16), + 'vid' / BitsInteger(16), + 'version' / BitsInteger(3), +) + + +class CommissioningFlow(enum.IntEnum): + Standard = 0, + UserIntent = 1, + Custom = 2 + + +class SetupPayload: + def __init__(self, discriminator, pincode, rendezvous=4, flow=CommissioningFlow.Standard, vid=0, pid=0): + self.long_discriminator = discriminator + self.short_discriminator = discriminator >> 8 + self.pincode = pincode + self.discovery = rendezvous + self.flow = flow + self.vid = vid + self.pid = pid + + def p_print(self): + print('{:<{}} :{}'.format('Flow', 24, self.flow)) + print('{:<{}} :{}'.format('Pincode', 24, self.pincode)) + print('{:<{}} :{}'.format('Short Discriminator', 24, self.short_discriminator)) + if self.long_discriminator: + print('{:<{}} :{}'.format('Long Discriminator', 24, self.long_discriminator)) + if self.discovery: + print('{:<{}} :{}'.format('Discovery Capabilities', 24, self.discovery)) + if self.vid is not None and self.pid is not None: + print('{:<{}} :{:<{}} (0x{:04x})'.format('Vendor Id', 24, self.vid, 6, self.vid)) + print('{:<{}} :{:<{}} (0x{:04x})'.format('Product Id', 24, self.pid, 6, self.pid)) + + def qrcode_dict(self): + return { + 'version': 0, + 'vid': self.vid, + 'pid': self.pid, + 'flow': int(self.flow), + 'discovery': self.discovery, + 'discriminator': self.long_discriminator, + 'pincode': self.pincode, + 'padding': 0, + } + + def manualcode_dict(self): + return { + 'version': 0, + 'vid_pid_present': 0 if self.flow == CommissioningFlow.Standard else 1, + 'discriminator': self.short_discriminator, + 'pincode_lsb': self.pincode & 0x3FFF, # 14 ls-bits + 'pincode_msb': self.pincode >> 14, # 13 ms-bits + 'vid': 0 if self.flow == CommissioningFlow.Standard else self.vid, + 'pid': 0 if self.flow == CommissioningFlow.Standard else self.pid, + 'padding': 0, + } + + def generate_qrcode(self): + data = qrcode_format.build(self.qrcode_dict()) + b38_encoded = Base38.encode(data[::-1]) # reversing + return 'MT:{}'.format(b38_encoded) + + def generate_manualcode(self): + CHUNK1_START = 0 + CHUNK1_LEN = 4 + CHUNK2_START = CHUNK1_START + CHUNK1_LEN + CHUNK2_LEN = 16 + CHUNK3_START = CHUNK2_START + CHUNK2_LEN + CHUNK3_LEN = 13 + + bytes = manualcode_format.build(self.manualcode_dict()) + bits = bitarray() + bits.frombytes(bytes) + + chunk1 = str(int(bits[CHUNK1_START:CHUNK1_START + CHUNK1_LEN].to01(), 2)).zfill(1) + chunk2 = str(int(bits[CHUNK2_START:CHUNK2_START + CHUNK2_LEN].to01(), 2)).zfill(5) + chunk3 = str(int(bits[CHUNK3_START:CHUNK3_START + CHUNK3_LEN].to01(), 2)).zfill(4) + chunk4 = str(self.vid).zfill(5) if self.flow != CommissioningFlow.Standard else '' + chunk5 = str(self.pid).zfill(5) if self.flow != CommissioningFlow.Standard else '' + payload = '{}{}{}{}{}'.format(chunk1, chunk2, chunk3, chunk4, chunk5) + return '{}{}'.format(payload, calc_check_digit(payload)) + + @staticmethod + def from_container(container, is_qrcode): + payload = None + if is_qrcode: + payload = SetupPayload(container['discriminator'], container['pincode'], + container['discovery'], CommissioningFlow(container['flow'].__int__()), + container['vid'], container['pid']) + else: + payload = SetupPayload(discriminator=container['discriminator'], + pincode=(container['pincode_msb'] << 14) | container['pincode_lsb'], + vid=container['vid'] if container['vid_pid_present'] else None, + pid=container['pid'] if container['vid_pid_present'] else None) + payload.short_discriminator = container['discriminator'] + payload.long_discriminator = None + payload.discovery = None + payload.flow = 2 if container['vid_pid_present'] else 0 + + return payload + + @staticmethod + def parse_qrcode(payload): + payload = payload[3:] # remove 'MT:' + b38_decoded = Base38.decode(payload)[::-1] + container = qrcode_format.parse(b38_decoded) + return SetupPayload.from_container(container, is_qrcode=True) + + @staticmethod + def parse_manualcode(payload): + payload_len = len(payload) + if payload_len != 11 and payload_len != 21: + print('Invalid length') + return None + + # if first digit is greater than 7 the its not v1 + if int(str(payload)[0]) > 7: + print('incorrect first digit') + return None + + if calc_check_digit(payload[:-1]) != str(payload)[-1]: + print('check digit mismatch') + return None + + # vid_pid_present bit position + is_long = int(str(payload)[0]) & (1 << 2) + + bits = int2ba(int(payload[0]), length=4) + bits += int2ba(int(payload[1:6]), length=16) + bits += int2ba(int(payload[6:10]), length=13) + bits += int2ba(int(payload[10:15]), length=16) if is_long else zeros(16) + bits += int2ba(int(payload[15:20]), length=16) if is_long else zeros(16) + bits += zeros(7) # padding + + container = manualcode_format.parse(bits.tobytes()) + return SetupPayload.from_container(container, is_qrcode=False) + + @staticmethod + def parse(payload): + if payload.startswith('MT:'): + return SetupPayload.parse_qrcode(payload) + else: + return SetupPayload.parse_manualcode(payload) + + +@click.group() +def cli(): + pass + + +@cli.command() +@click.argument('payload') +def parse(payload): + click.echo(f'Parsing payload: {payload}') + SetupPayload.parse(payload).p_print() + + +@cli.command() +@click.option('--discriminator', '-d', required=True, type=click.IntRange(0, 0xFFF), help='Discriminator') +@click.option('--passcode', '-p', required=True, type=click.IntRange(1, 0x5F5E0FE), help='setup pincode') +@click.option('--vendor-id', '-vid', type=click.IntRange(0, 0xFFFF), default=0, help='Vendor ID') +@click.option('--product-id', '-pid', type=click.IntRange(0, 0xFFFF), default=0, help='Product ID') +@click.option('--discovery-cap-bitmask', '-dm', type=click.IntRange(0, 7), default=4, help='Commissionable device discovery capability bitmask. 0:SoftAP, 1:BLE, 2:OnNetwork. Default: OnNetwork') +@click.option('--commissioning-flow', '-cf', type=click.IntRange(0, 2), default=0, help='Commissioning flow, 0:Standard, 1:User-Intent, 2:Custom') +def generate(passcode, discriminator, vendor_id, product_id, discovery_cap_bitmask, commissioning_flow): + payload = SetupPayload(discriminator, passcode, discovery_cap_bitmask, commissioning_flow, vendor_id, product_id) + print("Manualcode : {}".format(payload.generate_manualcode())) + print("QRCode : {}".format(payload.generate_qrcode())) + + +if __name__ == '__main__': + cli() diff --git a/src/setup_payload/python/generate_setup_payload.py b/src/setup_payload/python/generate_setup_payload.py deleted file mode 100755 index 28a834651214b1..00000000000000 --- a/src/setup_payload/python/generate_setup_payload.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2022 Project CHIP Authors -# 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. -# -import argparse -import enum -import sys - -import Base38 -from bitarray import bitarray -from stdnum.verhoeff import calc_check_digit - -# See section 5.1.4.1 Manual Pairing Code in the Matter specification v1.0 -MANUAL_DISCRIMINATOR_LEN = 4 -PINCODE_LEN = 27 - -MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_LEN = 2 -MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_POS = 0 -MANUAL_CHUNK1_VID_PID_PRESENT_BIT_POS = MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_POS + MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_LEN -MANUAL_CHUNK1_LEN = 1 - -MANUAL_CHUNK2_DISCRIMINATOR_LSBITS_LEN = 2 -MANUAL_CHUNK2_PINCODE_LSBITS_LEN = 14 -MANUAL_CHUNK2_PINCODE_LSBITS_POS = 0 -MANUAL_CHUNK2_DISCRIMINATOR_LSBITS_POS = MANUAL_CHUNK2_PINCODE_LSBITS_POS + MANUAL_CHUNK2_PINCODE_LSBITS_LEN -MANUAL_CHUNK2_LEN = 5 - -MANUAL_CHUNK3_PINCODE_MSBITS_LEN = 13 -MANUAL_CHUNK3_PINCODE_MSBITS_POS = 0 -MANUAL_CHUNK3_LEN = 4 - -MANUAL_VID_LEN = 5 -MANUAL_PID_LEN = 5 - -# See section 5.1.3. QR Code in the Matter specification v1.0 -QRCODE_VERSION_LEN = 3 -QRCODE_DISCRIMINATOR_LEN = 12 -QRCODE_VID_LEN = 16 -QRCODE_PID_LEN = 16 -QRCODE_COMMISSIONING_FLOW_LEN = 2 -QRCODE_DISCOVERY_CAP_BITMASK_LEN = 8 -QRCODE_PADDING_LEN = 4 -QRCODE_VERSION = 0 -QRCODE_PADDING = 0 - -INVALID_PASSCODES = [00000000, 11111111, 22222222, 33333333, 44444444, 55555555, - 66666666, 77777777, 88888888, 99999999, 12345678, 87654321] - - -class CommissioningFlow(enum.IntEnum): - Standard = 0, - UserIntent = 1, - Custom = 2 - - -class SetupPayload: - def __init__(self, discriminator, pincode, rendezvous=4, flow=CommissioningFlow.Standard, vid=0, pid=0): - self.long_discriminator = discriminator - self.short_discriminator = discriminator >> 8 - self.pincode = pincode - self.rendezvous = rendezvous - self.flow = flow - self.vid = vid - self.pid = pid - - def manual_chunk1(self): - discriminator_shift = (MANUAL_DISCRIMINATOR_LEN - MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_LEN) - discriminator_mask = (1 << MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_LEN) - 1 - discriminator_chunk = (self.short_discriminator >> discriminator_shift) & discriminator_mask - vid_pid_present_flag = 0 if self.flow == CommissioningFlow.Standard else 1 - return (discriminator_chunk << MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_POS) | (vid_pid_present_flag << MANUAL_CHUNK1_VID_PID_PRESENT_BIT_POS) - - def manual_chunk2(self): - discriminator_mask = (1 << MANUAL_CHUNK2_DISCRIMINATOR_LSBITS_LEN) - 1 - pincode_mask = (1 << MANUAL_CHUNK2_PINCODE_LSBITS_LEN) - 1 - discriminator_chunk = self.short_discriminator & discriminator_mask - return ((self.pincode & pincode_mask) << MANUAL_CHUNK2_PINCODE_LSBITS_POS) | (discriminator_chunk << MANUAL_CHUNK2_DISCRIMINATOR_LSBITS_POS) - - def manual_chunk3(self): - pincode_shift = PINCODE_LEN - MANUAL_CHUNK3_PINCODE_MSBITS_LEN - pincode_mask = (1 << MANUAL_CHUNK3_PINCODE_MSBITS_LEN) - 1 - return ((self.pincode >> pincode_shift) & pincode_mask) << MANUAL_CHUNK3_PINCODE_MSBITS_POS - - def generate_manualcode(self): - payload = str(self.manual_chunk1()).zfill(MANUAL_CHUNK1_LEN) - payload += str(self.manual_chunk2()).zfill(MANUAL_CHUNK2_LEN) - payload += str(self.manual_chunk3()).zfill(MANUAL_CHUNK3_LEN) - - if self.flow != CommissioningFlow.Standard: - payload += str(self.vid).zfill(MANUAL_VID_LEN) - payload += str(self.pid).zfill(MANUAL_PID_LEN) - - payload += calc_check_digit(payload) - return payload - - def generate_qrcode(self): - qrcode_bit_string = '{0:b}'.format(QRCODE_PADDING).zfill(QRCODE_PADDING_LEN) - qrcode_bit_string += '{0:b}'.format(self.pincode).zfill(PINCODE_LEN) - qrcode_bit_string += '{0:b}'.format(self.long_discriminator).zfill(QRCODE_DISCRIMINATOR_LEN) - qrcode_bit_string += '{0:b}'.format(self.rendezvous).zfill(QRCODE_DISCOVERY_CAP_BITMASK_LEN) - qrcode_bit_string += '{0:b}'.format(int(self.flow)).zfill(QRCODE_COMMISSIONING_FLOW_LEN) - qrcode_bit_string += '{0:b}'.format(self.pid).zfill(QRCODE_PID_LEN) - qrcode_bit_string += '{0:b}'.format(self.vid).zfill(QRCODE_VID_LEN) - qrcode_bit_string += '{0:b}'.format(QRCODE_VERSION).zfill(QRCODE_VERSION_LEN) - - qrcode_bits = bitarray(qrcode_bit_string) - bytes = list(qrcode_bits.tobytes()) - bytes.reverse() - return 'MT:{}'.format(Base38.encode(bytes)) - - -def validate_args(args): - def check_int_range(value, min_value, max_value, name): - if value and ((value < min_value) or (value > max_value)): - print('{} is out of range, should be in range from {} to {}'.format(name, min_value, max_value)) - sys.exit(1) - - if args.passcode is not None: - if ((args.passcode < 0x0000001 and args.passcode > 0x5F5E0FE) or (args.passcode in INVALID_PASSCODES)): - print('Invalid passcode:' + str(args.passcode)) - sys.exit(1) - - check_int_range(args.discriminator, 0x0000, 0x0FFF, 'Discriminator') - check_int_range(args.product_id, 0x0000, 0xFFFF, 'Product id') - check_int_range(args.vendor_id, 0x0000, 0xFFFF, 'Vendor id') - check_int_range(args.discovery_cap_bitmask, 0x0001, 0x0007, 'Discovery Capability Mask') - - -def main(): - def any_base_int(s): return int(s, 0) - parser = argparse.ArgumentParser(description='Matter Manual and QRCode Setup Payload Generator Tool') - parser.add_argument('-d', '--discriminator', type=any_base_int, required=True, - help='The discriminator for pairing, range: 0x00-0x0FFF') - parser.add_argument('-p', '--passcode', type=any_base_int, required=True, - help='The setup passcode for pairing, range: 0x01-0x5F5E0FE') - parser.add_argument('-vid', '--vendor-id', type=any_base_int, default=0, help='Vendor id') - parser.add_argument('-pid', '--product-id', type=any_base_int, default=0, help='Product id') - parser.add_argument('-cf', '--commissioning-flow', type=any_base_int, default=0, - help='Device commissioning flow, 0:Standard, 1:User-Intent, 2:Custom. \ - Default is 0.', choices=[0, 1, 2]) - parser.add_argument('-dm', '--discovery-cap-bitmask', type=any_base_int, default=4, - help='Commissionable device discovery capability bitmask. \ - 0:SoftAP, 1:BLE, 2:OnNetwork. Default: OnNetwork') - args = parser.parse_args() - validate_args(args) - - payloads = SetupPayload(args.discriminator, args.passcode, args.discovery_cap_bitmask, - CommissioningFlow(args.commissioning_flow), args.vendor_id, args.product_id) - manualcode = payloads.generate_manualcode() - qrcode = payloads.generate_qrcode() - - print("Manualcode : {}".format(manualcode)) - print("QRCode : {}".format(qrcode)) - - -if __name__ == '__main__': - main() diff --git a/src/setup_payload/python/requirements.txt b/src/setup_payload/python/requirements.txt index 43800d601c7736..1026fa98d061c5 100644 --- a/src/setup_payload/python/requirements.txt +++ b/src/setup_payload/python/requirements.txt @@ -1,2 +1,4 @@ -bitarray==2.6.0 +bitarray>=2.8.1 +click>=8.1.3 +construct>=2.10.68 python_stdnum==1.18 diff --git a/src/setup_payload/tests/run_python_setup_payload_gen_test.py b/src/setup_payload/tests/run_python_setup_payload_gen_test.py deleted file mode 100644 index b52000c1a6fd98..00000000000000 --- a/src/setup_payload/tests/run_python_setup_payload_gen_test.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2023 Project CHIP Authors -# -# 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. - -import os -import re -import subprocess -import sys - -CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('src', 'setup_payload', 'tests'))] -sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'src', 'setup_payload', 'python')) -from generate_setup_payload import CommissioningFlow, SetupPayload # noqa: E402 - - -def payload_param_dict(): - return { - 'Version': None, - 'VendorID': None, - 'ProductID': None, - 'Custom flow': None, - 'Discovery Bitmask': None, - 'Short discriminator': None, - 'Long discriminator': None, - 'Passcode': None - } - - -def remove_escape_sequence(data): - ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') - result = ansi_escape.sub('', data) - return result - - -def parse_setup_payload(chip_tool, payload): - cmd_args = [chip_tool, 'payload', 'parse-setup-payload', payload] - data = subprocess.check_output(cmd_args).decode('utf-8') - data = remove_escape_sequence(data) - parsed_params = payload_param_dict() - for key in parsed_params: - k_st = data.find(key) - if k_st == -1: - continue - - # 1 is for ":" - k_end = k_st + len(key) + 1 - - k_nl = data.find('\n', k_end) - parsed_params[key] = data[k_end:k_nl].split()[0] - - return parsed_params - - -def generate_payloads(in_params): - payloads = SetupPayload(in_params['Long discriminator'], in_params['Passcode'], - in_params['Discovery Bitmask'], CommissioningFlow(in_params['Custom flow']), - in_params['VendorID'], in_params['ProductID']) - manualcode = payloads.generate_manualcode() - qrcode = payloads.generate_qrcode() - return manualcode, qrcode - - -def verify_payloads(in_params, manualcode_params, qrcode_params): - assert in_params['Version'] == int(manualcode_params['Version'], 0) - assert in_params['Passcode'] == int(manualcode_params['Passcode'], 0) - assert in_params['Short discriminator'] == int(manualcode_params['Short discriminator'], 0) - if in_params['Custom flow'] != 0: - assert in_params['VendorID'] == int(manualcode_params['VendorID'], 0) - assert in_params['ProductID'] == int(manualcode_params['ProductID'], 0) - - assert in_params['Version'] == int(qrcode_params['Version'], 0) - assert in_params['VendorID'] == int(qrcode_params['VendorID'], 0) - assert in_params['ProductID'] == int(qrcode_params['ProductID'], 0) - assert in_params['Custom flow'] == int(qrcode_params['Custom flow'], 0) - assert in_params['Discovery Bitmask'] == int(qrcode_params['Discovery Bitmask'], 0) - assert in_params['Passcode'] == int(qrcode_params['Passcode'], 0) - assert in_params['Long discriminator'] == int(qrcode_params['Long discriminator'], 0) - - -def get_payload_params(discriminator, passcode, discovery=4, flow=0, vid=0, pid=0, version=0): - p = payload_param_dict() - p['Version'] = version - p['VendorID'] = vid - p['ProductID'] = pid - p['Custom flow'] = flow - p['Discovery Bitmask'] = discovery - p['Long discriminator'] = discriminator - p['Short discriminator'] = discriminator >> 8 - p['Passcode'] = passcode - return p - - -def run_tests(chip_tool): - test_data_set = [ - get_payload_params(3840, 20202021), - get_payload_params(3781, 12349876, flow=1, vid=1, pid=1), - get_payload_params(2310, 23005908, flow=2, vid=0xFFF3, pid=0x8098), - get_payload_params(3091, 43338551, discovery=2, flow=2, vid=0x1123, pid=0x0012), - get_payload_params(80, 54757432, discovery=6, flow=2, vid=0x2345, pid=0x1023), - get_payload_params(174, 81235604, discovery=7, flow=1, vid=0x45, pid=0x10), - ] - - for test_params in test_data_set: - manualcode, qrcode = generate_payloads(test_params) - manualcode_params = parse_setup_payload(chip_tool, manualcode) - qrcode_params = parse_setup_payload(chip_tool, qrcode) - - print("Input parameters:", test_params) - print("Manualcode:", manualcode) - print("QRCode:", qrcode) - print("Manualcode parsed by chip-tool:", manualcode_params) - print("QRCode parsed by chip-tool:", qrcode_params) - print("") - - verify_payloads(test_params, manualcode_params, qrcode_params) - - -def main(): - if len(sys.argv) == 2: - chip_tool = sys.argv[1] - run_tests(chip_tool) - - -if __name__ == '__main__': - main() diff --git a/src/setup_payload/tests/run_python_setup_payload_test.py b/src/setup_payload/tests/run_python_setup_payload_test.py new file mode 100644 index 00000000000000..da62a45c05fe97 --- /dev/null +++ b/src/setup_payload/tests/run_python_setup_payload_test.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2023 Project CHIP Authors +# +# 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. + +import os +import re +import subprocess +import sys + +CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('src', 'setup_payload', 'tests'))] +sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'src', 'setup_payload', 'python')) +from SetupPayload import CommissioningFlow, SetupPayload # noqa: E402 + + +def payload_param_dict(): + return { + 'Version': None, + 'VendorID': None, + 'ProductID': None, + 'Custom flow': None, + 'Discovery Bitmask': None, + 'Short discriminator': None, + 'Long discriminator': None, + 'Passcode': None + } + + +def remove_escape_sequence(data): + ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + result = ansi_escape.sub('', data) + return result + + +def chip_tool_parse_setup_payload(chip_tool, payload): + cmd_args = [chip_tool, 'payload', 'parse-setup-payload', payload] + data = subprocess.check_output(cmd_args).decode('utf-8') + data = remove_escape_sequence(data) + parsed_params = payload_param_dict() + for key in parsed_params: + k_st = data.find(key) + if k_st == -1: + continue + + # 1 is for ":" + k_end = k_st + len(key) + 1 + + k_nl = data.find('\n', k_end) + parsed_params[key] = data[k_end:k_nl].split()[0] + + return parsed_params + + +def generate_payloads(in_params): + payloads = SetupPayload(in_params['Long discriminator'], in_params['Passcode'], + in_params['Discovery Bitmask'], CommissioningFlow(in_params['Custom flow']), + in_params['VendorID'], in_params['ProductID']) + manualcode = payloads.generate_manualcode() + qrcode = payloads.generate_qrcode() + return manualcode, qrcode + + +def verify_generated_payloads(in_params, manualcode_params, qrcode_params): + assert in_params['Version'] == int(manualcode_params['Version'], 0) + assert in_params['Passcode'] == int(manualcode_params['Passcode'], 0) + assert in_params['Short discriminator'] == int(manualcode_params['Short discriminator'], 0) + if in_params['Custom flow'] != 0: + assert in_params['VendorID'] == int(manualcode_params['VendorID'], 0) + assert in_params['ProductID'] == int(manualcode_params['ProductID'], 0) + + assert in_params['Version'] == int(qrcode_params['Version'], 0) + assert in_params['VendorID'] == int(qrcode_params['VendorID'], 0) + assert in_params['ProductID'] == int(qrcode_params['ProductID'], 0) + assert in_params['Custom flow'] == int(qrcode_params['Custom flow'], 0) + assert in_params['Discovery Bitmask'] == int(qrcode_params['Discovery Bitmask'], 0) + assert in_params['Passcode'] == int(qrcode_params['Passcode'], 0) + assert in_params['Long discriminator'] == int(qrcode_params['Long discriminator'], 0) + + +def get_payload_params(discriminator, passcode, discovery=4, flow=0, vid=0, pid=0, version=0, short_discriminator=None): + p = payload_param_dict() + p['Version'] = version + p['VendorID'] = vid + p['ProductID'] = pid + p['Custom flow'] = flow + p['Discovery Bitmask'] = discovery + p['Long discriminator'] = discriminator + p['Short discriminator'] = short_discriminator if short_discriminator is not None else (discriminator >> 8) + p['Passcode'] = passcode + return p + + +def test_code_generation(chip_tool): + test_data_set = [ + get_payload_params(3840, 20202021), + get_payload_params(3781, 12349876, flow=1, vid=1, pid=1), + get_payload_params(2310, 23005908, flow=2, vid=0xFFF3, pid=0x8098), + get_payload_params(3091, 43338551, discovery=2, flow=2, vid=0x1123, pid=0x0012), + get_payload_params(80, 54757432, discovery=6, flow=2, vid=0x2345, pid=0x1023), + get_payload_params(174, 81235604, discovery=7, flow=1, vid=0x45, pid=0x10), + ] + + for test_params in test_data_set: + manualcode, qrcode = generate_payloads(test_params) + manualcode_params = chip_tool_parse_setup_payload(chip_tool, manualcode) + qrcode_params = chip_tool_parse_setup_payload(chip_tool, qrcode) + + verify_generated_payloads(test_params, manualcode_params, qrcode_params) + + +def test_onboardingcode_parsing(): + # This test dataset is generated using `chip-tool payload parse-setup-payload ` + + test_data_set = [ + { + 'code': '34970112332', + 'res': get_payload_params(discriminator=None, passcode=20202021, discovery=None, + flow=0, vid=None, pid=None, version=0, short_discriminator=15), + }, + { + 'code': '745492075300001000013', + 'res': get_payload_params(discriminator=None, passcode=12349876, discovery=None, + flow=2, vid=1, pid=1, version=0, short_discriminator=14), + }, + { + 'code': '619156140465523329207', + 'res': get_payload_params(discriminator=None, passcode=23005908, discovery=None, + flow=2, vid=65523, pid=32920, version=0, short_discriminator=9), + }, + { + 'code': '702871264504387000187', + 'res': get_payload_params(discriminator=None, passcode=43338551, discovery=None, + flow=2, vid=4387, pid=18, version=0, short_discriminator=12), + }, + { + 'code': '402104334209029041311', + 'res': get_payload_params(discriminator=None, passcode=54757432, discovery=None, + flow=2, vid=9029, pid=4131, version=0, short_discriminator=0), + }, + { + 'code': '403732495800069000166', + 'res': get_payload_params(discriminator=None, passcode=81235604, discovery=None, + flow=2, vid=69, pid=16, version=0, short_discriminator=0), + }, + { + 'code': 'MT:U9VJ0OMV172PX813210', + 'res': get_payload_params(discriminator=3431, passcode=49910688, discovery=2, + flow=0, vid=4891, pid=2, version=0, short_discriminator=None), + }, + { + 'code': 'MT:00000CQM00KA0648G00', + 'res': get_payload_params(discriminator=3840, passcode=20202021, discovery=4, + flow=0, vid=0, pid=0, version=0, short_discriminator=None), + }, + { + 'code': 'MT:A3L90ARR15G6N57Y900', + 'res': get_payload_params(discriminator=3781, passcode=12349876, discovery=4, + flow=1, vid=1, pid=1, version=0, short_discriminator=None), + }, + { + 'code': 'MT:MZWA6G6026O2XP0II00', + 'res': get_payload_params(discriminator=2310, passcode=23005908, discovery=4, + flow=2, vid=65523, pid=32920, version=0, short_discriminator=None), + }, + { + 'code': 'MT:KSNK4M5113-JPR4UY00', + 'res': get_payload_params(discriminator=3091, passcode=43338551, discovery=2, + flow=2, vid=4387, pid=18, version=0, short_discriminator=12), + }, + { + 'code': 'MT:0A.T0P--00Y0OJ0.510', + 'res': get_payload_params(discriminator=80, passcode=54757432, discovery=6, + flow=2, vid=9029, pid=4131, version=0, short_discriminator=None), + }, + { + 'code': 'MT:EPX0482F26DAVY09R10', + 'res': get_payload_params(discriminator=174, passcode=81235604, discovery=7, + flow=1, vid=69, pid=16, version=0, short_discriminator=None), + }, + ] + + for test_payload in test_data_set: + payload = SetupPayload.parse(test_payload['code']) + + assert payload.long_discriminator == test_payload['res']['Long discriminator'] + assert payload.short_discriminator == test_payload['res']['Short discriminator'] + assert payload.pincode == test_payload['res']['Passcode'] + assert payload.discovery == test_payload['res']['Discovery Bitmask'] + assert payload.flow == test_payload['res']['Custom flow'] + assert payload.vid == test_payload['res']['VendorID'] + assert payload.pid == test_payload['res']['ProductID'] + + +def main(): + if len(sys.argv) == 2: + chip_tool = sys.argv[1] + test_code_generation(chip_tool) + test_onboardingcode_parsing() + + +if __name__ == '__main__': + main()