feat(Matter): Creates New Matter Fan Controller Endpoint (#10691)

* feat(matter): creates new matter fan controller endpoint
This commit is contained in:
Rodrigo Garcia 2024-12-09 10:01:24 -03:00 committed by GitHub
parent 76d1f9e643
commit 7a82b8be83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 709 additions and 1 deletions

View file

@ -174,6 +174,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS
libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp
libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp
libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp
libraries/Matter/src/MatterEndpoints/MatterFan.cpp
libraries/Matter/src/Matter.cpp)
set(ARDUINO_LIBRARY_PPP_SRCS

View file

@ -0,0 +1,202 @@
// 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.
// Matter Manager
#include <Matter.h>
#include <WiFi.h>
// List of Matter Endpoints for this Node
// Fan Endpoint - On/Off control + Speed Percent Control + Fan Modes
MatterFan Fan;
// set your board USER BUTTON pin here - used for toggling On/Off
const uint8_t buttonPin = 0; // Set your pin here. Using BOOT Button. C6/C3 use GPIO9.
// set your board Analog Pin here - used for changing the Fan speed
const uint8_t analogPin = A0; // Analog Pin depends on each board
// set your board PWM Pin here - used for controlling the Fan speed (DC motor example)
// for this example, it will use the builtin board RGB LED to simulate the Fan DC motor using its brightness
#ifdef RGB_BUILTIN
const uint8_t dcMotorPin = RGB_BUILTIN;
#else
const uint8_t dcMotorPin = 2; // Set your pin here if your board has not defined LED_BUILTIN
#warning "Do not forget to set the RGB LED pin"
#endif
// 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
void fanDCMotorDrive(bool fanState, uint8_t speedPercent) {
// drive the Fan DC motor
if (fanState == false) {
// turn off the Fan
digitalWrite(dcMotorPin, LOW);
} else {
// set the Fan speed
uint8_t fanDCMotorPWM = map(speedPercent, 0, 100, 0, 255);
#ifdef RGB_BUILTIN
rgbLedWrite(dcMotorPin, fanDCMotorPWM, fanDCMotorPWM, fanDCMotorPWM);
#else
analogWrite(dcMotorPin, fanDCMotorPWM);
#endif
}
}
void setup() {
// Initialize the USER BUTTON (Boot button) GPIO that will toggle the Fan (On/Off)
pinMode(buttonPin, INPUT_PULLUP);
// Initialize the Analog Pin A0 used to read input voltage and to set the Fan speed accordingly
pinMode(analogPin, INPUT);
analogReadResolution(10); // 10 bits resolution reading 0..1023
// Initialize the PWM output pin for a Fan DC motor
pinMode(dcMotorPin, OUTPUT);
Serial.begin(115200);
while (!Serial) {
delay(100);
}
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
// enable IPv6
WiFi.enableIPv6(true);
// Manually connect to WiFi
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\r\nWiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(500);
// On Boot or Reset, Fan is set at 0% speed, OFF, changing between OFF, ON, SMART and HIGH
Fan.begin(0, MatterFan::FAN_MODE_OFF, MatterFan::FAN_MODE_SEQ_OFF_HIGH);
// callback functions would control Fan motor
// the Matter Controller will send new data whenever the User APP or Automation request
// single feature callbacks take place before the generic (all features) callback
// This callback will be executed whenever the speed percent matter attribute is updated
Fan.onChangeSpeedPercent([](uint8_t speedPercent) {
// setting speed to Zero, while the Fan is ON, shall turn the Fan OFF
if (speedPercent == MatterFan::OFF_SPEED && Fan.getMode() != MatterFan::FAN_MODE_OFF) {
// ATTR_SET do not update the attribute, just SET it to avoid infinite loop
return Fan.setOnOff(false, Fan.ATTR_SET);
}
// changing the speed to higher than Zero, while the Fan is OFF, shall turn the Fan ON
if (speedPercent > MatterFan::OFF_SPEED && Fan.getMode() == MatterFan::FAN_MODE_OFF) {
// ATTR_SET do not update the attribute, just SET it to avoid infinite loop
return Fan.setOnOff(true, Fan.ATTR_SET);
}
// for other case, just return true
return true;
});
// This callback will be executed whenever the fan mode matter attribute is updated
// This will take action when user APP starts the Fan by changing the mode
Fan.onChangeMode([](MatterFan::FanMode_t fanMode) {
// when the Fan is turned ON using Mode Selection, while it is OFF, shall start it by setting the speed to 50%
if (Fan.getSpeedPercent() == MatterFan::OFF_SPEED && fanMode != MatterFan::FAN_MODE_OFF) {
Serial.printf("Fan set to %s mode -- speed percentage will go to 50%%\r\n", Fan.getFanModeString(fanMode));
// ATTR_SET do not update the attribute, just SET it to avoid infinite loop
return Fan.setSpeedPercent(50, Fan.ATTR_SET);
}
return true;
});
// Generic callback will be executed as soon as a single feature callback is done
// In this example, it will just print status messages
Fan.onChange([](MatterFan::FanMode_t fanMode, uint8_t speedPercent) {
// just report state
Serial.printf("Fan State: Mode %s | %d%% speed.\r\n", Fan.getFanModeString(fanMode), speedPercent);
// drive the Fan DC motor
fanDCMotorDrive(fanMode != MatterFan::FAN_MODE_OFF, speedPercent);
// returns success
return true;
});
// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
// This may be a restart of a already commissioned Matter accessory
if (Matter.isDeviceCommissioned()) {
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
}
}
// Builtin Button control
uint32_t button_time_stamp = 0; // debouncing control
bool button_state = false; // false = released | true = pressed
const uint32_t debouceTime = 250; // button debouncing time (ms)
const uint32_t decommissioningTimeout = 10000; // keep the button pressed for 10s to decommission the Matter Fabric
void loop() {
// 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 Generic Switch 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.");
}
// A builtin button is used to trigger and send a command to the Matter Controller
// 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.
}
// Onboard User Button is used as a smart button or to decommission it
uint32_t time_diff = millis() - button_time_stamp;
if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) {
button_state = false; // released
// button is released - toggle Fan On/Off
Fan.toggle();
Serial.printf("User button released. Setting the Fan %s.\r\n", Fan > 0 ? "ON" : "OFF");
// Factory reset is triggered if the button is pressed longer than 10 seconds
if (time_diff > decommissioningTimeout) {
Serial.println("Decommissioning the Generic Switch Matter Accessory. It shall be commissioned again.");
Matter.decommission();
}
}
// checks Analog pin and adjust the speed only if it has changed
static int lastRead = 0;
// analog values (0..1023) / 103 => mapped into 10 steps (0..9)
int anaVal = analogRead(analogPin) / 103;
if (lastRead != anaVal) {
// speed percent moves in steps of 10. Range is 10..100
if (Fan.setSpeedPercent((anaVal + 1) * 10)) {
lastRead = anaVal;
}
}
}

