From 03e95d412fde4a39b4f25473bab7fa9d9ac25b60 Mon Sep 17 00:00:00 2001 From: pcoleman Date: Tue, 23 Jul 2024 14:32:34 +0100 Subject: [PATCH 01/30] Add WHM to the all clusters app --- .../all-clusters-app.matter | 140 +++++ .../all-clusters-common/all-clusters-app.zap | 377 ++++++++++++ .../include/WhmAppCmdLineOptions.h | 34 ++ .../all-clusters-common/include/WhmDelegate.h | 349 +++++++++++ .../all-clusters-common/include/WhmInstance.h | 59 ++ .../all-clusters-common/include/WhmMain.h | 32 ++ .../include/WhmManufacturer.h | 132 +++++ .../src/WhmDelegateImpl.cpp | 541 ++++++++++++++++++ .../all-clusters-common/src/WhmInstance.cpp | 34 ++ .../all-clusters-common/src/WhmMain.cpp | 196 +++++++ .../src/WhmManufacturer.cpp | 270 +++++++++ examples/all-clusters-app/linux/BUILD.gn | 6 + examples/all-clusters-app/linux/args.gni | 1 + .../all-clusters-app/linux/main-common.cpp | 6 + examples/platform/linux/AppMain.cpp | 7 + examples/platform/linux/BUILD.gn | 7 + src/app/chip_data_model.gni | 6 + ...rHeaterManagementTestEventTriggerHandler.h | 86 +++ .../water-heater-management-server.cpp | 192 +++++++ .../water-heater-management-server.h | 132 +++++ src/app/common/templates/config-data.yaml | 3 + src/app/util/util.cpp | 2 + .../zcl/zcl-with-test-extensions.json | 10 + src/app/zap-templates/zcl/zcl.json | 10 + src/app/zap_cluster_list.json | 4 + .../data_model/controller-clusters.zap | 61 ++ .../python/chip/clusters/Objects.py | 10 +- .../zap-generated/attributes/Accessors.cpp | 387 +------------ .../zap-generated/attributes/Accessors.h | 65 +-- .../app-common/zap-generated/callback.h | 18 - .../zap-generated/cluster-enums-check.h | 14 - .../app-common/zap-generated/cluster-enums.h | 10 +- 32 files changed, 2709 insertions(+), 492 deletions(-) create mode 100755 examples/all-clusters-app/all-clusters-common/include/WhmAppCmdLineOptions.h create mode 100644 examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h create mode 100644 examples/all-clusters-app/all-clusters-common/include/WhmInstance.h create mode 100644 examples/all-clusters-app/all-clusters-common/include/WhmMain.h create mode 100644 examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h create mode 100644 examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp create mode 100644 examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp create mode 100644 examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp create mode 100644 examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp create mode 100644 src/app/clusters/water-heater-management-server/WaterHeaterManagementTestEventTriggerHandler.h create mode 100644 src/app/clusters/water-heater-management-server/water-heater-management-server.cpp create mode 100644 src/app/clusters/water-heater-management-server/water-heater-management-server.h 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 8c87eb8a6a6c20..5991c16983cfaf 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 @@ -4075,6 +4075,64 @@ cluster ElectricalEnergyMeasurement = 145 { readonly attribute int16u clusterRevision = 65533; } +/** This cluster is used to allow clients to control the operation of a hot water heating appliance so that it can be used with energy management. */ +provisional cluster WaterHeaterManagement = 148 { + revision 1; + + enum BoostStateEnum : enum8 { + kInactive = 0; + kActive = 1; + } + + bitmap Feature : bitmap32 { + kEnergyManagement = 0x1; + kTankPercent = 0x2; + } + + bitmap WaterHeaterDemandBitmap : bitmap8 { + kImmersionElement1 = 0x1; + kImmersionElement2 = 0x2; + kHeatPump = 0x4; + kBoiler = 0x8; + kOther = 0x10; + } + + bitmap WaterHeaterTypeBitmap : bitmap8 { + kImmersionElement1 = 0x1; + kImmersionElement2 = 0x2; + kHeatPump = 0x4; + kBoiler = 0x8; + kOther = 0x10; + } + + readonly attribute WaterHeaterTypeBitmap heaterTypes = 0; + readonly attribute WaterHeaterDemandBitmap heatDemand = 1; + readonly attribute optional int16u tankVolume = 2; + readonly attribute optional energy_mwh estimatedHeatRequired = 3; + readonly attribute optional percent tankPercentage = 4; + readonly attribute BoostStateEnum boostState = 5; + 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 BoostRequest { + elapsed_s duration = 0; + optional boolean oneShot = 1; + optional boolean emergencyBoost = 2; + optional temperature temporarySetpoint = 3; + optional percent targetPercentage = 4; + optional percent targetReheat = 5; + } + + /** Allows a client to request that the water heater is put into a Boost state. */ + command access(invoke: manage) Boost(BoostRequest): DefaultSuccess = 0; + /** Allows a client to cancel an ongoing Boost operation. */ + command access(invoke: manage) CancelBoost(): DefaultSuccess = 1; +} + /** This cluster allows a client to manage the power draw of a device. An example of such a client could be an Energy Management System (EMS) which controls an Energy Smart Appliance (ESA). */ provisional cluster DeviceEnergyManagement = 152 { revision 4; @@ -4582,6 +4640,56 @@ cluster EnergyEvseMode = 157 { command ChangeToMode(ChangeToModeRequest): ChangeToModeResponse = 0; } +/** Attributes and commands for selecting a mode from a list of supported options. */ +cluster WaterHeaterMode = 158 { + revision 1; + + enum ModeTag : enum16 { + kOff = 16384; + kManual = 16385; + kTimed = 16386; + } + + bitmap Feature : bitmap32 { + kOnOff = 0x1; + } + + struct ModeTagStruct { + optional vendor_id mfgCode = 0; + enum16 value = 1; + } + + struct ModeOptionStruct { + char_string<64> label = 0; + int8u mode = 1; + ModeTagStruct modeTags[] = 2; + } + + readonly attribute ModeOptionStruct supportedModes[] = 0; + readonly attribute int8u currentMode = 1; + attribute optional nullable int8u startUpMode = 2; + attribute optional nullable int8u onMode = 3; + 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 ChangeToModeRequest { + int8u newMode = 0; + } + + response struct ChangeToModeResponse = 1 { + enum8 status = 0; + optional char_string statusText = 1; + } + + /** This command is used to change device modes. + On receipt of this command the device SHALL respond with a ChangeToModeResponse command. */ + command ChangeToMode(ChangeToModeRequest): ChangeToModeResponse = 0; +} + /** Attributes and commands for selecting a mode from a list of supported options. */ provisional cluster DeviceEnergyManagementMode = 159 { revision 1; @@ -8377,6 +8485,24 @@ endpoint 1 { ram attribute clusterRevision default = 1; } + server cluster WaterHeaterManagement { + callback attribute heaterTypes; + callback attribute heatDemand; + callback attribute tankVolume; + callback attribute estimatedHeatRequired; + callback attribute tankPercentage; + callback attribute boostState; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + + handle command Boost; + handle command CancelBoost; + } + server cluster DeviceEnergyManagement { emits event PowerAdjustStart; emits event PowerAdjustEnd; @@ -8489,6 +8615,20 @@ endpoint 1 { handle command ChangeToModeResponse; } + server cluster WaterHeaterMode { + callback attribute supportedModes; + callback attribute currentMode; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + ram attribute clusterRevision default = 1; + + handle command ChangeToMode; + handle command ChangeToModeResponse; + } + server cluster DeviceEnergyManagementMode { callback attribute supportedModes; callback attribute currentMode; 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 6491378c9e83d7..348c3db8affcd8 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 @@ -13413,6 +13413,227 @@ } ] }, + { + "name": "Water Heater Management", + "code": 148, + "mfgCode": null, + "define": "WATER_HEATER_MANAGEMENT_CLUSTER", + "side": "server", + "enabled": 1, + "apiMaturity": "provisional", + "commands": [ + { + "name": "Boost", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CancelBoost", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "HeaterTypes", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "WaterHeaterTypeBitmap", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "HeatDemand", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "WaterHeaterDemandBitmap", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TankVolume", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EstimatedHeatRequired", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "energy_mwh", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TankPercentage", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "percent", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BoostState", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "BoostStateEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Device Energy Management", "code": 152, @@ -14745,6 +14966,162 @@ } ] }, + { + "name": "Water Heater Mode", + "code": 158, + "mfgCode": null, + "define": "WATER_HEATER_MODE_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "ChangeToMode", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ChangeToModeResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "SupportedModes", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentMode", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Device Energy Management Mode", "code": 159, diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmAppCmdLineOptions.h b/examples/all-clusters-app/all-clusters-common/include/WhmAppCmdLineOptions.h new file mode 100755 index 00000000000000..1f995c78503035 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmAppCmdLineOptions.h @@ -0,0 +1,34 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +chip::BitMask GetFeatureMapFromCmdLine(); + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h new file mode 100644 index 00000000000000..36651b9c70140b --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -0,0 +1,349 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +using ModeTagStructType = detail::Structs::ModeTagStruct::Type; + +class WhmManufacturer; + +// This is an application level delegate to handle operational state commands according to the specific business logic. +class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate, public ModeBase::Delegate +{ +public: + WaterHeaterManagementDelegate(EndpointId clustersEndpoint); + + virtual ~WaterHeaterManagementDelegate() = default; + + void SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance); + + void SetWhmManufacturer(WhmManufacturer & whmManufacturer); + + /********************************************************************************* + * + * Methods implementing the WaterHeaterManagement::Delegate interace + * + *********************************************************************************/ + + /** + * @brief Delegate should implement a handler to start boosting the water temperature as required. + * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the water in the tank (or the + * TargetPercentage of the water, if included) to be heated towards the set point (or the TemporarySetpoint, if included), which + * in turn may cause a call for heat, even if the mode is OFF, or is TIMED and it is during one of the Off periods. + * + * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts + * to the previous mode (e.g. OFF, MANUAL or TIMED). + * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the + * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). + * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause + * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). + * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be + * used instead of the normal set point temperature whilst the BOOST state is active. + * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be + * heated by this Boost command before the heater is switched off. + * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because + * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field + * indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. + * + * @return Success if the boost command is accepted; otherwise the command SHALL be rejected with appropriate error. + */ + Protocols::InteractionModel::Status HandleBoost(uint32_t duration, Optional oneShot, Optional emergencyBoost, + Optional temporarySetpoint, Optional targetPercentage, + Optional targetReheat) override; + + /** + * @brief Delegate should implement a handler to cancel a boost command. + * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode (e.g. OFF, MANUAL or + * TIMED). + * + * @return It should report SUCCESS if successful and FAILURE otherwise. + */ + Protocols::InteractionModel::Status HandleCancelBoost() override; + + // ------------------------------------------------------------------ + // Get attribute methods + BitMask GetHeaterTypes() override; + BitMask GetHeatDemand() override; + uint16_t GetTankVolume() override; + int64_t GetEstimatedHeatRequired() override; + Percent GetTankPercentage() override; + BoostStateEnum GetBoostState() override; + + // ------------------------------------------------------------------ + // Set attribute methods + void SetHeaterTypes(BitMask heaterTypes); + void SetHeatDemand(BitMask heatDemand); + void SetTankVolume(uint16_t tankVolume); + void SetEstimatedHeatRequired(int64_t estimatedHeatRequired); + void SetTankPercentage(Percent tankPercentage); + void SetBoostState(BoostStateEnum boostState); + + /********************************************************************************* + * + * Methods implementing the ModeBase::Delegate interface + * + *********************************************************************************/ + + CHIP_ERROR Init() override; + + /** + * Handle application logic when the mode is changing. + * + * @param mode The new mode that the device is requested to transition to. + * @param response A reference to a response that will be sent to the client. The contents of which con be modified by the + * application. + */ + void HandleChangeToMode(uint8_t mode, ModeBase::Commands::ChangeToModeResponse::Type & response) override; + + /** + * Get the mode label of the Nth mode in the list of modes. + * + * @param modeIndex The index of the mode to be returned. It is assumed that modes are indexable from 0 and with no gaps. + * @param label A reference to the mutable char span which will be mutated to receive the label on success. Use + * CopyCharSpanToMutableCharSpan to copy into the MutableCharSpan. + * + * @return Returns a CHIP_NO_ERROR if there was no error and the label was returned successfully. + * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available labels. + */ + CHIP_ERROR GetModeLabelByIndex(uint8_t modeIndex, MutableCharSpan & label) override; + + /** + * Get the mode value of the Nth mode in the list of modes. + * + * @param modeIndex The index of the mode to be returned. It is assumed that modes are indexable from 0 and with no gaps. + * @param value a reference to the uint8_t variable that is to contain the mode value. + * + * @return Returns a CHIP_NO_ERROR if there was no error and the value was returned successfully. + * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available values. + */ + CHIP_ERROR GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) override; + + /** + * Get the mode tags of the Nth mode in the list of modes. + * @param modeIndex The index of the mode to be returned. It is assumed that modes are indexable from 0 and with no gaps. + * @param tags a reference to an existing and initialised buffer that is to contain the mode tags. std::copy can be used + * to copy into the buffer. + * + * @return Returns a CHIP_NO_ERROR if there was no error and the mode tags were returned successfully. + * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available mode tags. + */ + CHIP_ERROR GetModeTagsByIndex(uint8_t modeIndex, DataModel::List & tags) override; + + /********************************************************************************* + * + * WaterHeaterManagementDelegate specific methods + * + *********************************************************************************/ + + /** + * @brief Set the Water Header Mode and act accordingly. + * + * @param mode The Water Heater Mode (e.g. OFF, MANUAL or TIMED). + */ + void SetWaterHeaterMode(uint8_t mode); + + /** + * @brief Set the water temperature of the tank + * + * @param waterTemperature The water temperature in 100th's Celsius + */ + void SetWaterTemperature(uint16_t waterTemperature); + + /** + * @brief Set the target water temperature of the tank + * + * @param targetWaterTemperature The water temperature in 100th's Celsius + */ + void SetTargetWaterTemperature(uint16_t targetWaterTemperature); + + /** + * @brief Determine whether the heating sources need to be turned on or off + */ + Protocols::InteractionModel::Status CheckIfHeatNeedsToBeTurnedOnOrOff(); + + /** + * @brief Static timer callback for when Boost timer expires. + */ + static void BoostTimerExpiry(System::Layer * systemLayer, void * delegate); + + /** + * @brief Object timer callback for when Boost timer expires. + */ + void HandleBoostTimerExpiry(); + + /** + * Determines whether the tank water temperature has reached the target temperature. + * + * @return Returns True is tank water temperature has reached the target temperature, False otherwise. + */ + bool HasWaterTemperatureReachedTarget() const; + + /** + * Simulates water being drawn from the water tank. + * + * @param percentageReplaced The % of water being replaced with water with a temperature of replacedWaterTemperature. + * @param replacedWaterTemperature The temperature of the percentageReplaced water. + */ + void DrawOffHotWater(uint8_t percentageReplaced, uint16_t replacedWaterTemperature); + + /********************************************************************************* + * + * Public constants + * + *********************************************************************************/ + + // The Water Header Modes + + // The device will not attempt to keep the water warm. + static constexpr uint8_t ModeOff = 0; + + // The device will attempt to keep the water warm based on the OccupiedHeatingSetpoint attribute of the associated Thermostat + // cluster. + static constexpr uint8_t ModeManual = 1; + + // The device will attempt to keep the water warm based on the Schedules attribute of the associated Thermostat cluster. + static constexpr uint8_t ModeTimed = 2; + +private: + /********************************************************************************* + * + * WaterHeaterManagementDelegate specific attributes + * + *********************************************************************************/ + + // Need the following so can determine which features are supported + WaterHeaterManagement::Instance * mpWhmInstance; + + // Pointer to the manufacturer specific object which understand the hardware + WhmManufacturer * mpWhmManufacturer; + + // Target water temperature in 100ths of a C + uint16_t mTargetWaterTemperature; + + // Actual water temperature in 100ths of a C + uint16_t mHotWaterTemperature; + + // The % of hot water remaining at mHotWaterTemperature +// uint8_t mPercentageHotWater; + + // The (100 - mPercentageHotWater)% of water at mReplacedWaterTemperature + uint16_t mReplacedWaterTemperature; + + // Boost command parameters + + // This field SHALL indicate whether the BOOST state should be automatically canceled once the hot water has first reached the + // set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). + Optional mBoostOneShot; + + // This field indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause multiple heat + // sources to be activated (e.g. a heat pump and direct electric heating element). + Optional mBoostEmergencyBoost; + + // This field indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be used instead + // of the normal set point temperature whilst the BOOST state is active. + Optional mBoostTemporarySetpoint; + + // If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be heated by this Boost + // command before the heater is switched off. This field is optional, however it SHALL be included if the TargetReheat field is + // included. + Optional mBoostTargetPercentage; + + // If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because the TargetPercentage + // of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field indicates the + // percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. For example if + // the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% hot water, the tank may have + // hot water drawn off until only 40% hot water remains. At this point the heater will begin to heat back up to 80% of hot + // water. If this field and the OneShot field were both omitted, heating would begin again after any water draw which reduced + // the TankPercentage below 80%. + Optional mBoostTargetReheat; + + // Track whether the water temperature has reached the water temperature specified in the boost command. Used in conjunction + // with the boost command boostTargetReheat parameter + bool mBoostTargetTemperatureReached; + + /********************************************************************************* + * + * Member variables implementing the WaterHeaterManagement::Delegate interface + * + *********************************************************************************/ + + // Access to the Water Heater Mode instance + ModeBase::Instance mWaterHeaterModeInstance; + + // This attribute SHALL indicate the methods to call for heat that the controller supports. If a bit is set then the controller + // supports the corresponding method. + BitMask mHeaterTypes; + + // This attribute SHALL indicate if the controller is asking for heat. If a bit is set then the corresponding call for heat is + // active. + BitMask mHeatDemand; + + // This attribute SHALL indicate the volume of water that the hot water tank can hold (in units of Litres). This allows an + // energy management system to estimate the required heating energy needed to reach the target temperature. + uint16_t mTankVolume; + + // This attribute SHALL indicate the estimated heat energy needed to raise the water temperature to the target setpoint. This + // can be computed by taking the specific heat capacity of water (4182 J/kg °C) and by knowing the current temperature of the + // water, the tank volume and target temperature. + int64_t mEstimatedHeatRequired; + + // This attribute SHALL indicate an approximate level of hot water stored in the tank, which may help consumers understand the + // amount of hot water remaining in the tank. + Percent mTankPercentage; + + // This attribute SHALL indicate if the BOOST state, as triggered by a Boost command, is currently active. + BoostStateEnum mBoostState; + + /********************************************************************************* + * + * Member variables implementing the ModeBase::Delegate interface + * + *********************************************************************************/ + + ModeTagStructType modeTagsOff[1] = { { .value = to_underlying(WaterHeaterMode::ModeTag::kOff) } }; + ModeTagStructType modeTagsManual[1] = { { .value = to_underlying(WaterHeaterMode::ModeTag::kManual) } }; + ModeTagStructType modeTagsTimed[1] = { { .value = to_underlying(WaterHeaterMode::ModeTag::kTimed) } }; + + const detail::Structs::ModeOptionStruct::Type kModeOptions[3] = { + detail::Structs::ModeOptionStruct::Type{ .label = CharSpan::fromCharString("Off"), + .mode = ModeOff, + .modeTags = DataModel::List(modeTagsOff) }, + detail::Structs::ModeOptionStruct::Type{ .label = CharSpan::fromCharString("Manual"), + .mode = ModeManual, + .modeTags = DataModel::List(modeTagsManual) }, + detail::Structs::ModeOptionStruct::Type{ .label = CharSpan::fromCharString("Timed"), + .mode = ModeTimed, + .modeTags = DataModel::List(modeTagsTimed) } + }; +}; + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h b/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h new file mode 100644 index 00000000000000..4170f2c0c80896 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h @@ -0,0 +1,59 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { +using namespace chip::app::Clusters::WaterHeaterManagement; + + +class WaterHeaterManagementInstance : public Instance +{ +public: + WaterHeaterManagementInstance(EndpointId aEndpointId, WaterHeaterManagementDelegate & aDelegate, Feature aFeature) : + WaterHeaterManagement::Instance(aEndpointId, aDelegate, aFeature) + { + mDelegate = &aDelegate; + } + + // Delete copy constructor and assignment operator. + WaterHeaterManagementInstance(const WaterHeaterManagementInstance &) = delete; + WaterHeaterManagementInstance(const WaterHeaterManagementInstance &&) = delete; + WaterHeaterManagementInstance & operator=(const WaterHeaterManagementInstance &) = delete; + + CHIP_ERROR Init(); + void Shutdown(); + + WaterHeaterManagementDelegate * GetDelegate() { return mDelegate; }; + +private: + WaterHeaterManagementDelegate * mDelegate; +}; + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmMain.h b/examples/all-clusters-app/all-clusters-common/include/WhmMain.h new file mode 100644 index 00000000000000..21dc9d86519307 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmMain.h @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +void WhmApplicationInit(); +void WhmApplicationShutdown(); + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h new file mode 100644 index 00000000000000..09d1dff17a9c27 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h @@ -0,0 +1,132 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +/** + * The WhmManufacturer example class + * + * Helps with handling the test triggers. + */ + +class WhmManufacturer +{ +public: + WhmManufacturer(WaterHeaterManagementInstance * whmInstance) { mWhmInstance = whmInstance; } + + WaterHeaterManagementInstance * GetWhmInstance() { return mWhmInstance; } + + WaterHeaterManagementDelegate * GetWhmDelegate() + { + if (mWhmInstance) + { + return mWhmInstance->GetDelegate(); + } + + return nullptr; + } + + /** + * @brief Called at start up to apply hardware settings + */ + CHIP_ERROR Init(); + + /** + * @brief Called at shutdown + */ + CHIP_ERROR Shutdown(); + + /** + * @brief Called to determine which heating sources to use, + */ + BitMask DetermineHeatingSources(); + + /** + * @brief Turn the heating of the water tank on. + */ + Protocols::InteractionModel::Status TurnHeatingOn(); + + /** + * @brief Turn the heating of the water tank off. + */ + Protocols::InteractionModel::Status TurnHeatingOff(); + + /** + * @brief Called to handle a boost command. + * + * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts + * to the previous mode (e.g. OFF, MANUAL or TIMED). + * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the + * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). + * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause + * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). + * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be + * used instead of the normal set point temperature whilst the BOOST state is active. + * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be + * heated by this Boost command before the heater is switched off. + * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because + * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field + * indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. + * + * @return Success if the boost command is successful; otherwise return the appropriate error. + */ + Protocols::InteractionModel::Status BoostCommandStarted(uint32_t duration, Optional oneShot, + Optional emergencyBoost, Optional temporarySetpoint, + Optional targetPercentage, + Optional targetReheat); + + /** + * @brief Called when the Boost command has been cancelled. + * + * @return It should report SUCCESS if successful and FAILURE otherwise. + */ + Protocols::InteractionModel::Status BoostCommandCancelled(); + + /** + * @brief Called when a boost command has completed. + */ + void BoostCommandFinished(); + +private: + WaterHeaterManagementInstance * mWhmInstance; + bool mBoostActive; +}; + +/** @brief Helper function to return the singleton WhmManufacturer instance + * + * This is needed by the WhmManufacturer class to support TestEventTriggers + * which are called outside of any class context. This allows the WhmManufacturer + * class to return the relevant Delegate instance in which to invoke the test + * events on. + * + * This function is typically found in main.cpp or wherever the singleton is created. + */ +WhmManufacturer * GetWhmManufacturer(); + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp new file mode 100644 index 00000000000000..ee864ba6b1a6ee --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -0,0 +1,541 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::WaterHeaterManagement; + +using Protocols::InteractionModel::Status; + +WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clustersEndpoint) : + mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mBoostTargetTemperatureReached(false), + mWaterHeaterModeInstance(this, clustersEndpoint, WaterHeaterMode::Id, 0), mTankVolume(0), mEstimatedHeatRequired(0), + mTankPercentage(0), mBoostState(BoostStateEnum::kInactive) +{ + // Initialise the WaterHeaterMode instance + mWaterHeaterModeInstance.Init(); +} + +void WaterHeaterManagementDelegate::SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance) +{ + mpWhmInstance = &instance; + + if (!mpWhmInstance->HasFeature(Feature::kTankPercent)) + { + // If the feature kTankPercent is not supported then set mTankPercentage to 100% so calculations below need + // less code for the feature kTankPercent case. + mTankPercentage = 100; + } +} + +void WaterHeaterManagementDelegate::SetWhmManufacturer(WhmManufacturer & whmManufacturer) +{ + mpWhmManufacturer = &whmManufacturer; +} + +/********************************************************************************* + * + * Methods implementing the WaterHeaterManagement::Delegate interace + * + *********************************************************************************/ + +BitMask WaterHeaterManagementDelegate::GetHeaterTypes() +{ + return mHeaterTypes; +} + +BitMask WaterHeaterManagementDelegate::GetHeatDemand() +{ + return mHeatDemand; +} + +uint16_t WaterHeaterManagementDelegate::GetTankVolume() +{ + return mTankVolume; +} + +int64_t WaterHeaterManagementDelegate::GetEstimatedHeatRequired() +{ + return mEstimatedHeatRequired; +} + +Percent WaterHeaterManagementDelegate::GetTankPercentage() +{ + return mTankPercentage; +} + +BoostStateEnum WaterHeaterManagementDelegate::GetBoostState() +{ + return mBoostState; +} + +void WaterHeaterManagementDelegate::SetHeaterTypes(BitMask heaterTypes) +{ + if (mHeaterTypes != heaterTypes) + { + mHeaterTypes = heaterTypes; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::HeaterTypes::Id); + } +} + +void WaterHeaterManagementDelegate::SetHeatDemand(BitMask heatDemand) +{ + if (mHeatDemand != heatDemand) + { + mHeatDemand = heatDemand; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::HeatDemand::Id); + } +} + +void WaterHeaterManagementDelegate::SetTankVolume(uint16_t tankVolume) +{ + if (mTankVolume != tankVolume) + { + mTankVolume = tankVolume; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::TankVolume::Id); + } +} + +void WaterHeaterManagementDelegate::SetEstimatedHeatRequired(int64_t estimatedHeatRequired) +{ + if (mEstimatedHeatRequired != estimatedHeatRequired) + { + mEstimatedHeatRequired = estimatedHeatRequired; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::EstimatedHeatRequired::Id); + } +} + +void WaterHeaterManagementDelegate::SetTankPercentage(Percent tankPercentage) +{ + if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) + { + if (mTankPercentage != tankPercentage) + { + mTankPercentage = tankPercentage; + + CheckIfHeatNeedsToBeTurnedOnOrOff(); + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::TankPercentage::Id); + } + } +} + +void WaterHeaterManagementDelegate::SetBoostState(BoostStateEnum boostState) +{ + if (mBoostState != boostState) + { + mBoostState = boostState; + + MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::BoostState::Id); + } +} + +/** + * @brief Handles the boost command + * + * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the water in the tank (or + * the TargetPercentage of the water, if included) to be heated towards the set point (or the TemporarySetpoint, if + * included), which in turn may cause a call for heat, even if the mode is OFF, or is TIMED and it is during one of + * the Off periods. + */ +Status WaterHeaterManagementDelegate::HandleBoost(uint32_t durationS, Optional oneShot, Optional emergencyBoost, + Optional temporarySetpoint, Optional targetPercentage, + Optional targetReheat) +{ + Status status = Status::Success; + + ChipLogProgress(AppServer, "HandleBoost"); + + // Keep track of the boost command parameters + mBoostOneShot = oneShot; + mBoostEmergencyBoost = emergencyBoost; + mBoostTemporarySetpoint = temporarySetpoint; + mBoostTargetPercentage = targetPercentage; + mBoostTargetReheat = targetReheat; + + mBoostTargetTemperatureReached = false; + + // If a timer is running, cancel it so we can start a new boost command with the new duration + if (mBoostState == BoostStateEnum::kActive) + { + DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); + } + + CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(durationS), BoostTimerExpiry, this); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "HandleBoost: Unable to start a Boost timer: %" CHIP_ERROR_FORMAT, err.Format()); + + // Not a lot we can do -> just set the boost state to inactive + SetBoostState(BoostStateEnum::kInactive); + + return Status::Failure; + } + + // Now running a boost command + SetBoostState(BoostStateEnum::kActive); + + if (mpWhmManufacturer != nullptr) + { + status = mpWhmManufacturer->BoostCommandStarted(durationS, oneShot, emergencyBoost, temporarySetpoint, targetPercentage, + targetReheat); + } + else + { + status = Status::InvalidInState; + ChipLogError(AppServer, "HandleBoost: mpWhmManufacturer == nullptr"); + } + + if (status == Status::Success) + { + // See if the heat needs to be turned on or off as a result of this boost command + status = CheckIfHeatNeedsToBeTurnedOnOrOff(); + } + + return status; +} + +void WaterHeaterManagementDelegate::BoostTimerExpiry(System::Layer * systemLayer, void * delegate) +{ + WaterHeaterManagementDelegate * dg = reinterpret_cast(delegate); + + dg->HandleBoostTimerExpiry(); +} + +/** + * @brief Timer for handling the completion of a boost command + */ +void WaterHeaterManagementDelegate::HandleBoostTimerExpiry() +{ + ChipLogError(AppServer, "HandleBoostTimerExpiry"); + + // The PowerAdjustment is no longer in progress + SetBoostState(BoostStateEnum::kInactive); + + if (mpWhmManufacturer != nullptr) + { + mpWhmManufacturer->BoostCommandFinished(); + } + else + { + ChipLogError(AppServer, "HandleBoostTimerExpiry: mpWhmManufacturer == nullptr"); + } + + CheckIfHeatNeedsToBeTurnedOnOrOff(); +} + +/** + * @brief Cancels a boost command + * + * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode (e.g. OFF, MANUAL or TIMED). + */ +Status WaterHeaterManagementDelegate::HandleCancelBoost() +{ + Status status = Status::Success; + + ChipLogProgress(AppServer, "HandleCancelBoost"); + + if (mBoostState == BoostStateEnum::kActive) + { + SetBoostState(BoostStateEnum::kInactive); + + DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); + + if (mpWhmManufacturer != nullptr) + { + status = mpWhmManufacturer->BoostCommandCancelled(); + } + else + { + status = Status::InvalidInState; + ChipLogError(AppServer, "HandleCancelBoost: mpWhmManufacturer == nullptr"); + } + + status = CheckIfHeatNeedsToBeTurnedOnOrOff(); + } + + return status; +} + +/********************************************************************************* + * + * Methods implementing the ModeBase::Delegate interface + * + *********************************************************************************/ + +CHIP_ERROR WaterHeaterManagementDelegate::Init() +{ + return CHIP_NO_ERROR; +} + +void WaterHeaterManagementDelegate::HandleChangeToMode(uint8_t NewMode, ModeBase::Commands::ChangeToModeResponse::Type & response) +{ + response.status = to_underlying(ModeBase::StatusCode::kSuccess); +} + +CHIP_ERROR WaterHeaterManagementDelegate::GetModeLabelByIndex(uint8_t modeIndex, chip::MutableCharSpan & label) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + + return chip::CopyCharSpanToMutableCharSpan(kModeOptions[modeIndex].label, label); +} + +CHIP_ERROR WaterHeaterManagementDelegate::GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + + value = kModeOptions[modeIndex].mode; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WaterHeaterManagementDelegate::GetModeTagsByIndex(uint8_t modeIndex, DataModel::List & tags) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + + if (tags.size() < kModeOptions[modeIndex].modeTags.size()) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + std::copy(kModeOptions[modeIndex].modeTags.begin(), kModeOptions[modeIndex].modeTags.end(), tags.begin()); + tags.reduce_size(kModeOptions[modeIndex].modeTags.size()); + + return CHIP_NO_ERROR; +} + +/********************************************************************************* + * + * WaterHeaterManagementDelegate specific methods + * + *********************************************************************************/ + +void WaterHeaterManagementDelegate::SetWaterTemperature(uint16_t waterTemperature) +{ + // This method assumed 100% of the water in the tank has reached the waterTemperature specified + mHotWaterTemperature = waterTemperature; + + // Do not change mTankPercentage if the kTankPercent feature is not supported + if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) + { + mTankPercentage = 100; + } + + // See if the heat needs to be turned on or off + CheckIfHeatNeedsToBeTurnedOnOrOff(); +} + +void WaterHeaterManagementDelegate::SetTargetWaterTemperature(uint16_t targetWaterTemperature) +{ + mTargetWaterTemperature = targetWaterTemperature; + + // See if the heat needs to be turned on or off + CheckIfHeatNeedsToBeTurnedOnOrOff(); +} + +void WaterHeaterManagementDelegate::DrawOffHotWater(uint8_t percentageReplaced, uint16_t replacedWaterTemperature) +{ + // Only supported in the kTankPercent is supported. + // Replaces percentageReplaced% of the water in the tank with water of a temperature replacedWaterTemperature + if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) + { + // See if all of the water has now been replaced with replacedWaterTemperature + if (mTankPercentage >= percentageReplaced) + { + mTankPercentage -= percentageReplaced; + } + else + { + mTankPercentage = 0; + } + + mReplacedWaterTemperature = replacedWaterTemperature; + + CheckIfHeatNeedsToBeTurnedOnOrOff(); + } +} + +bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const +{ + // Determine the target temperature. If a boost command is in progress and has a mBoostTemporarySetpoint value use that as the + // target temperature + uint16_t targetTemperature = (mBoostState == BoostStateEnum::kActive && mBoostTemporarySetpoint.HasValue()) + ? static_cast(mBoostTemporarySetpoint.Value()) + : mTargetWaterTemperature; + uint8_t targetPercentage; + + if (mBoostState == BoostStateEnum::kActive && mBoostTargetTemperatureReached && mBoostTargetReheat.HasValue()) + { + // If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because the + // TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field + // indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. + // + // For example if the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% hot water, + // the tank may have hot water drawn off until only 40% hot water remains. At this point the heater will begin to heat back + // up to 80% of hot water. If this field and the OneShot field were both omitted, heating would begin again after any water + // draw which reduced the TankPercentage below 80%. + + // If this field is included then the TargetPercentage field SHALL also be included, and the OneShot excluded. + + targetPercentage = mBoostTargetReheat.Value(); + } + else + { + // Determine the target %. If a boost command is in progress and has a mBoostTargetPercentage value use that as the target + // %, otherwise 100% of the water in the tank must be at the target temperature + targetPercentage = + (mBoostState == BoostStateEnum::kActive && mBoostTargetPercentage.HasValue()) ? mBoostTargetPercentage.Value() : 100; + } + + // Return whether the water is at the target temperature + return (mTankPercentage >= targetPercentage) && (mHotWaterTemperature >= targetTemperature); +} + +Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() +{ + Status status = Status::Success; + bool turningHeatOff = false; + + if (!HasWaterTemperatureReachedTarget()) + { + uint8_t mode = mInstance->GetCurrentMode(); + + // The water in the tank is not at the target temperature. See if we heating is currently off + if (mHeatDemand.Raw() == 0) + { + // Need to track whether the water temperature has reached the target temperature for the boost + // command when a oneShot option has been applied. + if (mBoostState == BoostStateEnum::kActive) + { + mBoostTargetTemperatureReached = false; + } + + // If a boost command is in progress or in manual mode, find a heating source and "turn it on". + if (mBoostState == BoostStateEnum::kActive || mode == ModeManual) + { + if (mpWhmManufacturer != nullptr) + { + // Find out from the manufacturer object the heating sources to use. + BitMask heaterDemand = mpWhmManufacturer->DetermineHeatingSources(); + + SetHeatDemand(heaterDemand); + + // And turn the heating of the water tank on. + status = mpWhmManufacturer->TurnHeatingOn(); + } + else + { + status = Status::InvalidInState; + ChipLogError(AppServer, "CheckIfHeatNeedsToBeTurnedOnOrOff: Failed as mpWhmManufacturer == nullptr"); + } + } + } + else if (mBoostState == BoostStateEnum::kInactive && mode == ModeOff) + { + // The water temperature is not at the target temperature but there is no boost command in progress and the mode is Off + // so need to ensure the heating is turned off. + ChipLogError(AppServer, "CheckIfHeatNeedsToBeTurnedOnOrOff turning heating off due to mode"); + + SetHeatDemand(BitMask(0)); + + turningHeatOff = true; + } + } + else if (mHeatDemand.Raw() != 0) + { + // The water in the tank has reached the target temperature - need to turn the heating off + SetHeatDemand(BitMask(0)); + + turningHeatOff = true; + + // If a boost command is in progress, record that the target temperature has been reached. + mBoostTargetTemperatureReached = (mBoostState == BoostStateEnum::kActive); + } + + if (turningHeatOff) + { + // If running a boost command with the oneShot parameter and turning heat off, then must have + // reached the boost command target temperature -> that's the boost command complete. + if (mBoostState == BoostStateEnum::kActive && mBoostOneShot.HasValue() && mBoostOneShot.Value()) + { + SetBoostState(BoostStateEnum::kInactive); + + DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); + + if (mpWhmManufacturer != nullptr) + { + status = mpWhmManufacturer->BoostCommandCancelled(); + } + else + { + status = Status::InvalidInState; + ChipLogError(AppServer, "CheckIfHeatNeedsToBeTurnedOnOrOff: mpWhmManufacturer == nullptr"); + } + } + + // Turn the heating off + if (mpWhmManufacturer != nullptr) + { + status = mpWhmManufacturer->TurnHeatingOff(); + } + else + { + status = Status::InvalidInState; + ChipLogError(AppServer, + "CheckIfHeatNeedsToBeTurnedOnOrOff: Failed to turn the heating off as mpWhmManufacturer == nullptr"); + } + } + + return status; +} + +void WaterHeaterManagementDelegate::SetWaterHeaterMode(uint8_t modeValue) +{ + if (!mInstance->IsSupportedMode(modeValue)) + { + ChipLogError(AppServer, "SetWaterHeaterMode bad mode"); + return; + } + + Status status = mInstance->UpdateCurrentMode(modeValue); + if (status != Status::Success) + { + ChipLogError(AppServer, "SetWaterHeaterMode updateMode failed"); + return; + } + + CheckIfHeatNeedsToBeTurnedOnOrOff(); +} diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp new file mode 100644 index 00000000000000..9d4ad58fefd976 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp @@ -0,0 +1,34 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::WaterHeaterManagement; + +CHIP_ERROR WaterHeaterManagementInstance::Init() +{ + ChipLogDetail(AppServer, "WaterHeaterManagementInstance::Init()"); + return Instance::Init(); +} + +void WaterHeaterManagementInstance::Shutdown() +{ + Instance::Shutdown(); +} diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp new file mode 100644 index 00000000000000..4b20b04886981d --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp @@ -0,0 +1,196 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +static constexpr int WHM_ENDPOINT = 1; + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +static std::unique_ptr gWhmDelegate; +static std::unique_ptr gWhmInstance; + +static std::unique_ptr gWhmManufacturer; + +WhmManufacturer * GetWhmManufacturer() +{ + return gWhmManufacturer.get(); +} + +/* + * @brief Creates a Delegate and Instance for Water Heater Management cluster + * + * The Instance is a container around the Delegate, so + * create the Delegate first, then wrap it in the Instance + * Then call the Instance->Init() to register the attribute and command handlers + */ +CHIP_ERROR WhmInit() +{ + CHIP_ERROR err; + + if (gWhmDelegate || gWhmInstance) + { + ChipLogError(AppServer, "WaterHeaterManager Instance or Delegate already exist."); + return CHIP_ERROR_INCORRECT_STATE; + } + + gWhmDelegate = std::make_unique(WHM_ENDPOINT); + if (!gWhmDelegate) + { + ChipLogError(AppServer, "Failed to allocate memory for WaterHeaterManagementDelegate"); + return CHIP_ERROR_NO_MEMORY; + } + + /* Manufacturer may optionally not support all features, commands & attributes */ + gWhmInstance = + std::make_unique(EndpointId(WHM_ENDPOINT), *gWhmDelegate, BitMask(Feature::kEnergyManagement, Feature::kTankPercent)); //GetFeatureMapFromCmdLine()); + + if (!gWhmInstance) + { + ChipLogError(AppServer, "Failed to allocate memory for WaterHeaterManagementInstance"); + gWhmDelegate.reset(); + return CHIP_ERROR_NO_MEMORY; + } + + /* Register Attribute & Command handlers */ + err = gWhmInstance->Init(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "gWhmInstance->Init failed %s", chip::ErrorStr(err)); + gWhmInstance.reset(); + gWhmDelegate.reset(); + return err; + } + + gWhmDelegate->SetWaterHeaterManagementInstance(*gWhmInstance); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WhmShutdown() +{ + /* Do this in the order Instance first, then delegate + * Ensure we call the Instance->Shutdown to free attribute & command handlers first + */ + if (gWhmInstance) + { + /* Deregister attribute & command handlers */ + gWhmInstance->Shutdown(); + gWhmInstance.reset(); + } + + if (gWhmDelegate) + { + gWhmDelegate.reset(); + } + + return CHIP_NO_ERROR; +} + +/* + * @brief Creates a WhmManufacturer class to hold the Whm cluster + * + * The Instance is a container around the Delegate, so + * create the Delegate first, then wrap it in the Instance + * Then call the Instance->Init() to register the attribute and command handlers + */ +CHIP_ERROR WhmManufacturerInit() +{ + CHIP_ERROR err; + + if (gWhmManufacturer) + { + ChipLogError(AppServer, "WhmManufacturer already exist."); + return CHIP_ERROR_INCORRECT_STATE; + } + + /* Now create WhmManufacturer */ + gWhmManufacturer = std::make_unique(gWhmInstance.get()); + if (!gWhmManufacturer) + { + ChipLogError(AppServer, "Failed to allocate memory for WhmManufacturer"); + return CHIP_ERROR_NO_MEMORY; + } + + /* Call Manufacturer specific init */ + err = gWhmManufacturer->Init(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Init failed on gWhmManufacturer"); + gWhmManufacturer.reset(); + return err; + } + + // Let the WhmDelegate know about the WhmManufacturer object. + gWhmDelegate->SetWhmManufacturer(*gWhmManufacturer); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WhmManufacturerShutdown() +{ + if (gWhmManufacturer) + { + /* Shutdown the WhmManufacturer */ + gWhmManufacturer->Shutdown(); + gWhmManufacturer.reset(); + } + + return CHIP_NO_ERROR; +} + +void WhmApplicationInit() +{ + if (WhmInit() != CHIP_NO_ERROR) + { + return; + } + + /* Do this last so that the instances for other clusters can be wrapped inside */ + if (WhmManufacturerInit() != CHIP_NO_ERROR) + { + WhmShutdown(); + return; + } +} + +void WhmApplicationShutdown() +{ + /* Shutdown in reverse order that they were created */ + WhmManufacturerShutdown(); +} + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp new file mode 100644 index 00000000000000..f9ffe87c466d20 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -0,0 +1,270 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include + +using namespace chip; + +using Protocols::InteractionModel::Status; + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +CHIP_ERROR WhmManufacturer::Init() +{ + WaterHeaterManagementDelegate * dg = GetWhmManufacturer()->GetWhmDelegate(); + if (dg == nullptr) + { + ChipLogError(AppServer, "WhmDelegate is not initialized"); + return CHIP_ERROR_UNINITIALIZED; + } + + mBoostActive = false; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WhmManufacturer::Shutdown() +{ + return CHIP_NO_ERROR; +} + +BitMask WhmManufacturer::DetermineHeatingSources() +{ + WaterHeaterManagementDelegate * dg = GetWhmManufacturer()->GetWhmDelegate(); + if (dg == nullptr) + { + ChipLogError(AppServer, "WhmDelegate is not initialized"); + return BitMask(0); + } + + // A list of valid heaterTypes + uint8_t waterHeaterTypeValues[] = { + static_cast(WaterHeaterTypeBitmap::kImmersionElement1), + static_cast(WaterHeaterTypeBitmap::kImmersionElement2), + static_cast(WaterHeaterTypeBitmap::kHeatPump), + static_cast(WaterHeaterTypeBitmap::kBoiler), + static_cast(WaterHeaterTypeBitmap::kOther), + }; + + // The corresponding list of valid headerDemands + uint8_t waterHeaterDemandValues[] = { + static_cast(WaterHeaterTypeBitmap::kImmersionElement1), + static_cast(WaterHeaterTypeBitmap::kImmersionElement2), + static_cast(WaterHeaterTypeBitmap::kHeatPump), + static_cast(WaterHeaterTypeBitmap::kBoiler), + static_cast(WaterHeaterTypeBitmap::kOther), + }; + + // Iterate across the valid waterHeaterTypes seeing which heating sources are available based on heaterTypes. + // Set the corresponding bit in the heaterDemand bitmap. + BitMask heaterTypes = dg->GetHeaterTypes(); + + uint8_t heaterDemandMask = 0; + for (uint8_t idx = 0; idx < sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0]); idx++) + { + // Is this heating source being used? + if (heaterTypes.Raw() & waterHeaterTypeValues[idx]) + { + heaterDemandMask |= waterHeaterDemandValues[idx]; + } + } + + return BitMask(heaterDemandMask); +} + +Status WhmManufacturer::TurnHeatingOn() +{ + Status status = Status::Success; + + ChipLogProgress(AppServer, "WhmManufacturer::TurnHeatingOn"); + + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + if (dg->GetBoostState() == BoostStateEnum::kActive) + { + mBoostActive = true; + } + + return status; +} + +Status WhmManufacturer::TurnHeatingOff() +{ + Status status = Status::Success; + + ChipLogProgress(AppServer, "WhmManufacturer::TurnHeatingOff"); + + if (mBoostActive) + { + mBoostActive = false; + } + + return status; +} + +Status WhmManufacturer::BoostCommandStarted(uint32_t duration, Optional oneShot, Optional emergencyBoost, + Optional temporarySetpoint, Optional targetPercentage, + Optional targetReheat) +{ + return Status::Success; +} + +Status WhmManufacturer::BoostCommandCancelled() +{ + return Status::Success; +} + +void WhmManufacturer::BoostCommandFinished() {} + +WaterHeaterManagementDelegate * GetWhmDelegate() +{ + WhmManufacturer * mn = GetWhmManufacturer(); + VerifyOrDieWithMsg(mn != nullptr, AppServer, "WhmManufacturer is null"); + + WaterHeaterManagementDelegate * wg = mn->GetWhmDelegate(); + VerifyOrDieWithMsg(wg != nullptr, AppServer, "WhmDelegate is null"); + + return wg; +} + +void SetTestEventTrigger_BasicInstallationTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate installation in a 100L tank full of water at 20C, with a target temperature of 60C, in OFF mode + dg->SetTankVolume(100); + dg->SetTargetWaterTemperature(6000); + dg->SetHeaterTypes(BitMask(WaterHeaterTypeBitmap::kImmersionElement1)); + dg->DrawOffHotWater(100, 2000); +} + +void SetTestEventTrigger_BasicInstallationTestEventClear() {} + +void SetTestEventTrigger_WaterTemperature20CTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate 100% of the water in the tank being at 20C + dg->SetWaterTemperature(2000); +} + +void SetTestEventTrigger_WaterTemperature61CTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate 100% of the water in the tank being at 61C + dg->SetWaterTemperature(6100); +} + +void SetTestEventTrigger_WaterTemperature66CTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate 100% of the water in the tank being at 66C + dg->SetWaterTemperature(6600); +} + +void SetTestEventTrigger_ManualModeTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate the Water Heater Mode being set to MANUAL + dg->SetWaterHeaterMode(WaterHeaterManagementDelegate::ModeManual); +} + +void SetTestEventTrigger_OffModeTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate the Water Heater Mode being set to OFF + dg->SetWaterHeaterMode(WaterHeaterManagementDelegate::ModeOff); +} + +void SetTestEventTrigger_DrawOffHotWaterTestEvent() +{ + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + // Simulate drawing off 25% of the tank volume of hot water, replaced with water at 20C + dg->DrawOffHotWater(25, 2000); +} + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip + +using namespace chip::app::Clusters::WaterHeaterManagement; + +bool HandleWaterHeaterManagementTestEventTrigger(uint64_t eventTrigger) +{ + WaterHeaterManagementTrigger trigger = static_cast(eventTrigger); + + switch (trigger) + { + case WaterHeaterManagementTrigger::kBasicInstallationTestEvent: + ChipLogProgress(Support, + "[Whm::kBasicInstallationTestEvent] => Simulate installation in a 100L tank full of water at 20C, with a " + "target temperature of 60C, in OFF mode"); + SetTestEventTrigger_BasicInstallationTestEvent(); + break; + case WaterHeaterManagementTrigger::kBasicInstallationTestEventClear: + ChipLogProgress(Support, "[Whm::kBasicInstallationTestEventClear] => End simulation of installation"); + SetTestEventTrigger_BasicInstallationTestEventClear(); + break; + case WaterHeaterManagementTrigger::kWaterTemperature20CTestEvent: + ChipLogProgress(Support, "[Whm::kWaterTemperature20CTestEvent] => Simulate 100%% of the water in the tank being at 20C"); + SetTestEventTrigger_WaterTemperature20CTestEvent(); + break; + case WaterHeaterManagementTrigger::kWaterTemperature61CTestEvent: + ChipLogProgress(Support, "[Whm::kWaterTemperature61CTestEvent] => Simulate 100%% of the water in the tank being at 61C"); + SetTestEventTrigger_WaterTemperature61CTestEvent(); + break; + case WaterHeaterManagementTrigger::kWaterTemperature66CTestEvent: + ChipLogProgress(Support, "[Whm::kWaterTemperature66CTestEvent] => Simulate 100%% of the water in the tank being at 66C"); + SetTestEventTrigger_WaterTemperature66CTestEvent(); + break; + case WaterHeaterManagementTrigger::kManualModeTestEvent: + ChipLogProgress(Support, "[Whm::kManualModeTestEvent] => Simulate the Water Heater Mode being set to MANUAL"); + SetTestEventTrigger_ManualModeTestEvent(); + break; + case WaterHeaterManagementTrigger::kOffModeTestEvent: + ChipLogProgress(Support, "[Whm::kOffModeTestEvent] => Simulate the Water Heater Mode being set to OFF"); + SetTestEventTrigger_OffModeTestEvent(); + break; + case WaterHeaterManagementTrigger::kDrawOffHotWaterTestEvent: + ChipLogProgress(Support, + "[Whm::kDrawOffHotWaterTestEvent] => Simulate drawing off 25%% of the tank volume of hot water, replaced " + "with water at 20C"); + SetTestEventTrigger_DrawOffHotWaterTestEvent(); + break; + default: + return false; + } + + return true; +} + diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 3d52ef748de90d..0d91c277b74300 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -65,6 +65,12 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp", + + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp", + "AllClustersCommandDelegate.cpp", "AppOptions.cpp", "ValveControlDelegate.cpp", diff --git a/examples/all-clusters-app/linux/args.gni b/examples/all-clusters-app/linux/args.gni index d414ad5dedaf5d..92d01ea3358b30 100644 --- a/examples/all-clusters-app/linux/args.gni +++ b/examples/all-clusters-app/linux/args.gni @@ -29,3 +29,4 @@ matter_log_json_payload_decode_full = true matter_log_json_payload_hex = true chip_enable_smoke_co_trigger = true chip_enable_boolean_state_configuration_trigger = true +chip_enable_water_heater_management_trigger = true diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp index 73af031ecbdb5d..83a019f8bc98e8 100644 --- a/examples/all-clusters-app/linux/main-common.cpp +++ b/examples/all-clusters-app/linux/main-common.cpp @@ -62,6 +62,8 @@ #include +#include + using namespace chip; using namespace chip::app; using namespace chip::DeviceLayer; @@ -234,6 +236,8 @@ void ApplicationInit() Clusters::ValveConfigurationAndControl::SetDefaultDelegate(chip::EndpointId(1), &sValveDelegate); Clusters::TimeSynchronization::SetDefaultDelegate(&sTimeSyncDelegate); + Clusters::WaterHeaterManagement::WhmApplicationInit(); + SetTagList(/* endpoint= */ 0, Span(gEp0TagList)); SetTagList(/* endpoint= */ 1, Span(gEp1TagList)); SetTagList(/* endpoint= */ 2, Span(gEp2TagList)); @@ -260,6 +264,8 @@ void ApplicationShutdown() Clusters::DeviceEnergyManagementMode::Shutdown(); Clusters::EnergyEvseMode::Shutdown(); + Clusters::WaterHeaterManagement::WhmApplicationShutdown(); + if (sChipNamedPipeCommands.Stop() != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "Failed to stop CHIP NamedPipeCommands"); diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index b92d76e613e618..5fdddec40ea092 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -90,6 +90,9 @@ #if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER #include #endif +#if CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER +#include +#endif #include #include @@ -553,6 +556,10 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl) static EnergyReportingTestEventTriggerHandler sEnergyReportingTestEventTriggerHandler; sTestEventTriggerDelegate.AddHandler(&sEnergyReportingTestEventTriggerHandler); #endif +#if CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER + static WaterHeaterManagementTestEventTriggerHandler sWaterHeaterManagementTestEventTriggerHandler; + sTestEventTriggerDelegate.AddHandler(&sWaterHeaterManagementTestEventTriggerHandler); +#endif initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate; diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index 4641eae6446e04..5729d21d211d69 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -24,6 +24,7 @@ declare_args() { chip_enable_boolean_state_configuration_trigger = false chip_enable_energy_evse_trigger = false chip_enable_energy_reporting_trigger = false + chip_enable_water_heater_management_trigger = false } config("app-main-config") { @@ -52,6 +53,10 @@ source_set("energy-reporting-test-event-trigger") { sources = [ "${chip_root}/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h" ] } +source_set("water-heater-management-test-event-trigger") { + sources = [ "${chip_root}/src/app/clusters/water-heater-management-server/WaterHeaterManagementTestEventTriggerHandler.h" ] +} + source_set("app-main") { defines = [ "ENABLE_TRACING=${matter_enable_tracing_support}" ] sources = [ @@ -78,6 +83,7 @@ source_set("app-main") { ":energy-evse-test-event-trigger", ":energy-reporting-test-event-trigger", ":smco-test-event-trigger", + ":water-heater-management-test-event-trigger", "${chip_root}/src/lib", "${chip_root}/src/platform/logging:stdio", ] @@ -117,6 +123,7 @@ source_set("app-main") { "CHIP_DEVICE_CONFIG_ENABLE_BOOLEAN_STATE_CONFIGURATION_TRIGGER=${chip_enable_boolean_state_configuration_trigger}", "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER=${chip_enable_energy_evse_trigger}", "CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER=${chip_enable_energy_reporting_trigger}", + "CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER=${chip_enable_water_heater_management_trigger}", ] public_configs = [ ":app-main-config" ] diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 90b5bd1cfec3f3..f1fa43ca604c1c 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -392,6 +392,12 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/thread-network-diagnostics-provider.cpp", "${_app_root}/clusters/${cluster}/thread-network-diagnostics-provider.h", ] + } else if (cluster == "water-heater-management-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/${cluster}.h", + "${_app_root}/clusters/${cluster}/WaterHeaterManagementTestEventTriggerHandler.h", + ] } else if (cluster == "thread-network-directory-server") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", diff --git a/src/app/clusters/water-heater-management-server/WaterHeaterManagementTestEventTriggerHandler.h b/src/app/clusters/water-heater-management-server/WaterHeaterManagementTestEventTriggerHandler.h new file mode 100644 index 00000000000000..54b07cde0c2158 --- /dev/null +++ b/src/app/clusters/water-heater-management-server/WaterHeaterManagementTestEventTriggerHandler.h @@ -0,0 +1,86 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +/** + * @brief User handler for handling the test event trigger + * + * @note If TestEventTrigger is enabled, it needs to be implemented in the app + * + * @param eventTrigger Event trigger to handle + * + * @retval true on success + * @retval false if error happened + */ +bool HandleWaterHeaterManagementTestEventTrigger(uint64_t eventTrigger); + +namespace chip { + +/* + * These Test EventTrigger values can be used to produce artificial water heater configuration + * and water temperatures. + * + * They are sent along with the enableKey (manufacturer defined secret) + * in the General Diagnostic cluster TestEventTrigger command + */ +enum class WaterHeaterManagementTrigger : uint64_t +{ + // Simulate installation in a 100L tank full of water at 20C, with a target temperature of 60C, in OFF mode + kBasicInstallationTestEvent = 0x0094'0000'0000'0000, + + // End simulation of installation + kBasicInstallationTestEventClear = 0x0094'0000'0000'0001, + + // Simulate 100% of the water in the tank being at 20C + kWaterTemperature20CTestEvent = 0x0094'0000'0000'0002, + + // Simulate 100% of the water in the tank being at 61C + kWaterTemperature61CTestEvent = 0x0094'0000'0000'0003, + + // Simulate 100% of the water in the tank being at 66C + kWaterTemperature66CTestEvent = 0x0094'0000'0000'0004, + + // Simulate the Water Heater Mode being set to MANUAL + kManualModeTestEvent = 0x0094'0000'0000'0005, + + // Simulate the Water Heater Mode being set to OFF + kOffModeTestEvent = 0x0094'0000'0000'0006, + + // Simulate drawing off 25% of the tank volume of hot water, replaced with water at 20C + kDrawOffHotWaterTestEvent = 0x0094'0000'0000'0007, +}; + +class WaterHeaterManagementTestEventTriggerHandler : public TestEventTriggerHandler +{ +public: + WaterHeaterManagementTestEventTriggerHandler() {} + + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override + { + if (HandleWaterHeaterManagementTestEventTrigger(eventTrigger)) + { + return CHIP_NO_ERROR; + } + return CHIP_ERROR_INVALID_ARGUMENT; + } +}; + +} // namespace chip diff --git a/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp b/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp new file mode 100644 index 00000000000000..447a6646766452 --- /dev/null +++ b/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "water-heater-management-server.h" + +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::WaterHeaterManagement; +using namespace chip::app::Clusters::WaterHeaterManagement::Attributes; + +using chip::Protocols::InteractionModel::Status; + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +constexpr uint16_t kClusterRevision = 1; + +CHIP_ERROR Instance::Init() +{ + ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->RegisterCommandHandler(this)); + VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); + + return CHIP_NO_ERROR; +} + +void Instance::Shutdown() +{ + InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this); + unregisterAttributeAccessOverride(this); +} + +bool Instance::HasFeature(Feature aFeature) const +{ + return mFeature.Has(aFeature); +} + +// AttributeAccessInterface +CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + switch (aPath.mAttributeId) + { + case HeaterTypes::Id: + return aEncoder.Encode(mDelegate.GetHeaterTypes()); + case HeatDemand::Id: + return aEncoder.Encode(mDelegate.GetHeatDemand()); + case TankVolume::Id: + if (!HasFeature(Feature::kEnergyManagement)) + { + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } + return aEncoder.Encode(mDelegate.GetTankVolume()); + case EstimatedHeatRequired::Id: + if (!HasFeature(Feature::kEnergyManagement)) + { + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } + return aEncoder.Encode(mDelegate.GetEstimatedHeatRequired()); + case TankPercentage::Id: + if (!HasFeature(Feature::kTankPercent)) + { + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } + return aEncoder.Encode(mDelegate.GetTankPercentage()); + case BoostState::Id: + return aEncoder.Encode(mDelegate.GetBoostState()); + + /* FeatureMap - is held locally */ + case FeatureMap::Id: + return aEncoder.Encode(mFeature); + case ClusterRevision::Id: + return aEncoder.Encode(kClusterRevision); + } + + /* Allow all other unhandled attributes to fall through to Ember */ + return CHIP_NO_ERROR; +} + +void Instance::InvokeCommand(HandlerContext & handlerContext) +{ + using namespace Commands; + + switch (handlerContext.mRequestPath.mCommandId) + { + case Boost::Id: + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleBoost(ctx, commandData); }); + return; + case CancelBoost::Id: + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleCancelBoost(ctx, commandData); }); + return; + } +} + +void Instance::HandleBoost(HandlerContext & ctx, const Commands::Boost::DecodableType & commandData) +{ + uint32_t duration = commandData.duration; + Optional oneShot = commandData.oneShot; + Optional emergencyBoost = commandData.emergencyBoost; + Optional temporarySetpoint = commandData.temporarySetpoint; + Optional targetPercentage = commandData.targetPercentage; + Optional targetReheat = commandData.targetReheat; + + // Notify the appliance if the appliance hardware cannot be adjusted, then return Failure + if (HasFeature(WaterHeaterManagement::Feature::kTankPercent)) + { + if (targetPercentage.HasValue()) + { + if (targetPercentage.Value() > 100) + { + ChipLogError(Zcl, "Bad targetPercentage %u", targetPercentage.Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + return; + } + } + + if (targetReheat.HasValue()) + { + if (targetReheat.Value() > 100) + { + ChipLogError(Zcl, "Bad targetReheat %u", targetReheat.Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + return; + } + + if (!targetPercentage.HasValue()) + { + ChipLogError(Zcl, "targetPercentage must be specified if targetReheat specified"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + return; + } + + if (oneShot.HasValue()) + { + ChipLogError(Zcl, "Cannot specify targetReheat with targetPercentage and oneShot. oneShot must be excluded"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + return; + } + } + } + else if (targetPercentage.HasValue() || targetReheat.HasValue()) + { + ChipLogError(Zcl, "Cannot specify targetPercentage or targetReheat if the feature TankPercent is not supported"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + return; + } + + Status status = mDelegate.HandleBoost(duration, oneShot, emergencyBoost, temporarySetpoint, targetPercentage, targetReheat); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + if (status != Status::Success) + { + ChipLogError(Zcl, "WHM: Boost command failed. status " ChipLogFormatIMStatus, ChipLogValueIMStatus(status)); + } +} + +void Instance::HandleCancelBoost(HandlerContext & ctx, const Commands::CancelBoost::DecodableType & commandData) +{ + Status status = mDelegate.HandleCancelBoost(); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + if (status != Status::Success) + { + ChipLogError(Zcl, "WHM: CancelBoost command failed. status " ChipLogFormatIMStatus, ChipLogValueIMStatus(status)); + return; + } +} + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/water-heater-management-server/water-heater-management-server.h b/src/app/clusters/water-heater-management-server/water-heater-management-server.h new file mode 100644 index 00000000000000..a0a48ab900c200 --- /dev/null +++ b/src/app/clusters/water-heater-management-server/water-heater-management-server.h @@ -0,0 +1,132 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace WaterHeaterManagement { + +class Delegate +{ +public: + Delegate() = default; + virtual ~Delegate() = default; + + void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; } + + /** + * @brief Delegate should implement a handler to start boosting the water temperature as required. + * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the water in the + * tank (or the TargetPercentage of the water, if included) to be heated towards the set point (or the + * TemporarySetpoint, if included), which in turn may cause a call for heat, even if the mode is OFF, or + * is TIMED and it is during one of the Off periods. + * + * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts + * to the previous mode (e.g. OFF, MANUAL or TIMED). + * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the + * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if + * specified). + * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause + * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). + * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be + * used instead of the normal set point temperature whilst the BOOST state is active. + * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be + * heated by this Boost command before the heater is switched off. + * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because + * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if + * included), this field indicates the percentage to which the hot water in the tank SHALL be allowed to + * fall before again beginning to reheat it. + * + * @return Success if the boost command is accepted; otherwise the command SHALL be rejected with appropriate error. + */ + virtual Protocols::InteractionModel::Status HandleBoost(uint32_t duration, Optional oneShot, + Optional emergencyBoost, Optional temporarySetpoint, + Optional targetPercentage, Optional targetReheat) = 0; + + /** + * @brief Delegate should implement a handler to cancel a boost command. + * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode (e.g. OFF, + * MANUAL or TIMED). + * + * @return It should report SUCCESS if successful and FAILURE otherwise. + */ + virtual Protocols::InteractionModel::Status HandleCancelBoost() = 0; + + // ------------------------------------------------------------------ + // Get attribute methods + virtual BitMask GetHeaterTypes() = 0; + virtual BitMask GetHeatDemand() = 0; + virtual uint16_t GetTankVolume() = 0; + virtual int64_t GetEstimatedHeatRequired() = 0; + virtual Percent GetTankPercentage() = 0; + virtual BoostStateEnum GetBoostState() = 0; + +protected: + EndpointId mEndpointId = 0; +}; + +class Instance : public AttributeAccessInterface, public CommandHandlerInterface +{ +public: + Instance(EndpointId aEndpointId, Delegate & aDelegate, Feature aFeature) : + AttributeAccessInterface(MakeOptional(aEndpointId), Id), CommandHandlerInterface(MakeOptional(aEndpointId), Id), + mDelegate(aDelegate), mFeature(aFeature) + { + /* set the base class delegates endpointId */ + mDelegate.SetEndpointId(aEndpointId); + } + + ~Instance() { Shutdown(); } + + CHIP_ERROR Init(); + void Shutdown(); + + bool HasFeature(Feature aFeature) const; + +private: + Delegate & mDelegate; + BitMask mFeature; + + // AttributeAccessInterface + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + // NOTE there are no writable attributes + + // CommandHandlerInterface + void InvokeCommand(HandlerContext & handlerContext) override; + + void HandleBoost(HandlerContext & ctx, const Commands::Boost::DecodableType & commandData); + void HandleCancelBoost(HandlerContext & ctx, const Commands::CancelBoost::DecodableType & commandData); +}; + +} // namespace WaterHeaterManagement +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index fd060149c8ff20..43f063a6098288 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -13,6 +13,7 @@ EnumsNotUsedAsTypeInXML: - "RvcOperationalState::OperationalStateEnum" - "RvcOperationalState::ErrorStateEnum" - "EnergyEvseMode::ModeTag" + - "WaterHeaterMode::ModeTag" - "DeviceEnergyManagementMode::ModeTag" CommandHandlerInterfaceOnlyClusters: @@ -42,6 +43,8 @@ CommandHandlerInterfaceOnlyClusters: - Electrical Energy Measurement - Wi-Fi Network Management - Thread Network Directory + - Water Heater Management + - Water Heater Mode # We need a more configurable way of deciding which clusters have which init functions.... # See https://github.com/project-chip/connectedhomeip/issues/4369 diff --git a/src/app/util/util.cpp b/src/app/util/util.cpp index b485e7c5dfb4cb..f903b5b52ea8da 100644 --- a/src/app/util/util.cpp +++ b/src/app/util/util.cpp @@ -139,6 +139,8 @@ void MatterEnergyEvseModePluginServerInitCallback() {} void MatterPowerTopologyPluginServerInitCallback() {} void MatterElectricalEnergyMeasurementPluginServerInitCallback() {} void MatterElectricalPowerMeasurementPluginServerInitCallback() {} +void MatterWaterHeaterManagementPluginServerInitCallback() {} +void MatterWaterHeaterModePluginServerInitCallback() {} bool emberAfContainsAttribute(chip::EndpointId endpoint, chip::ClusterId clusterId, chip::AttributeId attributeId) { diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index 1a76205be885f9..9bfc8e90397254 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -644,6 +644,16 @@ "Power Topology": ["FeatureMap"], "Valve Configuration and Control": ["RemainingDuration"], "Boolean State Configuration": ["CurrentSensitivityLevel"], + "Water Heater Management": [ + "HeaterTypes", + "HeatDemand", + "TankVolume", + "EstimatedHeatRequired", + "TankPercentage", + "BoostState", + "FeatureMap", + "ClusterRevision" + ], "Water Heater Mode": ["SupportedModes", "CurrentMode", "FeatureMap"], "Wi-Fi Network Management": ["SSID"], "Thread Network Directory": [ diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index 5388a12156601a..260d18e6d36a69 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -642,6 +642,16 @@ "Power Topology": ["FeatureMap"], "Valve Configuration and Control": ["RemainingDuration"], "Boolean State Configuration": ["CurrentSensitivityLevel"], + "Water Heater Management": [ + "HeaterTypes", + "HeatDemand", + "TankVolume", + "EstimatedHeatRequired", + "TankPercentage", + "BoostState", + "FeatureMap", + "ClusterRevision" + ], "Water Heater Mode": ["SupportedModes", "CurrentMode", "FeatureMap"], "Wi-Fi Network Management": ["SSID"], "Thread Network Directory": [ diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index 00bcb55adb90a7..29449329c9c27a 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -131,6 +131,8 @@ "WAKE_ON_LAN_CLUSTER": [], "LAUNDRY_WASHER_CONTROLS_CLUSTER": [], "LAUNDRY_DRYER_CONTROLS_CLUSTER": [], + "WATER_HEATER_MANAGEMENT_CLUSTER": [], + "WATER_HEATER_MODE_CLUSTER": [], "WIFI_NETWORK_DIAGNOSTICS_CLUSTER": [], "WINDOW_COVERING_CLUSTER": [], "ZLL_COMMISSIONING_CLUSTER": [] @@ -314,6 +316,8 @@ "WIFI_NETWORK_DIAGNOSTICS_CLUSTER": ["wifi-network-diagnostics-server"], "WIFI_NETWORK_MANAGEMENT_CLUSTER": ["wifi-network-management-server"], "WINDOW_COVERING_CLUSTER": ["window-covering-server"], + "WATER_HEATER_MANAGEMENT_CLUSTER": ["water-heater-management-server"], + "WATER_HEATER_MODE_CLUSTER": ["mode-base-server"], "ZLL_COMMISSIONING_CLUSTER": [] } } diff --git a/src/controller/data_model/controller-clusters.zap b/src/controller/data_model/controller-clusters.zap index 97d960bb7f4d30..bbb0a0ed22b814 100644 --- a/src/controller/data_model/controller-clusters.zap +++ b/src/controller/data_model/controller-clusters.zap @@ -3189,6 +3189,67 @@ } ] }, + { + "name": "Water Heater Management", + "code": 148, + "mfgCode": null, + "define": "WATER_HEATER_MANAGEMENT_CLUSTER", + "side": "client", + "enabled": 1, + "apiMaturity": "provisional", + "commands": [ + { + "name": "Boost", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "CancelBoost", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "client", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "client", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Device Energy Management", "code": 152, diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 52e2be80aa74f5..31a0c9b81d25ff 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -26838,11 +26838,11 @@ class ModeTag(MatterIntEnum): kOff = 0x4000 kManual = 0x4001 kTimed = 0x4002 - # 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 = 0, + # kUnknownEnumValue intentionally not defined. This enum never goes + # through DataModel::Decode, likely because it is a part of a derived + # cluster. As a result having kUnknownEnumValue in this enum is error + # prone, and was removed. See + # src/app/common/templates/config-data.yaml. class Bitmaps: class Feature(IntFlag): 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 0e519739bea690..02725b6e9ddf26 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 @@ -15002,392 +15002,7 @@ Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t valu } // namespace ElectricalEnergyMeasurement namespace WaterHeaterManagement { -namespace Attributes { - -namespace HeaterTypes { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, - chip::BitMask * value) -{ - using Traits = NumericAttributeTraits>; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::BitMask value, - MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits>; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_BITMAP8_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::BitMask value) -{ - using Traits = NumericAttributeTraits>; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_BITMAP8_ATTRIBUTE_TYPE); -} - -} // namespace HeaterTypes - -namespace HeatDemand { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, - chip::BitMask * value) -{ - using Traits = NumericAttributeTraits>; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::BitMask value, - MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits>; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_BITMAP8_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::BitMask value) -{ - using Traits = NumericAttributeTraits>; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_BITMAP8_ATTRIBUTE_TYPE); -} - -} // namespace HeatDemand - -namespace TankVolume { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_INT16U_ATTRIBUTE_TYPE, markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_INT16U_ATTRIBUTE_TYPE); -} - -} // namespace TankVolume - -namespace EstimatedHeatRequired { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, int64_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, int64_t value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_ENERGY_MWH_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, int64_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_ENERGY_MWH_ATTRIBUTE_TYPE); -} - -} // namespace EstimatedHeatRequired - -namespace TankPercentage { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, chip::Percent * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::Percent value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_PERCENT_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::Percent value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_PERCENT_ATTRIBUTE_TYPE); -} - -} // namespace TankPercentage - -namespace BoostState { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, - chip::app::Clusters::WaterHeaterManagement::BoostStateEnum * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::app::Clusters::WaterHeaterManagement::BoostStateEnum value, - MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_ENUM8_ATTRIBUTE_TYPE, markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::app::Clusters::WaterHeaterManagement::BoostStateEnum value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_ENUM8_ATTRIBUTE_TYPE); -} - -} // namespace BoostState - -namespace FeatureMap { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_BITMAP32_ATTRIBUTE_TYPE, - markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_BITMAP32_ATTRIBUTE_TYPE); -} - -} // namespace FeatureMap - -namespace ClusterRevision { - -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - Protocols::InteractionModel::Status status = - emberAfReadAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(Protocols::InteractionModel::Status::Success == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - *value = Traits::StorageToWorking(temp); - return status; -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value, MarkAttributeDirty markDirty) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_INT16U_ATTRIBUTE_TYPE, markDirty); -} - -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return Protocols::InteractionModel::Status::ConstraintError; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::WaterHeaterManagement::Id, Id, writable, ZCL_INT16U_ATTRIBUTE_TYPE); -} - -} // namespace ClusterRevision - -} // namespace Attributes +namespace Attributes {} // namespace Attributes } // namespace WaterHeaterManagement namespace DemandResponseLoadControl { 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 3fed44373526df..8b20afc7debd56 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 @@ -2468,70 +2468,7 @@ Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t valu } // namespace ElectricalEnergyMeasurement namespace WaterHeaterManagement { -namespace Attributes { - -namespace HeaterTypes { -Protocols::InteractionModel::Status -Get(chip::EndpointId endpoint, - chip::BitMask * value); // WaterHeaterTypeBitmap -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::BitMask value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::BitMask value, - MarkAttributeDirty markDirty); -} // namespace HeaterTypes - -namespace HeatDemand { -Protocols::InteractionModel::Status -Get(chip::EndpointId endpoint, - chip::BitMask * value); // WaterHeaterDemandBitmap -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::BitMask value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::BitMask value, - MarkAttributeDirty markDirty); -} // namespace HeatDemand - -namespace TankVolume { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value); // int16u -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value, MarkAttributeDirty markDirty); -} // namespace TankVolume - -namespace EstimatedHeatRequired { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, int64_t * value); // energy_mwh -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, int64_t value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, int64_t value, MarkAttributeDirty markDirty); -} // namespace EstimatedHeatRequired - -namespace TankPercentage { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, chip::Percent * value); // percent -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::Percent value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::Percent value, MarkAttributeDirty markDirty); -} // namespace TankPercentage - -namespace BoostState { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, - chip::app::Clusters::WaterHeaterManagement::BoostStateEnum * value); // BoostStateEnum -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, - chip::app::Clusters::WaterHeaterManagement::BoostStateEnum value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, chip::app::Clusters::WaterHeaterManagement::BoostStateEnum value, - MarkAttributeDirty markDirty); -} // namespace BoostState - -namespace FeatureMap { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint32_t * value); // bitmap32 -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint32_t value, MarkAttributeDirty markDirty); -} // namespace FeatureMap - -namespace ClusterRevision { -Protocols::InteractionModel::Status Get(chip::EndpointId endpoint, uint16_t * value); // int16u -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value); -Protocols::InteractionModel::Status Set(chip::EndpointId endpoint, uint16_t value, MarkAttributeDirty markDirty); -} // namespace ClusterRevision - -} // namespace Attributes +namespace Attributes {} // namespace Attributes } // namespace WaterHeaterManagement namespace DemandResponseLoadControl { 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 ab6514fdf02103..2f77b274bd46c2 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -6010,18 +6010,6 @@ bool emberAfValveConfigurationAndControlClusterOpenCallback( bool emberAfValveConfigurationAndControlClusterCloseCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::ValveConfigurationAndControl::Commands::Close::DecodableType & commandData); -/** - * @brief Water Heater Management Cluster Boost Command callback (from client) - */ -bool emberAfWaterHeaterManagementClusterBoostCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WaterHeaterManagement::Commands::Boost::DecodableType & commandData); -/** - * @brief Water Heater Management Cluster CancelBoost Command callback (from client) - */ -bool emberAfWaterHeaterManagementClusterCancelBoostCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WaterHeaterManagement::Commands::CancelBoost::DecodableType & commandData); /** * @brief Demand Response Load Control Cluster RegisterLoadControlProgramRequest Command callback (from client) */ @@ -6065,12 +6053,6 @@ bool emberAfMessagesClusterPresentMessagesRequestCallback( bool emberAfMessagesClusterCancelMessagesRequestCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::Messages::Commands::CancelMessagesRequest::DecodableType & commandData); -/** - * @brief Water Heater Mode Cluster ChangeToMode Command callback (from client) - */ -bool emberAfWaterHeaterModeClusterChangeToModeCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WaterHeaterMode::Commands::ChangeToMode::DecodableType & commandData); /** * @brief Door Lock Cluster LockDoor 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 13951c0ff2bc00..0304b683406b8b 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 @@ -1895,20 +1895,6 @@ static auto __attribute__((unused)) EnsureKnownEnumValue(EnergyPreference::Energ } } -static auto __attribute__((unused)) EnsureKnownEnumValue(WaterHeaterMode::ModeTag val) -{ - using EnumType = WaterHeaterMode::ModeTag; - switch (val) - { - case EnumType::kOff: - case EnumType::kManual: - case EnumType::kTimed: - return val; - default: - return EnumType::kUnknownEnumValue; - } -} - static auto __attribute__((unused)) EnsureKnownEnumValue(DoorLock::AlarmCodeEnum val) { using EnumType = DoorLock::AlarmCodeEnum; 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 99b7167b26bee3..6e8ba769abd97d 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 @@ -2816,11 +2816,11 @@ enum class ModeTag : uint16_t kOff = 0x4000, kManual = 0x4001, kTimed = 0x4002, - // 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 = 0, + // kUnknownEnumValue intentionally not defined. This enum never goes + // through DataModel::Decode, likely because it is a part of a derived + // cluster. As a result having kUnknownEnumValue in this enum is error + // prone, and was removed. See + // src/app/common/templates/config-data.yaml. }; // Bitmap for Feature From 5fcf76fd569dfbf039018abd6072a6c5c8a6eb54 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Tue, 23 Jul 2024 13:42:39 +0000 Subject: [PATCH 02/30] Restyled by whitespace --- .../all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index f9ffe87c466d20..70813692e141e0 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -267,4 +267,3 @@ bool HandleWaterHeaterManagementTestEventTrigger(uint64_t eventTrigger) return true; } - From 05b3809b818232b89bf6e15d58555ebc50943837 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Tue, 23 Jul 2024 13:42:41 +0000 Subject: [PATCH 03/30] Restyled by clang-format --- .../all-clusters-common/include/WhmDelegate.h | 2 +- .../all-clusters-common/include/WhmInstance.h | 1 - .../all-clusters-app/all-clusters-common/src/WhmMain.cpp | 5 +++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h index 36651b9c70140b..444ef9c9df7fa8 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -250,7 +250,7 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate, pu uint16_t mHotWaterTemperature; // The % of hot water remaining at mHotWaterTemperature -// uint8_t mPercentageHotWater; + // uint8_t mPercentageHotWater; // The (100 - mPercentageHotWater)% of water at mReplacedWaterTemperature uint16_t mReplacedWaterTemperature; diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h b/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h index 4170f2c0c80896..a776bd92b99534 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmInstance.h @@ -29,7 +29,6 @@ namespace Clusters { namespace WaterHeaterManagement { using namespace chip::app::Clusters::WaterHeaterManagement; - class WaterHeaterManagementInstance : public Instance { public: diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp index 4b20b04886981d..5365cbe0f4aa6d 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp @@ -72,8 +72,9 @@ CHIP_ERROR WhmInit() } /* Manufacturer may optionally not support all features, commands & attributes */ - gWhmInstance = - std::make_unique(EndpointId(WHM_ENDPOINT), *gWhmDelegate, BitMask(Feature::kEnergyManagement, Feature::kTankPercent)); //GetFeatureMapFromCmdLine()); + gWhmInstance = std::make_unique( + EndpointId(WHM_ENDPOINT), *gWhmDelegate, + BitMask(Feature::kEnergyManagement, Feature::kTankPercent)); // GetFeatureMapFromCmdLine()); if (!gWhmInstance) { From 294901395f1e441ce0cb6dec5d3ea1c00d8b715a Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Tue, 23 Jul 2024 13:42:42 +0000 Subject: [PATCH 04/30] Restyled by gn --- examples/all-clusters-app/linux/BUILD.gn | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 0d91c277b74300..9c79be2d095d71 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -29,6 +29,10 @@ if (chip_enable_pw_rpc) { source_set("chip-all-clusters-common") { sources = [ + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/air-quality-instance.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/binding-handler.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/boolcfg-stub.cpp", @@ -65,12 +69,6 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp", - - "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp", - "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmInstance.cpp", - "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp", - "${chip_root}/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp", - "AllClustersCommandDelegate.cpp", "AppOptions.cpp", "ValveControlDelegate.cpp", From 39275f3d9e324ed74dc6f806052af5a6eb366316 Mon Sep 17 00:00:00 2001 From: pcoleman Date: Tue, 23 Jul 2024 16:22:06 +0100 Subject: [PATCH 05/30] Fix CI test --- .../all-clusters-common/src/WhmManufacturer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index 70813692e141e0..8bf29e77da3011 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -84,7 +84,7 @@ BitMask WhmManufacturer::DetermineHeatingSources() BitMask heaterTypes = dg->GetHeaterTypes(); uint8_t heaterDemandMask = 0; - for (uint8_t idx = 0; idx < sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0]); idx++) + for (uint16_t idx = 0; idx < static_cast(sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0])); idx++) { // Is this heating source being used? if (heaterTypes.Raw() & waterHeaterTypeValues[idx]) From 871ee3841d93ff3e4e0e1d4db7bbe263a3bd93aa Mon Sep 17 00:00:00 2001 From: pcoleman Date: Thu, 25 Jul 2024 14:11:33 +0100 Subject: [PATCH 06/30] Address review comments from JamesH --- .../all-clusters-common/include/WhmDelegate.h | 117 ++++-------------- .../include/water-heater-mode.h | 73 +++++++++++ .../src/WhmDelegateImpl.cpp | 64 ++-------- .../all-clusters-common/src/WhmMain.cpp | 3 +- .../src/WhmManufacturer.cpp | 15 ++- .../src/water-heater-mode.cpp | 105 ++++++++++++++++ examples/all-clusters-app/linux/BUILD.gn | 1 + .../all-clusters-app/linux/main-common.cpp | 2 + .../water-heater-management-server.cpp | 5 +- 9 files changed, 224 insertions(+), 161 deletions(-) create mode 100755 examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h create mode 100755 examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h index 444ef9c9df7fa8..8c18b5ef375e64 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -19,7 +19,6 @@ #pragma once #include -#include #include #include @@ -34,7 +33,7 @@ using ModeTagStructType = detail::Structs::ModeTagStruct::Type; class WhmManufacturer; // This is an application level delegate to handle operational state commands according to the specific business logic. -class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate, public ModeBase::Delegate +class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate { public: WaterHeaterManagementDelegate(EndpointId clustersEndpoint); @@ -47,29 +46,33 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate, pu /********************************************************************************* * - * Methods implementing the WaterHeaterManagement::Delegate interace + * Methods implementing the WaterHeaterManagement::Delegate interface * *********************************************************************************/ /** * @brief Delegate should implement a handler to start boosting the water temperature as required. - * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the water in the tank (or the - * TargetPercentage of the water, if included) to be heated towards the set point (or the TemporarySetpoint, if included), which - * in turn may cause a call for heat, even if the mode is OFF, or is TIMED and it is during one of the Off periods. + * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the + * water in the tank (or the TargetPercentage of the water, if included) to be heated towards + * the set point (or the TemporarySetpoint, if included), which in turn may cause a call for heat, + * even if the mode is OFF, or is TIMED and it is during one of the Off periods. * - * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts - * to the previous mode (e.g. OFF, MANUAL or TIMED). - * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the - * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). - * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause - * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). - * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be - * used instead of the normal set point temperature whilst the BOOST state is active. - * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be - * heated by this Boost command before the heater is switched off. - * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because - * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field - * indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. + * @param duration Indicates the time period in seconds for which the BOOST state is activated before it + * automatically reverts to the previous mode (e.g. OFF, MANUAL or TIMED). + * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has + * first reached the set point temperature (or the TemporarySetpoint temperature, if specified) + * for the TargetPercentage (if specified). + * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. + * This MAY cause multiple heat sources to be activated (e.g. a heat pump and direct + * electric heating element). + * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. + * It SHALL be used instead of the normal set point temperature whilst the BOOST state is active. + * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water + * that SHALL be heated by this Boost command before the heater is switched off. + * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased + * because the TargetPercentage of the water in the tank has been heated to the set point (or + * TemporarySetpoint if included), this field indicates the percentage to which the hot water in + * the tank SHALL be allowed to fall before again beginning to reheat it. * * @return Success if the boost command is accepted; otherwise the command SHALL be rejected with appropriate error. */ @@ -104,57 +107,6 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate, pu void SetTankPercentage(Percent tankPercentage); void SetBoostState(BoostStateEnum boostState); - /********************************************************************************* - * - * Methods implementing the ModeBase::Delegate interface - * - *********************************************************************************/ - - CHIP_ERROR Init() override; - - /** - * Handle application logic when the mode is changing. - * - * @param mode The new mode that the device is requested to transition to. - * @param response A reference to a response that will be sent to the client. The contents of which con be modified by the - * application. - */ - void HandleChangeToMode(uint8_t mode, ModeBase::Commands::ChangeToModeResponse::Type & response) override; - - /** - * Get the mode label of the Nth mode in the list of modes. - * - * @param modeIndex The index of the mode to be returned. It is assumed that modes are indexable from 0 and with no gaps. - * @param label A reference to the mutable char span which will be mutated to receive the label on success. Use - * CopyCharSpanToMutableCharSpan to copy into the MutableCharSpan. - * - * @return Returns a CHIP_NO_ERROR if there was no error and the label was returned successfully. - * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available labels. - */ - CHIP_ERROR GetModeLabelByIndex(uint8_t modeIndex, MutableCharSpan & label) override; - - /** - * Get the mode value of the Nth mode in the list of modes. - * - * @param modeIndex The index of the mode to be returned. It is assumed that modes are indexable from 0 and with no gaps. - * @param value a reference to the uint8_t variable that is to contain the mode value. - * - * @return Returns a CHIP_NO_ERROR if there was no error and the value was returned successfully. - * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available values. - */ - CHIP_ERROR GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) override; - - /** - * Get the mode tags of the Nth mode in the list of modes. - * @param modeIndex The index of the mode to be returned. It is assumed that modes are indexable from 0 and with no gaps. - * @param tags a reference to an existing and initialised buffer that is to contain the mode tags. std::copy can be used - * to copy into the buffer. - * - * @return Returns a CHIP_NO_ERROR if there was no error and the mode tags were returned successfully. - * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available mode tags. - */ - CHIP_ERROR GetModeTagsByIndex(uint8_t modeIndex, DataModel::List & tags) override; - /********************************************************************************* * * WaterHeaterManagementDelegate specific methods @@ -293,9 +245,6 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate, pu * *********************************************************************************/ - // Access to the Water Heater Mode instance - ModeBase::Instance mWaterHeaterModeInstance; - // This attribute SHALL indicate the methods to call for heat that the controller supports. If a bit is set then the controller // supports the corresponding method. BitMask mHeaterTypes; @@ -319,28 +268,6 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate, pu // This attribute SHALL indicate if the BOOST state, as triggered by a Boost command, is currently active. BoostStateEnum mBoostState; - - /********************************************************************************* - * - * Member variables implementing the ModeBase::Delegate interface - * - *********************************************************************************/ - - ModeTagStructType modeTagsOff[1] = { { .value = to_underlying(WaterHeaterMode::ModeTag::kOff) } }; - ModeTagStructType modeTagsManual[1] = { { .value = to_underlying(WaterHeaterMode::ModeTag::kManual) } }; - ModeTagStructType modeTagsTimed[1] = { { .value = to_underlying(WaterHeaterMode::ModeTag::kTimed) } }; - - const detail::Structs::ModeOptionStruct::Type kModeOptions[3] = { - detail::Structs::ModeOptionStruct::Type{ .label = CharSpan::fromCharString("Off"), - .mode = ModeOff, - .modeTags = DataModel::List(modeTagsOff) }, - detail::Structs::ModeOptionStruct::Type{ .label = CharSpan::fromCharString("Manual"), - .mode = ModeManual, - .modeTags = DataModel::List(modeTagsManual) }, - detail::Structs::ModeOptionStruct::Type{ .label = CharSpan::fromCharString("Timed"), - .mode = ModeTimed, - .modeTags = DataModel::List(modeTagsTimed) } - }; }; } // namespace WaterHeaterManagement diff --git a/examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h b/examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h new file mode 100755 index 00000000000000..34724232bd46ed --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/water-heater-mode.h @@ -0,0 +1,73 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { + +namespace WaterHeaterMode { + +constexpr uint8_t ModeOff = 0; +constexpr uint8_t ModeManual = 1; +constexpr uint8_t ModeTimed = 2; + +/// This is an application level delegate to handle WaterHeaterMode commands according to the specific business logic. +class ExampleWaterHeaterModeDelegate : public ModeBase::Delegate +{ +private: + using ModeTagStructType = detail::Structs::ModeTagStruct::Type; + ModeTagStructType modeTagsOff[1] = { { .value = to_underlying(ModeTag::kOff) } }; + ModeTagStructType modeTagsManual[1] = { { .value = to_underlying(ModeTag::kManual) } }; + ModeTagStructType modeTagsTimed[1] = { { .value = to_underlying(ModeTag::kTimed) } }; + + const detail::Structs::ModeOptionStruct::Type kModeOptions[3] = { + detail::Structs::ModeOptionStruct::Type{ + .label = "Off"_span, .mode = ModeOff, .modeTags = DataModel::List(modeTagsOff) }, + detail::Structs::ModeOptionStruct::Type{ + .label = "Manual"_span, .mode = ModeManual, .modeTags = DataModel::List(modeTagsManual) }, + detail::Structs::ModeOptionStruct::Type{ + .label = "Timed"_span, .mode = ModeTimed, .modeTags = DataModel::List(modeTagsTimed) } + }; + + CHIP_ERROR Init() override; + void HandleChangeToMode(uint8_t mode, ModeBase::Commands::ChangeToModeResponse::Type & response) override; + + CHIP_ERROR GetModeLabelByIndex(uint8_t modeIndex, MutableCharSpan & label) override; + CHIP_ERROR GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) override; + CHIP_ERROR GetModeTagsByIndex(uint8_t modeIndex, DataModel::List & tags) override; + +public: + ~ExampleWaterHeaterModeDelegate() override = default; +}; + +ModeBase::Instance * Instance(); + +void Shutdown(); + +} // namespace WaterHeaterMode + +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index ee864ba6b1a6ee..ccae9d630e7135 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -17,8 +17,10 @@ */ #include + #include #include +#include using namespace chip; using namespace chip::app; @@ -29,11 +31,9 @@ using Protocols::InteractionModel::Status; WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clustersEndpoint) : mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mBoostTargetTemperatureReached(false), - mWaterHeaterModeInstance(this, clustersEndpoint, WaterHeaterMode::Id, 0), mTankVolume(0), mEstimatedHeatRequired(0), + mTankVolume(0), mEstimatedHeatRequired(0), mTankPercentage(0), mBoostState(BoostStateEnum::kInactive) { - // Initialise the WaterHeaterMode instance - mWaterHeaterModeInstance.Init(); } void WaterHeaterManagementDelegate::SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance) @@ -287,56 +287,6 @@ Status WaterHeaterManagementDelegate::HandleCancelBoost() * *********************************************************************************/ -CHIP_ERROR WaterHeaterManagementDelegate::Init() -{ - return CHIP_NO_ERROR; -} - -void WaterHeaterManagementDelegate::HandleChangeToMode(uint8_t NewMode, ModeBase::Commands::ChangeToModeResponse::Type & response) -{ - response.status = to_underlying(ModeBase::StatusCode::kSuccess); -} - -CHIP_ERROR WaterHeaterManagementDelegate::GetModeLabelByIndex(uint8_t modeIndex, chip::MutableCharSpan & label) -{ - if (modeIndex >= ArraySize(kModeOptions)) - { - return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; - } - - return chip::CopyCharSpanToMutableCharSpan(kModeOptions[modeIndex].label, label); -} - -CHIP_ERROR WaterHeaterManagementDelegate::GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) -{ - if (modeIndex >= ArraySize(kModeOptions)) - { - return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; - } - - value = kModeOptions[modeIndex].mode; - - return CHIP_NO_ERROR; -} - -CHIP_ERROR WaterHeaterManagementDelegate::GetModeTagsByIndex(uint8_t modeIndex, DataModel::List & tags) -{ - if (modeIndex >= ArraySize(kModeOptions)) - { - return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; - } - - if (tags.size() < kModeOptions[modeIndex].modeTags.size()) - { - return CHIP_ERROR_INVALID_ARGUMENT; - } - - std::copy(kModeOptions[modeIndex].modeTags.begin(), kModeOptions[modeIndex].modeTags.end(), tags.begin()); - tags.reduce_size(kModeOptions[modeIndex].modeTags.size()); - - return CHIP_NO_ERROR; -} - /********************************************************************************* * * WaterHeaterManagementDelegate specific methods @@ -431,9 +381,9 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() if (!HasWaterTemperatureReachedTarget()) { - uint8_t mode = mInstance->GetCurrentMode(); + uint8_t mode = WaterHeaterMode::Instance()->GetCurrentMode(); - // The water in the tank is not at the target temperature. See if we heating is currently off + // The water in the tank is not at the target temperature. See if heating is currently off if (mHeatDemand.Raw() == 0) { // Need to track whether the water temperature has reached the target temperature for the boost @@ -524,13 +474,13 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() void WaterHeaterManagementDelegate::SetWaterHeaterMode(uint8_t modeValue) { - if (!mInstance->IsSupportedMode(modeValue)) + if (!WaterHeaterMode::Instance()->IsSupportedMode(modeValue)) { ChipLogError(AppServer, "SetWaterHeaterMode bad mode"); return; } - Status status = mInstance->UpdateCurrentMode(modeValue); + Status status = WaterHeaterMode::Instance()->UpdateCurrentMode(modeValue); if (status != Status::Success) { ChipLogError(AppServer, "SetWaterHeaterMode updateMode failed"); diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp index 5365cbe0f4aa6d..02cd4214b94785 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp @@ -74,8 +74,7 @@ CHIP_ERROR WhmInit() /* Manufacturer may optionally not support all features, commands & attributes */ gWhmInstance = std::make_unique( EndpointId(WHM_ENDPOINT), *gWhmDelegate, - BitMask(Feature::kEnergyManagement, Feature::kTankPercent)); // GetFeatureMapFromCmdLine()); - + BitMask(Feature::kEnergyManagement, Feature::kTankPercent)); if (!gWhmInstance) { ChipLogError(AppServer, "Failed to allocate memory for WaterHeaterManagementInstance"); diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index 8bf29e77da3011..f07467011d71b2 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -25,6 +25,7 @@ #include using namespace chip; +using namespace chip::app::Clusters::WaterHeaterManagement; using Protocols::InteractionModel::Status; @@ -44,6 +45,10 @@ CHIP_ERROR WhmManufacturer::Init() mBoostActive = false; + dg->SetHeaterTypes(BitMask(WaterHeaterTypeBitmap::kImmersionElement1)); + dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); + dg->SetEstimatedHeatRequired(10000); + return CHIP_NO_ERROR; } @@ -72,11 +77,11 @@ BitMask WhmManufacturer::DetermineHeatingSources() // The corresponding list of valid headerDemands uint8_t waterHeaterDemandValues[] = { - static_cast(WaterHeaterTypeBitmap::kImmersionElement1), - static_cast(WaterHeaterTypeBitmap::kImmersionElement2), - static_cast(WaterHeaterTypeBitmap::kHeatPump), - static_cast(WaterHeaterTypeBitmap::kBoiler), - static_cast(WaterHeaterTypeBitmap::kOther), + static_cast(WaterHeaterDemandBitmap::kImmersionElement1), + static_cast(WaterHeaterDemandBitmap::kImmersionElement2), + static_cast(WaterHeaterDemandBitmap::kHeatPump), + static_cast(WaterHeaterDemandBitmap::kBoiler), + static_cast(WaterHeaterDemandBitmap::kOther), }; // Iterate across the valid waterHeaterTypes seeing which heating sources are available based on heaterTypes. diff --git a/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp b/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp new file mode 100755 index 00000000000000..9c4121fb70d668 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp @@ -0,0 +1,105 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::WaterHeaterMode; +using chip::Protocols::InteractionModel::Status; +template +using List = chip::app::DataModel::List; +using ModeTagStructType = chip::app::Clusters::detail::Structs::ModeTagStruct::Type; + +static ExampleWaterHeaterModeDelegate * gWaterHeaterModeDelegate = nullptr; +static ModeBase::Instance * gWaterHeaterModeInstance = nullptr; + +CHIP_ERROR ExampleWaterHeaterModeDelegate::Init() +{ + return CHIP_NO_ERROR; +} + +// todo refactor code by making a parent class for all ModeInstance classes to reduce flash usage. +void ExampleWaterHeaterModeDelegate::HandleChangeToMode(uint8_t NewMode, ModeBase::Commands::ChangeToModeResponse::Type & response) +{ + response.status = to_underlying(ModeBase::StatusCode::kSuccess); +} + +CHIP_ERROR ExampleWaterHeaterModeDelegate::GetModeLabelByIndex(uint8_t modeIndex, chip::MutableCharSpan & label) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + return chip::CopyCharSpanToMutableCharSpan(kModeOptions[modeIndex].label, label); +} + +CHIP_ERROR ExampleWaterHeaterModeDelegate::GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + value = kModeOptions[modeIndex].mode; + return CHIP_NO_ERROR; +} + +CHIP_ERROR ExampleWaterHeaterModeDelegate::GetModeTagsByIndex(uint8_t modeIndex, List & tags) +{ + if (modeIndex >= ArraySize(kModeOptions)) + { + return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + } + + if (tags.size() < kModeOptions[modeIndex].modeTags.size()) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + std::copy(kModeOptions[modeIndex].modeTags.begin(), kModeOptions[modeIndex].modeTags.end(), tags.begin()); + tags.reduce_size(kModeOptions[modeIndex].modeTags.size()); + + return CHIP_NO_ERROR; +} + +ModeBase::Instance * WaterHeaterMode::Instance() +{ + return gWaterHeaterModeInstance; +} + +void WaterHeaterMode::Shutdown() +{ + if (gWaterHeaterModeInstance != nullptr) + { + delete gWaterHeaterModeInstance; + gWaterHeaterModeInstance = nullptr; + } + if (gWaterHeaterModeDelegate != nullptr) + { + delete gWaterHeaterModeDelegate; + gWaterHeaterModeDelegate = nullptr; + } +} + +void emberAfWaterHeaterModeClusterInitCallback(chip::EndpointId endpointId) +{ + VerifyOrDie(gWaterHeaterModeDelegate == nullptr && gWaterHeaterModeInstance == nullptr); + gWaterHeaterModeDelegate = new WaterHeaterMode::ExampleWaterHeaterModeDelegate; + gWaterHeaterModeInstance = + new ModeBase::Instance(gWaterHeaterModeDelegate, endpointId, WaterHeaterMode::Id, chip::to_underlying(Feature::kOnOff)); + gWaterHeaterModeInstance->Init(); +} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 9c79be2d095d71..838df03741bd00 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -61,6 +61,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/tcc-mode.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/water-heater-mode.cpp", "${chip_root}/examples/all-clusters-app/linux/diagnostic-logs-provider-delegate-impl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementDelegateImpl.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/DeviceEnergyManagementManager.cpp", diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp index 83a019f8bc98e8..78400c56022a4d 100644 --- a/examples/all-clusters-app/linux/main-common.cpp +++ b/examples/all-clusters-app/linux/main-common.cpp @@ -37,6 +37,7 @@ #include "rvc-modes.h" #include "rvc-operational-state-delegate-impl.h" #include "tcc-mode.h" +#include "water-heater-mode.h" #include #include #include @@ -265,6 +266,7 @@ void ApplicationShutdown() Clusters::EnergyEvseMode::Shutdown(); Clusters::WaterHeaterManagement::WhmApplicationShutdown(); + Clusters::WaterHeaterMode::Shutdown(); if (sChipNamedPipeCommands.Stop() != CHIP_NO_ERROR) { diff --git a/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp b/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp index 447a6646766452..6af249370e8fb8 100644 --- a/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp +++ b/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -39,7 +40,7 @@ constexpr uint16_t kClusterRevision = 1; CHIP_ERROR Instance::Init() { - ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->RegisterCommandHandler(this)); + ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::RegisterCommandHandler(this)); VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); return CHIP_NO_ERROR; @@ -47,7 +48,7 @@ CHIP_ERROR Instance::Init() void Instance::Shutdown() { - InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this); + CommandHandlerInterfaceRegistry::UnregisterCommandHandler(this); unregisterAttributeAccessOverride(this); } From b3597647e746013648076450c1de4dffa1142bc2 Mon Sep 17 00:00:00 2001 From: pcoleman Date: Thu, 25 Jul 2024 14:35:27 +0100 Subject: [PATCH 07/30] Address review comments from JamesH --- .../all-clusters-common/include/WhmDelegate.h | 29 ++----------------- .../src/WhmDelegateImpl.cpp | 7 ++--- .../src/WhmManufacturer.cpp | 6 ++-- 3 files changed, 9 insertions(+), 33 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h index 8c18b5ef375e64..5ac36244f4e8cf 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -28,8 +28,6 @@ namespace app { namespace Clusters { namespace WaterHeaterManagement { -using ModeTagStructType = detail::Structs::ModeTagStruct::Type; - class WhmManufacturer; // This is an application level delegate to handle operational state commands according to the specific business logic. @@ -82,8 +80,8 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate /** * @brief Delegate should implement a handler to cancel a boost command. - * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode (e.g. OFF, MANUAL or - * TIMED). + * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode + * (e.g. OFF, MANUAL or TIMED). * * @return It should report SUCCESS if successful and FAILURE otherwise. */ @@ -164,24 +162,6 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate */ void DrawOffHotWater(uint8_t percentageReplaced, uint16_t replacedWaterTemperature); - /********************************************************************************* - * - * Public constants - * - *********************************************************************************/ - - // The Water Header Modes - - // The device will not attempt to keep the water warm. - static constexpr uint8_t ModeOff = 0; - - // The device will attempt to keep the water warm based on the OccupiedHeatingSetpoint attribute of the associated Thermostat - // cluster. - static constexpr uint8_t ModeManual = 1; - - // The device will attempt to keep the water warm based on the Schedules attribute of the associated Thermostat cluster. - static constexpr uint8_t ModeTimed = 2; - private: /********************************************************************************* * @@ -201,10 +181,7 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate // Actual water temperature in 100ths of a C uint16_t mHotWaterTemperature; - // The % of hot water remaining at mHotWaterTemperature - // uint8_t mPercentageHotWater; - - // The (100 - mPercentageHotWater)% of water at mReplacedWaterTemperature + // The % of water at temperature mReplacedWaterTemperature uint16_t mReplacedWaterTemperature; // Boost command parameters diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index ccae9d630e7135..5b9b2f381f1e58 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -44,7 +44,7 @@ void WaterHeaterManagementDelegate::SetWaterHeaterManagementInstance(WaterHeater { // If the feature kTankPercent is not supported then set mTankPercentage to 100% so calculations below need // less code for the feature kTankPercent case. - mTankPercentage = 100; + // mTankPercentage = 100; } } @@ -295,7 +295,6 @@ Status WaterHeaterManagementDelegate::HandleCancelBoost() void WaterHeaterManagementDelegate::SetWaterTemperature(uint16_t waterTemperature) { - // This method assumed 100% of the water in the tank has reached the waterTemperature specified mHotWaterTemperature = waterTemperature; // Do not change mTankPercentage if the kTankPercent feature is not supported @@ -394,7 +393,7 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() } // If a boost command is in progress or in manual mode, find a heating source and "turn it on". - if (mBoostState == BoostStateEnum::kActive || mode == ModeManual) + if (mBoostState == BoostStateEnum::kActive || mode == WaterHeaterMode::ModeManual) { if (mpWhmManufacturer != nullptr) { @@ -413,7 +412,7 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() } } } - else if (mBoostState == BoostStateEnum::kInactive && mode == ModeOff) + else if (mBoostState == BoostStateEnum::kInactive && mode == WaterHeaterMode::ModeOff) { // The water temperature is not at the target temperature but there is no boost command in progress and the mode is Off // so need to ensure the heating is turned off. diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index f07467011d71b2..c8779b2f272bb7 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -18,10 +18,10 @@ #include #include +#include #include #include - #include using namespace chip; @@ -198,7 +198,7 @@ void SetTestEventTrigger_ManualModeTestEvent() WaterHeaterManagementDelegate * dg = GetWhmDelegate(); // Simulate the Water Heater Mode being set to MANUAL - dg->SetWaterHeaterMode(WaterHeaterManagementDelegate::ModeManual); + dg->SetWaterHeaterMode(WaterHeaterMode::ModeManual); } void SetTestEventTrigger_OffModeTestEvent() @@ -206,7 +206,7 @@ void SetTestEventTrigger_OffModeTestEvent() WaterHeaterManagementDelegate * dg = GetWhmDelegate(); // Simulate the Water Heater Mode being set to OFF - dg->SetWaterHeaterMode(WaterHeaterManagementDelegate::ModeOff); + dg->SetWaterHeaterMode(WaterHeaterMode::ModeOff); } void SetTestEventTrigger_DrawOffHotWaterTestEvent() From 97d6354c79c15e6e79999b2b383a13688c22bafb Mon Sep 17 00:00:00 2001 From: pcoleman Date: Thu, 25 Jul 2024 14:51:04 +0100 Subject: [PATCH 08/30] Address review comments from JamesH --- .../all-clusters-common/src/WhmDelegateImpl.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 5b9b2f381f1e58..30555efebc0ab3 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -39,13 +39,6 @@ WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clusters void WaterHeaterManagementDelegate::SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance) { mpWhmInstance = &instance; - - if (!mpWhmInstance->HasFeature(Feature::kTankPercent)) - { - // If the feature kTankPercent is not supported then set mTankPercentage to 100% so calculations below need - // less code for the feature kTankPercent case. - // mTankPercentage = 100; - } } void WaterHeaterManagementDelegate::SetWhmManufacturer(WhmManufacturer & whmManufacturer) @@ -340,7 +333,10 @@ void WaterHeaterManagementDelegate::DrawOffHotWater(uint8_t percentageReplaced, bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const { // Determine the target temperature. If a boost command is in progress and has a mBoostTemporarySetpoint value use that as the - // target temperature + // target temperature. + // Note, in practise the actual heating is likely to be controlled by the thermostat's occupiedHeatingSetpoint most of the + // time, and the TemporarySetpoint (if not null) would be overiding the thermostat's occupiedHeatingSetpoint. + // However, this code doesn't rely upon the thermostat cluster. uint16_t targetTemperature = (mBoostState == BoostStateEnum::kActive && mBoostTemporarySetpoint.HasValue()) ? static_cast(mBoostTemporarySetpoint.Value()) : mTargetWaterTemperature; From 19763567c0f057618250c808c35450c2f3813fde Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 25 Jul 2024 13:51:45 +0000 Subject: [PATCH 09/30] Restyled by clang-format --- .../all-clusters-common/src/WhmDelegateImpl.cpp | 9 +++------ .../all-clusters-app/all-clusters-common/src/WhmMain.cpp | 3 +-- .../all-clusters-common/src/WhmManufacturer.cpp | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 30555efebc0ab3..157f0e4224ff05 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -17,7 +17,6 @@ */ #include - #include #include #include @@ -30,11 +29,9 @@ using namespace chip::app::Clusters::WaterHeaterManagement; using Protocols::InteractionModel::Status; WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clustersEndpoint) : - mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mBoostTargetTemperatureReached(false), - mTankVolume(0), mEstimatedHeatRequired(0), - mTankPercentage(0), mBoostState(BoostStateEnum::kInactive) -{ -} + mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mBoostTargetTemperatureReached(false), mTankVolume(0), + mEstimatedHeatRequired(0), mTankPercentage(0), mBoostState(BoostStateEnum::kInactive) +{} void WaterHeaterManagementDelegate::SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance) { diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp index 02cd4214b94785..18741c5257bc79 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmMain.cpp @@ -73,8 +73,7 @@ CHIP_ERROR WhmInit() /* Manufacturer may optionally not support all features, commands & attributes */ gWhmInstance = std::make_unique( - EndpointId(WHM_ENDPOINT), *gWhmDelegate, - BitMask(Feature::kEnergyManagement, Feature::kTankPercent)); + EndpointId(WHM_ENDPOINT), *gWhmDelegate, BitMask(Feature::kEnergyManagement, Feature::kTankPercent)); if (!gWhmInstance) { ChipLogError(AppServer, "Failed to allocate memory for WaterHeaterManagementInstance"); diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index c8779b2f272bb7..a239d0207fd0bd 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -20,9 +20,9 @@ #include #include +#include #include #include -#include using namespace chip; using namespace chip::app::Clusters::WaterHeaterManagement; From 58fcfa7f0a1ae5610f09f43b16171ad76a759843 Mon Sep 17 00:00:00 2001 From: pcoleman Date: Thu, 25 Jul 2024 20:42:17 +0100 Subject: [PATCH 10/30] Get tests passing again --- .../src/WhmDelegateImpl.cpp | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 157f0e4224ff05..cf19cc16731ed5 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -287,7 +287,6 @@ void WaterHeaterManagementDelegate::SetWaterTemperature(uint16_t waterTemperatur { mHotWaterTemperature = waterTemperature; - // Do not change mTankPercentage if the kTankPercent feature is not supported if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) { mTankPercentage = 100; @@ -362,8 +361,24 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const (mBoostState == BoostStateEnum::kActive && mBoostTargetPercentage.HasValue()) ? mBoostTargetPercentage.Value() : 100; } - // Return whether the water is at the target temperature - return (mTankPercentage >= targetPercentage) && (mHotWaterTemperature >= targetTemperature); + + // Determine whether the water is at the target temperature + bool tempReached = true; + if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) + { + if (mTankPercentage < targetPercentage) + { + tempReached = false; + } + } + + if (mHotWaterTemperature < targetTemperature) + { + tempReached = false; + } + + + return tempReached; } Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() From 8e94baa5de86475ab321314db44cfd6d9c52f536 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 25 Jul 2024 19:42:53 +0000 Subject: [PATCH 11/30] Restyled by clang-format --- .../all-clusters-common/src/WhmDelegateImpl.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index cf19cc16731ed5..a38a1d46c8a6b7 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -361,7 +361,6 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const (mBoostState == BoostStateEnum::kActive && mBoostTargetPercentage.HasValue()) ? mBoostTargetPercentage.Value() : 100; } - // Determine whether the water is at the target temperature bool tempReached = true; if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) @@ -377,7 +376,6 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const tempReached = false; } - return tempReached; } From d9c58ca6557a3001433161454870f18454d6459a Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Thu, 25 Jul 2024 13:36:41 -0400 Subject: [PATCH 12/30] Declare some global items for future testing (#34509) Co-authored-by: Andrei Litvin --- .../zcl/data-model/chip/global-structs.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/app/zap-templates/zcl/data-model/chip/global-structs.xml b/src/app/zap-templates/zcl/data-model/chip/global-structs.xml index 54b4f69bc93bb8..c68c4aa1d000d7 100644 --- a/src/app/zap-templates/zcl/data-model/chip/global-structs.xml +++ b/src/app/zap-templates/zcl/data-model/chip/global-structs.xml @@ -31,4 +31,25 @@ TODO: Make these structures global rather than defining them for each cluster. + + + + + + + + + + + + + + + + + + From e794db3f42609d25289d2c9e7a5e0b4c2586f6d3 Mon Sep 17 00:00:00 2001 From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:01:27 -0400 Subject: [PATCH 13/30] =?UTF-8?q?[LevelControl]=20Implemented=20the=20Q=20?= =?UTF-8?q?quality=20logic=20for=20the=20CurrentLevel=20a=E2=80=A6=20(#344?= =?UTF-8?q?88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implemented the Quiete reporting quality logic for the CurrentLevel and RemainingTime attributes * Restyled by clang-format * use c++ struct rather than c struct format * add cluster-building-blocks to the data model public dep --------- Co-authored-by: Restyled.io --- src/app/chip_data_model.gni | 1 + .../clusters/level-control/level-control.cpp | 198 ++++++++++++------ 2 files changed, 130 insertions(+), 69 deletions(-) diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index f1fa43ca604c1c..56e921960197ea 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -420,6 +420,7 @@ template("chip_data_model") { ":${_data_model_name}_zapgen", "${chip_root}/src/access", "${chip_root}/src/app", + "${chip_root}/src/app/cluster-building-blocks", "${chip_root}/src/app/common:attribute-type", "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/common:enums", diff --git a/src/app/clusters/level-control/level-control.cpp b/src/app/clusters/level-control/level-control.cpp index 3f15620181dfd0..814436c39e179a 100644 --- a/src/app/clusters/level-control/level-control.cpp +++ b/src/app/clusters/level-control/level-control.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ #include using namespace chip; +using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::LevelControl; using chip::Protocols::InteractionModel::Status; @@ -85,7 +87,7 @@ struct CallbackScheduleState // when called consecutively }; -typedef struct +struct EmberAfLevelControlState { CommandId commandId; uint8_t moveToLevel; @@ -98,23 +100,24 @@ typedef struct uint32_t transitionTimeMs; uint32_t elapsedTimeMs; CallbackScheduleState callbackSchedule; -} EmberAfLevelControlState; + QuieterReportingAttribute quietCurrentLevel{ DataModel::NullNullable }; + QuieterReportingAttribute quietRemainingTime{ DataModel::MakeNullable(0) }; +}; static EmberAfLevelControlState stateTable[kLevelControlStateTableSize]; static EmberAfLevelControlState * getState(EndpointId endpoint); static Status moveToLevelHandler(EndpointId endpoint, CommandId commandId, uint8_t level, - app::DataModel::Nullable transitionTimeDs, - chip::Optional> optionsMask, + DataModel::Nullable transitionTimeDs, chip::Optional> optionsMask, chip::Optional> optionsOverride, uint16_t storedLevel); -static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, MoveModeEnum moveMode, - app::DataModel::Nullable rate, chip::Optional> optionsMask, +static void moveHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, MoveModeEnum moveMode, + DataModel::Nullable rate, chip::Optional> optionsMask, chip::Optional> optionsOverride); -static void stepHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, StepModeEnum stepMode, - uint8_t stepSize, app::DataModel::Nullable transitionTimeDs, +static void stepHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, StepModeEnum stepMode, + uint8_t stepSize, DataModel::Nullable transitionTimeDs, chip::Optional> optionsMask, chip::Optional> optionsOverride); -static void stopHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +static void stopHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, chip::Optional> optionsMask, chip::Optional> optionsOverride); static void setOnOffValue(EndpointId endpoint, bool onOff); @@ -122,6 +125,9 @@ static void writeRemainingTime(EndpointId endpoint, uint16_t remainingTimeMs); static bool shouldExecuteIfOff(EndpointId endpoint, CommandId commandId, chip::Optional> optionsMask, chip::Optional> optionsOverride); +static Status SetCurrentLevelQuietReport(EndpointId endpoint, EmberAfLevelControlState * state, + DataModel::Nullable newValue, bool isStartOrEndOfTransition); + #if defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl { @@ -162,7 +168,7 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl { using AttributeValuePair = ScenesManagement::Structs::AttributeValuePairStruct::Type; - app::DataModel::Nullable level; + DataModel::Nullable level; VerifyOrReturnError(Status::Success == Attributes::CurrentLevel::Get(endpoint, level), CHIP_ERROR_READ_FAILED); AttributeValuePair pairs[kLevelMaxScenableAttributes]; @@ -177,7 +183,7 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl } else { - pairs[0].valueUnsigned8.SetValue(app::NumericAttributeTraits::kNullValue); + pairs[0].valueUnsigned8.SetValue(NumericAttributeTraits::kNullValue); } size_t attributeCount = 1; if (LevelControlHasFeature(endpoint, LevelControl::Feature::kFrequency)) @@ -189,7 +195,7 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl attributeCount++; } - app::DataModel::List attributeValueList(pairs, attributeCount); + DataModel::List attributeValueList(pairs, attributeCount); return EncodeAttributeValueList(attributeValueList, serializedBytes); } @@ -203,7 +209,7 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl CHIP_ERROR ApplyScene(EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes, scenes::TransitionTimeMs timeMs) override { - app::DataModel::DecodableList attributeValueList; + DataModel::DecodableList attributeValueList; ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList)); @@ -245,15 +251,15 @@ class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl EmberAfLevelControlState * state = getState(endpoint); if (level < state->minLevel || level > state->maxLevel) { - chip::app::NumericAttributeTraits::SetNull(level); + NumericAttributeTraits::SetNull(level); } - if (!app::NumericAttributeTraits::IsNullValue(level)) + if (!NumericAttributeTraits::IsNullValue(level)) { CommandId command = LevelControlHasFeature(endpoint, LevelControl::Feature::kOnOff) ? Commands::MoveToLevelWithOnOff::Id : Commands::MoveToLevel::Id; - moveToLevelHandler(endpoint, command, level, app::DataModel::MakeNullable(static_cast(timeMs / 100)), + moveToLevelHandler(endpoint, command, level, DataModel::MakeNullable(static_cast(timeMs / 100)), chip::Optional>(), chip::Optional>(), INVALID_STORED_LEVEL); } @@ -363,18 +369,68 @@ static void reallyUpdateCoupledColorTemp(EndpointId endpoint) } #endif // IGNORE_LEVEL_CONTROL_CLUSTER_OPTIONS && MATTER_DM_PLUGIN_COLOR_CONTROL_SERVER_TEMP +/* + * @brief + * This function is used to update the current level attribute + * while respecting it's defined quiet reporting quality: + * The attribute will be reported: + * - At most once per second, or + * - At the start of the movement/transition, or + * - At the end of the movement/transition, or + * - When it changes from null to any other value and vice versa. + * + * @param endpoint: endpoint on which the currentLevel attribute must be updated. + * @param state: LevelControlState struct of this given endpoint. + * @param newValue: Value to update the attribute with + * @param isStartOrEndOfTransition: Boolean that indicate whether the update is occuring at the start or end of a level transition + * @return Success in setting the attribute value or the IM error code for the failure. + */ +static Status SetCurrentLevelQuietReport(EndpointId endpoint, EmberAfLevelControlState * state, + DataModel::Nullable newValue, bool isStartOrEndOfTransition) +{ + AttributeDirtyState dirtyState; + MarkAttributeDirty markDirty = MarkAttributeDirty::kNo; + auto now = System::SystemClock().GetMonotonicTimestamp(); + + if (isStartOrEndOfTransition) + { + // At the start or end of the movement/transition we must report + auto predicate = [](const decltype(state->quietCurrentLevel)::SufficientChangePredicateCandidate &) -> bool { + return true; + }; + dirtyState = state->quietCurrentLevel.SetValue(newValue, now, predicate); + } + else + { + // During transtions, reports should be at most once per second + System::Clock::Milliseconds64 reportInterval = + std::max(System::Clock::Milliseconds64(1000), System::Clock::Milliseconds64(state->transitionTimeMs / 4)); + auto predicate = state->quietCurrentLevel.GetPredicateForSufficientTimeSinceLastDirty(reportInterval); + dirtyState = state->quietCurrentLevel.SetValue(newValue, now, predicate); + } + + if (dirtyState == AttributeDirtyState::kMustReport) + { + markDirty = MarkAttributeDirty::kIfChanged; + } + return Attributes::CurrentLevel::Set(endpoint, state->quietCurrentLevel.value(), markDirty); +} + void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint) { EmberAfLevelControlState * state = getState(endpoint); Status status; - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; const auto callbackStartTimestamp = System::SystemClock().GetMonotonicTimestamp(); + bool isTransitionStart = false; + bool isTransitionEnd = false; if (state == nullptr) { return; } + isTransitionStart = (state->elapsedTimeMs == 0); state->elapsedTimeMs += state->eventDurationMs; // Read the attribute; print error message and return if it can't be read @@ -412,7 +468,9 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint) ChipLogDetail(Zcl, " to %d ", currentLevel.Value()); ChipLogDetail(Zcl, "(diff %c1)", state->increasing ? '+' : '-'); - status = Attributes::CurrentLevel::Set(endpoint, currentLevel); + // Are we at the requested level? + isTransitionEnd = (currentLevel.Value() == state->moveToLevel); + status = SetCurrentLevelQuietReport(endpoint, state, currentLevel, (isTransitionStart || isTransitionEnd)); if (status != Status::Success) { ChipLogProgress(Zcl, "ERR: writing current level %x", to_underlying(status)); @@ -423,8 +481,7 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint) updateCoupledColorTemp(endpoint); - // Are we at the requested level? - if (currentLevel.Value() == state->moveToLevel) + if (isTransitionEnd) { if (state->commandId == Commands::MoveToLevelWithOnOff::Id || state->commandId == Commands::MoveWithOnOff::Id || state->commandId == Commands::StepWithOnOff::Id) @@ -478,11 +535,20 @@ static void writeRemainingTime(EndpointId endpoint, uint16_t remainingTimeMs) // This is done to ensure that the attribute, in tenths of a second, only // goes to zero when the remaining time in milliseconds is actually zero. uint16_t remainingTimeDs = static_cast((remainingTimeMs + 99) / 100); - Status status = LevelControl::Attributes::RemainingTime::Set(endpoint, remainingTimeDs); - if (status != Status::Success) + auto markDirty = MarkAttributeDirty::kNo; + auto state = getState(endpoint); + auto now = System::SystemClock().GetMonotonicTimestamp(); + + // Establish the quiet report condition for the RemainingTime Attribute + // The quiet report is determined by the previously set policies: + // - kMarkDirtyOnChangeToFromZero : When the value changes from 0 to any other value and vice versa, or + // - kMarkDirtyOnIncrement : When the value increases. + if (state->quietRemainingTime.SetValue(remainingTimeDs, now) == AttributeDirtyState::kMustReport) { - ChipLogProgress(Zcl, "ERR: writing remaining time %x", to_underlying(status)); + markDirty = MarkAttributeDirty::kIfChanged; } + + Attributes::RemainingTime::Set(endpoint, state->quietRemainingTime.value().ValueOr(0), markDirty); } #endif // IGNORE_LEVEL_CONTROL_CLUSTER_LEVEL_CONTROL_REMAINING_TIME } @@ -583,7 +649,7 @@ static bool shouldExecuteIfOff(EndpointId endpoint, CommandId commandId, chip::O return true; } -bool emberAfLevelControlClusterMoveToLevelCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterMoveToLevelCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::MoveToLevel::DecodableType & commandData) { MATTER_TRACE_SCOPE("MoveToLevel", "LevelControl"); @@ -629,8 +695,7 @@ chip::scenes::SceneHandler * GetSceneHandler() } // namespace LevelControlServer -bool emberAfLevelControlClusterMoveToLevelWithOnOffCallback(app::CommandHandler * commandObj, - const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterMoveToLevelWithOnOffCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::MoveToLevelWithOnOff::DecodableType & commandData) { MATTER_TRACE_SCOPE("MoveToLevelWithOnOff", "LevelControl"); @@ -660,7 +725,7 @@ bool emberAfLevelControlClusterMoveToLevelWithOnOffCallback(app::CommandHandler return true; } -bool emberAfLevelControlClusterMoveCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterMoveCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::Move::DecodableType & commandData) { MATTER_TRACE_SCOPE("Move", "LevelControl"); @@ -685,7 +750,7 @@ bool emberAfLevelControlClusterMoveCallback(app::CommandHandler * commandObj, co return true; } -bool emberAfLevelControlClusterMoveWithOnOffCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterMoveWithOnOffCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::MoveWithOnOff::DecodableType & commandData) { MATTER_TRACE_SCOPE("MoveWithOnOff", "LevelControl"); @@ -710,7 +775,7 @@ bool emberAfLevelControlClusterMoveWithOnOffCallback(app::CommandHandler * comma return true; } -bool emberAfLevelControlClusterStepCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterStepCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::Step::DecodableType & commandData) { MATTER_TRACE_SCOPE("Step", "LevelControl"); @@ -736,7 +801,7 @@ bool emberAfLevelControlClusterStepCallback(app::CommandHandler * commandObj, co return true; } -bool emberAfLevelControlClusterStepWithOnOffCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterStepWithOnOffCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::StepWithOnOff::DecodableType & commandData) { MATTER_TRACE_SCOPE("StepWithOnOff", "LevelControl"); @@ -762,7 +827,7 @@ bool emberAfLevelControlClusterStepWithOnOffCallback(app::CommandHandler * comma return true; } -bool emberAfLevelControlClusterStopCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterStopCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::Stop::DecodableType & commandData) { MATTER_TRACE_SCOPE("Stop", "LevelControl"); @@ -775,7 +840,7 @@ bool emberAfLevelControlClusterStopCallback(app::CommandHandler * commandObj, co return true; } -bool emberAfLevelControlClusterStopWithOnOffCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +bool emberAfLevelControlClusterStopWithOnOffCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::StopWithOnOff::DecodableType & commandData) { MATTER_TRACE_SCOPE("StopWithOnOff", "LevelControl"); @@ -788,12 +853,11 @@ bool emberAfLevelControlClusterStopWithOnOffCallback(app::CommandHandler * comma } static Status moveToLevelHandler(EndpointId endpoint, CommandId commandId, uint8_t level, - app::DataModel::Nullable transitionTimeDs, - chip::Optional> optionsMask, + DataModel::Nullable transitionTimeDs, chip::Optional> optionsMask, chip::Optional> optionsOverride, uint16_t storedLevel) { EmberAfLevelControlState * state = getState(endpoint); - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; uint8_t actualStepSize; if (state == nullptr) @@ -916,11 +980,9 @@ static Status moveToLevelHandler(EndpointId endpoint, CommandId commandId, uint8 // The duration between events will be the transition time divided by the // distance we must move. - state->eventDurationMs = state->transitionTimeMs / std::max(static_cast(1u), actualStepSize); - state->elapsedTimeMs = 0; - - state->storedLevel = storedLevel; - + state->eventDurationMs = state->transitionTimeMs / std::max(static_cast(1u), actualStepSize); + state->elapsedTimeMs = 0; + state->storedLevel = storedLevel; state->callbackSchedule.runTime = System::Clock::Milliseconds32(0); #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT @@ -946,14 +1008,14 @@ static Status moveToLevelHandler(EndpointId endpoint, CommandId commandId, uint8 return Status::Success; } -static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, MoveModeEnum moveMode, - app::DataModel::Nullable rate, chip::Optional> optionsMask, +static void moveHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, MoveModeEnum moveMode, + DataModel::Nullable rate, chip::Optional> optionsMask, chip::Optional> optionsOverride) { Status status; uint8_t difference; EmberAfLevelControlState * state; - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; EndpointId endpoint = commandPath.mEndpointId; CommandId commandId = commandPath.mCommandId; @@ -983,7 +1045,7 @@ static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCom // Otherwise, move as fast as possible if (rate.IsNull()) { - app::DataModel::Nullable defaultMoveRate; + DataModel::Nullable defaultMoveRate; status = Attributes::DefaultMoveRate::Get(endpoint, defaultMoveRate); if (status != Status::Success || defaultMoveRate.IsNull()) { @@ -1078,8 +1140,7 @@ static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCom state->elapsedTimeMs = 0; // storedLevel is not used for Move commands. - state->storedLevel = INVALID_STORED_LEVEL; - + state->storedLevel = INVALID_STORED_LEVEL; state->callbackSchedule.runTime = System::Clock::Milliseconds32(0); // The setup was successful, so mark the new state as active and return. @@ -1090,13 +1151,13 @@ static void moveHandler(app::CommandHandler * commandObj, const app::ConcreteCom commandObj->AddStatus(commandPath, status); } -static void stepHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, StepModeEnum stepMode, - uint8_t stepSize, app::DataModel::Nullable transitionTimeDs, +static void stepHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, StepModeEnum stepMode, + uint8_t stepSize, DataModel::Nullable transitionTimeDs, chip::Optional> optionsMask, chip::Optional> optionsOverride) { Status status; EmberAfLevelControlState * state; - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; EndpointId endpoint = commandPath.mEndpointId; CommandId commandId = commandPath.mCommandId; @@ -1226,8 +1287,7 @@ static void stepHandler(app::CommandHandler * commandObj, const app::ConcreteCom state->elapsedTimeMs = 0; // storedLevel is not used for Step commands - state->storedLevel = INVALID_STORED_LEVEL; - + state->storedLevel = INVALID_STORED_LEVEL; state->callbackSchedule.runTime = System::Clock::Milliseconds32(0); // The setup was successful, so mark the new state as active and return. @@ -1238,14 +1298,13 @@ static void stepHandler(app::CommandHandler * commandObj, const app::ConcreteCom commandObj->AddStatus(commandPath, status); } -static void stopHandler(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, +static void stopHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, chip::Optional> optionsMask, chip::Optional> optionsOverride) { - EndpointId endpoint = commandPath.mEndpointId; - CommandId commandId = commandPath.mCommandId; - + EndpointId endpoint = commandPath.mEndpointId; + CommandId commandId = commandPath.mCommandId; EmberAfLevelControlState * state = getState(endpoint); - Status status; + Status status = Status::Success; if (state == nullptr) { @@ -1255,14 +1314,13 @@ static void stopHandler(app::CommandHandler * commandObj, const app::ConcreteCom if (!shouldExecuteIfOff(endpoint, commandId, optionsMask, optionsOverride)) { - status = Status::Success; goto send_default_response; } // Cancel any currently active command. cancelEndpointTimerCallback(endpoint); + SetCurrentLevelQuietReport(endpoint, state, state->quietCurrentLevel.value(), true /*isStartOrEndOfTransition*/); writeRemainingTime(endpoint, 0); - status = Status::Success; send_default_response: commandObj->AddStatus(commandPath, status); @@ -1272,9 +1330,9 @@ static void stopHandler(app::CommandHandler * commandObj, const app::ConcreteCom // Quotes are from table 3.46. void emberAfOnOffClusterLevelControlEffectCallback(EndpointId endpoint, bool newValue) { - app::DataModel::Nullable resolvedLevel; - app::DataModel::Nullable temporaryCurrentLevelCache; - app::DataModel::Nullable transitionTime; + DataModel::Nullable resolvedLevel; + DataModel::Nullable temporaryCurrentLevelCache; + DataModel::Nullable transitionTime; uint16_t currentOnOffTransitionTime; Status status; @@ -1355,7 +1413,7 @@ void emberAfOnOffClusterLevelControlEffectCallback(EndpointId endpoint, bool new { // If newValue is OnOff::Commands::On::Id... // "Set CurrentLevel to minimum level allowed for the device." - status = Attributes::CurrentLevel::Set(endpoint, minimumLevelAllowedForTheDevice); + status = SetCurrentLevelQuietReport(endpoint, state, minimumLevelAllowedForTheDevice, true /*isStartOrEndOfTransition*/); if (status != Status::Success) { ChipLogProgress(Zcl, "ERR: reading current level %x", to_underlying(status)); @@ -1398,6 +1456,9 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) return; } + state->quietRemainingTime.policy() + .Set(QuieterReportingPolicyEnum::kMarkDirtyOnIncrement) + .Set(QuieterReportingPolicyEnum::kMarkDirtyOnChangeToFromZero); state->minLevel = MATTER_DM_PLUGIN_LEVEL_CONTROL_MINIMUM_LEVEL; state->maxLevel = MATTER_DM_PLUGIN_LEVEL_CONTROL_MAXIMUM_LEVEL; @@ -1419,7 +1480,7 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) } } - app::DataModel::Nullable currentLevel; + DataModel::Nullable currentLevel; Status status = Attributes::CurrentLevel::Get(endpoint, currentLevel); if (status == Status::Success) { @@ -1440,7 +1501,7 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) // 0xFF Work Around ZAP Can't set default value to NULL // https://github.com/project-chip/zap/issues/354 - app::DataModel::Nullable startUpCurrentLevel; + DataModel::Nullable startUpCurrentLevel; status = Attributes::StartUpCurrentLevel::Get(endpoint, startUpCurrentLevel); if (status == Status::Success) { @@ -1470,25 +1531,24 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) } } // Otherwise Set the CurrentLevel attribute to its previous value which was already fetch above - - Attributes::CurrentLevel::Set(endpoint, currentLevel); + SetCurrentLevelQuietReport(endpoint, state, currentLevel, true /*isStartOrEndOfTransition*/); } } #endif // IGNORE_LEVEL_CONTROL_CLUSTER_START_UP_CURRENT_LEVEL // In any case, we make sure that the respects min/max if (currentLevel.IsNull() || currentLevel.Value() < state->minLevel) { - Attributes::CurrentLevel::Set(endpoint, state->minLevel); + SetCurrentLevelQuietReport(endpoint, state, state->minLevel, true /*isStartOrEndOfTransition*/); } else if (currentLevel.Value() > state->maxLevel) { - Attributes::CurrentLevel::Set(endpoint, state->maxLevel); + SetCurrentLevelQuietReport(endpoint, state, state->maxLevel, true /*isStartOrEndOfTransition*/); } } #if defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS // Registers Scene handlers for the level control cluster on the server - app::Clusters::ScenesManagement::ScenesServer::Instance().RegisterSceneHandler(endpoint, LevelControlServer::GetSceneHandler()); + Clusters::ScenesManagement::ScenesServer::Instance().RegisterSceneHandler(endpoint, LevelControlServer::GetSceneHandler()); #endif // defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS emberAfPluginLevelControlClusterServerPostInitCallback(endpoint); From 51650bfc45e8900bbb01116d611ea11776a971ac Mon Sep 17 00:00:00 2001 From: C Freeman Date: Thu, 25 Jul 2024 15:15:56 -0400 Subject: [PATCH 14/30] Revert thermostat stuff breaking tot (#34518) * Revert "update tests and thermostat server cluster for new constraints for LocalTemperatureCalibration and MinSetpointDeadBand (#34474)" This reverts commit 335ac96cdde78fd81d69d6fb451427e769b7f413. * Revert "update constraints for LocalTemperatureCalibration and MinSetpointDeadBand attributes (#34473)" This reverts commit 21a5bd6a402b699c0d64283918e266092a771bd1. --- .../all-clusters-app/app-templates/endpoint_config.h | 2 +- src/app/clusters/thermostat-server/thermostat-server.cpp | 2 +- src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml | 6 +++--- .../zcl/data-model/chip/thermostat-cluster.xml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h index a77bd74f11f267..f2c082b1d0d4a1 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h @@ -307,7 +307,7 @@ { (uint16_t) 0xBB8, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MaxHeatSetpointLimit */ \ { (uint16_t) 0x640, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MinCoolSetpointLimit */ \ { (uint16_t) 0xC80, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MaxCoolSetpointLimit */ \ - { (uint16_t) 0x19, (uint16_t) 0x0, (uint16_t) 0x7F }, /* MinSetpointDeadBand */ \ + { (uint16_t) 0x19, (uint16_t) 0x0, (uint16_t) 0x19 }, /* MinSetpointDeadBand */ \ { (uint16_t) 0x4, (uint16_t) 0x0, (uint16_t) 0x5 }, /* ControlSequenceOfOperation */ \ { (uint16_t) 0x1, (uint16_t) 0x0, (uint16_t) 0x9 }, /* SystemMode */ \ \ diff --git a/src/app/clusters/thermostat-server/thermostat-server.cpp b/src/app/clusters/thermostat-server/thermostat-server.cpp index e802a9201f33dc..c650a30c871989 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server.cpp @@ -409,7 +409,7 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr requested = *value; if (!AutoSupported) return imcode::UnsupportedAttribute; - if (requested < 0 || requested > 127) + if (requested < 0 || requested > 25) return imcode::InvalidValue; return imcode::Success; } diff --git a/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml b/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml index 4558b08745a949..faf34fdea43e92 100644 --- a/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_TSTAT_2_1.yaml @@ -243,8 +243,8 @@ tests: response: constraints: type: int8s - minValue: -127 - maxValue: 127 + minValue: -25 + maxValue: 25 - label: "Step 13a: TH reads attribute OccupiedCoolingSetpoint from the DUT" PICS: TSTAT.S.F01 && TSTAT.S.A0017 && TSTAT.S.A0018 @@ -426,7 +426,7 @@ tests: constraints: type: int8s minValue: 0 - maxValue: 127 + maxValue: 25 - label: "Step 22: TH reads the RemoteSensing attribute from the DUT" PICS: TSTAT.S.A001a diff --git a/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml index c2979487e45ae3..91a89497d7c8cc 100644 --- a/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/thermostat-cluster.xml @@ -337,7 +337,7 @@ limitations under the License. - + LocalTemperatureCalibration @@ -361,7 +361,7 @@ limitations under the License. MaxCoolSetpointLimit - + MinSetpointDeadBand From a13bd221c01de44bbbcf4701a77ef0209065a13b Mon Sep 17 00:00:00 2001 From: Alex Tsitsiura Date: Thu, 25 Jul 2024 22:25:54 +0300 Subject: [PATCH 15/30] [Telink] Update Docker image (Zephyr update) (#34503) --- integrations/docker/images/base/chip-build/version | 2 +- integrations/docker/images/stage-2/chip-build-telink/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index 8a1624d292e305..ba77442b28dc23 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -65 : [nrfconnect] Update nRF Connect SDK version. +66 : [Telink] Update Docker image (Zephyr update) diff --git a/integrations/docker/images/stage-2/chip-build-telink/Dockerfile b/integrations/docker/images/stage-2/chip-build-telink/Dockerfile index 66d5eccdc0d3a1..bea580245e8639 100644 --- a/integrations/docker/images/stage-2/chip-build-telink/Dockerfile +++ b/integrations/docker/images/stage-2/chip-build-telink/Dockerfile @@ -18,7 +18,7 @@ RUN set -x \ && : # last line # Setup Zephyr -ARG ZEPHYR_REVISION=ab81a585fca6a83b30e1f4e58a021113d6a3acb8 +ARG ZEPHYR_REVISION=ef7bfc2214602ecf237332b71e6a2bdd69205fbd WORKDIR /opt/telink/zephyrproject RUN set -x \ && python3 -m pip install --break-system-packages -U --no-cache-dir west \ From 66c416e0a59aeeb6fc56817baea7b071c2bc5cd1 Mon Sep 17 00:00:00 2001 From: Pradip De Date: Thu, 25 Jul 2024 12:46:52 -0700 Subject: [PATCH 16/30] Add TransportPayloadCapability flag for GetConnectedDevices and bubble (#34450) up the flag to the wrapper IM Python APIs. Add python script binding methods for LargePayload tests --to check if session allows large payload. --to close the underlying TCP connection. --to check if the session is active. --- .../ChipDeviceController-ScriptBinding.cpp | 61 +++++++++++- src/controller/python/chip/ChipDeviceCtrl.py | 99 +++++++++++++++---- src/python_testing/matter_testing_support.py | 6 +- 3 files changed, 144 insertions(+), 22 deletions(-) diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp index d4603efe05e469..d9b557bb62be67 100644 --- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp +++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp @@ -213,7 +213,8 @@ PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::Devic const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode); PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, - chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback); + chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback, + int transportPayloadCapability); PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy); PyChipError pychip_GetLocalSessionId(chip::OperationalDeviceProxy * deviceProxy, uint16_t * localSessionId); PyChipError pychip_GetNumSessionsToPeer(chip::OperationalDeviceProxy * deviceProxy, uint32_t * numSessions); @@ -239,6 +240,13 @@ void pychip_Storage_ShutdownAdapter(chip::Controller::Python::StorageAdapter * s // ICD // void pychip_CheckInDelegate_SetOnCheckInCompleteCallback(PyChipCheckInDelegate::OnCheckInCompleteCallback * callback); + +// +// LargePayload and TCP +PyChipError pychip_SessionAllowsLargePayload(chip::OperationalDeviceProxy * deviceProxy, bool * allowsLargePayload); +PyChipError pychip_IsSessionOverTCPConnection(chip::OperationalDeviceProxy * deviceProxy, bool * isSessionOverTCP); +PyChipError pychip_IsActiveSession(chip::OperationalDeviceProxy * deviceProxy, bool * isActiveSession); +PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy); } void * pychip_Storage_InitializeStorageAdapter(chip::Controller::Python::PyObject * context, @@ -807,11 +815,58 @@ struct GetDeviceCallbacks } // anonymous namespace PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId, - chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback) + chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback, + int transportPayloadCapability) { VerifyOrReturnError(devCtrl != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); auto * callbacks = new GetDeviceCallbacks(context, callback); - return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure)); + return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure, + static_cast(transportPayloadCapability))); +} + +PyChipError pychip_SessionAllowsLargePayload(chip::OperationalDeviceProxy * deviceProxy, bool * allowsLargePayload) +{ + VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); + VerifyOrReturnError(allowsLargePayload != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + + *allowsLargePayload = deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload(); + + return ToPyChipError(CHIP_NO_ERROR); +} + +PyChipError pychip_IsSessionOverTCPConnection(chip::OperationalDeviceProxy * deviceProxy, bool * isSessionOverTCP) +{ + VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); + VerifyOrReturnError(isSessionOverTCP != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + + *isSessionOverTCP = deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection() != nullptr; + + return ToPyChipError(CHIP_NO_ERROR); +} + +PyChipError pychip_IsActiveSession(chip::OperationalDeviceProxy * deviceProxy, bool * isActiveSession) +{ + VerifyOrReturnError(isActiveSession != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + + *isActiveSession = false; + if (deviceProxy->GetSecureSession().HasValue()) + { + *isActiveSession = deviceProxy->GetSecureSession().Value()->AsSecureSession()->IsActiveSession(); + } + + return ToPyChipError(CHIP_NO_ERROR); +} + +PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy) +{ + VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); + VerifyOrReturnError(deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload(), + ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + + deviceProxy->GetExchangeManager()->GetSessionManager()->TCPDisconnect( + deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection(), /* shouldAbort = */ false); + + return ToPyChipError(CHIP_NO_ERROR); } PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy) diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py index 0e568803b1e523..91c83e8954fa37 100644 --- a/src/controller/python/chip/ChipDeviceCtrl.py +++ b/src/controller/python/chip/ChipDeviceCtrl.py @@ -84,6 +84,16 @@ _ChipDeviceController_IterateDiscoveredCommissionableNodesFunct = CFUNCTYPE(None, c_char_p, c_size_t) +# Defines for the transport payload types to use to select the suitable +# underlying transport of the session. +# class TransportPayloadCapability(ctypes.c_int): + + +class TransportPayloadCapability(ctypes.c_int): + MRP_PAYLOAD = 0 + LARGE_PAYLOAD = 1 + MRP_OR_TCP_PAYLOAD = 2 + @dataclass class CommissioningParameters: @@ -371,6 +381,53 @@ def attestationChallenge(self) -> bytes: return bytes(buf) + @property + def sessionAllowsLargePayload(self) -> bool: + self._dmLib.pychip_SessionAllowsLargePayload.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)] + self._dmLib.pychip_SessionAllowsLargePayload.restype = PyChipError + + supportsLargePayload = ctypes.c_bool(False) + + builtins.chipStack.Call( + lambda: self._dmLib.pychip_SessionAllowsLargePayload(self._deviceProxy, pointer(supportsLargePayload)) + ).raise_on_error() + + return supportsLargePayload.value + + @property + def isSessionOverTCPConnection(self) -> bool: + self._dmLib.pychip_IsSessionOverTCPConnection.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)] + self._dmLib.pychip_IsSessionOverTCPConnection.restype = PyChipError + + isSessionOverTCP = ctypes.c_bool(False) + + builtins.chipStack.Call( + lambda: self._dmLib.pychip_IsSessionOverTCPConnection(self._deviceProxy, pointer(isSessionOverTCP)) + ).raise_on_error() + + return isSessionOverTCP.value + + @property + def isActiveSession(self) -> bool: + self._dmLib.pychip_IsActiveSession.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)] + self._dmLib.pychip_IsActiveSession.restype = PyChipError + + isActiveSession = ctypes.c_bool(False) + + builtins.chipStack.Call( + lambda: self._dmLib.pychip_IsActiveSession(self._deviceProxy, pointer(isActiveSession)) + ).raise_on_error() + + return isActiveSession.value + + def closeTCPConnectionWithPeer(self): + self._dmLib.pychip_CloseTCPConnectionWithPeer.argtypes = [ctypes.c_void_p] + self._dmLib.pychip_CloseTCPConnectionWithPeer.restype = PyChipError + + builtins.chipStack.Call( + lambda: self._dmLib.pychip_CloseTCPConnectionWithPeer(self._deviceProxy) + ).raise_on_error() + DiscoveryFilterType = discovery.FilterType DiscoveryType = discovery.DiscoveryType @@ -906,7 +963,7 @@ async def FindOrEstablishPASESession(self, setupCode: str, nodeid: int, timeoutM if res.is_success: return DeviceProxyWrapper(returnDevice, DeviceProxyWrapper.DeviceProxyType.COMMISSIONEE, self._dmLib) - def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None): + def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None, payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node. nodeId: Target's Node ID @@ -943,7 +1000,7 @@ def deviceAvailable(self, device, err): closure = DeviceAvailableClosure() ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure)) self._ChipStack.Call(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId( - self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback), + self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability), timeoutMs).raise_on_error() # The callback might have been received synchronously (during self._ChipStack.Call()). @@ -975,7 +1032,8 @@ async def WaitForActive(self, nodeid, *, timeoutSeconds=30.0, stayActiveDuration await WaitForCheckIn(ScopedNodeId(nodeid, self._fabricIndex), timeoutSeconds=timeoutSeconds) return await self.SendCommand(nodeid, 0, Clusters.IcdManagement.Commands.StayActiveRequest(stayActiveDuration=stayActiveDurationMs)) - async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None): + async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node. nodeId: Target's Node ID @@ -1020,7 +1078,7 @@ def deviceAvailable(self, device, err): closure = DeviceAvailableClosure(eventLoop, future) ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure)) await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId( - self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback), + self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability), timeoutMs) # The callback might have been received synchronously (during self._ChipStack.CallAsync()). @@ -1124,7 +1182,8 @@ async def TestOnlySendCommandTimedRequestFlagWithNoTimedInvoke(self, nodeid: int async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.ClusterCommand, responseType=None, timedRequestTimeoutMs: typing.Union[None, int] = None, interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None, - suppressResponse: typing.Union[None, bool] = None): + suppressResponse: typing.Union[None, bool] = None, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Send a cluster-object encapsulated command to a node and get returned a future that can be awaited upon to receive the response. If a valid responseType is passed in, that will be used to de-serialize the object. If not, @@ -1144,7 +1203,7 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects. eventLoop = asyncio.get_running_loop() future = eventLoop.create_future() - device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs) + device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability) res = await ClusterCommand.SendCommand( future, eventLoop, responseType, device.deviceProxy, ClusterCommand.CommandPath( EndpointId=endpoint, @@ -1158,7 +1217,8 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects. async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo], timedRequestTimeoutMs: typing.Optional[int] = None, interactionTimeoutMs: typing.Optional[int] = None, busyWaitMs: typing.Optional[int] = None, - suppressResponse: typing.Optional[bool] = None): + suppressResponse: typing.Optional[bool] = None, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Send a batch of cluster-object encapsulated commands to a node and get returned a future that can be awaited upon to receive the responses. If a valid responseType is passed in, that will be used to de-serialize the object. If not, @@ -1186,7 +1246,7 @@ async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterComm eventLoop = asyncio.get_running_loop() future = eventLoop.create_future() - device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs) + device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability) res = await ClusterCommand.SendBatchCommands( future, eventLoop, device.deviceProxy, commands, @@ -1215,7 +1275,8 @@ def SendGroupCommand(self, groupid: int, payload: ClusterObjects.ClusterCommand, async def WriteAttribute(self, nodeid: int, attributes: typing.List[typing.Tuple[int, ClusterObjects.ClusterAttributeDescriptor]], timedRequestTimeoutMs: typing.Union[None, int] = None, - interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None): + interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Write a list of attributes on a target node. @@ -1237,7 +1298,7 @@ async def WriteAttribute(self, nodeid: int, eventLoop = asyncio.get_running_loop() future = eventLoop.create_future() - device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs) + device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability) attrs = [] for v in attributes: @@ -1396,7 +1457,8 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[ ]] = None, eventNumberFilter: typing.Optional[int] = None, returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None, - fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True): + fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Read a list of attributes and/or events from a target node @@ -1456,7 +1518,7 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[ eventLoop = asyncio.get_running_loop() future = eventLoop.create_future() - device = await self.GetConnectedDevice(nodeid) + device = await self.GetConnectedDevice(nodeid, payloadCapability=payloadCapability) attributePaths = [self._parseAttributePathTuple( v) for v in attributes] if attributes else None clusterDataVersionFilters = [self._parseDataVersionFilterTuple( @@ -1487,7 +1549,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[ ]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None, returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None, - fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True): + fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Read a list of attributes from a target node, this is a wrapper of DeviceController.Read() @@ -1547,7 +1610,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[ reportInterval=reportInterval, fabricFiltered=fabricFiltered, keepSubscriptions=keepSubscriptions, - autoResubscribe=autoResubscribe) + autoResubscribe=autoResubscribe, + payloadCapability=payloadCapability) if isinstance(res, ClusterAttribute.SubscriptionTransaction): return res else: @@ -1569,7 +1633,8 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[ fabricFiltered: bool = True, reportInterval: typing.Tuple[int, int] = None, keepSubscriptions: bool = False, - autoResubscribe: bool = True): + autoResubscribe: bool = True, + payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD): ''' Read a list of events from a target node, this is a wrapper of DeviceController.Read() @@ -1616,7 +1681,7 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[ ''' res = await self.Read(nodeid=nodeid, events=events, eventNumberFilter=eventNumberFilter, fabricFiltered=fabricFiltered, reportInterval=reportInterval, keepSubscriptions=keepSubscriptions, - autoResubscribe=autoResubscribe) + autoResubscribe=autoResubscribe, payloadCapability=payloadCapability) if isinstance(res, ClusterAttribute.SubscriptionTransaction): return res else: @@ -1764,7 +1829,7 @@ def _InitLib(self): self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete.restype = PyChipError self._dmLib.pychip_GetConnectedDeviceByNodeId.argtypes = [ - c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct] + c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct, c_int] self._dmLib.pychip_GetConnectedDeviceByNodeId.restype = PyChipError self._dmLib.pychip_FreeOperationalDeviceProxy.argtypes = [ diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index e3c8683acd52ae..11b1dfb01c0760 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -928,7 +928,8 @@ async def read_single_attribute_expect_error( async def send_single_cmd( self, cmd: Clusters.ClusterObjects.ClusterCommand, dev_ctrl: ChipDeviceCtrl = None, node_id: int = None, endpoint: int = None, - timedRequestTimeoutMs: typing.Union[None, int] = None) -> object: + timedRequestTimeoutMs: typing.Union[None, int] = None, + payloadCapability: int = ChipDeviceCtrl.TransportPayloadCapability.MRP_PAYLOAD) -> object: if dev_ctrl is None: dev_ctrl = self.default_controller if node_id is None: @@ -936,7 +937,8 @@ async def send_single_cmd( if endpoint is None: endpoint = self.matter_test_config.endpoint - result = await dev_ctrl.SendCommand(nodeid=node_id, endpoint=endpoint, payload=cmd, timedRequestTimeoutMs=timedRequestTimeoutMs) + result = await dev_ctrl.SendCommand(nodeid=node_id, endpoint=endpoint, payload=cmd, timedRequestTimeoutMs=timedRequestTimeoutMs, + payloadCapability=payloadCapability) return result async def send_test_event_triggers(self, eventTrigger: int, enableKey: bytes = None): From d59cb0234994bc8c3f3b3cd346023352b97e45dd Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 25 Jul 2024 16:03:15 -0400 Subject: [PATCH 17/30] Remove no-longer-used MTRDevice logic for truncating data version lists (#34183) * Remove no-longer-used MTRDevice logic for truncating data version lists After https://github.com/project-chip/connectedhomeip/pull/34111, ReadClient handles this logic itself, so the attempted truncation in MTRDevice was now dead code. * Address review comment. * Fix compile issues. * Address another review comment. * Address review comment. --- src/app/ReadClient.cpp | 22 ++++- src/darwin/Framework/CHIP/MTRDevice.mm | 132 ++++++++++--------------- 2 files changed, 74 insertions(+), 80 deletions(-) diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index 470c875fbaac30..51adf6e8ddc143 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -403,6 +403,11 @@ CHIP_ERROR ReadClient::BuildDataVersionFilterList(DataVersionFilterIBs::Builder const Span & aDataVersionFilters, bool & aEncodedDataVersionList) { +#if CHIP_PROGRESS_LOGGING + size_t encodedFilterCount = 0; + size_t irrelevantFilterCount = 0; + size_t skippedFilterCount = 0; +#endif for (auto & filter : aDataVersionFilters) { VerifyOrReturnError(filter.IsValidDataVersionFilter(), CHIP_ERROR_INVALID_ARGUMENT); @@ -420,6 +425,9 @@ CHIP_ERROR ReadClient::BuildDataVersionFilterList(DataVersionFilterIBs::Builder if (!intersected) { +#if CHIP_PROGRESS_LOGGING + ++irrelevantFilterCount; +#endif continue; } @@ -428,19 +436,31 @@ CHIP_ERROR ReadClient::BuildDataVersionFilterList(DataVersionFilterIBs::Builder CHIP_ERROR err = EncodeDataVersionFilter(aDataVersionFilterIBsBuilder, filter); if (err == CHIP_NO_ERROR) { +#if CHIP_PROGRESS_LOGGING + ++encodedFilterCount; +#endif aEncodedDataVersionList = true; } else if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL) { // Packet is full, ignore the rest of the list aDataVersionFilterIBsBuilder.Rollback(backup); - return CHIP_NO_ERROR; +#if CHIP_PROGRESS_LOGGING + ssize_t nonSkippedFilterCount = &filter - aDataVersionFilters.data(); + skippedFilterCount = aDataVersionFilters.size() - static_cast(nonSkippedFilterCount); +#endif // CHIP_PROGRESS_LOGGING + break; } else { return err; } } + + ChipLogProgress(DataManagement, + "%lu data version filters provided, %lu not relevant, %lu encoded, %lu skipped due to lack of space", + static_cast(aDataVersionFilters.size()), static_cast(irrelevantFilterCount), + static_cast(encodedFilterCount), static_cast(skippedFilterCount)); return CHIP_NO_ERROR; } diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 1c68d788943ce2..b8ef8694825e1f 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -2277,32 +2277,26 @@ - (void)_removeCachedAttribute:(NSNumber *)attributeID fromCluster:(MTRClusterPa [clusterData removeValueForAttribute:attributeID]; } -- (void)_createDataVersionFilterListFromDictionary:(NSDictionary *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count sizeReduction:(size_t)sizeReduction +- (void)_createDataVersionFilterListFromDictionary:(NSDictionary *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count { - size_t maxDataVersionFilterSize = dataVersions.count; + size_t dataVersionFilterSize = dataVersions.count; // Check if any filter list should be generated - if (!dataVersions.count || (maxDataVersionFilterSize <= sizeReduction)) { + if (dataVersionFilterSize == 0) { *count = 0; *dataVersionFilterList = nullptr; return; } - maxDataVersionFilterSize -= sizeReduction; - DataVersionFilter * dataVersionFilterArray = new DataVersionFilter[maxDataVersionFilterSize]; + DataVersionFilter * dataVersionFilterArray = new DataVersionFilter[dataVersionFilterSize]; size_t i = 0; for (MTRClusterPath * path in dataVersions) { NSNumber * dataVersionNumber = dataVersions[path]; - if (dataVersionNumber) { - dataVersionFilterArray[i++] = DataVersionFilter(static_cast(path.endpoint.unsignedShortValue), static_cast(path.cluster.unsignedLongValue), static_cast(dataVersionNumber.unsignedLongValue)); - } - if (i == maxDataVersionFilterSize) { - break; - } + dataVersionFilterArray[i++] = DataVersionFilter(static_cast(path.endpoint.unsignedShortValue), static_cast(path.cluster.unsignedLongValue), static_cast(dataVersionNumber.unsignedLongValue)); } *dataVersionFilterList = dataVersionFilterArray; - *count = maxDataVersionFilterSize; + *count = dataVersionFilterSize; } - (void)_setupConnectivityMonitoring @@ -2530,75 +2524,55 @@ - (void)_setupSubscriptionWithReason:(NSString *)reason auto readClient = std::make_unique(InteractionModelEngine::GetInstance(), exchangeManager, clusterStateCache->GetBufferedCallback(), ReadClient::InteractionType::Subscribe); - // Subscribe with data version filter list and retry with smaller list if out of packet space - CHIP_ERROR err; - NSDictionary * dataVersions = [self _getCachedDataVersions]; - size_t dataVersionFilterListSizeReduction = 0; - for (;;) { - // Wildcard endpoint, cluster, attribute, event. - auto attributePath = std::make_unique(); - auto eventPath = std::make_unique(); - // We want to get event reports at the minInterval, not the maxInterval. - eventPath->mIsUrgentEvent = true; - ReadPrepareParams readParams(session.Value()); - - readParams.mMinIntervalFloorSeconds = 0; - // Select a max interval based on the device's claimed idle sleep interval. - auto idleSleepInterval = std::chrono::duration_cast( - session.Value()->GetRemoteMRPConfig().mIdleRetransTimeout); - - auto maxIntervalCeilingMin = System::Clock::Seconds32(MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MIN); - if (idleSleepInterval < maxIntervalCeilingMin) { - idleSleepInterval = maxIntervalCeilingMin; - } - - auto maxIntervalCeilingMax = System::Clock::Seconds32(MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MAX); - if (idleSleepInterval > maxIntervalCeilingMax) { - idleSleepInterval = maxIntervalCeilingMax; - } + // Wildcard endpoint, cluster, attribute, event. + auto attributePath = std::make_unique(); + auto eventPath = std::make_unique(); + // We want to get event reports at the minInterval, not the maxInterval. + eventPath->mIsUrgentEvent = true; + ReadPrepareParams readParams(session.Value()); + + readParams.mMinIntervalFloorSeconds = 0; + // Select a max interval based on the device's claimed idle sleep interval. + auto idleSleepInterval = std::chrono::duration_cast( + session.Value()->GetRemoteMRPConfig().mIdleRetransTimeout); + + auto maxIntervalCeilingMin = System::Clock::Seconds32(MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MIN); + if (idleSleepInterval < maxIntervalCeilingMin) { + idleSleepInterval = maxIntervalCeilingMin; + } + + auto maxIntervalCeilingMax = System::Clock::Seconds32(MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MAX); + if (idleSleepInterval > maxIntervalCeilingMax) { + idleSleepInterval = maxIntervalCeilingMax; + } #ifdef DEBUG - if (maxIntervalOverride.HasValue()) { - idleSleepInterval = maxIntervalOverride.Value(); - } -#endif - readParams.mMaxIntervalCeilingSeconds = static_cast(idleSleepInterval.count()); - - readParams.mpAttributePathParamsList = attributePath.get(); - readParams.mAttributePathParamsListSize = 1; - readParams.mpEventPathParamsList = eventPath.get(); - readParams.mEventPathParamsListSize = 1; - readParams.mKeepSubscriptions = true; - readParams.mIsFabricFiltered = false; - size_t dataVersionFilterListSize = 0; - DataVersionFilter * dataVersionFilterList; - [self _createDataVersionFilterListFromDictionary:dataVersions dataVersionFilterList:&dataVersionFilterList count:&dataVersionFilterListSize sizeReduction:dataVersionFilterListSizeReduction]; - readParams.mDataVersionFilterListSize = dataVersionFilterListSize; - readParams.mpDataVersionFilterList = dataVersionFilterList; - attributePath.release(); - eventPath.release(); - - // TODO: Change from local filter list generation to rehydrating ClusterStateCache ot take advantage of existing filter list sorting algorithm - - // SendAutoResubscribeRequest cleans up the params, even on failure. - err = readClient->SendAutoResubscribeRequest(std::move(readParams)); - if (err == CHIP_NO_ERROR) { - break; - } - - // If error is not a "no memory" issue, then break and go through regular resubscribe logic - if (err != CHIP_ERROR_NO_MEMORY) { - break; - } - - // If "no memory" error is not caused by data version filter list, break as well - if (!dataVersionFilterListSize) { - break; - } - - // Now "no memory" could mean subscribe request packet space ran out. Reduce size and try again immediately - dataVersionFilterListSizeReduction++; + if (maxIntervalOverride.HasValue()) { + idleSleepInterval = maxIntervalOverride.Value(); } +#endif + readParams.mMaxIntervalCeilingSeconds = static_cast(idleSleepInterval.count()); + + readParams.mpAttributePathParamsList = attributePath.get(); + readParams.mAttributePathParamsListSize = 1; + readParams.mpEventPathParamsList = eventPath.get(); + readParams.mEventPathParamsListSize = 1; + readParams.mKeepSubscriptions = true; + readParams.mIsFabricFiltered = false; + + // Subscribe with data version filter list from our cache. + size_t dataVersionFilterListSize = 0; + DataVersionFilter * dataVersionFilterList; + [self _createDataVersionFilterListFromDictionary:[self _getCachedDataVersions] dataVersionFilterList:&dataVersionFilterList count:&dataVersionFilterListSize]; + + readParams.mDataVersionFilterListSize = dataVersionFilterListSize; + readParams.mpDataVersionFilterList = dataVersionFilterList; + attributePath.release(); + eventPath.release(); + + // TODO: Change from local filter list generation to rehydrating ClusterStateCache to take advantage of existing filter list sorting algorithm + // SendAutoResubscribeRequest cleans up the params, even on failure. + CHIP_ERROR err = readClient->SendAutoResubscribeRequest(std::move(readParams)); if (err != CHIP_NO_ERROR) { NSError * error = [MTRError errorForCHIPErrorCode:err logContext:self]; MTR_LOG_ERROR("%@ SendAutoResubscribeRequest error %@", self, error); @@ -2610,7 +2584,7 @@ - (void)_setupSubscriptionWithReason:(NSString *)reason return; } - MTR_LOG("%@ Subscribe with data version list size %lu, reduced by %lu", self, static_cast(dataVersions.count), static_cast(dataVersionFilterListSizeReduction)); + MTR_LOG("%@ Subscribe with data version list size %lu", self, static_cast(dataVersionFilterListSize)); // Callback and ClusterStateCache and ReadClient will be deleted // when OnDone is called. From 0f9802457bc4e7765942491163bac61b45f21c41 Mon Sep 17 00:00:00 2001 From: pcoleman Date: Fri, 26 Jul 2024 07:22:41 +0100 Subject: [PATCH 18/30] Address review comments by JamesH --- .../src/WhmDelegateImpl.cpp | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index a38a1d46c8a6b7..7460d7f0112fb5 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -29,7 +29,8 @@ using namespace chip::app::Clusters::WaterHeaterManagement; using Protocols::InteractionModel::Status; WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clustersEndpoint) : - mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mBoostTargetTemperatureReached(false), mTankVolume(0), + mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mHotWaterTemperature(0), mReplacedWaterTemperature(0), + mBoostTargetTemperatureReached(false), mTankVolume(0), mEstimatedHeatRequired(0), mTankPercentage(0), mBoostState(BoostStateEnum::kInactive) {} @@ -336,7 +337,9 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const uint16_t targetTemperature = (mBoostState == BoostStateEnum::kActive && mBoostTemporarySetpoint.HasValue()) ? static_cast(mBoostTemporarySetpoint.Value()) : mTargetWaterTemperature; - uint8_t targetPercentage; + + uint8_t targetPercentage = 0; + bool useTankPercentage = false; if (mBoostState == BoostStateEnum::kActive && mBoostTargetTemperatureReached && mBoostTargetReheat.HasValue()) { @@ -352,23 +355,27 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const // If this field is included then the TargetPercentage field SHALL also be included, and the OneShot excluded. targetPercentage = mBoostTargetReheat.Value(); + + useTankPercentage = true; + } + else if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent) && mBoostTargetPercentage.HasValue()) + { + // If tank percentage is supported AND the targetPercentage.HasValue() then use target percentage to heat up. + targetPercentage = mBoostTargetPercentage.Value(); + + useTankPercentage = true; } else { - // Determine the target %. If a boost command is in progress and has a mBoostTargetPercentage value use that as the target - // %, otherwise 100% of the water in the tank must be at the target temperature - targetPercentage = - (mBoostState == BoostStateEnum::kActive && mBoostTargetPercentage.HasValue()) ? mBoostTargetPercentage.Value() : 100; + // Don't support tankPercentage OR the targetPercent wasn't included then just rely on temperature alone + useTankPercentage = false; } // Determine whether the water is at the target temperature bool tempReached = true; - if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) + if (useTankPercentage && mTankPercentage < targetPercentage) { - if (mTankPercentage < targetPercentage) - { - tempReached = false; - } + tempReached = false; } if (mHotWaterTemperature < targetTemperature) From 5b7455df94f8f5426a2e4bc424d49c188035548c Mon Sep 17 00:00:00 2001 From: pcoleman Date: Fri, 26 Jul 2024 10:29:46 +0100 Subject: [PATCH 19/30] Remove unnecessary include file --- .../include/WhmAppCmdLineOptions.h | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100755 examples/all-clusters-app/all-clusters-common/include/WhmAppCmdLineOptions.h diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmAppCmdLineOptions.h b/examples/all-clusters-app/all-clusters-common/include/WhmAppCmdLineOptions.h deleted file mode 100755 index 1f995c78503035..00000000000000 --- a/examples/all-clusters-app/all-clusters-common/include/WhmAppCmdLineOptions.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * Copyright (c) 2024 Project CHIP Authors - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace chip { -namespace app { -namespace Clusters { -namespace WaterHeaterManagement { - -chip::BitMask GetFeatureMapFromCmdLine(); - -} // namespace WaterHeaterManagement -} // namespace Clusters -} // namespace app -} // namespace chip From 5f5fbff30180f0f4693ee1839582a76db8fcb6ea Mon Sep 17 00:00:00 2001 From: pcoleman Date: Sun, 28 Jul 2024 08:43:27 +0100 Subject: [PATCH 20/30] Address further review comments from JamhesH --- .../all-clusters-common/include/WhmDelegate.h | 4 +- .../include/WhmManufacturer.h | 23 ++- .../src/WhmDelegateImpl.cpp | 140 +++++++----------- .../src/WhmManufacturer.cpp | 14 +- 4 files changed, 78 insertions(+), 103 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h index 5ac36244f4e8cf..91ece3d19a6e4e 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -116,7 +116,7 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate * * @param mode The Water Heater Mode (e.g. OFF, MANUAL or TIMED). */ - void SetWaterHeaterMode(uint8_t mode); + Protocols::InteractionModel::Status SetWaterHeaterMode(uint8_t mode); /** * @brief Set the water temperature of the tank @@ -179,7 +179,7 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate uint16_t mTargetWaterTemperature; // Actual water temperature in 100ths of a C - uint16_t mHotWaterTemperature; + uint16_t mWaterTemperature; // The % of water at temperature mReplacedWaterTemperature uint16_t mReplacedWaterTemperature; diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h index 09d1dff17a9c27..f953e7f554dab5 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h @@ -66,11 +66,17 @@ class WhmManufacturer /** * @brief Turn the heating of the water tank on. + * + * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause + * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). + * @return An error if a problem occurs turning the heating on */ - Protocols::InteractionModel::Status TurnHeatingOn(); + Protocols::InteractionModel::Status TurnHeatingOn(bool emergencyBoost); /** * @brief Turn the heating of the water tank off. + * + * @return An error if a problem occurs turning the heating off */ Protocols::InteractionModel::Status TurnHeatingOff(); @@ -78,18 +84,19 @@ class WhmManufacturer * @brief Called to handle a boost command. * * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts - * to the previous mode (e.g. OFF, MANUAL or TIMED). + * to the previous mode (e.g. OFF, MANUAL or TIMED). * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the - * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). + * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause - * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). + * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be - * used instead of the normal set point temperature whilst the BOOST state is active. + * used instead of the normal set point temperature whilst the BOOST state is active. * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be - * heated by this Boost command before the heater is switched off. + * heated by this Boost command before the heater is switched off. * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because - * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field - * indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. + * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), + * this field indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again + * beginning to reheat it. * * @return Success if the boost command is successful; otherwise return the appropriate error. */ diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 6969b071294976..67ea727af91e11 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -29,7 +29,7 @@ using namespace chip::app::Clusters::WaterHeaterManagement; using Protocols::InteractionModel::Status; WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clustersEndpoint) : - mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mHotWaterTemperature(0), mReplacedWaterTemperature(0), + mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mWaterTemperature(0), mReplacedWaterTemperature(0), mBoostTargetTemperatureReached(false), mTankVolume(0), mEstimatedHeatRequired(0), mTankPercentage(0), mBoostState(BoostStateEnum::kInactive) {} @@ -253,6 +253,7 @@ Status WaterHeaterManagementDelegate::HandleCancelBoost() if (mBoostState == BoostStateEnum::kActive) { SetBoostState(BoostStateEnum::kInactive); + mBoostEmergencyBoost.ClearValue(); DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); @@ -272,12 +273,6 @@ Status WaterHeaterManagementDelegate::HandleCancelBoost() return status; } -/********************************************************************************* - * - * Methods implementing the ModeBase::Delegate interface - * - *********************************************************************************/ - /********************************************************************************* * * WaterHeaterManagementDelegate specific methods @@ -286,7 +281,7 @@ Status WaterHeaterManagementDelegate::HandleCancelBoost() void WaterHeaterManagementDelegate::SetWaterTemperature(uint16_t waterTemperature) { - mHotWaterTemperature = waterTemperature; + mWaterTemperature = waterTemperature; if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent)) { @@ -338,52 +333,34 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const ? static_cast(mBoostTemporarySetpoint.Value()) : mTargetWaterTemperature; - uint8_t targetPercentage = 0; - bool useTankPercentage = false; - - if (mBoostState == BoostStateEnum::kActive && mBoostTargetTemperatureReached && mBoostTargetReheat.HasValue()) - { - // If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because the - // TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field - // indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. - // - // For example if the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% hot water, - // the tank may have hot water drawn off until only 40% hot water remains. At this point the heater will begin to heat back - // up to 80% of hot water. If this field and the OneShot field were both omitted, heating would begin again after any water - // draw which reduced the TankPercentage below 80%. - - // If this field is included then the TargetPercentage field SHALL also be included, and the OneShot excluded. - - targetPercentage = mBoostTargetReheat.Value(); - - useTankPercentage = true; - } - else if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent) && mBoostTargetPercentage.HasValue()) - { - // If tank percentage is supported AND the targetPercentage.HasValue() then use target percentage to heat up. - targetPercentage = mBoostTargetPercentage.Value(); - - useTankPercentage = true; - } - else - { - // Don't support tankPercentage OR the targetPercent wasn't included then just rely on temperature alone - useTankPercentage = false; - } + VerifyOrReturnValue(mWaterTemperature >= targetTemperature, false); - // Determine whether the water is at the target temperature - bool tempReached = true; - if (useTankPercentage && mTankPercentage < targetPercentage) - { - tempReached = false; - } - - if (mHotWaterTemperature < targetTemperature) + if (mBoostState == BoostStateEnum::kActive) { - tempReached = false; + if (mBoostTargetTemperatureReached && mBoostTargetReheat.HasValue()) + { + // If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because the + // TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), + // mBoostTargetReheat indicates the percentage to which the hot water in the tank SHALL be allowed to fall before + // again beginning to reheat it. + // + // For example if the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% hot water, + // the tank may have hot water drawn off until only 40% hot water remains. At this point the heater will begin to heat back + // up to 80% of hot water. If this field and the OneShot field were both omitted, heating would begin again after any water + // draw which reduced the TankPercentage below 80%. + + // If this field is included then the TargetPercentage field SHALL also be included, and the OneShot excluded. + VerifyOrReturnValue(mTankPercentage >= mBoostTargetReheat.Value(), false); + } + else if (mBoostTargetPercentage.HasValue()) + { + // If tank percentage is supported AND the targetPercentage.HasValue() then use target percentage to heat up. + VerifyOrReturnValue(mTankPercentage >= mBoostTargetPercentage.Value(), false); + } } - return tempReached; + // Must have reached teh right temperature + return true; } Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() @@ -391,8 +368,12 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() Status status = Status::Success; bool turningHeatOff = false; + VerifyOrReturnError(mpWhmManufacturer != nullptr, Status::InvalidInState); + if (!HasWaterTemperatureReachedTarget()) { + VerifyOrReturnError(WaterHeaterMode::Instance() != nullptr, Status::InvalidInState); + uint8_t mode = WaterHeaterMode::Instance()->GetCurrentMode(); // The water in the tank is not at the target temperature. See if heating is currently off @@ -408,28 +389,20 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() // If a boost command is in progress or in manual mode, find a heating source and "turn it on". if (mBoostState == BoostStateEnum::kActive || mode == WaterHeaterMode::kModeManual) { - if (mpWhmManufacturer != nullptr) - { - // Find out from the manufacturer object the heating sources to use. - BitMask heaterDemand = mpWhmManufacturer->DetermineHeatingSources(); - - SetHeatDemand(heaterDemand); - - // And turn the heating of the water tank on. - status = mpWhmManufacturer->TurnHeatingOn(); - } - else - { - status = Status::InvalidInState; - ChipLogError(AppServer, "CheckIfHeatNeedsToBeTurnedOnOrOff: Failed as mpWhmManufacturer == nullptr"); - } + // Find out from the manufacturer object the heating sources to use. + BitMask heaterDemand = mpWhmManufacturer->DetermineHeatingSources(); + + SetHeatDemand(heaterDemand); + + // And turn the heating of the water tank on. + status = mpWhmManufacturer->TurnHeatingOn(mBoostEmergencyBoost.HasValue() ? mBoostEmergencyBoost.Value() : false); } } else if (mBoostState == BoostStateEnum::kInactive && mode == WaterHeaterMode::kModeOff) { // The water temperature is not at the target temperature but there is no boost command in progress and the mode is Off // so need to ensure the heating is turned off. - ChipLogError(AppServer, "CheckIfHeatNeedsToBeTurnedOnOrOff turning heating off due to mode"); + ChipLogError(AppServer, "CheckIfHeatNeedsToBeTurnedOnOrOff turning heating off due to no boost cmd and kModeOff"); SetHeatDemand(BitMask(0)); @@ -457,47 +430,34 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); - if (mpWhmManufacturer != nullptr) - { - status = mpWhmManufacturer->BoostCommandCancelled(); - } - else - { - status = Status::InvalidInState; - ChipLogError(AppServer, "CheckIfHeatNeedsToBeTurnedOnOrOff: mpWhmManufacturer == nullptr"); - } + mBoostEmergencyBoost.ClearValue(); + + status = mpWhmManufacturer->BoostCommandCancelled(); } // Turn the heating off - if (mpWhmManufacturer != nullptr) - { - status = mpWhmManufacturer->TurnHeatingOff(); - } - else - { - status = Status::InvalidInState; - ChipLogError(AppServer, - "CheckIfHeatNeedsToBeTurnedOnOrOff: Failed to turn the heating off as mpWhmManufacturer == nullptr"); - } + status = mpWhmManufacturer->TurnHeatingOff(); } return status; } -void WaterHeaterManagementDelegate::SetWaterHeaterMode(uint8_t modeValue) +Status WaterHeaterManagementDelegate::SetWaterHeaterMode(uint8_t modeValue) { + VerifyOrReturnError(WaterHeaterMode::Instance() != nullptr, Status::InvalidInState); + if (!WaterHeaterMode::Instance()->IsSupportedMode(modeValue)) { ChipLogError(AppServer, "SetWaterHeaterMode bad mode"); - return; + return Status::ConstraintError; } Status status = WaterHeaterMode::Instance()->UpdateCurrentMode(modeValue); if (status != Status::Success) { - ChipLogError(AppServer, "SetWaterHeaterMode updateMode failed"); - return; + ChipLogError(AppServer, "SetWaterHeaterMode updateMode failed 0x%02x", to_underlying(status)); + return status; } - CheckIfHeatNeedsToBeTurnedOnOrOff(); + return CheckIfHeatNeedsToBeTurnedOnOrOff(); } diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index 34352582545ce7..a2e7c6624832d3 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -101,7 +101,7 @@ BitMask WhmManufacturer::DetermineHeatingSources() return BitMask(heaterDemandMask); } -Status WhmManufacturer::TurnHeatingOn() +Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost) { Status status = Status::Success; @@ -198,7 +198,11 @@ void SetTestEventTrigger_ManualModeTestEvent() WaterHeaterManagementDelegate * dg = GetWhmDelegate(); // Simulate the Water Heater Mode being set to MANUAL - dg->SetWaterHeaterMode(WaterHeaterMode::kModeManual); + Status status = dg->SetWaterHeaterMode(WaterHeaterMode::kModeManual); + if (status != Status::Success) + { + ChipLogError(Zcl, "SetTestEventTrigger_OffModeTestEvent setting mode -> KModeManual failed 0x%02x", to_underlying(status)); + } } void SetTestEventTrigger_OffModeTestEvent() @@ -206,7 +210,11 @@ void SetTestEventTrigger_OffModeTestEvent() WaterHeaterManagementDelegate * dg = GetWhmDelegate(); // Simulate the Water Heater Mode being set to OFF - dg->SetWaterHeaterMode(WaterHeaterMode::kModeOff); + Status status = dg->SetWaterHeaterMode(WaterHeaterMode::kModeOff); + if (status != Status::Success) + { + ChipLogError(Zcl, "SetTestEventTrigger_OffModeTestEvent setting mode -> KModeOff failed 0x%02x", to_underlying(status)); + } } void SetTestEventTrigger_DrawOffHotWaterTestEvent() From f38b2f445174f2cfd5a991fe17dfc269b4ebbeae Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Sun, 28 Jul 2024 07:53:44 +0000 Subject: [PATCH 21/30] Restyled by whitespace --- .../all-clusters-common/include/WhmManufacturer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h index f953e7f554dab5..10a1a447f57c21 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h @@ -95,7 +95,7 @@ class WhmManufacturer * heated by this Boost command before the heater is switched off. * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), - * this field indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again + * this field indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again * beginning to reheat it. * * @return Success if the boost command is successful; otherwise return the appropriate error. From 5964de9a3a858e4763d0edc7b4bf61ac41a21e7e Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Sun, 28 Jul 2024 07:53:45 +0000 Subject: [PATCH 22/30] Restyled by clang-format --- .../all-clusters-common/include/WhmManufacturer.h | 7 ++++--- .../all-clusters-common/src/WhmDelegateImpl.cpp | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h index 10a1a447f57c21..b8346849d9a4b4 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h @@ -86,7 +86,8 @@ class WhmManufacturer * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts * to the previous mode (e.g. OFF, MANUAL or TIMED). * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the - * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). + * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if + * specified). * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be @@ -94,8 +95,8 @@ class WhmManufacturer * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be * heated by this Boost command before the heater is switched off. * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because - * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if included), - * this field indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again + * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if + * included), this field indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again * beginning to reheat it. * * @return Success if the boost command is successful; otherwise return the appropriate error. diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 67ea727af91e11..b5a1d887838261 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -30,8 +30,8 @@ using Protocols::InteractionModel::Status; WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clustersEndpoint) : mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mWaterTemperature(0), mReplacedWaterTemperature(0), - mBoostTargetTemperatureReached(false), mTankVolume(0), - mEstimatedHeatRequired(0), mTankPercentage(0), mBoostState(BoostStateEnum::kInactive) + mBoostTargetTemperatureReached(false), mTankVolume(0), mEstimatedHeatRequired(0), mTankPercentage(0), + mBoostState(BoostStateEnum::kInactive) {} void WaterHeaterManagementDelegate::SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance) @@ -344,10 +344,10 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const // mBoostTargetReheat indicates the percentage to which the hot water in the tank SHALL be allowed to fall before // again beginning to reheat it. // - // For example if the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% hot water, - // the tank may have hot water drawn off until only 40% hot water remains. At this point the heater will begin to heat back - // up to 80% of hot water. If this field and the OneShot field were both omitted, heating would begin again after any water - // draw which reduced the TankPercentage below 80%. + // For example if the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% hot + // water, the tank may have hot water drawn off until only 40% hot water remains. At this point the heater will begin to + // heat back up to 80% of hot water. If this field and the OneShot field were both omitted, heating would begin again + // after any water draw which reduced the TankPercentage below 80%. // If this field is included then the TargetPercentage field SHALL also be included, and the OneShot excluded. VerifyOrReturnValue(mTankPercentage >= mBoostTargetReheat.Value(), false); From 44c333f78550015b9696546b574c5e7ee78e5615 Mon Sep 17 00:00:00 2001 From: pcoleman Date: Sun, 28 Jul 2024 09:47:57 +0100 Subject: [PATCH 23/30] Address further review comments from JamesH --- .../all-clusters-common/include/WhmDelegate.h | 17 ++++ .../src/WhmDelegateImpl.cpp | 96 +++++++++---------- .../src/WhmManufacturer.cpp | 14 +++ 3 files changed, 78 insertions(+), 49 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h index 91ece3d19a6e4e..ecabfe22792f25 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -30,6 +30,13 @@ namespace WaterHeaterManagement { class WhmManufacturer; +enum HeatingOp +{ + TurnHeatingOn, + TurnHeatingOff, + LeaveHeatingUnchanged +}; + // This is an application level delegate to handle operational state commands according to the specific business logic. class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate { @@ -162,6 +169,16 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate */ void DrawOffHotWater(uint8_t percentageReplaced, uint16_t replacedWaterTemperature); +private: + /** + * @brief Determine whether heating needs to be turned on or off or left as is. + * + * @param heatingOp[out] Set as determined whether heating needs to be turned on/off or left unchanged. + * + * @return Success if the heating operation could be determined otherwise returns with appropriate error. + */ + Protocols::InteractionModel::Status DetermineIfChangingHeatingState(HeatingOp & heatingOp); + private: /********************************************************************************* * diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index b5a1d887838261..e5edf1e084b6de 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -246,8 +246,6 @@ void WaterHeaterManagementDelegate::HandleBoostTimerExpiry() */ Status WaterHeaterManagementDelegate::HandleCancelBoost() { - Status status = Status::Success; - ChipLogProgress(AppServer, "HandleCancelBoost"); if (mBoostState == BoostStateEnum::kActive) @@ -257,20 +255,16 @@ Status WaterHeaterManagementDelegate::HandleCancelBoost() DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); - if (mpWhmManufacturer != nullptr) - { - status = mpWhmManufacturer->BoostCommandCancelled(); - } - else - { - status = Status::InvalidInState; - ChipLogError(AppServer, "HandleCancelBoost: mpWhmManufacturer == nullptr"); - } + VerifyOrReturnValue(mpWhmManufacturer != nullptr, Status::InvalidInState); + + Status status = mpWhmManufacturer->BoostCommandCancelled(); + VerifyOrReturnValue(status == Status::Success, status); status = CheckIfHeatNeedsToBeTurnedOnOrOff(); + VerifyOrReturnValue(status == Status::Success, status); } - return status; + return Status::Success; } /********************************************************************************* @@ -365,11 +359,44 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() { - Status status = Status::Success; - bool turningHeatOff = false; - VerifyOrReturnError(mpWhmManufacturer != nullptr, Status::InvalidInState); + HeatingOp heatingOp = HeatingOp::LeaveHeatingUnchanged; + + Status status = DetermineIfChangingHeatingState(heatingOp); + + VerifyOrReturnError(status == Status::Success, status); + + if (heatingOp == HeatingOp::TurnHeatingOn) + { + status = mpWhmManufacturer->TurnHeatingOn(mBoostEmergencyBoost.HasValue() ? mBoostEmergencyBoost.Value() : false); + } + else if (heatingOp == HeatingOp::TurnHeatingOff) + { + // If running a boost command with the oneShot parameter and turning heat off, then must have + // reached the boost command target temperature -> that's the boost command complete. + if (mBoostState == BoostStateEnum::kActive && mBoostOneShot.HasValue() && mBoostOneShot.Value()) + { + SetBoostState(BoostStateEnum::kInactive); + + DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); + + mBoostEmergencyBoost.ClearValue(); + + status = mpWhmManufacturer->BoostCommandCancelled(); + } + + // Turn the heating off + status = mpWhmManufacturer->TurnHeatingOff(); + } + + return status; +} + +Status WaterHeaterManagementDelegate::DetermineIfChangingHeatingState(HeatingOp & heatingOp) +{ + heatingOp = LeaveHeatingUnchanged; + if (!HasWaterTemperatureReachedTarget()) { VerifyOrReturnError(WaterHeaterMode::Instance() != nullptr, Status::InvalidInState); @@ -389,57 +416,28 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff() // If a boost command is in progress or in manual mode, find a heating source and "turn it on". if (mBoostState == BoostStateEnum::kActive || mode == WaterHeaterMode::kModeManual) { - // Find out from the manufacturer object the heating sources to use. - BitMask heaterDemand = mpWhmManufacturer->DetermineHeatingSources(); - - SetHeatDemand(heaterDemand); - - // And turn the heating of the water tank on. - status = mpWhmManufacturer->TurnHeatingOn(mBoostEmergencyBoost.HasValue() ? mBoostEmergencyBoost.Value() : false); + heatingOp = HeatingOp::TurnHeatingOn; } } else if (mBoostState == BoostStateEnum::kInactive && mode == WaterHeaterMode::kModeOff) { // The water temperature is not at the target temperature but there is no boost command in progress and the mode is Off // so need to ensure the heating is turned off. - ChipLogError(AppServer, "CheckIfHeatNeedsToBeTurnedOnOrOff turning heating off due to no boost cmd and kModeOff"); + ChipLogError(AppServer, "DetermineIfChangingHeatingState turning heating off due to no boost cmd and kModeOff"); - SetHeatDemand(BitMask(0)); - - turningHeatOff = true; + heatingOp = HeatingOp::TurnHeatingOff; } } else if (mHeatDemand.Raw() != 0) { // The water in the tank has reached the target temperature - need to turn the heating off - SetHeatDemand(BitMask(0)); - - turningHeatOff = true; + heatingOp = HeatingOp::TurnHeatingOff; // If a boost command is in progress, record that the target temperature has been reached. mBoostTargetTemperatureReached = (mBoostState == BoostStateEnum::kActive); } - if (turningHeatOff) - { - // If running a boost command with the oneShot parameter and turning heat off, then must have - // reached the boost command target temperature -> that's the boost command complete. - if (mBoostState == BoostStateEnum::kActive && mBoostOneShot.HasValue() && mBoostOneShot.Value()) - { - SetBoostState(BoostStateEnum::kInactive); - - DeviceLayer::SystemLayer().CancelTimer(BoostTimerExpiry, this); - - mBoostEmergencyBoost.ClearValue(); - - status = mpWhmManufacturer->BoostCommandCancelled(); - } - - // Turn the heating off - status = mpWhmManufacturer->TurnHeatingOff(); - } - - return status; + return Status::Success; } Status WaterHeaterManagementDelegate::SetWaterHeaterMode(uint8_t modeValue) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index a2e7c6624832d3..39116e66a272ed 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -114,6 +114,16 @@ Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost) mBoostActive = true; } + if (emergencyBoost) + { + dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1, + WaterHeaterDemandBitmap::kImmersionElement2)); + } + else + { + dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); + } + return status; } @@ -128,6 +138,10 @@ Status WhmManufacturer::TurnHeatingOff() mBoostActive = false; } + WaterHeaterManagementDelegate * dg = GetWhmDelegate(); + + dg->SetHeatDemand(BitMask(0)); + return status; } From c13aca1eeaa4614cb51a66834db2655844fa6906 Mon Sep 17 00:00:00 2001 From: pcoleman Date: Sun, 28 Jul 2024 10:03:03 +0100 Subject: [PATCH 24/30] Address further review comments from JamesH --- .../all-clusters-common/include/WhmManufacturer.h | 1 - .../all-clusters-common/src/WhmManufacturer.cpp | 14 ++------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h index b8346849d9a4b4..d51e6a62f2021c 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h @@ -120,7 +120,6 @@ class WhmManufacturer private: WaterHeaterManagementInstance * mWhmInstance; - bool mBoostActive; }; /** @brief Helper function to return the singleton WhmManufacturer instance diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index 39116e66a272ed..dd8452a5c816ca 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -43,8 +43,6 @@ CHIP_ERROR WhmManufacturer::Init() return CHIP_ERROR_UNINITIALIZED; } - mBoostActive = false; - dg->SetHeaterTypes(BitMask(WaterHeaterTypeBitmap::kImmersionElement1)); dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); dg->SetEstimatedHeatRequired(10000); @@ -109,13 +107,10 @@ Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost) WaterHeaterManagementDelegate * dg = GetWhmDelegate(); - if (dg->GetBoostState() == BoostStateEnum::kActive) - { - mBoostActive = true; - } - if (emergencyBoost) { + // emergencyBoost that the consumer wants the water to be heated as quickly as practicable. + // Thus, cause multiple heat sources to be activated dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1, WaterHeaterDemandBitmap::kImmersionElement2)); } @@ -133,11 +128,6 @@ Status WhmManufacturer::TurnHeatingOff() ChipLogProgress(AppServer, "WhmManufacturer::TurnHeatingOff"); - if (mBoostActive) - { - mBoostActive = false; - } - WaterHeaterManagementDelegate * dg = GetWhmDelegate(); dg->SetHeatDemand(BitMask(0)); From 713fad01d7ae4de219fedc07412a144d14a1a0c4 Mon Sep 17 00:00:00 2001 From: jamesharrow <93921463+jamesharrow@users.noreply.github.com> Date: Mon, 29 Jul 2024 00:42:56 +0100 Subject: [PATCH 25/30] Update examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp Co-authored-by: Boris Zbarsky --- .../all-clusters-common/src/WhmDelegateImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index e5edf1e084b6de..92f62ec6686480 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -154,8 +154,8 @@ void WaterHeaterManagementDelegate::SetBoostState(BoostStateEnum boostState) * the Off periods. */ Status WaterHeaterManagementDelegate::HandleBoost(uint32_t durationS, Optional oneShot, Optional emergencyBoost, - Optional temporarySetpoint, Optional targetPercentage, - Optional targetReheat) + Optional temporarySetpoint, Optional targetPercentage, + Optional targetReheat) { Status status = Status::Success; From dc64dc5600581fb64ea0d78fe4be58cf178deb3f Mon Sep 17 00:00:00 2001 From: PeterC1965 <101805108+PeterC1965@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:17:34 +0100 Subject: [PATCH 26/30] Update examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp Co-authored-by: Boris Zbarsky --- .../all-clusters-common/src/WhmManufacturer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index dd8452a5c816ca..fc24a1ed0c5718 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -87,7 +87,7 @@ BitMask WhmManufacturer::DetermineHeatingSources() BitMask heaterTypes = dg->GetHeaterTypes(); uint8_t heaterDemandMask = 0; - for (uint16_t idx = 0; idx < static_cast(sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0])); idx++) + for (auto & waterHeaterType : waterHeaterTypeValues) { { // Is this heating source being used? if (heaterTypes.Raw() & waterHeaterTypeValues[idx]) From 966c7a17a64c90516b86f3dc4a94f5f968a36799 Mon Sep 17 00:00:00 2001 From: PeterC1965 <101805108+PeterC1965@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:17:58 +0100 Subject: [PATCH 27/30] Update examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp Co-authored-by: Boris Zbarsky --- .../all-clusters-common/src/WhmManufacturer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index fc24a1ed0c5718..0883938bee1b9a 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -90,7 +90,7 @@ BitMask WhmManufacturer::DetermineHeatingSources() for (auto & waterHeaterType : waterHeaterTypeValues) { { // Is this heating source being used? - if (heaterTypes.Raw() & waterHeaterTypeValues[idx]) + if (heaterTypes.Has(waterHeaterTypeValue)) { heaterDemandMask |= waterHeaterDemandValues[idx]; } From c45decd391316b8b915e8a6b7de12f83f9cfd92f Mon Sep 17 00:00:00 2001 From: pcoleman Date: Mon, 29 Jul 2024 09:19:57 +0100 Subject: [PATCH 28/30] Undo suggested change from Boris as idx needed in the loop --- .../all-clusters-common/src/WhmManufacturer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index 0883938bee1b9a..dd8452a5c816ca 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -87,10 +87,10 @@ BitMask WhmManufacturer::DetermineHeatingSources() BitMask heaterTypes = dg->GetHeaterTypes(); uint8_t heaterDemandMask = 0; - for (auto & waterHeaterType : waterHeaterTypeValues) { + for (uint16_t idx = 0; idx < static_cast(sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0])); idx++) { // Is this heating source being used? - if (heaterTypes.Has(waterHeaterTypeValue)) + if (heaterTypes.Raw() & waterHeaterTypeValues[idx]) { heaterDemandMask |= waterHeaterDemandValues[idx]; } From 8278e2569a52facd6d0cf4de2ecd882a564dc1ba Mon Sep 17 00:00:00 2001 From: PeterC1965 <101805108+PeterC1965@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:26:50 +0100 Subject: [PATCH 29/30] Update examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp Co-authored-by: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> --- .../all-clusters-common/src/WhmDelegateImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 92f62ec6686480..432f1e14cd4e7d 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -353,7 +353,7 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const } } - // Must have reached teh right temperature + // Must have reached the right temperature return true; } From 47d0f32b3387b8302141125623a9d32c4654842e Mon Sep 17 00:00:00 2001 From: pcoleman Date: Mon, 29 Jul 2024 16:36:54 +0100 Subject: [PATCH 30/30] Address review comments --- .../all-clusters-common/src/WhmDelegateImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 432f1e14cd4e7d..fd19a3c59d740b 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -212,7 +212,7 @@ Status WaterHeaterManagementDelegate::HandleBoost(uint32_t durationS, Optional(delegate); + WaterHeaterManagementDelegate * dg = static_cast(delegate); dg->HandleBoostTimerExpiry(); }