Skip to content

Commit 95dcc6c

Browse files
committed
Start addressing comments from issue project-chip#34565
1 parent 3b90fed commit 95dcc6c

File tree

5 files changed

+219
-69
lines changed

5 files changed

+219
-69
lines changed

examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h

+37-9
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate
104104
* SHALL be rejected with appropriate error.
105105
*/
106106
Protocols::InteractionModel::Status HandleBoost(uint32_t duration, Optional<bool> oneShot, Optional<bool> emergencyBoost,
107-
Optional<int16_t> temporarySetpoint, Optional<chip::Percent> targetPercentage,
108-
Optional<chip::Percent> targetReheat) override;
107+
Optional<int16_t> temporarySetpoint, Optional<Percent> targetPercentage,
108+
Optional<Percent> targetReheat) override;
109109

110110
/**
111111
* @brief Delegate should implement a handler to cancel a boost command.
@@ -162,9 +162,12 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate
162162
void SetTargetWaterTemperature(uint16_t targetWaterTemperature);
163163

164164
/**
165-
* @brief Determine whether the heating sources need to be turned on or off
165+
* @brief Determine whether the heating sources need to be turned on or off or left unchanged.
166+
*
167+
* @return Success if the heating was successfully turned on or off or left unchanged otherwise an error
168+
* code is returned if turning the heating on or off failed.
166169
*/
167-
Protocols::InteractionModel::Status CheckIfHeatNeedsToBeTurnedOnOrOff();
170+
Protocols::InteractionModel::Status ChangeHeatingIfNecessary();
168171

169172
/**
170173
* @brief Static timer callback for when Boost timer expires.
@@ -194,9 +197,34 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate
194197
* @param replacedWaterTemperature The temperature of the
195198
* percentageReplaced water.
196199
*/
197-
void DrawOffHotWater(chip::Percent percentageReplaced, uint16_t replacedWaterTemperature);
200+
void DrawOffHotWater(Percent percentageReplaced, uint16_t replacedWaterTemperature);
201+
202+
/**
203+
* Set the temperature of the cold water that fills the tank as the hot water
204+
* is drawn off.
205+
*
206+
* @param oldWaterTemperature The cold water temperature in 100th of a C
207+
*/
208+
void SetColdWaterTemperature(uint16_t coldWaterTemperature);
198209

