From 10836283c285862f610e8be8a30f515040a575ac Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Wed, 7 Aug 2024 13:00:21 -0400 Subject: [PATCH] fabric-bridge: Add ECOINFO to dynamic bridged endpoints (#34811) --------- Co-authored-by: Restyled.io Co-authored-by: saurabhst --- .../fabric-bridge-common/BUILD.gn | 14 ++++++++++++- .../src/BridgedDeviceManager.cpp | 8 ++++++++ .../fabric-bridge-app/linux/RpcServer.cpp | 5 +++++ examples/fabric-bridge-app/linux/main.cpp | 11 +++++++++- .../ecosystem-information-server.cpp | 11 ++++++++++ .../ecosystem-information-server.h | 20 ++++++++++++++++++- 6 files changed, 66 insertions(+), 3 deletions(-) diff --git a/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn b/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn index 38f2f8f329bd88..7f2fbcbbfe0556 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn +++ b/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn @@ -19,12 +19,24 @@ config("config") { include_dirs = [ "include" ] } -chip_data_model("fabric-bridge-common") { +chip_data_model("fabric-bridge-common-zap") { zap_file = "fabric-bridge-app.zap" is_server = true cflags = [ "-DDYNAMIC_ENDPOINT_COUNT=16" ] } +# This includes all the clusters that only exist on the dynamic endpoint. +source_set("fabric-bridge-common") { + public_configs = [ ":config" ] + + sources = [ + "${chip_root}/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp", + "${chip_root}/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h", + ] + + public_deps = [ ":fabric-bridge-common-zap" ] +} + source_set("fabric-bridge-lib") { public_configs = [ ":config" ] diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceManager.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceManager.cpp index 7e78baa7ded548..64b88e49bb5c96 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceManager.cpp +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceManager.cpp @@ -112,6 +112,13 @@ DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(bridgedDeviceBasicAttrs) kSoftwareVersionSize, 0), DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); +// Declare Ecosystem Information cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(ecosystemInformationBasicAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(EcosystemInformation::Attributes::RemovedOn::Id, EPOCH_US, kNodeLabelSize, ATTRIBUTE_MASK_NULLABLE), + DECLARE_DYNAMIC_ATTRIBUTE(EcosystemInformation::Attributes::DeviceDirectory::Id, ARRAY, kDescriptorAttributeArraySize, 0), + DECLARE_DYNAMIC_ATTRIBUTE(EcosystemInformation::Attributes::LocationDirectory::Id, ARRAY, kDescriptorAttributeArraySize, 0), + 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), @@ -131,6 +138,7 @@ constexpr CommandId administratorCommissioningCommands[] = { 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(EcosystemInformation::Id, ecosystemInformationBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER(AdministratorCommissioning::Id, AdministratorCommissioningAttrs, ZAP_CLUSTER_MASK(SERVER), administratorCommissioningCommands, nullptr) DECLARE_DYNAMIC_CLUSTER_LIST_END; diff --git a/examples/fabric-bridge-app/linux/RpcServer.cpp b/examples/fabric-bridge-app/linux/RpcServer.cpp index 76fe8f84653d39..a4053bea9f6a56 100644 --- a/examples/fabric-bridge-app/linux/RpcServer.cpp +++ b/examples/fabric-bridge-app/linux/RpcServer.cpp @@ -20,6 +20,7 @@ #include "pw_rpc_system_server/rpc_server.h" #include "pw_rpc_system_server/socket.h" +#include #include #include @@ -62,6 +63,10 @@ pw::Status FabricBridge::AddSynchronizedDevice(const chip_rpc_SynchronizedDevice return pw::Status::Unknown(); } + CHIP_ERROR err = EcosystemInformation::EcosystemInformationServer::Instance().AddEcosystemInformationClusterToEndpoint( + device->GetEndpointId()); + VerifyOrDie(err == CHIP_NO_ERROR); + return pw::OkStatus(); } diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index 24ad4aa9a1690c..4f227b1f4a0436 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -25,6 +25,7 @@ #include #include +#include #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE #include "RpcClient.h" @@ -35,8 +36,15 @@ #include #include -using namespace chip; +// This is declared here and not in a header because zap/embr assumes all clusters +// are defined in a static endpoint in the .zap file. From there, the codegen will +// automatically use PluginApplicationCallbacksHeader.jinja to declare and call +// the respective Init callbacks. However, because EcosystemInformation cluster is only +// ever on a dynamic endpoint, this doesn't get declared and called for us, so we +// need to declare and call it ourselves where the application is initialized. +void MatterEcosystemInformationPluginServerInitCallback(); +using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::AdministratorCommissioning; @@ -177,6 +185,7 @@ void ApplicationInit() { ChipLogDetail(NotSpecified, "Fabric-Bridge: ApplicationInit()"); + MatterEcosystemInformationPluginServerInitCallback(); CommandHandlerInterfaceRegistry::RegisterCommandHandler(&gAdministratorCommissioningCommandHandler); registerAttributeAccessOverride(&gBridgedDeviceBasicInformationAttributes); diff --git a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp index b962150e219c63..9d82f7064f0f1b 100644 --- a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp +++ b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp @@ -243,6 +243,17 @@ EcosystemInformationServer & EcosystemInformationServer::Instance() return mInstance; } +CHIP_ERROR EcosystemInformationServer::AddEcosystemInformationClusterToEndpoint(EndpointId aEndpoint) +{ + VerifyOrReturnError((aEndpoint != kRootEndpointId && aEndpoint != kInvalidEndpointId), CHIP_ERROR_INVALID_ARGUMENT); + auto it = mDevicesMap.find(aEndpoint); + // We expect that the device has not been previously added. + VerifyOrReturnError((it == mDevicesMap.end()), CHIP_ERROR_INCORRECT_STATE); + // This create an empty DeviceInfo in mDevicesMap. + mDevicesMap[aEndpoint] = DeviceInfo(); + return CHIP_NO_ERROR; +} + CHIP_ERROR EcosystemInformationServer::AddDeviceInfo(EndpointId aEndpoint, std::unique_ptr aDevice) { VerifyOrReturnError(aDevice, CHIP_ERROR_INVALID_ARGUMENT); diff --git a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h index daa6f0124d9e39..dce12e745bf14e 100644 --- a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h +++ b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h @@ -150,6 +150,24 @@ class EcosystemInformationServer public: static EcosystemInformationServer & Instance(); + /** + * @brief Add EcosystemInformation Cluster to endpoint so we respond appropriately on endpoint + * + * EcosystemInformation cluster is only ever on dynamic bridge endpoint. If cluster is added + * to a new endpoint, but does not contain any ecosystem information presently, + * this is called to let ECOINFO cluster code know it is supposed to provide blank attribute + * information on this endpoint. + * + * This approach was intentionally taken instead of relying on emberAfDeviceTypeListFromEndpoint + * to keep this cluster more unit testable. This does add burden to application but is worth + * the trade-off. + * + * @param[in] aEndpoint Which endpoint is the device being added to the device directory. + * @return #CHIP_NO_ERROR on success. + * @return Other CHIP_ERROR associated with issue. + */ + CHIP_ERROR AddEcosystemInformationClusterToEndpoint(EndpointId aEndpoint); + /** * @brief Adds device as entry to DeviceDirectory list Attribute. * @@ -187,7 +205,7 @@ class EcosystemInformationServer private: struct DeviceInfo { - Optional mRemovedOn; + Optional mRemovedOn = NullOptional; std::vector> mDeviceDirectory; // Map key is using the UniqueLocationId std::map> mLocationDirectory;