Skip to content

feat(matter): Adds Matter Enhanced Color Light Endpoint (CW/WW/RGB) #10657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 2, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -172,6 +172,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS
libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp
libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp
libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp
libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp
libraries/Matter/src/Matter.cpp)

set(ARDUINO_LIBRARY_PPP_SRCS
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// 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>
#include <Preferences.h>

// List of Matter Endpoints for this Node
// Color Light Endpoint
MatterEnhancedColorLight EnhancedColorLight;

// It will use HSV color to control all Matter Attribute Changes
HsvColor_t currentHSVColor = {0, 0, 0};

// it will keep last OnOff & HSV Color state stored, using Preferences
Preferences matterPref;
const char *onOffPrefKey = "OnOff";
const char *hsvColorPrefKey = "HSV";

// set your board RGB LED pin here
#ifdef RGB_BUILTIN
const uint8_t ledPin = RGB_BUILTIN;
#else
const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN
#warning "Do not forget to set the RGB LED pin"
#endif

// set your board USER BUTTON pin here
const uint8_t buttonPin = 0; // Set your pin here. Using BOOT Button. C6/C3 use GPIO9.

// 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 the RGB LED Light based on the current state of the Enhanced Color Light
bool setLightState(bool state, espHsvColor_t colorHSV, uint8_t brighteness, uint16_t temperature_Mireds) {

if (state) {
#ifdef RGB_BUILTIN
// currentHSVColor keeps final color result
espRgbColor_t rgbColor = espHsvColorToRgbColor(currentHSVColor);
// set the RGB LED
rgbLedWrite(ledPin, rgbColor.r, rgbColor.g, rgbColor.b);
#else
// No Color RGB LED, just use the HSV value (brightness) to control the LED
analogWrite(ledPin, colorHSV.v);
#endif
} else {
digitalWrite(ledPin, LOW);
}
// store last HSV Color and OnOff state for when the Light is restarted / power goes off
matterPref.putBool(onOffPrefKey, state);
matterPref.putUInt(hsvColorPrefKey, currentHSVColor.h << 16 | currentHSVColor.s << 8 | currentHSVColor.v);
// This callback must return the success state to Matter core
return true;
}

void setup() {
// Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch
pinMode(buttonPin, INPUT_PULLUP);
// Initialize the LED (light) GPIO and Matter End Point
pinMode(ledPin, 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);

// Initialize Matter EndPoint
matterPref.begin("MatterPrefs", false);
// default OnOff state is ON if not stored before
bool lastOnOffState = matterPref.getBool(onOffPrefKey, true);
// default HSV color is (21, 216, 25) - Warm White Color at 10% intensity
uint32_t prefHsvColor = matterPref.getUInt(hsvColorPrefKey, 21 << 16 | 216 << 8 | 25);
currentHSVColor = {uint8_t(prefHsvColor >> 16), uint8_t(prefHsvColor >> 8), uint8_t(prefHsvColor)};
EnhancedColorLight.begin(lastOnOffState, currentHSVColor);
// set the callback function to handle the Light state change
EnhancedColorLight.onChange(setLightState);

// lambda functions are used to set the attribute change callbacks
EnhancedColorLight.onChangeOnOff([](bool state) {
Serial.printf("Light OnOff changed to %s\r\n", state ? "ON" : "OFF");
return true;
});
EnhancedColorLight.onChangeColorTemperature([](uint16_t colorTemperature) {
Serial.printf("Light Color Temperature changed to %d\r\n", colorTemperature);
// get correspondent Hue and Saturation of the color temperature
HsvColor_t hsvTemperature = espRgbColorToHsvColor(espCTToRgbColor(colorTemperature));
// keep previous the brightness and just change the Hue and Saturation
currentHSVColor.h = hsvTemperature.h;
currentHSVColor.s = hsvTemperature.s;
return true;
});
EnhancedColorLight.onChangeBrightness([](uint8_t brightness) {
Serial.printf("Light brightness changed to %d\r\n", brightness);
// change current brightness (HSV value)
currentHSVColor.v = brightness;
return true;
});
EnhancedColorLight.onChangeColorHSV([](HsvColor_t hsvColor) {
Serial.printf("Light HSV Color changed to (%d,%d,%d)\r\n", hsvColor.h, hsvColor.s, hsvColor.v);
// keep the current brightness and just change Hue and Saturation
currentHSVColor.h = hsvColor.h;
currentHSVColor.s = hsvColor.s;
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.");
Serial.printf(
"Initial state: %s | RGB Color: (%d,%d,%d) \r\n", EnhancedColorLight ? "ON" : "OFF", EnhancedColorLight.getColorRGB().r,
EnhancedColorLight.getColorRGB().g, EnhancedColorLight.getColorRGB().b
);
// configure the Light based on initial on-off state and its color
EnhancedColorLight.updateAccessory();
}
}
// 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 light

void loop() {
// Check Matter Light 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 Light 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.printf(
"Initial state: %s | RGB Color: (%d,%d,%d) \r\n", EnhancedColorLight ? "ON" : "OFF", EnhancedColorLight.getColorRGB().r,
EnhancedColorLight.getColorRGB().g, EnhancedColorLight.getColorRGB().b
);
// configure the Light based on initial on-off state and its color
EnhancedColorLight.updateAccessory();
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
}

// A button is also used to control the light
// 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 Light toggle switch 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
// Toggle button is released - toggle the light
Serial.println("User button released. Toggling Light!");
EnhancedColorLight.toggle(); // Matter Controller also can see the change

// Factory reset is triggered if the button is pressed longer than 10 seconds
if (time_diff > decommissioningTimeout) {
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
EnhancedColorLight = false; // turn the light off
Matter.decommission();
}
}
}
7 changes: 7 additions & 0 deletions libraries/Matter/examples/MatterEnhancedColorLight/ci.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=huge_app",
"requires": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
]
}
1 change: 1 addition & 0 deletions libraries/Matter/keywords.txt
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ MatterOnOffLight KEYWORD1
MatterDimmableLight KEYWORD1
MatterColorTemperatureLight KEYWORD1
MatterColorLight KEYWORD1
MatterEnhancedColorLight KEYWORD1
MatterEndPoint KEYWORD1

