Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9d8df8b

Browse files
authoredDec 2, 2024
feat(matter): Adds Matter Enhanced Color Light Endpoint (CW/WW/RGB) (espressif#10657)
* feat(matter): created enhanced color light new matter endpoint and example
1 parent c2ce738 commit 9d8df8b

File tree

8 files changed

+708
-0
lines changed

8 files changed

+708
-0
lines changed
 

‎CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS
172172
libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp
173173
libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp
174174
libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp
175+
libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp
175176
libraries/Matter/src/Matter.cpp)
176177

177178
set(ARDUINO_LIBRARY_PPP_SRCS
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Matter Manager
16+
#include <Matter.h>
17+
#include <WiFi.h>
18+
#include <Preferences.h>
19+
20+
// List of Matter Endpoints for this Node
21+
// Color Light Endpoint
22+
MatterEnhancedColorLight EnhancedColorLight;
23+
24+
// It will use HSV color to control all Matter Attribute Changes
25+
HsvColor_t currentHSVColor = {0, 0, 0};
26+
27+
// it will keep last OnOff & HSV Color state stored, using Preferences
28+
Preferences matterPref;
29+
const char *onOffPrefKey = "OnOff";
30+
const char *hsvColorPrefKey = "HSV";
31+
32+
// set your board RGB LED pin here
33+
#ifdef RGB_BUILTIN
34+
const uint8_t ledPin = RGB_BUILTIN;
35+
#else
36+
const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN
37+
#warning "Do not forget to set the RGB LED pin"
38+
#endif
39+
40+
// set your board USER BUTTON pin here
41+
const uint8_t buttonPin = 0; // Set your pin here. Using BOOT Button. C6/C3 use GPIO9.
42+
43+
// WiFi is manually set and started
44+
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
45+
const char *password = "your-password"; // Change this to your WiFi password
46+
47+
// Set the RGB LED Light based on the current state of the Enhanced Color Light
48+
bool setLightState(bool state, espHsvColor_t colorHSV, uint8_t brighteness, uint16_t temperature_Mireds) {
49+
50+
if (state) {
51+
#ifdef RGB_BUILTIN
52+
// currentHSVColor keeps final color result
53+
espRgbColor_t rgbColor = espHsvColorToRgbColor(currentHSVColor);
54+
// set the RGB LED
55+
rgbLedWrite(ledPin, rgbColor.r, rgbColor.g, rgbColor.b);
56+
#else
57+
// No Color RGB LED, just use the HSV value (brightness) to control the LED
58+
analogWrite(ledPin, colorHSV.v);
59+
#endif
60+
} else {
61+
digitalWrite(ledPin, LOW);
62+
}
63+
// store last HSV Color and OnOff state for when the Light is restarted / power goes off
64+
matterPref.putBool(onOffPrefKey, state);
65+
matterPref.putUInt(hsvColorPrefKey, currentHSVColor.h << 16 | currentHSVColor.s << 8 | currentHSVColor.v);
66+
// This callback must return the success state to Matter core
67+
return true;
68+
}
69+
70+
void setup() {
71+
// Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch
72+
pinMode(buttonPin, INPUT_PULLUP);
73+
// Initialize the LED (light) GPIO and Matter End Point
74+
pinMode(ledPin, OUTPUT);
75+
76+
Serial.begin(115200);
77+
while (!Serial) {
78+
delay(100);
79+
}
80+
81+
// We start by connecting to a WiFi network
82+
Serial.print("Connecting to ");
83+
Serial.println(ssid);
84+
// enable IPv6
85+
WiFi.enableIPv6(true);
86+
// Manually connect to WiFi
87+
WiFi.begin(ssid, password);
88+
// Wait for connection
89+
while (WiFi.status() != WL_CONNECTED) {
90+
delay(500);
91+
Serial.print(".");
92+
}
93+
Serial.println("\r\nWiFi connected");
94+
Serial.println("IP address: ");
95+
Serial.println(WiFi.localIP());
96+
delay(500);
97+
98+
// Initialize Matter EndPoint
99+
matterPref.begin("MatterPrefs", false);
100+
// default OnOff state is ON if not stored before
101+
bool lastOnOffState = matterPref.getBool(onOffPrefKey, true);
102+
// default HSV color is (21, 216, 25) - Warm White Color at 10% intensity
103+
uint32_t prefHsvColor = matterPref.getUInt(hsvColorPrefKey, 21 << 16 | 216 << 8 | 25);
104+
currentHSVColor = {uint8_t(prefHsvColor >> 16), uint8_t(prefHsvColor >> 8), uint8_t(prefHsvColor)};
105+
EnhancedColorLight.begin(lastOnOffState, currentHSVColor);
106+
// set the callback function to handle the Light state change
107+
EnhancedColorLight.onChange(setLightState);
108+
109+
// lambda functions are used to set the attribute change callbacks
110+
EnhancedColorLight.onChangeOnOff([](bool state) {
111+
Serial.printf("Light OnOff changed to %s\r\n", state ? "ON" : "OFF");
112+
return true;
113+
});
114+
EnhancedColorLight.onChangeColorTemperature([](uint16_t colorTemperature) {
115+
Serial.printf("Light Color Temperature changed to %d\r\n", colorTemperature);
116+
// get correspondent Hue and Saturation of the color temperature
117+
HsvColor_t hsvTemperature = espRgbColorToHsvColor(espCTToRgbColor(colorTemperature));
118+
// keep previous the brightness and just change the Hue and Saturation
119+
currentHSVColor.h = hsvTemperature.h;
120+
currentHSVColor.s = hsvTemperature.s;
121+
return true;
122+
});
123+
EnhancedColorLight.onChangeBrightness([](uint8_t brightness) {
124+
Serial.printf("Light brightness changed to %d\r\n", brightness);
125+
// change current brightness (HSV value)
126+
currentHSVColor.v = brightness;
127+
return true;
128+
});
129+
EnhancedColorLight.onChangeColorHSV([](HsvColor_t hsvColor) {
130+
Serial.printf("Light HSV Color changed to (%d,%d,%d)\r\n", hsvColor.h, hsvColor.s, hsvColor.v);
131+
// keep the current brightness and just change Hue and Saturation
132+
currentHSVColor.h = hsvColor.h;
133+
currentHSVColor.s = hsvColor.s;
134+
return true;
135+
});
136+
137+
// Matter beginning - Last step, after all EndPoints are initialized
138+
Matter.begin();
139+
// This may be a restart of a already commissioned Matter accessory
140+
if (Matter.isDeviceCommissioned()) {
141+
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
142+
Serial.printf(
143+
"Initial state: %s | RGB Color: (%d,%d,%d) \r\n", EnhancedColorLight ? "ON" : "OFF", EnhancedColorLight.getColorRGB().r,
144+
EnhancedColorLight.getColorRGB().g, EnhancedColorLight.getColorRGB().b
145+
);
146+
// configure the Light based on initial on-off state and its color
147+
EnhancedColorLight.updateAccessory();
148+
}
149+
}
150+
// Button control
151+
uint32_t button_time_stamp = 0; // debouncing control
152+
bool button_state = false; // false = released | true = pressed
153+
const uint32_t debouceTime = 250; // button debouncing time (ms)
154+
const uint32_t decommissioningTimeout = 10000; // keep the button pressed for 10s to decommission the light
155+
156+
void loop() {
157+
// Check Matter Light Commissioning state, which may change during execution of loop()
158+
if (!Matter.isDeviceCommissioned()) {
159+
Serial.println("");
160+
Serial.println("Matter Node is not commissioned yet.");
161+
Serial.println("Initiate the device discovery in your Matter environment.");
162+
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
163+
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
164+
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
165+
// waits for Matter Light Commissioning.
166+
uint32_t timeCount = 0;
167+
while (!Matter.isDeviceCommissioned()) {
168+
delay(100);
169+
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
170+
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
171+
}
172+
}
173+
Serial.printf(
174+
"Initial state: %s | RGB Color: (%d,%d,%d) \r\n", EnhancedColorLight ? "ON" : "OFF", EnhancedColorLight.getColorRGB().r,
175+
EnhancedColorLight.getColorRGB().g, EnhancedColorLight.getColorRGB().b
176+
);
177+
// configure the Light based on initial on-off state and its color
178+
EnhancedColorLight.updateAccessory();
179+
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
180+
}
181+
182+
// A button is also used to control the light
183+
// Check if the button has been pressed
184+
if (digitalRead(buttonPin) == LOW && !button_state) {
185+
// deals with button debouncing
186+
button_time_stamp = millis(); // record the time while the button is pressed.
187+
button_state = true; // pressed.
188+
}
189+
190+
// Onboard User Button is used as a Light toggle switch or to decommission it
191+
uint32_t time_diff = millis() - button_time_stamp;
192+
if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) {
193+
button_state = false; // released
194+
// Toggle button is released - toggle the light
195+
Serial.println("User button released. Toggling Light!");
196+
EnhancedColorLight.toggle(); // Matter Controller also can see the change
197+
198+
// Factory reset is triggered if the button is pressed longer than 10 seconds
199+
if (time_diff > decommissioningTimeout) {
200+
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
201+
EnhancedColorLight = false; // turn the light off
202+
Matter.decommission();
203+
}
204+
}
205+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"fqbn_append": "PartitionScheme=huge_app",
3+
"requires": [
4+
"CONFIG_SOC_WIFI_SUPPORTED=y",
5+
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
6+
]
7+
}

‎libraries/Matter/keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ MatterOnOffLight KEYWORD1
1212
MatterDimmableLight KEYWORD1
1313
MatterColorTemperatureLight KEYWORD1
1414
MatterColorLight KEYWORD1
15+
MatterEnhancedColorLight KEYWORD1
1516
MatterEndPoint KEYWORD1
1617

1718
#######################################

‎libraries/Matter/src/Matter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <MatterEndpoints/MatterDimmableLight.h>
2424
#include <MatterEndpoints/MatterColorTemperatureLight.h>
2525
#include <MatterEndpoints/MatterColorLight.h>
26+
#include <MatterEndpoints/MatterEnhancedColorLight.h>
2627

2728
using namespace esp_matter;
2829

@@ -52,6 +53,7 @@ class ArduinoMatter {
5253
friend class MatterDimmableLight;
5354
friend class MatterColorTemperatureLight;
5455
friend class MatterColorLight;
56+
friend class MatterEnhancedColorLight;
5557

5658
protected:
5759
static void _init();

‎libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class MatterColorTemperatureLight : public MatterEndPoint {
5757
void onChangeOnOff(EndPointOnOffCB onChangeCB) {
5858
_onChangeOnOffCB = onChangeCB;
5959
}
60+
6061
// User Callback for whenever the Light brightness value [0..255] is changed by the Matter Controller
6162
using EndPointBrightnessCB = std::function<bool(uint8_t)>;
6263
void onChangeBrightness(EndPointBrightnessCB onChangeCB) {
Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <sdkconfig.h>
16+
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
17+
18+
#include <Matter.h>
19+
#include <app/server/Server.h>
20+
#include <MatterEndpoints/MatterEnhancedColorLight.h>
21+
22+
using namespace esp_matter;
23+
using namespace esp_matter::endpoint;
24+
using namespace chip::app::Clusters;
25+
26+
// endpoint for enhanced color light device
27+
namespace esp_matter {
28+
using namespace cluster;
29+
namespace endpoint {
30+
namespace enhanced_color_light {
31+
typedef struct config {
32+
cluster::descriptor::config_t descriptor;
33+
cluster::identify::config_t identify;
34+
cluster::groups::config_t groups;
35+
cluster::scenes_management::config_t scenes_management;
36+
cluster::on_off::config_t on_off;
37+
cluster::level_control::config_t level_control;
38+
cluster::color_control::config_t color_control;
39+
} config_t;
40+
41+
uint32_t get_device_type_id() {
42+
return ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID;
43+
}
44+
45+
uint8_t get_device_type_version() {
46+
return ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_VERSION;
47+
}
48+
49+
esp_err_t add(endpoint_t *endpoint, config_t *config) {
50+
if (!endpoint) {
51+
log_e("Endpoint cannot be NULL");
52+
return ESP_ERR_INVALID_ARG;
53+
}
54+
esp_err_t err = add_device_type(endpoint, get_device_type_id(), get_device_type_version());
55+
if (err != ESP_OK) {
56+
log_e("Failed to add device type id:%" PRIu32 ",err: %d", get_device_type_id(), err);
57+
return err;
58+
}
59+
60+
descriptor::create(endpoint, &(config->descriptor), CLUSTER_FLAG_SERVER);
61+
cluster_t *identify_cluster = identify::create(endpoint, &(config->identify), CLUSTER_FLAG_SERVER);
62+
identify::command::create_trigger_effect(identify_cluster);
63+
groups::create(endpoint, &(config->groups), CLUSTER_FLAG_SERVER);
64+
cluster_t *scenes_cluster = scenes_management::create(endpoint, &(config->scenes_management), CLUSTER_FLAG_SERVER);
65+
scenes_management::command::create_copy_scene(scenes_cluster);
66+
scenes_management::command::create_copy_scene_response(scenes_cluster);
67+
68+
on_off::create(endpoint, &(config->on_off), CLUSTER_FLAG_SERVER, on_off::feature::lighting::get_id());
69+
level_control::create(
70+
endpoint, &(config->level_control), CLUSTER_FLAG_SERVER, level_control::feature::on_off::get_id() | level_control::feature::lighting::get_id()
71+
);
72+
color_control::create(
73+
endpoint, &(config->color_control), CLUSTER_FLAG_SERVER,
74+
color_control::feature::hue_saturation::get_id() | color_control::feature::color_temperature::get_id()
75+
);
76+
return ESP_OK;
77+
}
78+
79+
endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data) {
80+
endpoint_t *endpoint = endpoint::create(node, flags, priv_data);
81+
add(endpoint, config);
82+
return endpoint;
83+
}
84+
} // namespace enhanced_color_light
85+
} // namespace endpoint
86+
} // namespace esp_matter
87+
88+
bool MatterEnhancedColorLight::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
89+
bool ret = true;
90+
if (!started) {
91+
log_e("Matter Enhanced ColorLight device has not begun.");
92+
return false;
93+
}
94+
95+
log_d(
96+
"Enhanced ColorAttr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u, type: %u", endpoint_id, cluster_id, attribute_id, val->val.u32,
97+
val->type
98+
);
99+
100+
if (endpoint_id == getEndPointId()) {
101+
switch (cluster_id) {
102+
case OnOff::Id:
103+
if (attribute_id == OnOff::Attributes::OnOff::Id) {
104+
log_d("Enhanced ColorLight On/Off State changed to %d", val->val.b);
105+
if (_onChangeOnOffCB != NULL) {
106+
ret &= _onChangeOnOffCB(val->val.b);
107+
}
108+
if (_onChangeCB != NULL) {
109+
ret &= _onChangeCB(val->val.b, colorHSV, brightnessLevel, colorTemperatureLevel);
110+
}
111+
if (ret == true) {
112+
onOffState = val->val.b;
113+
}
114+
}
115+
break;
116+
case LevelControl::Id:
117+
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
118+
log_d("Enhanced ColorLight Brightness changed to %d", val->val.u8);
119+
if (_onChangeBrightnessCB != NULL) {
120+
ret &= _onChangeBrightnessCB(val->val.u8);
121+
}
122+
if (_onChangeCB != NULL) {
123+
ret &= _onChangeCB(onOffState, colorHSV, val->val.u8, colorTemperatureLevel);
124+
}
125+
if (ret == true) {
126+
colorHSV.v = val->val.u8;
127+
}
128+
}
129+
break;
130+
case ColorControl::Id:
131+
{
132+
if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) {
133+
log_d("Enhanced ColorLight Temperature changed to %d", val->val.u16);
134+
if (_onChangeTemperatureCB != NULL) {
135+
ret &= _onChangeTemperatureCB(val->val.u16);
136+
}
137+
if (_onChangeCB != NULL) {
138+
ret &= _onChangeCB(onOffState, colorHSV, brightnessLevel, val->val.u16);
139+
}
140+
if (ret == true) {
141+
colorTemperatureLevel = val->val.u16;
142+
}
143+
break;
144+
}
145+
if (attribute_id != ColorControl::Attributes::CurrentHue::Id && attribute_id != ColorControl::Attributes::CurrentSaturation::Id) {
146+
log_i("Color Control Attribute ID [%x] not processed.", attribute_id);
147+
break;
148+
}
149+
espHsvColor_t hsvColor = {colorHSV.h, colorHSV.s, colorHSV.v};
150+
if (attribute_id == ColorControl::Attributes::CurrentHue::Id) {
151+
log_d("Enhanced ColorLight Hue changed to %d", val->val.u8);
152+
hsvColor.h = val->val.u8;
153+
} else { // attribute_id == ColorControl::Attributes::CurrentSaturation::Id)
154+
log_d("Enhanced ColorLight Saturation changed to %d", val->val.u8);
155+
hsvColor.s = val->val.u8;
156+
}
157+
if (_onChangeColorCB != NULL) {
158+
ret &= _onChangeColorCB(hsvColor);
159+
}
160+
if (_onChangeCB != NULL) {
161+
ret &= _onChangeCB(onOffState, hsvColor, brightnessLevel, colorTemperatureLevel);
162+
}
163+
if (ret == true) {
164+
colorHSV = {hsvColor.h, hsvColor.s, hsvColor.v};
165+
}
166+
break;
167+
}
168+
}
169+
}
170+
return ret;
171+
}
172+
173+
MatterEnhancedColorLight::MatterEnhancedColorLight() {}
174+
175+
MatterEnhancedColorLight::~MatterEnhancedColorLight() {
176+
end();
177+
}
178+
179+
bool MatterEnhancedColorLight::begin(bool initialState, espHsvColor_t _colorHSV, uint8_t brightness, uint16_t ColorTemperature) {
180+
ArduinoMatter::_init();
181+
enhanced_color_light::config_t light_config;
182+
183+
light_config.on_off.on_off = initialState;
184+
light_config.on_off.lighting.start_up_on_off = nullptr;
185+
onOffState = initialState;
186+
187+
light_config.level_control.current_level = brightness;
188+
light_config.level_control.lighting.start_up_current_level = nullptr;
189+
190+
light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
191+
light_config.color_control.color_temperature.color_temperature_mireds = ColorTemperature;
192+
light_config.color_control.color_temperature.startup_color_temperature_mireds = nullptr;
193+
colorTemperatureLevel = ColorTemperature;
194+
195+
light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kCurrentHueAndCurrentSaturation;
196+
light_config.color_control.hue_saturation.current_hue = _colorHSV.h;
197+
light_config.color_control.hue_saturation.current_saturation = _colorHSV.s;
198+
colorHSV = {_colorHSV.h, _colorHSV.s, _colorHSV.v};
199+
200+
// endpoint handles can be used to add/modify clusters.
201+
endpoint_t *endpoint = enhanced_color_light::create(node::get(), &light_config, ENDPOINT_FLAG_NONE, (void *)this);
202+
if (endpoint == nullptr) {
203+
log_e("Failed to create Enhanced ColorLight endpoint");
204+
return false;
205+
}
206+
207+
setEndPointId(endpoint::get_id(endpoint));
208+
log_i("Enhanced ColorLight created with endpoint_id %d", getEndPointId());
209+
210+
/* Mark deferred persistence for some attributes that might be changed rapidly */
211+
cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id);
212+
attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id);
213+
attribute::set_deferred_persistence(current_level_attribute);
214+
215+
started = true;
216+
return true;
217+
}
218+
219+
void MatterEnhancedColorLight::end() {
220+
started = false;
221+
}
222+
223+
bool MatterEnhancedColorLight::setOnOff(bool newState) {
224+
if (!started) {
225+
log_e("Matter Enhanced ColorLight device has not begun.");
226+
return false;
227+
}
228+
229+
// avoid processing the a "no-change"
230+
if (onOffState == newState) {
231+
return true;
232+
}
233+
234+
onOffState = newState;
235+
236+
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
237+
cluster_t *cluster = cluster::get(endpoint, OnOff::Id);
238+
attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id);
239+
240+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
241+
attribute::get_val(attribute, &val);
242+
243+
if (val.val.b != onOffState) {
244+
val.val.b = onOffState;
245+
attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val);
246+
}
247+
return true;
248+
}
249+
250+
void MatterEnhancedColorLight::updateAccessory() {
251+
if (_onChangeCB != NULL) {
252+
_onChangeCB(onOffState, colorHSV, brightnessLevel, colorTemperatureLevel);
253+
}
254+
}
255+
256+
bool MatterEnhancedColorLight::getOnOff() {
257+
return onOffState;
258+
}
259+
260+
bool MatterEnhancedColorLight::toggle() {
261+
return setOnOff(!onOffState);
262+
}
263+
264+
bool MatterEnhancedColorLight::setBrightness(uint8_t newBrightness) {
265+
if (!started) {
266+
log_w("Matter Enhanced ColorLight device has not begun.");
267+
return false;
268+
}
269+
270+
// avoid processing the a "no-change"
271+
if (brightnessLevel == newBrightness) {
272+
return true;
273+
}
274+
275+
brightnessLevel = newBrightness;
276+
277+
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
278+
cluster_t *cluster = cluster::get(endpoint, LevelControl::Id);
279+
attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id);
280+
281+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
282+
attribute::get_val(attribute, &val);
283+
284+
if (val.val.u8 != brightnessLevel) {
285+
val.val.u8 = brightnessLevel;
286+
attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val);
287+
}
288+
return true;
289+
}
290+
291+
uint8_t MatterEnhancedColorLight::getBrightness() {
292+
return brightnessLevel;
293+
}
294+
295+
bool MatterEnhancedColorLight::setColorTemperature(uint16_t newTemperature) {
296+
if (!started) {
297+
log_w("Matter Enhanced ColorLight device has not begun.");
298+
return false;
299+
}
300+
301+
// avoid processing the a "no-change"
302+
if (colorTemperatureLevel == newTemperature) {
303+
return true;
304+
}
305+
306+
colorTemperatureLevel = newTemperature;
307+
308+
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
309+
cluster_t *cluster = cluster::get(endpoint, ColorControl::Id);
310+
attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);
311+
312+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
313+
attribute::get_val(attribute, &val);
314+
315+
if (val.val.u16 != colorTemperatureLevel) {
316+
val.val.u16 = colorTemperatureLevel;
317+
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id, &val);
318+
}
319+
return true;
320+
}
321+
322+
uint16_t MatterEnhancedColorLight::getColorTemperature() {
323+
return colorTemperatureLevel;
324+
}
325+
326+
bool MatterEnhancedColorLight::setColorRGB(espRgbColor_t _rgbColor) {
327+
return setColorHSV(espRgbColorToHsvColor(_rgbColor));
328+
}
329+
330+
espRgbColor_t MatterEnhancedColorLight::getColorRGB() {
331+
return espHsvColorToRgbColor(colorHSV);
332+
}
333+
334+
bool MatterEnhancedColorLight::setColorHSV(espHsvColor_t _hsvColor) {
335+
336+
if (!started) {
337+
log_w("Matter Enhanced ColorLight device has not begun.");
338+
return false;
339+
}
340+
341+
// avoid processing the a "no-change"
342+
if (colorHSV.h == _hsvColor.h && colorHSV.s == _hsvColor.s && colorHSV.v == _hsvColor.v) {
343+
return true;
344+
}
345+
346+
colorHSV = {_hsvColor.h, _hsvColor.s, _hsvColor.v};
347+
348+
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
349+
cluster_t *cluster = cluster::get(endpoint, ColorControl::Id);
350+
// update hue
351+
attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id);
352+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
353+
attribute::get_val(attribute, &val);
354+
if (val.val.u8 != colorHSV.h) {
355+
val.val.u8 = colorHSV.h;
356+
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::CurrentHue::Id, &val);
357+
}
358+
// update saturation
359+
attribute = attribute::get(cluster, ColorControl::Attributes::CurrentSaturation::Id);
360+
val = esp_matter_invalid(NULL);
361+
attribute::get_val(attribute, &val);
362+
if (val.val.u8 != colorHSV.s) {
363+
val.val.u8 = colorHSV.s;
364+
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::CurrentSaturation::Id, &val);
365+
}
366+
// update value (brightness)
367+
cluster = cluster::get(endpoint, LevelControl::Id);
368+
attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id);
369+
val = esp_matter_invalid(NULL);
370+
attribute::get_val(attribute, &val);
371+
if (val.val.u8 != colorHSV.v) {
372+
val.val.u8 = colorHSV.v;
373+
attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val);
374+
}
375+
return true;
376+
}
377+
378+
espHsvColor_t MatterEnhancedColorLight::getColorHSV() {
379+
return colorHSV;
380+
}
381+
382+
MatterEnhancedColorLight::operator bool() {
383+
return getOnOff();
384+
}
385+
386+
void MatterEnhancedColorLight::operator=(bool newState) {
387+
setOnOff(newState);
388+
}
389+
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
#include <sdkconfig.h>
17+
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
18+
19+
#include <Matter.h>
20+
#include <MatterEndPoint.h>
21+
22+
class MatterEnhancedColorLight : public MatterEndPoint {
23+
public:
24+
static const uint8_t MAX_BRIGHTNESS = 255;
25+
static const uint16_t MAX_COLOR_TEMPERATURE = 500;
26+
static const uint16_t MIN_COLOR_TEMPERATURE = 100;
27+
28+
MatterEnhancedColorLight();
29+
~MatterEnhancedColorLight();
30+
// default initial state is off, brightness = 25 (10%), HSV(21, 216, 25), color temperature is 454 (Warm White)
31+
virtual bool begin(bool initialState = false, espHsvColor_t colorHSV = {21, 216, 25}, uint8_t newBrightness = 25, uint16_t colorTemperature = 454);
32+
// this will just stop processing Light Matter events
33+
void end();
34+
35+
bool setOnOff(bool newState); // returns true if successful
36+
bool getOnOff(); // returns current light state
37+
bool toggle(); // returns true if successful
38+
39+
bool setColorTemperature(uint16_t newTemperature); // returns true if successful
40+
uint16_t getColorTemperature(); // returns current temperature
41+
42+
bool setBrightness(uint8_t newBrightness); // returns true if successful
43+
uint8_t getBrightness(); // returns current brightness
44+
45+
bool setColorRGB(espRgbColor_t rgbColor); // returns true if successful
46+
espRgbColor_t getColorRGB(); // returns current RGB Color
47+
bool setColorHSV(espHsvColor_t hsvColor); // returns true if successful
48+
espHsvColor_t getColorHSV(); // returns current HSV Color
49+
50+
// used to update the state of the light using the current Matter Light internal state
51+
// It is necessary to set a user callback function using onChange() to handle the physical light state
52+
void updateAccessory();
53+
54+
operator bool(); // returns current on/off light state
55+
void operator=(bool state); // turns light on or off
56+
57+
// this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
58+
bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val);
59+
60+
// User Callback for whenever the Light On/Off state is changed by the Matter Controller
61+
using EndPointOnOffCB = std::function<bool(bool)>;
62+
void onChangeOnOff(EndPointOnOffCB onChangeCB) {
63+
_onChangeOnOffCB = onChangeCB;
64+
}
65+
66+
// User Callback for whenever the Light brightness value [0..255] is changed by the Matter Controller
67+
using EndPointBrightnessCB = std::function<bool(uint8_t)>;
68+
void onChangeBrightness(EndPointBrightnessCB onChangeCB) {
69+
_onChangeBrightnessCB = onChangeCB;
70+
}
71+
72+
// User Callback for whenever the HSV Color value is changed by the Matter Controller
73+
using EndPointRGBColorCB = std::function<bool(espHsvColor_t)>;
74+
void onChangeColorHSV(EndPointRGBColorCB onChangeCB) {
75+
_onChangeColorCB = onChangeCB;
76+
}
77+
78+
// User Callbqck for whenever the Light temperature value is changed by the Matter Controller
79+
using EndPointTemperatureCB = std::function<bool(uint16_t)>;
80+
void onChangeColorTemperature(EndPointTemperatureCB onChangeCB) {
81+
_onChangeTemperatureCB = onChangeCB;
82+
}
83+
84+
// User Callback for whenever any parameter is changed by the Matter Controller
85+
using EndPointCB = std::function<bool(bool, espHsvColor_t, uint8_t, uint16_t)>;
86+
void onChange(EndPointCB onChangeCB) {
87+
_onChangeCB = onChangeCB;
88+
}
89+
90+
protected:
91+
bool started = false;
92+
bool onOffState = false; // default initial state is off, but it can be changed by begin(bool)
93+
uint8_t brightnessLevel = 0; // default initial brightness is 0, but it can be changed by begin(bool, uint8_t)
94+
espHsvColor_t colorHSV = {0}; // default initial color HSV is black, but it can be changed by begin(bool, uint8_t, espHsvColor_t)
95+
uint16_t colorTemperatureLevel = 0; // default initial color temperature is 0, but it can be changed by begin(bool, uint8_t, espHsvColor_t, uint16_t)
96+
EndPointOnOffCB _onChangeOnOffCB = NULL;
97+
EndPointBrightnessCB _onChangeBrightnessCB = NULL;
98+
EndPointRGBColorCB _onChangeColorCB = NULL;
99+
EndPointTemperatureCB _onChangeTemperatureCB = NULL;
100+
EndPointCB _onChangeCB = NULL;
101+
};
102+
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

0 commit comments

Comments
 (0)
Please sign in to comment.