feat(matter): new Matter Endpoint for Thermostat (#10755)
* feat(matter): add new matter endpoint for thermostat * fix(matter): not used variable from log_e() message * feat(matter): adds specifc type name for thermostat auto mode enabled * fix(matter): suggested changes in pr review * feat(matter): added the whole list of thermostat operational modes * fix(matter): fixed type in thermostat operational modes * ci(pre-commit): Apply automatic fixes * fix(matter): typos caught by CI codespell --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
parent
b07eb175d8
commit
e3cc04084d
7 changed files with 872 additions and 0 deletions
|
|
@ -181,6 +181,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS
|
|||
libraries/Matter/src/MatterEndpoints/MatterPressureSensor.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterOnOffPlugin.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp
|
||||
libraries/Matter/src/Matter.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_PPP_SRCS
|
||||
|
|
|
|||
243
libraries/Matter/examples/MatterThermostat/MatterThermostat.ino
Normal file
243
libraries/Matter/examples/MatterThermostat/MatterThermostat.ino
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
/*
|
||||
This example is an example code that will create a Matter Device which can be
|
||||
commissioned and controlled from a Matter Environment APP.
|
||||
Additionally the ESP32 will send debug messages indicating the Matter activity.
|
||||
Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages.
|
||||
*/
|
||||
|
||||
// Matter Manager
|
||||
#include <Matter.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
// List of Matter Endpoints for this Node
|
||||
// Matter Thermostat Endpoint
|
||||
MatterThermostat SimulatedThermostat;
|
||||
|
||||
// WiFi is manually set and started
|
||||
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
|
||||
const char *password = "your-password"; // Change this to your WiFi password
|
||||
|
||||
// set your board USER BUTTON pin here - decommissioning button
|
||||
const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button.
|
||||
|
||||
// Button control - decommision the Matter Node
|
||||
uint32_t button_time_stamp = 0; // debouncing control
|
||||
bool button_state = false; // false = released | true = pressed
|
||||
const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission
|
||||
|
||||
// Simulate a system that will activate heating/cooling in addition to a temperature sensor - add your preferred code here
|
||||
float getSimulatedTemperature(bool isHeating, bool isCooling) {
|
||||
// read sensor temperature and apply heating/cooling
|
||||
float simulatedTempHWSensor = SimulatedThermostat.getLocalTemperature();
|
||||
|
||||
if (isHeating) {
|
||||
// it will increase to simulate a heating system
|
||||
simulatedTempHWSensor = simulatedTempHWSensor + 0.5;
|
||||
}
|
||||
if (isCooling) {
|
||||
// it will decrease to simulate a colling system
|
||||
simulatedTempHWSensor = simulatedTempHWSensor - 0.5;
|
||||
}
|
||||
// otherwise, it will keep the temperature stable
|
||||
return simulatedTempHWSensor;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node
|
||||
pinMode(buttonPin, INPUT_PULLUP);
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
// Manually connect to WiFi
|
||||
WiFi.begin(ssid, password);
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Simulated Thermostat in COOLING and HEATING mode with Auto Mode to keep the temperature between setpoints
|
||||
// Auto Mode can only be used when the control sequence of operation is Cooling & Heating
|
||||
SimulatedThermostat.begin(MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_HEATING, MatterThermostat::THERMOSTAT_AUTO_MODE_ENABLED);
|
||||
|
||||
// Matter beginning - Last step, after all EndPoints are initialized
|
||||
Matter.begin();
|
||||
|
||||
// Check Matter Accessory Commissioning state, which may change during execution of loop()
|
||||
if (!Matter.isDeviceCommissioned()) {
|
||||
Serial.println("");
|
||||
Serial.println("Matter Node is not commissioned yet.");
|
||||
Serial.println("Initiate the device discovery in your Matter environment.");
|
||||
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
|
||||
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
|
||||
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
|
||||
// waits for Matter Thermostat Commissioning.
|
||||
uint32_t timeCount = 0;
|
||||
while (!Matter.isDeviceCommissioned()) {
|
||||
delay(100);
|
||||
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
|
||||
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
|
||||
}
|
||||
}
|
||||
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
|
||||
|
||||
// after commissioning, set initial thermostat parameters
|
||||
// start the thermostat in AUTO mode
|
||||
SimulatedThermostat.setMode(MatterThermostat::THERMOSTAT_MODE_AUTO);
|
||||
// cooling setpoint must be lower than heating setpoint by at least 2.5C (deadband), in auto mode
|
||||
SimulatedThermostat.setCoolingHeatingSetpoints(20.0, 23.00); // the target cooler and heating setpoint
|
||||
// set the local temperature sensor in Celsius
|
||||
SimulatedThermostat.setLocalTemperature(12.50);
|
||||
|
||||
Serial.println();
|
||||
Serial.printf(
|
||||
"Initial Setpoints are %.01fC to %.01fC with a minimum 2.5C difference\r\n", SimulatedThermostat.getHeatingSetpoint(),
|
||||
SimulatedThermostat.getCoolingSetpoint()
|
||||
);
|
||||
Serial.printf("Auto mode is ON. Initial Temperature of %.01fC \r\n", SimulatedThermostat.getLocalTemperature());
|
||||
Serial.println("Local Temperature Sensor will be simulated every 10 seconds and changed by a simulated heater and cooler to move in between setpoints.");
|
||||
}
|
||||
}
|
||||
|
||||
// This will simulate the thermostat control system (heating and cooling)
|
||||
// User can set a local temperature using the Serial input (type a number and press Enter)
|
||||
// New temperature can be an positive or negative temperature in Celsius, between -50C and 50C
|
||||
// Initial local temperature is 10C as defined in getSimulatedTemperature() function
|
||||
void readSerialForNewTemperature() {
|
||||
static String newTemperatureStr;
|
||||
|
||||
while (Serial.available()) {
|
||||
char c = Serial.read();
|
||||
if (c == '\n' || c == '\r') {
|
||||
if (newTemperatureStr.length() > 0) {
|
||||
// convert the string to a float value
|
||||
float newTemperature = newTemperatureStr.toFloat();
|
||||
// check if the new temperature is valid
|
||||
if (newTemperature >= -50.0 && newTemperature <= 50.0) {
|
||||
// set the new temperature
|
||||
SimulatedThermostat.setLocalTemperature(newTemperature);
|
||||
Serial.printf("New Temperature is %.01fC\r\n", newTemperature);
|
||||
} else {
|
||||
Serial.println("Invalid Temperature value. Please type a number between -50 and 50");
|
||||
}
|
||||
newTemperatureStr = "";
|
||||
}
|
||||
} else {
|
||||
if (c == '+' || c == '-' || (c >= '0' && c <= '9') || c == '.') {
|
||||
newTemperatureStr += c;
|
||||
} else {
|
||||
Serial.println("Invalid character. Please type a number between -50 and 50");
|
||||
newTemperatureStr = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop will simulate the thermostat control system
|
||||
// User can set a local temperature using the Serial input (type a number and press Enter)
|
||||
// User can change the thermostat mode using the Matter APP (smartphone)
|
||||
// The loop will simulate a heating and cooling system and the associated local temperature change
|
||||
void loop() {
|
||||
static uint32_t timeCounter = 0;
|
||||
|
||||
// Simulate the heating and cooling systems
|
||||
static bool isHeating = false;
|
||||
static bool isCooling = false;
|
||||
|
||||
// check if a new temperature is typed in the Serial Monitor
|
||||
readSerialForNewTemperature();
|
||||
|
||||
// simulate thermostat with heating/cooling system and the associated local temperature change, every 10s
|
||||
if (!(timeCounter++ % 20)) { // delaying for 500ms x 20 = 10s
|
||||
float localTemperature = getSimulatedTemperature(isHeating, isCooling);
|
||||
// Print the current thermostat local temperature value
|
||||
Serial.printf("Current Local Temperature is %.01fC\r\n", localTemperature);
|
||||
SimulatedThermostat.setLocalTemperature(localTemperature); // publish the new temperature value
|
||||
|
||||
// Simulate the thermostat control system - User has 4 modes: OFF, HEAT, COOL, AUTO
|
||||
switch (SimulatedThermostat.getMode()) {
|
||||
case MatterThermostat::THERMOSTAT_MODE_OFF:
|
||||
// turn off the heating and cooling systems
|
||||
isHeating = false;
|
||||
isCooling = false;
|
||||
break;
|
||||
case MatterThermostat::THERMOSTAT_MODE_AUTO:
|
||||
// User APP has set the thermostat to AUTO mode -- keeping the tempeature between both setpoints
|
||||
// check if the heating system should be turned on or off
|
||||
if (localTemperature < SimulatedThermostat.getHeatingSetpoint() + SimulatedThermostat.getDeadBand()) {
|
||||
// turn on the heating system and turn off the cooling system
|
||||
isHeating = true;
|
||||
isCooling = false;
|
||||
}
|
||||
if (localTemperature > SimulatedThermostat.getCoolingSetpoint() - SimulatedThermostat.getDeadBand()) {
|
||||
// turn off the heating system and turn on the cooling system
|
||||
isHeating = false;
|
||||
isCooling = true;
|
||||
}
|
||||
break;
|
||||
case MatterThermostat::THERMOSTAT_MODE_HEAT:
|
||||
// Simulate the heating system - User has turned the heating system ON
|
||||
isHeating = true;
|
||||
isCooling = false; // keep the cooling system off as it is in heating mode
|
||||
// when the heating system is in HEATING mode, it will be turned off as soon as the local temperature is above the setpoint
|
||||
if (localTemperature > SimulatedThermostat.getHeatingSetpoint()) {
|
||||
// turn off the heating system
|
||||
isHeating = false;
|
||||
}
|
||||
break;
|
||||
case MatterThermostat::THERMOSTAT_MODE_COOL:
|
||||
// Simulate the cooling system - User has turned the cooling system ON
|
||||
if (SimulatedThermostat.getMode() == MatterThermostat::THERMOSTAT_MODE_COOL) {
|
||||
isCooling = true;
|
||||
isHeating = false; // keep the heating system off as it is in cooling mode
|
||||
// when the cooling system is in COOLING mode, it will be turned off as soon as the local temperature is bellow the setpoint
|
||||
if (localTemperature < SimulatedThermostat.getCoolingSetpoint()) {
|
||||
// turn off the cooling system
|
||||
isCooling = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: log_e("Invalid Thermostat Mode %d", SimulatedThermostat.getMode());
|
||||
}
|
||||
// Reporting Heating and Cooling status
|
||||
Serial.printf(
|
||||
"\tThermostat Mode: %s >>> Heater is %s -- Cooler is %s\r\n", MatterThermostat::getThermostatModeString(SimulatedThermostat.getMode()),
|
||||
isHeating ? "ON" : "OFF", isCooling ? "ON" : "OFF"
|
||||
);
|
||||
}
|
||||
// Check if the button has been pressed
|
||||
if (digitalRead(buttonPin) == LOW && !button_state) {
|
||||
// deals with button debouncing
|
||||
button_time_stamp = millis(); // record the time while the button is pressed.
|
||||
button_state = true; // pressed.
|
||||
}
|
||||
|
||||
if (digitalRead(buttonPin) == HIGH && button_state) {
|
||||
button_state = false; // released
|
||||
}
|
||||
|
||||
// Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node
|
||||
uint32_t time_diff = millis() - button_time_stamp;
|
||||
if (button_state && time_diff > decommissioningTimeout) {
|
||||
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
|
||||
Matter.decommission();
|
||||
button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so
|
||||
}
|
||||
|
||||
delay(500);
|
||||
}
|
||||
7
libraries/Matter/examples/MatterThermostat/ci.json
Normal file
7
libraries/Matter/examples/MatterThermostat/ci.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"fqbn_append": "PartitionScheme=huge_app",
|
||||
"requires": [
|
||||
"CONFIG_SOC_WIFI_SUPPORTED=y",
|
||||
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
|
||||
]
|
||||
}
|
||||
|
|
@ -24,6 +24,18 @@ MatterContactSensor KEYWORD1
|
|||
MatterPressureSensor KEYWORD1
|
||||
MatterOccupancySensor KEYWORD1
|
||||
MatterOnOffPlugin KEYWORD1
|
||||
MatterThermostat KEYWORD1
|
||||
ControlSequenceOfOperation_t KEYWORD1
|
||||
ThermostatMode_t KEYWORD1
|
||||
EndPointCB KEYWORD1
|
||||
EndPointHeatingSetpointCB KEYWORD1
|
||||
EndPointCoolingSetpointCB KEYWORD1
|
||||
EndPointTemperatureCB KEYWORD1
|
||||
EndPointModeCB KEYWORD1
|
||||
EndPointSpeedCB KEYWORD1
|
||||
EndPointOnOffCB KEYWORD1
|
||||
EndPointBrightnessCB KEYWORD1
|
||||
EndPointRGBColorCB KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
|
|
@ -78,6 +90,24 @@ setPressure KEYWORD2
|
|||
getPressure KEYWORD2
|
||||
setOccupancy KEYWORD2
|
||||
getOccupancy KEYWORD2
|
||||
getControlSequence KEYWORD2
|
||||
getMinHeatSetpoint KEYWORD2
|
||||
getMaxHeatSetpoint KEYWORD2
|
||||
getMinCoolSetpoint KEYWORD2
|
||||
getMaxCoolSetpoint KEYWORD2
|
||||
getDeadBand KEYWORD2
|
||||
setCoolingSetpoint KEYWORD2
|
||||
getCoolingSetpoint KEYWORD2
|
||||
setHeatingSetpoint KEYWORD2
|
||||
getHeatingSetpoint KEYWORD2
|
||||
setCoolingHeatingSetpoints KEYWORD2
|
||||
setLocalTemperature KEYWORD2
|
||||
getLocalTemperature KEYWORD2
|
||||
getThermostatModeString KEYWORD2
|
||||
onChangeMode KEYWORD2
|
||||
onChangeLocalTemperature KEYWORD2
|
||||
onChangeCoolingSetpoint KEYWORD2
|
||||
onChangeHeatingSetpoint KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
|
|
@ -104,3 +134,15 @@ FAN_MODE_SEQ_OFF_LOW_MED_HIGH_AUTO LITERAL1
|
|||
FAN_MODE_SEQ_OFF_LOW_HIGH_AUTO LITERAL1
|
||||
FAN_MODE_SEQ_OFF_HIGH_AUTO LITERAL1
|
||||
FAN_MODE_SEQ_OFF_HIGH LITERAL1
|
||||
THERMOSTAT_SEQ_OP_COOLING LITERAL1
|
||||
THERMOSTAT_SEQ_OP_COOLING_REHEAT LITERAL1
|
||||
THERMOSTAT_SEQ_OP_HEATING LITERAL1
|
||||
THERMOSTAT_SEQ_OP_HEATING_REHEAT LITERAL1
|
||||
THERMOSTAT_SEQ_OP_COOLING_HEATING LITERAL1
|
||||
THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT LITERAL1
|
||||
THERMOSTAT_MODE_OFF LITERAL1
|
||||
THERMOSTAT_MODE_AUTO LITERAL1
|
||||
THERMOSTAT_MODE_COOL LITERAL1
|
||||
THERMOSTAT_MODE_HEAT LITERAL1
|
||||
THERMOSTAT_AUTO_MODE_DISABLED LITERAL1
|
||||
THERMOSTAT_AUTO_MODE_ENABLED LITERAL1
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <MatterEndpoints/MatterPressureSensor.h>
|
||||
#include <MatterEndpoints/MatterOccupancySensor.h>
|
||||
#include <MatterEndpoints/MatterOnOffPlugin.h>
|
||||
#include <MatterEndpoints/MatterThermostat.h>
|
||||
|
||||
using namespace esp_matter;
|
||||
|
||||
|
|
@ -70,6 +71,7 @@ public:
|
|||
friend class MatterPressureSensor;
|
||||
friend class MatterOccupancySensor;
|
||||
friend class MatterOnOffPlugin;
|
||||
friend class MatterThermostat;
|
||||
|
||||
protected:
|
||||
static void _init();
|
||||
|
|
|
|||
370
libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp
Normal file
370
libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <sdkconfig.h>
|
||||
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
|
||||
|
||||
#include <Matter.h>
|
||||
#include <MatterEndpoints/MatterThermostat.h>
|
||||
|
||||
using namespace esp_matter;
|
||||
using namespace esp_matter::endpoint;
|
||||
using namespace chip::app::Clusters;
|
||||
|
||||
// string helper for the THERMOSTAT MODE
|
||||
const char *MatterThermostat::thermostatModeString[5] = {"OFF", "AUTO", "UNKNOWN", "COOL", "HEAT"};
|
||||
|
||||
// endpoint for color light device
|
||||
namespace esp_matter {
|
||||
using namespace cluster;
|
||||
namespace endpoint {
|
||||
namespace multi_mode_thermostat {
|
||||
typedef struct config {
|
||||
cluster::descriptor::config_t descriptor;
|
||||
cluster::identify::config_t identify;
|
||||
cluster::scenes_management::config_t scenes_management;
|
||||
cluster::groups::config_t groups;
|
||||
cluster::thermostat::config_t thermostat;
|
||||
} config_t;
|
||||
|
||||
uint32_t get_device_type_id() {
|
||||
return ESP_MATTER_THERMOSTAT_DEVICE_TYPE_ID;
|
||||
}
|
||||
|
||||
uint8_t get_device_type_version() {
|
||||
return ESP_MATTER_THERMOSTAT_DEVICE_TYPE_VERSION;
|
||||
}
|
||||
|
||||
esp_err_t add(endpoint_t *endpoint, config_t *config) {
|
||||
if (!endpoint) {
|
||||
log_e("Endpoint cannot be NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_err_t err = add_device_type(endpoint, get_device_type_id(), get_device_type_version());
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to add device type id:%" PRIu32 ",err: %d", get_device_type_id(), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
descriptor::create(endpoint, &(config->descriptor), CLUSTER_FLAG_SERVER);
|
||||
identify::create(endpoint, &(config->identify), CLUSTER_FLAG_SERVER);
|
||||
groups::create(endpoint, &(config->groups), CLUSTER_FLAG_SERVER);
|
||||
uint32_t thermostatFeatures = 0;
|
||||
switch (config->thermostat.control_sequence_of_operation) {
|
||||
case MatterThermostat::THERMOSTAT_SEQ_OP_COOLING:
|
||||
case MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_REHEAT: thermostatFeatures = cluster::thermostat::feature::cooling::get_id(); break;
|
||||
case MatterThermostat::THERMOSTAT_SEQ_OP_HEATING:
|
||||
case MatterThermostat::THERMOSTAT_SEQ_OP_HEATING_REHEAT: thermostatFeatures = cluster::thermostat::feature::heating::get_id(); break;
|
||||
case MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_HEATING:
|
||||
case MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT:
|
||||
thermostatFeatures = cluster::thermostat::feature::cooling::get_id() | cluster::thermostat::feature::heating::get_id();
|
||||
break;
|
||||
}
|
||||
cluster::thermostat::create(endpoint, &(config->thermostat), CLUSTER_FLAG_SERVER, thermostatFeatures);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data) {
|
||||
endpoint_t *endpoint = endpoint::create(node, flags, priv_data);
|
||||
add(endpoint, config);
|
||||
return endpoint;
|
||||
}
|
||||
} // namespace multi_mode_thermostat
|
||||
} // namespace endpoint
|
||||
} // namespace esp_matter
|
||||
|
||||
bool MatterThermostat::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
|
||||
bool ret = true;
|
||||
if (!started) {
|
||||
log_e("Matter Thermostat device has not begun.");
|
||||
return false;
|
||||
}
|
||||
log_d("Thermostat Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
|
||||
|
||||
if (cluster_id == Thermostat::Id) {
|
||||
switch (attribute_id) {
|
||||
case Thermostat::Attributes::SystemMode::Id:
|
||||
if (_onChangeModeCB != NULL) {
|
||||
ret &= _onChangeModeCB((ThermostatMode_t)val->val.u8);
|
||||
}
|
||||
if (_onChangeCB != NULL) {
|
||||
ret &= _onChangeCB();
|
||||
}
|
||||
if (ret == true) {
|
||||
currentMode = (ThermostatMode_t)val->val.u8;
|
||||
log_v("Thermostat Mode updated to %d", val->val.u8);
|
||||
}
|
||||
break;
|
||||
case Thermostat::Attributes::LocalTemperature::Id:
|
||||
if (_onChangeTemperatureCB != NULL) {
|
||||
ret &= _onChangeTemperatureCB((float)val->val.i16 / 100.00);
|
||||
}
|
||||
if (_onChangeCB != NULL) {
|
||||
ret &= _onChangeCB();
|
||||
}
|
||||
if (ret == true) {
|
||||
localTemperature = val->val.i16;
|
||||
log_v("Local Temperature updated to %.01fC", (float)val->val.i16 / 100.00);
|
||||
}
|
||||
break;
|
||||
case Thermostat::Attributes::OccupiedCoolingSetpoint::Id:
|
||||
if (_onChangeCoolingSetpointCB != NULL) {
|
||||
ret &= _onChangeCoolingSetpointCB((float)val->val.i16 / 100.00);
|
||||
}
|
||||
if (_onChangeCB != NULL) {
|
||||
ret &= _onChangeCB();
|
||||
}
|
||||
if (ret == true) {
|
||||
coolingSetpointTemperature = val->val.i16;
|
||||
log_v("Cooling Setpoint updated to %.01fC", (float)val->val.i16 / 100.00);
|
||||
}
|
||||
break;
|
||||
case Thermostat::Attributes::OccupiedHeatingSetpoint::Id:
|
||||
if (_onChangeHeatingSetpointCB != NULL) {
|
||||
ret &= _onChangeHeatingSetpointCB((float)val->val.i16 / 100.00);
|
||||
}
|
||||
if (_onChangeCB != NULL) {
|
||||
ret &= _onChangeCB();
|
||||
}
|
||||
if (ret == true) {
|
||||
heatingSetpointTemperature = val->val.i16;
|
||||
log_v("Heating Setpoint updated to %.01fC", (float)val->val.i16 / 100.00);
|
||||
}
|
||||
break;
|
||||
default: log_w("Unhandled Thermostat Attribute ID: %u", attribute_id); break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
MatterThermostat::MatterThermostat() {}
|
||||
|
||||
MatterThermostat::~MatterThermostat() {
|
||||
end();
|
||||
}
|
||||
|
||||
bool MatterThermostat::begin(ControlSequenceOfOperation_t _controlSequence, ThermostatAutoMode_t _autoMode) {
|
||||
ArduinoMatter::_init();
|
||||
|
||||
if (getEndPointId() != 0) {
|
||||
log_e("Matter Thermostat with Endpoint Id %d device has already been created.", getEndPointId());
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if auto mode is allowed with the control sequence of operation - only allowed for Cooling & Heating
|
||||
if (_autoMode == THERMOSTAT_AUTO_MODE_ENABLED && _controlSequence != THERMOSTAT_SEQ_OP_COOLING_HEATING
|
||||
&& _controlSequence != THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT) {
|
||||
log_e("Thermostat in Auto Mode requires a Cooling & Heating Control Sequence of Operation.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int16_t _localTemperature = 2000; // initial value to be automatically changed by the Matter Thermostat
|
||||
const int16_t _coolingSetpointTemperature = 2400; // 24C cooling setpoint
|
||||
const int16_t _heatingSetpointTemperature = 1600; // 16C heating setpoint
|
||||
const ThermostatMode_t _currentMode = THERMOSTAT_MODE_OFF;
|
||||
|
||||
multi_mode_thermostat::config_t thermostat_config;
|
||||
thermostat_config.thermostat.control_sequence_of_operation = (uint8_t)_controlSequence;
|
||||
thermostat_config.thermostat.cooling.occupied_cooling_setpoint = _coolingSetpointTemperature;
|
||||
thermostat_config.thermostat.heating.occupied_heating_setpoint = _heatingSetpointTemperature;
|
||||
thermostat_config.thermostat.system_mode = (uint8_t)_currentMode;
|
||||
thermostat_config.thermostat.local_temperature = _localTemperature;
|
||||
|
||||
// endpoint handles can be used to add/modify clusters
|
||||
endpoint_t *endpoint = multi_mode_thermostat::create(node::get(), &thermostat_config, ENDPOINT_FLAG_NONE, (void *)this);
|
||||
if (endpoint == nullptr) {
|
||||
log_e("Failed to create Thermostat endpoint");
|
||||
return false;
|
||||
}
|
||||
if (_autoMode == THERMOSTAT_AUTO_MODE_ENABLED) {
|
||||
cluster_t *cluster = cluster::get(endpoint, Thermostat::Id);
|
||||
thermostat_config.thermostat.auto_mode.min_setpoint_dead_band = kDefaultDeadBand; // fixed by default to 2.5C
|
||||
cluster::thermostat::feature::auto_mode::add(cluster, &thermostat_config.thermostat.auto_mode);
|
||||
}
|
||||
|
||||
controlSequence = _controlSequence;
|
||||
autoMode = _autoMode;
|
||||
coolingSetpointTemperature = _coolingSetpointTemperature;
|
||||
heatingSetpointTemperature = _heatingSetpointTemperature;
|
||||
localTemperature = _localTemperature;
|
||||
currentMode = _currentMode;
|
||||
|
||||
setEndPointId(endpoint::get_id(endpoint));
|
||||
log_i("Thermostat created with endpoint_id %d", getEndPointId());
|
||||
started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MatterThermostat::end() {
|
||||
started = false;
|
||||
}
|
||||
|
||||
bool MatterThermostat::setMode(ThermostatMode_t _mode) {
|
||||
if (!started) {
|
||||
log_e("Matter Thermostat device has not begun.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (autoMode == THERMOSTAT_AUTO_MODE_DISABLED && _mode == THERMOSTAT_MODE_AUTO) {
|
||||
log_e("Thermostat can't set Auto Mode.");
|
||||
return false;
|
||||
}
|
||||
// check if the requested mode is valid based on the control sequence of operation
|
||||
// no restrictions for OFF mode
|
||||
if (_mode != THERMOSTAT_MODE_OFF) {
|
||||
// check HEAT, COOL and AUTO modes
|
||||
switch (controlSequence) {
|
||||
case THERMOSTAT_SEQ_OP_COOLING:
|
||||
case THERMOSTAT_SEQ_OP_COOLING_REHEAT:
|
||||
if (_mode == THERMOSTAT_MODE_HEAT || _mode == THERMOSTAT_MODE_AUTO) {
|
||||
break;
|
||||
}
|
||||
log_e("Invalid Thermostat Mode for Cooling Control Sequence of Operation.");
|
||||
return false;
|
||||
case THERMOSTAT_SEQ_OP_HEATING:
|
||||
case THERMOSTAT_SEQ_OP_HEATING_REHEAT:
|
||||
if (_mode == THERMOSTAT_MODE_COOL || _mode == THERMOSTAT_MODE_AUTO) {
|
||||
break;
|
||||
}
|
||||
log_e("Invalid Thermostat Mode for Heating Control Sequence of Operation.");
|
||||
return false;
|
||||
default:
|
||||
// compiler warning about not handling all enum values
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid processing if there was no change
|
||||
if (currentMode == _mode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_matter_attr_val_t modeVal = esp_matter_invalid(NULL);
|
||||
if (!getAttributeVal(Thermostat::Id, Thermostat::Attributes::SystemMode::Id, &modeVal)) {
|
||||
log_e("Failed to get Thermostat Mode Attribute.");
|
||||
return false;
|
||||
}
|
||||
if (modeVal.val.u8 != _mode) {
|
||||
modeVal.val.u8 = _mode;
|
||||
bool ret;
|
||||
ret = updateAttributeVal(Thermostat::Id, Thermostat::Attributes::SystemMode::Id, &modeVal);
|
||||
if (!ret) {
|
||||
log_e("Failed to update Thermostat Mode Attribute.");
|
||||
return false;
|
||||
}
|
||||
currentMode = _mode;
|
||||
}
|
||||
log_v("Thermostat Mode set to %d", _mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MatterThermostat::setRawTemperature(int16_t _rawTemperature, uint32_t attribute_id, int16_t *internalValue) {
|
||||
if (!started) {
|
||||
log_e("Matter Thermostat device has not begun.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// avoid processing if there was no change
|
||||
if (*internalValue == _rawTemperature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_matter_attr_val_t temperatureVal = esp_matter_invalid(NULL);
|
||||
if (!getAttributeVal(Thermostat::Id, attribute_id, &temperatureVal)) {
|
||||
log_e("Failed to get Thermostat Temperature or Setpoint Attribute.");
|
||||
return false;
|
||||
}
|
||||
if (temperatureVal.val.i16 != _rawTemperature) {
|
||||
temperatureVal.val.i16 = _rawTemperature;
|
||||
bool ret;
|
||||
ret = updateAttributeVal(Thermostat::Id, attribute_id, &temperatureVal);
|
||||
if (!ret) {
|
||||
log_e("Failed to update Thermostat Temperature or Setpoint Attribute.");
|
||||
return false;
|
||||
}
|
||||
*internalValue = _rawTemperature;
|
||||
}
|
||||
log_v("Temperature set to %.01fC", (float)_rawTemperature / 100.00);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MatterThermostat::setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCollingTemperature) {
|
||||
// at least one of the setpoints must be valid
|
||||
bool settingCooling = _setpointCollingTemperature != (float)0xffff;
|
||||
bool settingHeating = _setpointHeatingTemperature != (float)0xffff;
|
||||
if (!settingCooling && !settingHeating) {
|
||||
log_e("Invalid Setpoints values. Set correctly at least one of them in Celsius.");
|
||||
return false;
|
||||
}
|
||||
int16_t _rawHeatValue = static_cast<int16_t>(_setpointHeatingTemperature * 100.0f);
|
||||
int16_t _rawCoolValue = static_cast<int16_t>(_setpointCollingTemperature * 100.0f);
|
||||
|
||||
// check limits for the setpoints
|
||||
if (settingHeating && (_rawHeatValue < kDefaultMinHeatSetpointLimit || _rawHeatValue > kDefaultMaxHeatSetpointLimit)) {
|
||||
log_e(
|
||||
"Invalid Heating Setpoint value: %.01fC - valid range %d..%d", _setpointHeatingTemperature, kDefaultMinHeatSetpointLimit / 100,
|
||||
kDefaultMaxHeatSetpointLimit / 100
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (settingCooling && (_rawCoolValue < kDefaultMinCoolSetpointLimit || _rawCoolValue > kDefaultMaxCoolSetpointLimit)) {
|
||||
log_e(
|
||||
"Invalid Cooling Setpoint value: %.01fC - valid range %d..%d", _setpointCollingTemperature, kDefaultMinCoolSetpointLimit / 100,
|
||||
kDefaultMaxCoolSetpointLimit / 100
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// AUTO mode requires both setpoints to be valid to each other and respect the deadband
|
||||
if (currentMode == THERMOSTAT_MODE_AUTO) {
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
|
||||
float deadband = getDeadBand();
|
||||
#endif
|
||||
// only setting Cooling Setpoint
|
||||
if (settingCooling && !settingHeating && _rawCoolValue < (heatingSetpointTemperature + (kDefaultDeadBand * 10))) {
|
||||
log_e(
|
||||
"AutoMode :: Invalid Cooling Setpoint value: %.01fC - must be higher or equal than %.01fC", _setpointCollingTemperature, getHeatingSetpoint() + deadband
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// only setting Heating Setpoint
|
||||
if (!settingCooling && settingHeating && _rawHeatValue > (coolingSetpointTemperature - (kDefaultDeadBand * 10))) {
|
||||
log_e(
|
||||
"AutoMode :: Invalid Heating Setpoint value: %.01fC - must be lower or equal than %.01fC", _setpointHeatingTemperature, getCoolingSetpoint() - deadband
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// setting both setpoints
|
||||
if (settingCooling && settingHeating && (_rawCoolValue <= _rawHeatValue || _rawCoolValue - _rawHeatValue < kDefaultDeadBand * 10.0)) {
|
||||
log_e(
|
||||
"AutoMode :: Error - Heating Setpoint %.01fC must be lower than Cooling Setpoint %.01fC with a minimum difference of %0.1fC",
|
||||
_setpointHeatingTemperature, _setpointCollingTemperature, deadband
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
if (settingCooling) {
|
||||
ret &= setRawTemperature(_rawCoolValue, Thermostat::Attributes::OccupiedCoolingSetpoint::Id, &coolingSetpointTemperature);
|
||||
}
|
||||
if (settingHeating) {
|
||||
ret &= setRawTemperature(_rawHeatValue, Thermostat::Attributes::OccupiedHeatingSetpoint::Id, &heatingSetpointTemperature);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
|
||||
207
libraries/Matter/src/MatterEndpoints/MatterThermostat.h
Normal file
207
libraries/Matter/src/MatterEndpoints/MatterThermostat.h
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <sdkconfig.h>
|
||||
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
|
||||
|
||||
#include <Matter.h>
|
||||
#include <MatterEndPoint.h>
|
||||
#include <app-common/zap-generated/cluster-enums.h>
|
||||
|
||||
using namespace chip::app::Clusters;
|
||||
|
||||
class MatterThermostat : public MatterEndPoint {
|
||||
public:
|
||||
// clang-format off
|
||||
enum ControlSequenceOfOperation_t {
|
||||
THERMOSTAT_SEQ_OP_COOLING = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kCoolingOnly,
|
||||
THERMOSTAT_SEQ_OP_COOLING_REHEAT = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kCoolingWithReheat,
|
||||
THERMOSTAT_SEQ_OP_HEATING = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kHeatingOnly,
|
||||
THERMOSTAT_SEQ_OP_HEATING_REHEAT = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kHeatingWithReheat,
|
||||
THERMOSTAT_SEQ_OP_COOLING_HEATING = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kCoolingAndHeating,
|
||||
THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kCoolingAndHeatingWithReheat,
|
||||
};
|
||||
|
||||
enum ThermostatMode_t {
|
||||
THERMOSTAT_MODE_OFF = (uint8_t) Thermostat::SystemModeEnum::kOff,
|
||||
THERMOSTAT_MODE_AUTO = (uint8_t) Thermostat::SystemModeEnum::kAuto,
|
||||
THERMOSTAT_MODE_COOL = (uint8_t) Thermostat::SystemModeEnum::kCool,
|
||||
THERMOSTAT_MODE_HEAT = (uint8_t) Thermostat::SystemModeEnum::kHeat,
|
||||
THERMOSTAT_MODE_EMERGENCY_HEAT = (uint8_t) Thermostat::SystemModeEnum::kEmergencyHeat,
|
||||
THERMOSTAT_MODE_PRECOOLING = (uint8_t) Thermostat::SystemModeEnum::kPrecooling,
|
||||
THERMOSTAT_MODE_FAN_ONLY = (uint8_t) Thermostat::SystemModeEnum::kFanOnly,
|
||||
THERMOSTAT_MODE_DRY = (uint8_t) Thermostat::SystemModeEnum::kDry,
|
||||
THERMOSTAT_MODE_SLEEP = (uint8_t) Thermostat::SystemModeEnum::kSleep
|
||||
};
|
||||
|
||||
enum ThermostatAutoMode_t {
|
||||
THERMOSTAT_AUTO_MODE_DISABLED = (uint8_t) Thermostat::SystemModeEnum::kOff,
|
||||
THERMOSTAT_AUTO_MODE_ENABLED = (uint8_t) Thermostat::SystemModeEnum::kAuto,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
MatterThermostat();
|
||||
~MatterThermostat();
|
||||
// begin Matter Thermostat endpoint with initial Operation Mode
|
||||
bool begin(ControlSequenceOfOperation_t controlSequence = THERMOSTAT_SEQ_OP_COOLING, ThermostatAutoMode_t autoMode = THERMOSTAT_AUTO_MODE_DISABLED);
|
||||
// this will stop processing Thermostat Matter events
|
||||
void end();
|
||||
|
||||
// set the Thermostat Mode
|
||||
bool setMode(ThermostatMode_t mode);
|
||||
// get the Thermostat Mode
|
||||
ThermostatMode_t getMode() {
|
||||
return currentMode;
|
||||
}
|
||||
// returns a friendly string for the Fan Mode
|
||||
static const char *getThermostatModeString(uint8_t mode) {
|
||||
return thermostatModeString[mode];
|
||||
}
|
||||
|
||||
// get the Thermostat Control Sequence of Operation
|
||||
ControlSequenceOfOperation_t getControlSequence() {
|
||||
return controlSequence;
|
||||
}
|
||||
|
||||
// get the minimum heating setpoint in 1/100th of a Celsio degree
|
||||
float getMinHeatSetpoint() {
|
||||
return (float)kDefaultMinHeatSetpointLimit / 100.00;
|
||||
}
|
||||
// get the maximum heating setpoint in 1/100th of a Celsio degree
|
||||
float getMaxHeatSetpoint() {
|
||||
return (float)kDefaultMaxHeatSetpointLimit / 100.00;
|
||||
}
|
||||
// get the minimum cooling setpoint in 1/100th of a Celsio degree
|
||||
float getMinCoolSetpoint() {
|
||||
return (float)kDefaultMinCoolSetpointLimit / 100.00;
|
||||
}
|
||||
// get the maximum cooling setpoint in 1/100th of a Celsio degree
|
||||
float getMaxCoolSetpoint() {
|
||||
return (float)kDefaultMaxCoolSetpointLimit / 100.00;
|
||||
}
|
||||
// get the deadband in 1/10th of a Celsio degree
|
||||
float getDeadBand() {
|
||||
return (float)kDefaultDeadBand / 10.00;
|
||||
}
|
||||
|
||||
// generic function for setting the cooling and heating setpoints - checks if the setpoints are valid
|
||||
// it can be used to set both setpoints at the same time or only one of them, by setting the other to (float)0xffff
|
||||
// Heating Setpoint must be lower than Cooling Setpoint
|
||||
// When using AUTO mode the Cooling Setpoint must be higher than Heating Setpoint by at least the 2.5C (deadband)
|
||||
// Thermostat Matter Server will enforce those rules and the Max/Min setpoints limits as in the Matter Specification
|
||||
bool setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCollingTemperature);
|
||||
|
||||
// set the heating setpoint in 1/100th of a Celsio degree
|
||||
bool setHeatingSetpoint(double _setpointHeatingTemperature) {
|
||||
return setCoolingHeatingSetpoints((double)0xffff, _setpointHeatingTemperature);
|
||||
}
|
||||
// get the heating setpoint in 1/100th of a Celsio degree
|
||||
double getHeatingSetpoint() {
|
||||
return heatingSetpointTemperature / 100.0;
|
||||
}
|
||||
// set the cooling setpoint in 1/100th of a Celsio degree
|
||||
bool setCoolingSetpoint(double _setpointCollingTemperature) {
|
||||
return setCoolingHeatingSetpoints(_setpointCollingTemperature, (double)0xffff);
|
||||
}
|
||||
// get the cooling setpoint in 1/100th of a Celsio degree
|
||||
double getCoolingSetpoint() {
|
||||
return coolingSetpointTemperature / 100.0;
|
||||
}
|
||||
|
||||
// set the local Thermostat temperature in Celsio degrees
|
||||
bool setLocalTemperature(double temperature) {
|
||||
// stores up to 1/100th of a Celsio degree precision
|
||||
int16_t rawValue = static_cast<int16_t>(temperature * 100.0f);
|
||||
return setRawTemperature(rawValue, Thermostat::Attributes::LocalTemperature::Id, &localTemperature);
|
||||
}
|
||||
// returns the local Thermostat float temperature with 1/100th of a Celsio degree precision
|
||||
double getLocalTemperature() {
|
||||
return (double)localTemperature / 100.0;
|
||||
}
|
||||
|
||||
// User Callback for whenever the Thermostat Mode is changed by the Matter Controller
|
||||
using EndPointModeCB = std::function<bool(ThermostatMode_t)>;
|
||||
void onChangeMode(EndPointModeCB onChangeCB) {
|
||||
_onChangeModeCB = onChangeCB;
|
||||
}
|
||||
|
||||
// User Callback for whenever the Local Temperature is changed by the Matter Controller
|
||||
using EndPointTemperatureCB = std::function<bool(float)>;
|
||||
void onChangeLocalTemperature(EndPointTemperatureCB onChangeCB) {
|
||||
_onChangeTemperatureCB = onChangeCB;
|
||||
}
|
||||
|
||||
// User Callback for whenever the Cooling or Heating Setpoint is changed by the Matter Controller
|
||||
using EndPointCoolingSetpointCB = std::function<bool(double)>;
|
||||
void onChangeCoolingSetpoint(EndPointCoolingSetpointCB onChangeCB) {
|
||||
_onChangeCoolingSetpointCB = onChangeCB;
|
||||
}
|
||||
|
||||
// User Callback for whenever the Cooling or Heating Setpoint is changed by the Matter Controller
|
||||
using EndPointHeatingSetpointCB = std::function<bool(double)>;
|
||||
void onChangeHeatingSetpoint(EndPointHeatingSetpointCB onChangeCB) {
|
||||
_onChangeHeatingSetpointCB = onChangeCB;
|
||||
}
|
||||
|
||||
// User Callback for whenever any parameter is changed by the Matter Controller
|
||||
// Main parameters are Thermostat Mode, Local Temperature, Cooling Setpoint and Heating Setpoint
|
||||
// Those can be obtained using getMode(), getTemperature(), getCoolingSetpoint() and getHeatingSetpoint()
|
||||
using EndPointCB = std::function<bool(void)>;
|
||||
void onChange(EndPointCB onChangeCB) {
|
||||
_onChangeCB = onChangeCB;
|
||||
}
|
||||
|
||||
// this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
|
||||
bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val);
|
||||
|
||||
protected:
|
||||
bool started = false;
|
||||
// implementation keeps temperature in 1/100th of a Celsio degree
|
||||
int16_t coolingSetpointTemperature = 2400; // 24C cooling setpoint
|
||||
int16_t localTemperature = 2000; // 20C local temperature
|
||||
int16_t heatingSetpointTemperature = 1600; // 16C heating setpoint
|
||||
|
||||
ThermostatMode_t currentMode = THERMOSTAT_MODE_OFF;
|
||||
ControlSequenceOfOperation_t controlSequence = THERMOSTAT_SEQ_OP_COOLING;
|
||||
ThermostatAutoMode_t autoMode = THERMOSTAT_AUTO_MODE_DISABLED;
|
||||
|
||||
EndPointModeCB _onChangeModeCB = NULL;
|
||||
EndPointTemperatureCB _onChangeTemperatureCB = NULL;
|
||||
EndPointCoolingSetpointCB _onChangeCoolingSetpointCB = NULL;
|
||||
EndPointHeatingSetpointCB _onChangeHeatingSetpointCB = NULL;
|
||||
EndPointCB _onChangeCB = NULL;
|
||||
|
||||
// internal function to set the raw temperature value (Matter Cluster)
|
||||
bool setRawTemperature(int16_t _rawTemperature, uint32_t attribute_id, int16_t *internalValue);
|
||||
|
||||
// clang-format off
|
||||
// Default Thermostat values - can't be changed - defined in the Thermostat Cluster Server code
|
||||
static const int16_t kDefaultAbsMinHeatSetpointLimit = 700; // 7C (44.5 F)
|
||||
static const int16_t kDefaultMinHeatSetpointLimit = 700; // 7C (44.5 F)
|
||||
static const int16_t kDefaultAbsMaxHeatSetpointLimit = 3000; // 30C (86 F)
|
||||
static const int16_t kDefaultMaxHeatSetpointLimit = 3000; // 30C (86 F)
|
||||
|
||||
static const int16_t kDefaultAbsMinCoolSetpointLimit = 1600; // 16C (61 F)
|
||||
static const int16_t kDefaultMinCoolSetpointLimit = 1600; // 16C (61 F)
|
||||
static const int16_t kDefaultAbsMaxCoolSetpointLimit = 3200; // 32C (90 F)
|
||||
static const int16_t kDefaultMaxCoolSetpointLimit = 3200; // 32C (90 F)
|
||||
|
||||
static const int8_t kDefaultDeadBand = 25; // 2.5C when in AUTO mode
|
||||
// clang-format on
|
||||
|
||||
// string helper for the THERMOSTAT MODE
|
||||
static const char *thermostatModeString[5];
|
||||
};
|
||||
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
|
||||
Loading…
Reference in a new issue