diff --git a/examples/air-quality-sensor-app/air-quality-sensor-common/air-quality-sensor-app.matter b/examples/air-quality-sensor-app/air-quality-sensor-common/air-quality-sensor-app.matter index bfe7a2737aac8c..324835a1268058 100644 --- a/examples/air-quality-sensor-app/air-quality-sensor-common/air-quality-sensor-app.matter +++ b/examples/air-quality-sensor-app/air-quality-sensor-common/air-quality-sensor-app.matter @@ -2046,7 +2046,7 @@ endpoint 1 { } server cluster AirQuality { - ram attribute airQuality default = 0; + callback attribute airQuality default = 0; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; 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 7fbee2897d73bf..a1f968b5fc5536 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 @@ -6772,7 +6772,7 @@ endpoint 1 { } server cluster AirQuality { - ram attribute airQuality default = 0; + callback attribute airQuality default = 0; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute attributeList; diff --git a/examples/all-clusters-app/all-clusters-common/include/air-quality-instance.h b/examples/all-clusters-app/all-clusters-common/include/air-quality-instance.h new file mode 100644 index 00000000000000..eb2b7c89288da4 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/air-quality-instance.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace AirQuality { + +void Shutdown(); + +} // namespace AirQuality +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/src/air-quality-instance.cpp b/examples/all-clusters-app/all-clusters-common/src/air-quality-instance.cpp new file mode 100644 index 00000000000000..eb5f4dc5607dc3 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/air-quality-instance.cpp @@ -0,0 +1,25 @@ +#include + +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::AirQuality; + +Instance * gAirQualityCluster = nullptr; + +void AirQuality::Shutdown() +{ + if (gAirQualityCluster != nullptr) + { + delete gAirQualityCluster; + gAirQualityCluster = nullptr; + } +} + +void emberAfAirQualityClusterInitCallback(chip::EndpointId endpointId) +{ + VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1. + VerifyOrDie(gAirQualityCluster == nullptr); + chip::BitMask airQualityFeatures(Feature::kModerate, Feature::kFair, Feature::kVeryPoor, + Feature::kExtremelyPoor); + gAirQualityCluster = new Instance(1, airQualityFeatures.Raw()); + gAirQualityCluster->Init(); +} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 457f911debc9b0..5bc7858e2d6306 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -21,6 +21,7 @@ import("${chip_root}/src/platform/device.gni") source_set("chip-all-clusters-common") { sources = [ + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/air-quality-instance.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/binding-handler.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/bridged-actions-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/dishwasher-alarm-stub.cpp", diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp index 3f0c5071b13de1..2eae63b5541f4d 100644 --- a/examples/all-clusters-app/linux/main-common.cpp +++ b/examples/all-clusters-app/linux/main-common.cpp @@ -18,6 +18,7 @@ #include "AllClustersCommandDelegate.h" #include "WindowCoveringManager.h" +#include "air-quality-instance.h" #include "dishwasher-mode.h" #include "include/tv-callbacks.h" #include "laundry-washer-controls-delegate-impl.h" @@ -196,6 +197,7 @@ void ApplicationShutdown() Clusters::RvcRunMode::Shutdown(); Clusters::RefrigeratorAndTemperatureControlledCabinetMode::Shutdown(); + Clusters::AirQuality::Shutdown(); Clusters::OperationalState::Shutdown(); Clusters::RvcOperationalState::Shutdown(); diff --git a/examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter b/examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter index ad28e8f41c59f0..f153239a0ad6ad 100644 --- a/examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter +++ b/examples/chef/devices/rootnode_airpurifier_airqualitysensor_temperaturesensor_humiditysensor_thermostat_56de3d5f45.matter @@ -1994,7 +1994,7 @@ endpoint 2 { } server cluster AirQuality { - ram attribute airQuality default = 0; + callback attribute airQuality default = 0; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 62176c6530b4b8..0bab48f1331f9a 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -301,6 +301,11 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/SmokeCOTestEventTriggerDelegate.cpp", "${_app_root}/clusters/${cluster}/SmokeCOTestEventTriggerDelegate.h", ] + } else if (cluster == "air-quality-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/${cluster}.h", + ] } else { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ] } diff --git a/src/app/clusters/air-quality-server/air-quality-server.cpp b/src/app/clusters/air-quality-server/air-quality-server.cpp new file mode 100644 index 00000000000000..938c158e49ca56 --- /dev/null +++ b/src/app/clusters/air-quality-server/air-quality-server.cpp @@ -0,0 +1,120 @@ +/* + * + * Copyright (c) 2023 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 "app-common/zap-generated/ids/Clusters.h" +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using chip::Protocols::InteractionModel::Status; + +namespace chip { +namespace app { +namespace Clusters { +namespace AirQuality { + +Instance::Instance(EndpointId aEndpointId, uint32_t aFeature) : + AttributeAccessInterface(Optional(aEndpointId), Id), mEndpointId(aEndpointId), mFeature(aFeature) +{} + +Instance::~Instance() +{ + unregisterAttributeAccessOverride(this); +} + +CHIP_ERROR Instance::Init() +{ + // Check if the cluster has been selected in zap + VerifyOrDie(emberAfContainsServer(mEndpointId, Id) == true); + + VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); + + return CHIP_NO_ERROR; +} + +bool Instance::HasFeature(AirQualityEnum aFeature) const +{ + return mFeature & to_underlying(aFeature); +} + +Protocols::InteractionModel::Status Instance::UpdateAirQuality(AirQualityEnum aNewAirQuality) +{ + // Check that the value in is valid according to the enabled features. + switch (aNewAirQuality) + { + case AirQualityEnum::kFair: { + if (!HasFeature(AirQualityEnum::kFair)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + } + break; + case AirQualityEnum::kModerate: { + if (!HasFeature(AirQualityEnum::kModerate)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + } + break; + case AirQualityEnum::kPoor: { + if (!HasFeature(AirQualityEnum::kPoor)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + } + break; + case AirQualityEnum::kExtremelyPoor: { + if (!HasFeature(AirQualityEnum::kExtremelyPoor)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + } + break; + default: + break; + } + + mAirQuality = aNewAirQuality; + MatterReportingAttributeChangeCallback(ConcreteAttributePath(mEndpointId, Id, Attributes::AirQuality::Id)); + return Protocols::InteractionModel::Status::Success; +} + +AirQualityEnum Instance::GetAirQuality() +{ + return mAirQuality; +} + +CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + switch (aPath.mAttributeId) + { + case Attributes::AirQuality::Id: + ReturnErrorOnFailure(aEncoder.Encode(mAirQuality)); + break; + } + return CHIP_NO_ERROR; +} + +} // namespace AirQuality +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/air-quality-server/air-quality-server.h b/src/app/clusters/air-quality-server/air-quality-server.h new file mode 100644 index 00000000000000..90abf3b17e290a --- /dev/null +++ b/src/app/clusters/air-quality-server/air-quality-server.h @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2023 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 + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace AirQuality { + +class Instance : public AttributeAccessInterface +{ +public: + /** + * Creates an air quality cluster instance. The Init() function needs to be called for this instance to be registered and + * called by the interaction model at the appropriate times. + * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. + * @param aFeature The bitmask value that identifies which features are supported by this instance. + */ + Instance(EndpointId aEndpointId, uint32_t eFeature); + + ~Instance() override; + + /** + * Initialises the air quality cluster instance + * @return Returns an error if an air quality cluster has not been enabled in zap for the given endpoint ID or + * if the AttributeHandler registration fails. + */ + CHIP_ERROR Init(); + + /** + * Returns true if the feature is supported. + * @param feature the feature to check. + */ + bool HasFeature(AirQualityEnum) const; + + /** + * Sets the AirQuality attribute. + * @param aNewAirQuality The value to which the AirQuality attribute is to be set. + * @return Returns a ConstraintError if the aNewAirQuality value is not valid. Returns Success otherwise. + */ + Protocols::InteractionModel::Status UpdateAirQuality(AirQualityEnum aNewAirQuality); + + /** + * @return The current AirQuality enum. + */ + AirQualityEnum GetAirQuality(); + +private: + EndpointId mEndpointId; + AirQualityEnum mAirQuality = AirQualityEnum::kUnknown; + uint32_t mFeature; + + // AttributeAccessInterface + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; +}; + +} // namespace AirQuality +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index 7a7c992d6f3084..3f0bc893b36929 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -378,7 +378,8 @@ "LastChangedTime", "ReplacementProductList", "FeatureMap" - ] + ], + "Air Quality": ["AirQuality", "FeatureMap"] }, "defaultReportingPolicy": "mandatory", "ZCLDataTypes": ["ARRAY", "BITMAP", "ENUM", "NUMBER", "STRING", "STRUCT"], diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index 73475a6196662f..fda0bcab98615b 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -376,7 +376,8 @@ "LastChangedTime", "ReplacementProductList", "FeatureMap" - ] + ], + "Air Quality": ["AirQuality", "FeatureMap"] }, "defaultReportingPolicy": "mandatory", "ZCLDataTypes": ["ARRAY", "BITMAP", "ENUM", "NUMBER", "STRING", "STRUCT"], diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index 1212b7b28ab8ff..29193fb85d6c3f 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -115,7 +115,7 @@ "ServerDirectories": { "ACCESS_CONTROL_CLUSTER": ["access-control-server"], "ACCOUNT_LOGIN_CLUSTER": ["account-login-server"], - "ACTIONS_CLUSTER": [], + "ACTIONS_CLUSTER": ["air-quality-server"], "ACTIVATED_CARBON_FILTER_MONITORING_CLUSTER": [ "resource-monitoring-server" ], diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp index df0f618f25c559..6bb84885eb7489 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp @@ -7869,37 +7869,6 @@ EmberAfStatus Set(chip::EndpointId endpoint, uint16_t value) namespace AirQuality { namespace Attributes { -namespace AirQuality { - -EmberAfStatus Get(chip::EndpointId endpoint, chip::app::Clusters::AirQuality::AirQualityEnum * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - EmberAfStatus status = emberAfReadAttribute(endpoint, Clusters::AirQuality::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return EMBER_ZCL_STATUS_CONSTRAINT_ERROR; - } - *value = Traits::StorageToWorking(temp); - return status; -} -EmberAfStatus Set(chip::EndpointId endpoint, chip::app::Clusters::AirQuality::AirQualityEnum value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return EMBER_ZCL_STATUS_CONSTRAINT_ERROR; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::AirQuality::Id, Id, writable, ZCL_ENUM8_ATTRIBUTE_TYPE); -} - -} // namespace AirQuality - namespace FeatureMap { EmberAfStatus Get(chip::EndpointId endpoint, uint32_t * value) diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h index db0d78a387c052..7b2289af2895be 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h @@ -1526,11 +1526,6 @@ EmberAfStatus Set(chip::EndpointId endpoint, uint16_t value); namespace AirQuality { namespace Attributes { -namespace AirQuality { -EmberAfStatus Get(chip::EndpointId endpoint, chip::app::Clusters::AirQuality::AirQualityEnum * value); // AirQualityEnum -EmberAfStatus Set(chip::EndpointId endpoint, chip::app::Clusters::AirQuality::AirQualityEnum value); -} // namespace AirQuality - namespace FeatureMap { EmberAfStatus Get(chip::EndpointId endpoint, uint32_t * value); // bitmap32 EmberAfStatus Set(chip::EndpointId endpoint, uint32_t value);