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-Bridge] Add new commissioned device as a synchronized device to Fabric Bridge #33908

Merged
merged 1 commit into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Add new commissioned device as a synchronized device to Fabric Bridge
  • Loading branch information
yufengwangca committed Jun 17, 2024
commit e2a06ce41555ed57e61859c933d0da42bb554a73
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);
yufengwangca marked this conversation as resolved.
Show resolved Hide resolved
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
Loading