From 298e8b1f1d676c55f73de8241457c1525edcea79 Mon Sep 17 00:00:00 2001 From: Matt Hazley Date: Tue, 30 May 2023 13:51:49 +0100 Subject: [PATCH] Fan Control Cluster updates for Fall 2023: TE1 (#26296) * Updated fan-control-server with better support for feature checking, step command handler and checks around the supported attributes in the pre-callback * Updated fan-control-cluster.xml for TE1 for fall release and regenerated the generated code * Ensured status was always initialised before returning, added missing break in switch case * Update optional parameters of Step command and matched spec naming. Improved checking on Auto feature when receiving smart mode. * Added a stop-gap fan-stub.cpp so that the existing FanControl tests pass for TE1 * removed unused variable * fixed return after else warning * reordered files as per restyler --- .../all-clusters-app.matter | 22 ++ .../all-clusters-common/all-clusters-app.zap | 28 +- .../all-clusters-common/src/fan-stub.cpp | 115 ++++++++ examples/all-clusters-app/linux/BUILD.gn | 1 + .../all-clusters-minimal-app.matter | 12 + .../devices/rootnode_fan_7N2TobIlOX.matter | 12 + ...tnode_heatingcoolingunit_ncdGai1E5a.matter | 12 + .../rootnode_thermostat_bm3fb8dhYi.matter | 22 ++ .../fan-control-server/fan-control-server.cpp | 262 +++++++++++++----- .../data-model/chip/fan-control-cluster.xml | 23 ++ .../data_model/controller-clusters.matter | 22 ++ .../devicecontroller/ClusterIDMapping.java | 23 +- .../devicecontroller/ClusterReadMapping.java | 11 + .../devicecontroller/ClusterWriteMapping.java | 22 ++ .../CHIPAttributeTLVValueDecoder.cpp | 15 + .../zap-generated/CHIPClustersWrite-JNI.cpp | 52 ++++ .../chip/devicecontroller/ChipClusters.java | 30 ++ .../chip/devicecontroller/ChipIdLookup.java | 3 + .../python/chip/clusters/CHIPClusters.py | 16 ++ .../python/chip/clusters/Objects.py | 59 ++++ .../MTRAttributeSpecifiedCheck.mm | 3 + .../MTRAttributeTLVValueDecoder.mm | 11 + .../CHIP/zap-generated/MTRBaseClusters.h | 36 +++ .../CHIP/zap-generated/MTRBaseClusters.mm | 131 +++++++++ .../CHIP/zap-generated/MTRCallbackBridge.h | 141 ++++++++++ .../CHIP/zap-generated/MTRCallbackBridge.mm | 100 +++++++ .../CHIP/zap-generated/MTRClusterConstants.h | 6 + .../CHIP/zap-generated/MTRClusters.h | 12 + .../CHIP/zap-generated/MTRClusters.mm | 114 ++++++++ .../zap-generated/MTRCommandPayloadsObjc.h | 34 +++ .../zap-generated/MTRCommandPayloadsObjc.mm | 37 +++ .../zap-generated/attributes/Accessors.cpp | 31 +++ .../zap-generated/attributes/Accessors.h | 5 + .../app-common/zap-generated/callback.h | 6 + .../zap-generated/cluster-enums-check.h | 24 ++ .../app-common/zap-generated/cluster-enums.h | 34 ++- .../zap-generated/cluster-objects.cpp | 58 +++- .../zap-generated/cluster-objects.h | 65 +++++ .../app-common/zap-generated/ids/Attributes.h | 4 + .../app-common/zap-generated/ids/Commands.h | 10 + .../zap-generated/cluster/Commands.h | 40 +++ .../cluster/logging/DataModelLogger.cpp | 5 + .../zap-generated/cluster/Commands.h | 174 ++++++++++++ 43 files changed, 1773 insertions(+), 70 deletions(-) create mode 100644 examples/all-clusters-app/all-clusters-common/src/fan-stub.cpp 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 95e20cb968be24..b087a5f8a997cc 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 @@ -3285,6 +3285,16 @@ server cluster Thermostat = 513 { /** An interface for controlling a fan in a heating/cooling system. */ server cluster FanControl = 514 { + enum AirflowDirectionEnum : ENUM8 { + kForward = 0; + kReverse = 1; + } + + enum DirectionEnum : ENUM8 { + kIncrease = 0; + kDecrease = 1; + } + enum FanModeSequenceType : ENUM8 { kOffLowMedHigh = 0; kOffLowHigh = 1; @@ -3309,6 +3319,8 @@ server cluster FanControl = 514 { kAuto = 0x2; kRocking = 0x4; kWind = 0x8; + kStep = 0x10; + kAirflowDirection = 0x20; } bitmap RockSupportMask : BITMAP8 { @@ -3338,12 +3350,21 @@ server cluster FanControl = 514 { attribute bitmap8 rockSetting = 8; readonly attribute bitmap8 windSupport = 9; attribute bitmap8 windSetting = 10; + attribute AirflowDirectionEnum airflowDirection = 11; readonly attribute command_id generatedCommandList[] = 65528; readonly attribute command_id acceptedCommandList[] = 65529; readonly attribute event_id eventList[] = 65530; readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; + + request struct StepRequest { + DirectionEnum direction = 0; + optional boolean wrap = 1; + optional boolean lowestOff = 2; + } + + command Step(StepRequest): DefaultSuccess = 0; } /** An interface for configuring the user interface of a thermostat (which may be remote from the thermostat). */ @@ -5330,6 +5351,7 @@ endpoint 1 { ram attribute rockSetting default = 0x00; ram attribute windSupport default = 0x00; ram attribute windSetting default = 0x00; + ram attribute airflowDirection default = 0; ram attribute featureMap default = 0x0F; ram attribute clusterRevision default = 2; } 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 c87250960da3ec..4cea97853c4758 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 @@ -15716,7 +15716,17 @@ "mfgCode": null, "define": "FAN_CONTROL_CLUSTER", "side": "client", - "enabled": 0 + "enabled": 0, + "commands": [ + { + "name": "Step", + "code": 0, + "mfgCode": null, + "source": "client", + "incoming": 1, + "outgoing": 0 + } + ] }, { "name": "Fan Control", @@ -15902,6 +15912,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "AirflowDirection", + "code": 11, + "mfgCode": null, + "side": "server", + "type": "AirflowDirectionEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, diff --git a/examples/all-clusters-app/all-clusters-common/src/fan-stub.cpp b/examples/all-clusters-app/all-clusters-common/src/fan-stub.cpp new file mode 100644 index 00000000000000..4f70f80f7ccb24 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/fan-stub.cpp @@ -0,0 +1,115 @@ +/* + * + * Copyright (c) 2023 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 +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::FanControl::Attributes; + +namespace { + +/* + * TODO: This is a stop-gap solution to allow the existing fan control cluster tests to run after changes to + * the cluster objects for TE1. This should be removed once #6496 is resolved as it will likely result in a + * FanControl delegate added to the SDK. + * + * FYI... The previous implementation of the FanControl cluster set the speedCurrent/percentCurrent when it received + * speedSetting/percentSetting. The new implementation of the FanControl cluster does not do this as this should + * really be done by the application. + */ + +class FanAttrAccess : public AttributeAccessInterface +{ +public: + // Register for the FanControl cluster on all endpoints. + FanAttrAccess() : AttributeAccessInterface(Optional::Missing(), FanControl::Id) {} + + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + +private: + CHIP_ERROR ReadPercentCurrent(EndpointId endpoint, AttributeValueEncoder & aEncoder); + CHIP_ERROR ReadSpeedCurrent(EndpointId endpoint, AttributeValueEncoder & aEncoder); +}; + +CHIP_ERROR FanAttrAccess::ReadPercentCurrent(EndpointId endpoint, AttributeValueEncoder & aEncoder) +{ + // Return PercentSetting attribute value for now + DataModel::Nullable percentSetting; + PercentSetting::Get(endpoint, percentSetting); + uint8_t ret = 0; + if (!percentSetting.IsNull()) + { + ret = percentSetting.Value(); + } + + return aEncoder.Encode(ret); +} + +CHIP_ERROR FanAttrAccess::ReadSpeedCurrent(EndpointId endpoint, AttributeValueEncoder & aEncoder) +{ + // Return SpeedCurrent attribute value for now + DataModel::Nullable speedSetting; + SpeedSetting::Get(endpoint, speedSetting); + uint8_t ret = 0; + if (!speedSetting.IsNull()) + { + ret = speedSetting.Value(); + } + + return aEncoder.Encode(ret); +} + +FanAttrAccess gAttrAccess; + +CHIP_ERROR FanAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + VerifyOrDie(aPath.mClusterId == FanControl::Id); + + switch (aPath.mAttributeId) + { + case SpeedCurrent::Id: + return ReadSpeedCurrent(aPath.mEndpointId, aEncoder); + case PercentCurrent::Id: + return ReadPercentCurrent(aPath.mEndpointId, aEncoder); + default: + break; + } + return CHIP_NO_ERROR; +} +} // anonymous namespace + +void emberAfFanControlClusterInitCallback(EndpointId endpoint) +{ + uint32_t featureMap = 0; + + featureMap |= to_underlying(FanControl::Feature::kMultiSpeed); + featureMap |= to_underlying(FanControl::Feature::kMultiSpeed); + featureMap |= to_underlying(FanControl::Feature::kAuto); + + FeatureMap::Set(endpoint, featureMap); + + registerAttributeAccessOverride(&gAttrAccess); +} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 1b8ff71bc66e76..402649c13bba05 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -23,6 +23,7 @@ source_set("chip-all-clusters-common") { sources = [ "${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/fan-stub.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "AllClustersCommandDelegate.cpp", "AppOptions.cpp", diff --git a/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter b/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter index 1193a78f0bb220..8281f563aa69a1 100644 --- a/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter +++ b/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter @@ -2769,6 +2769,16 @@ server cluster Thermostat = 513 { /** An interface for controlling a fan in a heating/cooling system. */ server cluster FanControl = 514 { + enum AirflowDirectionEnum : ENUM8 { + kForward = 0; + kReverse = 1; + } + + enum DirectionEnum : ENUM8 { + kIncrease = 0; + kDecrease = 1; + } + enum FanModeSequenceType : ENUM8 { kOffLowMedHigh = 0; kOffLowHigh = 1; @@ -2793,6 +2803,8 @@ server cluster FanControl = 514 { kAuto = 0x2; kRocking = 0x4; kWind = 0x8; + kStep = 0x10; + kAirflowDirection = 0x20; } bitmap RockSupportMask : BITMAP8 { diff --git a/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter b/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter index d89740666008ab..00a4d9078572d8 100644 --- a/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter +++ b/examples/chef/devices/rootnode_fan_7N2TobIlOX.matter @@ -1187,6 +1187,16 @@ server cluster FixedLabel = 64 { /** An interface for controlling a fan in a heating/cooling system. */ server cluster FanControl = 514 { + enum AirflowDirectionEnum : ENUM8 { + kForward = 0; + kReverse = 1; + } + + enum DirectionEnum : ENUM8 { + kIncrease = 0; + kDecrease = 1; + } + enum FanModeSequenceType : ENUM8 { kOffLowMedHigh = 0; kOffLowHigh = 1; @@ -1211,6 +1221,8 @@ server cluster FanControl = 514 { kAuto = 0x2; kRocking = 0x4; kWind = 0x8; + kStep = 0x10; + kAirflowDirection = 0x20; } bitmap RockSupportMask : BITMAP8 { diff --git a/examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter b/examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter index dce6707393cb12..14a61685af9e68 100644 --- a/examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter +++ b/examples/chef/devices/rootnode_heatingcoolingunit_ncdGai1E5a.matter @@ -1500,6 +1500,16 @@ client cluster Thermostat = 513 { /** An interface for controlling a fan in a heating/cooling system. */ server cluster FanControl = 514 { + enum AirflowDirectionEnum : ENUM8 { + kForward = 0; + kReverse = 1; + } + + enum DirectionEnum : ENUM8 { + kIncrease = 0; + kDecrease = 1; + } + enum FanModeSequenceType : ENUM8 { kOffLowMedHigh = 0; kOffLowHigh = 1; @@ -1524,6 +1534,8 @@ server cluster FanControl = 514 { kAuto = 0x2; kRocking = 0x4; kWind = 0x8; + kStep = 0x10; + kAirflowDirection = 0x20; } bitmap RockSupportMask : BITMAP8 { diff --git a/examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter b/examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter index 17d0226399062d..a5a3b484743641 100644 --- a/examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter +++ b/examples/chef/devices/rootnode_thermostat_bm3fb8dhYi.matter @@ -1309,6 +1309,16 @@ server cluster Thermostat = 513 { /** An interface for controlling a fan in a heating/cooling system. */ client cluster FanControl = 514 { + enum AirflowDirectionEnum : ENUM8 { + kForward = 0; + kReverse = 1; + } + + enum DirectionEnum : ENUM8 { + kIncrease = 0; + kDecrease = 1; + } + enum FanModeSequenceType : ENUM8 { kOffLowMedHigh = 0; kOffLowHigh = 1; @@ -1333,6 +1343,8 @@ client cluster FanControl = 514 { kAuto = 0x2; kRocking = 0x4; kWind = 0x8; + kStep = 0x10; + kAirflowDirection = 0x20; } bitmap RockSupportMask : BITMAP8 { @@ -1362,12 +1374,22 @@ client cluster FanControl = 514 { attribute optional bitmap8 rockSetting = 8; readonly attribute optional bitmap8 windSupport = 9; attribute optional bitmap8 windSetting = 10; + attribute optional AirflowDirectionEnum airflowDirection = 11; readonly attribute command_id generatedCommandList[] = 65528; readonly attribute command_id acceptedCommandList[] = 65529; readonly attribute event_id eventList[] = 65530; readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; + + request struct StepRequest { + DirectionEnum direction = 0; + optional boolean wrap = 1; + optional boolean lowestOff = 2; + } + + /** The Step command speeds up or slows down the fan, in steps. */ + command Step(StepRequest): DefaultSuccess = 0; } /** An interface for configuring the user interface of a thermostat (which may be remote from the thermostat). */ diff --git a/src/app/clusters/fan-control-server/fan-control-server.cpp b/src/app/clusters/fan-control-server/fan-control-server.cpp index 25b593bbf9ce2f..c2c9c1cc2fe3ee 100644 --- a/src/app/clusters/fan-control-server/fan-control-server.cpp +++ b/src/app/clusters/fan-control-server/fan-control-server.cpp @@ -28,7 +28,10 @@ #include #include #include +#include +#include #include +#include #include #include @@ -57,6 +60,45 @@ EmberAfStatus SetFanModeToOff(EndpointId endpointId) return status; } +bool HasFeature(EndpointId endpoint, Feature feature) +{ + bool success; + uint32_t featureMap; + success = (Attributes::FeatureMap::Get(endpoint, &featureMap) == EMBER_ZCL_STATUS_SUCCESS); + + return success ? ((featureMap & to_underlying(feature)) != 0) : false; +} + +inline bool SupportsMultiSpeed(chip::EndpointId endpointId) +{ + return HasFeature(endpointId, Feature::kMultiSpeed); +} + +inline bool SupportsAuto(chip::EndpointId endpointId) +{ + return HasFeature(endpointId, Feature::kAuto); +} + +inline bool SupportsRocking(chip::EndpointId endpointId) +{ + return HasFeature(endpointId, Feature::kRocking); +} + +inline bool SupportsWind(chip::EndpointId endpointId) +{ + return HasFeature(endpointId, Feature::kWind); +} + +inline bool SupportsStep(chip::EndpointId endpointId) +{ + return HasFeature(endpointId, Feature::kStep); +} + +inline bool SupportsAirflowDirection(chip::EndpointId endpointId) +{ + return HasFeature(endpointId, Feature::kAirflowDirection); +} + } // anonymous namespace // ============================================================================= @@ -73,37 +115,75 @@ MatterFanControlClusterServerPreAttributeChangedCallback(const ConcreteAttribute switch (attributePath.mAttributeId) { - case SpeedSetting::Id: - // Check if the SpeedSetting is null. - if (NumericAttributeTraits::IsNullValue(*value)) + case FanMode::Id: { + if (*value == to_underlying(FanModeType::kOn)) + { + FanMode::Set(attributePath.mEndpointId, FanModeType::kHigh); + res = Status::WriteIgnored; + } + else if (*value == to_underlying(FanModeType::kSmart)) { + FanModeSequenceType fanModeSequence; + EmberAfStatus status = FanModeSequence::Get(attributePath.mEndpointId, &fanModeSequence); + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == status, Status::Failure); - if (gWriteFromClusterLogic) + if (SupportsAuto(attributePath.mEndpointId) && + ((fanModeSequence == FanModeSequenceType::kOffLowHighAuto) || + (fanModeSequence == FanModeSequenceType::kOffLowMedHighAuto))) { - res = Status::Success; - gWriteFromClusterLogic = false; + FanMode::Set(attributePath.mEndpointId, FanModeType::kAuto); } else { - res = Status::WriteIgnored; + FanMode::Set(attributePath.mEndpointId, FanModeType::kHigh); } + res = Status::WriteIgnored; } else { - uint8_t speedMax; - EmberAfStatus status = SpeedMax::Get(attributePath.mEndpointId, &speedMax); - VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == status, Status::ConstraintError); - - if (*value <= speedMax) + res = Status::Success; + } + break; + } + case SpeedSetting::Id: { + if (SupportsMultiSpeed(attributePath.mEndpointId)) + { + // Check if the SpeedSetting is null. + if (NumericAttributeTraits::IsNullValue(*value)) { - res = Status::Success; + + if (gWriteFromClusterLogic) + { + res = Status::Success; + gWriteFromClusterLogic = false; + } + else + { + res = Status::WriteIgnored; + } } else { - res = Status::ConstraintError; + uint8_t speedMax; + EmberAfStatus status = SpeedMax::Get(attributePath.mEndpointId, &speedMax); + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == status, Status::ConstraintError); + + if (*value <= speedMax) + { + res = Status::Success; + } + else + { + res = Status::ConstraintError; + } } } + else + { + res = Status::UnsupportedAttribute; + } break; + } case PercentSetting::Id: { // Check if the PercentSetting is null. if (NumericAttributeTraits::IsNullValue(*value)) @@ -124,6 +204,59 @@ MatterFanControlClusterServerPreAttributeChangedCallback(const ConcreteAttribute } break; } + case RockSetting::Id: { + if (SupportsRocking(attributePath.mEndpointId)) + { + uint8_t rockSupport; + EmberAfStatus status = RockSupport::Get(attributePath.mEndpointId, &rockSupport); + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == status, Status::ConstraintError); + if ((*value & rockSupport) == *value) + { + res = Status::Success; + } + else + { + res = Status::ConstraintError; + } + } + else + { + res = Status::UnsupportedAttribute; + } + break; + } + case WindSupport::Id: { + if (SupportsWind(attributePath.mEndpointId)) + { + uint8_t windSupport; + EmberAfStatus status = WindSupport::Get(attributePath.mEndpointId, &windSupport); + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == status, Status::ConstraintError); + if ((*value & windSupport) == *value) + { + res = Status::Success; + } + else + { + res = Status::ConstraintError; + } + } + else + { + res = Status::UnsupportedAttribute; + } + break; + } + case AirflowDirection::Id: { + if (SupportsAirflowDirection(attributePath.mEndpointId)) + { + res = Status::Success; + } + else + { + res = Status::UnsupportedAttribute; + } + break; + } default: res = Status::Success; break; @@ -134,17 +267,6 @@ MatterFanControlClusterServerPreAttributeChangedCallback(const ConcreteAttribute void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAttributePath & attributePath) { - bool multiSpeedSupported = false; - - { - uint32_t ourFeatureMap; - if (FeatureMap::Get(attributePath.mEndpointId, &ourFeatureMap) == EMBER_ZCL_STATUS_SUCCESS) - { - if (ourFeatureMap & to_underlying(Feature::kMultiSpeed)) - multiSpeedSupported = true; - } - } - switch (attributePath.mAttributeId) { case FanMode::Id: { @@ -164,7 +286,7 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(Zcl, "Failed to write PercentCurrent with error: 0x%02x", status)); - if (multiSpeedSupported) + if (SupportsMultiSpeed(attributePath.mEndpointId)) { status = SpeedSetting::Set(attributePath.mEndpointId, 0); VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, @@ -185,7 +307,7 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(Zcl, "Failed to write PercentSetting with error: 0x%02x", status)); - if (multiSpeedSupported) + if (SupportsMultiSpeed(attributePath.mEndpointId)) { gWriteFromClusterLogic = true; status = SpeedSetting::SetNull(attributePath.mEndpointId); @@ -208,7 +330,7 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", status)); } - if (multiSpeedSupported) + if (SupportsMultiSpeed(attributePath.mEndpointId)) { // Adjust SpeedSetting from a percent value change for PercentSetting // speed = ceil( SpeedMax * (percent * 0.01) ) @@ -231,53 +353,69 @@ void MatterFanControlClusterServerAttributeChangedCallback(const app::ConcreteAt VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(Zcl, "Failed to set SpeedSetting with error: 0x%02x", status)); } - - status = SpeedCurrent::Set(attributePath.mEndpointId, speedSetting); - VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, - ChipLogError(Zcl, "Failed to set SpeedCurrent with error: 0x%02x", status)); } break; } case SpeedSetting::Id: { - DataModel::Nullable speedSetting; - EmberAfStatus status = SpeedSetting::Get(attributePath.mEndpointId, speedSetting); - VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status && !speedSetting.IsNull()); - - // If SpeedSetting is set to 0, the server SHALL set the FanMode attribute value to Off. - if (speedSetting.Value() == 0) + if (SupportsMultiSpeed(attributePath.mEndpointId)) { - status = SetFanModeToOff(attributePath.mEndpointId); - VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, - ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", status)); - } - - // Adjust PercentSetting from a speed value change for SpeedSetting - // percent = floor( speed/SpeedMax * 100 ) - uint8_t speedMax; - status = SpeedMax::Get(attributePath.mEndpointId, &speedMax); - VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", status)); + DataModel::Nullable speedSetting; + EmberAfStatus status = SpeedSetting::Get(attributePath.mEndpointId, speedSetting); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status && !speedSetting.IsNull()); - DataModel::Nullable currentPercentSetting; - status = PercentSetting::Get(attributePath.mEndpointId, currentPercentSetting); - VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, - ChipLogError(Zcl, "Failed to get PercentSetting with error: 0x%02x", status)); + // If SpeedSetting is set to 0, the server SHALL set the FanMode attribute value to Off. + if (speedSetting.Value() == 0) + { + status = SetFanModeToOff(attributePath.mEndpointId); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(Zcl, "Failed to set FanMode to off with error: 0x%02x", status)); + } - float speed = speedSetting.Value(); - uint8_t percentSetting = static_cast(speed / speedMax * 100); + // Adjust PercentSetting from a speed value change for SpeedSetting + // percent = floor( speed/SpeedMax * 100 ) + uint8_t speedMax; + status = SpeedMax::Get(attributePath.mEndpointId, &speedMax); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(Zcl, "Failed to get SpeedMax with error: 0x%02x", status)); - if (currentPercentSetting.IsNull() || percentSetting != currentPercentSetting.Value()) - { - status = PercentSetting::Set(attributePath.mEndpointId, percentSetting); + DataModel::Nullable currentPercentSetting; + status = PercentSetting::Get(attributePath.mEndpointId, currentPercentSetting); VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, - ChipLogError(Zcl, "Failed to set PercentSetting with error: 0x%02x", status)); - } + ChipLogError(Zcl, "Failed to get PercentSetting with error: 0x%02x", status)); - status = PercentCurrent::Set(attributePath.mEndpointId, percentSetting); - VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, - ChipLogError(Zcl, "Failed to set PercentCurrent with error: 0x%02x", status)); + float speed = speedSetting.Value(); + uint8_t percentSetting = static_cast(speed / speedMax * 100); + + if (currentPercentSetting.IsNull() || percentSetting != currentPercentSetting.Value()) + { + status = PercentSetting::Set(attributePath.mEndpointId, percentSetting); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(Zcl, "Failed to set PercentSetting with error: 0x%02x", status)); + } + } break; } default: break; } } + +bool emberAfFanControlClusterStepCallback(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::FanControl::Commands::Step::DecodableType & commandData) +{ + /* + * TODO: Clarification needed in spec issue #6496 - if this is tied to the SpeedSetting attribute, then + * the attribute can be updated here, if it is supposed to be implementation specific, then the command + * will have to be handed off to an application specific callback which will require some sort of delegate. + */ + + Protocols::InteractionModel::Status status = Status::Success; + + if (!SupportsStep(commandPath.mEndpointId)) + { + status = Status::UnsupportedCommand; + } + commandObj->AddStatus(commandPath, status); + return true; +} diff --git a/src/app/zap-templates/zcl/data-model/chip/fan-control-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/fan-control-cluster.xml index ce9ef44851b6e9..8460756a679d30 100644 --- a/src/app/zap-templates/zcl/data-model/chip/fan-control-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/fan-control-cluster.xml @@ -23,6 +23,8 @@ limitations under the License. + + @@ -65,6 +67,18 @@ limitations under the License. + + + + + + + + + + + + Fan Control HVAC @@ -86,5 +100,14 @@ limitations under the License. RockSetting WindSupport WindSetting + AirflowDirection + + + The Step command speeds up or slows down the fan, in steps. + + + + + diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index ed238fa6fefe24..f8bf49523fb259 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -4519,6 +4519,16 @@ client cluster Thermostat = 513 { /** An interface for controlling a fan in a heating/cooling system. */ client cluster FanControl = 514 { + enum AirflowDirectionEnum : ENUM8 { + kForward = 0; + kReverse = 1; + } + + enum DirectionEnum : ENUM8 { + kIncrease = 0; + kDecrease = 1; + } + enum FanModeSequenceType : ENUM8 { kOffLowMedHigh = 0; kOffLowHigh = 1; @@ -4543,6 +4553,8 @@ client cluster FanControl = 514 { kAuto = 0x2; kRocking = 0x4; kWind = 0x8; + kStep = 0x10; + kAirflowDirection = 0x20; } bitmap RockSupportMask : BITMAP8 { @@ -4572,12 +4584,22 @@ client cluster FanControl = 514 { attribute optional bitmap8 rockSetting = 8; readonly attribute optional bitmap8 windSupport = 9; attribute optional bitmap8 windSetting = 10; + attribute optional AirflowDirectionEnum airflowDirection = 11; readonly attribute command_id generatedCommandList[] = 65528; readonly attribute command_id acceptedCommandList[] = 65529; readonly attribute event_id eventList[] = 65530; readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; + + request struct StepRequest { + DirectionEnum direction = 0; + optional boolean wrap = 1; + optional boolean lowestOff = 2; + } + + /** The Step command speeds up or slows down the fan, in steps. */ + command Step(StepRequest): DefaultSuccess = 0; } /** An interface for configuring the user interface of a thermostat (which may be remote from the thermostat). */ diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index 63b53c7ccae9a7..d558f167999302 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -9328,6 +9328,7 @@ public enum Attribute { RockSetting(8L), WindSupport(9L), WindSetting(10L), + AirflowDirection(11L), GeneratedCommandList(65528L), AcceptedCommandList(65529L), EventList(65530L), @@ -9373,7 +9374,8 @@ public static Event value(long id) throws NoSuchFieldError { } } - public enum Command {; + public enum Command { + Step(0L),; private final long id; Command(long id) { this.id = id; @@ -9391,7 +9393,24 @@ public static Command value(long id) throws NoSuchFieldError { } throw new NoSuchFieldError(); } - }@Override + }public enum StepCommandField {Direction(0),Wrap(1),LowestOff(2),; + private final int id; + StepCommandField(int id) { + this.id = id; + } + + public int getID() { + return id; + } + public static StepCommandField value(int id) throws NoSuchFieldError { + for (StepCommandField field : StepCommandField.values()) { + if (field.getID() == id) { + return field; + } + } + throw new NoSuchFieldError(); + } + }@Override public String getAttributeName(long id) throws NoSuchFieldError { return Attribute.value(id).toString(); } diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java index acf9c51e4f5b1e..3678b7d66815f9 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java @@ -10072,6 +10072,17 @@ private static Map readFanControlInteractionInfo() { readFanControlWindSettingCommandParams ); result.put("readWindSettingAttribute", readFanControlWindSettingAttributeInteractionInfo); + Map readFanControlAirflowDirectionCommandParams = new LinkedHashMap(); + InteractionInfo readFanControlAirflowDirectionAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.FanControlCluster) cluster).readAirflowDirectionAttribute( + (ChipClusters.IntegerAttributeCallback) callback + ); + }, + () -> new ClusterInfoMapping.DelegatedIntegerAttributeCallback(), + readFanControlAirflowDirectionCommandParams + ); + result.put("readAirflowDirectionAttribute", readFanControlAirflowDirectionAttributeInteractionInfo); Map readFanControlGeneratedCommandListCommandParams = new LinkedHashMap(); InteractionInfo readFanControlGeneratedCommandListAttributeInteractionInfo = new InteractionInfo( (cluster, callback, commandArguments) -> { diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java index 508627da1ea7f1..4f517da9491200 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java @@ -2223,6 +2223,28 @@ public Map> getWriteAttributeMap() { writeFanControlWindSettingCommandParams ); writeFanControlInteractionInfo.put("writeWindSettingAttribute", writeFanControlWindSettingAttributeInteractionInfo); + Map writeFanControlAirflowDirectionCommandParams = new LinkedHashMap(); + CommandParameterInfo fanControlairflowDirectionCommandParameterInfo = + new CommandParameterInfo( + "value", + Integer.class, + Integer.class + ); + writeFanControlAirflowDirectionCommandParams.put( + "value", + fanControlairflowDirectionCommandParameterInfo + ); + InteractionInfo writeFanControlAirflowDirectionAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.FanControlCluster) cluster).writeAirflowDirectionAttribute( + (DefaultClusterCallback) callback, + (Integer) commandArguments.get("value") + ); + }, + () -> new ClusterInfoMapping.DelegatedDefaultClusterCallback(), + writeFanControlAirflowDirectionCommandParams + ); + writeFanControlInteractionInfo.put("writeAirflowDirectionAttribute", writeFanControlAirflowDirectionAttributeInteractionInfo); writeAttributeMap.put("fanControl", writeFanControlInteractionInfo); Map writeThermostatUserInterfaceConfigurationInteractionInfo = new LinkedHashMap<>(); Map writeThermostatUserInterfaceConfigurationTemperatureDisplayModeCommandParams = new LinkedHashMap(); diff --git a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp index db04f2398bc259..bed47f67c2004a 100644 --- a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp @@ -19055,6 +19055,21 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR cppValue, value); return value; } + case Attributes::AirflowDirection::Id: { + using TypeInfo = Attributes::AirflowDirection::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value; + std::string valueClassName = "java/lang/Integer"; + std::string valueCtorSignature = "(I)V"; + chip::JniReferences::GetInstance().CreateBoxedObject(valueClassName.c_str(), valueCtorSignature.c_str(), + static_cast(cppValue), value); + return value; + } case Attributes::GeneratedCommandList::Id: { using TypeInfo = Attributes::GeneratedCommandList::TypeInfo; TypeInfo::DecodableType cppValue; diff --git a/src/controller/java/zap-generated/CHIPClustersWrite-JNI.cpp b/src/controller/java/zap-generated/CHIPClustersWrite-JNI.cpp index 3b922d4f5a09d4..14111e71f88c6a 100644 --- a/src/controller/java/zap-generated/CHIPClustersWrite-JNI.cpp +++ b/src/controller/java/zap-generated/CHIPClustersWrite-JNI.cpp @@ -5722,6 +5722,58 @@ JNI_METHOD(void, FanControlCluster, writeWindSettingAttribute) onFailure.release(); } +JNI_METHOD(void, FanControlCluster, writeAirflowDirectionAttribute) +(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback, jobject value, jobject timedWriteTimeoutMs) +{ + chip::DeviceLayer::StackLock lock; + ListFreer listFreer; + using TypeInfo = chip::app::Clusters::FanControl::Attributes::AirflowDirection::TypeInfo; + TypeInfo::Type cppValue; + + std::vector> cleanupByteArrays; + std::vector> cleanupStrings; + + cppValue = + static_cast>(chip::JniReferences::GetInstance().IntegerToPrimitive(value)); + + std::unique_ptr onSuccess( + Platform::New(callback), Platform::Delete); + VerifyOrReturn(onSuccess.get() != nullptr, + chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException( + env, callback, "Error creating native success callback", CHIP_ERROR_NO_MEMORY)); + + std::unique_ptr onFailure( + Platform::New(callback), Platform::Delete); + VerifyOrReturn(onFailure.get() != nullptr, + chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException( + env, callback, "Error creating native failure callback", CHIP_ERROR_NO_MEMORY)); + + CHIP_ERROR err = CHIP_NO_ERROR; + FanControlCluster * cppCluster = reinterpret_cast(clusterPtr); + VerifyOrReturn(cppCluster != nullptr, + chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException( + env, callback, "Could not get native cluster", CHIP_ERROR_INCORRECT_STATE)); + + auto successFn = chip::Callback::Callback::FromCancelable(onSuccess->Cancel()); + auto failureFn = chip::Callback::Callback::FromCancelable(onFailure->Cancel()); + + if (timedWriteTimeoutMs == nullptr) + { + err = cppCluster->WriteAttribute(cppValue, onSuccess->mContext, successFn->mCall, failureFn->mCall); + } + else + { + err = cppCluster->WriteAttribute(cppValue, onSuccess->mContext, successFn->mCall, failureFn->mCall, + chip::JniReferences::GetInstance().IntegerToPrimitive(timedWriteTimeoutMs)); + } + VerifyOrReturn( + err == CHIP_NO_ERROR, + chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error writing attribute", err)); + + onSuccess.release(); + onFailure.release(); +} + JNI_METHOD(void, ThermostatUserInterfaceConfigurationCluster, writeTemperatureDisplayModeAttribute) (JNIEnv * env, jobject self, jlong clusterPtr, jobject callback, jobject value, jobject timedWriteTimeoutMs) { diff --git a/src/controller/java/zap-generated/chip/devicecontroller/ChipClusters.java b/src/controller/java/zap-generated/chip/devicecontroller/ChipClusters.java index afc88c277b524e..d689415d39e7dd 100644 --- a/src/controller/java/zap-generated/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/zap-generated/chip/devicecontroller/ChipClusters.java @@ -22720,6 +22720,24 @@ public void subscribeWindSettingAttribute( subscribeWindSettingAttribute(chipClusterPtr, callback, minInterval, maxInterval); } + public void readAirflowDirectionAttribute(IntegerAttributeCallback callback) { + readAirflowDirectionAttribute(chipClusterPtr, callback); + } + + public void writeAirflowDirectionAttribute(DefaultClusterCallback callback, Integer value) { + writeAirflowDirectionAttribute(chipClusterPtr, callback, value, null); + } + + public void writeAirflowDirectionAttribute( + DefaultClusterCallback callback, Integer value, int timedWriteTimeoutMs) { + writeAirflowDirectionAttribute(chipClusterPtr, callback, value, timedWriteTimeoutMs); + } + + public void subscribeAirflowDirectionAttribute( + IntegerAttributeCallback callback, int minInterval, int maxInterval) { + subscribeAirflowDirectionAttribute(chipClusterPtr, callback, minInterval, maxInterval); + } + public void readGeneratedCommandListAttribute(GeneratedCommandListAttributeCallback callback) { readGeneratedCommandListAttribute(chipClusterPtr, callback); } @@ -22882,6 +22900,18 @@ private native void writeWindSettingAttribute( private native void subscribeWindSettingAttribute( long chipClusterPtr, IntegerAttributeCallback callback, int minInterval, int maxInterval); + private native void readAirflowDirectionAttribute( + long chipClusterPtr, IntegerAttributeCallback callback); + + private native void writeAirflowDirectionAttribute( + long chipClusterPtr, + DefaultClusterCallback callback, + Integer value, + @Nullable Integer timedWriteTimeoutMs); + + private native void subscribeAirflowDirectionAttribute( + long chipClusterPtr, IntegerAttributeCallback callback, int minInterval, int maxInterval); + private native void readGeneratedCommandListAttribute( long chipClusterPtr, GeneratedCommandListAttributeCallback callback); diff --git a/src/controller/java/zap-generated/chip/devicecontroller/ChipIdLookup.java b/src/controller/java/zap-generated/chip/devicecontroller/ChipIdLookup.java index f0281a9c128344..7698da80bca76b 100644 --- a/src/controller/java/zap-generated/chip/devicecontroller/ChipIdLookup.java +++ b/src/controller/java/zap-generated/chip/devicecontroller/ChipIdLookup.java @@ -3297,6 +3297,9 @@ public static String attributeIdToName(long clusterId, long attributeId) { if (attributeId == 10L) { return "WindSetting"; } + if (attributeId == 11L) { + return "AirflowDirection"; + } if (attributeId == 65528L) { return "GeneratedCommandList"; } diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index 245a04822f7c69..06c25900a89a2f 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -7054,6 +7054,15 @@ class ChipClusters: "clusterName": "FanControl", "clusterId": 0x00000202, "commands": { + 0x00000000: { + "commandId": 0x00000000, + "commandName": "Step", + "args": { + "direction": "int", + "wrap": "bool", + "lowestOff": "bool", + }, + }, }, "attributes": { 0x00000000: { @@ -7128,6 +7137,13 @@ class ChipClusters: "reportable": True, "writable": True, }, + 0x0000000B: { + "attributeName": "AirflowDirection", + "attributeId": 0x0000000B, + "type": "int", + "reportable": True, + "writable": True, + }, 0x0000FFF8: { "attributeName": "GeneratedCommandList", "attributeId": 0x0000FFF8, diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 0afd782c6e90ea..ae4d95e1d774ed 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -24539,6 +24539,7 @@ def descriptor(cls) -> ClusterObjectDescriptor: ClusterObjectFieldDescriptor(Label="rockSetting", Tag=0x00000008, Type=typing.Optional[uint]), ClusterObjectFieldDescriptor(Label="windSupport", Tag=0x00000009, Type=typing.Optional[uint]), ClusterObjectFieldDescriptor(Label="windSetting", Tag=0x0000000A, Type=typing.Optional[uint]), + ClusterObjectFieldDescriptor(Label="airflowDirection", Tag=0x0000000B, Type=typing.Optional[FanControl.Enums.AirflowDirectionEnum]), ClusterObjectFieldDescriptor(Label="generatedCommandList", Tag=0x0000FFF8, Type=typing.List[uint]), ClusterObjectFieldDescriptor(Label="acceptedCommandList", Tag=0x0000FFF9, Type=typing.List[uint]), ClusterObjectFieldDescriptor(Label="eventList", Tag=0x0000FFFA, Type=typing.List[uint]), @@ -24558,6 +24559,7 @@ def descriptor(cls) -> ClusterObjectDescriptor: rockSetting: 'typing.Optional[uint]' = None windSupport: 'typing.Optional[uint]' = None windSetting: 'typing.Optional[uint]' = None + airflowDirection: 'typing.Optional[FanControl.Enums.AirflowDirectionEnum]' = None generatedCommandList: 'typing.List[uint]' = None acceptedCommandList: 'typing.List[uint]' = None eventList: 'typing.List[uint]' = None @@ -24566,6 +24568,24 @@ def descriptor(cls) -> ClusterObjectDescriptor: clusterRevision: 'uint' = None class Enums: + class AirflowDirectionEnum(MatterIntEnum): + kForward = 0x00 + kReverse = 0x01 + # All received enum values that are not listed above will be mapped + # to kUnknownEnumValue. This is a helper enum value that should only + # be used by code to process how it handles receiving and unknown + # enum value. This specific should never be transmitted. + kUnknownEnumValue = 2, + + class DirectionEnum(MatterIntEnum): + kIncrease = 0x00 + kDecrease = 0x01 + # All received enum values that are not listed above will be mapped + # to kUnknownEnumValue. This is a helper enum value that should only + # be used by code to process how it handles receiving and unknown + # enum value. This specific should never be transmitted. + kUnknownEnumValue = 2, + class FanModeSequenceType(MatterIntEnum): kOffLowMedHigh = 0x00 kOffLowHigh = 0x01 @@ -24599,6 +24619,8 @@ class Feature(IntFlag): kAuto = 0x2 kRocking = 0x4 kWind = 0x8 + kStep = 0x10 + kAirflowDirection = 0x20 class RockSupportMask(IntFlag): kRockLeftRight = 0x1 @@ -24613,6 +24635,27 @@ class WindSupportMask(IntFlag): kSleepWind = 0x1 kNaturalWind = 0x2 + class Commands: + @dataclass + class Step(ClusterCommand): + cluster_id: typing.ClassVar[int] = 0x0202 + command_id: typing.ClassVar[int] = 0x00000000 + is_client: typing.ClassVar[bool] = True + response_type: typing.ClassVar[str] = None + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="direction", Tag=0, Type=FanControl.Enums.DirectionEnum), + ClusterObjectFieldDescriptor(Label="wrap", Tag=1, Type=typing.Optional[bool]), + ClusterObjectFieldDescriptor(Label="lowestOff", Tag=2, Type=typing.Optional[bool]), + ]) + + direction: 'FanControl.Enums.DirectionEnum' = 0 + wrap: 'typing.Optional[bool]' = None + lowestOff: 'typing.Optional[bool]' = None + class Attributes: @dataclass class FanMode(ClusterAttributeDescriptor): @@ -24790,6 +24833,22 @@ def attribute_type(cls) -> ClusterObjectFieldDescriptor: value: 'typing.Optional[uint]' = None + @dataclass + class AirflowDirection(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x0202 + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x0000000B + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.Optional[FanControl.Enums.AirflowDirectionEnum]) + + value: 'typing.Optional[FanControl.Enums.AirflowDirectionEnum]' = None + @dataclass class GeneratedCommandList(ClusterAttributeDescriptor): @ChipUtility.classproperty diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm index 5dda77c184e37d..4e92c0b02e92ba 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeSpecifiedCheck.mm @@ -3081,6 +3081,9 @@ static BOOL AttributeIsSpecifiedInFanControlCluster(AttributeId aAttributeId) case Attributes::WindSetting::Id: { return YES; } + case Attributes::AirflowDirection::Id: { + return YES; + } case Attributes::GeneratedCommandList::Id: { return YES; } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm index e65888cfe20a17..9d6b6601ca3a38 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm @@ -15082,6 +15082,17 @@ static id _Nullable DecodeAttributeValueForFanControlCluster( value = [NSNumber numberWithUnsignedChar:cppValue]; return value; } + case Attributes::AirflowDirection::Id: { + using TypeInfo = Attributes::AirflowDirection::TypeInfo; + TypeInfo::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + NSNumber * _Nonnull value; + value = [NSNumber numberWithUnsignedChar:chip::to_underlying(cppValue)]; + return value; + } case Attributes::GeneratedCommandList::Id: { using TypeInfo = Attributes::GeneratedCommandList::TypeInfo; TypeInfo::DecodableType cppValue; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index 204bbf829732e0..a215e2e4eddee0 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -13584,6 +13584,13 @@ API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) queue:(dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); +/** + * Command Step + * + * The Step command speeds up or slows down the fan, in steps. + */ +- (void)stepWithParams:(MTRFanControlClusterStepParams *)params completion:(MTRStatusCompletion)completion MTR_NEWLY_AVAILABLE; + - (void)readAttributeFanModeWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); - (void)writeAttributeFanModeWithValue:(NSNumber * _Nonnull)value @@ -13759,6 +13766,23 @@ API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) completion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); +- (void)readAttributeAirflowDirectionWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion + MTR_NEWLY_AVAILABLE; +- (void)writeAttributeAirflowDirectionWithValue:(NSNumber * _Nonnull)value + completion:(MTRStatusCompletion)completion MTR_NEWLY_AVAILABLE; +- (void)writeAttributeAirflowDirectionWithValue:(NSNumber * _Nonnull)value + params:(MTRWriteParams * _Nullable)params + completion:(MTRStatusCompletion)completion MTR_NEWLY_AVAILABLE; +- (void)subscribeAttributeAirflowDirectionWithParams:(MTRSubscribeParams *)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler + MTR_NEWLY_AVAILABLE; ++ (void)readAttributeAirflowDirectionWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer + endpoint:(NSNumber *)endpoint + queue:(dispatch_queue_t)queue + completion:(void (^)(NSNumber * _Nullable value, + NSError * _Nullable error))completion MTR_NEWLY_AVAILABLE; + - (void)readAttributeGeneratedCommandListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); - (void)subscribeAttributeGeneratedCommandListWithParams:(MTRSubscribeParams *)params @@ -32847,6 +32871,16 @@ typedef NS_OPTIONS(uint8_t, MTRThermostatModeForSequence) { = 0x2, } API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)); +typedef NS_ENUM(uint8_t, MTRFanControlAirflowDirection) { + MTRFanControlAirflowDirectionForward MTR_NEWLY_AVAILABLE = 0x00, + MTRFanControlAirflowDirectionReverse MTR_NEWLY_AVAILABLE = 0x01, +} MTR_NEWLY_AVAILABLE; + +typedef NS_ENUM(uint8_t, MTRFanControlDirection) { + MTRFanControlDirectionIncrease MTR_NEWLY_AVAILABLE = 0x00, + MTRFanControlDirectionDecrease MTR_NEWLY_AVAILABLE = 0x01, +} MTR_NEWLY_AVAILABLE; + typedef NS_ENUM(uint8_t, MTRFanControlFanModeSequenceType) { MTRFanControlFanModeSequenceTypeOffLowMedHigh API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) = 0x00, MTRFanControlFanModeSequenceTypeOffLowHigh API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) = 0x01, @@ -32871,6 +32905,8 @@ typedef NS_OPTIONS(uint32_t, MTRFanControlFeature) { MTRFanControlFeatureAuto API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) = 0x2, MTRFanControlFeatureRocking API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) = 0x4, MTRFanControlFeatureWind API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) = 0x8, + MTRFanControlFeatureStep MTR_NEWLY_AVAILABLE = 0x10, + MTRFanControlFeatureAirflowDirection MTR_NEWLY_AVAILABLE = 0x20, } API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)); typedef NS_OPTIONS(uint8_t, MTRFanControlRockSupportMask) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm index b9045be6c0909f..62d11498b1d53f 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm @@ -76146,6 +76146,51 @@ - (instancetype)initWithDevice:(MTRBaseDevice *)device endpointID:(NSNumber *)en return self; } +- (void)stepWithParams:(MTRFanControlClusterStepParams *)params completion:(MTRStatusCompletion)completion +{ + // Make a copy of params before we go async. + params = [params copy]; + auto * bridge = new MTRCommandSuccessCallbackBridge( + self.callbackQueue, + ^(id _Nullable value, NSError * _Nullable error) { + completion(error); + }, + ^(ExchangeManager & exchangeManager, const SessionHandle & session, CommandSuccessCallbackType successCb, + MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) { + auto * typedBridge = static_cast(bridge); + Optional timedInvokeTimeoutMs; + Optional invokeTimeout; + ListFreer listFreer; + FanControl::Commands::Step::Type request; + if (params != nil) { + if (params.timedInvokeTimeoutMs != nil) { + params.timedInvokeTimeoutMs = MTRClampedNumber(params.timedInvokeTimeoutMs, @(1), @(UINT16_MAX)); + timedInvokeTimeoutMs.SetValue(params.timedInvokeTimeoutMs.unsignedShortValue); + } + if (params.serverSideProcessingTimeout != nil) { + // Clamp to a number of seconds that will not overflow 32-bit + // int when converted to ms. + auto * serverSideProcessingTimeout = MTRClampedNumber(params.serverSideProcessingTimeout, @(0), @(UINT16_MAX)); + invokeTimeout.SetValue(Seconds16(serverSideProcessingTimeout.unsignedShortValue)); + } + } + request.direction + = static_cast>(params.direction.unsignedCharValue); + if (params.wrap != nil) { + auto & definedValue_0 = request.wrap.Emplace(); + definedValue_0 = params.wrap.boolValue; + } + if (params.lowestOff != nil) { + auto & definedValue_0 = request.lowestOff.Emplace(); + definedValue_0 = params.lowestOff.boolValue; + } + + return MTRStartInvokeInteraction(typedBridge, request, exchangeManager, session, successCb, failureCb, self->_endpoint, + timedInvokeTimeoutMs, invokeTimeout); + }); + std::move(*bridge).DispatchAction(self.device); +} + - (void)readAttributeFanModeWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion { MTRReadParams * params = [[MTRReadParams alloc] init]; @@ -76852,6 +76897,87 @@ + (void)readAttributeWindSettingWithClusterStateCache:(MTRClusterStateCacheConta }); } +- (void)readAttributeAirflowDirectionWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + MTRReadParams * params = [[MTRReadParams alloc] init]; + using TypeInfo = FanControl::Attributes::AirflowDirection::TypeInfo; + return MTRReadAttribute( + params, completion, self.callbackQueue, self.device, self->_endpoint, TypeInfo::GetClusterId(), TypeInfo::GetAttributeId()); +} + +- (void)writeAttributeAirflowDirectionWithValue:(NSNumber * _Nonnull)value completion:(MTRStatusCompletion)completion +{ + [self writeAttributeAirflowDirectionWithValue:(NSNumber * _Nonnull) value params:nil completion:completion]; +} +- (void)writeAttributeAirflowDirectionWithValue:(NSNumber * _Nonnull)value + params:(MTRWriteParams * _Nullable)params + completion:(MTRStatusCompletion)completion +{ + // Make a copy of params before we go async. + params = [params copy]; + value = [value copy]; + + auto * bridge = new MTRDefaultSuccessCallbackBridge( + self.callbackQueue, + ^(id _Nullable ignored, NSError * _Nullable error) { + completion(error); + }, + ^(ExchangeManager & exchangeManager, const SessionHandle & session, DefaultSuccessCallbackType successCb, + MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) { + chip::Optional timedWriteTimeout; + if (params != nil) { + if (params.timedWriteTimeout != nil) { + timedWriteTimeout.SetValue(params.timedWriteTimeout.unsignedShortValue); + } + } + + ListFreer listFreer; + using TypeInfo = FanControl::Attributes::AirflowDirection::TypeInfo; + TypeInfo::Type cppValue; + cppValue = static_cast>(value.unsignedCharValue); + + chip::Controller::ClusterBase cppCluster(exchangeManager, session, self->_endpoint); + return cppCluster.WriteAttribute(cppValue, bridge, successCb, failureCb, timedWriteTimeout); + }); + std::move(*bridge).DispatchAction(self.device); +} + +- (void)subscribeAttributeAirflowDirectionWithParams:(MTRSubscribeParams * _Nonnull)params + subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished + reportHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))reportHandler +{ + using TypeInfo = FanControl::Attributes::AirflowDirection::TypeInfo; + MTRSubscribeAttribute(params, subscriptionEstablished, reportHandler, self.callbackQueue, self.device, self->_endpoint, + TypeInfo::GetClusterId(), TypeInfo::GetAttributeId()); +} + ++ (void)readAttributeAirflowDirectionWithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer + endpoint:(NSNumber *)endpoint + queue:(dispatch_queue_t)queue + completion: + (void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion +{ + auto * bridge = new MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge(queue, completion); + std::move(*bridge).DispatchLocalAction(clusterStateCacheContainer.baseDevice, + ^(FanControlClusterAirflowDirectionEnumAttributeCallback successCb, MTRErrorCallback failureCb) { + if (clusterStateCacheContainer.cppClusterStateCache) { + chip::app::ConcreteAttributePath path; + using TypeInfo = FanControl::Attributes::AirflowDirection::TypeInfo; + path.mEndpointId = static_cast([endpoint unsignedShortValue]); + path.mClusterId = TypeInfo::GetClusterId(); + path.mAttributeId = TypeInfo::GetAttributeId(); + TypeInfo::DecodableType value; + CHIP_ERROR err = clusterStateCacheContainer.cppClusterStateCache->Get(path, value); + if (err == CHIP_NO_ERROR) { + successCb(bridge, value); + } + return err; + } + return CHIP_ERROR_NOT_FOUND; + }); +} + - (void)readAttributeGeneratedCommandListWithCompletion:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))completion { MTRReadParams * params = [[MTRReadParams alloc] init]; @@ -77119,6 +77245,11 @@ + (void)readAttributeClusterRevisionWithClusterStateCache:(MTRClusterStateCacheC @implementation MTRBaseClusterFanControl (Deprecated) +- (void)stepWithParams:(MTRFanControlClusterStepParams *)params completionHandler:(MTRStatusCompletion)completionHandler +{ + [self stepWithParams:params completion:completionHandler]; +} + - (void)readAttributeFanModeWithCompletionHandler:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completionHandler { [self readAttributeFanModeWithCompletion:^(NSNumber * _Nullable value, NSError * _Nullable error) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCallbackBridge.h b/src/darwin/Framework/CHIP/zap-generated/MTRCallbackBridge.h index c9022d3ddea220..b2949985c6f876 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCallbackBridge.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCallbackBridge.h @@ -595,6 +595,13 @@ typedef void (*ThermostatClusterThermostatSystemModeAttributeCallback)(void *, chip::app::Clusters::Thermostat::ThermostatSystemMode); typedef void (*NullableThermostatClusterThermostatSystemModeAttributeCallback)( void *, const chip::app::DataModel::Nullable &); +typedef void (*FanControlClusterAirflowDirectionEnumAttributeCallback)(void *, + chip::app::Clusters::FanControl::AirflowDirectionEnum); +typedef void (*NullableFanControlClusterAirflowDirectionEnumAttributeCallback)( + void *, const chip::app::DataModel::Nullable &); +typedef void (*FanControlClusterDirectionEnumAttributeCallback)(void *, chip::app::Clusters::FanControl::DirectionEnum); +typedef void (*NullableFanControlClusterDirectionEnumAttributeCallback)( + void *, const chip::app::DataModel::Nullable &); typedef void (*FanControlClusterFanModeSequenceTypeAttributeCallback)(void *, chip::app::Clusters::FanControl::FanModeSequenceType); typedef void (*NullableFanControlClusterFanModeSequenceTypeAttributeCallback)( void *, const chip::app::DataModel::Nullable &); @@ -31597,6 +31604,140 @@ class MTRNullableThermostatClusterThermostatSystemModeAttributeCallbackSubscript MTRSubscriptionEstablishedHandler mEstablishedHandler; }; +class MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge + : public MTRCallbackBridge +{ +public: + MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler) : + MTRCallbackBridge(queue, handler, OnSuccessFn){}; + + MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler, + MTRActionBlock action) : + MTRCallbackBridge(queue, handler, action, OnSuccessFn){}; + + static void OnSuccessFn(void * context, chip::app::Clusters::FanControl::AirflowDirectionEnum value); +}; + +class MTRFanControlClusterAirflowDirectionEnumAttributeCallbackSubscriptionBridge + : public MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge +{ +public: + MTRFanControlClusterAirflowDirectionEnumAttributeCallbackSubscriptionBridge( + dispatch_queue_t queue, ResponseHandler handler, MTRActionBlock action, + MTRSubscriptionEstablishedHandler establishedHandler) : + MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge(queue, handler, action), + mEstablishedHandler(establishedHandler) + {} + + void OnSubscriptionEstablished(); + using MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge::KeepAliveOnCallback; + using MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge::OnDone; + +private: + MTRSubscriptionEstablishedHandler mEstablishedHandler; +}; + +class MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackBridge + : public MTRCallbackBridge +{ +public: + MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler) : + MTRCallbackBridge(queue, handler, OnSuccessFn){}; + + MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler, + MTRActionBlock action) : + MTRCallbackBridge(queue, handler, action, OnSuccessFn){}; + + static void OnSuccessFn(void * context, + const chip::app::DataModel::Nullable & value); +}; + +class MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackSubscriptionBridge + : public MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackBridge +{ +public: + MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackSubscriptionBridge( + dispatch_queue_t queue, ResponseHandler handler, MTRActionBlock action, + MTRSubscriptionEstablishedHandler establishedHandler) : + MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackBridge(queue, handler, action), + mEstablishedHandler(establishedHandler) + {} + + void OnSubscriptionEstablished(); + using MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackBridge::KeepAliveOnCallback; + using MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackBridge::OnDone; + +private: + MTRSubscriptionEstablishedHandler mEstablishedHandler; +}; + +class MTRFanControlClusterDirectionEnumAttributeCallbackBridge + : public MTRCallbackBridge +{ +public: + MTRFanControlClusterDirectionEnumAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler) : + MTRCallbackBridge(queue, handler, OnSuccessFn){}; + + MTRFanControlClusterDirectionEnumAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler, + MTRActionBlock action) : + MTRCallbackBridge(queue, handler, action, OnSuccessFn){}; + + static void OnSuccessFn(void * context, chip::app::Clusters::FanControl::DirectionEnum value); +}; + +class MTRFanControlClusterDirectionEnumAttributeCallbackSubscriptionBridge + : public MTRFanControlClusterDirectionEnumAttributeCallbackBridge +{ +public: + MTRFanControlClusterDirectionEnumAttributeCallbackSubscriptionBridge(dispatch_queue_t queue, ResponseHandler handler, + MTRActionBlock action, + MTRSubscriptionEstablishedHandler establishedHandler) : + MTRFanControlClusterDirectionEnumAttributeCallbackBridge(queue, handler, action), + mEstablishedHandler(establishedHandler) + {} + + void OnSubscriptionEstablished(); + using MTRFanControlClusterDirectionEnumAttributeCallbackBridge::KeepAliveOnCallback; + using MTRFanControlClusterDirectionEnumAttributeCallbackBridge::OnDone; + +private: + MTRSubscriptionEstablishedHandler mEstablishedHandler; +}; + +class MTRNullableFanControlClusterDirectionEnumAttributeCallbackBridge + : public MTRCallbackBridge +{ +public: + MTRNullableFanControlClusterDirectionEnumAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler) : + MTRCallbackBridge(queue, handler, OnSuccessFn){}; + + MTRNullableFanControlClusterDirectionEnumAttributeCallbackBridge(dispatch_queue_t queue, ResponseHandler handler, + MTRActionBlock action) : + MTRCallbackBridge(queue, handler, action, OnSuccessFn){}; + + static void OnSuccessFn(void * context, + const chip::app::DataModel::Nullable & value); +}; + +class MTRNullableFanControlClusterDirectionEnumAttributeCallbackSubscriptionBridge + : public MTRNullableFanControlClusterDirectionEnumAttributeCallbackBridge +{ +public: + MTRNullableFanControlClusterDirectionEnumAttributeCallbackSubscriptionBridge( + dispatch_queue_t queue, ResponseHandler handler, MTRActionBlock action, + MTRSubscriptionEstablishedHandler establishedHandler) : + MTRNullableFanControlClusterDirectionEnumAttributeCallbackBridge(queue, handler, action), + mEstablishedHandler(establishedHandler) + {} + + void OnSubscriptionEstablished(); + using MTRNullableFanControlClusterDirectionEnumAttributeCallbackBridge::KeepAliveOnCallback; + using MTRNullableFanControlClusterDirectionEnumAttributeCallbackBridge::OnDone; + +private: + MTRSubscriptionEstablishedHandler mEstablishedHandler; +}; + class MTRFanControlClusterFanModeSequenceTypeAttributeCallbackBridge : public MTRCallbackBridge { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCallbackBridge.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCallbackBridge.mm index 1ab0b6fb85417f..fa6adc0121a03b 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCallbackBridge.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCallbackBridge.mm @@ -29505,6 +29505,106 @@ } } +void MTRFanControlClusterAirflowDirectionEnumAttributeCallbackBridge::OnSuccessFn( + void * context, chip::app::Clusters::FanControl::AirflowDirectionEnum value) +{ + NSNumber * _Nonnull objCValue; + objCValue = [NSNumber numberWithUnsignedChar:chip::to_underlying(value)]; + DispatchSuccess(context, objCValue); +}; + +void MTRFanControlClusterAirflowDirectionEnumAttributeCallbackSubscriptionBridge::OnSubscriptionEstablished() +{ + if (!mQueue) { + return; + } + + if (mEstablishedHandler != nil) { + dispatch_async(mQueue, mEstablishedHandler); + // On failure, mEstablishedHandler will be cleaned up by our destructor, + // but we can clean it up earlier on successful subscription + // establishment. + mEstablishedHandler = nil; + } +} + +void MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackBridge::OnSuccessFn( + void * context, const chip::app::DataModel::Nullable & value) +{ + NSNumber * _Nullable objCValue; + if (value.IsNull()) { + objCValue = nil; + } else { + objCValue = [NSNumber numberWithUnsignedChar:chip::to_underlying(value.Value())]; + } + DispatchSuccess(context, objCValue); +}; + +void MTRNullableFanControlClusterAirflowDirectionEnumAttributeCallbackSubscriptionBridge::OnSubscriptionEstablished() +{ + if (!mQueue) { + return; + } + + if (mEstablishedHandler != nil) { + dispatch_async(mQueue, mEstablishedHandler); + // On failure, mEstablishedHandler will be cleaned up by our destructor, + // but we can clean it up earlier on successful subscription + // establishment. + mEstablishedHandler = nil; + } +} + +void MTRFanControlClusterDirectionEnumAttributeCallbackBridge::OnSuccessFn( + void * context, chip::app::Clusters::FanControl::DirectionEnum value) +{ + NSNumber * _Nonnull objCValue; + objCValue = [NSNumber numberWithUnsignedChar:chip::to_underlying(value)]; + DispatchSuccess(context, objCValue); +}; + +void MTRFanControlClusterDirectionEnumAttributeCallbackSubscriptionBridge::OnSubscriptionEstablished() +{ + if (!mQueue) { + return; + } + + if (mEstablishedHandler != nil) { + dispatch_async(mQueue, mEstablishedHandler); + // On failure, mEstablishedHandler will be cleaned up by our destructor, + // but we can clean it up earlier on successful subscription + // establishment. + mEstablishedHandler = nil; + } +} + +void MTRNullableFanControlClusterDirectionEnumAttributeCallbackBridge::OnSuccessFn( + void * context, const chip::app::DataModel::Nullable & value) +{ + NSNumber * _Nullable objCValue; + if (value.IsNull()) { + objCValue = nil; + } else { + objCValue = [NSNumber numberWithUnsignedChar:chip::to_underlying(value.Value())]; + } + DispatchSuccess(context, objCValue); +}; + +void MTRNullableFanControlClusterDirectionEnumAttributeCallbackSubscriptionBridge::OnSubscriptionEstablished() +{ + if (!mQueue) { + return; + } + + if (mEstablishedHandler != nil) { + dispatch_async(mQueue, mEstablishedHandler); + // On failure, mEstablishedHandler will be cleaned up by our destructor, + // but we can clean it up earlier on successful subscription + // establishment. + mEstablishedHandler = nil; + } +} + void MTRFanControlClusterFanModeSequenceTypeAttributeCallbackBridge::OnSuccessFn( void * context, chip::app::Clusters::FanControl::FanModeSequenceType value) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h index 60ef563a8ed5e4..aa7ddfac2bde8f 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h @@ -5273,6 +5273,7 @@ typedef NS_ENUM(uint32_t, MTRAttributeIDType) { = 0x00000009, MTRAttributeIDTypeClusterFanControlAttributeWindSettingID API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = 0x0000000A, + MTRAttributeIDTypeClusterFanControlAttributeAirflowDirectionID MTR_NEWLY_AVAILABLE = 0x0000000B, MTRAttributeIDTypeClusterFanControlAttributeGeneratedCommandListID API_AVAILABLE( ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = MTRAttributeIDTypeGlobalAttributeGeneratedCommandListID, @@ -10474,6 +10475,11 @@ typedef NS_ENUM(uint32_t, MTRCommandIDType) { MTRCommandIDTypeClusterThermostatCommandClearWeeklyScheduleID API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) = 0x00000003, + // Cluster FanControl deprecated command id names + + // Cluster FanControl commands + MTRCommandIDTypeClusterFanControlCommandStepID MTR_NEWLY_AVAILABLE = 0x00000000, + // Cluster ColorControl deprecated command id names MTRClusterColorControlCommandMoveToHueID MTR_DEPRECATED("Please use MTRCommandIDTypeClusterColorControlCommandMoveToHueID", ios(16.1, 16.4), macos(13.0, 13.3), watchos(9.1, 9.4), tvos(16.1, 16.4)) diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h index f5a0b6a0ee34e4..277d4d1b8390cd 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h @@ -4761,6 +4761,11 @@ API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) queue:(dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); +- (void)stepWithParams:(MTRFanControlClusterStepParams *)params + expectedValues:(NSArray *> * _Nullable)expectedDataValueDictionaries + expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs + completion:(MTRStatusCompletion)completion MTR_NEWLY_AVAILABLE; + - (NSDictionary *)readAttributeFanModeWithParams:(MTRReadParams * _Nullable)params API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)); - (void)writeAttributeFanModeWithValue:(NSDictionary *)dataValueDictionary @@ -4836,6 +4841,13 @@ API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) params:(MTRWriteParams * _Nullable)params API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)); +- (NSDictionary *)readAttributeAirflowDirectionWithParams:(MTRReadParams * _Nullable)params MTR_NEWLY_AVAILABLE; +- (void)writeAttributeAirflowDirectionWithValue:(NSDictionary *)dataValueDictionary + expectedValueInterval:(NSNumber *)expectedValueIntervalMs MTR_NEWLY_AVAILABLE; +- (void)writeAttributeAirflowDirectionWithValue:(NSDictionary *)dataValueDictionary + expectedValueInterval:(NSNumber *)expectedValueIntervalMs + params:(MTRWriteParams * _Nullable)params MTR_NEWLY_AVAILABLE; + - (NSDictionary *)readAttributeGeneratedCommandListWithParams:(MTRReadParams * _Nullable)params API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)); diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm index c20c8018ca737e..37861d9b42dd98 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm @@ -21555,6 +21555,83 @@ - (instancetype)initWithDevice:(MTRDevice *)device endpointID:(NSNumber *)endpoi return self; } +- (void)stepWithParams:(MTRFanControlClusterStepParams *)params + expectedValues:(NSArray *> *)expectedValues + expectedValueInterval:(NSNumber *)expectedValueIntervalMs + completion:(MTRStatusCompletion)completion +{ + NSString * logPrefix = + [NSString stringWithFormat:@"MTRDevice command %u %u %u %u", self.device.deviceController.fabricIndex, _endpoint, + (unsigned int) MTRClusterIDTypeFanControlID, (unsigned int) MTRCommandIDTypeClusterFanControlCommandStepID]; + // Make a copy of params before we go async. + params = [params copy]; + NSNumber * timedInvokeTimeoutMsParam = params.timedInvokeTimeoutMs; + if (timedInvokeTimeoutMsParam) { + timedInvokeTimeoutMsParam = MTRClampedNumber(timedInvokeTimeoutMsParam, @(1), @(UINT16_MAX)); + } + MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:self.device.queue]; + MTRAsyncCallbackReadyHandler readyHandler = ^(MTRDevice * device, NSUInteger retryCount) { + MTRClustersLogDequeue(logPrefix, self.device.asyncCallbackWorkQueue); + MTRBaseDevice * baseDevice = [[MTRBaseDevice alloc] initWithNodeID:self.device.nodeID + controller:self.device.deviceController]; + auto * bridge = new MTRCommandSuccessCallbackBridge( + self.device.queue, + ^(id _Nullable value, NSError * _Nullable error) { + MTRClustersLogCompletion(logPrefix, value, error); + dispatch_async(self.callbackQueue, ^{ + completion(error); + }); + [workItem endWork]; + }, + ^(ExchangeManager & exchangeManager, const SessionHandle & session, CommandSuccessCallbackType successCb, + MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) { + auto * typedBridge = static_cast(bridge); + Optional timedInvokeTimeoutMs; + Optional invokeTimeout; + ListFreer listFreer; + FanControl::Commands::Step::Type request; + if (timedInvokeTimeoutMsParam != nil) { + timedInvokeTimeoutMs.SetValue(timedInvokeTimeoutMsParam.unsignedShortValue); + } + if (params != nil) { + if (params.serverSideProcessingTimeout != nil) { + // Clamp to a number of seconds that will not overflow 32-bit + // int when converted to ms. + auto * serverSideProcessingTimeout + = MTRClampedNumber(params.serverSideProcessingTimeout, @(0), @(UINT16_MAX)); + invokeTimeout.SetValue(Seconds16(serverSideProcessingTimeout.unsignedShortValue)); + } + } + request.direction + = static_cast>(params.direction.unsignedCharValue); + if (params.wrap != nil) { + auto & definedValue_0 = request.wrap.Emplace(); + definedValue_0 = params.wrap.boolValue; + } + if (params.lowestOff != nil) { + auto & definedValue_0 = request.lowestOff.Emplace(); + definedValue_0 = params.lowestOff.boolValue; + } + + return MTRStartInvokeInteraction(typedBridge, request, exchangeManager, session, successCb, failureCb, + self->_endpoint, timedInvokeTimeoutMs, invokeTimeout); + }); + std::move(*bridge).DispatchAction(baseDevice); + }; + workItem.readyHandler = readyHandler; + MTRClustersLogEnqueue(logPrefix, self.device.asyncCallbackWorkQueue); + [self.device.asyncCallbackWorkQueue enqueueWorkItem:workItem]; + + if (!expectedValueIntervalMs || ([expectedValueIntervalMs compare:@(0)] == NSOrderedAscending)) { + expectedValues = nil; + } else { + expectedValueIntervalMs = MTRClampedNumber(expectedValueIntervalMs, @(1), @(UINT32_MAX)); + } + if (expectedValues) { + [self.device setExpectedValues:expectedValues expectedValueInterval:expectedValueIntervalMs]; + } +} + - (NSDictionary *)readAttributeFanModeWithParams:(MTRReadParams * _Nullable)params { return [self.device readAttributeWithEndpointID:@(_endpoint) @@ -21757,6 +21834,33 @@ - (void)writeAttributeWindSettingWithValue:(NSDictionary *)dataV timedWriteTimeout:timedWriteTimeout]; } +- (NSDictionary *)readAttributeAirflowDirectionWithParams:(MTRReadParams * _Nullable)params +{ + return [self.device readAttributeWithEndpointID:@(_endpoint) + clusterID:@(MTRClusterIDTypeFanControlID) + attributeID:@(MTRAttributeIDTypeClusterFanControlAttributeAirflowDirectionID) + params:params]; +} + +- (void)writeAttributeAirflowDirectionWithValue:(NSDictionary *)dataValueDictionary + expectedValueInterval:(NSNumber *)expectedValueIntervalMs +{ + [self writeAttributeAirflowDirectionWithValue:dataValueDictionary expectedValueInterval:expectedValueIntervalMs params:nil]; +} +- (void)writeAttributeAirflowDirectionWithValue:(NSDictionary *)dataValueDictionary + expectedValueInterval:(NSNumber *)expectedValueIntervalMs + params:(MTRWriteParams * _Nullable)params +{ + NSNumber * timedWriteTimeout = params.timedWriteTimeout; + + [self.device writeAttributeWithEndpointID:@(_endpoint) + clusterID:@(MTRClusterIDTypeFanControlID) + attributeID:@(MTRAttributeIDTypeClusterFanControlAttributeAirflowDirectionID) + value:dataValueDictionary + expectedValueInterval:expectedValueIntervalMs + timedWriteTimeout:timedWriteTimeout]; +} + - (NSDictionary *)readAttributeGeneratedCommandListWithParams:(MTRReadParams * _Nullable)params { return [self.device readAttributeWithEndpointID:@(_endpoint) @@ -21814,6 +21918,16 @@ - (instancetype)initWithDevice:(MTRDevice *)device endpoint:(uint16_t)endpoint q return [self initWithDevice:device endpointID:@(endpoint) queue:queue]; } +- (void)stepWithParams:(MTRFanControlClusterStepParams *)params + expectedValues:(NSArray *> * _Nullable)expectedDataValueDictionaries + expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs + completionHandler:(MTRStatusCompletion)completionHandler +{ + [self stepWithParams:params + expectedValues:expectedDataValueDictionaries + expectedValueInterval:expectedValueIntervalMs + completion:completionHandler]; +} @end @implementation MTRClusterThermostatUserInterfaceConfiguration diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h index 23a469fca59c39..24c4e750f138da 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h @@ -5995,6 +5995,40 @@ API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) @property (nonatomic, copy, nullable) NSNumber * serverSideProcessingTimeout; @end +MTR_NEWLY_AVAILABLE +@interface MTRFanControlClusterStepParams : NSObject + +@property (nonatomic, copy) NSNumber * _Nonnull direction MTR_NEWLY_AVAILABLE; + +@property (nonatomic, copy) NSNumber * _Nullable wrap MTR_NEWLY_AVAILABLE; + +@property (nonatomic, copy) NSNumber * _Nullable lowestOff MTR_NEWLY_AVAILABLE; +/** + * Controls whether the command is a timed command (using Timed Invoke). + * + * If nil (the default value), a regular invoke is done for commands that do + * not require a timed invoke and a timed invoke with some default timed request + * timeout is done for commands that require a timed invoke. + * + * If not nil, a timed invoke is done, with the provided value used as the timed + * request timeout. The value should be chosen small enough to provide the + * desired security properties but large enough that it will allow a round-trip + * from the sever to the client (for the status response and actual invoke + * request) within the timeout window. + * + */ +@property (nonatomic, copy, nullable) NSNumber * timedInvokeTimeoutMs; + +/** + * Controls how much time, in seconds, we will allow for the server to process the command. + * + * The command will then time out if that much time, plus an allowance for retransmits due to network failures, passes. + * + * If nil, the framework will try to select an appropriate timeout value itself. + */ +@property (nonatomic, copy, nullable) NSNumber * serverSideProcessingTimeout; +@end + API_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) @interface MTRColorControlClusterMoveToHueParams : NSObject diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm index b3731db438dab9..54b2ccaabd56cd 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm @@ -9098,6 +9098,43 @@ - (NSString *)description return descriptionString; } +@end +@implementation MTRFanControlClusterStepParams +- (instancetype)init +{ + if (self = [super init]) { + + _direction = @(0); + + _wrap = nil; + + _lowestOff = nil; + _timedInvokeTimeoutMs = nil; + _serverSideProcessingTimeout = nil; + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone; +{ + auto other = [[MTRFanControlClusterStepParams alloc] init]; + + other.direction = self.direction; + other.wrap = self.wrap; + other.lowestOff = self.lowestOff; + other.timedInvokeTimeoutMs = self.timedInvokeTimeoutMs; + other.serverSideProcessingTimeout = self.serverSideProcessingTimeout; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: direction:%@; wrap:%@; lowestOff:%@; >", + NSStringFromClass([self class]), _direction, _wrap, _lowestOff]; + return descriptionString; +} + @end @implementation MTRColorControlClusterMoveToHueParams - (instancetype)init 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 b403e7ba649597..b305704c5807b4 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 @@ -16609,6 +16609,37 @@ EmberAfStatus Set(chip::EndpointId endpoint, uint8_t value) } // namespace WindSetting +namespace AirflowDirection { + +EmberAfStatus Get(chip::EndpointId endpoint, chip::app::Clusters::FanControl::AirflowDirectionEnum * value) +{ + using Traits = NumericAttributeTraits; + Traits::StorageType temp; + uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); + EmberAfStatus status = emberAfReadAttribute(endpoint, Clusters::FanControl::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::FanControl::AirflowDirectionEnum 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::FanControl::Id, Id, writable, ZCL_ENUM8_ATTRIBUTE_TYPE); +} + +} // namespace AirflowDirection + 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 94bf7b15cced7a..086820b98325db 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 @@ -2988,6 +2988,11 @@ EmberAfStatus Get(chip::EndpointId endpoint, uint8_t * value); // bitmap8 EmberAfStatus Set(chip::EndpointId endpoint, uint8_t value); } // namespace WindSetting +namespace AirflowDirection { +EmberAfStatus Get(chip::EndpointId endpoint, chip::app::Clusters::FanControl::AirflowDirectionEnum * value); // AirflowDirectionEnum +EmberAfStatus Set(chip::EndpointId endpoint, chip::app::Clusters::FanControl::AirflowDirectionEnum value); +} // namespace AirflowDirection + namespace FeatureMap { EmberAfStatus Get(chip::EndpointId endpoint, uint32_t * value); // bitmap32 EmberAfStatus Set(chip::EndpointId endpoint, uint32_t value); diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index abe72492d66c6b..0c2f241fb9e519 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -11481,6 +11481,12 @@ bool emberAfThermostatClusterGetWeeklyScheduleCallback( bool emberAfThermostatClusterClearWeeklyScheduleCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::Thermostat::Commands::ClearWeeklySchedule::DecodableType & commandData); +/** + * @brief Fan Control Cluster Step Command callback (from client) + */ +bool emberAfFanControlClusterStepCallback(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::FanControl::Commands::Step::DecodableType & commandData); /** * @brief Color Control Cluster MoveToHue Command callback (from client) */ diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h index 07c3a13093393e..552065201c028a 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h @@ -2165,6 +2165,30 @@ static auto __attribute__((unused)) EnsureKnownEnumValue(Thermostat::ThermostatS } } +static auto __attribute__((unused)) EnsureKnownEnumValue(FanControl::AirflowDirectionEnum val) +{ + using EnumType = FanControl::AirflowDirectionEnum; + switch (val) + { + case EnumType::kForward: + case EnumType::kReverse: + return val; + default: + return static_cast(2); + } +} +static auto __attribute__((unused)) EnsureKnownEnumValue(FanControl::DirectionEnum val) +{ + using EnumType = FanControl::DirectionEnum; + switch (val) + { + case EnumType::kIncrease: + case EnumType::kDecrease: + return val; + default: + return static_cast(2); + } +} static auto __attribute__((unused)) EnsureKnownEnumValue(FanControl::FanModeSequenceType val) { using EnumType = FanControl::FanModeSequenceType; diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index 8b375098cd1df6..47bec8ad525f4d 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -2849,6 +2849,30 @@ enum class ModeForSequence : uint8_t namespace FanControl { +// Enum for AirflowDirectionEnum +enum class AirflowDirectionEnum : uint8_t +{ + kForward = 0x00, + kReverse = 0x01, + // All received enum values that are not listed above will be mapped + // to kUnknownEnumValue. This is a helper enum value that should only + // be used by code to process how it handles receiving and unknown + // enum value. This specific should never be transmitted. + kUnknownEnumValue = 2, +}; + +// Enum for DirectionEnum +enum class DirectionEnum : uint8_t +{ + kIncrease = 0x00, + kDecrease = 0x01, + // All received enum values that are not listed above will be mapped + // to kUnknownEnumValue. This is a helper enum value that should only + // be used by code to process how it handles receiving and unknown + // enum value. This specific should never be transmitted. + kUnknownEnumValue = 2, +}; + // Enum for FanModeSequenceType enum class FanModeSequenceType : uint8_t { @@ -2885,10 +2909,12 @@ enum class FanModeType : uint8_t // Bitmap for Feature enum class Feature : uint32_t { - kMultiSpeed = 0x1, - kAuto = 0x2, - kRocking = 0x4, - kWind = 0x8, + kMultiSpeed = 0x1, + kAuto = 0x2, + kRocking = 0x4, + kWind = 0x8, + kStep = 0x10, + kAirflowDirection = 0x20, }; // Bitmap for RockSupportMask diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index eb2f49dbf438e3..67f09f88a33d55 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -16854,7 +16854,53 @@ namespace Events {} // namespace Events } // namespace Thermostat namespace FanControl { -namespace Commands {} // namespace Commands +namespace Commands { +namespace Step { +CHIP_ERROR Type::Encode(TLV::TLVWriter & writer, TLV::Tag tag) const +{ + TLV::TLVType outer; + ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outer)); + ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(Fields::kDirection), direction)); + ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(Fields::kWrap), wrap)); + ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(Fields::kLowestOff), lowestOff)); + ReturnErrorOnFailure(writer.EndContainer(outer)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TLV::TLVType outer; + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE); + ReturnErrorOnFailure(reader.EnterContainer(outer)); + while ((err = reader.Next()) == CHIP_NO_ERROR) + { + if (!TLV::IsContextTag(reader.GetTag())) + { + continue; + } + switch (TLV::TagNumFromTag(reader.GetTag())) + { + case to_underlying(Fields::kDirection): + ReturnErrorOnFailure(DataModel::Decode(reader, direction)); + break; + case to_underlying(Fields::kWrap): + ReturnErrorOnFailure(DataModel::Decode(reader, wrap)); + break; + case to_underlying(Fields::kLowestOff): + ReturnErrorOnFailure(DataModel::Decode(reader, lowestOff)); + break; + default: + break; + } + } + + VerifyOrReturnError(err == CHIP_END_OF_TLV, err); + ReturnErrorOnFailure(reader.ExitContainer(outer)); + return CHIP_NO_ERROR; +} +} // namespace Step. +} // namespace Commands namespace Attributes { CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const ConcreteAttributePath & path) @@ -16894,6 +16940,9 @@ CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const Concre case Attributes::WindSetting::TypeInfo::GetAttributeId(): ReturnErrorOnFailure(DataModel::Decode(reader, windSetting)); break; + case Attributes::AirflowDirection::TypeInfo::GetAttributeId(): + ReturnErrorOnFailure(DataModel::Decode(reader, airflowDirection)); + break; case Attributes::GeneratedCommandList::TypeInfo::GetAttributeId(): ReturnErrorOnFailure(DataModel::Decode(reader, generatedCommandList)); break; @@ -27229,6 +27278,13 @@ bool CommandIsFabricScoped(ClusterId aCluster, CommandId aCommand) return false; } } + case Clusters::FanControl::Id: { + switch (aCommand) + { + default: + return false; + } + } case Clusters::ColorControl::Id: { switch (aCommand) { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index d14b3901dbb4b7..8b8c7fb08a173c 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -22138,6 +22138,57 @@ struct TypeInfo } // namespace Thermostat namespace FanControl { +namespace Commands { +// Forward-declarations so we can reference these later. + +namespace Step { +struct Type; +struct DecodableType; +} // namespace Step + +} // namespace Commands + +namespace Commands { +namespace Step { +enum class Fields : uint8_t +{ + kDirection = 0, + kWrap = 1, + kLowestOff = 2, +}; + +struct Type +{ +public: + // Use GetCommandId instead of commandId directly to avoid naming conflict with CommandIdentification in ExecutionOfACommand + static constexpr CommandId GetCommandId() { return Commands::Step::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::FanControl::Id; } + + DirectionEnum direction = static_cast(0); + Optional wrap; + Optional lowestOff; + + CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag) const; + + using ResponseType = DataModel::NullObjectType; + + static constexpr bool MustUseTimedInvoke() { return false; } +}; + +struct DecodableType +{ +public: + static constexpr CommandId GetCommandId() { return Commands::Step::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::FanControl::Id; } + + DirectionEnum direction = static_cast(0); + Optional wrap; + Optional lowestOff; + CHIP_ERROR Decode(TLV::TLVReader & reader); +}; +}; // namespace Step +} // namespace Commands + namespace Attributes { namespace FanMode { @@ -22272,6 +22323,18 @@ struct TypeInfo static constexpr bool MustUseTimedWrite() { return false; } }; } // namespace WindSetting +namespace AirflowDirection { +struct TypeInfo +{ + using Type = chip::app::Clusters::FanControl::AirflowDirectionEnum; + using DecodableType = chip::app::Clusters::FanControl::AirflowDirectionEnum; + using DecodableArgType = chip::app::Clusters::FanControl::AirflowDirectionEnum; + + static constexpr ClusterId GetClusterId() { return Clusters::FanControl::Id; } + static constexpr AttributeId GetAttributeId() { return Attributes::AirflowDirection::Id; } + static constexpr bool MustUseTimedWrite() { return false; } +}; +} // namespace AirflowDirection namespace GeneratedCommandList { struct TypeInfo : public Clusters::Globals::Attributes::GeneratedCommandList::TypeInfo { @@ -22329,6 +22392,8 @@ struct TypeInfo Attributes::RockSetting::TypeInfo::DecodableType rockSetting = static_cast(0); Attributes::WindSupport::TypeInfo::DecodableType windSupport = static_cast(0); Attributes::WindSetting::TypeInfo::DecodableType windSetting = static_cast(0); + Attributes::AirflowDirection::TypeInfo::DecodableType airflowDirection = + static_cast(0); Attributes::GeneratedCommandList::TypeInfo::DecodableType generatedCommandList; Attributes::AcceptedCommandList::TypeInfo::DecodableType acceptedCommandList; Attributes::EventList::TypeInfo::DecodableType eventList; diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h b/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h index 88b4d8762d1b01..98137f41b0da0e 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h @@ -4038,6 +4038,10 @@ namespace WindSetting { static constexpr AttributeId Id = 0x0000000A; } // namespace WindSetting +namespace AirflowDirection { +static constexpr AttributeId Id = 0x0000000B; +} // namespace AirflowDirection + namespace GeneratedCommandList { static constexpr AttributeId Id = Globals::Attributes::GeneratedCommandList::Id; } // namespace GeneratedCommandList diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h b/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h index 566f589cb7ed9d..f1649335f7d8ed 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h @@ -997,6 +997,16 @@ static constexpr CommandId Id = 0x00000003; } // namespace Commands } // namespace Thermostat +namespace FanControl { +namespace Commands { + +namespace Step { +static constexpr CommandId Id = 0x00000000; +} // namespace Step + +} // namespace Commands +} // namespace FanControl + namespace ColorControl { namespace Commands { diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index e0387eb485c90c..01b4df44e383df 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -6324,6 +6324,7 @@ class ThermostatClearWeeklySchedule : public ClusterCommand | Cluster FanControl | 0x0202 | |------------------------------------------------------------------------------| | Commands: | | +| * Step | 0x00 | |------------------------------------------------------------------------------| | Attributes: | | | * FanMode | 0x0000 | @@ -6337,6 +6338,7 @@ class ThermostatClearWeeklySchedule : public ClusterCommand | * RockSetting | 0x0008 | | * WindSupport | 0x0009 | | * WindSetting | 0x000A | +| * AirflowDirection | 0x000B | | * GeneratedCommandList | 0xFFF8 | | * AcceptedCommandList | 0xFFF9 | | * EventList | 0xFFFA | @@ -6347,6 +6349,38 @@ class ThermostatClearWeeklySchedule : public ClusterCommand | Events: | | \*----------------------------------------------------------------------------*/ +/* + * Command Step + */ +class FanControlStep : public ClusterCommand +{ +public: + FanControlStep(CredentialIssuerCommands * credsIssuerConfig) : ClusterCommand("step", credsIssuerConfig) + { + AddArgument("Direction", 0, UINT8_MAX, &mRequest.direction); + AddArgument("Wrap", 0, 1, &mRequest.wrap); + AddArgument("LowestOff", 0, 1, &mRequest.lowestOff); + ClusterCommand::AddArguments(); + } + + CHIP_ERROR SendCommand(chip::DeviceProxy * device, std::vector endpointIds) override + { + ChipLogProgress(chipTool, "Sending cluster (0x00000202) command (0x00000000) on endpoint %u", endpointIds.at(0)); + + return ClusterCommand::SendCommand(device, endpointIds.at(0), 0x00000202, 0x00000000, mRequest); + } + + CHIP_ERROR SendGroupCommand(chip::GroupId groupId, chip::FabricIndex fabricIndex) override + { + ChipLogProgress(chipTool, "Sending cluster (0x00000202) command (0x00000000) on Group %u", groupId); + + return ClusterCommand::SendGroupCommand(groupId, fabricIndex, 0x00000202, 0x00000000, mRequest); + } + +private: + chip::app::Clusters::FanControl::Commands::Step::Type mRequest; +}; + /*----------------------------------------------------------------------------*\ | Cluster ThermostatUserInterfaceConfiguration | 0x0204 | |------------------------------------------------------------------------------| @@ -16514,6 +16548,7 @@ void registerClusterFanControl(Commands & commands, CredentialIssuerCommands * c // Commands // make_unique(Id, credsIssuerConfig), // + make_unique(credsIssuerConfig), // // // Attributes // @@ -16529,6 +16564,7 @@ void registerClusterFanControl(Commands & commands, CredentialIssuerCommands * c make_unique(Id, "rock-setting", Attributes::RockSetting::Id, credsIssuerConfig), // make_unique(Id, "wind-support", Attributes::WindSupport::Id, credsIssuerConfig), // make_unique(Id, "wind-setting", Attributes::WindSetting::Id, credsIssuerConfig), // + make_unique(Id, "airflow-direction", Attributes::AirflowDirection::Id, credsIssuerConfig), // make_unique(Id, "generated-command-list", Attributes::GeneratedCommandList::Id, credsIssuerConfig), // make_unique(Id, "accepted-command-list", Attributes::AcceptedCommandList::Id, credsIssuerConfig), // make_unique(Id, "event-list", Attributes::EventList::Id, credsIssuerConfig), // @@ -16558,6 +16594,9 @@ void registerClusterFanControl(Commands & commands, CredentialIssuerCommands * c WriteCommandType::kForceWrite, credsIssuerConfig), // make_unique>(Id, "wind-setting", 0, UINT8_MAX, Attributes::WindSetting::Id, WriteCommandType::kWrite, credsIssuerConfig), // + make_unique>( + Id, "airflow-direction", 0, UINT8_MAX, Attributes::AirflowDirection::Id, WriteCommandType::kWrite, + credsIssuerConfig), // make_unique>>( Id, "generated-command-list", Attributes::GeneratedCommandList::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // @@ -16583,6 +16622,7 @@ void registerClusterFanControl(Commands & commands, CredentialIssuerCommands * c make_unique(Id, "rock-setting", Attributes::RockSetting::Id, credsIssuerConfig), // make_unique(Id, "wind-support", Attributes::WindSupport::Id, credsIssuerConfig), // make_unique(Id, "wind-setting", Attributes::WindSetting::Id, credsIssuerConfig), // + make_unique(Id, "airflow-direction", Attributes::AirflowDirection::Id, credsIssuerConfig), // make_unique(Id, "generated-command-list", Attributes::GeneratedCommandList::Id, credsIssuerConfig), // make_unique(Id, "accepted-command-list", Attributes::AcceptedCommandList::Id, credsIssuerConfig), // make_unique(Id, "event-list", Attributes::EventList::Id, credsIssuerConfig), // diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index a4d2745f7bacf4..dcb545deebe5a8 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -9744,6 +9744,11 @@ CHIP_ERROR DataModelLogger::LogAttribute(const chip::app::ConcreteDataAttributeP ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); return DataModelLogger::LogValue("WindSetting", 1, value); } + case FanControl::Attributes::AirflowDirection::Id: { + chip::app::Clusters::FanControl::AirflowDirectionEnum value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("AirflowDirection", 1, value); + } case FanControl::Attributes::GeneratedCommandList::Id: { chip::app::DataModel::DecodableList value; ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index 65f4b62b962dc3..5840ca5d195d1c 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -70204,6 +70204,7 @@ class SubscribeAttributeThermostatClusterRevision : public SubscribeAttribute { | Cluster FanControl | 0x0202 | |------------------------------------------------------------------------------| | Commands: | | +| * Step | 0x00 | |------------------------------------------------------------------------------| | Attributes: | | | * FanMode | 0x0000 | @@ -70217,6 +70218,7 @@ class SubscribeAttributeThermostatClusterRevision : public SubscribeAttribute { | * RockSetting | 0x0008 | | * WindSupport | 0x0009 | | * WindSetting | 0x000A | +| * AirflowDirection | 0x000B | | * GeneratedCommandList | 0xFFF8 | | * AcceptedCommandList | 0xFFF9 | | * EventList | 0xFFFA | @@ -70227,6 +70229,64 @@ class SubscribeAttributeThermostatClusterRevision : public SubscribeAttribute { | Events: | | \*----------------------------------------------------------------------------*/ +/* + * Command Step + */ +class FanControlStep : public ClusterCommand { +public: + FanControlStep() + : ClusterCommand("step") + { + AddArgument("Direction", 0, UINT8_MAX, &mRequest.direction); + AddArgument("Wrap", 0, 1, &mRequest.wrap); + AddArgument("LowestOff", 0, 1, &mRequest.lowestOff); + ClusterCommand::AddArguments(); + } + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + ChipLogProgress(chipTool, "Sending cluster (0x00000202) command (0x00000000) on endpoint %u", endpointId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterFanControl alloc] initWithDevice:device + endpointID:@(endpointId) + queue:callbackQueue]; + __auto_type * params = [[MTRFanControlClusterStepParams alloc] init]; + params.timedInvokeTimeoutMs + = mTimedInteractionTimeoutMs.HasValue() ? [NSNumber numberWithUnsignedShort:mTimedInteractionTimeoutMs.Value()] : nil; + params.direction = [NSNumber numberWithUnsignedChar:chip::to_underlying(mRequest.direction)]; + if (mRequest.wrap.HasValue()) { + params.wrap = [NSNumber numberWithBool:mRequest.wrap.Value()]; + } else { + params.wrap = nil; + } + if (mRequest.lowestOff.HasValue()) { + params.lowestOff = [NSNumber numberWithBool:mRequest.lowestOff.Value()]; + } else { + params.lowestOff = nil; + } + uint16_t repeatCount = mRepeatCount.ValueOr(1); + uint16_t __block responsesNeeded = repeatCount; + while (repeatCount--) { + [cluster stepWithParams:params + completion:^(NSError * _Nullable error) { + responsesNeeded--; + if (error != nil) { + mError = error; + LogNSError("Error", error); + } + if (responsesNeeded == 0) { + SetCommandExitStatus(mError); + } + }]; + } + return CHIP_NO_ERROR; + } + +private: + chip::app::Clusters::FanControl::Commands::Step::Type mRequest; +}; + /* * Attribute FanMode */ @@ -71237,6 +71297,116 @@ class SubscribeAttributeFanControlWindSetting : public SubscribeAttribute { } }; +/* + * Attribute AirflowDirection + */ +class ReadFanControlAirflowDirection : public ReadAttribute { +public: + ReadFanControlAirflowDirection() + : ReadAttribute("airflow-direction") + { + } + + ~ReadFanControlAirflowDirection() {} + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + ChipLogProgress(chipTool, "Sending cluster (0x00000202) ReadAttribute (0x0000000B) on endpoint %u", endpointId); + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterFanControl alloc] initWithDevice:device + endpointID:@(endpointId) + queue:callbackQueue]; + [cluster readAttributeAirflowDirectionWithCompletion:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"FanControl.AirflowDirection response %@", [value description]); + if (error != nil) { + LogNSError("FanControl AirflowDirection read Error", error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } +}; + +class WriteFanControlAirflowDirection : public WriteAttribute { +public: + WriteFanControlAirflowDirection() + : WriteAttribute("airflow-direction") + { + AddArgument("attr-name", "airflow-direction"); + AddArgument("attr-value", 0, UINT8_MAX, &mValue); + WriteAttribute::AddArguments(); + } + + ~WriteFanControlAirflowDirection() {} + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + ChipLogProgress(chipTool, "Sending cluster (0x00000202) WriteAttribute (0x0000000B) on endpoint %u", endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterFanControl alloc] initWithDevice:device + endpointID:@(endpointId) + queue:callbackQueue]; + __auto_type * params = [[MTRWriteParams alloc] init]; + params.timedWriteTimeout + = mTimedInteractionTimeoutMs.HasValue() ? [NSNumber numberWithUnsignedShort:mTimedInteractionTimeoutMs.Value()] : nil; + params.dataVersion = mDataVersion.HasValue() ? [NSNumber numberWithUnsignedInt:mDataVersion.Value()] : nil; + NSNumber * _Nonnull value = [NSNumber numberWithUnsignedChar:mValue]; + + [cluster writeAttributeAirflowDirectionWithValue:value + params:params + completion:^(NSError * _Nullable error) { + if (error != nil) { + LogNSError("FanControl AirflowDirection write Error", error); + } + SetCommandExitStatus(error); + }]; + return CHIP_NO_ERROR; + } + +private: + uint8_t mValue; +}; + +class SubscribeAttributeFanControlAirflowDirection : public SubscribeAttribute { +public: + SubscribeAttributeFanControlAirflowDirection() + : SubscribeAttribute("airflow-direction") + { + } + + ~SubscribeAttributeFanControlAirflowDirection() {} + + CHIP_ERROR SendCommand(MTRBaseDevice * device, chip::EndpointId endpointId) override + { + ChipLogProgress(chipTool, "Sending cluster (0x00000202) ReportAttribute (0x0000000B) on endpoint %u", endpointId); + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL); + __auto_type * cluster = [[MTRBaseClusterFanControl alloc] initWithDevice:device + endpointID:@(endpointId) + queue:callbackQueue]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)]; + if (mKeepSubscriptions.HasValue()) { + params.replaceExistingSubscriptions = !mKeepSubscriptions.Value(); + } + if (mFabricFiltered.HasValue()) { + params.filterByFabric = mFabricFiltered.Value(); + } + if (mAutoResubscribe.HasValue()) { + params.resubscribeAutomatically = mAutoResubscribe.Value(); + } + [cluster subscribeAttributeAirflowDirectionWithParams:params + subscriptionEstablished:^() { + mSubscriptionEstablished = YES; + } + reportHandler:^(NSNumber * _Nullable value, NSError * _Nullable error) { + NSLog(@"FanControl.AirflowDirection response %@", [value description]); + SetCommandExitStatus(error); + }]; + + return CHIP_NO_ERROR; + } +}; + /* * Attribute GeneratedCommandList */ @@ -162828,6 +162998,7 @@ void registerClusterFanControl(Commands & commands) commands_list clusterCommands = { make_unique(Id), // + make_unique(), // make_unique(Id), // make_unique(), // make_unique(Id), // @@ -162859,6 +163030,9 @@ void registerClusterFanControl(Commands & commands) make_unique(), // make_unique(), // make_unique(), // + make_unique(), // + make_unique(), // + make_unique(), // make_unique(), // make_unique(), // make_unique(), //