#######################################
2 changes: 2 additions & 0 deletions libraries/Matter/src/Matter.h
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
#include <MatterEndpoints/MatterDimmableLight.h>
#include <MatterEndpoints/MatterColorTemperatureLight.h>
#include <MatterEndpoints/MatterColorLight.h>
#include <MatterEndpoints/MatterEnhancedColorLight.h>

using namespace esp_matter;

@@ -52,6 +53,7 @@ class ArduinoMatter {
friend class MatterDimmableLight;
friend class MatterColorTemperatureLight;
friend class MatterColorLight;
friend class MatterEnhancedColorLight;

protected:
static void _init();
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ class MatterColorTemperatureLight : public MatterEndPoint {
void onChangeOnOff(EndPointOnOffCB onChangeCB) {
_onChangeOnOffCB = onChangeCB;
}

// User Callback for whenever the Light brightness value [0..255] is changed by the Matter Controller
using EndPointBrightnessCB = std::function<bool(uint8_t)>;
void onChangeBrightness(EndPointBrightnessCB onChangeCB) {
389 changes: 389 additions & 0 deletions libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,389 @@
// 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 <app/server/Server.h>
#include <MatterEndpoints/MatterEnhancedColorLight.h>

using namespace esp_matter;
using namespace esp_matter::endpoint;
using namespace chip::app::Clusters;

// endpoint for enhanced color light device
namespace esp_matter {
using namespace cluster;
namespace endpoint {
namespace enhanced_color_light {
typedef struct config {
cluster::descriptor::config_t descriptor;
cluster::identify::config_t identify;
cluster::groups::config_t groups;
cluster::scenes_management::config_t scenes_management;
cluster::on_off::config_t on_off;
cluster::level_control::config_t level_control;
cluster::color_control::config_t color_control;
} config_t;

uint32_t get_device_type_id() {
return ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID;
}

uint8_t get_device_type_version() {
return ESP_MATTER_EXTENDED_COLOR_LIGHT_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);
cluster_t *identify_cluster = identify::create(endpoint, &(config->identify), CLUSTER_FLAG_SERVER);
identify::command::create_trigger_effect(identify_cluster);
groups::create(endpoint, &(config->groups), CLUSTER_FLAG_SERVER);
cluster_t *scenes_cluster = scenes_management::create(endpoint, &(config->scenes_management), CLUSTER_FLAG_SERVER);
scenes_management::command::create_copy_scene(scenes_cluster);
scenes_management::command::create_copy_scene_response(scenes_cluster);

on_off::create(endpoint, &(config->on_off), CLUSTER_FLAG_SERVER, on_off::feature::lighting::get_id());
level_control::create(
endpoint, &(config->level_control), CLUSTER_FLAG_SERVER, level_control::feature::on_off::get_id() | level_control::feature::lighting::get_id()
);
color_control::create(
endpoint, &(config->color_control), CLUSTER_FLAG_SERVER,
color_control::feature::hue_saturation::get_id() | color_control::feature::color_temperature::get_id()
);
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 enhanced_color_light
} // namespace endpoint
} // namespace esp_matter

bool MatterEnhancedColorLight::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 Enhanced ColorLight device has not begun.");
return false;
}

log_d(
"Enhanced ColorAttr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u, type: %u", endpoint_id, cluster_id, attribute_id, val->val.u32,
val->type
);

if (endpoint_id == getEndPointId()) {
switch (cluster_id) {
case OnOff::Id:
if (attribute_id == OnOff::Attributes::OnOff::Id) {
log_d("Enhanced ColorLight On/Off State changed to %d", val->val.b);
if (_onChangeOnOffCB != NULL) {
ret &= _onChangeOnOffCB(val->val.b);
}
if (_onChangeCB != NULL) {
ret &= _onChangeCB(val->val.b, colorHSV, brightnessLevel, colorTemperatureLevel);
}
if (ret == true) {
onOffState = val->val.b;
}
}
break;
case LevelControl::Id:
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
log_d("Enhanced ColorLight Brightness changed to %d", val->val.u8);
if (_onChangeBrightnessCB != NULL) {
ret &= _onChangeBrightnessCB(val->val.u8);
}
if (_onChangeCB != NULL) {
ret &= _onChangeCB(onOffState, colorHSV, val->val.u8, colorTemperatureLevel);
}
if (ret == true) {
colorHSV.v = val->val.u8;
}
}
break;
case ColorControl::Id:
{
if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) {
log_d("Enhanced ColorLight Temperature changed to %d", val->val.u16);
if (_onChangeTemperatureCB != NULL) {
ret &= _onChangeTemperatureCB(val->val.u16);
}
if (_onChangeCB != NULL) {
ret &= _onChangeCB(onOffState, colorHSV, brightnessLevel, val->val.u16);
}
if (ret == true) {
colorTemperatureLevel = val->val.u16;
}
break;
}
if (attribute_id != ColorControl::Attributes::CurrentHue::Id && attribute_id != ColorControl::Attributes::CurrentSaturation::Id) {
log_i("Color Control Attribute ID [%x] not processed.", attribute_id);
break;
}
espHsvColor_t hsvColor = {colorHSV.h, colorHSV.s, colorHSV.v};
if (attribute_id == ColorControl::Attributes::CurrentHue::Id) {
log_d("Enhanced ColorLight Hue changed to %d", val->val.u8);
hsvColor.h = val->val.u8;
} else { // attribute_id == ColorControl::Attributes::CurrentSaturation::Id)
log_d("Enhanced ColorLight Saturation changed to %d", val->val.u8);
hsvColor.s = val->val.u8;
}
if (_onChangeColorCB != NULL) {
ret &= _onChangeColorCB(hsvColor);
}
if (_onChangeCB != NULL) {
ret &= _onChangeCB(onOffState, hsvColor, brightnessLevel, colorTemperatureLevel);
}
if (ret == true) {
colorHSV = {hsvColor.h, hsvColor.s, hsvColor.v};
}
break;
}
}
}
return ret;
}