199210
private:
211+
/**
212+
* Return the target temperature.
213+
* If a boost command is in progress and has a mBoostTemporarySetpoint value use that as the
214+
* target temperature otherwise use the temperature set via SetTargetWaterTemperature().
215+
*
216+
* @return the target temperature
217+
*/
218+
uint16_t GetActiveTargetWaterTemperature() const;
219+
220+
/**
221+
* @brief Calculate the percentage of the water in the tank at the target
222+
* temperature.
223+
*
224+
* @return Percentage of water at the target temperature
225+
*/
226+
uint8_t CalculateTankPercentage() const;
227+
200228
/**
201229
* @brief Determine whether heating needs to be turned on or off or left as
202230
* is.
@@ -228,8 +256,8 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate
228256
// Actual water temperature in 100ths of a C
229257
uint16_t mWaterTemperature;
230258

231-
// The % of water at temperature mReplacedWaterTemperature
232-
uint16_t mReplacedWaterTemperature;
259+
// The cold water temperature in 100ths of a C
260+
uint16_t mColdWaterTemperature;
233261

234262
// Boost command parameters
235263

@@ -253,7 +281,7 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate
253281
// amount of water that SHALL be heated by this Boost command before the
254282
// heater is switched off. This field is optional, however it SHALL be
255283
// included if the TargetReheat field is included.
256-
Optional<chip::Percent> mBoostTargetPercentage;
284+
Optional<Percent> mBoostTargetPercentage;
257285

258286
// If the tank supports the TankPercent feature, and the heating by this
259287
// Boost command has ceased because the TargetPercentage of the water in the
@@ -266,7 +294,7 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate
266294
// heat back up to 80% of hot water. If this field and the OneShot field
267295
// were both omitted, heating would begin again after any water draw which
268296
// reduced the TankPercentage below 80%.
269-
Optional<chip::Percent> mBoostTargetReheat;
297+
Optional<Percent> mBoostTargetReheat;
270298

271299
// Track whether the water temperature has reached the water temperature
272300
// specified in the boost command. Used in conjunction with the boost

examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp

+89-38
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <WhmManufacturer.h>
2222
#include <water-heater-mode.h>
2323

24+
#include <algorithm>
25+
2426
using namespace chip;
2527
using namespace chip::app;
2628
using namespace chip::app::Clusters;
@@ -29,10 +31,11 @@ using namespace chip::app::Clusters::WaterHeaterManagement;
2931
using Protocols::InteractionModel::Status;
3032

3133
WaterHeaterManagementDelegate::WaterHeaterManagementDelegate(EndpointId clustersEndpoint) :
32-
mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mWaterTemperature(0), mReplacedWaterTemperature(0),
33-
mBoostTargetTemperatureReached(false), mTankVolume(0), mEstimatedHeatRequired(0), mTankPercentage(0),
34-
mBoostState(BoostStateEnum::kInactive)
35-
{}
34+
mpWhmInstance(nullptr), mpWhmManufacturer(nullptr), mTargetWaterTemperature(0), mWaterTemperature(0),
35+
mColdWaterTemperature(0), mBoostTargetTemperatureReached(false), mTankVolume(0),
36+
mEstimatedHeatRequired(0), mTankPercentage(0), mBoostState(BoostStateEnum::kInactive)
37+
{
38+
}
3639

3740
void WaterHeaterManagementDelegate::SetWaterHeaterManagementInstance(WaterHeaterManagement::Instance & instance)
3841
{
@@ -128,8 +131,6 @@ void WaterHeaterManagementDelegate::SetTankPercentage(Percent tankPercentage)
128131
{
129132
mTankPercentage = tankPercentage;
130133

131-
CheckIfHeatNeedsToBeTurnedOnOrOff();
132-
133134
MatterReportingAttributeChangeCallback(mEndpointId, WaterHeaterManagement::Id, Attributes::TankPercentage::Id);
134135
}
135136
}
@@ -201,13 +202,22 @@ Status WaterHeaterManagementDelegate::HandleBoost(uint32_t durationS, Optional<b
201202
ChipLogError(AppServer, "HandleBoost: mpWhmManufacturer == nullptr");
202203
}
203204

204-
if (status == Status::Success)
205+
VerifyOrReturnValue(status == Status::Success, status);
206+
207+
// See if the heat needs to be turned on or off as a result of this boost command
208+
status = ChangeHeatingIfNecessary();
209+
VerifyOrReturnValue(status == Status::Success, status);
210+
211+
// Now generate a BoostStarted event
212+
err = GenerateBoostStartedEvent(durationS, oneShot, emergencyBoost, temporarySetpoint, targetPercentage, targetReheat);
213+
if (err != CHIP_NO_ERROR)
205214
{
206-
// See if the heat needs to be turned on or off as a result of this boost command
207-
status = CheckIfHeatNeedsToBeTurnedOnOrOff();
215+
ChipLogError(AppServer, "HandleBoost: Failed to send BoostStarted event: %" CHIP_ERROR_FORMAT, err.Format());
216+
217+
return Status::Failure;
208218
}
209219

210-
return status;
220+
return Status::Success;
211221
}
212222

213223
void WaterHeaterManagementDelegate::BoostTimerExpiry(System::Layer * systemLayer, void * delegate)
@@ -236,7 +246,15 @@ void WaterHeaterManagementDelegate::HandleBoostTimerExpiry()
236246
ChipLogError(AppServer, "HandleBoostTimerExpiry: mpWhmManufacturer == nullptr");
237247
}
238248

239-
CheckIfHeatNeedsToBeTurnedOnOrOff();
249+
// Note ChangeHeatingIfNecessary can generate a BoostEnded event but only if the boost state is kActive
250+
// which it cannot be when called from here.
251+
ChangeHeatingIfNecessary();
252+
253+
CHIP_ERROR err = GenerateBoostEndedEvent();
254+
if (err != CHIP_NO_ERROR)
255+
{
256+
ChipLogError(AppServer, "HandleBoostTimerExpiry: Failed to send BoostEnded event: %" CHIP_ERROR_FORMAT, err.Format());
257+
}
240258
}
241259

242260
/**
@@ -260,8 +278,18 @@ Status WaterHeaterManagementDelegate::HandleCancelBoost()
260278
Status status = mpWhmManufacturer->BoostCommandCancelled();
261279
VerifyOrReturnValue(status == Status::Success, status);
262280

263-
status = CheckIfHeatNeedsToBeTurnedOnOrOff();
281+
// Note ChangeHeatingIfNecessary can generate a BoostEnded event but only if the boost state is kActive
282+
// which it cannot be when called from here.
283+
status = ChangeHeatingIfNecessary();
264284
VerifyOrReturnValue(status == Status::Success, status);
285+
286+
CHIP_ERROR err = GenerateBoostEndedEvent();
287+
if (err != CHIP_NO_ERROR)
288+
{
289+
ChipLogError(AppServer, "HandleCancelBoost: Failed to send BoostEnded event: %" CHIP_ERROR_FORMAT, err.Format());
290+
291+
return Status::Failure;
292+
}
265293
}
266294

267295
return Status::Success;
@@ -273,59 +301,75 @@ Status WaterHeaterManagementDelegate::HandleCancelBoost()
273301
*
274302
*********************************************************************************/
275303

