Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fabric-Admin] Add sync-device command to sync a device from another fabric #33912

Merged
merged 6 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/fabric-admin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ static_library("fabric-admin-utils") {
"${chip_root}/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp",
"commands/clusters/ModelCommand.cpp",
"commands/clusters/ModelCommand.h",
"commands/clusters/ReportCommand.cpp",
"commands/clusters/ReportCommand.h",
"commands/common/CHIPCommand.cpp",
"commands/common/CHIPCommand.h",
"commands/common/Command.cpp",
Expand Down
80 changes: 80 additions & 0 deletions examples/fabric-admin/commands/clusters/ReportCommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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.
*
*/

#include "ReportCommand.h"

#include <app/InteractionModelEngine.h>
#include <inttypes.h>

using namespace ::chip;

void ReportCommand::OnAttributeData(const app::ConcreteDataAttributePath & path, TLV::TLVReader * data,
const app::StatusIB & status)
{
CHIP_ERROR error = status.ToChipError();
if (CHIP_NO_ERROR != error)
{
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(path, status));

ChipLogError(NotSpecified, "Response Failure: %s", ErrorStr(error));
mError = error;
return;
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
mError = CHIP_ERROR_INTERNAL;
return;
}

LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data));
}

void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status)
{
if (status != nullptr)
{
CHIP_ERROR error = status->ToChipError();
if (CHIP_NO_ERROR != error)
{
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(eventHeader, *status));

ChipLogError(NotSpecified, "Response Failure: %s", ErrorStr(error));
mError = error;
return;
}
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
mError = CHIP_ERROR_INTERNAL;
return;
}

LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));

CHIP_ERROR error = DataModelLogger::LogEvent(eventHeader, data);
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
mError = error;
return;
}
}
63 changes: 2 additions & 61 deletions examples/fabric-admin/commands/clusters/ReportCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,69 +32,10 @@ class ReportCommand : public InteractionModelReports, public ModelCommand, publi

/////////// ReadClient Callback Interface /////////
void OnAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data,
const chip::app::StatusIB & status) override
{
CHIP_ERROR error = status.ToChipError();
if (CHIP_NO_ERROR != error)
{
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(path, status));

ChipLogError(NotSpecified, "Response Failure: %s", chip::ErrorStr(error));
mError = error;
return;
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
mError = CHIP_ERROR_INTERNAL;
return;
}

LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data));

error = DataModelLogger::LogAttribute(path, data);
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
mError = error;
return;
}
}
const chip::app::StatusIB & status) override;

void OnEventData(const chip::app::EventHeader & eventHeader, chip::TLV::TLVReader * data,
const chip::app::StatusIB * status) override
{
if (status != nullptr)
{
CHIP_ERROR error = status->ToChipError();
if (CHIP_NO_ERROR != error)
{
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(eventHeader, *status));

ChipLogError(NotSpecified, "Response Failure: %s", chip::ErrorStr(error));
mError = error;
return;
}
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
mError = CHIP_ERROR_INTERNAL;
return;
}

LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));

CHIP_ERROR error = DataModelLogger::LogEvent(eventHeader, data);
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
mError = error;
return;
}
}
const chip::app::StatusIB * status) override;

void OnError(CHIP_ERROR error) override
{
Expand Down
21 changes: 21 additions & 0 deletions examples/fabric-admin/commands/common/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ static void DetectAndLogMismatchedDoubleQuotes(int argc, char ** argv)

} // namespace

// Define the static member
Commands Commands::sInstance;

