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 3 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
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
93 changes: 93 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 = 11;
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,84 @@ 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: 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)
{
return;
}

if (err == CHIP_NO_ERROR)
{
// TODO: AddSyncedDevice
}
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: 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;
}
33 changes: 33 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,16 @@
#pragma once

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

constexpr uint32_t kSetupPinCode = 20202021;
constexpr uint16_t kMaxCommandSize = 64;
constexpr uint16_t kDiscriminator = 3840;
constexpr uint16_t kWindowTimeout = 300;
constexpr uint16_t kIteration = 1000;
constexpr uint16_t kAggragatorEndpointId = 1;
constexpr uint8_t kEnhancedCommissioningMethod = 1;

class FabricSyncAddDeviceCommand : public CHIPCommand
{
Expand All @@ -38,3 +48,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);
};
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ char * InteractiveStartCommand::GetCommand(char * command)
command = new char[cmd.length() + 1];
strcpy(command, cmd.c_str());

ChipLogProgress(NotSpecified, "GetCommand: %s", command);

// Do not save empty lines
if (command != nullptr && *command)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,14 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand()
void OpenCommissioningWindowCommand::OnOpenCommissioningWindowResponse(void * context, NodeId remoteId, CHIP_ERROR err,
chip::SetupPayload payload)
{
LogErrorOnFailure(err);
OpenCommissioningWindowCommand * self = static_cast<OpenCommissioningWindowCommand *>(context);
if (self->mDelegate)
{
self->mDelegate->OnCommissioningWindowOpened(remoteId, err, payload);
self->UnregisterDelegate();
}

LogErrorOnFailure(err);
OnOpenBasicCommissioningWindowResponse(context, remoteId, err);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
#include <controller/CommissioningWindowOpener.h>
#include <lib/support/CHIPMem.h>

class CommissioningWindowDelegate
{
public:
virtual void OnCommissioningWindowOpened(chip::NodeId deviceId, CHIP_ERROR err, chip::SetupPayload payload) = 0;
virtual ~CommissioningWindowDelegate() = default;
};

class OpenCommissioningWindowCommand : public CHIPCommand
{
public:
Expand All @@ -31,6 +38,7 @@ class OpenCommissioningWindowCommand : public CHIPCommand
mOnOpenBasicCommissioningWindowCallback(OnOpenBasicCommissioningWindowResponse, this)
{
AddArgument("node-id", 0, UINT64_MAX, &mNodeId, "Node to send command to.");
AddArgument("endpoint-id", 0, UINT16_MAX, &mEndpointId, "Endpoint to send command to.");
AddArgument("option", 0, 2, &mCommissioningWindowOption,
"1 to use Enhanced Commissioning Method.\n 0 to use Basic Commissioning Method.");
AddArgument("window-timeout", 0, UINT16_MAX, &mCommissioningWindowTimeout,
Expand All @@ -41,6 +49,9 @@ class OpenCommissioningWindowCommand : public CHIPCommand
AddArgument("timeout", 0, UINT16_MAX, &mTimeout, "Time, in seconds, before this command is considered to have timed out.");
}

void RegisterDelegate(CommissioningWindowDelegate * delegate) { mDelegate = delegate; }
void UnregisterDelegate() { mDelegate = nullptr; }

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override;

Expand All @@ -50,7 +61,9 @@ class OpenCommissioningWindowCommand : public CHIPCommand

private:
NodeId mNodeId;
chip::EndpointId mEndpointId;
chip::Controller::CommissioningWindowOpener::CommissioningWindowOption mCommissioningWindowOption;
CommissioningWindowDelegate * mDelegate = nullptr;
uint16_t mCommissioningWindowTimeout;
uint32_t mIteration;
uint16_t mDiscriminator;
Expand Down
31 changes: 29 additions & 2 deletions examples/fabric-admin/commands/pairing/PairingCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@

#include <string>

#if defined(PW_RPC_ENABLED)
#include <rpc/RpcClient.h>
#endif

using namespace ::chip;
using namespace ::chip::Controller;

Expand Down Expand Up @@ -389,7 +393,12 @@ void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(NotSpecified, "Device commissioning completed with success");
// print to console
fprintf(stderr, "New device with Node ID: 0x%lx has been successfully added.\n", nodeId);

#if defined(PW_RPC_ENABLED)
AddSynchronizedDevice(nodeId);
#endif
}
else
{
Expand All @@ -406,6 +415,12 @@ void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
ChipLogProgress(NotSpecified, "Device commissioning Failure: %s", ErrorStr(err));
}

if (mCommissioningDelegate)
{
mCommissioningDelegate->OnCommissioningComplete(nodeId, err);
this->UnregisterCommissioningDelegate();
}

SetCommandExitStatus(err);
}

Expand Down Expand Up @@ -520,13 +535,25 @@ void PairingCommand::OnCurrentFabricRemove(void * context, NodeId nodeId, CHIP_E

if (err == CHIP_NO_ERROR)
{
ChipLogProgress(NotSpecified, "Device unpair completed with success: " ChipLogFormatX64, ChipLogValueX64(nodeId));
// print to console
fprintf(stderr, "Device with Node ID: 0x%lx has been successfully removed.\n", nodeId);

#if defined(PW_RPC_ENABLED)
RemoveSynchronizedDevice(nodeId);
#endif
}
else
{
ChipLogProgress(NotSpecified, "Device unpair Failure: " ChipLogFormatX64 " %s", ChipLogValueX64(nodeId), ErrorStr(err));
}

PairingDelegate * pairingDelegate = command->GetPairingDelegate();
if (pairingDelegate)
{
pairingDelegate->OnDeviceRemoved(nodeId, err);
command->UnregisterPairingDelegate();
}

command->SetCommandExitStatus(err);
}

Expand Down
Loading
Loading