feat(Matter): Creates New Matter Fan Controller Endpoint (#10691)
* feat(matter): creates new matter fan controller endpoint
This commit is contained in:
parent
76d1f9e643
commit
7a82b8be83
8 changed files with 709 additions and 1 deletions
|
|
@ -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
|
||||
|
|
|
|||
202
libraries/Matter/examples/MatterFan/MatterFan.ino
Normal file
202
libraries/Matter/examples/MatterFan/MatterFan.ino
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
libraries/Matter/examples/MatterFan/ci.json
Normal file
7
libraries/Matter/examples/MatterFan/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"
|
||||
]
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
230
libraries/Matter/src/MatterEndpoints/MatterFan.cpp
Normal file
230
libraries/Matter/src/MatterEndpoints/MatterFan.cpp
Normal 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 */
|
||||
163
libraries/Matter/src/MatterEndpoints/MatterFan.h
Normal file
163
libraries/Matter/src/MatterEndpoints/MatterFan.h
Normal 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 */
|
||||
Loading…
Reference in a new issue