View file

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=huge_app",
"requires": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
]
}

View file

@ -15,6 +15,9 @@ MatterColorTemperatureLight KEYWORD1
MatterColorLight KEYWORD1
MatterEnhancedColorLight KEYWORD1
MatterEndPoint KEYWORD1
MatterFan KEYWORD1
FanMode_t KEYWORD1
FanModeSequence_t KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -32,6 +35,7 @@ decommission KEYWORD2
attributeChangeCB KEYWORD2
setOnOff KEYWORD2
getOnOff KEYWORD2
toggle KEYWORD2
setBrightness KEYWORD2
getBrightness KEYWORD2
setColorTemperature KEYWORD2
@ -40,7 +44,6 @@ setColorRGB KEYWORD2
getColorRGB KEYWORD2
setColorHSV KEYWORD2
getColorHSV KEYWORD2
toggle KEYWORD2
updateAccessory KEYWORD2
onChange KEYWORD2
onChangeOnOff KEYWORD2
@ -48,6 +51,17 @@ onChangeBrightness KEYWORD2
onChangeColorTemperature KEYWORD2
onChangeColorHSV KEYWORD2
click KEYWORD2
getAttribute KEYWORD2
getAttributeVal KEYWORD2
setAttributeVal KEYWORD2
updateAttributeVal KEYWORD2
getFanModeString KEYWORD2
setSpeedPercent KEYWORD2
getSpeedPercent KEYWORD2
setMode KEYWORD2
getMode KEYWORD2
onChangeMode KEYWORD2
onChangeSpeedPercent KEYWORD2
#######################################
# Constants (LITERAL1)
@ -56,3 +70,21 @@ click KEYWORD2
MAX_BRIGHTNESS LITERAL1
MAX_COLOR_TEMPERATURE LITERAL1
MIN_COLOR_TEMPERATURE LITERAL1
ATTR_SET LITERAL1
ATTR_UPDATE LITERAL1
MAX_SPEED LITERAL1
MIN_SPEED LITERAL1
OFF_SPEED LITERAL1
FAN_MODE_OFF LITERAL1
FAN_MODE_LOW LITERAL1
FAN_MODE_MEDIUM LITERAL1
FAN_MODE_HIGH LITERAL1
FAN_MODE_ON LITERAL1
FAN_MODE_AUTO LITERAL1
FAN_MODE_SMART LITERAL1
FAN_MODE_SEQ_OFF_LOW_MED_HIGH LITERAL1
FAN_MODE_SEQ_OFF_LOW_HIGH LITERAL1
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

