Skip to content

Commit 243af60

Browse files
committed
feat(zigbee): Support HSV color commands
1 parent 5ba4c21 commit 243af60

File tree

4 files changed

+74
-115
lines changed

4 files changed

+74
-115
lines changed

libraries/Zigbee/src/ZigbeeEP.h

+1-15
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,12 @@
66
#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED
77

88
#include <Arduino.h>
9+
#include <ColorFormat.h>
910

1011
/* Useful defines */
1112
#define ZB_CMD_TIMEOUT 10000 // 10 seconds
1213

1314
#define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0]))
14-
#define XYZ_TO_RGB(X, Y, Z, r, g, b) \
15-
{ \
16-
r = (float)(3.240479 * (X) - 1.537150 * (Y) - 0.498535 * (Z)); \
17-
g = (float)(-0.969256 * (X) + 1.875992 * (Y) + 0.041556 * (Z)); \
18-
b = (float)(0.055648 * (X) - 0.204043 * (Y) + 1.057311 * (Z)); \
19-
if (r > 1) { \
20-
r = 1; \
21-
} \
22-
if (g > 1) { \
23-
g = 1; \
24-
} \
25-
if (b > 1) { \
26-
b = 1; \
27-
} \
28-
}
2915

3016
#define RGB_TO_XYZ(r, g, b, X, Y, Z) \
3117
{ \

libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp

+50-54
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ ZigbeeColorDimmableLight::ZigbeeColorDimmableLight(uint8_t endpoint) : ZigbeeEP(
1313
//set default values
1414
_current_state = false;
1515
_current_level = 255;
16-
_current_red = 255;
17-
_current_green = 255;
18-
_current_blue = 255;
16+
_current_color = {255, 255, 255};
1917
}
2018

2119
uint16_t ZigbeeColorDimmableLight::getCurrentColorX() {
@@ -32,37 +30,18 @@ uint16_t ZigbeeColorDimmableLight::getCurrentColorY() {
3230
->data_p);
3331
}
3432

35-
void ZigbeeColorDimmableLight::calculateRGB(uint16_t x, uint16_t y, uint8_t &red, uint8_t &green, uint8_t &blue) {
36-
float r, g, b, color_x, color_y;
37-
color_x = (float)x / 65535;
38-
color_y = (float)y / 65535;
39-
40-
float color_X = color_x / color_y;
41-
float color_Z = (1 - color_x - color_y) / color_y;
42-
43-
XYZ_TO_RGB(color_X, 1, color_Z, r, g, b);
44-
45-
red = (uint8_t)(r * (float)255);
46-
green = (uint8_t)(g * (float)255);
47-
blue = (uint8_t)(b * (float)255);
33+
uint16_t ZigbeeColorDimmableLight::getCurrentColorHue() {
34+
return (*(uint16_t *)esp_zb_zcl_get_attribute(
35+
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID
36+
)
37+
->data_p);
4838
}
4939

50-
void ZigbeeColorDimmableLight::calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y) {
51-
// Convert RGB to XYZ
52-
float r = (float)red / 255.0f;
53-
float g = (float)green / 255.0f;
54-
float b = (float)blue / 255.0f;
55-
56-
float X, Y, Z;
57-
RGB_TO_XYZ(r, g, b, X, Y, Z);
58-
59-
// Convert XYZ to xy chromaticity coordinates
60-
float color_x = X / (X + Y + Z);
61-
float color_y = Y / (X + Y + Z);
62-
63-
// Convert normalized xy to 16-bit values
64-
x = (uint16_t)(color_x * 65535.0f);
65-
y = (uint16_t)(color_y * 65535.0f);
40+
uint8_t ZigbeeColorDimmableLight::getCurrentColorSaturation() {
41+
return (*(uint16_t *)esp_zb_zcl_get_attribute(
42+
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID
43+
)
44+
->data_p);
6645
}
6746

6847
//set attribute method -> method overridden in child class
@@ -94,23 +73,25 @@ void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_me
9473
uint16_t light_color_x = (*(uint16_t *)message->attribute.data.value);
9574
uint16_t light_color_y = getCurrentColorY();
9675
//calculate RGB from XY and call setColor()
97-
uint8_t red, green, blue;
98-
calculateRGB(light_color_x, light_color_y, red, green, blue);
99-
_current_blue = blue;
100-
_current_green = green;
101-
_current_red = red;
76+
_current_color = espXYToRgbColor(255, light_color_x, light_color_y); //TODO: Check if level is correct
10277
lightChanged();
10378
return;
10479

10580
} else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) {
10681
uint16_t light_color_x = getCurrentColorX();
10782
uint16_t light_color_y = (*(uint16_t *)message->attribute.data.value);
10883
//calculate RGB from XY and call setColor()
109-
uint8_t red, green, blue;
110-
calculateRGB(light_color_x, light_color_y, red, green, blue);
111-
_current_blue = blue;
112-
_current_green = green;
113-
_current_red = red;
84+
_current_color = espXYToRgbColor(255, light_color_x, light_color_y); //TODO: Check if level is correct
85+
lightChanged();
86+
return;
87+
} else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) {
88+
uint16_t light_color_hue = (*(uint16_t *)message->attribute.data.value);
89+
_current_color = espHsvToRgbColor(light_color_hue, getCurrentColorSaturation(), 255);
90+
lightChanged();
91+
return;
92+
} else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) {
93+
uint8_t light_color_saturation = (*(uint8_t *)message->attribute.data.value);
94+
_current_color = espHsvToRgbColor(getCurrentColorHue(), light_color_saturation, 255);
11495
lightChanged();
11596
return;
11697
} else {
@@ -123,20 +104,21 @@ void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_me
123104

124105
void ZigbeeColorDimmableLight::lightChanged() {
125106
if (_on_light_change) {
126-
_on_light_change(_current_state, _current_red, _current_green, _current_blue, _current_level);
107+
_on_light_change(_current_state, _current_color.r, _current_color.g, _current_color.b, _current_level);
127108
}
128109
}
129110

130111
void ZigbeeColorDimmableLight::setLight(bool state, uint8_t level, uint8_t red, uint8_t green, uint8_t blue) {
131112
//Update all attributes
132113
_current_state = state;
133114
_current_level = level;
134-
_current_red = red;
135-
_current_green = green;
136-
_current_blue = blue;
115+
_current_color = {red, green, blue};
137116
lightChanged();
138117

139-
log_v("Updating on/off light state to %d", state);
118+
espXyColor_t xy_color = espRgbColorToXYColor(_current_color);
119+
espHsvColor_t hsv_color = espRgbColorToHsvColor(_current_color);
120+
121+
log_v("Updating light state: %d, level: %d, color: %d, %d, %d", state, level, red, green, blue);
140122
/* Update light clusters */
141123
esp_zb_lock_acquire(portMAX_DELAY);
142124
//set on/off state
@@ -147,28 +129,42 @@ void ZigbeeColorDimmableLight::setLight(bool state, uint8_t level, uint8_t red,
147129
esp_zb_zcl_set_attribute_val(
148130
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, &_current_level, false
149131
);
150-
//set color
151-
uint16_t color_x, color_y;
152-
calculateXY(red, green, blue, color_x, color_y);
132+
//set xy color
133+
esp_zb_zcl_set_attribute_val(
134+
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID, &xy_color.x, false
135+
);
153136
esp_zb_zcl_set_attribute_val(
154-
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID, &color_x, false
137+
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID, &xy_color.y, false
155138
);
139+
//set hsv color
156140
esp_zb_zcl_set_attribute_val(
157-
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID, &color_y, false
141+
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, &hsv_color.h, false
142+
);
143+
esp_zb_zcl_set_attribute_val(
144+
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID, &hsv_color.s, false
158145
);
159146
esp_zb_lock_release();
160147
}
161148

162149
void ZigbeeColorDimmableLight::setLightState(bool state) {
163-
setLight(state, _current_level, _current_red, _current_green, _current_blue);
150+
setLight(state, _current_level, _current_color.r, _current_color.g, _current_color.b);
164151
}
165152

166153
void ZigbeeColorDimmableLight::setLightLevel(uint8_t level) {
167-
setLight(_current_state, level, _current_red, _current_green, _current_blue);
154+
setLight(_current_state, level, _current_color.r, _current_color.g, _current_color.b);
168155
}
169156

170157
void ZigbeeColorDimmableLight::setLightColor(uint8_t red, uint8_t green, uint8_t blue) {
171158
setLight(_current_state, _current_level, red, green, blue);
172159
}
173160

161+
void ZigbeeColorDimmableLight::setLightColor(espRgbColor_t rgb_color) {
162+
setLight(_current_state, _current_level, rgb_color.r, rgb_color.g, rgb_color.b);
163+
}
164+
165+
void ZigbeeColorDimmableLight::setLightColor(espHsvColor_t hsv_color) {
166+
espRgbColor_t rgb_color = espHsvColorToRgbColor(hsv_color);
167+
setLight(_current_state, _current_level, rgb_color.r, rgb_color.g, rgb_color.b);
168+
}
169+
174170
#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED

libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h

+11-8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class ZigbeeColorDimmableLight : public ZigbeeEP {
2424
void setLightState(bool state);
2525
void setLightLevel(uint8_t level);
2626
void setLightColor(uint8_t red, uint8_t green, uint8_t blue);
27+
void setLightColor(espRgbColor_t rgb_color);
28+
void setLightColor(espHsvColor_t hsv_color);
2729
void setLight(bool state, uint8_t level, uint8_t red, uint8_t green, uint8_t blue);
2830

2931
bool getLightState() {
@@ -32,33 +34,34 @@ class ZigbeeColorDimmableLight : public ZigbeeEP {
3234
uint8_t getLightLevel() {
3335
return _current_level;
3436
}
37+
espRgbColor_t getLightColor() {
38+
return _current_color;
39+
}
3540
uint8_t getLightRed() {
36-
return _current_red;
41+
return _current_color.r;
3742
}
3843
uint8_t getLightGreen() {
39-
return _current_green;
44+
return _current_color.g;
4045
}
4146
uint8_t getLightBlue() {
42-
return _current_blue;
47+
return _current_color.b;
4348
}
4449

4550
private:
4651
void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override;
47-
void calculateRGB(uint16_t x, uint16_t y, uint8_t &red, uint8_t &green, uint8_t &blue);
48-
void calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y);
4952

5053
uint16_t getCurrentColorX();
5154
uint16_t getCurrentColorY();
55+
uint16_t getCurrentColorHue();
56+
uint8_t getCurrentColorSaturation();
5257

5358
void lightChanged();
5459
//callback function to be called on light change (State, R, G, B, Level)
5560
void (*_on_light_change)(bool, uint8_t, uint8_t, uint8_t, uint8_t);
5661

5762
bool _current_state;
5863
uint8_t _current_level;
59-
uint16_t _current_red;
60-
uint16_t _current_green;
61-
uint16_t _current_blue;
64+
espRgbColor_t _current_color;
6265
};
6366

6467
#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED

libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp

+12-38
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,6 @@ ZigbeeColorDimmerSwitch::ZigbeeColorDimmerSwitch(uint8_t endpoint) : ZigbeeEP(en
1616
};
1717
}
1818

19-
void ZigbeeColorDimmerSwitch::calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y) {
20-
// Convert RGB to XYZ
21-
float r = (float)red / 255.0f;
22-
float g = (float)green / 255.0f;
23-
float b = (float)blue / 255.0f;
24-
25-
float X, Y, Z;
26-
RGB_TO_XYZ(r, g, b, X, Y, Z);
27-
28-
// Convert XYZ to xy chromaticity coordinates
29-
float color_x = X / (X + Y + Z);
30-
float color_y = Y / (X + Y + Z);
31-
32-
// Convert normalized xy to 16-bit values
33-
x = (uint16_t)(color_x * 65535.0f);
34-
y = (uint16_t)(color_y * 65535.0f);
35-
}
36-
3719
void ZigbeeColorDimmerSwitch::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
3820
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
3921
log_i("Bound successfully!");
@@ -417,15 +399,13 @@ void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint8_t endpoint, esp
417399

418400
void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t blue) {
419401
if (_is_bound) {
420-
//Convert RGB to XY
421-
uint16_t color_x, color_y;
422-
calculateXY(red, green, blue, color_x, color_y);
402+
espXyColor_t xy_color = espRgbToXYColor(red, green, blue);
423403

424404
esp_zb_zcl_color_move_to_color_cmd_t cmd_req;
425405
cmd_req.zcl_basic_cmd.src_endpoint = _endpoint;
426406
cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
427-
cmd_req.color_x = color_x;
428-
cmd_req.color_y = color_y;
407+
cmd_req.color_x = xy_color.x;
408+
cmd_req.color_y = xy_color.y;
429409
cmd_req.transition_time = 0;
430410
log_v("Sending 'set light color' command");
431411
esp_zb_lock_acquire(portMAX_DELAY);
@@ -438,16 +418,14 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t
438418

439419
void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t blue, uint16_t group_addr) {
440420
if (_is_bound) {
441-
//Convert RGB to XY
442-
uint16_t color_x, color_y;
443-
calculateXY(red, green, blue, color_x, color_y);
421+
espXyColor_t xy_color = espRgbToXYColor(red, green, blue);
444422

445423
esp_zb_zcl_color_move_to_color_cmd_t cmd_req;
446424
cmd_req.zcl_basic_cmd.src_endpoint = _endpoint;
447425
cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr;
448426
cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT;
449-
cmd_req.color_x = color_x;
450-
cmd_req.color_y = color_y;
427+
cmd_req.color_x = xy_color.x;
428+
cmd_req.color_y = xy_color.y;
451429
cmd_req.transition_time = 0;
452430
log_v("Sending 'set light color' command to group address 0x%x", group_addr);
453431
esp_zb_lock_acquire(portMAX_DELAY);
@@ -460,17 +438,15 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t
460438

461439
void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t endpoint, uint16_t short_addr) {
462440
if (_is_bound) {
463-
//Convert RGB to XY
464-
uint16_t color_x, color_y;
465-
calculateXY(red, green, blue, color_x, color_y);
441+
espXyColor_t xy_color = espRgbToXYColor(red, green, blue);
466442

467443
esp_zb_zcl_color_move_to_color_cmd_t cmd_req;
468444
cmd_req.zcl_basic_cmd.src_endpoint = _endpoint;
469445
cmd_req.zcl_basic_cmd.dst_endpoint = endpoint;
470446
cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
471447
cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
472-
cmd_req.color_x = color_x;
473-
cmd_req.color_y = color_y;
448+
cmd_req.color_x = xy_color.x;
449+
cmd_req.color_y = xy_color.y;
474450
cmd_req.transition_time = 0;
475451
log_v("Sending 'set light color' command to endpoint %d, address 0x%x", endpoint, short_addr);
476452
esp_zb_lock_acquire(portMAX_DELAY);
@@ -483,17 +459,15 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t
483459

484460
void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) {
485461
if (_is_bound) {
486-
//Convert RGB to XY
487-
uint16_t color_x, color_y;
488-
calculateXY(red, green, blue, color_x, color_y);
462+
espXyColor_t xy_color = espRgbToXYColor(red, green, blue);
489463

490464
esp_zb_zcl_color_move_to_color_cmd_t cmd_req;
491465
cmd_req.zcl_basic_cmd.src_endpoint = _endpoint;
492466
cmd_req.zcl_basic_cmd.dst_endpoint = endpoint;
493467
cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
494468
memcpy(cmd_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
495-
cmd_req.color_x = color_x;
496-
cmd_req.color_y = color_y;
469+
cmd_req.color_x = xy_color.x;
470+
cmd_req.color_y = xy_color.y;
497471
cmd_req.transition_time = 0;
498472
log_v(
499473
"Sending 'set light color' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6],

0 commit comments

Comments
 (0)