diff --git a/examples/air-purifier-app/air-purifier-common/README.md b/examples/air-purifier-app/air-purifier-common/README.md new file mode 100644 index 00000000000000..2ed26cacf9a229 --- /dev/null +++ b/examples/air-purifier-app/air-purifier-common/README.md @@ -0,0 +1,353 @@ +# CHIP Air Purifier Example + +This example implements the following PICS: + +``` +# Fan Control +FAN.S=1 +FAN.C=0 +FAN.S.F00=1 +FAN.S.F01=1 +FAN.S.F02=1 +FAN.S.F03=1 +FAN.S.F04=1 +FAN.S.F05=1 +FAN.S.A0000=1 +FAN.S.A0001=1 +FAN.S.A0002=1 +FAN.S.A0003=1 +FAN.S.A0004=1 +FAN.S.A0005=1 +FAN.S.A0006=1 +FAN.S.A0007=1 +FAN.S.A0008=1 +FAN.S.A0009=1 +FAN.S.A000A=1 +FAN.S.A000B=1 +FAN.S.C00.Rsp=1 + +# HEPA Filter Monitoring Cluster +HEPAFREMON.S=1 +HEPAFREMON.C=0 +HEPAFREMON.S.F00=1 +HEPAFREMON.S.F01=1 +HEPAFREMON.S.F02=1 +HEPAFREMON.S.A0000=1 +HEPAFREMON.S.A0001=1 +HEPAFREMON.S.A0002=1 +HEPAFREMON.S.A0003=1 +HEPAFREMON.S.A0004=1 +HEPAFREMON.S.A0005=1 +HEPAFREMON.S.C00.Rsp=1 + +# Activated Carbon Filter Monitoring Cluster +ACFREMON.S=1 +ACFREMON.C=0 +ACFREMON.S.F00=1 +ACFREMON.S.F01=1 +ACFREMON.S.F02=1 +ACFREMON.S.A0000=1 +ACFREMON.S.A0001=1 +ACFREMON.S.A0002=1 +ACFREMON.S.A0003=1 +ACFREMON.S.A0004=1 +ACFREMON.S.A0005=1 +ACFREMON.S.C00.Rsp=1 + +# Air Quality Cluster +AIRQUAL.C=0 +AIRQUAL.S=1 +AIRQUAL.S.F00=1 +AIRQUAL.S.F01=1 +AIRQUAL.S.F02=1 +AIRQUAL.S.F03=1 +AIRQUAL.S.A0000=1 +AIRQUAL.M.AirQualityChange=0 + +# Concentration Measurement CLusters +CDOCONC.C=0 +CDOCONC.S=1 +CDOCONC.S.F00=1 +CDOCONC.S.F01=1 +CDOCONC.S.F02=1 +CDOCONC.S.F03=1 +CDOCONC.S.F04=1 +CDOCONC.S.F05=1 +CDOCONC.S.A0000=1 +CDOCONC.S.A0001=1 +CDOCONC.S.A0002=1 +CDOCONC.S.A0003=1 +CDOCONC.S.A0004=1 +CDOCONC.S.A0005=1 +CDOCONC.S.A0006=1 +CDOCONC.S.A0007=1 +CDOCONC.S.A0008=1 +CDOCONC.S.A0009=1 +CDOCONC.S.A000a=1 + +CMOCONC.C=0 +CMOCONC.S=1 +CMOCONC.S.F00=1 +CMOCONC.S.F01=1 +CMOCONC.S.F02=1 +CMOCONC.S.F03=1 +CMOCONC.S.F04=1 +CMOCONC.S.F05=1 +CMOCONC.S.A0000=1 +CMOCONC.S.A0001=1 +CMOCONC.S.A0002=1 +CMOCONC.S.A0003=1 +CMOCONC.S.A0004=1 +CMOCONC.S.A0005=1 +CMOCONC.S.A0006=1 +CMOCONC.S.A0007=1 +CMOCONC.S.A0008=1 +CMOCONC.S.A0009=1 +CMOCONC.S.A000a=1 + +NDOCONC.C=0 +NDOCONC.S=1 +NDOCONC.S.F00=1 +NDOCONC.S.F01=1 +NDOCONC.S.F02=1 +NDOCONC.S.F03=1 +NDOCONC.S.F04=1 +NDOCONC.S.F05=1 +NDOCONC.S.A0000=1 +NDOCONC.S.A0001=1 +NDOCONC.S.A0002=1 +NDOCONC.S.A0003=1 +NDOCONC.S.A0004=1 +NDOCONC.S.A0005=1 +NDOCONC.S.A0006=1 +NDOCONC.S.A0007=1 +NDOCONC.S.A0008=1 +NDOCONC.S.A0009=1 +NDOCONC.S.A000a=1 + +OZCONC.C=0 +OZCONC.S=1 +OZCONC.S.F00=1 +OZCONC.S.F01=1 +OZCONC.S.F02=1 +OZCONC.S.F03=1 +OZCONC.S.F04=1 +OZCONC.S.F05=1 +OZCONC.S.A0000=1 +OZCONC.S.A0001=1 +OZCONC.S.A0002=1 +OZCONC.S.A0003=1 +OZCONC.S.A0004=1 +OZCONC.S.A0005=1 +OZCONC.S.A0006=1 +OZCONC.S.A0007=1 +OZCONC.S.A0008=1 +OZCONC.S.A0009=1 +OZCONC.S.A000a=1 + +PMICONC.C=0 +PMICONC.S=1 +PMICONC.S.F00=1 +PMICONC.S.F01=1 +PMICONC.S.F02=1 +PMICONC.S.F03=1 +PMICONC.S.F04=1 +PMICONC.S.F05=1 +PMICONC.S.A0000=1 +PMICONC.S.A0001=1 +PMICONC.S.A0002=1 +PMICONC.S.A0003=1 +PMICONC.S.A0004=1 +PMICONC.S.A0005=1 +PMICONC.S.A0006=1 +PMICONC.S.A0007=1 +PMICONC.S.A0008=1 +PMICONC.S.A0009=1 +PMICONC.S.A000a=1 + +FLDCONC.C=0 +FLDCONC.S=1 +FLDCONC.S.F00=1 +FLDCONC.S.F01=1 +FLDCONC.S.F02=1 +FLDCONC.S.F03=1 +FLDCONC.S.F04=1 +FLDCONC.S.F05=1 +FLDCONC.S.A0000=1 +FLDCONC.S.A0001=1 +FLDCONC.S.A0002=1 +FLDCONC.S.A0003=1 +FLDCONC.S.A0004=1 +FLDCONC.S.A0005=1 +FLDCONC.S.A0006=1 +FLDCONC.S.A0007=1 +FLDCONC.S.A0008=1 +FLDCONC.S.A0009=1 +FLDCONC.S.A000a=1 + +PMHCONC.C=0 +PMHCONC.S=1 +PMHCONC.S.F00=1 +PMHCONC.S.F01=1 +PMHCONC.S.F02=1 +PMHCONC.S.F03=1 +PMHCONC.S.F04=1 +PMHCONC.S.F05=1 +PMHCONC.S.A0000=1 +PMHCONC.S.A0001=1 +PMHCONC.S.A0002=1 +PMHCONC.S.A0003=1 +PMHCONC.S.A0004=1 +PMHCONC.S.A0005=1 +PMHCONC.S.A0006=1 +PMHCONC.S.A0007=1 +PMHCONC.S.A0008=1 +PMHCONC.S.A0009=1 +PMHCONC.S.A000a=1 + +PMKCONC.C=0 +PMKCONC.S=1 +PMKCONC.S.F00=1 +PMKCONC.S.F01=1 +PMKCONC.S.F02=1 +PMKCONC.S.F03=1 +PMKCONC.S.F04=1 +PMKCONC.S.F05=1 +PMKCONC.S.A0000=1 +PMKCONC.S.A0001=1 +PMKCONC.S.A0002=1 +PMKCONC.S.A0003=1 +PMKCONC.S.A0004=1 +PMKCONC.S.A0005=1 +PMKCONC.S.A0006=1 +PMKCONC.S.A0007=1 +PMKCONC.S.A0008=1 +PMKCONC.S.A0009=1 +PMKCONC.S.A000a=1 + +TVOCCONC.C=0 +TVOCCONC.S=1 +TVOCCONC.S.F00=1 +TVOCCONC.S.F01=1 +TVOCCONC.S.F02=1 +TVOCCONC.S.F03=1 +TVOCCONC.S.F04=1 +TVOCCONC.S.F05=1 +TVOCCONC.S.A0000=1 +TVOCCONC.S.A0001=1 +TVOCCONC.S.A0002=1 +TVOCCONC.S.A0003=1 +TVOCCONC.S.A0004=1 +TVOCCONC.S.A0005=1 +TVOCCONC.S.A0006=1 +TVOCCONC.S.A0007=1 +TVOCCONC.S.A0008=1 +TVOCCONC.S.A0009=1 +TVOCCONC.S.A000a=1 + +RNCONC.C=0 +RNCONC.S=1 +RNCONC.S.F00=1 +RNCONC.S.F01=1 +RNCONC.S.F02=1 +RNCONC.S.F03=1 +RNCONC.S.F04=1 +RNCONC.S.F05=1 +RNCONC.S.A0000=1 +RNCONC.S.A0001=1 +RNCONC.S.A0002=1 +RNCONC.S.A0003=1 +RNCONC.S.A0004=1 +RNCONC.S.A0005=1 +RNCONC.S.A0006=1 +RNCONC.S.A0007=1 +RNCONC.S.A0008=1 +RNCONC.S.A0009=1 +RNCONC.S.A000a=1 + +# Temperature Measurement Cluster +TMP.S=1 +TMP.S.A0000=1 +TMP.S.A0001=1 +TMP.S.A0002=1 +TMP.S.A0003=1 +TMP.M.ManuallyControlled=0 + +# Relative Humidity Cluster +RH.S=1 +RH.S.A0000=1 +RH.S.A0001=1 +RH.S.A0002=1 +RH.S.A0003=1 +RH.M.ManuallyControlled=0 + +# Thermostat Cluster +TSTAT.S = 1 +TSTAT.S.F00 = 1 +TSTAT.S.F01 = 0 +TSTAT.S.F02 = 0 +TSTAT.S.F03 = 0 +TSTAT.S.F04 = 0 +TSTAT.S.F05 = 0 +TSTAT.S.F06 = 0 + +TSTAT.S.A0000 = 1 +TSTAT.S.A0001 = 0 +TSTAT.S.A0002 = 0 +TSTAT.S.A0003 = 1 +TSTAT.S.A0004 = 1 +TSTAT.S.A0005 = 0 +TSTAT.S.A0006 = 0 +TSTAT.S.A0007 = 0 +TSTAT.S.A0008 = 0 +TSTAT.S.A0009 = 0 +TSTAT.S.A0010 = 0 +TSTAT.S.A0011 = 0 +TSTAT.S.A0012 = 1 +TSTAT.S.A0013 = 0 +TSTAT.S.A0014 = 0 +TSTAT.S.A0015 = 0 +TSTAT.S.A0016 = 0 +TSTAT.S.A0017 = 0 +TSTAT.S.A0018 = 0 +TSTAT.S.A0019 = 0 +TSTAT.S.A001a = 0 +TSTAT.S.A001b = 1 +TSTAT.S.A001c = 1 +TSTAT.S.A001d = 0 +TSTAT.S.A001e = 0 +TSTAT.S.A0020 = 0 +TSTAT.S.A0021 = 0 +TSTAT.S.A0022 = 0 +TSTAT.S.A0023 = 0 +TSTAT.S.A0024 = 0 +TSTAT.S.A0025 = 0 +TSTAT.S.A0029 = 1 +TSTAT.S.A0030 = 0 +TSTAT.S.A0031 = 0 +TSTAT.S.A0032 = 0 +TSTAT.S.A0034 = 0 +TSTAT.S.A0035 = 0 +TSTAT.S.A0036 = 0 +TSTAT.S.A0037 = 0 +TSTAT.S.A0038 = 0 +TSTAT.S.A0039 = 0 +TSTAT.S.A003a = 0 +TSTAT.S.A0040 = 0 +TSTAT.S.A0041 = 0 +TSTAT.S.A0042 = 0 +TSTAT.S.A0043 = 0 +TSTAT.S.A0044 = 0 +TSTAT.S.A0045 = 0 +TSTAT.S.A0046 = 0 +TSTAT.S.A0047 = 0 +TSTAT.S.M.MinSetpointDeadBandWritable = 0 +TSTAT.S.M.HVACSystemTypeConfigurationWritable = 0 + +# Server Commands +TSTAT.S.C00.Rsp = 1 +TSTAT.S.C01.Rsp = 0 +TSTAT.S.C02.Rsp = 0 +TSTAT.S.C03.Rsp = 0 +TSTAT.S.C04.Rsp = 0 +``` diff --git a/examples/air-purifier-app/air-purifier-common/air-purifier-app.matter b/examples/air-purifier-app/air-purifier-common/air-purifier-app.matter index 33de78383c41fc..ac6dc5a4c04dec 100644 --- a/examples/air-purifier-app/air-purifier-common/air-purifier-app.matter +++ b/examples/air-purifier-app/air-purifier-common/air-purifier-app.matter @@ -1207,6 +1207,377 @@ cluster ActivatedCarbonFilterMonitoring = 114 { command ResetCondition(): DefaultSuccess = 0; } +/** An interface for configuring and controlling the functionality of a thermostat. */ +cluster Thermostat = 513 { + revision 6; + + enum ACCapacityFormatEnum : enum8 { + kBTUh = 0; + } + + enum ACCompressorTypeEnum : enum8 { + kUnknown = 0; + kT1 = 1; + kT2 = 2; + kT3 = 3; + } + + enum ACLouverPositionEnum : enum8 { + kClosed = 1; + kOpen = 2; + kQuarter = 3; + kHalf = 4; + kThreeQuarters = 5; + } + + enum ACRefrigerantTypeEnum : enum8 { + kUnknown = 0; + kR22 = 1; + kR410a = 2; + kR407c = 3; + } + + enum ACTypeEnum : enum8 { + kUnknown = 0; + kCoolingFixed = 1; + kHeatPumpFixed = 2; + kCoolingInverter = 3; + kHeatPumpInverter = 4; + } + + enum ControlSequenceOfOperationEnum : enum8 { + kCoolingOnly = 0; + kCoolingWithReheat = 1; + kHeatingOnly = 2; + kHeatingWithReheat = 3; + kCoolingAndHeating = 4; + kCoolingAndHeatingWithReheat = 5; + } + + enum PresetScenarioEnum : enum8 { + kUnspecified = 0; + kOccupied = 1; + kUnoccupied = 2; + kSleep = 3; + kWake = 4; + kVacation = 5; + kUserDefined = 6; + } + + enum SetpointChangeSourceEnum : enum8 { + kManual = 0; + kSchedule = 1; + kExternal = 2; + } + + enum SetpointRaiseLowerModeEnum : enum8 { + kHeat = 0; + kCool = 1; + kBoth = 2; + } + + enum StartOfWeekEnum : enum8 { + kSunday = 0; + kMonday = 1; + kTuesday = 2; + kWednesday = 3; + kThursday = 4; + kFriday = 5; + kSaturday = 6; + } + + enum SystemModeEnum : enum8 { + kOff = 0; + kAuto = 1; + kCool = 3; + kHeat = 4; + kEmergencyHeat = 5; + kPrecooling = 6; + kFanOnly = 7; + kDry = 8; + kSleep = 9; + } + + enum TemperatureSetpointHoldEnum : enum8 { + kSetpointHoldOff = 0; + kSetpointHoldOn = 1; + } + + enum ThermostatRunningModeEnum : enum8 { + kOff = 0; + kCool = 3; + kHeat = 4; + } + + bitmap ACErrorCodeBitmap : bitmap32 { + kCompressorFail = 0x1; + kRoomSensorFail = 0x2; + kOutdoorSensorFail = 0x4; + kCoilSensorFail = 0x8; + kFanFail = 0x10; + } + + bitmap Feature : bitmap32 { + kHeating = 0x1; + kCooling = 0x2; + kOccupancy = 0x4; + kScheduleConfiguration = 0x8; + kSetback = 0x10; + kAutoMode = 0x20; + kLocalTemperatureNotExposed = 0x40; + kMatterScheduleConfiguration = 0x80; + kPresets = 0x100; + kSetpoints = 0x200; + kQueuedPresetsSupported = 0x400; + } + + bitmap HVACSystemTypeBitmap : bitmap8 { + kCoolingStage = 0x3; + kHeatingStage = 0xC; + kHeatingIsHeatPump = 0x10; + kHeatingUsesFuel = 0x20; + } + + bitmap PresetTypeFeaturesBitmap : bitmap16 { + kAutomatic = 0x1; + kSupportsNames = 0x2; + } + + bitmap ProgrammingOperationModeBitmap : bitmap8 { + kScheduleActive = 0x1; + kAutoRecovery = 0x2; + kEconomy = 0x4; + } + + bitmap RelayStateBitmap : bitmap16 { + kHeat = 0x1; + kCool = 0x2; + kFan = 0x4; + kHeatStage2 = 0x8; + kCoolStage2 = 0x10; + kFanStage2 = 0x20; + kFanStage3 = 0x40; + } + + bitmap RemoteSensingBitmap : bitmap8 { + kLocalTemperature = 0x1; + kOutdoorTemperature = 0x2; + kOccupancy = 0x4; + } + + bitmap ScheduleDayOfWeekBitmap : bitmap8 { + kSunday = 0x1; + kMonday = 0x2; + kTuesday = 0x4; + kWednesday = 0x8; + kThursday = 0x10; + kFriday = 0x20; + kSaturday = 0x40; + kAway = 0x80; + } + + bitmap ScheduleModeBitmap : bitmap8 { + kHeatSetpointPresent = 0x1; + kCoolSetpointPresent = 0x2; + } + + bitmap ScheduleTypeFeaturesBitmap : bitmap16 { + kSupportsPresets = 0x1; + kSupportsSetpoints = 0x2; + kSupportsNames = 0x4; + kSupportsOff = 0x8; + } + + bitmap TemperatureSetpointHoldPolicyBitmap : bitmap8 { + kHoldDurationElapsed = 0x1; + kHoldDurationElapsedOrPresetChanged = 0x2; + } + + struct ScheduleTransitionStruct { + ScheduleDayOfWeekBitmap dayOfWeek = 0; + int16u transitionTime = 1; + optional octet_string<16> presetHandle = 2; + optional SystemModeEnum systemMode = 3; + optional temperature coolingSetpoint = 4; + optional temperature heatingSetpoint = 5; + } + + struct ScheduleStruct { + nullable octet_string<16> scheduleHandle = 0; + SystemModeEnum systemMode = 1; + optional char_string<64> name = 2; + optional octet_string<16> presetHandle = 3; + ScheduleTransitionStruct transitions[] = 4; + optional nullable boolean builtIn = 5; + } + + struct PresetStruct { + nullable octet_string<16> presetHandle = 0; + PresetScenarioEnum presetScenario = 1; + optional nullable char_string<64> name = 2; + optional temperature coolingSetpoint = 3; + optional temperature heatingSetpoint = 4; + nullable boolean builtIn = 5; + } + + struct PresetTypeStruct { + PresetScenarioEnum presetScenario = 0; + int8u numberOfPresets = 1; + PresetTypeFeaturesBitmap presetTypeFeatures = 2; + } + + struct QueuedPresetStruct { + nullable octet_string<16> presetHandle = 0; + nullable epoch_s transitionTimestamp = 1; + } + + struct ScheduleTypeStruct { + SystemModeEnum systemMode = 0; + int8u numberOfSchedules = 1; + ScheduleTypeFeaturesBitmap scheduleTypeFeatures = 2; + } + + struct WeeklyScheduleTransitionStruct { + int16u transitionTime = 0; + nullable temperature heatSetpoint = 1; + nullable temperature coolSetpoint = 2; + } + + readonly attribute nullable temperature localTemperature = 0; + readonly attribute optional nullable temperature outdoorTemperature = 1; + readonly attribute optional bitmap8 occupancy = 2; + readonly attribute optional temperature absMinHeatSetpointLimit = 3; + readonly attribute optional temperature absMaxHeatSetpointLimit = 4; + readonly attribute optional temperature absMinCoolSetpointLimit = 5; + readonly attribute optional temperature absMaxCoolSetpointLimit = 6; + readonly attribute optional int8u PICoolingDemand = 7; + readonly attribute optional int8u PIHeatingDemand = 8; + attribute access(write: manage) optional bitmap8 HVACSystemTypeConfiguration = 9; + attribute access(write: manage) optional int8s localTemperatureCalibration = 16; + attribute optional int16s occupiedCoolingSetpoint = 17; + attribute optional int16s occupiedHeatingSetpoint = 18; + attribute optional int16s unoccupiedCoolingSetpoint = 19; + attribute optional int16s unoccupiedHeatingSetpoint = 20; + attribute access(write: manage) optional int16s minHeatSetpointLimit = 21; + attribute access(write: manage) optional int16s maxHeatSetpointLimit = 22; + attribute access(write: manage) optional int16s minCoolSetpointLimit = 23; + attribute access(write: manage) optional int16s maxCoolSetpointLimit = 24; + attribute access(write: manage) optional int8s minSetpointDeadBand = 25; + attribute access(write: manage) optional RemoteSensingBitmap remoteSensing = 26; + attribute access(write: manage) ControlSequenceOfOperationEnum controlSequenceOfOperation = 27; + attribute access(write: manage) SystemModeEnum systemMode = 28; + readonly attribute optional ThermostatRunningModeEnum thermostatRunningMode = 30; + readonly attribute optional StartOfWeekEnum startOfWeek = 32; + readonly attribute optional int8u numberOfWeeklyTransitions = 33; + readonly attribute optional int8u numberOfDailyTransitions = 34; + attribute access(write: manage) optional TemperatureSetpointHoldEnum temperatureSetpointHold = 35; + attribute access(write: manage) optional nullable int16u temperatureSetpointHoldDuration = 36; + attribute access(write: manage) optional ProgrammingOperationModeBitmap thermostatProgrammingOperationMode = 37; + readonly attribute optional RelayStateBitmap thermostatRunningState = 41; + readonly attribute optional SetpointChangeSourceEnum setpointChangeSource = 48; + readonly attribute optional nullable int16s setpointChangeAmount = 49; + readonly attribute optional epoch_s setpointChangeSourceTimestamp = 50; + attribute access(write: manage) optional nullable int8u occupiedSetback = 52; + readonly attribute optional nullable int8u occupiedSetbackMin = 53; + readonly attribute optional nullable int8u occupiedSetbackMax = 54; + attribute access(write: manage) optional nullable int8u unoccupiedSetback = 55; + readonly attribute optional nullable int8u unoccupiedSetbackMin = 56; + readonly attribute optional nullable int8u unoccupiedSetbackMax = 57; + attribute access(write: manage) optional int8u emergencyHeatDelta = 58; + attribute access(write: manage) optional ACTypeEnum ACType = 64; + attribute access(write: manage) optional int16u ACCapacity = 65; + attribute access(write: manage) optional ACRefrigerantTypeEnum ACRefrigerantType = 66; + attribute access(write: manage) optional ACCompressorTypeEnum ACCompressorType = 67; + attribute access(write: manage) optional ACErrorCodeBitmap ACErrorCode = 68; + attribute access(write: manage) optional ACLouverPositionEnum ACLouverPosition = 69; + readonly attribute optional nullable temperature ACCoilTemperature = 70; + attribute access(write: manage) optional ACCapacityFormatEnum ACCapacityformat = 71; + readonly attribute optional PresetTypeStruct presetTypes[] = 72; + readonly attribute optional ScheduleTypeStruct scheduleTypes[] = 73; + readonly attribute optional int8u numberOfPresets = 74; + readonly attribute optional int8u numberOfSchedules = 75; + readonly attribute optional int8u numberOfScheduleTransitions = 76; + readonly attribute optional nullable int8u numberOfScheduleTransitionPerDay = 77; + readonly attribute optional nullable octet_string<16> activePresetHandle = 78; + readonly attribute optional nullable octet_string<16> activeScheduleHandle = 79; + attribute access(write: manage) optional PresetStruct presets[] = 80; + attribute access(write: manage) optional ScheduleStruct schedules[] = 81; + readonly attribute optional boolean presetsSchedulesEditable = 82; + readonly attribute optional TemperatureSetpointHoldPolicyBitmap temperatureSetpointHoldPolicy = 83; + readonly attribute optional nullable epoch_s setpointHoldExpiryTimestamp = 84; + readonly attribute optional nullable QueuedPresetStruct queuedPreset = 85; + 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 SetpointRaiseLowerRequest { + SetpointRaiseLowerModeEnum mode = 0; + int8s amount = 1; + } + + response struct GetWeeklyScheduleResponse = 0 { + int8u numberOfTransitionsForSequence = 0; + ScheduleDayOfWeekBitmap dayOfWeekForSequence = 1; + ScheduleModeBitmap modeForSequence = 2; + WeeklyScheduleTransitionStruct transitions[] = 3; + } + + request struct SetWeeklyScheduleRequest { + int8u numberOfTransitionsForSequence = 0; + ScheduleDayOfWeekBitmap dayOfWeekForSequence = 1; + ScheduleModeBitmap modeForSequence = 2; + WeeklyScheduleTransitionStruct transitions[] = 3; + } + + request struct GetWeeklyScheduleRequest { + ScheduleDayOfWeekBitmap daysToReturn = 0; + ScheduleModeBitmap modeToReturn = 1; + } + + request struct SetActiveScheduleRequestRequest { + octet_string<16> scheduleHandle = 0; + } + + request struct SetActivePresetRequestRequest { + octet_string<16> presetHandle = 0; + optional int16u delayMinutes = 1; + } + + request struct StartPresetsSchedulesEditRequestRequest { + int16u timeoutSeconds = 0; + } + + request struct SetTemperatureSetpointHoldPolicyRequest { + TemperatureSetpointHoldPolicyBitmap temperatureSetpointHoldPolicy = 0; + } + + /** Command description for SetpointRaiseLower */ + command SetpointRaiseLower(SetpointRaiseLowerRequest): DefaultSuccess = 0; + /** Command description for SetWeeklySchedule */ + command access(invoke: manage) SetWeeklySchedule(SetWeeklyScheduleRequest): DefaultSuccess = 1; + /** Command description for GetWeeklySchedule */ + command GetWeeklySchedule(GetWeeklyScheduleRequest): GetWeeklyScheduleResponse = 2; + /** This command is used to clear the weekly schedule. The ClearWeeklySchedule command has no payload. */ + command access(invoke: manage) ClearWeeklySchedule(): DefaultSuccess = 3; + /** This command is used to set the active schedule. */ + command SetActiveScheduleRequest(SetActiveScheduleRequestRequest): DefaultSuccess = 5; + /** This command is used to set the active preset. */ + command SetActivePresetRequest(SetActivePresetRequestRequest): DefaultSuccess = 6; + /** This command is used to start editing the presets and schedules. */ + command access(invoke: manage) StartPresetsSchedulesEditRequest(StartPresetsSchedulesEditRequestRequest): DefaultSuccess = 7; + /** This command is used to cancel editing presets and schedules. */ + command access(invoke: manage) CancelPresetsSchedulesEditRequest(): DefaultSuccess = 8; + /** This command is used to notify the server that all edits are done and should be committed. */ + command access(invoke: manage) CommitPresetsSchedulesRequest(): DefaultSuccess = 9; + /** This command is sent to cancel a queued preset. */ + command access(invoke: manage) CancelSetActivePresetRequest(): DefaultSuccess = 10; + /** This command sets the set point hold policy. */ + command SetTemperatureSetpointHoldPolicy(SetTemperatureSetpointHoldPolicyRequest): DefaultSuccess = 11; +} + /** An interface for controlling a fan in a heating/cooling system. */ provisional cluster FanControl = 514 { revision 4; @@ -2407,7 +2778,7 @@ endpoint 2 { } } endpoint 3 { - device type ma_tempsensor = 770, version 1; + device type ma_tempsensor = 770, version 2; server cluster Identify { @@ -2451,7 +2822,7 @@ endpoint 3 { } } endpoint 4 { - device type ma_humiditysensor = 775, version 1; + device type ma_humiditysensor = 775, version 2; server cluster Identify { @@ -2494,5 +2865,54 @@ endpoint 4 { ram attribute clusterRevision default = 3; } } +endpoint 5 { + device type ma_thermostat = 769, version 2; + + + server cluster Identify { + ram attribute identifyTime default = 0x0; + ram attribute identifyType default = 0x00; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command Identify; + handle command TriggerEffect; + } + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster Thermostat { + ram attribute localTemperature; + ram attribute absMinHeatSetpointLimit default = 1000; + ram attribute absMaxHeatSetpointLimit default = 3000; + ram attribute occupiedHeatingSetpoint default = 2000; + ram attribute controlSequenceOfOperation default = 2; + ram attribute systemMode default = 0; + ram attribute thermostatRunningState default = 0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 1; + ram attribute clusterRevision default = 6; + + handle command SetpointRaiseLower; + } +} diff --git a/examples/air-purifier-app/air-purifier-common/air-purifier-app.zap b/examples/air-purifier-app/air-purifier-common/air-purifier-app.zap index d71c47a6d98758..b868916264f4ba 100644 --- a/examples/air-purifier-app/air-purifier-common/air-purifier-app.zap +++ b/examples/air-purifier-app/air-purifier-common/air-purifier-app.zap @@ -6514,7 +6514,7 @@ } ], "deviceVersions": [ - 1 + 2 ], "deviceIdentifiers": [ 770 @@ -7039,7 +7039,7 @@ } ], "deviceVersions": [ - 1 + 2 ], "deviceIdentifiers": [ 775 @@ -7545,43 +7545,634 @@ ] } ] - } - ], - "endpoints": [ - { - "endpointTypeName": "MA-rootdevice", - "endpointTypeIndex": 0, - "profileId": 259, - "endpointId": 0, - "networkId": 0 - }, - { - "endpointTypeName": "Anonymous Endpoint Type", - "endpointTypeIndex": 1, - "profileId": 259, - "endpointId": 1, - "networkId": 0 - }, - { - "endpointTypeName": "Anonymous Endpoint Type", - "endpointTypeIndex": 2, - "profileId": 259, - "endpointId": 2, - "networkId": 0 - }, - { - "endpointTypeName": "Anonymous Endpoint Type", - "endpointTypeIndex": 3, - "profileId": 259, - "endpointId": 3, - "networkId": 0 }, { - "endpointTypeName": "Anonymous Endpoint Type", - "endpointTypeIndex": 4, - "profileId": 259, - "endpointId": 4, - "networkId": 0 - } - ] + "id": 6, + "name": "Anonymous Endpoint Type", + "deviceTypeRef": { + "code": 769, + "profileId": 259, + "label": "MA-thermostat", + "name": "MA-thermostat" + }, + "deviceTypes": [ + { + "code": 769, + "profileId": 259, + "label": "MA-thermostat", + "name": "MA-thermostat" + } + ], + "deviceVersions": [ + 2 + ], + "deviceIdentifiers": [ + 769 + ], + "deviceTypeName": "MA-thermostat", + "deviceTypeCode": 769, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Identify", + "code": 3, + "mfgCode": null, + "define": "IDENTIFY_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Identify", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TriggerEffect", + "code": 64, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "IdentifyTime", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "IdentifyType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "IdentifyTypeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x00", + "reportable": 0, + "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": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "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": "4", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "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": "ServerList", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClientList", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "PartsList", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "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": "0", + "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": "2", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Thermostat", + "code": 513, + "mfgCode": null, + "define": "THERMOSTAT_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "SetpointRaiseLower", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "LocalTemperature", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "temperature", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AbsMinHeatSetpointLimit", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "temperature", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1000", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AbsMaxHeatSetpointLimit", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "temperature", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "3000", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "OccupiedHeatingSetpoint", + "code": 18, + "mfgCode": null, + "side": "server", + "type": "int16s", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "2000", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ControlSequenceOfOperation", + "code": 27, + "mfgCode": null, + "side": "server", + "type": "ControlSequenceOfOperationEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "2", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SystemMode", + "code": 28, + "mfgCode": null, + "side": "server", + "type": "SystemModeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ThermostatRunningState", + "code": 41, + "mfgCode": null, + "side": "server", + "type": "RelayStateBitmap", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "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": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "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": "6", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + } + ] + } + ], + "endpoints": [ + { + "endpointTypeName": "MA-rootdevice", + "endpointTypeIndex": 0, + "profileId": 259, + "endpointId": 0, + "networkId": 0 + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 1, + "profileId": 259, + "endpointId": 1, + "networkId": 0 + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 2, + "profileId": 259, + "endpointId": 2, + "networkId": 0 + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 3, + "profileId": 259, + "endpointId": 3, + "networkId": 0 + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 4, + "profileId": 259, + "endpointId": 4, + "networkId": 0 + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 5, + "profileId": 259, + "endpointId": 5, + "networkId": 0 + } + ], + "log": [] } \ No newline at end of file diff --git a/examples/air-purifier-app/air-purifier-common/include/air-purifier-manager.h b/examples/air-purifier-app/air-purifier-common/include/air-purifier-manager.h index 81565d532e4e7e..e50ccd09958283 100644 --- a/examples/air-purifier-app/air-purifier-common/include/air-purifier-manager.h +++ b/examples/air-purifier-app/air-purifier-common/include/air-purifier-manager.h @@ -22,6 +22,7 @@ #include #include #include +#include #pragma once @@ -52,12 +53,13 @@ class AirPurifierManager : public FanControl::Delegate, public DeviceManager::De AirPurifierManager & operator=(const AirPurifierManager &) = delete; static void InitInstance(EndpointId aEndpointId = 1, EndpointId aAirQualitySensorEndpointId = 2, - EndpointId aTemperatureSensorEndpointId = 3, EndpointId aHumiditySensorEndpointId = 4) + EndpointId aTemperatureSensorEndpointId = 3, EndpointId aHumiditySensorEndpointId = 4, + EndpointId aThermostatEndpointId = 5) { if (mInstance == nullptr) { mInstance = new AirPurifierManager(aEndpointId, aAirQualitySensorEndpointId, aTemperatureSensorEndpointId, - aHumiditySensorEndpointId); + aHumiditySensorEndpointId, aThermostatEndpointId); mInstance->Init(); } }; @@ -89,6 +91,11 @@ class AirPurifierManager : public FanControl::Delegate, public DeviceManager::De */ Protocols::InteractionModel::Status HandleStep(FanControl::StepDirectionEnum aDirection, bool aWrap, bool aLowestOff) override; + /** + * @brief Callback that thermostat manager calls when the heating state changes + */ + void HeatingCallback(); + private: inline static AirPurifierManager * mInstance; @@ -96,10 +103,13 @@ class AirPurifierManager : public FanControl::Delegate, public DeviceManager::De EndpointId mAirQualitySensorEndpointId; EndpointId mTemperatureSensorEndpointId; EndpointId mHumiditySensorEndpointId; + EndpointId mThermostatEndpointId; uint8_t percentCurrent; uint8_t speedCurrent; + bool fanWasStartedByUser = false; + // Set up for Activated Carbon Filter Monitoring ActivatedCarbonFilterMonitoringDelegate activatedCarbonFilterDelegate; ResourceMonitoring::Instance activatedCarbonFilterInstance; @@ -112,6 +122,7 @@ class AirPurifierManager : public FanControl::Delegate, public DeviceManager::De AirQualitySensorManager mAirQualitySensorManager; TemperatureSensorManager mTemperatureSensorManager; RelativeHumiditySensorManager mHumiditySensorManager; + ThermostatManager mThermostatManager; // Fan Mode Limits static constexpr int FAN_MODE_LOW_LOWER_BOUND = 1; @@ -129,7 +140,7 @@ class AirPurifierManager : public FanControl::Delegate, public DeviceManager::De * @param[in] aHumiditySensorEndpointId Endpoint that the humidity sensor is on */ AirPurifierManager(EndpointId aEndpointId, EndpointId aAirQualitySensorEndpointId, EndpointId aTemperatureSensorEndpointId, - EndpointId aHumiditySensorEndpointId) : + EndpointId aHumiditySensorEndpointId, EndpointId aThermostatEndpointId) : FanControl::Delegate(aEndpointId), mEndpointId(aEndpointId), activatedCarbonFilterInstance(&activatedCarbonFilterDelegate, mEndpointId, ActivatedCarbonFilterMonitoring::Id, @@ -139,7 +150,9 @@ class AirPurifierManager : public FanControl::Delegate, public DeviceManager::De static_cast(gHepaFilterFeatureMap.to_ulong()), ResourceMonitoring::DegradationDirectionEnum::kDown, true), mAirQualitySensorManager(aAirQualitySensorEndpointId), mTemperatureSensorManager(aTemperatureSensorEndpointId), - mHumiditySensorManager(aHumiditySensorEndpointId){}; + mHumiditySensorManager(aHumiditySensorEndpointId), + mThermostatManager(aThermostatEndpointId, [this]() { HeatingCallback(); }) + {} /** * @brief Handle attribute changes for the Fan Control Cluster @@ -155,6 +168,12 @@ class AirPurifierManager : public FanControl::Delegate, public DeviceManager::De void FanModeWriteCallback(FanControl::FanModeEnum aNewFanMode); void SetSpeedSetting(DataModel::Nullable aNewSpeedSetting); + DataModel::Nullable GetSpeedSetting(); + DataModel::Nullable GetPercentSetting(); + + void HandleThermostatAttributeChange(AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value); + void ThermostatHeatingSetpointWriteCallback(int16_t aNewHeatingSetpoint); + void ThermostatSystemModeWriteCallback(uint8_t aNewSystemMode); }; } // namespace Clusters diff --git a/examples/air-purifier-app/air-purifier-common/include/thermostat-manager.h b/examples/air-purifier-app/air-purifier-common/include/thermostat-manager.h new file mode 100644 index 00000000000000..f19e596f66991b --- /dev/null +++ b/examples/air-purifier-app/air-purifier-common/include/thermostat-manager.h @@ -0,0 +1,53 @@ +/* + * + * 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 + +namespace chip { +namespace app { +namespace Clusters { + +class ThermostatManager +{ +public: + using HeatingCallbackType = std::function; + + ThermostatManager(EndpointId aEndpointId, const HeatingCallbackType & callback) : + mEndpointId(aEndpointId), heatingCallback(callback) + {} + + void Init(); + + void HeatingSetpointWriteCallback(int16_t newValue); + void SystemModeWriteCallback(uint8_t newValue); + void OnLocalTemperatureChangeCallback(int16_t temperature); + void SetHeatMode(bool heat); + +private: + EndpointId mEndpointId; + + void SetHeating(bool isHeating); + HeatingCallbackType heatingCallback; +}; + +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/air-purifier-app/air-purifier-common/src/air-purifier-manager.cpp b/examples/air-purifier-app/air-purifier-common/src/air-purifier-manager.cpp index 93e1e37a73dd77..7ce8ac5f338fcc 100644 --- a/examples/air-purifier-app/air-purifier-common/src/air-purifier-manager.cpp +++ b/examples/air-purifier-app/air-purifier-common/src/air-purifier-manager.cpp @@ -33,37 +33,31 @@ void AirPurifierManager::Init() mAirQualitySensorManager.Init(); mTemperatureSensorManager.Init(); mHumiditySensorManager.Init(); + mThermostatManager.Init(); - DataModel::Nullable percentSetting; - EmberAfStatus status = FanControl::Attributes::PercentSetting::Get(mEndpointId, percentSetting); - if (EMBER_ZCL_STATUS_SUCCESS == status) + DataModel::Nullable percentSetting = GetPercentSetting(); + if (percentSetting.IsNull()) { - if (percentSetting.IsNull()) - { - PercentSettingWriteCallback(0); - } - else - { - PercentSettingWriteCallback(percentSetting.Value()); - } + PercentSettingWriteCallback(0); + } + else + { + PercentSettingWriteCallback(percentSetting.Value()); } - DataModel::Nullable speedSetting; - status = FanControl::Attributes::SpeedSetting::Get(mEndpointId, speedSetting); - if (EMBER_ZCL_STATUS_SUCCESS == status) + DataModel::Nullable speedSetting = GetSpeedSetting(); + if (speedSetting.IsNull()) { - if (speedSetting.IsNull()) - { - SpeedSettingWriteCallback(0); - } - else - { - SpeedSettingWriteCallback(speedSetting.Value()); - } + SpeedSettingWriteCallback(0); + } + else + { + SpeedSettingWriteCallback(speedSetting.Value()); } // Set up some sane initial values for temperature and humidity - note these are fixed values for testing purposes only mTemperatureSensorManager.OnTemperatureChangeHandler(2000); + mThermostatManager.OnLocalTemperatureChangeCallback(2000); mHumiditySensorManager.OnHumidityChangeHandler(5000); } @@ -77,6 +71,11 @@ void AirPurifierManager::PostAttributeChangeCallback(EndpointId endpoint, Cluste break; } + case Thermostat::Id: { + HandleThermostatAttributeChange(attributeId, type, size, value); + break; + } + default: break; } @@ -92,8 +91,7 @@ Status AirPurifierManager::HandleStep(FanControl::StepDirectionEnum aDirection, uint8_t speedMax; FanControl::Attributes::SpeedMax::Get(mEndpointId, &speedMax); - DataModel::Nullable speedSetting; - FanControl::Attributes::SpeedSetting::Get(mEndpointId, speedSetting); + DataModel::Nullable speedSetting = GetSpeedSetting(); uint8_t newSpeedSetting = speedSetting.IsNull() ? 0 : speedSetting.Value(); @@ -309,3 +307,68 @@ void AirPurifierManager::SetSpeedSetting(DataModel::Nullable aNewSpeedS } } } + +DataModel::Nullable AirPurifierManager::GetSpeedSetting() +{ + DataModel::Nullable speedSetting; + EmberAfStatus status = FanControl::Attributes::SpeedSetting::Get(mEndpointId, speedSetting); + + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + ChipLogError(NotSpecified, "AirPurifierManager::GetSpeedSetting: failed to get SpeedSetting attribute: %d", status); + } + + return speedSetting; +} + +DataModel::Nullable AirPurifierManager::GetPercentSetting() +{ + DataModel::Nullable percentSetting; + EmberAfStatus status = FanControl::Attributes::PercentSetting::Get(mEndpointId, percentSetting); + + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + ChipLogError(NotSpecified, "AirPurifierManager::GetPercentSetting: failed to get PercentSetting attribute: %d", status); + } + + return percentSetting; +} + +void AirPurifierManager::HandleThermostatAttributeChange(AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value) +{ + switch (attributeId) + { + case Thermostat::Attributes::OccupiedHeatingSetpoint::Id: { + int16_t heatingSetpoint = static_cast(chip::Encoding::LittleEndian::Get16(value)); + ThermostatHeatingSetpointWriteCallback(heatingSetpoint); + break; + } + case Thermostat::Attributes::SystemMode::Id: { + uint8_t systemMode = static_cast(*value); + ThermostatSystemModeWriteCallback(systemMode); + break; + } + } +} + +void AirPurifierManager::ThermostatHeatingSetpointWriteCallback(int16_t aNewHeatingSetpoint) +{ + mThermostatManager.HeatingSetpointWriteCallback(aNewHeatingSetpoint); +} + +void AirPurifierManager::ThermostatSystemModeWriteCallback(uint8_t aNewSystemMode) +{ + mThermostatManager.SystemModeWriteCallback(aNewSystemMode); +} + +void AirPurifierManager::HeatingCallback() +{ + // Check if the Fan is off and if it is, turn it on to 50% speed + DataModel::Nullable speedSetting = GetSpeedSetting(); + + if (speedSetting.IsNull() || speedSetting.Value() == 0) + { + DataModel::Nullable newSpeedSetting(5); + SetSpeedSetting(newSpeedSetting); + } +} diff --git a/examples/air-purifier-app/air-purifier-common/src/thermostat-manager.cpp b/examples/air-purifier-app/air-purifier-common/src/thermostat-manager.cpp new file mode 100644 index 00000000000000..c2536e4d99e38b --- /dev/null +++ b/examples/air-purifier-app/air-purifier-common/src/thermostat-manager.cpp @@ -0,0 +1,135 @@ +/* + * + * 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 "thermostat-manager.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; + +void ThermostatManager::Init() +{ + BitMask FeatureMap; + FeatureMap.Set(Thermostat::Feature::kHeating); + EmberAfStatus status = Thermostat::Attributes::FeatureMap::Set(mEndpointId, FeatureMap.Raw()); + + status = Thermostat::Attributes::ControlSequenceOfOperation::Set(mEndpointId, + Thermostat::ControlSequenceOfOperationEnum::kHeatingOnly); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(NotSpecified, "Failed to set Thermostat ControlSequenceOfOperation attribute")); + + status = Thermostat::Attributes::AbsMinHeatSetpointLimit::Set(mEndpointId, 500); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(NotSpecified, "Failed to set Thermostat MinHeatSetpointLimit attribute")); + + status = Thermostat::Attributes::AbsMaxHeatSetpointLimit::Set(mEndpointId, 3000); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(NotSpecified, "Failed to set Thermostat MaxHeatSetpointLimit attribute")); +} + +void ThermostatManager::HeatingSetpointWriteCallback(int16_t newValue) +{ + ChipLogDetail(NotSpecified, "ThermostatManager::HeatingSetpointWriteCallback: %d", newValue); + Thermostat::SystemModeEnum systemMode; + EmberAfStatus status = Thermostat::Attributes::SystemMode::Get(mEndpointId, &systemMode); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to get Thermostat SystemMode attribute")); + + // A new setpoint has been set, so we shall infer that the we want to be in Heating mode + if (systemMode == Thermostat::SystemModeEnum::kOff) + { + SetHeatMode(true); + } + + // Check the current temperature and turn on the heater if needed + DataModel::Nullable localTemperature; + status = Thermostat::Attributes::LocalTemperature::Get(mEndpointId, localTemperature); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(NotSpecified, "Failed to get TemperatureMeasurement MeasuredValue attribute")); + + if (localTemperature.Value() < newValue) + { + SetHeating(true); + } + else + { + SetHeating(false); + } +} + +void ThermostatManager::SystemModeWriteCallback(uint8_t newValue) +{ + ChipLogDetail(NotSpecified, "ThermostatManager::SystemModeWriteCallback: %d", newValue); + if ((Thermostat::SystemModeEnum) newValue == Thermostat::SystemModeEnum::kOff) + { + SetHeating(false); + } + else if ((Thermostat::SystemModeEnum) newValue == Thermostat::SystemModeEnum::kHeat) + { + DataModel::Nullable localTemperature; + EmberAfStatus status = Thermostat::Attributes::LocalTemperature::Get(mEndpointId, localTemperature); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(NotSpecified, "Failed to get TemperatureMeasurement MeasuredValue attribute")); + + int16_t heatingSetpoint; + status = Thermostat::Attributes::OccupiedHeatingSetpoint::Get(mEndpointId, &heatingSetpoint); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(NotSpecified, "Failed to get Thermostat HeatingSetpoint attribute")); + + if (localTemperature.Value() < heatingSetpoint) + { + SetHeating(true); + } + } +} + +void ThermostatManager::OnLocalTemperatureChangeCallback(int16_t temperature) +{ + EmberAfStatus status = Thermostat::Attributes::LocalTemperature::Set(mEndpointId, temperature); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(NotSpecified, "Failed to set TemperatureMeasurement MeasuredValue attribute")); +} + +void ThermostatManager::SetHeating(bool isHeating) +{ + BitMask runningState; + + if (isHeating) + { + runningState.Set(Thermostat::RelayStateBitmap::kHeat); + + if (heatingCallback) + { + heatingCallback(); + } + } + else + { + runningState.Clear(Thermostat::RelayStateBitmap::kHeat); + } + + EmberAfStatus status = Thermostat::Attributes::ThermostatRunningState::Set(mEndpointId, runningState); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, + ChipLogError(NotSpecified, "Failed to set Thermostat RunningState attribute")); +} + +void ThermostatManager::SetHeatMode(bool heat) +{ + EmberAfStatus status = Thermostat::Attributes::SystemMode::Set( + mEndpointId, heat ? Thermostat::SystemModeEnum::kHeat : Thermostat::SystemModeEnum::kOff); + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to set Thermostat SystemMode attribute")); +} diff --git a/examples/air-purifier-app/ameba/README.md b/examples/air-purifier-app/ameba/README.md index be11ad8d063210..ee2a451203cb7f 100644 --- a/examples/air-purifier-app/ameba/README.md +++ b/examples/air-purifier-app/ameba/README.md @@ -10,7 +10,7 @@ This example demonstrates the Matter air purifier application on Ameba platform. - [Commissioning](#commissioning) - [BLE mode](#ble-mode) - [IP mode](#ip-mode) - - [Cluster control](#cluster-control) + - [Cluster Control](#cluster-control) --- @@ -106,6 +106,7 @@ follows: - Air quality sensor on endpoint 2 - Temperature sensor on endpoint 3 - Relative humidity sensor on endpoint 4 +- Thermostat on endpoint 5 Example commands using the chip tool: diff --git a/examples/air-purifier-app/ameba/main/chipinterface.cpp b/examples/air-purifier-app/ameba/main/chipinterface.cpp index bcda329cdb8536..264ad532b513aa 100644 --- a/examples/air-purifier-app/ameba/main/chipinterface.cpp +++ b/examples/air-purifier-app/ameba/main/chipinterface.cpp @@ -48,6 +48,7 @@ #define AIR_QUALITY_SENSOR_ENDPOINT 2 #define TEMPERATURE_SENSOR_ENDPOINT 3 #define RELATIVE_HUMIDITY_SENSOR_ENDPOINT 4 +#define THERMOSTAT_ENDPOINT 5 using namespace ::chip; using namespace ::chip::app; @@ -116,11 +117,12 @@ static void InitAirPurifierManager(void) { Clusters::AirPurifierManager::InitInstance(EndpointId(AIR_PURIFIER_ENDPOINT), EndpointId(AIR_QUALITY_SENSOR_ENDPOINT), EndpointId(TEMPERATURE_SENSOR_ENDPOINT), - EndpointId(RELATIVE_HUMIDITY_SENSOR_ENDPOINT)); + EndpointId(RELATIVE_HUMIDITY_SENSOR_ENDPOINT), EndpointId(THERMOSTAT_ENDPOINT)); SetParentEndpointForEndpoint(AIR_QUALITY_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); SetParentEndpointForEndpoint(TEMPERATURE_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); SetParentEndpointForEndpoint(RELATIVE_HUMIDITY_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); + SetParentEndpointForEndpoint(THERMOSTAT_ENDPOINT, AIR_PURIFIER_ENDPOINT); } static void InitServer(intptr_t context) diff --git a/examples/air-purifier-app/cc32xx/BUILD.gn b/examples/air-purifier-app/cc32xx/BUILD.gn index 51916cc32d646f..f79e76c82ce0d3 100755 --- a/examples/air-purifier-app/cc32xx/BUILD.gn +++ b/examples/air-purifier-app/cc32xx/BUILD.gn @@ -85,6 +85,7 @@ ti_simplelink_executable("air-purifier_app") { "${chip_root}/examples/air-purifier-app/air-purifier-common/src/air-purifier-manager.cpp", "${chip_root}/examples/air-purifier-app/air-purifier-common/src/air-quality-sensor-manager.cpp", "${chip_root}/examples/air-purifier-app/air-purifier-common/src/filter-delegates.cpp", + "${chip_root}/examples/air-purifier-app/air-purifier-common/src/thermostat-manager.cpp", "${project_dir}/main/AppTask.cpp", "${project_dir}/main/CHIPDeviceManager.cpp", "${project_dir}/main/CXXExceptionStubs.cpp", diff --git a/examples/air-purifier-app/cc32xx/main/AppTask.cpp b/examples/air-purifier-app/cc32xx/main/AppTask.cpp index e753e5ac4aed8f..0b284254b0c80f 100644 --- a/examples/air-purifier-app/cc32xx/main/AppTask.cpp +++ b/examples/air-purifier-app/cc32xx/main/AppTask.cpp @@ -64,6 +64,7 @@ extern void DisplayBanner(); #define AIR_QUALITY_SENSOR_ENDPOINT 2 #define TEMPERATURE_SENSOR_ENDPOINT 3 #define RELATIVE_HUMIDITY_SENSOR_ENDPOINT 4 +#define THERMOSTAT_ENDPOINT 5 // Added the below three for DNS Server Initialization using namespace ::chip; @@ -178,9 +179,11 @@ int AppTask::Init() SetParentEndpointForEndpoint(AIR_QUALITY_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); SetParentEndpointForEndpoint(TEMPERATURE_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); SetParentEndpointForEndpoint(RELATIVE_HUMIDITY_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); + SetParentEndpointForEndpoint(THERMOSTAT_ENDPOINT, AIR_PURIFIER_ENDPOINT); AirPurifierManager::InitInstance(EndpointId(AIR_PURIFIER_ENDPOINT), EndpointId(AIR_QUALITY_SENSOR_ENDPOINT), - EndpointId(TEMPERATURE_SENSOR_ENDPOINT), EndpointId(RELATIVE_HUMIDITY_SENSOR_ENDPOINT)); + EndpointId(TEMPERATURE_SENSOR_ENDPOINT), EndpointId(RELATIVE_HUMIDITY_SENSOR_ENDPOINT), + EndpointId(THERMOSTAT_ENDPOINT)); ConfigurationMgr().LogDeviceConfig(); diff --git a/examples/air-purifier-app/linux/BUILD.gn b/examples/air-purifier-app/linux/BUILD.gn index 0a0f86cac16ec4..0f4d2a489dc18a 100644 --- a/examples/air-purifier-app/linux/BUILD.gn +++ b/examples/air-purifier-app/linux/BUILD.gn @@ -33,6 +33,7 @@ executable("chip-air-purifier-app") { "${chip_root}/examples/air-purifier-app/air-purifier-common/src/air-purifier-manager.cpp", "${chip_root}/examples/air-purifier-app/air-purifier-common/src/air-quality-sensor-manager.cpp", "${chip_root}/examples/air-purifier-app/air-purifier-common/src/filter-delegates.cpp", + "${chip_root}/examples/air-purifier-app/air-purifier-common/src/thermostat-manager.cpp", "include/CHIPProjectAppConfig.h", "main.cpp", ] diff --git a/examples/air-purifier-app/linux/README.md b/examples/air-purifier-app/linux/README.md index 88f5be738c926d..d2352bca201e1a 100644 --- a/examples/air-purifier-app/linux/README.md +++ b/examples/air-purifier-app/linux/README.md @@ -4,9 +4,10 @@ An example showing the use of CHIP on the Linux. The document will describe how to build and run CHIP Air Purifier Example on A Linux System. This doc is tested on **Ubuntu 20.04 LTS**. -The Air Purifier is a composed device with Endpoint 1 being the Air Purifier. -Endpoint 2 is an Air Quality Sensor, Endpoint 3 is a Relative Humidity Sensor -and endpoint 4 is a Temperature Sensor. +The Air Purifier example demonstrates a fully functional Matter Air Purifier +which is a composed device with Endpoint 1 being the Air Purifier. Endpoint 2 is +an Air Quality Sensor, Endpoint 3 is a Relative Humidity Sensor, Endpoint 4 is a +Temperature Sensor and Endpoint 5 is a Thermostat. To cross-compile this example on x64 host and run on **NXP i.MX 8M Mini** **EVK**, see the associated @@ -99,11 +100,11 @@ To cross-compile this example on x64 host and run on **NXP i.MX 8M Mini** RX bytes:8609495 acl:14 sco:0 events:217484 errors:0 TX bytes:92185 acl:20 sco:0 commands:5259 errors:0 - - Run Linux Lighting Example App + - Run Linux Air Purifier Example App - $ cd ~/connectedhomeip/examples/lighting-app/linux - $ sudo out/debug/chip-lighting-app --ble-device [bluetooth device number] + $ cd ~/connectedhomeip/examples/air-purifier-app/linux + $ sudo out/debug/chip-air-purifier-app --ble-device [bluetooth device number] # In this example, the device we want to use is hci1 - $ sudo out/debug/chip-lighting-app --ble-device 1 + $ sudo out/debug/chip-air-purifier-app --ble-device 1 - Test the device using ChipTool on your laptop / workstation etc. diff --git a/examples/air-purifier-app/linux/main.cpp b/examples/air-purifier-app/linux/main.cpp index 59ef9cf9595755..98d8d405cfadfb 100644 --- a/examples/air-purifier-app/linux/main.cpp +++ b/examples/air-purifier-app/linux/main.cpp @@ -27,7 +27,7 @@ #define AIR_QUALITY_SENSOR_ENDPOINT 2 #define TEMPERATURE_SENSOR_ENDPOINT 3 #define RELATIVE_HUMIDITY_SENSOR_ENDPOINT 4 -// TODO: Add support for the thermostat endpoint in future PR. +#define THERMOSTAT_ENDPOINT 5 using namespace chip; using namespace chip::app; @@ -48,11 +48,13 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & void ApplicationInit() { AirPurifierManager::InitInstance(EndpointId(AIR_PURIFIER_ENDPOINT), EndpointId(AIR_QUALITY_SENSOR_ENDPOINT), - EndpointId(TEMPERATURE_SENSOR_ENDPOINT), EndpointId(RELATIVE_HUMIDITY_SENSOR_ENDPOINT)); + EndpointId(TEMPERATURE_SENSOR_ENDPOINT), EndpointId(RELATIVE_HUMIDITY_SENSOR_ENDPOINT), + EndpointId(THERMOSTAT_ENDPOINT)); SetParentEndpointForEndpoint(AIR_QUALITY_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); SetParentEndpointForEndpoint(TEMPERATURE_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); SetParentEndpointForEndpoint(RELATIVE_HUMIDITY_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT); + SetParentEndpointForEndpoint(THERMOSTAT_ENDPOINT, AIR_PURIFIER_ENDPOINT); } void ApplicationShutdown()