Support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809)

This commit is contained in:
Stephan Hadinger 2019-04-17 21:21:56 +02:00
parent 768cf56ee8
commit 78ffc3085d
3 changed files with 136 additions and 67 deletions

View File

@ -5,6 +5,7 @@
* Add all temperature, humidity and pressure for global access * Add all temperature, humidity and pressure for global access
* Add Shelly 2.5 overtemp functionality * Add Shelly 2.5 overtemp functionality
* Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592) * Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592)
* Support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809)
* *
* 6.5.0.7 20190410 * 6.5.0.7 20190410
* Add command LedMask to assign which relay has access to power LED (#5602, #5612) * Add command LedMask to assign which relay has access to power LED (#5602, #5612)

View File

@ -599,6 +599,11 @@ void LightSetColorTemp(uint16_t ct)
* ct = 153 = 2000K = Warm = CCWW = 00FF * ct = 153 = 2000K = Warm = CCWW = 00FF
* ct = 500 = 6500K = Cold = CCWW = FF00 * ct = 500 = 6500K = Cold = CCWW = FF00
*/ */
// don't set CT if not supported
if ((LST_COLDWARM != light_subtype) && (LST_RGBWC != light_subtype)) {
return;
}
uint16_t my_ct = ct - 153; uint16_t my_ct = ct - 153;
if (my_ct > 347) { if (my_ct > 347) {
my_ct = 347; my_ct = 347;
@ -624,19 +629,37 @@ void LightSetColorTemp(uint16_t ct)
} }
} }
uint16_t _light_ct; // memorise the last ct
uint16_t LightGetColorTemp(void) uint16_t LightGetColorTemp(void)
{ {
// don't calculate CT for unsupported devices
if ((LST_COLDWARM != light_subtype) && (LST_RGBWC != light_subtype)) {
return 0;
}
uint16_t ct = 0;
uint8_t ct_idx = 0; uint8_t ct_idx = 0;
if (LST_RGBWC == light_subtype) { if (LST_RGBWC == light_subtype) {
ct_idx = 3; ct_idx = 3;
} }
uint16_t my_ct = Settings.light_color[ct_idx +1]; uint16_t my_ct = Settings.light_color[ct_idx +1];
if (my_ct > 0) { if (my_ct > 0) {
return ((my_ct * 136) / 100) + 154; ct = ((my_ct * 136) / 100) + 154;
} else { } else {
my_ct = Settings.light_color[ct_idx]; my_ct = Settings.light_color[ct_idx];
return 499 - ((my_ct * 136) / 100); if (my_ct > 0) {
ct = 499 - ((my_ct * 136) / 100);
} else {
ct = 0;
}
} }
// for Alexa, send back the original CT value if close enough
// avoids rounding errors
if ((ct > _light_ct ? ct - _light_ct : _light_ct - ct) < 5)
_light_ct = ct;
return _light_ct;
} }
void LightSetDimmer(uint8_t myDimmer) void LightSetDimmer(uint8_t myDimmer)
@ -655,12 +678,12 @@ void LightSetDimmer(uint8_t myDimmer)
if (LT_PWM1 == light_type) { if (LT_PWM1 == light_type) {
Settings.light_color[0] = 255; // One PWM channel only supports Dimmer but needs max color Settings.light_color[0] = 255; // One PWM channel only supports Dimmer but needs max color
} }
float dimmer = 100 / (float)myDimmer; float dimmer = 100.0f / (float)myDimmer;
for (uint8_t i = 0; i < light_subtype; i++) { for (uint8_t i = 0; i < light_subtype; i++) {
if (Settings.flag.light_signal) { if (Settings.flag.light_signal) {
temp = (float)light_signal_color[i] / dimmer; temp = (float)light_signal_color[i] / dimmer + 0.5f;
} else { } else {
temp = (float)Settings.light_color[i] / dimmer; temp = (float)Settings.light_color[i] / dimmer + 0.5f;
} }
light_current_color[i] = (uint8_t)temp; light_current_color[i] = (uint8_t)temp;
} }
@ -675,11 +698,11 @@ void LightSetColor(void)
highest = light_current_color[i]; highest = light_current_color[i];
} }
} }
float mDim = (float)highest / 2.55; float mDim = (float)highest / 2.55f;
Settings.light_dimmer = (uint8_t)mDim; Settings.light_dimmer = (uint8_t)(mDim + 0.5f);
float dimmer = 100 / mDim; float dimmer = 100 / mDim;
for (uint8_t i = 0; i < light_subtype; i++) { for (uint8_t i = 0; i < light_subtype; i++) {
float temp = (float)light_current_color[i] * dimmer; float temp = (float)light_current_color[i] * dimmer + 0.5f;
Settings.light_color[i] = (uint8_t)temp; Settings.light_color[i] = (uint8_t)temp;
} }
} }
@ -845,7 +868,7 @@ void LightWheel(uint8_t wheel_pos)
light_entry_color[4] = 0; light_entry_color[4] = 0;
float dimmer = 100 / (float)Settings.light_dimmer; float dimmer = 100 / (float)Settings.light_dimmer;
for (uint8_t i = 0; i < LST_RGB; i++) { for (uint8_t i = 0; i < LST_RGB; i++) {
float temp = (float)light_entry_color[i] / dimmer; float temp = (float)light_entry_color[i] / dimmer + 0.5f;
light_entry_color[i] = (uint8_t)temp; light_entry_color[i] = (uint8_t)temp;
} }
} }
@ -872,6 +895,7 @@ void LightRandomColor(void)
light_wheel = random(255); light_wheel = random(255);
LightWheel(light_wheel); LightWheel(light_wheel);
memcpy(light_current_color, light_entry_color, sizeof(light_current_color)); memcpy(light_current_color, light_entry_color, sizeof(light_current_color));
LightSetColor();
} }
LightFade(); LightFade();
} }
@ -1048,103 +1072,123 @@ void LightAnimate(void)
* Hue support * Hue support
\*********************************************************************************************/ \*********************************************************************************************/
float light_hue = 0.0; float light_hue = 0.0f;
float light_saturation = 0.0; float light_saturation = 0.0f;
float light_brightness = 0.0; float light_brightness = 0.0f;
void LightRgbToHsb(void) void LightRgbToHsb(bool from_settings = false)
{ {
LightSetDimmer(Settings.light_dimmer); // We get a previse Hue and Sat from the Settings parameter with full brightness
// and apply the Dimmer value for Brightness
// 'from_settings' default to actual RGB color for retro-compatibility
// convert colors to float between (0.0 - 1.0) // convert colors to float between (0.0 - 1.0)
float r = light_current_color[0] / 255.0f; float r, g, b;
float g = light_current_color[1] / 255.0f; if (from_settings) {
float b = light_current_color[2] / 255.0f; r = Settings.light_color[0] / 255.0f;
g = Settings.light_color[1] / 255.0f;
b = Settings.light_color[2] / 255.0f;
} else {
r = light_current_color[0] / 255.0f;
g = light_current_color[1] / 255.0f;
b = light_current_color[2] / 255.0f;
}
float max = (r > g && r > b) ? r : (g > b) ? g : b; float max = (r > g && r > b) ? r : (g > b) ? g : b;
float min = (r < g && r < b) ? r : (g < b) ? g : b; float min = (r < g && r < b) ? r : (g < b) ? g : b;
float d = max - min; float d = max - min;
light_hue = 0.0; float hue = 0.0f;
light_brightness = max; float brightness;
light_saturation = (0.0f == light_brightness) ? 0 : (d / light_brightness); if (from_settings) {
brightness = max * Settings.light_dimmer / 100.0f;
} else {
brightness = max;
}
float saturation = (0.0f == max) ? 0 : 1.0f - min / max;
if (d != 0.0f) if (d != 0.0f)
{ {
if (r == max) { if (r == max) {
light_hue = (g - b) / d + (g < b ? 6.0f : 0.0f); hue = (g - b) / d + (g < b ? 6.0f : 0.0f);
} else if (g == max) { } else if (g == max) {
light_hue = (b - r) / d + 2.0f; hue = (b - r) / d + 2.0f;
} else { } else {
light_hue = (r - g) / d + 4.0f; hue = (r - g) / d + 4.0f;
} }
light_hue /= 6.0f; hue /= 6.0f;
} }
// change the value only if it's significantly different from last value
if ((light_hue - hue > 0.01f) || (hue - light_hue > 0.01f))
light_hue = hue;
if ((light_saturation - saturation > 0.01f) || (saturation - light_saturation > 0.01f))
light_saturation = saturation;
if ((light_brightness - brightness > 0.01f) || (brightness - light_brightness > 0.01f))
light_brightness = brightness;
} }
void LightHsbToRgb(void) void LightHsToRgb(void)
{ {
float r; float r = 1.0f; // default to white
float g; float g = 1.0f;
float b; float b = 1.0f;
float h = light_hue; float h = light_hue;
float s = light_saturation; float s = light_saturation;
float v = light_brightness; // brightness is set to 100%, and controlled via Dimmer
if (0.0f == light_saturation) { if (0.0f < light_saturation) {
r = g = b = v; // Achromatic or black
} else {
if (h < 0.0f) { if (h < 0.0f) {
h += 1.0f; h += 1.0f;
} } else if (h >= 1.0f) {
else if (h >= 1.0f) {
h -= 1.0f; h -= 1.0f;
} }
h *= 6.0f; h *= 6.0f;
int i = (int)h; int i = (int)h;
float f = h - i; float f = h - i;
float q = v * (1.0f - s * f); float q = 1.0f - s * f;
float p = v * (1.0f - s); float p = 1.0f - s;
float t = v * (1.0f - s * (1.0f - f)); float t = 1.0f - s * (1.0f - f);
switch (i) { switch (i) {
case 0: case 0:
r = v; //r = 1.0f;
g = t; g = t;
b = p; b = p;
break; break;
case 1: case 1:
r = q; r = q;
g = v; //g = 1.0f;
b = p; b = p;
break; break;
case 2: case 2:
r = p; r = p;
g = v; //g = 1.0f;
b = t; b = t;
break; break;
case 3: case 3:
r = p; r = p;
g = q; g = q;
b = v; //b = 1.0f;
break; break;
case 4: case 4:
r = t; r = t;
g = p; g = p;
b = v; //b = 1.0f;
break; break;
default: default:
r = v; //r = 1.0f;
g = p; g = p;
b = q; b = q;
break; break;
} }
} }
light_current_color[0] = (uint8_t)(r * 255.0f); light_current_color[0] = (uint8_t)(r * 255.0f + 0.5f);
light_current_color[1] = (uint8_t)(g * 255.0f); light_current_color[1] = (uint8_t)(g * 255.0f + 0.5f);
light_current_color[2] = (uint8_t)(b * 255.0f); light_current_color[2] = (uint8_t)(b * 255.0f + 0.5f);
if(light_ct_rgb_linked){ if(light_ct_rgb_linked){
light_current_color[3] = 0; light_current_color[3] = 0;
light_current_color[4] = 0; light_current_color[4] = 0;
@ -1156,14 +1200,17 @@ void LightHsbToRgb(void)
void LightGetHsb(float *hue, float *sat, float *bri, bool gotct) void LightGetHsb(float *hue, float *sat, float *bri, bool gotct)
{ {
if (light_subtype > LST_COLDWARM && !gotct) { if (light_subtype > LST_COLDWARM && !gotct) {
LightRgbToHsb(); LightRgbToHsb(true);
*hue = light_hue; *hue = light_hue;
*sat = light_saturation; *sat = light_saturation;
*bri = light_brightness; *bri = light_brightness;
} else { } else {
*hue = 0; *hue = light_hue = 0.0f;
*sat = 0; *sat = light_saturation = 0.0f;
*bri = (0.01f * (float)Settings.light_dimmer); float brightness = (float)Settings.light_dimmer / 100.0f;
if ((light_brightness - brightness > 0.01f) || (brightness - light_brightness > 0.01f))
light_brightness = brightness;
*bri = light_brightness;
} }
} }
@ -1171,25 +1218,34 @@ void LightSetHsb(float hue, float sat, float bri, uint16_t ct, bool gotct)
{ {
if (light_subtype > LST_COLDWARM) { if (light_subtype > LST_COLDWARM) {
if ((LST_RGBWC == light_subtype) && (gotct)) { if ((LST_RGBWC == light_subtype) && (gotct)) {
uint8_t tmp = (uint8_t)(bri * 100); light_brightness = bri;
Settings.light_dimmer = tmp; Settings.light_dimmer = (uint8_t)(bri * 100.0f +0.5f);
if (ct > 0) { if (ct > 0) {
light_hue = 0.0f;
light_saturation = 0.0f;
_light_ct = ct;
LightSetColorTemp(ct); LightSetColorTemp(ct);
} }
} else { } else {
_light_ct = 0;
light_hue = hue; light_hue = hue;
light_saturation = sat; light_saturation = sat;
light_brightness = bri; light_brightness = bri;
LightHsbToRgb(); LightHsToRgb();
LightSetColor(); LightSetColor();
Settings.light_dimmer = (uint8_t)(bri * 100.0f + 0.5f);
LightSetDimmer(Settings.light_dimmer);
} }
LightPreparePower(); LightPreparePower();
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR)); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR));
} else { } else {
uint8_t tmp = (uint8_t)(bri * 100); light_brightness = bri;
Settings.light_dimmer = tmp; Settings.light_dimmer = (uint8_t)(bri * 100.0f +0.5f);
if (LST_COLDWARM == light_subtype) { if (LST_COLDWARM == light_subtype) {
if (ct > 0) { if (ct > 0) {
light_hue = 0.0f;
light_saturation = 0.0f;
_light_ct = ct;
LightSetColorTemp(ct); LightSetColorTemp(ct);
} }
LightPreparePower(); LightPreparePower();

View File

@ -466,13 +466,9 @@ const char HUE_DESCRIPTION_XML[] PROGMEM =
"</device>" "</device>"
"</root>\r\n" "</root>\r\n"
"\r\n"; "\r\n";
const char HUE_LIGHTS_STATUS_JSON[] PROGMEM = const char HUE_LIGHTS_STATUS_JSON1[] PROGMEM =
"{\"on\":{state}," "{\"on\":{state},"
"\"bri\":{b}," "{light_status}"
"\"hue\":{h},"
"\"sat\":{s},"
"\"xy\":[0.5, 0.5],"
"\"ct\":{t},"
"\"alert\":\"none\"," "\"alert\":\"none\","
"\"effect\":\"none\"," "\"effect\":\"none\","
"\"colormode\":\"{m}\"," "\"colormode\":\"{m}\","
@ -575,18 +571,32 @@ void HueLightStatus1(uint8_t device, String *response)
float hue = 0; float hue = 0;
float sat = 0; float sat = 0;
float bri = 254; float bri = 254;
uint16_t ct = 500; uint16_t ct = 0;
String light_status = "";
// force ct mode for LST_COLDWARM
if (LST_COLDWARM == light_subtype) {
g_gotct = true;
}
if (light_type) { if (light_type) {
LightGetHsb(&hue, &sat, &bri, g_gotct); LightGetHsb(&hue, &sat, &bri, g_gotct);
ct = LightGetColorTemp(); ct = LightGetColorTemp();
} }
*response += FPSTR(HUE_LIGHTS_STATUS_JSON); *response += FPSTR(HUE_LIGHTS_STATUS_JSON1);
response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false"); response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false");
response->replace("{h}", String((uint16_t)(65535.0f * hue)));
response->replace("{s}", String((uint8_t)(254.0f * sat))); if (LST_SINGLE <= light_subtype) {
response->replace("{b}", String((uint8_t)(254.0f * bri))); light_status += "\"bri\":" + String((uint8_t)(254.0f * bri + 0.5f)) + ",";
response->replace("{t}", String(ct)); }
if (LST_RGB <= light_subtype) { // colors
light_status += "\"hue\":" + String((uint16_t)(65535.0f * hue + 0.5f)) + ",";
light_status += "\"sat\":" + String((uint8_t)(254.0f * sat + 0.5f)) + ",";
}
if (LST_COLDWARM == light_subtype || LST_RGBWC == light_subtype) { // white temp
light_status += "\"ct\":" + String(ct) + ",";
}
response->replace("{light_status}", light_status);
response->replace("{m}", g_gotct?"ct":"hs"); response->replace("{m}", g_gotct?"ct":"hs");
} }
@ -760,6 +770,7 @@ void HueLights(String *path)
change = false; change = false;
} }
response += "]"; response += "]";
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str());
if (2 == response.length()) { if (2 == response.length()) {
response = FPSTR(HUE_ERROR_JSON); response = FPSTR(HUE_ERROR_JSON);
} }
@ -777,6 +788,7 @@ void HueLights(String *path)
response += F("{\"state\":"); response += F("{\"state\":");
HueLightStatus1(device, &response); HueLightStatus1(device, &response);
HueLightStatus2(device, &response); HueLightStatus2(device, &response);
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " LightResult (%s)"), response.c_str());
} }
else { else {
response = "{}"; response = "{}";