forked from project-chip/connectedhomeip
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Fabric-Admin] Implement CommissionerControl command APIs (project-ch…
…ip#36057) * Implement CommissionerControl command APIs * Address review comment * Add missing change * Init mCommissionerControl in SetRemoteBridgeNodeId * Remove redundant optimization * Fix merge conflict * Call OnDone on failure * Fix build error after merge
- Loading branch information
1 parent
ede20d6
commit 851c05b
Showing
6 changed files
with
304 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
141 changes: 141 additions & 0 deletions
141
examples/fabric-admin/device_manager/CommissionerControl.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
#include "CommissionerControl.h" | ||
#include <device_manager/DeviceManager.h> | ||
|
||
using namespace ::chip; | ||
|
||
void CommissionerControl::Init(Controller::DeviceCommissioner & commissioner, NodeId nodeId, EndpointId endpointId) | ||
{ | ||
// Ensure that mCommissioner is not already initialized | ||
VerifyOrDie(mCommissioner == nullptr); | ||
|
||
ChipLogProgress(NotSpecified, "Initilize CommissionerControl"); | ||
mCommissioner = &commissioner; | ||
mDestinationId = nodeId; | ||
mEndpointId = endpointId; | ||
} | ||
|
||
CHIP_ERROR CommissionerControl::RequestCommissioningApproval(uint64_t requestId, uint16_t vendorId, uint16_t productId, | ||
Optional<CharSpan> label) | ||
{ | ||
VerifyOrReturnError(mCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE); | ||
|
||
ChipLogProgress(NotSpecified, "Sending RequestCommissioningApproval to node " ChipLogFormatX64, | ||
ChipLogValueX64(mDestinationId)); | ||
|
||
mRequestCommissioningApproval.requestID = requestId; | ||
mRequestCommissioningApproval.vendorID = static_cast<VendorId>(vendorId); | ||
mRequestCommissioningApproval.productID = productId; | ||
|
||
if (label.HasValue()) | ||
{ | ||
VerifyOrReturnError(label.Value().size() <= kMaxDeviceLabelLength, CHIP_ERROR_BUFFER_TOO_SMALL); | ||
memcpy(mLabelBuffer, label.Value().data(), label.Value().size()); | ||
mRequestCommissioningApproval.label = Optional<Span<const char>>(CharSpan(mLabelBuffer, label.Value().size())); | ||
} | ||
|
||
mCommandType = CommandType::kRequestCommissioningApproval; | ||
return mCommissioner->GetConnectedDevice(mDestinationId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback); | ||
} | ||
|
||
CHIP_ERROR CommissionerControl::CommissionNode(uint64_t requestId, uint16_t responseTimeoutSeconds) | ||
{ | ||
VerifyOrReturnError(mCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE); | ||
|
||
ChipLogProgress(NotSpecified, "Sending CommissionNode to node " ChipLogFormatX64, ChipLogValueX64(mDestinationId)); | ||
|
||
mCommissionNode.requestID = requestId; | ||
mCommissionNode.responseTimeoutSeconds = responseTimeoutSeconds; | ||
|
||
mCommandType = CommandType::kCommissionNode; | ||
return mCommissioner->GetConnectedDevice(mDestinationId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback); | ||
} | ||
|
||
void CommissionerControl::OnResponse(app::CommandSender * client, const app::ConcreteCommandPath & path, | ||
const app::StatusIB & status, TLV::TLVReader * data) | ||
{ | ||
ChipLogProgress(NotSpecified, "CommissionerControl: OnResponse."); | ||
|
||
CHIP_ERROR error = status.ToChipError(); | ||
if (CHIP_NO_ERROR != error) | ||
{ | ||
ChipLogError(NotSpecified, "Response Failure: %s", ErrorStr(error)); | ||
return; | ||
} | ||
|
||
if (data != nullptr) | ||
{ | ||
DeviceMgr().HandleCommandResponse(path, *data); | ||
} | ||
} | ||
|
||
void CommissionerControl::OnError(const app::CommandSender * client, CHIP_ERROR error) | ||
{ | ||
// Handle the error, then reset mCommandSender | ||
ChipLogProgress(NotSpecified, "CommissionerControl: OnError: Error: %s", ErrorStr(error)); | ||
} | ||
|
||
void CommissionerControl::OnDone(app::CommandSender * client) | ||
{ | ||
ChipLogProgress(NotSpecified, "CommissionerControl: OnDone."); | ||
|
||
switch (mCommandType) | ||
{ | ||
case CommandType::kRequestCommissioningApproval: | ||
ChipLogProgress(NotSpecified, "CommissionerControl: Command RequestCommissioningApproval has been successfully processed."); | ||
break; | ||
|
||
case CommandType::kCommissionNode: | ||
ChipLogProgress(NotSpecified, "CommissionerControl: Command CommissionNode has been successfully processed."); | ||
break; | ||
|
||
default: | ||
ChipLogError(NotSpecified, "CommissionerControl: Unknown or unhandled command type in OnDone."); | ||
break; | ||
} | ||
|
||
// Reset command type to undefined after processing is done | ||
mCommandType = CommandType::kUndefined; | ||
|
||
// Ensure that mCommandSender is cleaned up after it is done | ||
mCommandSender.reset(); | ||
} | ||
|
||
CHIP_ERROR CommissionerControl::SendCommandForType(CommandType commandType, DeviceProxy * device) | ||
{ | ||
switch (commandType) | ||
{ | ||
case CommandType::kRequestCommissioningApproval: | ||
return SendCommand(device, mEndpointId, app::Clusters::CommissionerControl::Id, | ||
app::Clusters::CommissionerControl::Commands::RequestCommissioningApproval::Id, | ||
mRequestCommissioningApproval); | ||
case CommandType::kCommissionNode: | ||
return SendCommand(device, mEndpointId, app::Clusters::CommissionerControl::Id, | ||
app::Clusters::CommissionerControl::Commands::CommissionNode::Id, mCommissionNode); | ||
default: | ||
return CHIP_ERROR_INVALID_ARGUMENT; | ||
} | ||
} | ||
|
||
void CommissionerControl::OnDeviceConnectedFn(void * context, Messaging::ExchangeManager & exchangeMgr, | ||
const SessionHandle & sessionHandle) | ||
{ | ||
CommissionerControl * self = reinterpret_cast<CommissionerControl *>(context); | ||
VerifyOrReturn(self != nullptr, ChipLogError(NotSpecified, "OnDeviceConnectedFn: context is null")); | ||
|
||
OperationalDeviceProxy device(&exchangeMgr, sessionHandle); | ||
|
||
CHIP_ERROR err = self->SendCommandForType(self->mCommandType, &device); | ||
if (err != CHIP_NO_ERROR) | ||
{ | ||
ChipLogError(NotSpecified, "Failed to send CommissionerControl command."); | ||
self->OnDone(nullptr); | ||
} | ||
} | ||
|
||
void CommissionerControl::OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR err) | ||
{ | ||
LogErrorOnFailure(err); | ||
CommissionerControl * self = reinterpret_cast<CommissionerControl *>(context); | ||
VerifyOrReturn(self != nullptr, ChipLogError(NotSpecified, "OnDeviceConnectedFn: context is null")); | ||
self->OnDone(nullptr); | ||
} |
125 changes: 125 additions & 0 deletions
125
examples/fabric-admin/device_manager/CommissionerControl.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/* | ||
* 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. | ||
* | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <app/CommandSender.h> | ||
#include <controller/CHIPDeviceController.h> | ||
|
||
/** | ||
* @class CommissionerControl | ||
* @brief This class handles sending CHIP commands related to commissioning, including sending | ||
* commissioning approval requests and commissioning nodes. | ||
* | ||
* The class acts as a command sender and implements the `chip::app::CommandSender::Callback` interface | ||
* to handle responses, errors, and completion events for the commands it sends. It relies on external | ||
* CCTRL delegate and server mechanisms to manage the overall protocol and state transitions, including | ||
* processing the CommissioningRequestResult and invoking CommissionNode. | ||
*/ | ||
class CommissionerControl : public chip::app::CommandSender::Callback | ||
{ | ||
public: | ||
CommissionerControl() : | ||
mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this) | ||
{} | ||
|
||
/** | ||
* @brief Initializes the CommissionerControl with a DeviceCommissioner, NodeId, and EndpointId. | ||
* | ||
* @param commissioner The DeviceCommissioner to use for the commissioning process. | ||
* @param nodeId The node ID of the remote fabric bridge. | ||
* @param endpointId The endpoint on which to send CommissionerControl commands. | ||
*/ | ||
void Init(chip::Controller::DeviceCommissioner & commissioner, chip::NodeId nodeId, chip::EndpointId endpointId); | ||
|
||
/** | ||
* @brief Sends a RequestCommissioningApproval command to the device. | ||
* | ||
* @param requestId The unique request ID. | ||
* @param vendorId The vendor ID of the device. | ||
* @param productId The product ID of the device. | ||
* @param label Optional label for the device. | ||
* @return CHIP_ERROR CHIP_NO_ERROR on success, or an appropriate error code on failure. | ||
*/ | ||
CHIP_ERROR RequestCommissioningApproval(uint64_t requestId, uint16_t vendorId, uint16_t productId, | ||
chip::Optional<chip::CharSpan> label); | ||
/** | ||
* @brief Sends a CommissionNode command to the device. | ||
* | ||
* @param requestId The unique request ID. | ||
* @param responseTimeoutSeconds Timeout for the response in seconds. | ||
* @return CHIP_ERROR CHIP_NO_ERROR on success, or an appropriate error code on failure. | ||
*/ | ||
CHIP_ERROR CommissionNode(uint64_t requestId, uint16_t responseTimeoutSeconds); | ||
|
||
/////////// CommandSender Callback Interface ///////// | ||
virtual void OnResponse(chip::app::CommandSender * client, const chip::app::ConcreteCommandPath & path, | ||
const chip::app::StatusIB & status, chip::TLV::TLVReader * data) override; | ||
|
||
virtual void OnError(const chip::app::CommandSender * client, CHIP_ERROR error) override; | ||
|
||
virtual void OnDone(chip::app::CommandSender * client) override; | ||
|
||
private: | ||
static constexpr uint16_t kMaxDeviceLabelLength = 64; | ||
|
||
enum class CommandType : uint8_t | ||
{ | ||
kUndefined = 0, | ||
kRequestCommissioningApproval = 1, | ||
kCommissionNode = 2, | ||
}; | ||
|
||
template <class T> | ||
CHIP_ERROR SendCommand(chip::DeviceProxy * device, chip::EndpointId endpointId, chip::ClusterId clusterId, | ||
chip::CommandId commandId, const T & value) | ||
{ | ||
chip::app::CommandPathParams commandPath = { endpointId, clusterId, commandId, | ||
(chip::app::CommandPathFlags::kEndpointIdValid) }; | ||
mCommandSender = std::make_unique<chip::app::CommandSender>(this, device->GetExchangeManager(), false, false, | ||
device->GetSecureSession().Value()->AllowsLargePayload()); | ||
|
||
VerifyOrReturnError(mCommandSender != nullptr, CHIP_ERROR_NO_MEMORY); | ||
|
||
chip::app::CommandSender::AddRequestDataParameters addRequestDataParams(chip::NullOptional); | ||
ReturnErrorOnFailure(mCommandSender->AddRequestData(commandPath, value, addRequestDataParams)); | ||
ReturnErrorOnFailure(mCommandSender->SendCommandRequest(device->GetSecureSession().Value())); | ||
|
||
return CHIP_NO_ERROR; | ||
} | ||
|
||
CHIP_ERROR SendCommandForType(CommandType commandType, chip::DeviceProxy * device); | ||
|
||
static void OnDeviceConnectedFn(void * context, chip::Messaging::ExchangeManager & exchangeMgr, | ||
const chip::SessionHandle & sessionHandle); | ||
static void OnDeviceConnectionFailureFn(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR error); | ||
|
||
// Private data members | ||
chip::Controller::DeviceCommissioner * mCommissioner = nullptr; | ||
std::unique_ptr<chip::app::CommandSender> mCommandSender; | ||
chip::NodeId mDestinationId = chip::kUndefinedNodeId; | ||
chip::EndpointId mEndpointId = chip::kRootEndpointId; | ||
CommandType mCommandType = CommandType::kUndefined; | ||
char mLabelBuffer[kMaxDeviceLabelLength]; | ||
|
||
chip::Callback::Callback<chip::OnDeviceConnected> mOnDeviceConnectedCallback; | ||
chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback; | ||
|
||
chip::app::Clusters::CommissionerControl::Commands::RequestCommissioningApproval::Type mRequestCommissioningApproval; | ||
chip::app::Clusters::CommissionerControl::Commands::CommissionNode::Type mCommissionNode; | ||
}; |
Oops, something went wrong.