MatterEnhancedColorLight::MatterEnhancedColorLight() {}

MatterEnhancedColorLight::~MatterEnhancedColorLight() {
end();
}

bool MatterEnhancedColorLight::begin(bool initialState, espHsvColor_t _colorHSV, uint8_t brightness, uint16_t ColorTemperature) {
ArduinoMatter::_init();
enhanced_color_light::config_t light_config;

light_config.on_off.on_off = initialState;
light_config.on_off.lighting.start_up_on_off = nullptr;
onOffState = initialState;

light_config.level_control.current_level = brightness;
light_config.level_control.lighting.start_up_current_level = nullptr;

light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
light_config.color_control.color_temperature.color_temperature_mireds = ColorTemperature;
light_config.color_control.color_temperature.startup_color_temperature_mireds = nullptr;
colorTemperatureLevel = ColorTemperature;

light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kCurrentHueAndCurrentSaturation;
light_config.color_control.hue_saturation.current_hue = _colorHSV.h;
light_config.color_control.hue_saturation.current_saturation = _colorHSV.s;
colorHSV = {_colorHSV.h, _colorHSV.s, _colorHSV.v};

// endpoint handles can be used to add/modify clusters.
endpoint_t *endpoint = enhanced_color_light::create(node::get(), &light_config, ENDPOINT_FLAG_NONE, (void *)this);
if (endpoint == nullptr) {
log_e("Failed to create Enhanced ColorLight endpoint");
return false;
}

setEndPointId(endpoint::get_id(endpoint));
log_i("Enhanced ColorLight created with endpoint_id %d", getEndPointId());

/* Mark deferred persistence for some attributes that might be changed rapidly */
cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id);
attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id);
attribute::set_deferred_persistence(current_level_attribute);

