Skip to content

Commit

Permalink
Add new commissioned device as a synchronized device to Fabric Bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
yufengwangca committed Jun 17, 2024
1 parent 1d4ac45 commit e2a06ce
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 27 deletions.
6 changes: 3 additions & 3 deletions examples/fabric-bridge-app/linux/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
124 changes: 115 additions & 9 deletions examples/fabric-bridge-app/linux/DeviceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,77 @@ using namespace chip::app::Clusters;
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

Expand All @@ -60,11 +131,13 @@ void DeviceManager::Init()
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])
Expand All @@ -81,8 +154,9 @@ int DeviceManager::AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep,
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)
Expand Down Expand Up @@ -125,11 +199,43 @@ int DeviceManager::RemoveDeviceEndpoint(Device * dev)
return -1;
}

Device * DeviceManager::GetDevice(uint16_t index) const
Device * DeviceManager::GetDevice(chip::EndpointId endpointId) const
{
for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; ++index)
{
if (mDevices[index] && mDevices[index]->GetEndpointId() == endpointId)
{
return mDevices[index];
}
}
return nullptr;
}

Device * DeviceManager::GetDeviceByNodeId(chip::NodeId nodeId) 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]->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;
}
2 changes: 1 addition & 1 deletion examples/fabric-bridge-app/linux/RpcClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ CHIP_ERROR InitRpcClient(uint16_t rpcServerPort)

CHIP_ERROR OpenCommissioningWindow(NodeId nodeId)
{
ChipLogProgress(NotSpecified, "OpenCommissioningWindow\n");
ChipLogProgress(NotSpecified, "OpenCommissioningWindow with Node Id 0x:" ChipLogFormatX64, ChipLogValueX64(nodeId));

if (openCommissioningWindowCall.active())
{
Expand Down
35 changes: 29 additions & 6 deletions examples/fabric-bridge-app/linux/RpcServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,49 @@
#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
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)
{
chip::NodeId nodeId = request.node_id;
ChipLogProgress(NotSpecified, "Received AddSynchronizedDevice: " ChipLogFormatX64, ChipLogValueX64(nodeId));
return pw::OkStatus();
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
Expand Down
3 changes: 2 additions & 1 deletion examples/fabric-bridge-app/linux/include/Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Device
public:
static const int kDeviceNameSize = 32;

Device(chip::NodeId nodeId, const char * name);
Device(chip::NodeId nodeId);
virtual ~Device() {}

bool IsReachable();
Expand All @@ -41,6 +41,7 @@ class Device
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; };
Expand Down
48 changes: 41 additions & 7 deletions examples/fabric-bridge-app/linux/include/DeviceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class DeviceManager
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();

/**
Expand All @@ -38,15 +44,10 @@ class DeviceManager
* 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.
Expand All @@ -61,7 +62,40 @@ class DeviceManager
*/
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();
Expand Down

0 comments on commit e2a06ce

Please sign in to comment.