Skip to content

Commit af7cb90

Browse files
committed
Implemented the JCM mDNS advertisement and Open Commissioning Window
1 parent e665c9f commit af7cb90

24 files changed

+483
-47
lines changed

docs/guides/joint_fabric_guide.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
- [Joint Fabric Guide](#joint-fabric-guide)
44
- [Joint Fabric Example Applications](#joint-fabric-example-applications)
5-
- [Bootstrap Joint Fabric Demo on Linux](#bootstrap-joint-fabric-demo-on-linux)
6-
- [Run Joint Fabric Demo](#run-joint-fabric-demo)
5+
- [Building the Example Application](#building-the-example-application)
6+
- [Bootstrap Joint Fabric Demo on Linux](#bootstrap-joint-fabric-demo-on-linux)
7+
- [Initialize Ecosystem A (Vendor ID = 0xFFF1)](#initialize-ecosystem-a-vendor-id--0xfff1)
8+
- [Initialize Ecosystem B (Vendor ID = 0xFFF2)](#initialize-ecosystem-b-vendor-id--0xfff2)
9+
- [Run Joint Fabric Demo](#run-joint-fabric-demo)
710

811
## Joint Fabric Example Applications
912

@@ -251,3 +254,15 @@ A `Subjects` field equal to `18446744065119551489` (`FFFFFFFDFFFF0001` in hex)
251254
should be found.
252255

253256
## Run Joint Fabric Demo
257+
258+
- Open Joint Commissioning Window on JF Admin App of Ecosystem B
259+
260+
```
261+
>>> jcm open-joint-commissioning-window 11 400 1000 1261 1
262+
```
263+
264+
Check for the following logs on the jf-admin-app side:
265+
266+
```
267+
>>> [DIS] Advertise commission parameter vendorID=65521 productID=32769 discriminator=1261/04 cm=2 cp=0 jf=14
268+
```

examples/jf-admin-app/linux/JFAManager.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ using namespace chip::app;
3030
using namespace chip::app::Clusters;
3131
using namespace chip::Controller;
3232

33+
constexpr uint8_t kJFAvailableShift = 0;
34+
constexpr uint8_t kJFAdminShift = 1;
35+
constexpr uint8_t kJFAnchorShift = 2;
36+
constexpr uint8_t kJFDatastoreShift = 3;
37+
3338
JFAManager JFAManager::sJFA;
3439

3540
CHIP_ERROR JFAManager::Init(Server & server)
@@ -40,6 +45,15 @@ CHIP_ERROR JFAManager::Init(Server & server)
4045
return CHIP_NO_ERROR;
4146
}
4247

48+
CHIP_ERROR JFAManager::GetJointFabricMode(uint8_t & jointFabricMode)
49+
{
50+
jointFabricMode = ((IsDeviceCommissioned() ? 0 : 1) << kJFAvailableShift) |
51+
(IsDeviceCommissioned() ? (IsDeviceJFAdmin() ? (1 << kJFAdminShift) : 0) : 0) |
52+
(IsDeviceCommissioned() ? (IsDeviceJFAnchor() ? (1 << kJFAnchorShift) : 0) : 0) |
53+
(IsDeviceCommissioned() ? (1 << kJFDatastoreShift) : 0);
54+
return CHIP_NO_ERROR;
55+
}
56+
4357
CHIP_ERROR JFAManager::FinalizeCommissioning(NodeId nodeId)
4458
{
4559
if (jfFabricIndex == kUndefinedFabricId)
@@ -72,6 +86,46 @@ void JFAManager::HandleCommissioningCompleteEvent()
7286
}
7387
}
7488

89+
bool JFAManager::IsDeviceJFAdmin()
90+
{
91+
if (jfFabricIndex == kUndefinedFabricId)
92+
{
93+
return false;
94+
}
95+
96+
CATValues cats;
97+
98+
if (mServer->GetFabricTable().FetchCATs(jfFabricIndex, cats) == CHIP_NO_ERROR)
99+
{
100+
if (cats.ContainsIdentifier(kAdminCATIdentifier))
101+
{
102+
return true;
103+
}
104+
}
105+
106+
return false;
107+
}
108+
109+
bool JFAManager::IsDeviceJFAnchor()
110+
{
111+
if (jfFabricIndex == kUndefinedFabricId)
112+
{
113+
return false;
114+
}
115+
116+
CATValues cats;
117+
118+
if (mServer->GetFabricTable().FetchCATs(jfFabricIndex, cats) == CHIP_NO_ERROR)
119+
{
120+
if (cats.ContainsIdentifier(kAnchorCATIdentifier))
121+
{
122+
return true;
123+
}
124+
}
125+
126+
return false;
127+
}
128+
75129
void JFAManager::ReleaseSession()
76130
{
77131
auto optionalSessionHandle = mSessionHolder.Get();

examples/jf-admin-app/linux/include/JFAManager.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ class JFAManager
3838
void HandleCommissioningCompleteEvent();
3939
CHIP_ERROR FinalizeCommissioning(NodeId nodeId);
4040

41+
CHIP_ERROR GetJointFabricMode(uint8_t & jointFabricMode);
42+
43+
bool IsDeviceCommissioned() { return jfFabricIndex != kUndefinedFabricId; }
44+
bool IsDeviceJFAdmin();
45+
bool IsDeviceJFAnchor();
46+
4147
private:
4248
// Various actions to take when OnConnected callback is called
4349
enum OnConnectedAction

examples/jf-admin-app/linux/main.cpp

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,31 +25,81 @@
2525
#include <app/ConcreteAttributePath.h>
2626
#include <app/server/Server.h>
2727
#include <lib/support/logging/CHIPLogging.h>
28+
#include <platform/DeviceInstanceInfoProvider.h>
2829

2930
#include <string>
3031

3132
using namespace chip;
3233
using namespace chip::app;
3334
using namespace chip::app::Clusters;
35+
using namespace chip::DeviceLayer;
36+
37+
namespace {
38+
39+
class ExampleDeviceInstanceInfoProvider : public DeviceInstanceInfoProvider
40+
{
41+
public:
42+
void Init(DeviceInstanceInfoProvider * defaultProvider) { mDefaultProvider = defaultProvider; }
43+
44+
CHIP_ERROR GetVendorName(char * buf, size_t bufSize) override { return mDefaultProvider->GetVendorName(buf, bufSize); }
45+
CHIP_ERROR GetVendorId(uint16_t & vendorId) override { return mDefaultProvider->GetVendorId(vendorId); }
46+
CHIP_ERROR GetProductName(char * buf, size_t bufSize) override { return mDefaultProvider->GetProductName(buf, bufSize); }
47+
CHIP_ERROR GetProductId(uint16_t & productId) override { return mDefaultProvider->GetProductId(productId); }
48+
CHIP_ERROR GetPartNumber(char * buf, size_t bufSize) override { return mDefaultProvider->GetPartNumber(buf, bufSize); }
49+
CHIP_ERROR GetProductURL(char * buf, size_t bufSize) override { return mDefaultProvider->GetPartNumber(buf, bufSize); }
50+
CHIP_ERROR GetProductLabel(char * buf, size_t bufSize) override { return mDefaultProvider->GetProductLabel(buf, bufSize); }
51+
CHIP_ERROR GetSerialNumber(char * buf, size_t bufSize) override { return mDefaultProvider->GetSerialNumber(buf, bufSize); }
52+
CHIP_ERROR GetManufacturingDate(uint16_t & year, uint8_t & month, uint8_t & day) override
53+
{
54+
return mDefaultProvider->GetManufacturingDate(year, month, day);
55+
}
56+
CHIP_ERROR GetHardwareVersion(uint16_t & hardwareVersion) override
57+
{
58+
return mDefaultProvider->GetHardwareVersion(hardwareVersion);
59+
}
60+
CHIP_ERROR GetHardwareVersionString(char * buf, size_t bufSize) override
61+
{
62+
return mDefaultProvider->GetHardwareVersionString(buf, bufSize);
63+
}
64+
CHIP_ERROR GetRotatingDeviceIdUniqueId(MutableByteSpan & uniqueIdSpan) override
65+
{
66+
return mDefaultProvider->GetRotatingDeviceIdUniqueId(uniqueIdSpan);
67+
}
68+
CHIP_ERROR GetJointFabricMode(uint8_t & jointFabricMode) override { return JFAMgr().GetJointFabricMode(jointFabricMode); }
69+
70+
private:
71+
DeviceInstanceInfoProvider * mDefaultProvider;
72+
};
73+
74+
ExampleDeviceInstanceInfoProvider gExampleDeviceInstanceInfoProvider;
75+
76+
} // namespace
3477

3578
void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size,
3679
uint8_t * value)
3780
{}
3881

39-
void EventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg)
82+
void EventHandler(const ChipDeviceEvent * event, intptr_t arg)
4083
{
4184
(void) arg;
4285

43-
if (event->Type == DeviceLayer::DeviceEventType::kCommissioningComplete)
86+
if (event->Type == DeviceEventType::kCommissioningComplete)
4487
{
4588
JFAMgr().HandleCommissioningCompleteEvent();
4689
}
4790
}
4891

4992
void ApplicationInit()
5093
{
94+
auto * defaultProvider = GetDeviceInstanceInfoProvider();
95+
if (defaultProvider != &gExampleDeviceInstanceInfoProvider)
96+
{
97+
gExampleDeviceInstanceInfoProvider.Init(defaultProvider);
98+
SetDeviceInstanceInfoProvider(&gExampleDeviceInstanceInfoProvider);
99+
}
100+
51101
JFAMgr().Init(Server::GetInstance());
52-
DeviceLayer::PlatformMgrImpl().AddEventHandler(EventHandler, 0);
102+
PlatformMgrImpl().AddEventHandler(EventHandler, 0);
53103
}
54104

55105
void ApplicationShutdown() {}

examples/jf-control-app/BUILD.gn

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ config("config") {
6666

6767
static_library("jfc-utils") {
6868
sources = [
69-
"${chip_root}/examples/chip-tool/CHIPCommand.h",
7069
"${chip_root}/examples/chip-tool/commands/clusters/ModelCommand.cpp",
7170
"${chip_root}/examples/chip-tool/commands/clusters/ModelCommand.h",
7271
"${chip_root}/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.cpp",
72+
"${chip_root}/examples/chip-tool/commands/common/CHIPCommand.h",
7373
"${chip_root}/examples/chip-tool/commands/common/Command.cpp",
7474
"${chip_root}/examples/chip-tool/commands/common/Command.h",
7575
"${chip_root}/examples/chip-tool/commands/common/Commands.cpp",
@@ -93,6 +93,8 @@ static_library("jfc-utils") {
9393
"commands/common/CHIPCommand.cpp",
9494
"commands/example/ExampleOperationalCredentialsIssuer.cpp",
9595
"commands/example/ExampleOperationalCredentialsIssuer.h",
96+
"commands/pairing/OpenJointCommissioningWindowCommand.cpp",
97+
"commands/pairing/OpenJointCommissioningWindowCommand.h",
9698
"commands/pairing/PairingCommand.cpp",
9799
"commands/pairing/PairingCommand.h",
98100
]
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2025 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 "OpenJointCommissioningWindowCommand.h"
20+
21+
#include <system/SystemClock.h>
22+
23+
using namespace ::chip;
24+
25+
CHIP_ERROR OpenJointCommissioningWindowCommand::RunCommand()
26+
{
27+
mWindowOpener = Platform::MakeUnique<Controller::CommissioningWindowOpener>(&CurrentCommissioner());
28+
29+
SetupPayload ignored;
30+
return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
31+
.SetNodeId(mNodeId)
32+
.SetTimeout(mCommissioningWindowTimeout)
33+
.SetIteration(mIteration)
34+
.SetDiscriminator(mDiscriminator)
35+
.SetReadVIDPIDAttributes(true)
36+
.SetCallback(&mOnOpenCommissioningWindowCallback)
37+
.SetEndpointId(mEndpointId),
38+
ignored, true);
39+
}
40+
41+
void OpenJointCommissioningWindowCommand::OnOpenCommissioningWindowResponse(void * context, NodeId remoteId, CHIP_ERROR err,
42+
SetupPayload payload)
43+
{
44+
LogErrorOnFailure(err);
45+
46+
OpenJointCommissioningWindowCommand * command = reinterpret_cast<OpenJointCommissioningWindowCommand *>(context);
47+
VerifyOrReturn(command != nullptr, ChipLogError(chipTool, "OnOpenJointCommissioningWindowCommand: context is null"));
48+
command->SetCommandExitStatus(err);
49+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2025 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+
#pragma once
20+
21+
#include "../common/CHIPCommand.h"
22+
23+
#include <controller/CommissioningWindowOpener.h>
24+
#include <lib/support/CHIPMem.h>
25+
26+
class OpenJointCommissioningWindowCommand : public CHIPCommand
27+
{
28+
public:
29+
OpenJointCommissioningWindowCommand(CredentialIssuerCommands * credIssuerCommands) :
30+
CHIPCommand("open-joint-commissioning-window", credIssuerCommands),
31+
mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this)
32+
{
33+
AddArgument("node-id", 0, UINT64_MAX, &mNodeId, "Node to send command to.");
34+
AddArgument("window-timeout", 0, UINT16_MAX, &mCommissioningWindowTimeout,
35+
"Time, in seconds, before the commissioning window closes.");
36+
AddArgument("iteration", chip::Crypto::kSpake2p_Min_PBKDF_Iterations, chip::Crypto::kSpake2p_Max_PBKDF_Iterations,
37+
&mIteration, "Number of PBKDF iterations to use to derive the verifier. Ignored if 'option' is 0.");
38+
AddArgument("discriminator", 0, 4095, &mDiscriminator, "Discriminator to use for advertising. Ignored if 'option' is 0.");
39+
AddArgument("timeout", 0, UINT16_MAX, &mTimeout, "Time, in seconds, before this command is considered to have timed out.");
40+
AddArgument("endpoint-id", 0, UINT16_MAX, &mEndpointId, "Endpoint the command is targeted at.");
41+
}
42+
43+
/////////// CHIPCommand Interface /////////
44+
CHIP_ERROR RunCommand() override;
45+
// We issue multiple data model operations for this command, and the default
46+
// timeout for those is 10 seconds, so default to 20 seconds.
47+
chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(mTimeout.ValueOr(20)); }
48+
49+
private:
50+
NodeId mNodeId;
51+
uint16_t mCommissioningWindowTimeout;
52+
uint32_t mIteration;
53+
uint16_t mDiscriminator;
54+
55+
chip::Optional<uint16_t> mTimeout;
56+
57+
chip::EndpointId mEndpointId;
58+
59+
chip::Platform::UniquePtr<chip::Controller::CommissioningWindowOpener> mWindowOpener;
60+
61+
static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
62+
63+
chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
64+
};

examples/jf-control-app/main.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "commands/icd/ICDCommand.h"
2828
#include "commands/interactive/Commands.h"
2929
#include "commands/pairing/Commands.h"
30+
#include "commands/pairing/OpenJointCommissioningWindowCommand.h"
3031

3132
#include "RpcClientProcessor.h"
3233

@@ -46,6 +47,17 @@ CHIP_ERROR RpcConnect(void)
4647
return chip::rpc::client::StartPacketProcessing();
4748
}
4849

50+
void registerCommandsJCM(Commands & commands, CredentialIssuerCommands * credsIssuerConfig)
51+
{
52+
const char * clusterName = "JCM";
53+
54+
commands_list clusterCommands = {
55+
make_unique<OpenJointCommissioningWindowCommand>(credsIssuerConfig),
56+
};
57+
58+
commands.RegisterCommandSet(clusterName, clusterCommands, "Commands for commissioning JF-enabled devices.");
59+
}
60+
4961
// ================================================================================
5062
// Main Code
5163
// ================================================================================
@@ -98,6 +110,7 @@ int main(int argc, char * argv[])
98110
registerCommandsPairing(commands, &credIssuerCommands);
99111
registerClusters(commands, &credIssuerCommands);
100112
registerCommandsSubscriptions(commands, &credIssuerCommands);
113+
registerCommandsJCM(commands, &credIssuerCommands);
101114

102115
return commands.Run(static_cast<int>(c_args.size()), c_args.data());
103116
}

0 commit comments

Comments
 (0)