void Commands::Register(const char * commandSetName, commands_list commandsList, const char * helpText, bool isCluster)
{
VerifyOrDieWithMsg(isCluster || helpText != nullptr, NotSpecified, "Non-cluster command sets must have help text");
Expand Down Expand Up @@ -337,6 +340,7 @@ Commands::CommandSetMap::iterator Commands::GetCommandSet(std::string commandSet
{
std::string key(commandSet.first);
std::transform(key.begin(), key.end(), key.begin(), ::tolower);

if (key.compare(commandSetName) == 0)
{
return mCommandSets.find(commandSet.first);
Expand All @@ -346,6 +350,23 @@ Commands::CommandSetMap::iterator Commands::GetCommandSet(std::string commandSet
return mCommandSets.end();
}

Command * Commands::GetCommandByName(std::string commandSetName, std::string commandName)
{
auto commandSetIter = GetCommandSet(commandSetName);
if (commandSetIter != mCommandSets.end())
{
auto & commandList = commandSetIter->second.commands;
for (auto & command : commandList)
{
if (command->GetName() == commandName)
{
return command.get();
}
}
}
return nullptr;
}

Command * Commands::GetCommand(CommandsVector & commands, std::string commandName)
{
for (auto & command : commands)
Expand Down
16 changes: 16 additions & 0 deletions examples/fabric-admin/commands/common/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Commands
int Run(int argc, char ** argv);
int RunInteractive(const char * command, const chip::Optional<char *> & storageDirectory, bool advertiseOperational);

Command * GetCommandByName(std::string commandSetName, std::string commandName);

private:
struct CommandSet
{
Expand Down Expand Up @@ -87,4 +89,18 @@ class Commands
#ifdef CONFIG_USE_LOCAL_STORAGE
PersistentStorage mStorage;
#endif // CONFIG_USE_LOCAL_STORAGE

friend Commands & CommandMgr();
static Commands sInstance;
};

/**
* Returns the public interface of the CommandManager singleton object.
*
* Applications should use this to access features of the CommandManager
* object.
*/
inline Commands & CommandMgr()
{
return Commands::sInstance;
}
1 change: 1 addition & 0 deletions examples/fabric-admin/commands/fabric-sync/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ void registerCommandsFabricSync(Commands & commands, CredentialIssuerCommands *

commands_list clusterCommands = {
make_unique<FabricSyncAddDeviceCommand>(credsIssuerConfig),
make_unique<FabricSyncDeviceCommand>(credsIssuerConfig),
};

commands.RegisterCommandSet(clusterName, clusterCommands, "Commands for fabric synchronization.");
Expand Down
95 changes: 95 additions & 0 deletions examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#include "FabricSyncCommand.h"
#include <commands/common/RemoteDataModelLogger.h>
#include <commands/interactive/InteractiveCommands.h>
#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <thread>
#include <unistd.h>

Expand All @@ -27,6 +29,16 @@

using namespace ::chip;

namespace {

// Constants
constexpr uint32_t kCommissionPrepareTimeMs = 500;
constexpr uint16_t kMaxManaulCodeLength = 21;
constexpr uint16_t kSubscribeMinInterval = 0;
constexpr uint16_t kSubscribeMaxInterval = 60;

} // namespace

CHIP_ERROR FabricSyncAddDeviceCommand::RunCommand(NodeId remoteId)
{
#if defined(PW_RPC_ENABLED)
Expand All @@ -36,3 +48,86 @@ CHIP_ERROR FabricSyncAddDeviceCommand::RunCommand(NodeId remoteId)
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif
}

void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ERROR err, chip::SetupPayload payload)
{
if (err == CHIP_NO_ERROR)
{
char payloadBuffer[kMaxManaulCodeLength + 1];
MutableCharSpan manualCode(payloadBuffer);
CHIP_ERROR error = ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualCode);
if (error == CHIP_NO_ERROR)
{
char command[kMaxCommandSize];
NodeId nodeId = 2; // TODO: (Issue #33947) need to switch to dynamically assigned ID
snprintf(command, sizeof(command), "pairing code %ld %s", nodeId, payloadBuffer);

PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "code"));

if (pairingCommand == nullptr)
{
ChipLogError(NotSpecified, "Pairing code command is not available");
return;
}

pairingCommand->RegisterCommissioningDelegate(this);
mAssignedNodeId = nodeId;

usleep(kCommissionPrepareTimeMs * 1000);

PushCommand(command);
}
else
{
ChipLogError(NotSpecified, "Unable to generate manual code for setup payload: %" CHIP_ERROR_FORMAT, error.Format());
}
}
else
{
ChipLogError(NotSpecified,
"Failed to open synced device (0x:" ChipLogFormatX64 ") commissioning window: %" CHIP_ERROR_FORMAT,
ChipLogValueX64(deviceId), err.Format());
}
}

void FabricSyncDeviceCommand::OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR err)
{
if (mAssignedNodeId != deviceId)
{
// Ignore if the deviceId does not match the mAssignedNodeId.
// This scenario should not occur because no other device should be commissioned during the fabric sync process.
return;
yufengwangca marked this conversation as resolved.
Show resolved Hide resolved
}

if (err == CHIP_NO_ERROR)
{
// TODO: (Issue #33947) Add Synced Device to device manager
}
else
{
ChipLogError(NotSpecified, "Failed to pair synced device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT,
ChipLogValueX64(deviceId), err.Format());
}
}

CHIP_ERROR FabricSyncDeviceCommand::RunCommand(EndpointId remoteId)
{
char command[kMaxCommandSize];
NodeId bridgeNodeId = 1; // TODO: (Issue #33947) need to switch to configured ID
snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d", bridgeNodeId, remoteId,
kEnhancedCommissioningMethod, kWindowTimeout, kIteration, kDiscriminator);

OpenCommissioningWindowCommand * openCommand =
static_cast<OpenCommissioningWindowCommand *>(CommandMgr().GetCommandByName("pairing", "open-commissioning-window"));

if (openCommand == nullptr)
{
return CHIP_ERROR_UNINITIALIZED;
}

openCommand->RegisterDelegate(this);

PushCommand(command);

return CHIP_NO_ERROR;
}
31 changes: 31 additions & 0 deletions examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
#pragma once

#include <commands/common/CHIPCommand.h>
#include <commands/pairing/OpenCommissioningWindowCommand.h>
#include <commands/pairing/PairingCommand.h>

constexpr uint16_t kMaxCommandSize = 64;
constexpr uint16_t kDiscriminator = 3840;
constexpr uint16_t kWindowTimeout = 300;
constexpr uint16_t kIteration = 1000;
constexpr uint8_t kEnhancedCommissioningMethod = 1;

class FabricSyncAddDeviceCommand : public CHIPCommand
{
Expand All @@ -38,3 +46,26 @@ class FabricSyncAddDeviceCommand : public CHIPCommand

CHIP_ERROR RunCommand(NodeId remoteId);
};

class FabricSyncDeviceCommand : public CHIPCommand, public CommissioningWindowDelegate, public CommissioningDelegate
{
public:
FabricSyncDeviceCommand(CredentialIssuerCommands * credIssuerCommands) : CHIPCommand("sync-device", credIssuerCommands)
{
AddArgument("endpointid", 0, UINT16_MAX, &mRemoteEndpointId);
}

void OnCommissioningWindowOpened(NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload) override;
void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR err) override;

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override { return RunCommand(mRemoteEndpointId); }

chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(1); }

private:
chip::EndpointId mRemoteEndpointId = chip::kInvalidEndpointId;
chip::NodeId mAssignedNodeId = chip::kUndefinedNodeId;

CHIP_ERROR RunCommand(chip::EndpointId remoteId);
};
Loading
Loading