Skip to content

Commit

Permalink
Add water heater management cluster sdk support (project-chip#34339)
Browse files Browse the repository at this point in the history
* Add base water-heater-mode-cluster support into control files and regenerate files

* Add support for the water-heater-management cluster SDK

* Add support for the water-heater-management cluster SDK

* Add support for the water-heater-management cluster SDK

* Add support for the water-heater-management cluster SDK

* Define chip_enable_water_heater_management_trigger and CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER

* Restyled by isort

* Add accidentally deleted Mode_WaterHeater.xml back

* Address review comments from Tennessee Carmel-Veilleux

* Add WaterHeaterManagementTestEventTriggerHandler.h

* Apply various review comments from Boris Zbarsky

* Apply various review comments from Boris Zbarsky

* Addressing review comments from Boris

* Addressing review comments from Boris

* Restyled by whitespace

* Restyled by clang-format

* Address review comment from Tennessee

* Update with review comments from Boris

* Fix compilation introduced during merge

---------

Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
2 people authored and rochaferraz committed Jul 31, 2024
1 parent d506204 commit e24f6de
Show file tree
Hide file tree
Showing 13 changed files with 511 additions and 450 deletions.
7 changes: 7 additions & 0 deletions examples/platform/linux/AppMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@
#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER
#include <app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h>
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER
#include <app/clusters/water-heater-management-server/WaterHeaterManagementTestEventTriggerHandler.h>
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_DEVICE_ENERGY_MANAGEMENT_TRIGGER
#include <app/clusters/device-energy-management-server/DeviceEnergyManagementTestEventTriggerHandler.h>
#endif
Expand Down Expand Up @@ -556,6 +559,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
#if CHIP_DEVICE_CONFIG_ENABLE_DEVICE_ENERGY_MANAGEMENT_TRIGGER
static DeviceEnergyManagementTestEventTriggerHandler sDeviceEnergyManagementTestEventTriggerHandler;
sTestEventTriggerDelegate.AddHandler(&sDeviceEnergyManagementTestEventTriggerHandler);
Expand Down
2 changes: 2 additions & 0 deletions examples/platform/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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
chip_enable_device_energy_management_trigger = false
}

Expand Down Expand Up @@ -125,6 +126,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}",
"CHIP_DEVICE_CONFIG_ENABLE_DEVICE_ENERGY_MANAGEMENT_TRIGGER=${chip_enable_device_energy_management_trigger}",
]

Expand Down
6 changes: 6 additions & 0 deletions src/app/chip_data_model.gni
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <app-common/zap-generated/cluster-objects.h>
#include <app/TestEventTriggerDelegate.h>

/**
* @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
Original file line number Diff line number Diff line change
@@ -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 <app/AttributeAccessInterface.h>
#include <app/AttributeAccessInterfaceRegistry.h>
#include <app/ConcreteAttributePath.h>
#include <app/InteractionModelEngine.h>
#include <app/util/attribute-storage.h>

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<Boost::DecodableType>(
handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleBoost(ctx, commandData); });
return;
case CancelBoost::Id:
HandleCommand<CancelBoost::DecodableType>(
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<bool> oneShot = commandData.oneShot;
Optional<bool> emergencyBoost = commandData.emergencyBoost;
Optional<int16_t> temporarySetpoint = commandData.temporarySetpoint;
Optional<Percent> targetPercentage = commandData.targetPercentage;
Optional<Percent> 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
Loading

0 comments on commit e24f6de

Please sign in to comment.