Add new commissioned device as a synchronized device to Fabric Bridge (#33908)
diff --git a/examples/fabric-bridge-app/linux/Device.cpp b/examples/fabric-bridge-app/linux/Device.cpp
index 215db48..e72fd85 100644
--- a/examples/fabric-bridge-app/linux/Device.cpp
+++ b/examples/fabric-bridge-app/linux/Device.cpp
@@ -25,11 +25,11 @@
using namespace chip::app::Clusters::Actions;
-Device::Device(chip::NodeId nodeId, const char * name)
+Device::Device(chip::NodeId nodeId)
{
- chip::Platform::CopyString(mName, name);
mReachable = false;
- mEndpointId = 0;
+ mNodeId = nodeId;
+ mEndpointId = chip::kInvalidEndpointId;
}
bool Device::IsReachable()
diff --git a/examples/fabric-bridge-app/linux/DeviceManager.cpp b/examples/fabric-bridge-app/linux/DeviceManager.cpp
index 3b158b8..bbfff03 100644
--- a/examples/fabric-bridge-app/linux/DeviceManager.cpp
+++ b/examples/fabric-bridge-app/linux/DeviceManager.cpp
@@ -46,6 +46,77 @@
namespace {
constexpr uint8_t kMaxRetries = 10;
+constexpr int kNodeLabelSize = 32;
+
+// Current ZCL implementation of Struct uses a max-size array of 254 bytes
+constexpr int kDescriptorAttributeArraySize = 254;
+
+// ENDPOINT DEFINITIONS:
+// =================================================================================
+//
+// Endpoint definitions will be reused across multiple endpoints for every instance of the
+// endpoint type.
+// There will be no intrinsic storage for the endpoint attributes declared here.
+// Instead, all attributes will be treated as EXTERNAL, and therefore all reads
+// or writes to the attributes must be handled within the emberAfExternalAttributeWriteCallback
+// and emberAfExternalAttributeReadCallback functions declared herein. This fits
+// the typical model of a bridge, since a bridge typically maintains its own
+// state database representing the devices connected to it.
+
+// (taken from matter-devices.xml)
+#define DEVICE_TYPE_BRIDGED_NODE 0x0013
+
+// Device Version for dynamic endpoints:
+#define DEVICE_VERSION_DEFAULT 1
+
+// ---------------------------------------------------------------------------
+//
+// SYNCED DEVICE ENDPOINT: contains the following clusters:
+// - Descriptor
+// - Bridged Device Basic Information
+// - Administrator Commissioning
+
+// Declare Descriptor cluster attributes
+DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(descriptorAttrs)
+DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::DeviceTypeList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* device list */
+ DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::ServerList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* server list */
+ DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::ClientList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* client list */
+ DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::PartsList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* parts list */
+ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END();
+
+// Declare Bridged Device Basic Information cluster attributes
+DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(bridgedDeviceBasicAttrs)
+DECLARE_DYNAMIC_ATTRIBUTE(BridgedDeviceBasicInformation::Attributes::NodeLabel::Id, CHAR_STRING, kNodeLabelSize, 0), /* NodeLabel */
+ DECLARE_DYNAMIC_ATTRIBUTE(BridgedDeviceBasicInformation::Attributes::Reachable::Id, BOOLEAN, 1, 0), /* Reachable */
+ DECLARE_DYNAMIC_ATTRIBUTE(BridgedDeviceBasicInformation::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* feature map */
+ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END();
+
+// Declare Administrator Commissioning cluster attributes
+DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(AdministratorCommissioningAttrs)
+DECLARE_DYNAMIC_ATTRIBUTE(AdministratorCommissioning::Attributes::WindowStatus::Id, ENUM8, 1, 0), /* NodeLabel */
+ DECLARE_DYNAMIC_ATTRIBUTE(AdministratorCommissioning::Attributes::AdminFabricIndex::Id, FABRIC_IDX, 1, 0), /* Reachable */
+ DECLARE_DYNAMIC_ATTRIBUTE(AdministratorCommissioning::Attributes::AdminVendorId::Id, VENDOR_ID, 2, 0), /* Reachable */
+ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END();
+
+constexpr CommandId administratorCommissioningCommands[] = {
+ app::Clusters::AdministratorCommissioning::Commands::OpenCommissioningWindow::Id,
+ app::Clusters::AdministratorCommissioning::Commands::OpenBasicCommissioningWindow::Id,
+ app::Clusters::AdministratorCommissioning::Commands::RevokeCommissioning::Id,
+ kInvalidCommandId,
+};
+
+// Declare Cluster List for Bridged Node endpoint
+DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(bridgedNodeClusters)
+DECLARE_DYNAMIC_CLUSTER(Descriptor::Id, descriptorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr),
+ DECLARE_DYNAMIC_CLUSTER(BridgedDeviceBasicInformation::Id, bridgedDeviceBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr),
+ DECLARE_DYNAMIC_CLUSTER(AdministratorCommissioning::Id, AdministratorCommissioningAttrs, ZAP_CLUSTER_MASK(SERVER),
+ administratorCommissioningCommands, nullptr) DECLARE_DYNAMIC_CLUSTER_LIST_END;
+
+// Declare Bridged Node endpoint
+DECLARE_DYNAMIC_ENDPOINT(sBridgedNodeEndpoint, bridgedNodeClusters);
+DataVersion sBridgedNodeDataVersions[ArraySize(bridgedNodeClusters)];
+
+const EmberAfDeviceType sBridgedDeviceTypes[] = { { DEVICE_TYPE_BRIDGED_NODE, DEVICE_VERSION_DEFAULT } };
} // namespace
@@ -60,11 +131,13 @@
mCurrentEndpointId = mFirstDynamicEndpointId;
}
-int DeviceManager::AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep,
- const chip::Span<const EmberAfDeviceType> & deviceTypeList,
- const chip::Span<chip::DataVersion> & dataVersionStorage, chip::EndpointId parentEndpointId)
+int DeviceManager::AddDeviceEndpoint(Device * dev, chip::EndpointId parentEndpointId)
{
- uint8_t index = 0;
+ uint8_t index = 0;
+ EmberAfEndpointType * ep = &sBridgedNodeEndpoint;
+ const chip::Span<const EmberAfDeviceType> & deviceTypeList = Span<const EmberAfDeviceType>(sBridgedDeviceTypes);
+ const chip::Span<chip::DataVersion> & dataVersionStorage = Span<DataVersion>(sBridgedNodeDataVersions);
+
while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT)
{
if (nullptr == mDevices[index])
@@ -81,8 +154,9 @@
emberAfSetDynamicEndpoint(index, mCurrentEndpointId, ep, dataVersionStorage, deviceTypeList, parentEndpointId);
if (err == CHIP_NO_ERROR)
{
- ChipLogProgress(NotSpecified, "Added device %s to dynamic endpoint %d (index=%d)", dev->GetName(),
- mCurrentEndpointId, index);
+ ChipLogProgress(NotSpecified,
+ "Added device with nodeId=0x" ChipLogFormatX64 " to dynamic endpoint %d (index=%d)",
+ ChipLogValueX64(dev->GetNodeId()), mCurrentEndpointId, index);
return index;
}
if (err != CHIP_ERROR_ENDPOINT_EXISTS)
@@ -125,11 +199,43 @@
return -1;
}
-Device * DeviceManager::GetDevice(uint16_t index) const
+Device * DeviceManager::GetDevice(chip::EndpointId endpointId) const
{
- if (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT)
+ for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; ++index)
{
- return mDevices[index];
+ if (mDevices[index] && mDevices[index]->GetEndpointId() == endpointId)
+ {
+ return mDevices[index];
+ }
}
return nullptr;
}
+
+Device * DeviceManager::GetDeviceByNodeId(chip::NodeId nodeId) const
+{
+ for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; ++index)
+ {
+ if (mDevices[index] && mDevices[index]->GetNodeId() == nodeId)
+ {
+ return mDevices[index];
+ }
+ }
+ return nullptr;
+}
+
+int DeviceManager::RemoveDeviceByNodeId(chip::NodeId nodeId)
+{
+ for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; ++index)
+ {
+ if (mDevices[index] && mDevices[index]->GetNodeId() == nodeId)
+ {
+ DeviceLayer::StackLock lock;
+ EndpointId ep = emberAfClearDynamicEndpoint(index);
+ mDevices[index] = nullptr;
+ ChipLogProgress(NotSpecified, "Removed device with NodeId=0x" ChipLogFormatX64 " from dynamic endpoint %d (index=%d)",
+ ChipLogValueX64(nodeId), ep, index);
+ return index;
+ }
+ }
+ return -1;
+}
diff --git a/examples/fabric-bridge-app/linux/RpcClient.cpp b/examples/fabric-bridge-app/linux/RpcClient.cpp
index c09a447..815250f 100644
--- a/examples/fabric-bridge-app/linux/RpcClient.cpp
+++ b/examples/fabric-bridge-app/linux/RpcClient.cpp
@@ -65,7 +65,7 @@
CHIP_ERROR OpenCommissioningWindow(NodeId nodeId)
{
- ChipLogProgress(NotSpecified, "OpenCommissioningWindow\n");
+ ChipLogProgress(NotSpecified, "OpenCommissioningWindow with Node Id 0x:" ChipLogFormatX64, ChipLogValueX64(nodeId));
if (openCommissioningWindowCall.active())
{
diff --git a/examples/fabric-bridge-app/linux/RpcServer.cpp b/examples/fabric-bridge-app/linux/RpcServer.cpp
index c971811..088fc9a 100644
--- a/examples/fabric-bridge-app/linux/RpcServer.cpp
+++ b/examples/fabric-bridge-app/linux/RpcServer.cpp
@@ -20,27 +20,50 @@
#include "pw_rpc_system_server/rpc_server.h"
#include "pw_rpc_system_server/socket.h"
-#include <system/SystemClock.h>
+#include <lib/core/CHIPError.h>
+
+#include <string>
#include <thread>
#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
#include "pigweed/rpc_services/FabricBridge.h"
#endif
+#include "Device.h"
+#include "DeviceManager.h"
+
+using namespace chip;
+using namespace chip::app;
+using namespace chip::app::Clusters;
+
namespace {
#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
class FabricBridge final : public chip::rpc::FabricBridge
{
public:
- pw::Status AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response) override
- {
- chip::NodeId nodeId = request.node_id;
- ChipLogProgress(NotSpecified, "Received AddSynchronizedDevice: " ChipLogFormatX64, ChipLogValueX64(nodeId));
- return pw::OkStatus();
- }
+ pw::Status AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response) override;
};
+pw::Status FabricBridge::AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response)
+{
+ NodeId nodeId = request.node_id;
+ ChipLogProgress(NotSpecified, "Received AddSynchronizedDevice: " ChipLogFormatX64, ChipLogValueX64(nodeId));
+
+ Device * device = new Device(nodeId);
+ device->SetReachable(true);
+
+ int result = DeviceMgr().AddDeviceEndpoint(device, 1);
+ if (result == -1)
+ {
+ delete device;
+ ChipLogError(NotSpecified, "Failed to add device with nodeId=0x" ChipLogFormatX64, ChipLogValueX64(nodeId));
+ return pw::Status::Unknown();
+ }
+
+ return pw::OkStatus();
+}
+
FabricBridge fabric_bridge_service;
#endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
diff --git a/examples/fabric-bridge-app/linux/include/Device.h b/examples/fabric-bridge-app/linux/include/Device.h
index 64940f3..c709af8 100644
--- a/examples/fabric-bridge-app/linux/include/Device.h
+++ b/examples/fabric-bridge-app/linux/include/Device.h
@@ -32,7 +32,7 @@
public:
static const int kDeviceNameSize = 32;
- Device(chip::NodeId nodeId, const char * name);
+ Device(chip::NodeId nodeId);
virtual ~Device() {}
bool IsReachable();
@@ -41,6 +41,7 @@
void SetLocation(std::string location) { mLocation = location; };
inline void SetEndpointId(chip::EndpointId id) { mEndpointId = id; };
inline chip::EndpointId GetEndpointId() { return mEndpointId; };
+ inline chip::NodeId GetNodeId() { return mNodeId; };
inline void SetParentEndpointId(chip::EndpointId id) { mParentEndpointId = id; };
inline chip::EndpointId GetParentEndpointId() { return mParentEndpointId; };
inline char * GetName() { return mName; };
diff --git a/examples/fabric-bridge-app/linux/include/DeviceManager.h b/examples/fabric-bridge-app/linux/include/DeviceManager.h
index 2667fc0..b1dd791 100644
--- a/examples/fabric-bridge-app/linux/include/DeviceManager.h
+++ b/examples/fabric-bridge-app/linux/include/DeviceManager.h
@@ -27,6 +27,12 @@
public:
DeviceManager() = default;
+ /**
+ * @brief Initializes the DeviceManager.
+ *
+ * This function sets up the initial state of the DeviceManager, clearing
+ * any existing devices and setting the starting dynamic endpoint ID.
+ */
void Init();
/**
@@ -38,15 +44,10 @@
* dynamic endpoint; otherwise, it returns -1.
*
* @param dev A pointer to the device to be added.
- * @param ep A pointer to the endpoint type.
- * @param deviceTypeList A span containing the list of device types.
- * @param dataVersionStorage A span containing the data version storage.
* @param parentEndpointId The parent endpoint ID. Defaults to an invalid endpoint ID.
* @return int The index of the dynamic endpoint if successful, -1 otherwise.
*/
- int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, const chip::Span<const EmberAfDeviceType> & deviceTypeList,
- const chip::Span<chip::DataVersion> & dataVersionStorage,
- chip::EndpointId parentEndpointId = chip::kInvalidEndpointId);
+ int AddDeviceEndpoint(Device * dev, chip::EndpointId parentEndpointId = chip::kInvalidEndpointId);
/**
* @brief Removes a device from a dynamic endpoint.
@@ -61,7 +62,40 @@
*/
int RemoveDeviceEndpoint(Device * dev);
- Device * GetDevice(uint16_t index) const;
+ /**
+ * @brief Gets a device from its endpoint ID.
+ *
+ * This function iterates through the available devices and returns the device that matches the
+ * specified endpoint ID. If no device matches the endpoint ID, it returns nullptr.
+ *
+ * @param endpointId The endpoint ID of the device to be retrieved.
+ * @return Device* A pointer to the device if found, nullptr otherwise.
+ */
+ Device * GetDevice(chip::EndpointId endpointId) const;
+
+ /**
+ * @brief Gets a device from its NodeId.
+ *
+ * This function iterates through the available devices and returns the device that matches the
+ * specified NodeId. If no device matches the NodeId, it returns nullptr.
+ *
+ * @param nodeId The NodeId of the device to be retrieved.
+ * @return Device* A pointer to the device if found, nullptr otherwise.
+ */
+ Device * GetDeviceByNodeId(chip::NodeId nodeId) const;
+
+ /**
+ * @brief Removes a device from a dynamic endpoint by its NodeId.
+ *
+ * This function attempts to remove a device from a dynamic endpoint by iterating through the
+ * available endpoints and checking if the device matches the specified NodeId. If the device is
+ * found, it clears the dynamic endpoint, logs the removal, and returns the index of the removed
+ * endpoint. If the device is not found, it returns -1.
+ *
+ * @param nodeId The NodeId of the device to be removed.
+ * @return int The index of the removed dynamic endpoint if successful, -1 otherwise.
+ */
+ int RemoveDeviceByNodeId(chip::NodeId nodeId);
private:
friend DeviceManager & DeviceMgr();