304+
uint16_t WaterHeaterManagementDelegate::GetActiveTargetWaterTemperature() const
305+
{
306+
// Determine the target temperature. If a boost command is in progress and has a mBoostTemporarySetpoint value use that as the
307+
// target temperature.
308+
// Note, in practise the actual heating is likely to be controlled by the thermostat's occupiedHeatingSetpoint most of the
309+
// time, and the TemporarySetpoint (if not null) would be overiding the thermostat's occupiedHeatingSetpoint.
310+
// However, this code doesn't rely upon the thermostat cluster.
311+
uint16_t targetTemperature = (mBoostState == BoostStateEnum::kActive && mBoostTemporarySetpoint.HasValue())
312+
? static_cast<uint16_t>(mBoostTemporarySetpoint.Value())
313+
: mTargetWaterTemperature;
314+
315+
return targetTemperature;
316+
}
317+
318+
uint8_t WaterHeaterManagementDelegate::CalculateTankPercentage() const
319+
{
320+
int16_t tankPercentage = 100 * (mWaterTemperature - mColdWaterTemperature) / (GetActiveTargetWaterTemperature() - mColdWaterTemperature);
321+
322+
tankPercentage = std::min(tankPercentage, static_cast<int16_t>(100));
323+
tankPercentage = std::max(tankPercentage, static_cast<int16_t>(0));
324+
325+
return static_cast<uint8_t>(tankPercentage);
326+
}
327+
328+
void WaterHeaterManagementDelegate::SetColdWaterTemperature(uint16_t coldWaterTemperature)
329+
{
330+
mColdWaterTemperature = coldWaterTemperature;
331+
}
332+
276333
void WaterHeaterManagementDelegate::SetWaterTemperature(uint16_t waterTemperature)
277334
{
278335
mWaterTemperature = waterTemperature;
279336

280337
if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent))
281338
{
282-
mTankPercentage = 100;
339+
// Recalculate the tankPercentage as the waterTemperature has changed
340+
SetTankPercentage(CalculateTankPercentage());
283341
}
284342

285343
// See if the heat needs to be turned on or off
286-
CheckIfHeatNeedsToBeTurnedOnOrOff();
344+
ChangeHeatingIfNecessary();
287345
}
288346

289347
void WaterHeaterManagementDelegate::SetTargetWaterTemperature(uint16_t targetWaterTemperature)
290348
{
291349
mTargetWaterTemperature = targetWaterTemperature;
292350

293351
// See if the heat needs to be turned on or off
294-
CheckIfHeatNeedsToBeTurnedOnOrOff();
352+
ChangeHeatingIfNecessary();
295353
}
296354

297355
void WaterHeaterManagementDelegate::DrawOffHotWater(Percent percentageReplaced, uint16_t replacedWaterTemperature)
298356
{
299-
// Only supported in the kTankPercent is supported.
357+
// First calculate the new average water temperature
358+
mWaterTemperature = (mWaterTemperature * (100 - percentageReplaced) + replacedWaterTemperature * percentageReplaced) / 100;
359+
300360
// Replaces percentageReplaced% of the water in the tank with water of a temperature replacedWaterTemperature
361+
// Only supported if the kTankPercent feature is supported.
301362
if (mpWhmInstance != nullptr && mpWhmInstance->HasFeature(Feature::kTankPercent))
302363
{
303-
// See if all of the water has now been replaced with replacedWaterTemperature
304-
if (mTankPercentage >= percentageReplaced)
305-
{
306-
mTankPercentage = static_cast<Percent>(mTankPercentage - percentageReplaced);
307-
}
308-
else
309-
{
310-
mTankPercentage = 0;
311-
}
312-
313-
mReplacedWaterTemperature = replacedWaterTemperature;
364+
SetTankPercentage(CalculateTankPercentage());
314365

315-
CheckIfHeatNeedsToBeTurnedOnOrOff();
366+
ChangeHeatingIfNecessary();
316367
}
317368
}
318369

319370
bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const
320371
{
321-
// Determine the target temperature. If a boost command is in progress and has a mBoostTemporarySetpoint value use that as the
322-
// target temperature.
323-
// Note, in practise the actual heating is likely to be controlled by the thermostat's occupiedHeatingSetpoint most of the
324-
// time, and the TemporarySetpoint (if not null) would be overiding the thermostat's occupiedHeatingSetpoint.
325-
// However, this code doesn't rely upon the thermostat cluster.
326-
uint16_t targetTemperature = (mBoostState == BoostStateEnum::kActive && mBoostTemporarySetpoint.HasValue())
327-
? static_cast<uint16_t>(mBoostTemporarySetpoint.Value())
328-
: mTargetWaterTemperature;
372+
uint16_t targetTemperature = GetActiveTargetWaterTemperature();
329373

330374
VerifyOrReturnValue(mWaterTemperature >= targetTemperature, false);
331375

@@ -357,7 +401,7 @@ bool WaterHeaterManagementDelegate::HasWaterTemperatureReachedTarget() const
357401
return true;
358402
}
359403

360-
Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff()
404+
Status WaterHeaterManagementDelegate::ChangeHeatingIfNecessary()
361405
{
362406
VerifyOrReturnError(mpWhmManufacturer != nullptr, Status::InvalidInState);
363407

@@ -384,6 +428,12 @@ Status WaterHeaterManagementDelegate::CheckIfHeatNeedsToBeTurnedOnOrOff()
384428
mBoostEmergencyBoost.ClearValue();
385429

386430
status = mpWhmManufacturer->BoostCommandCancelled();
431+
432+
CHIP_ERROR err = GenerateBoostEndedEvent();
433+
if (err != CHIP_NO_ERROR)
434+
{
435+
ChipLogError(AppServer, "ChangeHeatingIfNecessary: Failed to send BoostEnded event: %" CHIP_ERROR_FORMAT, err.Format());
436+
}
387437
}
388438

389439
// Turn the heating off
@@ -457,5 +507,6 @@ Status WaterHeaterManagementDelegate::SetWaterHeaterMode(uint8_t modeValue)
457507
return status;
458508
}
459509

460-
return CheckIfHeatNeedsToBeTurnedOnOrOff();
510+
return ChangeHeatingIfNecessary();
461511
}
512+

examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp

+14-22
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ CHIP_ERROR WhmManufacturer::Init()
4545

4646
dg->SetHeaterTypes(BitMask<WaterHeaterHeatSourceBitmap>(WaterHeaterHeatSourceBitmap::kImmersionElement1));
4747
dg->SetHeatDemand(BitMask<WaterHeaterHeatSourceBitmap>(WaterHeaterHeatSourceBitmap::kImmersionElement1));
48-
dg->SetEstimatedHeatRequired(10000);
48+
dg->SetColdWaterTemperature(2000);
4949

5050
return CHIP_NO_ERROR;
5151
}
@@ -65,38 +65,30 @@ BitMask<WaterHeaterHeatSourceBitmap> WhmManufacturer::DetermineHeatingSources()
6565
}
6666

6767
// A list of valid heaterTypes
68-
uint8_t waterHeaterTypeValues[] = {
69-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kImmersionElement1),
70-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kImmersionElement2),
71-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kHeatPump),
72-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kBoiler),
73-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kOther),
74-
};
75-
76-
// The corresponding list of valid headerDemands
77-
uint8_t waterHeaterDemandValues[] = {
78-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kImmersionElement1),
79-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kImmersionElement2),
80-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kHeatPump),
81-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kBoiler),
82-
static_cast<uint8_t>(WaterHeaterHeatSourceBitmap::kOther),
68+
BitMask<WaterHeaterHeatSourceBitmap> waterHeaterTypeValues[] = {
69+
BitMask<WaterHeaterHeatSourceBitmap>(WaterHeaterHeatSourceBitmap::kImmersionElement1),
70+
BitMask<WaterHeaterHeatSourceBitmap>(WaterHeaterHeatSourceBitmap::kImmersionElement2),
71+
BitMask<WaterHeaterHeatSourceBitmap>(WaterHeaterHeatSourceBitmap::kHeatPump),
72+
BitMask<WaterHeaterHeatSourceBitmap>(WaterHeaterHeatSourceBitmap::kBoiler),
73+
BitMask<WaterHeaterHeatSourceBitmap>(WaterHeaterHeatSourceBitmap::kOther),
8374
};
8475

8576
// Iterate across the valid waterHeaterTypes seeing which heating sources are available based on heaterTypes.
8677
// Set the corresponding bit in the heaterDemand bitmap.
87-
BitMask<WaterHeaterHeatSourceBitmap> heaterTypes = dg->GetHeaterTypes();
78+
BitMask<WaterHeaterHeatSourceBitmap> waterHeaterTypes = dg->GetHeaterTypes();
79+
80+
BitMask<WaterHeaterHeatSourceBitmap> waterHeaterDemand;
8881

89-
uint8_t heaterDemandMask = 0;
90-
for (uint16_t idx = 0; idx < static_cast<uint16_t>(sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0])); idx++)
82+
for (auto & waterHeaterType : waterHeaterTypeValues)
9183
{
9284
// Is this heating source being used?
93-
if (heaterTypes.Raw() & waterHeaterTypeValues[idx])
85+
if (waterHeaterTypes.Has(waterHeaterType))
9486
{
95-
heaterDemandMask |= waterHeaterDemandValues[idx];
87+
waterHeaterDemand.Set(waterHeaterType);
9688
}
9789
}
9890

99-
return BitMask<WaterHeaterHeatSourceBitmap>(heaterDemandMask);
91+
return waterHeaterDemand;
10092
}
10193

10294
Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost)

0 commit comments

Comments
 (0)