Skip to content

Commit 04b18d3

Browse files
committed
[HVAC] Clear ActivePresetHandle attribute when changing relevant setpoint attributes
1 parent f299484 commit 04b18d3

File tree

5 files changed

+67
-1
lines changed

5 files changed

+67
-1
lines changed

scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -2217,6 +2217,7 @@
22172217
}; \
22182218
const EmberAfGenericClusterFunction chipFuncArrayThermostatServer[] = { \
22192219
(EmberAfGenericClusterFunction) emberAfThermostatClusterServerInitCallback, \
2220+
(EmberAfGenericClusterFunction) MatterThermostatClusterServerAttributeChangedCallback, \
22202221
(EmberAfGenericClusterFunction) MatterThermostatClusterServerShutdownCallback, \
22212222
(EmberAfGenericClusterFunction) MatterThermostatClusterServerPreAttributeChangedCallback, \
22222223
}; \
@@ -3756,7 +3757,7 @@
37563757
.attributes = ZAP_ATTRIBUTE_INDEX(616), \
37573758
.attributeCount = 26, \
37583759
.clusterSize = 72, \
3759-
.mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(SHUTDOWN_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \
3760+
.mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION) | ZAP_CLUSTER_MASK(SHUTDOWN_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \
37603761
.functions = chipFuncArrayThermostatServer, \
37613762
.acceptedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 241 ), \
37623763
.generatedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 246 ), \

src/app/clusters/thermostat-server/thermostat-server.cpp

+51
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,52 @@ void ThermostatAttrAccess::OnFabricRemoved(const FabricTable & fabricTable, Fabr
468468
}
469469
}
470470

471+
void MatterThermostatClusterServerAttributeChangedCallback(const chip::app::ConcreteAttributePath & attributePath)
472+
{
473+
uint32_t flags;
474+
if (FeatureMap::Get(attributePath.mEndpointId, &flags) != Status::Success)
475+
{
476+
ChipLogError(Zcl, "MatterThermostatClusterServerAttributeChangedCallback: could not get feature flags");
477+
return;
478+
}
479+
480+
auto featureMap = chip::BitMask<Feature, uint32_t>(flags);
481+
if (!featureMap.Has(Feature::kPresets))
482+
{
483+
// This server does not support presets, so nothing to do
484+
return;
485+
}
486+
487+
bool occupied = true;
488+
if (featureMap.Has(Feature::kOccupancy))
489+
{
490+
chip::BitMask<OccupancyBitmap, uint8_t> occupancy;
491+
if (Occupancy::Get(attributePath.mEndpointId, &occupancy) == Status::Success)
492+
{
493+
occupied = occupancy.Has(OccupancyBitmap::kOccupied);
494+
}
495+
}
496+
497+
bool clearActivePreset = false;
498+
switch (attributePath.mAttributeId)
499+
{
500+
case OccupiedHeatingSetpoint::Id:
501+
case OccupiedCoolingSetpoint::Id:
502+
clearActivePreset = occupied;
503+
break;
504+
case UnoccupiedHeatingSetpoint::Id:
505+
case UnoccupiedCoolingSetpoint::Id:
506+
clearActivePreset = !occupied;
507+
break;
508+
}
509+
if (!clearActivePreset)
510+
{
511+
return;
512+
}
513+
ChipLogProgress(Zcl, "Setting active preset to null");
514+
gThermostatAttrAccess.SetActivePreset(attributePath.mEndpointId, std::nullopt);
515+
}
516+
471517
} // namespace Thermostat
472518
} // namespace Clusters
473519
} // namespace app
@@ -762,6 +808,11 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr
762808
}
763809
}
764810

811+
void MatterThermostatClusterServerAttributeChangedCallback(const chip::app::ConcreteAttributePath & attributePath)
812+
{
813+
Thermostat::MatterThermostatClusterServerAttributeChangedCallback(attributePath);
814+
}
815+
765816
bool emberAfThermostatClusterClearWeeklyScheduleCallback(app::CommandHandler * commandObj,
766817
const app::ConcreteCommandPath & commandPath,
767818
const Commands::ClearWeeklySchedule::DecodableType & commandData)

src/app/clusters/thermostat-server/thermostat-server.h

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public
207207
friend void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext);
208208

209209
friend void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint);
210+
friend void MatterThermostatClusterServerAttributeChangedCallback(const chip::app::ConcreteAttributePath & attributePath);
210211

211212
friend bool emberAfThermostatClusterSetActivePresetRequestCallback(
212213
CommandHandler * commandObj, const ConcreteCommandPath & commandPath,

src/app/common/templates/config-data.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ ClustersWithAttributeChangedFunctions:
7272
- Pump Configuration and Control
7373
- Window Covering
7474
- Fan Control
75+
- Thermostat
7576

7677
ClustersWithShutdownFunctions:
7778
- Barrier Control

src/python_testing/TC_TSTAT_4_2.py

+12
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,11 @@ async def test_TC_TSTAT_4_2(self):
287287
logger.info(f"Rx'd Presets: {presets}")
288288
asserts.assert_equal(presets, new_presets_with_handle, "Presets were not updated which is not expected")
289289

290+
# Send the SetActivePresetRequest command
291+
await self.send_set_active_preset_handle_request_command(value=b'\x03')
292+
293+
activePresetHandle = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.ActivePresetHandle)
294+
290295
self.step("5")
291296
if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")):
292297

@@ -323,6 +328,13 @@ async def test_TC_TSTAT_4_2(self):
323328
# Send the AtomicRequest commit command and expect InvalidInState for presets.
324329
await self.send_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.InvalidInState)
325330

331+
# Write the occupied cooling setpoint to a different value
332+
await self.write_single_attribute(attribute_value=cluster.Attributes.OccupiedCoolingSetpoint(2300), endpoint_id=endpoint)
333+
334+
activePresetHandle = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.ActivePresetHandle)
335+
logger.info(f"Rx'd ActivePresetHandle: {activePresetHandle}")
336+
asserts.assert_equal(activePresetHandle, NullValue, "Active preset handle was not cleared as expected")
337+
326338
self.step("7")
327339
if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")):
328340

0 commit comments

Comments
 (0)