Skip to content

Commit 4cdce52

Browse files
authored
[Fabric-Admin] Add sync-device command to sync a device from another fabric (#33912)
* Add sync-device command in fabricsync command sets * Update API documents * Address review comments * Adjust the legth of kMaxManaulCodeLength and move some long functions from .h to .cpp * Address new review comments * Remove un-used defines
1 parent 8ba371a commit 4cdce52

16 files changed

+328
-71
lines changed

examples/fabric-admin/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ static_library("fabric-admin-utils") {
6161
"${chip_root}/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp",
6262
"commands/clusters/ModelCommand.cpp",
6363
"commands/clusters/ModelCommand.h",
64+
"commands/clusters/ReportCommand.cpp",
65+
"commands/clusters/ReportCommand.h",
6466
"commands/common/CHIPCommand.cpp",
6567
"commands/common/CHIPCommand.h",
6668
"commands/common/Command.cpp",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (c) 2024 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#include "ReportCommand.h"
20+
21+
#include <app/InteractionModelEngine.h>
22+
#include <inttypes.h>
23+
24+
using namespace ::chip;
25+
26+
void ReportCommand::OnAttributeData(const app::ConcreteDataAttributePath & path, TLV::TLVReader * data,
27+
const app::StatusIB & status)
28+
{
29+
CHIP_ERROR error = status.ToChipError();
30+
if (CHIP_NO_ERROR != error)
31+
{
32+
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(path, status));
33+
34+
ChipLogError(NotSpecified, "Response Failure: %s", ErrorStr(error));
35+
mError = error;
36+
return;
37+
}
38+
39+
if (data == nullptr)
40+
{
41+
ChipLogError(NotSpecified, "Response Failure: No Data");
42+
mError = CHIP_ERROR_INTERNAL;
43+
return;
44+
}
45+
46+
LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data));
47+
}
48+
49+
void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status)
50+
{
51+
if (status != nullptr)
52+
{
53+
CHIP_ERROR error = status->ToChipError();
54+
if (CHIP_NO_ERROR != error)
55+
{
56+
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(eventHeader, *status));
57+
58+
ChipLogError(NotSpecified, "Response Failure: %s", ErrorStr(error));
59+
mError = error;
60+
return;
61+
}
62+
}
63+
64+
if (data == nullptr)
65+
{
66+
ChipLogError(NotSpecified, "Response Failure: No Data");
67+
mError = CHIP_ERROR_INTERNAL;
68+
return;
69+
}
70+
71+
LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));
72+
73+
CHIP_ERROR error = DataModelLogger::LogEvent(eventHeader, data);
74+
if (CHIP_NO_ERROR != error)
75+
{
76+
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
77+
mError = error;
78+
return;
79+
}
80+
}

examples/fabric-admin/commands/clusters/ReportCommand.h

+2-61
Original file line numberDiff line numberDiff line change
@@ -32,69 +32,10 @@ class ReportCommand : public InteractionModelReports, public ModelCommand, publi
3232

3333
/////////// ReadClient Callback Interface /////////
3434
void OnAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data,
35-
const chip::app::StatusIB & status) override
36-
{
37-
CHIP_ERROR error = status.ToChipError();
38-
if (CHIP_NO_ERROR != error)
39-
{
40-
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(path, status));
41-
42-
ChipLogError(NotSpecified, "Response Failure: %s", chip::ErrorStr(error));
43-
mError = error;
44-
return;
45-
}
46-
47-
if (data == nullptr)
48-
{
49-
ChipLogError(NotSpecified, "Response Failure: No Data");
50-
mError = CHIP_ERROR_INTERNAL;
51-
return;
52-
}
53-
54-
LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data));
55-
56-
error = DataModelLogger::LogAttribute(path, data);
57-
if (CHIP_NO_ERROR != error)
58-
{
59-
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
60-
mError = error;
61-
return;
62-
}
63-
}
35+
const chip::app::StatusIB & status) override;
6436

6537
void OnEventData(const chip::app::EventHeader & eventHeader, chip::TLV::TLVReader * data,
66-
const chip::app::StatusIB * status) override
67-
{
68-
if (status != nullptr)
69-
{
70-
CHIP_ERROR error = status->ToChipError();
71-
if (CHIP_NO_ERROR != error)
72-
{
73-
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(eventHeader, *status));
74-
75-
ChipLogError(NotSpecified, "Response Failure: %s", chip::ErrorStr(error));
76-
mError = error;
77-
return;
78-
}
79-
}
80-
81-
if (data == nullptr)
82-
{
83-
ChipLogError(NotSpecified, "Response Failure: No Data");
84-
mError = CHIP_ERROR_INTERNAL;
85-
return;
86-
}
87-
88-
LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));
89-
90-
CHIP_ERROR error = DataModelLogger::LogEvent(eventHeader, data);
91-
if (CHIP_NO_ERROR != error)
92-
{
93-
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
94-
mError = error;
95-
return;
96-
}
97-
}
38+
const chip::app::StatusIB * status) override;
9839