started = true;
return true;
}

void MatterEnhancedColorLight::end() {
started = false;
}

bool MatterEnhancedColorLight::setOnOff(bool newState) {
if (!started) {
log_e("Matter Enhanced ColorLight device has not begun.");
return false;
}

// avoid processing the a "no-change"
if (onOffState == newState) {
return true;
}

onOffState = newState;

endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
cluster_t *cluster = cluster::get(endpoint, OnOff::Id);
attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id);

esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);

if (val.val.b != onOffState) {
val.val.b = onOffState;
attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val);
}
return true;
}

void MatterEnhancedColorLight::updateAccessory() {
if (_onChangeCB != NULL) {
_onChangeCB(onOffState, colorHSV, brightnessLevel, colorTemperatureLevel);
}
}

bool MatterEnhancedColorLight::getOnOff() {
return onOffState;
}

bool MatterEnhancedColorLight::toggle() {
return setOnOff(!onOffState);
}

bool MatterEnhancedColorLight::setBrightness(uint8_t newBrightness) {
if (!started) {
log_w("Matter Enhanced ColorLight device has not begun.");
return false;
}

// avoid processing the a "no-change"
if (brightnessLevel == newBrightness) {
return true;
}

brightnessLevel = newBrightness;

endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
cluster_t *cluster = cluster::get(endpoint, LevelControl::Id);
attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id);

esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);

if (val.val.u8 != brightnessLevel) {
val.val.u8 = brightnessLevel;
attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val);
}
return true;
}

uint8_t MatterEnhancedColorLight::getBrightness() {
return brightnessLevel;
}

bool MatterEnhancedColorLight::setColorTemperature(uint16_t newTemperature) {
if (!started) {
log_w("Matter Enhanced ColorLight device has not begun.");
return false;
}

// avoid processing the a "no-change"
if (colorTemperatureLevel == newTemperature) {
return true;
}

colorTemperatureLevel = newTemperature;

endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
cluster_t *cluster = cluster::get(endpoint, ColorControl::Id);
attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);

esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);

if (val.val.u16 != colorTemperatureLevel) {
val.val.u16 = colorTemperatureLevel;
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id, &val);
}
return true;
}

uint16_t MatterEnhancedColorLight::getColorTemperature() {
return colorTemperatureLevel;
}

bool MatterEnhancedColorLight::setColorRGB(espRgbColor_t _rgbColor) {
return setColorHSV(espRgbColorToHsvColor(_rgbColor));
}

espRgbColor_t MatterEnhancedColorLight::getColorRGB() {
return espHsvColorToRgbColor(colorHSV);
}

bool MatterEnhancedColorLight::setColorHSV(espHsvColor_t _hsvColor) {

if (!started) {
log_w("Matter Enhanced ColorLight device has not begun.");
return false;
}

// avoid processing the a "no-change"
if (colorHSV.h == _hsvColor.h && colorHSV.s == _hsvColor.s && colorHSV.v == _hsvColor.v) {
return true;
}

colorHSV = {_hsvColor.h, _hsvColor.s, _hsvColor.v};

endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
cluster_t *cluster = cluster::get(endpoint, ColorControl::Id);
// update hue
attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id);
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
if (val.val.u8 != colorHSV.h) {
val.val.u8 = colorHSV.h;
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::CurrentHue::Id, &val);
}
// update saturation
attribute = attribute::get(cluster, ColorControl::Attributes::CurrentSaturation::Id);
val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
if (val.val.u8 != colorHSV.s) {
val.val.u8 = colorHSV.s;
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::CurrentSaturation::Id, &val);
}
// update value (brightness)
cluster = cluster::get(endpoint, LevelControl::Id);
attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id);
val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
if (val.val.u8 != colorHSV.v) {
val.val.u8 = colorHSV.v;
attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val);
}
return true;
}

espHsvColor_t MatterEnhancedColorLight::getColorHSV() {
return colorHSV;
}

MatterEnhancedColorLight::operator bool() {
return getOnOff();
}

void MatterEnhancedColorLight::operator=(bool newState) {
setOnOff(newState);
}
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
102 changes: 102 additions & 0 deletions libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// 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>

class MatterEnhancedColorLight : public MatterEndPoint {
public:
static const uint8_t MAX_BRIGHTNESS = 255;
static const uint16_t MAX_COLOR_TEMPERATURE = 500;
static const uint16_t MIN_COLOR_TEMPERATURE = 100;

MatterEnhancedColorLight();
~MatterEnhancedColorLight();
// default initial state is off, brightness = 25 (10%), HSV(21, 216, 25), color temperature is 454 (Warm White)
virtual bool begin(bool initialState = false, espHsvColor_t colorHSV = {21, 216, 25}, uint8_t newBrightness = 25, uint16_t colorTemperature = 454);
// this will just stop processing Light Matter events
void end();

bool setOnOff(bool newState); // returns true if successful
bool getOnOff(); // returns current light state
bool toggle(); // returns true if successful

bool setColorTemperature(uint16_t newTemperature); // returns true if successful
uint16_t getColorTemperature(); // returns current temperature

bool setBrightness(uint8_t newBrightness); // returns true if successful
uint8_t getBrightness(); // returns current brightness

bool setColorRGB(espRgbColor_t rgbColor); // returns true if successful
espRgbColor_t getColorRGB(); // returns current RGB Color
bool setColorHSV(espHsvColor_t hsvColor); // returns true if successful
espHsvColor_t getColorHSV(); // returns current HSV Color

// used to update the state of the light using the current Matter Light internal state
// It is necessary to set a user callback function using onChange() to handle the physical light state
void updateAccessory();

operator bool(); // returns current on/off light state
void operator=(bool state); // turns light on or off

// 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 Light On/Off state is changed by the Matter Controller
using EndPointOnOffCB = std::function<bool(bool)>;
void onChangeOnOff(EndPointOnOffCB onChangeCB) {
_onChangeOnOffCB = onChangeCB;
}

// User Callback for whenever the Light brightness value [0..255] is changed by the Matter Controller
using EndPointBrightnessCB = std::function<bool(uint8_t)>;
void onChangeBrightness(EndPointBrightnessCB onChangeCB) {
_onChangeBrightnessCB = onChangeCB;
}

// User Callback for whenever the HSV Color value is changed by the Matter Controller
using EndPointRGBColorCB = std::function<bool(espHsvColor_t)>;
void onChangeColorHSV(EndPointRGBColorCB onChangeCB) {
_onChangeColorCB = onChangeCB;
}

// User Callbqck for whenever the Light temperature value is changed by the Matter Controller
using EndPointTemperatureCB = std::function<bool(uint16_t)>;
void onChangeColorTemperature(EndPointTemperatureCB onChangeCB) {
_onChangeTemperatureCB = onChangeCB;
}

// User Callback for whenever any parameter is changed by the Matter Controller
using EndPointCB = std::function<bool(bool, espHsvColor_t, uint8_t, uint16_t)>;
void onChange(EndPointCB onChangeCB) {
_onChangeCB = onChangeCB;
}

protected:
bool started = false;
bool onOffState = false; // default initial state is off, but it can be changed by begin(bool)
uint8_t brightnessLevel = 0; // default initial brightness is 0, but it can be changed by begin(bool, uint8_t)
espHsvColor_t colorHSV = {0}; // default initial color HSV is black, but it can be changed by begin(bool, uint8_t, espHsvColor_t)
uint16_t colorTemperatureLevel = 0; // default initial color temperature is 0, but it can be changed by begin(bool, uint8_t, espHsvColor_t, uint16_t)
EndPointOnOffCB _onChangeOnOffCB = NULL;
EndPointBrightnessCB _onChangeBrightnessCB = NULL;
EndPointRGBColorCB _onChangeColorCB = NULL;
EndPointTemperatureCB _onChangeTemperatureCB = NULL;
EndPointCB _onChangeCB = NULL;
};
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */