diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index b3ae5b1f385a74..414a178e9c9c0a 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -8792,8 +8792,10 @@ endpoint 1 { ram attribute occupancy; ram attribute occupancySensorType; ram attribute occupancySensorTypeBitmap; - ram attribute featureMap default = 0; - ram attribute clusterRevision default = 4; + ram attribute holdTime default = 10; + callback attribute holdTimeLimits; + ram attribute featureMap default = 0x01; + ram attribute clusterRevision default = 5; } server cluster CarbonMonoxideConcentrationMeasurement { @@ -9261,8 +9263,10 @@ endpoint 2 { ram attribute occupancy; ram attribute occupancySensorType; ram attribute occupancySensorTypeBitmap; - ram attribute featureMap default = 0; - ram attribute clusterRevision default = 4; + ram attribute holdTime default = 20; + callback attribute holdTimeLimits; + ram attribute featureMap default = 0x01; + ram attribute clusterRevision default = 5; } } endpoint 65534 { diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index c547030731c6cf..823117db7cad17 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -18716,6 +18716,38 @@ "maxInterval": 65344, "reportableChange": 0 }, + { + "name": "HoldTime", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "10", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "HoldTimeLimits", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "HoldTimeLimitsStruct", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "FeatureMap", "code": 65532, @@ -18726,7 +18758,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "0", + "defaultValue": "0x01", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -18742,7 +18774,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "4", + "defaultValue": "5", "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -21900,6 +21932,14 @@ "isIncoming": 1, "isEnabled": 1 }, + { + "name": "StringEchoResponse", + "code": 13, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, { "name": "TestEnumsRequest", "code": 14, @@ -21973,32 +22013,24 @@ "isEnabled": 1 }, { - "name": "TestDifferentVendorMeiRequest", - "code": 4294049962, + "name": "StringEchoRequest", + "code": 24, "mfgCode": null, "source": "client", "isIncoming": 1, "isEnabled": 1 }, { - "name": "TestDifferentVendorMeiResponse", - "code": 4294049979, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "StringEchoRequest", - "code": 24, + "name": "TestDifferentVendorMeiRequest", + "code": 4294049962, "mfgCode": null, "source": "client", "isIncoming": 1, "isEnabled": 1 }, { - "name": "StringEchoResponse", - "code": 13, + "name": "TestDifferentVendorMeiResponse", + "code": 4294049979, "mfgCode": null, "source": "server", "isIncoming": 0, @@ -24745,6 +24777,38 @@ "maxInterval": 65344, "reportableChange": 0 }, + { + "name": "HoldTime", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "20", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "HoldTimeLimits", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "HoldTimeLimitsStruct", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "FeatureMap", "code": 65532, @@ -24755,7 +24819,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "0", + "defaultValue": "0x01", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -24771,7 +24835,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "4", + "defaultValue": "5", "reportable": 1, "minInterval": 0, "maxInterval": 65344, diff --git a/examples/all-clusters-app/all-clusters-common/src/occupancy-sensing-stub.cpp b/examples/all-clusters-app/all-clusters-common/src/occupancy-sensing-stub.cpp new file mode 100644 index 00000000000000..dcbb90ff659bcb --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/occupancy-sensing-stub.cpp @@ -0,0 +1,57 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * 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 +#include +#include +#include + +using namespace chip; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::OccupancySensing; +using namespace chip::app::Clusters::OccupancySensing::Structs; +using namespace chip::DeviceLayer; + +using chip::Protocols::InteractionModel::Status; + +static std::unique_ptr + gAttrAccess[MATTER_DM_OCCUPANCY_SENSING_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; + +void emberAfOccupancySensingClusterInitCallback(EndpointId endpointId) +{ + VerifyOrDie(!gAttrAccess[endpointId]); + + gAttrAccess[endpointId] = std::make_unique( + BitMask(OccupancySensing::Feature::kOther)); + + OccupancySensing::Structs::HoldTimeLimitsStruct::Type holdTimeLimits = { + .holdTimeMin = 1, + .holdTimeMax = 300, + .holdTimeDefault = 10, + }; + + uint16_t holdTime = 10; + + if (gAttrAccess[endpointId]) + { + gAttrAccess[endpointId]->Init(); + + SetHoldTimeLimits(endpointId, holdTimeLimits); + + SetHoldTime(endpointId, holdTime); + } +} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 3d52ef748de90d..7b0c70122217aa 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -46,6 +46,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-washer-controls-delegate-impl.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-washer-mode.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/microwave-oven-mode.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/occupancy-sensing-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/oven-modes.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/oven-operational-state-delegate.cpp", diff --git a/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.cpp b/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.cpp index 50056387e3c359..4a3ba4103a0b0d 100644 --- a/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.cpp +++ b/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.cpp @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,158 @@ */ #include "occupancy-sensor-server.h" +#include "occupancy-hal.h" -#include +#include +#include +#include +#include +#include +#include -#include "occupancy-hal.h" +using chip::Protocols::InteractionModel::Status; + +namespace chip { +namespace app { +namespace Clusters { +namespace OccupancySensing { + +namespace { +Structs::HoldTimeLimitsStruct::Type + sHoldTimeLimitsStructs[MATTER_DM_OCCUPANCY_SENSING_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; + +uint16_t sHoldTime[MATTER_DM_OCCUPANCY_SENSING_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; +} // namespace + +CHIP_ERROR OccupancySensingAttrAccess::Init() +{ + VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); + return CHIP_NO_ERROR; +} + +void OccupancySensingAttrAccess::Shutdown() +{ + unregisterAttributeAccessOverride(this); +} + +CHIP_ERROR OccupancySensingAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + VerifyOrDie(aPath.mClusterId == app::Clusters::OccupancySensing::Id); + + switch (aPath.mAttributeId) + { + case Attributes::FeatureMap::Id: + ReturnErrorOnFailure(aEncoder.Encode(mFeature)); + break; + case Attributes::HoldTime::Id: { + + uint16_t * holdTime = GetHoldTimeForEndpoint(aPath.mEndpointId); + + if (holdTime == nullptr) + { + return CHIP_ERROR_NOT_FOUND; + } + + return aEncoder.Encode(*holdTime); + } + case Attributes::HoldTimeLimits::Id: { + + Structs::HoldTimeLimitsStruct::Type * holdTimeLimitsStruct = GetHoldTimeLimitsForEndpoint(aPath.mEndpointId); + + if (holdTimeLimitsStruct == nullptr) + { + return CHIP_ERROR_NOT_FOUND; + } + + return aEncoder.Encode(*holdTimeLimitsStruct); + } + default: + return CHIP_NO_ERROR; + } + + return CHIP_NO_ERROR; +} + +bool OccupancySensingAttrAccess::HasFeature(Feature aFeature) const +{ + return mFeature.Has(aFeature); +} + +Structs::HoldTimeLimitsStruct::Type * GetHoldTimeLimitsForEndpoint(EndpointId endpoint) +{ + auto index = emberAfGetClusterServerEndpointIndex(endpoint, app::Clusters::OccupancySensing::Id, + MATTER_DM_OCCUPANCY_SENSING_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (index == kEmberInvalidEndpointIndex) + { + return nullptr; + } + + if (index >= ArraySize(sHoldTimeLimitsStructs)) + { + ChipLogError(NotSpecified, "Internal error: invalid/unexpected hold time limits index."); + return nullptr; + } + return &sHoldTimeLimitsStructs[index]; +} + +CHIP_ERROR SetHoldTimeLimits(EndpointId endpointId, const Structs::HoldTimeLimitsStruct::Type & holdTimeLimits) +{ + + VerifyOrReturnError(kInvalidEndpointId != endpointId, CHIP_ERROR_INVALID_ARGUMENT); + + Structs::HoldTimeLimitsStruct::Type * holdTimeLimitsForEndpoint = GetHoldTimeLimitsForEndpoint(endpointId); + VerifyOrReturnError(holdTimeLimitsForEndpoint != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + holdTimeLimitsForEndpoint->holdTimeMin = holdTimeLimits.holdTimeMin; + holdTimeLimitsForEndpoint->holdTimeMax = holdTimeLimits.holdTimeMax; + holdTimeLimitsForEndpoint->holdTimeDefault = holdTimeLimits.holdTimeDefault; + + MatterReportingAttributeChangeCallback(endpointId, OccupancySensing::Id, Attributes::HoldTimeLimits::Id); + + return CHIP_NO_ERROR; +} + +uint16_t * GetHoldTimeForEndpoint(EndpointId endpoint) +{ + auto index = emberAfGetClusterServerEndpointIndex(endpoint, app::Clusters::OccupancySensing::Id, + MATTER_DM_OCCUPANCY_SENSING_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (index == kEmberInvalidEndpointIndex) + { + return nullptr; + } + + if (index >= ArraySize(sHoldTimeLimitsStructs)) + { + ChipLogError(NotSpecified, "Internal error: invalid/unexpected hold time index."); + return nullptr; + } + return &sHoldTime[index]; +} + +CHIP_ERROR SetHoldTime(EndpointId endpointId, const uint16_t & holdTime) +{ + VerifyOrReturnError(kInvalidEndpointId != endpointId, CHIP_ERROR_INVALID_ARGUMENT); + + uint16_t * holdTimeForEndpoint = GetHoldTimeForEndpoint(endpointId); + VerifyOrReturnError(holdTimeForEndpoint != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + *holdTimeForEndpoint = holdTime; + + MatterReportingAttributeChangeCallback(endpointId, OccupancySensing::Id, Attributes::HoldTime::Id); + + return CHIP_NO_ERROR; +} + +} // namespace OccupancySensing +} // namespace Clusters +} // namespace app +} // namespace chip using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; using namespace chip::app::Clusters::OccupancySensing; //****************************************************************************** @@ -59,8 +205,6 @@ void emberAfOccupancySensingClusterServerInitCallback(EndpointId endpoint) break; } Attributes::OccupancySensorTypeBitmap::Set(endpoint, deviceTypeBitmap); - - emberAfPluginOccupancyClusterServerPostInitCallback(endpoint); } //****************************************************************************** @@ -82,8 +226,6 @@ void halOccupancyStateChangedCallback(EndpointId endpoint, HalOccupancyState occ Attributes::Occupancy::Set(endpoint, occupancyState); } -void emberAfPluginOccupancyClusterServerPostInitCallback(EndpointId endpoint) {} - HalOccupancySensorType __attribute__((weak)) halOccupancyGetSensorType(EndpointId endpoint) { return HAL_OCCUPANCY_SENSOR_TYPE_PIR; diff --git a/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.h b/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.h index 3ed762cbfb4013..f24c64f4dbfbb1 100644 --- a/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.h +++ b/src/app/clusters/occupancy-sensor-server/occupancy-sensor-server.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,48 @@ #pragma once +#include +#include +#include #include #include +#include +#include -/** @brief Occupancy Cluster Server Post Init - * - * Following resolution of the Occupancy state at startup for this endpoint, - * perform any additional initialization needed; e.g., synchronize hardware - * state. - * - * @param endpoint Endpoint that is being initialized Ver.: always - */ -void emberAfPluginOccupancyClusterServerPostInitCallback(chip::EndpointId endpoint); +namespace chip { +namespace app { +namespace Clusters { +namespace OccupancySensing { + +class OccupancySensingAttrAccess : public AttributeAccessInterface +{ +public: + OccupancySensingAttrAccess(BitMask aFeature) : + app::AttributeAccessInterface(Optional::Missing(), app::Clusters::OccupancySensing::Id), mFeature(aFeature) + {} + + ~OccupancySensingAttrAccess() { Shutdown(); } + + CHIP_ERROR Init(); + void Shutdown(); + + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + + bool HasFeature(Feature aFeature) const; + +private: + BitMask mFeature; +}; + +CHIP_ERROR SetHoldTimeLimits(EndpointId endpointId, const Structs::HoldTimeLimitsStruct::Type & holdTimeLimits); + +CHIP_ERROR SetHoldTime(EndpointId endpointId, const uint16_t & holdTime); + +Structs::HoldTimeLimitsStruct::Type * GetHoldTimeLimitsForEndpoint(EndpointId endpoint); + +uint16_t * GetHoldTimeForEndpoint(EndpointId endpoint); + +} // namespace OccupancySensing +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index dc324968991adc..ada4bde50f32b9 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -71,7 +71,7 @@ "NETWORK_COMMISSIONING_CLUSTER": [], "SAMPLE_MEI_CLUSTER": [], "NITROGEN_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER": [], - "OCCUPANCY_SENSING_CLUSTER": ["occupancy-sensor-server"], + "OCCUPANCY_SENSING_CLUSTER": [], "ON_OFF_CLUSTER": [], "ON_OFF_SWITCH_CONFIGURATION_CLUSTER": [], "OPERATIONAL_CREDENTIALS_CLUSTER": [],