9940
void OnError(CHIP_ERROR error) override
10041
{

examples/fabric-admin/commands/common/Commands.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ static void DetectAndLogMismatchedDoubleQuotes(int argc, char ** argv)
150150

151151
} // namespace
152152

153+
// Define the static member
154+
Commands Commands::sInstance;
155+
153156
void Commands::Register(const char * commandSetName, commands_list commandsList, const char * helpText, bool isCluster)
154157
{
155158
VerifyOrDieWithMsg(isCluster || helpText != nullptr, NotSpecified, "Non-cluster command sets must have help text");
@@ -337,6 +340,7 @@ Commands::CommandSetMap::iterator Commands::GetCommandSet(std::string commandSet
337340
{
338341
std::string key(commandSet.first);
339342
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
343+
340344
if (key.compare(commandSetName) == 0)
341345
{
342346
return mCommandSets.find(commandSet.first);
@@ -346,6 +350,23 @@ Commands::CommandSetMap::iterator Commands::GetCommandSet(std::string commandSet
346350
return mCommandSets.end();
347351
}
348352

353+
Command * Commands::GetCommandByName(std::string commandSetName, std::string commandName)
354+
{
355+
auto commandSetIter = GetCommandSet(commandSetName);
356+
if (commandSetIter != mCommandSets.end())
357+
{
358+
auto & commandList = commandSetIter->second.commands;
359+
for (auto & command : commandList)
360+
{
361+
if (command->GetName() == commandName)
362+
{
363+
return command.get();
364+
}
365+
}
366+
}
367+
return nullptr;
368+
}
369+
349370
Command * Commands::GetCommand(CommandsVector & commands, std::string commandName)
350371
{
351372
for (auto & command : commands)

examples/fabric-admin/commands/common/Commands.h

+16
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ class Commands
4545
int Run(int argc, char ** argv);
4646
int RunInteractive(const char * command, const chip::Optional<char *> & storageDirectory, bool advertiseOperational);
4747

48+
Command * GetCommandByName(std::string commandSetName, std::string commandName);
49+
4850
private:
4951
struct CommandSet
5052
{
@@ -87,4 +89,18 @@ class Commands
8789
#ifdef CONFIG_USE_LOCAL_STORAGE
8890
PersistentStorage mStorage;
8991
#endif // CONFIG_USE_LOCAL_STORAGE
92+
93+
friend Commands & CommandMgr();
94+
static Commands sInstance;
9095
};
96+
97+
/**
98+
* Returns the public interface of the CommandManager singleton object.
99+
*
100+
* Applications should use this to access features of the CommandManager
101+
* object.
102+
*/
103+
inline Commands & CommandMgr()
104+
{
105+
return Commands::sInstance;
106+
}

examples/fabric-admin/commands/fabric-sync/Commands.h

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ void registerCommandsFabricSync(Commands & commands, CredentialIssuerCommands *
2727

2828
commands_list clusterCommands = {
2929
make_unique<FabricSyncAddDeviceCommand>(credsIssuerConfig),
30+
make_unique<FabricSyncDeviceCommand>(credsIssuerConfig),
3031
};
3132

3233
commands.RegisterCommandSet(clusterName, clusterCommands, "Commands for fabric synchronization.");

examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp

+95
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
#include "FabricSyncCommand.h"
2020
#include <commands/common/RemoteDataModelLogger.h>
21+
#include <commands/interactive/InteractiveCommands.h>
22+
#include <setup_payload/ManualSetupPayloadGenerator.h>
2123
#include <thread>
2224
#include <unistd.h>
2325

@@ -27,6 +29,16 @@
2729

2830
using namespace ::chip;
2931

32+
namespace {
33+
34+
// Constants
35+
constexpr uint32_t kCommissionPrepareTimeMs = 500;
36+
constexpr uint16_t kMaxManaulCodeLength = 21;
37+
constexpr uint16_t kSubscribeMinInterval = 0;
38+
constexpr uint16_t kSubscribeMaxInterval = 60;
39+
40+
} // namespace
41+
3042
CHIP_ERROR FabricSyncAddDeviceCommand::RunCommand(NodeId remoteId)
3143
{
3244
#if defined(PW_RPC_ENABLED)
@@ -36,3 +48,86 @@ CHIP_ERROR FabricSyncAddDeviceCommand::RunCommand(NodeId remoteId)
3648
return CHIP_ERROR_NOT_IMPLEMENTED;
3749
#endif
3850
}
51+
52+
void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ERROR err, chip::SetupPayload payload)
53+
{
54+
if (err == CHIP_NO_ERROR)
55+
{
56+
char payloadBuffer[kMaxManaulCodeLength + 1];
57+
MutableCharSpan manualCode(payloadBuffer);
58+
CHIP_ERROR error = ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualCode);
59+
if (error == CHIP_NO_ERROR)
60+
{
61+
char command[kMaxCommandSize];
62+
NodeId nodeId = 2; // TODO: (Issue #33947) need to switch to dynamically assigned ID
63+
snprintf(command, sizeof(command), "pairing code %ld %s", nodeId, payloadBuffer);
64+
65+
PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "code"));
66+
67+
if (pairingCommand == nullptr)
68+
{
69+
ChipLogError(NotSpecified, "Pairing code command is not available");
70+
return;
71+
}
72+
73+
pairingCommand->RegisterCommissioningDelegate(this);
74+
mAssignedNodeId = nodeId;
75+
76+
usleep(kCommissionPrepareTimeMs * 1000);
77+
78+
PushCommand(command);
79+
}
80+
else
81+
{
82+
ChipLogError(NotSpecified, "Unable to generate manual code for setup payload: %" CHIP_ERROR_FORMAT, error.Format());
83+
}
84+
}
85+
else
86+
{
87+
ChipLogError(NotSpecified,
88+
"Failed to open synced device (0x:" ChipLogFormatX64 ") commissioning window: %" CHIP_ERROR_FORMAT,
89+
ChipLogValueX64(deviceId), err.Format());
90+
}
91+
}
92+
93+
void FabricSyncDeviceCommand::OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR err)
94+
{
95+
if (mAssignedNodeId != deviceId)
96+
{
97+
// Ignore if the deviceId does not match the mAssignedNodeId.
98+
// This scenario should not occur because no other device should be commissioned during the fabric sync process.
99+
return;
100+
}
101+
102+
if (err == CHIP_NO_ERROR)
103+
{
104+
// TODO: (Issue #33947) Add Synced Device to device manager
105+
}
106+
else
107+
{
108+
ChipLogError(NotSpecified, "Failed to pair synced device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT,
109+
ChipLogValueX64(deviceId), err.Format());
110+
}
111+
}
112+
113+
CHIP_ERROR FabricSyncDeviceCommand::RunCommand(EndpointId remoteId)
114+
{
115+
char command[kMaxCommandSize];
116+
NodeId bridgeNodeId = 1; // TODO: (Issue #33947) need to switch to configured ID
117+
snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d", bridgeNodeId, remoteId,
118+
kEnhancedCommissioningMethod, kWindowTimeout, kIteration, kDiscriminator);
119+
120+
OpenCommissioningWindowCommand * openCommand =
121+
static_cast<OpenCommissioningWindowCommand *>(CommandMgr().GetCommandByName("pairing", "open-commissioning-window"));
122+
123+
if (openCommand == nullptr)
124+
{
125+
return CHIP_ERROR_UNINITIALIZED;
126+
}
127+
128+
openCommand->RegisterDelegate(this);
129+
130+
PushCommand(command);
131+
132+
return CHIP_NO_ERROR;
133+
}

examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h

+31
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
#pragma once
2020

2121
#include <commands/common/CHIPCommand.h>
22+
#include <commands/pairing/OpenCommissioningWindowCommand.h>
23+
#include <commands/pairing/PairingCommand.h>
24+
25+
constexpr uint16_t kMaxCommandSize = 64;
26+
constexpr uint16_t kDiscriminator = 3840;
27+
constexpr uint16_t kWindowTimeout = 300;
28+
constexpr uint16_t kIteration = 1000;
29+
constexpr uint8_t kEnhancedCommissioningMethod = 1;
2230

2331
class FabricSyncAddDeviceCommand : public CHIPCommand
2432
{
@@ -38,3 +46,26 @@ class FabricSyncAddDeviceCommand : public CHIPCommand
3846

3947
CHIP_ERROR RunCommand(NodeId remoteId);
4048
};
49+
50+
class FabricSyncDeviceCommand : public CHIPCommand, public CommissioningWindowDelegate, public CommissioningDelegate
51+
{
52+
public:
53+
FabricSyncDeviceCommand(CredentialIssuerCommands * credIssuerCommands) : CHIPCommand("sync-device", credIssuerCommands)
54+
{
55+
AddArgument("endpointid", 0, UINT16_MAX, &mRemoteEndpointId);
56+
}
57+
58+
void OnCommissioningWindowOpened(NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload) override;
59+
void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR err) override;
60+
61+
/////////// CHIPCommand Interface /////////
62+
CHIP_ERROR RunCommand() override { return RunCommand(mRemoteEndpointId); }
63+
64+
chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(1); }
65+
66+
private:
67+
chip::EndpointId mRemoteEndpointId = chip::kInvalidEndpointId;
68+
chip::NodeId mAssignedNodeId = chip::kUndefinedNodeId;
69+
70+
CHIP_ERROR RunCommand(chip::EndpointId remoteId);
71+
};

0 commit comments

Comments
 (0)