View file

@ -25,6 +25,7 @@
#include <MatterEndpoints/MatterColorTemperatureLight.h>
#include <MatterEndpoints/MatterColorLight.h>
#include <MatterEndpoints/MatterEnhancedColorLight.h>
#include <MatterEndpoints/MatterFan.h>
using namespace esp_matter;
@ -56,6 +57,7 @@ public:
friend class MatterColorTemperatureLight;
friend class MatterColorLight;
friend class MatterEnhancedColorLight;
friend class MatterFan;
protected:
static void _init();

View file

@ -19,15 +19,86 @@
#include <Matter.h>
#include <functional>
using namespace esp_matter;
// Matter Endpoint Base Class. Controls the endpoint ID and allows the child class to overwrite attribute change call
class MatterEndPoint {
public:
enum attrOperation_t {
ATTR_SET = false,
ATTR_UPDATE = true
};
uint16_t getEndPointId() {
return endpoint_id;
}
void setEndPointId(uint16_t ep) {
endpoint_id = ep;
}
// helper functions for attribute manipulation
attribute_t *getAttribute(uint32_t cluster_id, uint32_t attribute_id) {
if (endpoint_id == 0) {
log_e("Endpoint ID is not set");
return NULL;
}
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
if (endpoint == NULL) {
log_e("Endpoint [%d] not found", endpoint_id);
return NULL;
}
cluster_t *cluster = cluster::get(endpoint, cluster_id);
if (cluster == NULL) {
log_e("Cluster [%d] not found", cluster_id);
return NULL;
}
attribute_t *attribute = attribute::get(cluster, attribute_id);
if (attribute == NULL) {
log_e("Attribute [%d] not found", attribute_id);
return NULL;
}
return attribute;
}
// get the value of an attribute from its cluster id and attribute it
bool getAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) {
attribute_t *attribute = getAttribute(cluster_id, attribute_id);
if (attribute == NULL) {
return false;
}
if (attribute::get_val(attribute, attrVal) == ESP_OK) {
log_v("GET_VAL Success for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
return true;
}
log_e("GET_VAL FAILED! for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
return false;
}
// set the value of an attribute from its cluster id and attribute it
bool setAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) {
attribute_t *attribute = getAttribute(cluster_id, attribute_id);
if (attribute == NULL) {
return false;
}
if (attribute::set_val(attribute, attrVal) == ESP_OK) {
log_v("SET_VAL Success for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
return true;
}
log_e("SET_VAL FAILED! for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
return false;
}
// update the value of an attribute from its cluster id and attribute it
bool updateAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) {
if (attribute::update(endpoint_id, cluster_id, attribute_id, attrVal) == ESP_OK) {
log_v("Update Success for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
return true;
}
log_e("Update FAILED! for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
return false;
}
// this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
virtual bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) = 0;

View file

@ -0,0 +1,230 @@
// 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/MatterFan.h>
using namespace esp_matter;
using namespace esp_matter::endpoint;
using namespace esp_matter::cluster;
using namespace chip::app::Clusters;
// string helper for the FAN MODE
const char *MatterFan::fanModeString[7] = {"OFF", "LOW", "MEDIUM", "HIGH", "ON", "AUTO", "SMART"};
// bitmap for valid Fan Modes based on order defined in Zap Generated Cluster Enums
const uint8_t MatterFan::fanModeSequence[6] = {fanSeqModeOffLowMedHigh, fanSeqModeOffLowHigh, fanSeqModeOffLowMedHighAuto,
fanSeqModeOffLowHighAuto, fanSeqModeOffHighAuto, fanSeqModeOffHigh};
// Constructor and Method Definitions
MatterFan::MatterFan() {}
MatterFan::~MatterFan() {
end();
}
bool MatterFan::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 Fan device has not begun.");
return false;
}
log_d("Fan Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
if (endpoint_id == getEndPointId() && cluster_id == FanControl::Id) {
switch (attribute_id) {
case FanControl::Attributes::FanMode::Id:
log_v("FanControl Fan Mode changed to %s (%x)", val->val.u8 < 7 ? fanModeString[val->val.u8] : "Unknown", val->val.u8);
if (_onChangeModeCB != NULL) {
ret &= _onChangeModeCB((FanMode_t)val->val.u8);
}
if (_onChangeCB != NULL) {
ret &= _onChangeCB((FanMode_t)val->val.u8, currentPercent);
}
if (ret == true) {
currentFanMode = (FanMode_t)val->val.u8;
}
break;
case FanControl::Attributes::PercentSetting::Id:
case FanControl::Attributes::PercentCurrent::Id:
log_v("FanControl Percent %s changed to %d", attribute_id == FanControl::Attributes::PercentSetting::Id ? "SETTING" : "CURRENT", val->val.u8);
if (_onChangeSpeedCB != NULL) {
ret &= _onChangeSpeedCB(val->val.u8);
}
if (_onChangeCB != NULL) {
ret &= _onChangeCB(currentFanMode, val->val.u8);
}
if (ret == true) {
// change setting speed percent
currentPercent = val->val.u8;
setAttributeVal(FanControl::Id, FanControl::Attributes::PercentSetting::Id, val);
setAttributeVal(FanControl::Id, FanControl::Attributes::PercentCurrent::Id, val);
}
break;
}
}
return ret;
}
bool MatterFan::begin(uint8_t percent, FanMode_t fanMode, FanModeSequence_t fanModeSeq) {
ArduinoMatter::_init();
// endpoint handles can be used to add/modify clusters.
fan::config_t fan_config;
fan_config.fan_control.fan_mode = fanMode;
fan_config.fan_control.percent_current = percent;
fan_config.fan_control.percent_setting = percent;
fan_config.fan_control.fan_mode_sequence = fanModeSeq;
validFanModes = fanModeSequence[fanModeSeq];
endpoint_t *endpoint = fan::create(node::get(), &fan_config, ENDPOINT_FLAG_NONE, (void *)this);
if (endpoint == nullptr) {
log_e("Failed to create Fan endpoint");
return false;
}
currentFanMode = fanMode;
currentPercent = percent;
setEndPointId(endpoint::get_id(endpoint));
log_i("Fan created with endpoint_id %d", getEndPointId());
started = true;
return true;
}
void MatterFan::end() {
started = false;
}
bool MatterFan::setMode(FanMode_t newMode, bool performUpdate) {
if (!started) {
log_w("Matter Fan device has not begun.");
return false;
}
// avoid processing the a "no-change"
if (currentFanMode == newMode) {
return true;
}
// check if the mode is valid based on the sequence used in its creation
if (!(validFanModes & (1 << newMode))) {
log_e("Invalid Fan Mode %s for the current Fan Mode Sequence.", fanModeString[newMode]);
return false;
}
esp_matter_attr_val_t modeVal = esp_matter_invalid(NULL);
if (!getAttributeVal(FanControl::Id, FanControl::Attributes::FanMode::Id, &modeVal)) {
log_e("Failed to get Fan Mode Attribute.");
return false;
}
if (modeVal.val.u8 != (uint8_t)newMode) {
modeVal.val.u8 = (uint8_t)newMode;
bool ret;
if (performUpdate) {
ret = updateAttributeVal(FanControl::Id, FanControl::Attributes::FanMode::Id, &modeVal);
} else {
ret = setAttributeVal(FanControl::Id, FanControl::Attributes::FanMode::Id, &modeVal);
}
if (!ret) {
log_e("Failed to %s Fan Mode Attribute.", performUpdate ? "update" : "set");
return false;
}
}
currentFanMode = newMode;
log_v("Fan Mode %s to %s ==> onOffState[%s]", performUpdate ? "updated" : "set", fanModeString[currentFanMode], getOnOff() ? "ON" : "OFF");
return true;
}
// this function will change the Fan Speed by calling the user application callback
// it is up to the application to decide to turn on, off or change the speed of the fan
bool MatterFan::setSpeedPercent(uint8_t newPercent, bool performUpdate) {
if (!started) {
log_w("Matter Fan device has not begun.");
return false;
}
// avoid processing the a "no-change"
if (currentPercent == newPercent) {
return true;
}
esp_matter_attr_val_t speedVal = esp_matter_invalid(NULL);
if (!getAttributeVal(FanControl::Id, FanControl::Attributes::PercentSetting::Id, &speedVal)) {
log_e("Failed to get Fan Speed Percent Attribute.");
return false;
}
if (speedVal.val.u8 != newPercent) {
speedVal.val.u8 = newPercent;
bool ret;
if (performUpdate) {
ret = updateAttributeVal(FanControl::Id, FanControl::Attributes::PercentSetting::Id, &speedVal);
} else {
ret = setAttributeVal(FanControl::Id, FanControl::Attributes::PercentSetting::Id, &speedVal);
ret = setAttributeVal(FanControl::Id, FanControl::Attributes::PercentCurrent::Id, &speedVal);
}
if (!ret) {
log_e("Failed to %s Fan Speed Percent Attribute.", performUpdate ? "update" : "set");
return false;
}
}
currentPercent = newPercent;
log_v("Fan Speed %s to %d ==> onOffState[%s]", performUpdate ? "updated" : "set", currentPercent, getOnOff() ? "ON" : "OFF");
return true;
}
bool MatterFan::setOnOff(bool newState, bool performUpdate) {
if (!started) {
log_w("Matter Fan device has not begun.");
return false;
}
// avoid processing the a "no-change"
if (getOnOff() == newState) {
return true;
}
esp_matter_attr_val_t modeVal = esp_matter_invalid(NULL);
if (!getAttributeVal(FanControl::Id, FanControl::Attributes::FanMode::Id, &modeVal)) {
log_e("Failed to get Fan Mode Attribute.");
return false;
}
if (modeVal.val.u8 != (uint8_t)newState) {
FanMode_t newMode = newState ? FAN_MODE_ON : FAN_MODE_OFF;
if (!setMode(newMode, performUpdate)) {
return false;
}
}
log_v(
"Fan State %s to %s :: Mode[%s]|Speed[%d]", performUpdate ? "updated" : "set", getOnOff() ? "ON" : "OFF", fanModeString[currentFanMode], currentPercent
);
return true;
}
bool MatterFan::getOnOff() {
return currentFanMode == FAN_MODE_OFF ? false : true;
}
bool MatterFan::toggle(bool performUpdate) {
if (getOnOff() == true) {
return setOnOff(false, performUpdate);
} else {
return setOnOff(true, performUpdate);
}
}
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

View file

@ -0,0 +1,163 @@
// 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-objects.h>
using namespace chip::app::Clusters::FanControl;
// Matter Fan endpoint with On/Off, Mode and Speed control
class MatterFan : public MatterEndPoint {
public:
// Fan feature constants
static const uint8_t MAX_SPEED = 100; // maximum High speed
static const uint8_t MIN_SPEED = 1; // minimum Low speed
static const uint8_t OFF_SPEED = 0; // speed set by Matter when FAN_MODE_OFF
// Default Fan Modes: ON, SMART, HIGH and OFF
// Other mode will depend on what is the configured Fan Mode Sequence
enum FanMode_t {
FAN_MODE_OFF = (uint8_t)FanModeEnum::kOff,
FAN_MODE_LOW = (uint8_t)FanModeEnum::kLow,
FAN_MODE_MEDIUM = (uint8_t)FanModeEnum::kMedium,
FAN_MODE_HIGH = (uint8_t)FanModeEnum::kHigh,
FAN_MODE_ON = (uint8_t)FanModeEnum::kOn,
FAN_MODE_AUTO = (uint8_t)FanModeEnum::kAuto,
FAN_MODE_SMART = (uint8_t)FanModeEnum::kSmart
};
// Menu will always have ON, OFF, HIGH and SMART.
// AUTO will show up only when a AUTO SEQ is CONFIGURED
// LOW and MEDIUM depend on the SEQ MODE configuration
enum FanModeSequence_t {
FAN_MODE_SEQ_OFF_LOW_MED_HIGH = (uint8_t)FanModeSequenceEnum::kOffLowMedHigh,
FAN_MODE_SEQ_OFF_LOW_HIGH = (uint8_t)FanModeSequenceEnum::kOffLowHigh,
FAN_MODE_SEQ_OFF_LOW_MED_HIGH_AUTO = (uint8_t)FanModeSequenceEnum::kOffLowMedHighAuto,
FAN_MODE_SEQ_OFF_LOW_HIGH_AUTO = (uint8_t)FanModeSequenceEnum::kOffLowHighAuto,
FAN_MODE_SEQ_OFF_HIGH_AUTO = (uint8_t)FanModeSequenceEnum::kOffHighAuto,
FAN_MODE_SEQ_OFF_HIGH = (uint8_t)FanModeSequenceEnum::kOffHigh
};
MatterFan();
~MatterFan();
virtual bool begin(uint8_t percent = 0, FanMode_t fanMode = FAN_MODE_OFF, FanModeSequence_t fanModeSeq = FAN_MODE_SEQ_OFF_HIGH);
void end(); // this will just stop processing Matter events
// returns a friendly string for the Fan Mode
static const char *getFanModeString(uint8_t mode) {
return fanModeString[mode];
}
// Fan Control of current On/Off state
bool setOnOff(bool newState, bool performUpdate = true); // sets Fan On/Off state
bool getOnOff(); // returns current Fan state
bool toggle(bool performUpdate = true); // toggle Fun On/Off state
// Fan Control of current speed percent
bool setSpeedPercent(uint8_t newPercent, bool performUpdate = true); // returns true if successful
uint8_t getSpeedPercent() { // returns current Fan Speed Percent
return currentPercent;
}
// Fan Control of current Fan Mode
bool setMode(FanMode_t newMode, bool performUpdate = true); // returns true if successful
FanMode_t getMode() { // returns current Fan Mode
return currentFanMode;
}
// used to update the state of the Fan using the current Matter Fan internal state
// It is necessary to set a user callback function using onChange() to handle the physical Fan motor state
void updateAccessory() {
if (_onChangeCB != NULL) {
_onChangeCB(currentFanMode, currentPercent);
}
}
// returns current Fan speed percent
operator uint8_t() {
return getSpeedPercent();
}
// sets Fan speed percent
void operator=(uint8_t speedPercent) {
setSpeedPercent(speedPercent);
}
// 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);
// User Callback for whenever the Fan Mode (state) is changed by the Matter Controller
using EndPointModeCB = std::function<bool(FanMode_t)>;
void onChangeMode(EndPointModeCB onChangeCB) {
_onChangeModeCB = onChangeCB;
}
// User Callback for whenever the Fan Speed Percentage value [0..100] is changed by the Matter Controller
using EndPointSpeedCB = std::function<bool(uint8_t)>;
void onChangeSpeedPercent(EndPointSpeedCB onChangeCB) {
_onChangeSpeedCB = onChangeCB;
}
// User Callback for whenever any parameter is changed by the Matter Controller
using EndPointCB = std::function<bool(FanMode_t, uint8_t)>;
void onChange(EndPointCB onChangeCB) {
_onChangeCB = onChangeCB;
}
protected:
bool started = false;
uint8_t validFanModes = 0; // bitmap for valid Fan Modes - index of fanModeSequence[]
uint8_t currentPercent = 0; // current speed percent
FanMode_t currentFanMode = FAN_MODE_OFF; // current Fan Mode
EndPointModeCB _onChangeModeCB = NULL;
EndPointSpeedCB _onChangeSpeedCB = NULL;
EndPointCB _onChangeCB = NULL;
// bitmap for Fan Sequence Modes (OFF, LOW, MEDIUM, HIGH, AUTO)
static const uint8_t fanSeqModeOff = 0x01;
static const uint8_t fanSeqModeLow = 0x02;
static const uint8_t fanSeqModeMedium = 0x04;
static const uint8_t fanSeqModeHigh = 0x08;
static const uint8_t fanSeqModeOn = 0x10;
static const uint8_t fanSeqModeAuto = 0x20;
static const uint8_t fanSeqModeSmart = 0x40;
// bitmap for common modes: ON, OFF, HIGH and SMART
static const uint8_t fanSeqCommonModes = fanSeqModeOff | fanSeqModeOn | fanSeqModeHigh | fanSeqModeSmart;
static const uint8_t fanSeqModeOffLowMedHigh = fanSeqCommonModes | fanSeqModeLow | fanSeqModeMedium;
static const uint8_t fanSeqModeOffLowHigh = fanSeqCommonModes | fanSeqModeLow;
static const uint8_t fanSeqModeOffLowMedHighAuto = fanSeqCommonModes | fanSeqModeLow | fanSeqModeMedium | fanSeqModeAuto;
static const uint8_t fanSeqModeOffLowHighAuto = fanSeqCommonModes | fanSeqModeLow | fanSeqModeAuto;
static const uint8_t fanSeqModeOffHighAuto = fanSeqCommonModes | fanSeqModeAuto;
static const uint8_t fanSeqModeOffHigh = fanSeqCommonModes;
// bitmap for valid Fan Modes based on order defined in Zap Generated Cluster Enums
static const uint8_t fanModeSequence[6];
// string helper for the FAN MODE
static const char *fanModeString[7];
};
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */