diff --git a/scripts/tools/check_includes_config.py b/scripts/tools/check_includes_config.py index 20c853b9906df8..d897c86e32cfe8 100644 --- a/scripts/tools/check_includes_config.py +++ b/scripts/tools/check_includes_config.py @@ -128,6 +128,8 @@ 'src/app/clusters/application-launcher-server/application-launcher-server.cpp': {'string'}, 'src/app/clusters/application-launcher-server/application-launcher-delegate.h': {'list'}, 'src/app/clusters/audio-output-server/audio-output-delegate.h': {'list'}, + # EcosystemInformationCluster is for Fabric Sync and is intended to run on device that are capable of handling these types. + 'src/app/clusters/ecosystem-information-server/ecosystem-information-server.h': {'map', 'string', 'vector'}, 'src/app/clusters/channel-server/channel-delegate.h': {'list'}, 'src/app/clusters/content-launch-server/content-launch-delegate.h': {'list'}, 'src/app/clusters/content-launch-server/content-launch-server.cpp': {'list'}, diff --git a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp new file mode 100644 index 00000000000000..26f1c96cd65fa6 --- /dev/null +++ b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp @@ -0,0 +1,377 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ecosystem-information-server.h" + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace EcosystemInformation { +namespace { + +constexpr size_t kDeviceNameMaxSize = 64; +constexpr size_t kUniqueLocationIdMaxSize = 64; +constexpr size_t kUniqueLocationIdsListMaxSize = 64; +constexpr size_t kLocationDescriptorNameMaxSize = 128; + +constexpr size_t kDeviceDirectoryMaxSize = 256; +constexpr size_t kLocationDirectoryMaxSize = 64; + +class AttrAccess : public AttributeAccessInterface +{ +public: + // Register for the EcosystemInformationCluster on all endpoints. + AttrAccess() : AttributeAccessInterface(Optional::Missing(), Clusters::EcosystemInformation::Id) {} + + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; +}; + +CHIP_ERROR AttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + VerifyOrDie(aPath.mClusterId == Clusters::EcosystemInformation::Id); + switch (aPath.mAttributeId) + { + case Attributes::RemovedOn::Id: + return EcosystemInformationServer::Instance().EncodeRemovedOnAttribute(aPath.mEndpointId, aEncoder); + case Attributes::DeviceDirectory ::Id: + return EcosystemInformationServer::Instance().EncodeDeviceDirectoryAttribute(aPath.mEndpointId, aEncoder); + case Attributes::LocationDirectory ::Id: + return EcosystemInformationServer::Instance().EncodeLocationStructAttribute(aPath.mEndpointId, aEncoder); + default: + break; + } + return CHIP_NO_ERROR; +} + +// WARNING: caller is expected to use the returned LocationDescriptorStruct::Type immediately. Caller must +// be certain that the provided aLocationDescriptor has not been destroyed, prior to using the return +// struct to encode. +// TODO(#33223) To improve safety we could make GetEncodableLocationDescriptorStruct a private +// memeber method where we explicitly delete member method for the parameter that matches +// (LocationDescriptorStruct && aLocationDescriptor). +Structs::LocationDescriptorStruct::Type GetEncodableLocationDescriptorStruct(const LocationDescriptorStruct & aLocationDescriptor) +{ + Structs::LocationDescriptorStruct::Type locationDescriptor; + // This would imply data is either not properly validated before being + // stored here or corruption has occurred. + VerifyOrDie(!aLocationDescriptor.mLocationName.empty()); + locationDescriptor.locationName = CharSpan(aLocationDescriptor.mLocationName.c_str(), aLocationDescriptor.mLocationName.size()); + + if (aLocationDescriptor.mFloorNumber.has_value()) + { + locationDescriptor.floorNumber.SetNonNull(aLocationDescriptor.mFloorNumber.value()); + } + else + { + locationDescriptor.floorNumber.SetNull(); + } + + if (aLocationDescriptor.mAreaType.has_value()) + { + locationDescriptor.areaType.SetNonNull(aLocationDescriptor.mAreaType.value()); + } + else + { + locationDescriptor.areaType.SetNull(); + } + return locationDescriptor; +} + +} // namespace + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::SetDeviceName(std::string aDeviceName, + uint64_t aDeviceNameLastEditEpochUs) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mDeviceName = std::move(aDeviceName); + mDeviceNameLastEditEpochUs = aDeviceNameLastEditEpochUs; + return *this; +} + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::SetBrigedEndpoint(EndpointId aBridgedEndpoint) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mBridgedEndpoint = aBridgedEndpoint; + return *this; +} + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::SetOriginalEndpoint(EndpointId aOriginalEndpoint) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mOriginalEndpoint = aOriginalEndpoint; + return *this; +} + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::AddDeviceType(Structs::DeviceTypeStruct::Type aDeviceType) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mDeviceTypes.push_back(std::move(aDeviceType)); + return *this; +} + +EcosystemDeviceStruct::Builder & EcosystemDeviceStruct::Builder::AddUniqueLocationId(std::string aUniqueLocationId, + uint64_t aUniqueLocationIdsLastEditEpochUs) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mUniqueLocationIds.push_back(std::move(aUniqueLocationId)); + mUniqueLocationIdsLastEditEpochUs = aUniqueLocationIdsLastEditEpochUs; + return *this; +} + +std::unique_ptr EcosystemDeviceStruct::Builder::Build() +{ + VerifyOrReturnValue(!mIsAlreadyBuilt, nullptr, ChipLogError(Zcl, "Build() already called")); + VerifyOrReturnValue(mDeviceName.size() <= kDeviceNameMaxSize, nullptr, ChipLogError(Zcl, "Device name too large")); + VerifyOrReturnValue(mOriginalEndpoint != kInvalidEndpointId, nullptr, ChipLogError(Zcl, "Invalid original endpoint")); + VerifyOrReturnValue(!mDeviceTypes.empty(), nullptr, ChipLogError(Zcl, "No device types added")); + VerifyOrReturnValue(mUniqueLocationIds.size() <= kUniqueLocationIdsListMaxSize, nullptr, + ChipLogError(Zcl, "Too many location ids")); + + for (auto & locationId : mUniqueLocationIds) + { + VerifyOrReturnValue(locationId.size() <= kUniqueLocationIdMaxSize, nullptr, ChipLogError(Zcl, "Location id too long")); + } + + // std::make_unique does not have access to private constructor we workaround with using new + std::unique_ptr ret{ new EcosystemDeviceStruct( + std::move(mDeviceName), mDeviceNameLastEditEpochUs, mBridgedEndpoint, mOriginalEndpoint, std::move(mDeviceTypes), + std::move(mUniqueLocationIds), mUniqueLocationIdsLastEditEpochUs) }; + mIsAlreadyBuilt = true; + return ret; +} + +CHIP_ERROR EcosystemDeviceStruct::Encode(const AttributeValueEncoder::ListEncodeHelper & aEncoder, const FabricIndex & aFabricIndex) +{ + Structs::EcosystemDeviceStruct::Type deviceStruct; + if (!mDeviceName.empty()) + { + deviceStruct.deviceName.SetValue(CharSpan(mDeviceName.c_str(), mDeviceName.size())); + // When there is a device name we also include mDeviceNameLastEditEpochUs + deviceStruct.deviceNameLastEdit.SetValue(mDeviceNameLastEditEpochUs); + } + deviceStruct.bridgedEndpoint = mBridgedEndpoint; + deviceStruct.originalEndpoint = mOriginalEndpoint; + deviceStruct.deviceTypes = DataModel::List(mDeviceTypes.data(), mDeviceTypes.size()); + + std::vector locationIds; + locationIds.reserve(mUniqueLocationIds.size()); + for (auto & id : mUniqueLocationIds) + { + locationIds.push_back(CharSpan(id.c_str(), id.size())); + } + deviceStruct.uniqueLocationIDs = DataModel::List(locationIds.data(), locationIds.size()); + + deviceStruct.uniqueLocationIDsLastEdit = mUniqueLocationIdsLastEditEpochUs; + + // TODO(#33223) this is a hack, use mFabricIndex when it exists. + deviceStruct.SetFabricIndex(aFabricIndex); + return aEncoder.Encode(deviceStruct); +} + +EcosystemLocationStruct::Builder & EcosystemLocationStruct::Builder::SetLocationName(std::string aLocationName) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mLocationDescriptor.mLocationName = std::move(aLocationName); + return *this; +} + +EcosystemLocationStruct::Builder & EcosystemLocationStruct::Builder::SetFloorNumber(std::optional aFloorNumber) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mLocationDescriptor.mFloorNumber = aFloorNumber; + return *this; +} + +EcosystemLocationStruct::Builder & EcosystemLocationStruct::Builder::SetAreaTypeTag(std::optional aAreaTypeTag) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mLocationDescriptor.mAreaType = aAreaTypeTag; + return *this; +} + +EcosystemLocationStruct::Builder & +EcosystemLocationStruct::Builder::SetLocationDescriptorLastEdit(uint64_t aLocationDescriptorLastEditEpochUs) +{ + VerifyOrDie(!mIsAlreadyBuilt); + mLocationDescriptorLastEditEpochUs = aLocationDescriptorLastEditEpochUs; + return *this; +} + +std::unique_ptr EcosystemLocationStruct::Builder::Build() +{ + VerifyOrReturnValue(!mIsAlreadyBuilt, nullptr, ChipLogError(Zcl, "Build() already called")); + VerifyOrReturnValue(!mLocationDescriptor.mLocationName.empty(), nullptr, ChipLogError(Zcl, "Must Provided Location Name")); + VerifyOrReturnValue(mLocationDescriptor.mLocationName.size() <= kLocationDescriptorNameMaxSize, nullptr, + ChipLogError(Zcl, "Must Location Name must be less than 64 bytes")); + + // std::make_unique does not have access to private constructor we workaround with using new + std::unique_ptr ret{ new EcosystemLocationStruct(std::move(mLocationDescriptor), + mLocationDescriptorLastEditEpochUs) }; + mIsAlreadyBuilt = true; + return ret; +} + +CHIP_ERROR EcosystemLocationStruct::Encode(const AttributeValueEncoder::ListEncodeHelper & aEncoder, + const std::string & aUniqueLocationId, const FabricIndex & aFabricIndex) +{ + Structs::EcosystemLocationStruct::Type locationStruct; + VerifyOrDie(!aUniqueLocationId.empty()); + locationStruct.uniqueLocationID = CharSpan(aUniqueLocationId.c_str(), aUniqueLocationId.size()); + locationStruct.locationDescriptor = GetEncodableLocationDescriptorStruct(mLocationDescriptor); + locationStruct.locationDescriptorLastEdit = mLocationDescriptorLastEditEpochUs; + + // TODO(#33223) this is a hack, use mFabricIndex when it exists. + locationStruct.SetFabricIndex(aFabricIndex); + return aEncoder.Encode(locationStruct); +} + +EcosystemInformationServer EcosystemInformationServer::mInstance; + +EcosystemInformationServer & EcosystemInformationServer::Instance() +{ + return mInstance; +} + +CHIP_ERROR EcosystemInformationServer::AddDeviceInfo(EndpointId aEndpoint, std::unique_ptr aDevice) +{ + VerifyOrReturnError(aDevice, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((aEndpoint != kRootEndpointId && aEndpoint != kInvalidEndpointId), CHIP_ERROR_INVALID_ARGUMENT); + + auto & deviceInfo = mDevicesMap[aEndpoint]; + VerifyOrReturnError((deviceInfo.mDeviceDirectory.size() < kDeviceDirectoryMaxSize), CHIP_ERROR_NO_MEMORY); + deviceInfo.mDeviceDirectory.push_back(std::move(aDevice)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EcosystemInformationServer::AddLocationInfo(EndpointId aEndpoint, const std::string & aLocationId, + std::unique_ptr aLocation) +{ + VerifyOrReturnError(aLocation, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((aEndpoint != kRootEndpointId && aEndpoint != kInvalidEndpointId), CHIP_ERROR_INVALID_ARGUMENT); + + auto & deviceInfo = mDevicesMap[aEndpoint]; + VerifyOrReturnError((deviceInfo.mLocationDirectory.find(aLocationId) == deviceInfo.mLocationDirectory.end()), + CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((deviceInfo.mLocationDirectory.size() < kLocationDirectoryMaxSize), CHIP_ERROR_NO_MEMORY); + deviceInfo.mLocationDirectory[aLocationId] = std::move(aLocation); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EcosystemInformationServer::RemoveDevice(EndpointId aEndpoint, uint64_t aEpochUs) +{ + auto it = mDevicesMap.find(aEndpoint); + VerifyOrReturnError((it != mDevicesMap.end()), CHIP_ERROR_INVALID_ARGUMENT); + auto & deviceInfo = it->second; + deviceInfo.mRemovedOn.SetValue(aEpochUs); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EcosystemInformationServer::EncodeRemovedOnAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder) +{ + auto it = mDevicesMap.find(aEndpoint); + if (it == mDevicesMap.end()) + { + // We are always going to be given a valid endpoint. If the endpoint + // doesn't exist in our map that indicate that the cluster was not + // added on this endpoint, hence UnsupportedCluster. + return CHIP_IM_GLOBAL_STATUS(UnsupportedCluster); + } + + auto & deviceInfo = it->second; + if (!deviceInfo.mRemovedOn.HasValue()) + { + aEncoder.EncodeNull(); + return CHIP_NO_ERROR; + } + + aEncoder.Encode(deviceInfo.mRemovedOn.Value()); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EcosystemInformationServer::EncodeDeviceDirectoryAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder) +{ + + auto it = mDevicesMap.find(aEndpoint); + if (it == mDevicesMap.end()) + { + // We are always going to be given a valid endpoint. If the endpoint + // doesn't exist in our map that indicate that the cluster was not + // added on this endpoint, hence UnsupportedCluster. + return CHIP_IM_GLOBAL_STATUS(UnsupportedCluster); + } + + auto & deviceInfo = it->second; + if (deviceInfo.mDeviceDirectory.empty() || deviceInfo.mRemovedOn.HasValue()) + { + return aEncoder.EncodeEmptyList(); + } + + FabricIndex fabricIndex = aEncoder.AccessingFabricIndex(); + return aEncoder.EncodeList([&](const auto & encoder) -> CHIP_ERROR { + for (auto & device : deviceInfo.mDeviceDirectory) + { + ReturnErrorOnFailure(device->Encode(encoder, fabricIndex)); + } + return CHIP_NO_ERROR; + }); +} + +CHIP_ERROR EcosystemInformationServer::EncodeLocationStructAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder) +{ + auto it = mDevicesMap.find(aEndpoint); + if (it == mDevicesMap.end()) + { + // We are always going to be given a valid endpoint. If the endpoint + // doesn't exist in our map that indicate that the cluster was not + // added on this endpoint, hence UnsupportedCluster. + return CHIP_IM_GLOBAL_STATUS(UnsupportedCluster); + } + + auto & deviceInfo = it->second; + if (deviceInfo.mLocationDirectory.empty() || deviceInfo.mRemovedOn.HasValue()) + { + return aEncoder.EncodeEmptyList(); + } + + FabricIndex fabricIndex = aEncoder.AccessingFabricIndex(); + return aEncoder.EncodeList([&](const auto & encoder) -> CHIP_ERROR { + for (auto & [id, device] : deviceInfo.mLocationDirectory) + { + ReturnErrorOnFailure(device->Encode(encoder, id, fabricIndex)); + } + return CHIP_NO_ERROR; + }); + return CHIP_NO_ERROR; +} + +} // namespace EcosystemInformation +} // namespace Clusters +} // namespace app +} // namespace chip + +// ----------------------------------------------------------------------------- +// Plugin initialization + +chip::app::Clusters::EcosystemInformation::AttrAccess gAttrAccess; + +void MatterEcosystemInformationPluginServerInitCallback() +{ + registerAttributeAccessOverride(&gAttrAccess); +} diff --git a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h new file mode 100644 index 00000000000000..f8407861992d3c --- /dev/null +++ b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h @@ -0,0 +1,205 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +// This cluster is targeted by devices that are not resource constrained, for +// that reason we use std containers to simplify implementation of the cluster. +#include +#include +#include +#include +#include + +#include + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace EcosystemInformation { + +// This intentionally mirrors Structs::EcosystemDeviceStruct::Type but has ownership +// of underlying types. +class EcosystemDeviceStruct +{ +public: + class Builder + { + public: + Builder(){}; + + Builder & SetDeviceName(std::string aDeviceName, uint64_t aDeviceNameLastEditEpochUs); + Builder & SetBrigedEndpoint(EndpointId aBridgedEndpoint); + Builder & SetOriginalEndpoint(EndpointId aOriginalEndpoint); + Builder & AddDeviceType(Structs::DeviceTypeStruct::Type aDeviceType); + Builder & AddUniqueLocationId(std::string aUniqueLocationId, uint64_t aUniqueLocationIdsLastEditEpochUs); + + // Upon success this object will have moved all ownership of underlying + // types to EcosystemDeviceStruct and should not be used afterwards. + std::unique_ptr Build(); + + private: + std::string mDeviceName; + uint64_t mDeviceNameLastEditEpochUs = 0; + EndpointId mBridgedEndpoint = kInvalidEndpointId; + EndpointId mOriginalEndpoint = kInvalidEndpointId; + std::vector mDeviceTypes; + std::vector mUniqueLocationIds; + uint64_t mUniqueLocationIdsLastEditEpochUs = 0; + bool mIsAlreadyBuilt = false; + }; + + CHIP_ERROR Encode(const AttributeValueEncoder::ListEncodeHelper & aEncoder, const FabricIndex & aFabricIndex); + +private: + // Constructor is intentionally private. This is to ensure that it is only constructed with + // values that conform to the spec. + explicit EcosystemDeviceStruct(std::string && aDeviceName, uint64_t aDeviceNameLastEditEpochUs, EndpointId aBridgedEndpoint, + EndpointId aOriginalEndpoint, std::vector && aDeviceTypes, + std::vector && aUniqueLocationIds, uint64_t aUniqueLocationIdsLastEditEpochUs) : + mDeviceName(std::move(aDeviceName)), + mDeviceNameLastEditEpochUs(aDeviceNameLastEditEpochUs), mBridgedEndpoint(aBridgedEndpoint), + mOriginalEndpoint(aOriginalEndpoint), mDeviceTypes(std::move(aDeviceTypes)), + mUniqueLocationIds(std::move(aUniqueLocationIds)), mUniqueLocationIdsLastEditEpochUs(aUniqueLocationIdsLastEditEpochUs) + {} + + const std::string mDeviceName; + uint64_t mDeviceNameLastEditEpochUs; + EndpointId mBridgedEndpoint; + EndpointId mOriginalEndpoint; + std::vector mDeviceTypes; + std::vector mUniqueLocationIds; + uint64_t mUniqueLocationIdsLastEditEpochUs; + // TODO(#33223) This structure needs to contain fabric index to be spec compliant. + // To keep initial PR smaller, we are going to assume that all entries + // here are for any fabric. This will allow follow up PR introducing + // fabric scoped to be more throughly reviewed with focus on fabric scoping. +}; + +struct LocationDescriptorStruct +{ + std::string mLocationName; + std::optional mFloorNumber; + std::optional mAreaType; +}; + +// This intentionally mirrors Structs::EcosystemLocationStruct::Type but has ownership +// of underlying types. +class EcosystemLocationStruct +{ +public: + class Builder + { + public: + Builder(){}; + + Builder & SetLocationName(std::string aLocationName); + Builder & SetFloorNumber(std::optional aFloorNumber); + Builder & SetAreaTypeTag(std::optional aAreaTypeTag); + Builder & SetLocationDescriptorLastEdit(uint64_t aLocationDescriptorLastEditEpochUs); + + // Upon success this object will have moved all ownership of underlying + // types to EcosystemDeviceStruct and should not be used afterwards. + std::unique_ptr Build(); + + private: + LocationDescriptorStruct mLocationDescriptor; + uint64_t mLocationDescriptorLastEditEpochUs = 0; + bool mIsAlreadyBuilt = false; + }; + + CHIP_ERROR Encode(const AttributeValueEncoder::ListEncodeHelper & aEncoder, const std::string & aUniqueLocationId, + const FabricIndex & aFabricIndex); + +private: + // Constructor is intentionally private. This is to ensure that it is only constructed with + // values that conform to the spec. + explicit EcosystemLocationStruct(LocationDescriptorStruct && aLocationDescriptor, uint64_t aLocationDescriptorLastEditEpochUs) : + mLocationDescriptor(aLocationDescriptor), mLocationDescriptorLastEditEpochUs(aLocationDescriptorLastEditEpochUs) + {} + // EcosystemLocationStruct is used as a value in a key-value map. + // Because UniqueLocationId is manditory when an entry exist, and + // it is unique, we use it as a key to the key-value pair and is why it is + // not explicitly in this struct. + LocationDescriptorStruct mLocationDescriptor; + uint64_t mLocationDescriptorLastEditEpochUs; + // TODO(#33223) This structure needs to contain fabric index to be spec compliant. + // To keep initial PR smaller, we are going to assume that all entries + // here are for any fabric. This will allow follow up PR introducing + // fabric scoped to be more throughly reviewed with focus on fabric scoping. +}; + +class EcosystemInformationServer +{ +public: + static EcosystemInformationServer & Instance(); + + /** + * @brief Adds device as entry to DeviceDirectory list Attribute. + * + * @param[in] aEndpoint Which endpoint is the device being added to the device directory. + * @param[in] aDevice Device information. + * @return #CHIP_NO_ERROR on success. + * @return Other CHIP_ERROR associated with issue. + */ + CHIP_ERROR AddDeviceInfo(EndpointId aEndpoint, std::unique_ptr aDevice); + /** + * @brief Adds location as entry to LocationDirectory list Attribute. + * + * @param[in] aEndpoint Which endpoint is the location being added to the location directory. + * @param[in] aLocationId LocationID associated with location. + * @param[in] aLocation Location information. + * @return #CHIP_NO_ERROR on success. + * @return Other CHIP_ERROR associated with issue. + */ + CHIP_ERROR AddLocationInfo(EndpointId aEndpoint, const std::string & aLocationId, + std::unique_ptr aLocation); + + /** + * @brief Removes device at the provided endpoint. + * + * @param aEndpoint Endpoint of the associated device that has been removed. + * @param aEpochUs Epoch time in micro seconds assoicated with when device was removed. + * @return #CHIP_NO_ERROR on success. + * @return Other CHIP_ERROR associated with issue. + */ + CHIP_ERROR RemoveDevice(EndpointId aEndpoint, uint64_t aEpochUs); + // TODO(#33223) Add removal and update counterparts to AddDeviceInfo and AddLocationInfo. + + CHIP_ERROR EncodeRemovedOnAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder); + CHIP_ERROR EncodeDeviceDirectoryAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder); + CHIP_ERROR EncodeLocationStructAttribute(EndpointId aEndpoint, AttributeValueEncoder & aEncoder); + +private: + struct DeviceInfo + { + Optional mRemovedOn; + std::vector> mDeviceDirectory; + // Map key is using the UniqueLocationId + std::map> mLocationDirectory; + }; + std::map mDevicesMap; + + static EcosystemInformationServer mInstance; +}; + +} // namespace EcosystemInformation +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index 285bd6e6398b73..4852b22b3bfa68 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -186,7 +186,7 @@ "DISHWASHER_MODE_CLUSTER": ["mode-base-server"], "MICROWAVE_OVEN_MODE_CLUSTER": ["mode-base-server"], "DOOR_LOCK_CLUSTER": ["door-lock-server"], - "ECOSYSTEM_INFORMATION_CLUSTER": [], + "ECOSYSTEM_INFORMATION_CLUSTER": ["ecosystem-information-server"], "ELECTRICAL_ENERGY_MEASUREMENT_CLUSTER": [ "electrical-energy-measurement-server" ],