blob: 194004e0dc93fbefff0f869a18667144410b912d [file] [log] [blame]
/*
*
* 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/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 <credentials/GroupDataProviderImpl.h>
#include <credentials/PersistentStorageOpCertStore.h>
#include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
#include <credentials/attestation_verifier/DeviceAttestationVerifier.h>
#include <inet/IPAddress.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;
static_assert(std::is_same<uint32_t, ChipError::StorageType>::value, "python assumes CHIP_ERROR maps to c_uint32");
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, ChipError::StorageType err);
typedef void (*ChipThreadTaskRunnerFunct)(intptr_t context);
}
namespace {
chip::Platform::ScopedMemoryBuffer<uint8_t> sSsidBuf;
chip::Platform::ScopedMemoryBuffer<uint8_t> sCredsBuf;
chip::Platform::ScopedMemoryBuffer<uint8_t> sThreadBuf;
chip::Controller::CommissioningParameters sCommissioningParameters;
} // namespace
chip::Controller::ScriptDevicePairingDelegate sPairingDelegate;
chip::Controller::ScriptPairingDeviceDiscoveryDelegate sPairingDeviceDiscoveryDelegate;
chip::Controller::Python::StorageAdapter * sStorageAdapter = nullptr;
chip::Credentials::GroupDataProviderImpl sGroupDataProvider;
chip::Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore;
// 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" {
ChipError::StorageType pychip_DeviceController_StackInit();
ChipError::StorageType pychip_DeviceController_StackShutdown();
ChipError::StorageType pychip_DeviceController_NewDeviceController(chip::Controller::DeviceCommissioner ** outDevCtrl,
chip::NodeId localDeviceId, bool useTestCommissioner);
ChipError::StorageType pychip_DeviceController_DeleteDeviceController(chip::Controller::DeviceCommissioner * devCtrl);
ChipError::StorageType pychip_DeviceController_GetAddressAndPort(chip::Controller::DeviceCommissioner * devCtrl,
chip::NodeId nodeId, char * outAddress, uint64_t maxAddressLen,
uint16_t * outPort);
ChipError::StorageType pychip_DeviceController_GetCompressedFabricId(chip::Controller::DeviceCommissioner * devCtrl,
uint64_t * outFabricId);
ChipError::StorageType pychip_DeviceController_GetFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId);
ChipError::StorageType pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outNodeId);
// Rendezvous
ChipError::StorageType pychip_DeviceController_ConnectBLE(chip::Controller::DeviceCommissioner * devCtrl, uint16_t discriminator,
uint32_t setupPINCode, chip::NodeId nodeid);
ChipError::StorageType pychip_DeviceController_ConnectIP(chip::Controller::DeviceCommissioner * devCtrl, const char * peerAddrStr,
uint32_t setupPINCode, chip::NodeId nodeid);
ChipError::StorageType pychip_DeviceController_ConnectWithCode(chip::Controller::DeviceCommissioner * devCtrl,
const char * onboardingPayload, chip::NodeId nodeid);
ChipError::StorageType pychip_DeviceController_SetThreadOperationalDataset(const char * threadOperationalDataset, uint32_t size);
ChipError::StorageType pychip_DeviceController_SetWiFiCredentials(const char * ssid, const char * credentials);
ChipError::StorageType pychip_DeviceController_CloseSession(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid);
ChipError::StorageType pychip_DeviceController_EstablishPASESessionIP(chip::Controller::DeviceCommissioner * devCtrl,
const char * peerAddrStr, uint32_t setupPINCode,
chip::NodeId nodeid);
ChipError::StorageType pychip_DeviceController_Commission(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid);
ChipError::StorageType
pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator(chip::Controller::DeviceCommissioner * devCtrl,
uint16_t long_discriminator);
ChipError::StorageType pychip_DeviceController_DiscoverAllCommissionableNodes(chip::Controller::DeviceCommissioner * devCtrl);
ChipError::StorageType
pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator(chip::Controller::DeviceCommissioner * devCtrl,
uint16_t short_discriminator);
ChipError::StorageType pychip_DeviceController_DiscoverCommissionableNodesVendor(chip::Controller::DeviceCommissioner * devCtrl,
uint16_t vendor);
ChipError::StorageType pychip_DeviceController_DiscoverCommissionableNodesDeviceType(chip::Controller::DeviceCommissioner * devCtrl,
uint16_t device_type);
ChipError::StorageType
pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled(chip::Controller::DeviceCommissioner * devCtrl);
ChipError::StorageType pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, uint64_t nodeId,
uint32_t setupPasscode, const uint8_t filterType,
const char * filterParam);
ChipError::StorageType pychip_DeviceController_PostTaskOnChipThread(ChipThreadTaskRunnerFunct callback, void * pythonContext);
ChipError::StorageType 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
ChipError::StorageType
pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(chip::Controller::DeviceCommissioner * devCtrl,
chip::Controller::DevicePairingDelegate_OnPairingCompleteFunct callback);
ChipError::StorageType pychip_ScriptDevicePairingDelegate_SetCommissioningCompleteCallback(
chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnCommissioningCompleteFunct callback);
ChipError::StorageType pychip_ScriptDevicePairingDelegate_SetCommissioningStatusUpdateCallback(
chip::Controller::DeviceCommissioner * devCtrl,
chip::Controller::DevicePairingDelegate_OnCommissioningStatusUpdateFunct callback);
// BLE
ChipError::StorageType 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);
ChipError::StorageType pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
DeviceAvailableFunc callback);
ChipError::StorageType pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy);
ChipError::StorageType pychip_GetDeviceBeingCommissioned(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
CommissioneeDeviceProxy ** proxy);
ChipError::StorageType pychip_ExpireSessions(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId);
uint64_t pychip_GetCommandSenderHandle(chip::DeviceProxy * device);
chip::ChipError::StorageType 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();
}
void pychip_Storage_InitializeStorageAdapter(chip::Controller::Python::PyObject * context,
chip::Controller::Python::SyncSetKeyValueCb setCb,
chip::Controller::Python::SetGetKeyValueCb getCb,
chip::Controller::Python::SyncDeleteKeyValueCb deleteCb)
{
sStorageAdapter = new chip::Controller::Python::StorageAdapter(context, setCb, getCb, deleteCb);
}
void pychip_Storage_ShutdownAdapter()
{
delete sStorageAdapter;
}
chip::Controller::Python::StorageAdapter * pychip_Storage_GetStorageAdapter()
{
return sStorageAdapter;
}
ChipError::StorageType pychip_DeviceController_StackInit()
{
VerifyOrDie(sStorageAdapter != nullptr);
FactoryInitParams factoryParams;
factoryParams.fabricIndependentStorage = sStorageAdapter;
sGroupDataProvider.SetStorageDelegate(sStorageAdapter);
ReturnErrorOnFailure(sGroupDataProvider.Init().AsInteger());
factoryParams.groupDataProvider = &sGroupDataProvider;
ReturnErrorOnFailure(sPersistentStorageOpCertStore.Init(sStorageAdapter).AsInteger());
factoryParams.opCertStore = &sPersistentStorageOpCertStore;
factoryParams.enableServerInteractions = true;
// 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);
ReturnErrorOnFailure(DeviceControllerFactory::GetInstance().Init(factoryParams).AsInteger());
//
// 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.
//
ReturnErrorOnFailure(chip::DeviceLayer::PlatformMgr().StartEventLoopTask().AsInteger());
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_DeviceController_StackShutdown()
{
ChipLogError(Controller, "Shutting down the stack...");
//
// Let's stop the Matter thread, and wait till the event loop has stopped.
//
ReturnErrorOnFailure(chip::DeviceLayer::PlatformMgr().StopEventLoopTask().AsInteger());
//
// 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 CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_DeviceController_GetAddressAndPort(chip::Controller::DeviceCommissioner * devCtrl,
chip::NodeId nodeId, char * outAddress, uint64_t maxAddressLen,
uint16_t * outPort)
{
Inet::IPAddress address;
ReturnErrorOnFailure(devCtrl->GetPeerAddressAndPort(nodeId, address, *outPort).AsInteger());
VerifyOrReturnError(address.ToString(outAddress, static_cast<uint32_t>(maxAddressLen)),
CHIP_ERROR_BUFFER_TOO_SMALL.AsInteger());
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_DeviceController_GetCompressedFabricId(chip::Controller::DeviceCommissioner * devCtrl,
uint64_t * outFabricId)
{
*outFabricId = devCtrl->GetCompressedFabricId();
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_DeviceController_GetFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId)
{
*outFabricId = devCtrl->GetFabricId();
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outNodeId)
{
*outNodeId = devCtrl->GetNodeId();
return CHIP_NO_ERROR.AsInteger();
}
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
}
ChipError::StorageType pychip_DeviceController_ConnectBLE(chip::Controller::DeviceCommissioner * devCtrl, uint16_t discriminator,
uint32_t setupPINCode, chip::NodeId nodeid)
{
return devCtrl
->PairDevice(nodeid,
chip::RendezvousParameters()
.SetPeerAddress(Transport::PeerAddress(Transport::Type::kBle))
.SetSetupPINCode(setupPINCode)
.SetDiscriminator(discriminator),
sCommissioningParameters)
.AsInteger();
}
ChipError::StorageType 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), CHIP_ERROR_INVALID_ARGUMENT.AsInteger());
// TODO: IP rendezvous should use TCP connection.
addr.SetTransportType(chip::Transport::Type::kUdp).SetIPAddress(peerAddr);
params.SetPeerAddress(addr).SetDiscriminator(0);
devCtrl->ReleaseOperationalDevice(nodeid);
return devCtrl->PairDevice(nodeid, params, sCommissioningParameters).AsInteger();
}
ChipError::StorageType pychip_DeviceController_ConnectWithCode(chip::Controller::DeviceCommissioner * devCtrl,
const char * onboardingPayload, chip::NodeId nodeid)
{
return devCtrl->PairDevice(nodeid, onboardingPayload, sCommissioningParameters).AsInteger();
}
ChipError::StorageType pychip_DeviceController_OnNetworkCommission(chip::Controller::DeviceCommissioner * devCtrl, uint64_t nodeId,
uint32_t setupPasscode, const uint8_t filterType,
const char * filterParam)
{
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 CHIP_ERROR_INVALID_ARGUMENT.AsInteger();
}
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 CHIP_ERROR_INVALID_ARGUMENT.AsInteger();
}
sPairingDeviceDiscoveryDelegate.Init(nodeId, setupPasscode, sCommissioningParameters, &sPairingDelegate, devCtrl);
devCtrl->RegisterDeviceDiscoveryDelegate(&sPairingDeviceDiscoveryDelegate);
return devCtrl->DiscoverCommissionableNodes(filter).AsInteger();
}
ChipError::StorageType pychip_DeviceController_SetThreadOperationalDataset(const char * threadOperationalDataset, uint32_t size)
{
ReturnErrorCodeIf(!sThreadBuf.Alloc(size), CHIP_ERROR_NO_MEMORY.AsInteger());
memcpy(sThreadBuf.Get(), threadOperationalDataset, size);
sCommissioningParameters.SetThreadOperationalDataset(ByteSpan(sThreadBuf.Get(), size));
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_DeviceController_SetWiFiCredentials(const char * ssid, const char * credentials)
{
size_t ssidSize = strlen(ssid);
ReturnErrorCodeIf(!sSsidBuf.Alloc(ssidSize), CHIP_ERROR_NO_MEMORY.AsInteger());
memcpy(sSsidBuf.Get(), ssid, ssidSize);
size_t credsSize = strlen(credentials);
ReturnErrorCodeIf(!sCredsBuf.Alloc(credsSize), CHIP_ERROR_NO_MEMORY.AsInteger());
memcpy(sCredsBuf.Get(), credentials, credsSize);
sCommissioningParameters.SetWiFiCredentials(
chip::Controller::WiFiCredentials(ByteSpan(sSsidBuf.Get(), ssidSize), ByteSpan(sCredsBuf.Get(), credsSize)));
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_DeviceController_CloseSession(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid)
{
#if 0
//
// 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.
//
// TODO: Enable this and remove the call below to DisconnectDevice once #19259 is completed. This is because
// OperationalDeviceProxy instances that are currently active will remain un-affected by this call and still
// provide a valid SessionHandle in the OnDeviceConnected call later when we call DeviceController::GetConnectedDevice.
// However, it provides a SessionHandle that is incapable of actually vending exchanges since it is in a defunct state.
//
// For now, calling DisconnectDevice will at least just correctly de-activate a currently active OperationalDeviceProxy
// instance and ensure that subsequent attempts to acquire one will correctly re-establish CASE on the fabric associated
// with the provided devCtrl.
//
auto err = devCtrl->SessionMgr()->ForEachCollidingSession(ScopedNodeId(nodeid, devCtrl->GetFabricIndex()), [](auto *session) {
session->MarkAsDefunct();
});
ReturnErrorOnFailure(err.AsInteger());
#else
return devCtrl->DisconnectDevice(nodeid).AsInteger();
#endif
}
ChipError::StorageType pychip_DeviceController_EstablishPASESessionIP(chip::Controller::DeviceCommissioner * devCtrl,
const char * peerAddrStr, uint32_t setupPINCode,
chip::NodeId nodeid)
{
chip::Inet::IPAddress peerAddr;
chip::Transport::PeerAddress addr;
RendezvousParameters params = chip::RendezvousParameters().SetSetupPINCode(setupPINCode);
VerifyOrReturnError(chip::Inet::IPAddress::FromString(peerAddrStr, peerAddr), CHIP_ERROR_INVALID_ARGUMENT.AsInteger());
addr.SetTransportType(chip::Transport::Type::kUdp).SetIPAddress(peerAddr);
params.SetPeerAddress(addr).SetDiscriminator(0);
return devCtrl->EstablishPASEConnection(nodeid, params).AsInteger();
}
ChipError::StorageType pychip_DeviceController_Commission(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid)
{
CommissioningParameters params;
return devCtrl->Commission(nodeid, params).AsInteger();
}
ChipError::StorageType pychip_DeviceController_DiscoverAllCommissionableNodes(chip::Controller::DeviceCommissioner * devCtrl)
{
Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone, static_cast<uint64_t>(0));
return devCtrl->DiscoverCommissionableNodes(filter).AsInteger();
}
ChipError::StorageType
pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator(chip::Controller::DeviceCommissioner * devCtrl,
uint16_t long_discriminator)
{
Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kLongDiscriminator, long_discriminator);
return devCtrl->DiscoverCommissionableNodes(filter).AsInteger();
}
ChipError::StorageType
pychip_DeviceController_DiscoverCommissionableNodesShortDiscriminator(chip::Controller::DeviceCommissioner * devCtrl,
uint16_t short_discriminator)
{
Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kShortDiscriminator, short_discriminator);
return devCtrl->DiscoverCommissionableNodes(filter).AsInteger();
}
ChipError::StorageType pychip_DeviceController_DiscoverCommissionableNodesVendor(chip::Controller::DeviceCommissioner * devCtrl,
uint16_t vendor)
{
Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kVendorId, vendor);
return devCtrl->DiscoverCommissionableNodes(filter).AsInteger();
}
ChipError::StorageType pychip_DeviceController_DiscoverCommissionableNodesDeviceType(chip::Controller::DeviceCommissioner * devCtrl,
uint16_t device_type)
{
Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kDeviceType, device_type);
return devCtrl->DiscoverCommissionableNodes(filter).AsInteger();
}
ChipError::StorageType
pychip_DeviceController_DiscoverCommissionableNodesCommissioningEnabled(chip::Controller::DeviceCommissioner * devCtrl)
{
Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kCommissioningMode);
return devCtrl->DiscoverCommissionableNodes(filter).AsInteger();
}
ChipError::StorageType 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 Controller::AutoCommissioningWindowOpener::OpenBasicCommissioningWindow(devCtrl, nodeid,
System::Clock::Seconds16(timeout))
.AsInteger();
}
if (option == Controller::CommissioningWindowOpener::CommissioningWindowOption::kTokenWithRandomPIN)
{
SetupPayload payload;
return Controller::AutoCommissioningWindowOpener::OpenCommissioningWindow(
devCtrl, nodeid, System::Clock::Seconds16(timeout), iteration, discriminator, NullOptional, NullOptional,
payload)
.AsInteger();
}
return CHIP_ERROR_INVALID_ARGUMENT.AsInteger();
}
void pychip_DeviceController_PrintDiscoveredDevices(chip::Controller::DeviceCommissioner * devCtrl)
{
for (int i = 0; i < devCtrl->GetMaxCommissionableNodesSupported(); ++i)
{
const chip::Dnssd::DiscoveredNodeData * dnsSdInfo = devCtrl->GetDiscoveredDevice(i);
if (dnsSdInfo == nullptr)
{
continue;
}
char rotatingId[chip::Dnssd::kMaxRotatingIdLen * 2 + 1] = "";
Encoding::BytesToUppercaseHexString(dnsSdInfo->commissionData.rotatingId, dnsSdInfo->commissionData.rotatingIdLen,
rotatingId, sizeof(rotatingId));
ChipLogProgress(Discovery, "Commissionable Node %d", i);
ChipLogProgress(Discovery, "\tInstance name:\t\t%s", dnsSdInfo->commissionData.instanceName);
ChipLogProgress(Discovery, "\tHost name:\t\t%s", dnsSdInfo->resolutionData.hostName);
ChipLogProgress(Discovery, "\tPort:\t\t\t%u", dnsSdInfo->resolutionData.port);
ChipLogProgress(Discovery, "\tLong discriminator:\t%u", dnsSdInfo->commissionData.longDiscriminator);
ChipLogProgress(Discovery, "\tVendor ID:\t\t%u", dnsSdInfo->commissionData.vendorId);
ChipLogProgress(Discovery, "\tProduct ID:\t\t%u", dnsSdInfo->commissionData.productId);
ChipLogProgress(Discovery, "\tCommissioning Mode\t%u", dnsSdInfo->commissionData.commissioningMode);
ChipLogProgress(Discovery, "\tDevice Type\t\t%u", dnsSdInfo->commissionData.deviceType);
ChipLogProgress(Discovery, "\tDevice Name\t\t%s", dnsSdInfo->commissionData.deviceName);
ChipLogProgress(Discovery, "\tRotating Id\t\t%s", rotatingId);
ChipLogProgress(Discovery, "\tPairing Instruction\t%s", dnsSdInfo->commissionData.pairingInstruction);
ChipLogProgress(Discovery, "\tPairing Hint\t\t%u", dnsSdInfo->commissionData.pairingHint);
if (dnsSdInfo->resolutionData.GetMrpRetryIntervalIdle().HasValue())
{
ChipLogProgress(Discovery, "\tMrp Interval idle\t%u",
dnsSdInfo->resolutionData.GetMrpRetryIntervalIdle().Value().count());
}
else
{
ChipLogProgress(Discovery, "\tMrp Interval idle\tNot present");
}
if (dnsSdInfo->resolutionData.GetMrpRetryIntervalActive().HasValue())
{
ChipLogProgress(Discovery, "\tMrp Interval active\t%u",
dnsSdInfo->resolutionData.GetMrpRetryIntervalActive().Value().count());
}
else
{
ChipLogProgress(Discovery, "\tMrp Interval active\tNot present");
}
ChipLogProgress(Discovery, "\tSupports TCP\t\t%d", dnsSdInfo->resolutionData.supportsTcp);
for (unsigned j = 0; j < dnsSdInfo->resolutionData.numIPs; ++j)
{
char buf[chip::Inet::IPAddress::kMaxStringLength];
dnsSdInfo->resolutionData.ipAddress[j].ToString(buf);
ChipLogProgress(Discovery, "\tAddress %d:\t\t%s", j, buf);
}
}
}
bool pychip_DeviceController_GetIPForDiscoveredDevice(chip::Controller::DeviceCommissioner * devCtrl, int idx, char * addrStr,
uint32_t len)
{
const chip::Dnssd::DiscoveredNodeData * dnsSdInfo = devCtrl->GetDiscoveredDevice(idx);
if (dnsSdInfo == nullptr)
{
return false;
}
// TODO(cecille): Select which one we actually want.
if (dnsSdInfo->resolutionData.ipAddress[0].ToString(addrStr, len) == addrStr)
{
return true;
}
return false;
}
ChipError::StorageType
pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(chip::Controller::DeviceCommissioner * devCtrl,
chip::Controller::DevicePairingDelegate_OnPairingCompleteFunct callback)
{
sPairingDelegate.SetKeyExchangeCallback(callback);
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_ScriptDevicePairingDelegate_SetCommissioningCompleteCallback(
chip::Controller::DeviceCommissioner * devCtrl, chip::Controller::DevicePairingDelegate_OnCommissioningCompleteFunct callback)
{
sPairingDelegate.SetCommissioningCompleteCallback(callback);
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_ScriptDevicePairingDelegate_SetCommissioningStatusUpdateCallback(
chip::Controller::DeviceCommissioner * devCtrl,
chip::Controller::DevicePairingDelegate_OnCommissioningStatusUpdateFunct callback)
{
sPairingDelegate.SetCommissioningStatusUpdateCallback(callback);
return CHIP_NO_ERROR.AsInteger();
}
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, SessionHandle & sessionHandle)
{
auto * self = static_cast<GetDeviceCallbacks *>(context);
auto * operationalDeviceProxy = new OperationalDeviceProxy(&exchangeMgr, sessionHandle);
self->mCallback(operationalDeviceProxy, CHIP_NO_ERROR.AsInteger());
delete self;
}
static void OnConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
{
auto * self = static_cast<GetDeviceCallbacks *>(context);
self->mCallback(nullptr, error.AsInteger());
delete self;
}
Callback::Callback<OnDeviceConnected> mOnSuccess;
Callback::Callback<OnDeviceConnectionFailure> mOnFailure;
DeviceAvailableFunc mCallback;
};
} // anonymous namespace
ChipError::StorageType pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
DeviceAvailableFunc callback)
{
VerifyOrReturnError(devCtrl != nullptr, CHIP_ERROR_INVALID_ARGUMENT.AsInteger());
auto * callbacks = new GetDeviceCallbacks(callback);
return devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure).AsInteger();
}
ChipError::StorageType pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy)
{
if (deviceProxy != nullptr)
{
delete deviceProxy;
}
return CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_GetDeviceBeingCommissioned(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
CommissioneeDeviceProxy ** proxy)
{
return devCtrl->GetDeviceBeingCommissioned(nodeId, proxy).AsInteger();
}
// This is a method called VERY seldom, just for RemoveFabric/UpdateNOC
ChipError::StorageType pychip_ExpireSessions(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId)
{
VerifyOrReturnError((devCtrl != nullptr) && (devCtrl->SessionMgr() != nullptr), CHIP_ERROR_INVALID_ARGUMENT.AsInteger());
(void) devCtrl->ReleaseOperationalDevice(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 CHIP_NO_ERROR.AsInteger();
}
ChipError::StorageType pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::DeviceCommissioner * devCtrl)
{
#if CONFIG_NETWORK_LAYER_BLE
devCtrl->CloseBleConnection();
return CHIP_NO_ERROR.AsInteger();
#else
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE.AsInteger();
#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.
}
ChipError::StorageType pychip_DeviceController_PostTaskOnChipThread(ChipThreadTaskRunnerFunct callback, void * pythonContext)
{
if (callback == nullptr || pythonContext == nullptr)
{
return CHIP_ERROR_INVALID_ARGUMENT.AsInteger();
}
PlatformMgr().ScheduleWork(callback, reinterpret_cast<intptr_t>(pythonContext));
return CHIP_NO_ERROR.AsInteger();
}