| /* |
| * |
| * Copyright (c) 2020-2022 Project CHIP Authors |
| * Copyright (c) 2019-2020 Google LLC. |
| * Copyright (c) 2013-2018 Nest Labs, Inc. |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * Implementation of the native methods expected by the Python |
| * version of Chip Device Manager. |
| * |
| */ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <memory> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/time.h> |
| #include <type_traits> |
| #include <unistd.h> |
| |
| #include <system/SystemError.h> |
| #include <system/SystemLayer.h> |
| |
| #include <inttypes.h> |
| #include <net/if.h> |
| |
| #include <app/DeviceProxy.h> |
| #include <app/InteractionModelEngine.h> |
| #include <app/server/Dnssd.h> |
| #include <controller/AutoCommissioner.h> |
| #include <controller/CHIPDeviceController.h> |
| #include <controller/CHIPDeviceControllerFactory.h> |
| #include <controller/CommissioningDelegate.h> |
| #include <controller/CommissioningWindowOpener.h> |
| #include <controller/CurrentFabricRemover.h> |
| #include <controller/ExampleOperationalCredentialsIssuer.h> |
| |
| #include <controller/python/ChipDeviceController-ScriptDevicePairingDelegate.h> |
| #include <controller/python/ChipDeviceController-ScriptPairingDeviceDiscoveryDelegate.h> |
| #include <controller/python/ChipDeviceController-StorageDelegate.h> |
| #include <controller/python/chip/interaction_model/Delegate.h> |
| #include <controller/python/chip/native/PyChipError.h> |
| |
| #include <credentials/GroupDataProviderImpl.h> |
| #include <credentials/PersistentStorageOpCertStore.h> |
| #include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h> |
| #include <credentials/attestation_verifier/DeviceAttestationVerifier.h> |
| #include <crypto/RawKeySessionKeystore.h> |
| #include <inet/IPAddress.h> |
| #include <lib/core/TLV.h> |
| #include <lib/dnssd/Resolver.h> |
| #include <lib/support/BytesToHex.h> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/DLLUtil.h> |
| #include <lib/support/ScopedBuffer.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <platform/CHIPDeviceLayer.h> |
| #include <setup_payload/QRCodeSetupPayloadParser.h> |
| #include <system/SystemClock.h> |
| |
| #include <platform/CommissionableDataProvider.h> |
| #include <platform/PlatformManager.h> |
| #include <platform/TestOnlyCommissionableDataProvider.h> |
| |
| using namespace chip; |
| using namespace chip::Ble; |
| using namespace chip::Controller; |
| using namespace chip::Credentials; |
| using namespace chip::DeviceLayer; |
| |
| extern "C" { |
| typedef void (*ConstructBytesArrayFunct)(const uint8_t * dataBuf, uint32_t dataLen); |
| typedef void (*LogMessageFunct)(uint64_t time, uint64_t timeUS, const char * moduleName, uint8_t category, const char * msg); |
| typedef void (*DeviceAvailableFunc)(DeviceProxy * device, PyChipError err); |
| typedef void (*ChipThreadTaskRunnerFunct)(intptr_t context); |
| typedef void (*DeviceUnpairingCompleteFunct)(uint64_t nodeId, PyChipError error); |
| } |
| |
| namespace { |
| chip::Platform::ScopedMemoryBuffer<uint8_t> sSsidBuf; |
| chip::Platform::ScopedMemoryBuffer<uint8_t> sCredsBuf; |
| chip::Platform::ScopedMemoryBuffer<uint8_t> sThreadBuf; |
| chip::Platform::ScopedMemoryBuffer<char> sDefaultNTPBuf; |
| app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type sDSTBuf; |
| app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type sTimeZoneBuf; |
| chip::Controller::CommissioningParameters sCommissioningParameters; |
| |
| } // namespace |
| |
| chip::Controller::ScriptDevicePairingDelegate sPairingDelegate; |
| chip::Controller::ScriptPairingDeviceDiscoveryDelegate sPairingDeviceDiscoveryDelegate; |
| chip::Credentials::GroupDataProviderImpl sGroupDataProvider; |
| chip::Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore; |
| chip::Crypto::RawKeySessionKeystore sSessionKeystore; |
| |
| // NOTE: Remote device ID is in sync with the echo server device id |
| // At some point, we may want to add an option to connect to a device without |
| // knowing its id, because the ID can be learned on the first response that is received. |
| chip::NodeId kDefaultLocalDeviceId = chip::kTestControllerNodeId; |
| chip::NodeId kRemoteDeviceId = chip::kTestDeviceNodeId; |
| |
| extern "C" { |
| PyChipError pychip_DeviceController_StackInit(Controller::Python::StorageAdapter * storageAdapter, bool enableServerInteractions); |
| PyChipError pychip_DeviceController_StackShutdown(); |
| |
| PyChipError pychip_DeviceController_NewDeviceController(chip::Controller::DeviceCommissioner ** outDevCtrl, |
| chip::NodeId localDeviceId, bool useTestCommissioner); |
| PyChipError pychip_DeviceController_DeleteDeviceController(chip::Controller::DeviceCommissioner * devCtrl); |
| PyChipError pychip_DeviceController_GetAddressAndPort(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, |
| char * outAddress, uint64_t maxAddressLen, uint16_t * outPort); |
| PyChipError pychip_DeviceController_GetCompressedFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId); |
| PyChipError pychip_DeviceController_GetFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId); |
| PyChipError pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outNodeId); |
| |
| // Rendezvous |
| PyChipError pychip_DeviceController_ConnectBLE(chip::Controller::DeviceCommissioner * devCtrl, uint16_t discriminator, |
| uint32_t setupPINCode, chip::NodeId nodeid); |
| PyChipError pychip_DeviceController_ConnectIP(chip::Controller::DeviceCommissioner * devCtrl, const char * peerAddrStr, |
| uint32_t setupPINCode, chip::NodeId nodeid); |
| PyChipError pychip_DeviceController_ConnectWithCode(chip::Controller::DeviceCommissioner * devCtrl, const char * onboardingPayload, |
| chip::NodeId nodeid); |
| PyChipError pychip_DeviceController_UnpairDevice(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId remoteDeviceId, |
| DeviceUnpairingCompleteFunct callback); |
| PyChipError pychip_DeviceController_SetThreadOperationalDataset(const char * threadOperationalDataset, uint32_t size); |
| PyChipError pychip_DeviceController_SetWiFiCredentials(const char * ssid, const char * credentials); |
| PyChipError pychip_DeviceController_SetTimeZone(int32_t offset, uint64_t validAt); |
| PyChipError pychip_DeviceController_SetDSTOffset(int32_t offset, uint64_t validStarting, uint64_t validUntil); |
| PyChipError pychip_DeviceController_SetDefaultNtp(const char * defaultNTP); |
| PyChipError pychip_DeviceController_SetTrustedTimeSource(chip::NodeId nodeId, chip::EndpointId endpoint); |
| PyChipError pychip_DeviceController_ResetCommissioningParameters(); |
| PyChipError pychip_DeviceController_CloseSession(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid); |
| PyChipError pychip_DeviceController_EstablishPASESessionIP(chip::Controller::DeviceCommissioner * devCtrl, const char * peerAddrStr, |
| uint32_t setupPINCode, chip::NodeId nodeid, uint16_t port); |
| PyChipError pychip_DeviceController_EstablishPASESessionBLE(chip::Controller::DeviceCommissioner * devCtrl, uint32_t setupPINCode, |
| uint16_t discriminator, chip::NodeId nodeid); |
| PyChipError pychip_DeviceController_Commission(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid); |
| |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator(chip::Controller::DeviceCommissioner * devCtrl, |
| uint16_t long_discriminator); |
| PyChipError pychip_DeviceController_DiscoverAllCommissionableNodes(chip::Controller::DeviceCommissioner * devCtrl); |
| |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator(chip::Controller::DeviceCommissioner * devCtrl, |
| uint16_t short_discriminator); |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesVendor(chip::Controller::DeviceCommissioner * devCtrl, |
| uint16_t vendor); |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesDeviceType(chip::Controller::DeviceCommissioner * devCtrl, |
| uint16_t device_type); |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled(chip::Controller::DeviceCommissioner * devCtrl); |
| |
| PyChipError pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, uint64_t nodeId, |
| uint32_t setupPasscode, const uint8_t filterType, const char * filterParam, |
| uint32_t discoveryTimeoutMsec); |
| |
| PyChipError pychip_DeviceController_PostTaskOnChipThread(ChipThreadTaskRunnerFunct callback, void * pythonContext); |
| |
| PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid, |
| uint16_t timeout, uint32_t iteration, uint16_t discriminator, |
| uint8_t optionInt); |
| |
| void pychip_DeviceController_PrintDiscoveredDevices(chip::Controller::DeviceCommissioner * devCtrl); |
| bool pychip_DeviceController_GetIPForDiscoveredDevice(chip::Controller::DeviceCommissioner * devCtrl, int idx, char * addrStr, |
| uint32_t len); |
| |
| // Pairing Delegate |
| PyChipError |
| pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(chip::Controller::DeviceCommissioner * devCtrl, |
| chip::Controller::DevicePairingDelegate_OnPairingCompleteFunct callback); |
| |
| PyChipError pychip_ScriptDevicePairingDelegate_SetCommissioningCompleteCallback( |
| chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnCommissioningCompleteFunct callback); |
| |
| PyChipError pychip_ScriptDevicePairingDelegate_SetCommissioningStatusUpdateCallback( |
| chip::Controller::DeviceCommissioner * devCtrl, |
| chip::Controller::DevicePairingDelegate_OnCommissioningStatusUpdateFunct callback); |
| PyChipError pychip_ScriptDevicePairingDelegate_SetOpenWindowCompleteCallback( |
| chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnWindowOpenCompleteFunct callback); |
| |
| // BLE |
| PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::DeviceCommissioner * devCtrl); |
| |
| uint8_t pychip_DeviceController_GetLogFilter(); |
| void pychip_DeviceController_SetLogFilter(uint8_t category); |
| |
| const char * pychip_Stack_ErrorToString(ChipError::StorageType err); |
| const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode); |
| void pychip_Stack_SetLogFunct(LogMessageFunct logFunct); |
| |
| PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, |
| DeviceAvailableFunc callback); |
| PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy); |
| PyChipError pychip_GetLocalSessionId(chip::OperationalDeviceProxy * deviceProxy, uint16_t * localSessionId); |
| PyChipError pychip_GetNumSessionsToPeer(chip::OperationalDeviceProxy * deviceProxy, uint32_t * numSessions); |
| PyChipError pychip_GetAttestationChallenge(chip::OperationalDeviceProxy * deviceProxy, uint8_t * buf, size_t * size); |
| PyChipError pychip_GetDeviceBeingCommissioned(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, |
| CommissioneeDeviceProxy ** proxy); |
| PyChipError pychip_ExpireSessions(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId); |
| |
| uint64_t pychip_GetCommandSenderHandle(chip::DeviceProxy * device); |
| |
| PyChipError pychip_InteractionModel_ShutdownSubscription(SubscriptionId subscriptionId); |
| |
| // |
| // Storage |
| // |
| void * pychip_Storage_InitializeStorageAdapter(chip::Controller::Python::PyObject * context, |
| chip::Controller::Python::SyncSetKeyValueCb setCb, |
| chip::Controller::Python::SetGetKeyValueCb getCb, |
| chip::Controller::Python::SyncDeleteKeyValueCb deleteCb); |
| void pychip_Storage_ShutdownAdapter(chip::Controller::Python::StorageAdapter * storageAdapter); |
| } |
| |
| void * pychip_Storage_InitializeStorageAdapter(chip::Controller::Python::PyObject * context, |
| chip::Controller::Python::SyncSetKeyValueCb setCb, |
| chip::Controller::Python::SetGetKeyValueCb getCb, |
| chip::Controller::Python::SyncDeleteKeyValueCb deleteCb) |
| { |
| auto ptr = new chip::Controller::Python::StorageAdapter(context, setCb, getCb, deleteCb); |
| return ptr; |
| } |
| |
| void pychip_Storage_ShutdownAdapter(chip::Controller::Python::StorageAdapter * storageAdapter) |
| { |
| delete storageAdapter; |
| } |
| |
| PyChipError pychip_DeviceController_StackInit(Controller::Python::StorageAdapter * storageAdapter, bool enableServerInteractions) |
| { |
| VerifyOrDie(storageAdapter != nullptr); |
| |
| FactoryInitParams factoryParams; |
| |
| factoryParams.fabricIndependentStorage = storageAdapter; |
| factoryParams.sessionKeystore = &sSessionKeystore; |
| |
| sGroupDataProvider.SetStorageDelegate(storageAdapter); |
| sGroupDataProvider.SetSessionKeystore(factoryParams.sessionKeystore); |
| PyReturnErrorOnFailure(ToPyChipError(sGroupDataProvider.Init())); |
| Credentials::SetGroupDataProvider(&sGroupDataProvider); |
| factoryParams.groupDataProvider = &sGroupDataProvider; |
| |
| PyReturnErrorOnFailure(ToPyChipError(sPersistentStorageOpCertStore.Init(storageAdapter))); |
| factoryParams.opCertStore = &sPersistentStorageOpCertStore; |
| |
| factoryParams.enableServerInteractions = enableServerInteractions; |
| |
| // Hack needed due to the fact that DnsSd server uses the CommissionableDataProvider even |
| // when never starting commissionable advertising. This will not be used but prevents |
| // null pointer dereferences. |
| static chip::DeviceLayer::TestOnlyCommissionableDataProvider TestOnlyCommissionableDataProvider; |
| chip::DeviceLayer::SetCommissionableDataProvider(&TestOnlyCommissionableDataProvider); |
| |
| PyReturnErrorOnFailure(ToPyChipError(DeviceControllerFactory::GetInstance().Init(factoryParams))); |
| |
| // |
| // In situations where all the controller instances get shutdown, the entire stack is then also |
| // implicitly shutdown. In the REPL, users can create such a situation by manually shutting down |
| // controllers (for example, when they call ChipReplStartup::LoadFabricAdmins multiple times). In |
| // that situation, momentarily, the stack gets de-initialized. This results in further interactions with |
| // the stack being dangerous (and in fact, causes crashes). |
| // |
| // This retain call ensures the stack doesn't get de-initialized in the REPL. |
| // |
| DeviceControllerFactory::GetInstance().RetainSystemState(); |
| |
| // |
| // Finally, start up the main Matter thread. Any further interactions with the stack |
| // will now need to happen on the Matter thread, OR protected with the stack lock. |
| // |
| PyReturnErrorOnFailure(ToPyChipError(chip::DeviceLayer::PlatformMgr().StartEventLoopTask())); |
| |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_StackShutdown() |
| { |
| ChipLogError(Controller, "Shutting down the stack..."); |
| |
| // |
| // Let's stop the Matter thread, and wait till the event loop has stopped. |
| // |
| PyReturnErrorOnFailure(ToPyChipError(chip::DeviceLayer::PlatformMgr().StopEventLoopTask())); |
| |
| // |
| // There is the symmetric call to match the Retain called at stack initialization |
| // time. This will release all resources (if there are no other controllers active). |
| // |
| DeviceControllerFactory::GetInstance().ReleaseSystemState(); |
| |
| DeviceControllerFactory::GetInstance().Shutdown(); |
| |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_GetAddressAndPort(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, |
| char * outAddress, uint64_t maxAddressLen, uint16_t * outPort) |
| { |
| Inet::IPAddress address; |
| PyReturnErrorOnFailure(ToPyChipError(devCtrl->GetPeerAddressAndPort(nodeId, address, *outPort))); |
| VerifyOrReturnError(address.ToString(outAddress, static_cast<uint32_t>(maxAddressLen)), |
| ToPyChipError(CHIP_ERROR_BUFFER_TOO_SMALL)); |
| |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_GetCompressedFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId) |
| { |
| *outFabricId = devCtrl->GetCompressedFabricId(); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_GetFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId) |
| { |
| *outFabricId = devCtrl->GetFabricId(); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outNodeId) |
| { |
| *outNodeId = devCtrl->GetNodeId(); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| const char * pychip_DeviceController_ErrorToString(ChipError::StorageType err) |
| { |
| return chip::ErrorStr(CHIP_ERROR(err)); |
| } |
| |
| const char * pychip_DeviceController_StatusReportToString(uint32_t profileId, uint16_t statusCode) |
| { |
| // return chip::StatusReportStr(profileId, statusCode); |
| return nullptr; |
| } |
| |
| uint8_t pychip_DeviceController_GetLogFilter() |
| { |
| #if _CHIP_USE_LOGGING |
| return chip::Logging::GetLogFilter(); |
| #else |
| return chip::Logging::kLogCategory_None; |
| #endif |
| } |
| |
| void pychip_DeviceController_SetLogFilter(uint8_t category) |
| { |
| #if _CHIP_USE_LOGGING |
| chip::Logging::SetLogFilter(category); |
| #endif |
| } |
| |
| PyChipError pychip_DeviceController_ConnectBLE(chip::Controller::DeviceCommissioner * devCtrl, uint16_t discriminator, |
| uint32_t setupPINCode, chip::NodeId nodeid) |
| { |
| sPairingDelegate.SetExpectingPairingComplete(true); |
| return ToPyChipError(devCtrl->PairDevice(nodeid, |
| chip::RendezvousParameters() |
| .SetPeerAddress(Transport::PeerAddress(Transport::Type::kBle)) |
| .SetSetupPINCode(setupPINCode) |
| .SetDiscriminator(discriminator), |
| sCommissioningParameters)); |
| } |
| |
| PyChipError pychip_DeviceController_ConnectIP(chip::Controller::DeviceCommissioner * devCtrl, const char * peerAddrStr, |
| uint32_t setupPINCode, chip::NodeId nodeid) |
| { |
| chip::Inet::IPAddress peerAddr; |
| chip::Transport::PeerAddress addr; |
| chip::RendezvousParameters params = chip::RendezvousParameters().SetSetupPINCode(setupPINCode); |
| |
| VerifyOrReturnError(chip::Inet::IPAddress::FromString(peerAddrStr, peerAddr), ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); |
| |
| // TODO: IP rendezvous should use TCP connection. |
| addr.SetTransportType(chip::Transport::Type::kUdp).SetIPAddress(peerAddr); |
| params.SetPeerAddress(addr).SetDiscriminator(0); |
| |
| sPairingDelegate.SetExpectingPairingComplete(true); |
| return ToPyChipError(devCtrl->PairDevice(nodeid, params, sCommissioningParameters)); |
| } |
| |
| PyChipError pychip_DeviceController_ConnectWithCode(chip::Controller::DeviceCommissioner * devCtrl, const char * onboardingPayload, |
| chip::NodeId nodeid) |
| { |
| sPairingDelegate.SetExpectingPairingComplete(true); |
| return ToPyChipError(devCtrl->PairDevice(nodeid, onboardingPayload, sCommissioningParameters)); |
| } |
| |
| namespace { |
| struct UnpairDeviceCallback |
| { |
| UnpairDeviceCallback(DeviceUnpairingCompleteFunct callback, chip::Controller::CurrentFabricRemover * remover) : |
| mOnCurrentFabricRemove(OnCurrentFabricRemoveFn, this), mCallback(callback), mRemover(remover) |
| {} |
| |
| static void OnCurrentFabricRemoveFn(void * context, chip::NodeId nodeId, CHIP_ERROR error) |
| { |
| auto * self = static_cast<UnpairDeviceCallback *>(context); |
| self->mCallback(nodeId, ToPyChipError(error)); |
| delete self->mRemover; |
| delete self; |
| } |
| |
| Callback::Callback<OnCurrentFabricRemove> mOnCurrentFabricRemove; |
| DeviceUnpairingCompleteFunct mCallback; |
| chip::Controller::CurrentFabricRemover * mRemover; |
| }; |
| } // anonymous namespace |
| |
| PyChipError pychip_DeviceController_UnpairDevice(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid, |
| DeviceUnpairingCompleteFunct callback) |
| { |
| // Create a new CurrentFabricRemover instance |
| auto * fabricRemover = new chip::Controller::CurrentFabricRemover(devCtrl); |
| |
| auto * callbacks = new UnpairDeviceCallback(callback, fabricRemover); |
| |
| // Pass the callback and nodeid to the RemoveCurrentFabric function |
| CHIP_ERROR err = fabricRemover->RemoveCurrentFabric(nodeid, &callbacks->mOnCurrentFabricRemove); |
| if (err != CHIP_NO_ERROR) |
| { |
| delete fabricRemover; |
| delete callbacks; |
| } |
| // Else will clean up when the callback is called. |
| return ToPyChipError(err); |
| } |
| |
| PyChipError pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, uint64_t nodeId, |
| uint32_t setupPasscode, const uint8_t filterType, const char * filterParam, |
| uint32_t discoveryTimeoutMsec) |
| { |
| Dnssd::DiscoveryFilter filter(static_cast<Dnssd::DiscoveryFilterType>(filterType)); |
| switch (static_cast<Dnssd::DiscoveryFilterType>(filterType)) |
| { |
| case chip::Dnssd::DiscoveryFilterType::kNone: |
| break; |
| case chip::Dnssd::DiscoveryFilterType::kShortDiscriminator: |
| case chip::Dnssd::DiscoveryFilterType::kLongDiscriminator: |
| case chip::Dnssd::DiscoveryFilterType::kCompressedFabricId: |
| case chip::Dnssd::DiscoveryFilterType::kVendorId: |
| case chip::Dnssd::DiscoveryFilterType::kDeviceType: { |
| // For any numerical filter, convert the string to a filter value |
| errno = 0; |
| unsigned long long int numericalArg = strtoull(filterParam, nullptr, 0); |
| if ((numericalArg == ULLONG_MAX) && (errno == ERANGE)) |
| { |
| return ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| filter.code = static_cast<uint64_t>(numericalArg); |
| break; |
| } |
| case chip::Dnssd::DiscoveryFilterType::kCommissioningMode: |
| break; |
| case chip::Dnssd::DiscoveryFilterType::kCommissioner: |
| filter.code = 1; |
| break; |
| case chip::Dnssd::DiscoveryFilterType::kInstanceName: |
| filter.code = 0; |
| filter.instanceName = filterParam; |
| break; |
| default: |
| return ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| |
| sPairingDelegate.SetExpectingPairingComplete(true); |
| CHIP_ERROR err = sPairingDeviceDiscoveryDelegate.Init(nodeId, setupPasscode, sCommissioningParameters, &sPairingDelegate, |
| devCtrl, discoveryTimeoutMsec); |
| VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err)); |
| return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); |
| } |
| |
| PyChipError pychip_DeviceController_SetThreadOperationalDataset(const char * threadOperationalDataset, uint32_t size) |
| { |
| ReturnErrorCodeIf(!sThreadBuf.Alloc(size), ToPyChipError(CHIP_ERROR_NO_MEMORY)); |
| memcpy(sThreadBuf.Get(), threadOperationalDataset, size); |
| sCommissioningParameters.SetThreadOperationalDataset(ByteSpan(sThreadBuf.Get(), size)); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| PyChipError pychip_DeviceController_SetWiFiCredentials(const char * ssid, const char * credentials) |
| { |
| size_t ssidSize = strlen(ssid); |
| ReturnErrorCodeIf(!sSsidBuf.Alloc(ssidSize), ToPyChipError(CHIP_ERROR_NO_MEMORY)); |
| memcpy(sSsidBuf.Get(), ssid, ssidSize); |
| |
| size_t credsSize = strlen(credentials); |
| ReturnErrorCodeIf(!sCredsBuf.Alloc(credsSize), ToPyChipError(CHIP_ERROR_NO_MEMORY)); |
| memcpy(sCredsBuf.Get(), credentials, credsSize); |
| |
| sCommissioningParameters.SetWiFiCredentials( |
| chip::Controller::WiFiCredentials(ByteSpan(sSsidBuf.Get(), ssidSize), ByteSpan(sCredsBuf.Get(), credsSize))); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_SetTimeZone(int32_t offset, uint64_t validAt) |
| { |
| sTimeZoneBuf.offset = offset; |
| sTimeZoneBuf.validAt = validAt; |
| app::DataModel::List<app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type> list(&sTimeZoneBuf, 1); |
| sCommissioningParameters.SetTimeZone(list); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| PyChipError pychip_DeviceController_SetDSTOffset(int32_t offset, uint64_t validStarting, uint64_t validUntil) |
| { |
| sDSTBuf.offset = offset; |
| sDSTBuf.validStarting = validStarting; |
| sDSTBuf.validUntil = chip::app::DataModel::MakeNullable(validUntil); |
| app::DataModel::List<app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type> list(&sDSTBuf, 1); |
| sCommissioningParameters.SetDSTOffsets(list); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| PyChipError pychip_DeviceController_SetDefaultNtp(const char * defaultNTP) |
| { |
| size_t len = strlen(defaultNTP); |
| ReturnErrorCodeIf(!sDefaultNTPBuf.Alloc(len), ToPyChipError(CHIP_ERROR_NO_MEMORY)); |
| memcpy(sDefaultNTPBuf.Get(), defaultNTP, len); |
| sCommissioningParameters.SetDefaultNTP(chip::app::DataModel::MakeNullable(CharSpan(sDefaultNTPBuf.Get(), len))); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_SetTrustedTimeSource(chip::NodeId nodeId, chip::EndpointId endpoint) |
| { |
| chip::app::Clusters::TimeSynchronization::Structs::FabricScopedTrustedTimeSourceStruct::Type timeSource = { .nodeID = nodeId, |
| .endpoint = |
| endpoint }; |
| sCommissioningParameters.SetTrustedTimeSource(chip::app::DataModel::MakeNullable(timeSource)); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_ResetCommissioningParameters() |
| { |
| sCommissioningParameters = CommissioningParameters(); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_CloseSession(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid) |
| { |
| // |
| // Since we permit multiple controllers per fabric and each is associated with a unique fabric index, closing a session |
| // requires us to do so across all controllers on the same logical fabric. |
| // |
| devCtrl->SessionMgr()->ForEachMatchingSessionOnLogicalFabric(ScopedNodeId(nodeid, devCtrl->GetFabricIndex()), |
| [](auto * session) { |
| if (session->IsActiveSession()) |
| { |
| session->MarkAsDefunct(); |
| } |
| }); |
| |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_EstablishPASESessionIP(chip::Controller::DeviceCommissioner * devCtrl, const char * peerAddrStr, |
| uint32_t setupPINCode, chip::NodeId nodeid, uint16_t port) |
| { |
| chip::Inet::IPAddress peerAddr; |
| chip::Transport::PeerAddress addr; |
| RendezvousParameters params = chip::RendezvousParameters().SetSetupPINCode(setupPINCode); |
| VerifyOrReturnError(chip::Inet::IPAddress::FromString(peerAddrStr, peerAddr), ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); |
| addr.SetTransportType(chip::Transport::Type::kUdp).SetIPAddress(peerAddr); |
| if (port != 0) |
| { |
| addr.SetPort(port); |
| } |
| params.SetPeerAddress(addr).SetDiscriminator(0); |
| sPairingDelegate.SetExpectingPairingComplete(true); |
| return ToPyChipError(devCtrl->EstablishPASEConnection(nodeid, params)); |
| } |
| |
| PyChipError pychip_DeviceController_EstablishPASESessionBLE(chip::Controller::DeviceCommissioner * devCtrl, uint32_t setupPINCode, |
| uint16_t discriminator, chip::NodeId nodeid) |
| { |
| chip::Transport::PeerAddress addr; |
| RendezvousParameters params = chip::RendezvousParameters().SetSetupPINCode(setupPINCode); |
| addr.SetTransportType(chip::Transport::Type::kBle); |
| params.SetPeerAddress(addr).SetDiscriminator(discriminator); |
| sPairingDelegate.SetExpectingPairingComplete(true); |
| return ToPyChipError(devCtrl->EstablishPASEConnection(nodeid, params)); |
| } |
| |
| PyChipError pychip_DeviceController_Commission(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid) |
| { |
| CommissioningParameters params; |
| return ToPyChipError(devCtrl->Commission(nodeid, params)); |
| } |
| |
| PyChipError pychip_DeviceController_DiscoverAllCommissionableNodes(chip::Controller::DeviceCommissioner * devCtrl) |
| { |
| Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone, static_cast<uint64_t>(0)); |
| return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); |
| } |
| |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator(chip::Controller::DeviceCommissioner * devCtrl, |
| uint16_t long_discriminator) |
| { |
| Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kLongDiscriminator, long_discriminator); |
| return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); |
| } |
| |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator(chip::Controller::DeviceCommissioner * devCtrl, |
| uint16_t short_discriminator) |
| { |
| Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kShortDiscriminator, short_discriminator); |
| return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); |
| } |
| |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesVendor(chip::Controller::DeviceCommissioner * devCtrl, |
| uint16_t vendor) |
| { |
| Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kVendorId, vendor); |
| return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); |
| } |
| |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesDeviceType(chip::Controller::DeviceCommissioner * devCtrl, |
| uint16_t device_type) |
| { |
| Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kDeviceType, device_type); |
| return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); |
| } |
| |
| PyChipError pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled(chip::Controller::DeviceCommissioner * devCtrl) |
| { |
| Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kCommissioningMode); |
| return ToPyChipError(devCtrl->DiscoverCommissionableNodes(filter)); |
| } |
| |
| PyChipError pychip_ScriptDevicePairingDelegate_SetOpenWindowCompleteCallback( |
| chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnWindowOpenCompleteFunct callback) |
| { |
| sPairingDelegate.SetCommissioningWindowOpenCallback(callback); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid, |
| uint16_t timeout, uint32_t iteration, uint16_t discriminator, |
| uint8_t optionInt) |
| { |
| const auto option = static_cast<Controller::CommissioningWindowOpener::CommissioningWindowOption>(optionInt); |
| if (option == Controller::CommissioningWindowOpener::CommissioningWindowOption::kOriginalSetupCode) |
| { |
| return ToPyChipError(Controller::AutoCommissioningWindowOpener::OpenBasicCommissioningWindow( |
| devCtrl, nodeid, System::Clock::Seconds16(timeout))); |
| } |
| |
| if (option == Controller::CommissioningWindowOpener::CommissioningWindowOption::kTokenWithRandomPIN) |
| { |
| SetupPayload payload; |
| auto opener = |
| Platform::New<Controller::CommissioningWindowOpener>(static_cast<chip::Controller::DeviceController *>(devCtrl)); |
| PyChipError err = ToPyChipError(opener->OpenCommissioningWindow(nodeid, System::Clock::Seconds16(timeout), iteration, |
| discriminator, NullOptional, NullOptional, |
| sPairingDelegate.GetOpenWindowCallback(opener), payload)); |
| return err; |
| } |
| |
| return ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| |
| PyChipError |
| pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(chip::Controller::DeviceCommissioner * devCtrl, |
| chip::Controller::DevicePairingDelegate_OnPairingCompleteFunct callback) |
| { |
| sPairingDelegate.SetKeyExchangeCallback(callback); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_ScriptDevicePairingDelegate_SetCommissioningCompleteCallback( |
| chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnCommissioningCompleteFunct callback) |
| { |
| sPairingDelegate.SetCommissioningCompleteCallback(callback); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_ScriptDevicePairingDelegate_SetCommissioningStatusUpdateCallback( |
| chip::Controller::DeviceCommissioner * devCtrl, |
| chip::Controller::DevicePairingDelegate_OnCommissioningStatusUpdateFunct callback) |
| { |
| sPairingDelegate.SetCommissioningStatusUpdateCallback(callback); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| const char * pychip_Stack_ErrorToString(ChipError::StorageType err) |
| { |
| return chip::ErrorStr(CHIP_ERROR(err)); |
| } |
| |
| const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode) |
| { |
| // return chip::StatusReportStr(profileId, statusCode); |
| return nullptr; |
| } |
| |
| namespace { |
| |
| struct GetDeviceCallbacks |
| { |
| GetDeviceCallbacks(DeviceAvailableFunc callback) : |
| mOnSuccess(OnDeviceConnectedFn, this), mOnFailure(OnConnectionFailureFn, this), mCallback(callback) |
| {} |
| |
| static void OnDeviceConnectedFn(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle) |
| { |
| auto * self = static_cast<GetDeviceCallbacks *>(context); |
| auto * operationalDeviceProxy = new OperationalDeviceProxy(&exchangeMgr, sessionHandle); |
| self->mCallback(operationalDeviceProxy, ToPyChipError(CHIP_NO_ERROR)); |
| delete self; |
| } |
| |
| static void OnConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR error) |
| { |
| auto * self = static_cast<GetDeviceCallbacks *>(context); |
| self->mCallback(nullptr, ToPyChipError(error)); |
| delete self; |
| } |
| |
| Callback::Callback<OnDeviceConnected> mOnSuccess; |
| Callback::Callback<OnDeviceConnectionFailure> mOnFailure; |
| DeviceAvailableFunc mCallback; |
| }; |
| } // anonymous namespace |
| |
| PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, |
| DeviceAvailableFunc callback) |
| { |
| VerifyOrReturnError(devCtrl != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); |
| auto * callbacks = new GetDeviceCallbacks(callback); |
| return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure)); |
| } |
| |
| PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy) |
| { |
| if (deviceProxy != nullptr) |
| { |
| delete deviceProxy; |
| } |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_GetLocalSessionId(chip::OperationalDeviceProxy * deviceProxy, uint16_t * localSessionId) |
| { |
| VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); |
| VerifyOrReturnError(localSessionId != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); |
| |
| *localSessionId = deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetLocalSessionId(); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_GetNumSessionsToPeer(chip::OperationalDeviceProxy * deviceProxy, uint32_t * numSessions) |
| { |
| VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); |
| VerifyOrReturnError(numSessions != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); |
| |
| *numSessions = 0; |
| deviceProxy->GetExchangeManager()->GetSessionManager()->ForEachMatchingSession( |
| deviceProxy->GetPeerScopedNodeId(), [numSessions](auto * session) { (*numSessions)++; }); |
| |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_GetAttestationChallenge(chip::OperationalDeviceProxy * deviceProxy, uint8_t * buf, size_t * size) |
| { |
| VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); |
| VerifyOrReturnError(buf != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); |
| |
| ByteSpan challenge = deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge(); |
| VerifyOrReturnError(challenge.size() <= *size, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); |
| memcpy(buf, challenge.data(), challenge.size()); |
| *size = challenge.size(); |
| |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_GetDeviceBeingCommissioned(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, |
| CommissioneeDeviceProxy ** proxy) |
| { |
| return ToPyChipError(devCtrl->GetDeviceBeingCommissioned(nodeId, proxy)); |
| } |
| |
| // This is a method called VERY seldom, just for RemoveFabric/UpdateNOC |
| PyChipError pychip_ExpireSessions(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId) |
| { |
| VerifyOrReturnError((devCtrl != nullptr) && (devCtrl->SessionMgr() != nullptr), ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); |
| |
| // |
| // Stop any active pairing sessions to this node. |
| // |
| devCtrl->StopPairing(nodeId); |
| |
| // |
| // Since we permit multiple controllers on the same fabric each associated with a different fabric index, expiring a session |
| // needs to correctly expire sessions on other controllers on matching fabrics as well. |
| // |
| devCtrl->SessionMgr()->ExpireAllSessionsOnLogicalFabric(ScopedNodeId(nodeId, devCtrl->GetFabricIndex())); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |
| |
| PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::DeviceCommissioner * devCtrl) |
| { |
| #if CONFIG_NETWORK_LAYER_BLE |
| devCtrl->CloseBleConnection(); |
| return ToPyChipError(CHIP_NO_ERROR); |
| #else |
| return ToPyChipError(CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); |
| #endif |
| } |
| |
| uint64_t pychip_GetCommandSenderHandle(chip::DeviceProxy * device) |
| { |
| return 0; |
| } |
| |
| void pychip_Stack_SetLogFunct(LogMessageFunct logFunct) |
| { |
| // TODO: determine if log redirection is supposed to be functioning in CHIP |
| // |
| // Background: original log baseline supported 'redirect logs to this |
| // function' however CHIP does not currently provide this. |
| // |
| // Ideally log redirection should work so that python code can do things |
| // like using the log module. |
| } |
| |
| PyChipError pychip_DeviceController_PostTaskOnChipThread(ChipThreadTaskRunnerFunct callback, void * pythonContext) |
| { |
| if (callback == nullptr || pythonContext == nullptr) |
| { |
| return ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| PlatformMgr().ScheduleWork(callback, reinterpret_cast<intptr_t>(pythonContext)); |
| return ToPyChipError(CHIP_NO_ERROR); |
| } |