From 78ffc3085db7a5333a35856e9d8ec88f0480d2b3 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 17 Apr 2019 21:21:56 +0200 Subject: [PATCH 1/6] Support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809) --- sonoff/_changelog.ino | 1 + sonoff/xdrv_04_light.ino | 166 ++++++++++++++++++++++++++------------- sonoff/xplg_wemohue.ino | 36 ++++++--- 3 files changed, 136 insertions(+), 67 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 3e1ddb71d..fed09a02c 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -5,6 +5,7 @@ * Add all temperature, humidity and pressure for global access * 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) + * Support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809) * * 6.5.0.7 20190410 * Add command LedMask to assign which relay has access to power LED (#5602, #5612) diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 7597415d6..f3f739b1b 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -599,6 +599,11 @@ void LightSetColorTemp(uint16_t ct) * ct = 153 = 2000K = Warm = CCWW = 00FF * 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; if (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) { + // 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; if (LST_RGBWC == light_subtype) { ct_idx = 3; } uint16_t my_ct = Settings.light_color[ct_idx +1]; if (my_ct > 0) { - return ((my_ct * 136) / 100) + 154; + ct = ((my_ct * 136) / 100) + 154; } else { 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) @@ -655,12 +678,12 @@ void LightSetDimmer(uint8_t myDimmer) if (LT_PWM1 == light_type) { 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++) { if (Settings.flag.light_signal) { - temp = (float)light_signal_color[i] / dimmer; + temp = (float)light_signal_color[i] / dimmer + 0.5f; } else { - temp = (float)Settings.light_color[i] / dimmer; + temp = (float)Settings.light_color[i] / dimmer + 0.5f; } light_current_color[i] = (uint8_t)temp; } @@ -675,11 +698,11 @@ void LightSetColor(void) highest = light_current_color[i]; } } - float mDim = (float)highest / 2.55; - Settings.light_dimmer = (uint8_t)mDim; + float mDim = (float)highest / 2.55f; + Settings.light_dimmer = (uint8_t)(mDim + 0.5f); float dimmer = 100 / mDim; 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; } } @@ -845,7 +868,7 @@ void LightWheel(uint8_t wheel_pos) light_entry_color[4] = 0; float dimmer = 100 / (float)Settings.light_dimmer; 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; } } @@ -872,6 +895,7 @@ void LightRandomColor(void) light_wheel = random(255); LightWheel(light_wheel); memcpy(light_current_color, light_entry_color, sizeof(light_current_color)); + LightSetColor(); } LightFade(); } @@ -1048,103 +1072,123 @@ void LightAnimate(void) * Hue support \*********************************************************************************************/ -float light_hue = 0.0; -float light_saturation = 0.0; -float light_brightness = 0.0; +float light_hue = 0.0f; +float light_saturation = 0.0f; +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) - float r = light_current_color[0] / 255.0f; - float g = light_current_color[1] / 255.0f; - float b = light_current_color[2] / 255.0f; + float r, g, b; + if (from_settings) { + 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 min = (r < g && r < b) ? r : (g < b) ? g : b; float d = max - min; - light_hue = 0.0; - light_brightness = max; - light_saturation = (0.0f == light_brightness) ? 0 : (d / light_brightness); + float hue = 0.0f; + float 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 (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) { - light_hue = (b - r) / d + 2.0f; + hue = (b - r) / d + 2.0f; } 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 g; - float b; + float r = 1.0f; // default to white + float g = 1.0f; + float b = 1.0f; float h = light_hue; float s = light_saturation; - float v = light_brightness; + // brightness is set to 100%, and controlled via Dimmer - if (0.0f == light_saturation) { - r = g = b = v; // Achromatic or black - } else { + if (0.0f < light_saturation) { if (h < 0.0f) { h += 1.0f; - } - else if (h >= 1.0f) { + } else if (h >= 1.0f) { h -= 1.0f; } h *= 6.0f; int i = (int)h; float f = h - i; - float q = v * (1.0f - s * f); - float p = v * (1.0f - s); - float t = v * (1.0f - s * (1.0f - f)); + float q = 1.0f - s * f; + float p = 1.0f - s; + float t = 1.0f - s * (1.0f - f); switch (i) { case 0: - r = v; + //r = 1.0f; g = t; b = p; break; case 1: r = q; - g = v; + //g = 1.0f; b = p; break; case 2: r = p; - g = v; + //g = 1.0f; b = t; break; case 3: r = p; g = q; - b = v; + //b = 1.0f; break; case 4: r = t; g = p; - b = v; + //b = 1.0f; break; default: - r = v; + //r = 1.0f; g = p; b = q; break; } } - light_current_color[0] = (uint8_t)(r * 255.0f); - light_current_color[1] = (uint8_t)(g * 255.0f); - light_current_color[2] = (uint8_t)(b * 255.0f); + light_current_color[0] = (uint8_t)(r * 255.0f + 0.5f); + light_current_color[1] = (uint8_t)(g * 255.0f + 0.5f); + light_current_color[2] = (uint8_t)(b * 255.0f + 0.5f); if(light_ct_rgb_linked){ light_current_color[3] = 0; light_current_color[4] = 0; @@ -1156,14 +1200,17 @@ void LightHsbToRgb(void) void LightGetHsb(float *hue, float *sat, float *bri, bool gotct) { if (light_subtype > LST_COLDWARM && !gotct) { - LightRgbToHsb(); + LightRgbToHsb(true); *hue = light_hue; *sat = light_saturation; *bri = light_brightness; } else { - *hue = 0; - *sat = 0; - *bri = (0.01f * (float)Settings.light_dimmer); + *hue = light_hue = 0.0f; + *sat = light_saturation = 0.0f; + 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 ((LST_RGBWC == light_subtype) && (gotct)) { - uint8_t tmp = (uint8_t)(bri * 100); - Settings.light_dimmer = tmp; + light_brightness = bri; + Settings.light_dimmer = (uint8_t)(bri * 100.0f +0.5f); if (ct > 0) { + light_hue = 0.0f; + light_saturation = 0.0f; + _light_ct = ct; LightSetColorTemp(ct); } } else { + _light_ct = 0; light_hue = hue; light_saturation = sat; light_brightness = bri; - LightHsbToRgb(); + LightHsToRgb(); LightSetColor(); + Settings.light_dimmer = (uint8_t)(bri * 100.0f + 0.5f); + LightSetDimmer(Settings.light_dimmer); } LightPreparePower(); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR)); } else { - uint8_t tmp = (uint8_t)(bri * 100); - Settings.light_dimmer = tmp; + light_brightness = bri; + Settings.light_dimmer = (uint8_t)(bri * 100.0f +0.5f); if (LST_COLDWARM == light_subtype) { if (ct > 0) { + light_hue = 0.0f; + light_saturation = 0.0f; + _light_ct = ct; LightSetColorTemp(ct); } LightPreparePower(); diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 0dfcc2f5d..99e570e8f 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -466,13 +466,9 @@ const char HUE_DESCRIPTION_XML[] PROGMEM = "" "\r\n" "\r\n"; -const char HUE_LIGHTS_STATUS_JSON[] PROGMEM = +const char HUE_LIGHTS_STATUS_JSON1[] PROGMEM = "{\"on\":{state}," - "\"bri\":{b}," - "\"hue\":{h}," - "\"sat\":{s}," - "\"xy\":[0.5, 0.5]," - "\"ct\":{t}," + "{light_status}" "\"alert\":\"none\"," "\"effect\":\"none\"," "\"colormode\":\"{m}\"," @@ -575,18 +571,32 @@ void HueLightStatus1(uint8_t device, String *response) float hue = 0; float sat = 0; 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) { LightGetHsb(&hue, &sat, &bri, g_gotct); 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("{h}", String((uint16_t)(65535.0f * hue))); - response->replace("{s}", String((uint8_t)(254.0f * sat))); - response->replace("{b}", String((uint8_t)(254.0f * bri))); - response->replace("{t}", String(ct)); + + if (LST_SINGLE <= light_subtype) { + light_status += "\"bri\":" + String((uint8_t)(254.0f * bri + 0.5f)) + ","; + } + 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"); } @@ -760,6 +770,7 @@ void HueLights(String *path) change = false; } response += "]"; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); if (2 == response.length()) { response = FPSTR(HUE_ERROR_JSON); } @@ -777,6 +788,7 @@ void HueLights(String *path) response += F("{\"state\":"); HueLightStatus1(device, &response); HueLightStatus2(device, &response); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " LightResult (%s)"), response.c_str()); } else { response = "{}"; From d2f1657e68a697e2dd6c467a83dffce9e0b95757 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 18 Apr 2019 17:08:32 +0200 Subject: [PATCH 2/6] Fixed issue with simple relay in Philips Hue emulation from Alexa app --- sonoff/xplg_wemohue.ino | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 99e570e8f..d505ae982 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -585,10 +585,8 @@ void HueLightStatus1(uint8_t device, String *response) } *response += FPSTR(HUE_LIGHTS_STATUS_JSON1); response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false"); + light_status += "\"bri\":" + String((uint8_t)(254.0f * bri + 0.5f)) + ","; - if (LST_SINGLE <= light_subtype) { - light_status += "\"bri\":" + String((uint8_t)(254.0f * bri + 0.5f)) + ","; - } 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)) + ","; From 599306a81f1389e2d28f8fb47e6165cdf4e8b765 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 18 Apr 2019 20:56:18 +0200 Subject: [PATCH 3/6] Fixed wrong ct results when initializing device --- sonoff/xdrv_04_light.ino | 2 +- sonoff/xplg_wemohue.ino | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index f3f739b1b..256f001e0 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -657,7 +657,7 @@ uint16_t LightGetColorTemp(void) // 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) + if ((ct > _light_ct ? ct - _light_ct : _light_ct - ct) > 5) _light_ct = ct; return _light_ct; } diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index d505ae982..45dc8c750 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -592,7 +592,8 @@ void HueLightStatus1(uint8_t device, String *response) 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) + ","; + // ct = 0 is non valid, so we put 284 as default value (medium white) + light_status += "\"ct\":" + String( (ct < 100) ? 284 : ct) + ","; } response->replace("{light_status}", light_status); response->replace("{m}", g_gotct?"ct":"hs"); From 5732efa27c20e01801fff4c07ea30d68ecb6eee3 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 18 Apr 2019 23:11:59 +0200 Subject: [PATCH 4/6] Adding 'xy' color response for Philips Hue emulation --- sonoff/xplg_wemohue.ino | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 45dc8c750..77928dfde 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -468,6 +468,7 @@ const char HUE_DESCRIPTION_XML[] PROGMEM = "\r\n"; const char HUE_LIGHTS_STATUS_JSON1[] PROGMEM = "{\"on\":{state}," + "\"xy\":[{x}, {y}]," "{light_status}" "\"alert\":\"none\"," "\"effect\":\"none\"," @@ -566,12 +567,42 @@ void HueConfig(String *path) bool g_gotct = false; +#define FORCE_RANGE(x, min, max) x < min ? min : (x > max ? max : x) + +void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) +{ + float x = 0.31271f; + float y = 0.32902f; + + if (i_r + i_b + i_g > 0) { + float r = (float)i_r / 255.0f; + float g = (float)i_g / 255.0f; + float b = (float)i_b / 255.0f; + // https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d + r = (r > 0.04045f) ? pow((r + 0.055f) / (1.0f + 0.055f), 2.4f) : (r / 12.92f); + g = (g > 0.04045f) ? pow((g + 0.055f) / (1.0f + 0.055f), 2.4f) : (g / 12.92f); + b = (b > 0.04045f) ? pow((b + 0.055f) / (1.0f + 0.055f), 2.4f) : (b / 12.92f); + + float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; + float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; + float Z = r * 0.000000f + g * 0.053077f + b * 1.035763f; + + x = X / (X + Y + Z); + y = Y / (X + Y + Z); + } + if (r_x) *r_x = x; + if (r_y) *r_y = y; + //*bri = Y; +} + void HueLightStatus1(uint8_t device, String *response) { float hue = 0; float sat = 0; float bri = 254; uint16_t ct = 0; + // default xy color to white D65, https://en.wikipedia.org/wiki/Illuminant_D65 + float x, y; String light_status = ""; // force ct mode for LST_COLDWARM @@ -595,6 +626,9 @@ void HueLightStatus1(uint8_t device, String *response) // ct = 0 is non valid, so we put 284 as default value (medium white) light_status += "\"ct\":" + String( (ct < 100) ? 284 : ct) + ","; } + RgbToXy(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], &x, &y); + response->replace("{x}", String(x)); + response->replace("{y}", String(y)); response->replace("{light_status}", light_status); response->replace("{m}", g_gotct?"ct":"hs"); } From c8f82399f3a54530cd5c45626c30fbb19b6cf28c Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Fri, 19 Apr 2019 14:26:40 +0200 Subject: [PATCH 5/6] Fixed responding 'xy' colot space only for devices supporting colors. --- sonoff/xplg_wemohue.ino | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 77928dfde..75e81a6a8 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -468,7 +468,6 @@ const char HUE_DESCRIPTION_XML[] PROGMEM = "\r\n"; const char HUE_LIGHTS_STATUS_JSON1[] PROGMEM = "{\"on\":{state}," - "\"xy\":[{x}, {y}]," "{light_status}" "\"alert\":\"none\"," "\"effect\":\"none\"," @@ -602,7 +601,6 @@ void HueLightStatus1(uint8_t device, String *response) float bri = 254; uint16_t ct = 0; // default xy color to white D65, https://en.wikipedia.org/wiki/Illuminant_D65 - float x, y; String light_status = ""; // force ct mode for LST_COLDWARM @@ -619,6 +617,9 @@ void HueLightStatus1(uint8_t device, String *response) light_status += "\"bri\":" + String((uint8_t)(254.0f * bri + 0.5f)) + ","; if (LST_RGB <= light_subtype) { // colors + float x, y; + RgbToXy(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], &x, &y); + light_status += "\"xy\":[" + String(x) + ", " + String(y) + "],"; light_status += "\"hue\":" + String((uint16_t)(65535.0f * hue + 0.5f)) + ","; light_status += "\"sat\":" + String((uint8_t)(254.0f * sat + 0.5f)) + ","; } @@ -626,9 +627,6 @@ void HueLightStatus1(uint8_t device, String *response) // ct = 0 is non valid, so we put 284 as default value (medium white) light_status += "\"ct\":" + String( (ct < 100) ? 284 : ct) + ","; } - RgbToXy(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], &x, &y); - response->replace("{x}", String(x)); - response->replace("{y}", String(y)); response->replace("{light_status}", light_status); response->replace("{m}", g_gotct?"ct":"hs"); } From b5860b9b94d3f6aa312144f740fa1db732188077 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Fri, 19 Apr 2019 21:39:43 +0200 Subject: [PATCH 6/6] Fixed wrong behavior of Alexa app with Philips Hue emulation --- sonoff/xdrv_04_light.ino | 22 +++------------ sonoff/xplg_wemohue.ino | 59 +++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 256f001e0..657c6f00c 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -629,8 +629,6 @@ void LightSetColorTemp(uint16_t ct) } } -uint16_t _light_ct; // memorise the last ct - uint16_t LightGetColorTemp(void) { // don't calculate CT for unsupported devices @@ -655,11 +653,7 @@ uint16_t LightGetColorTemp(void) } } - // 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; + return ct; } void LightSetDimmer(uint8_t myDimmer) @@ -1120,14 +1114,9 @@ void LightRgbToHsb(bool from_settings = false) } 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; + light_hue = hue; + light_saturation = saturation; + light_brightness = brightness; } void LightHsToRgb(void) @@ -1223,11 +1212,9 @@ void LightSetHsb(float hue, float sat, float bri, uint16_t ct, bool gotct) if (ct > 0) { light_hue = 0.0f; light_saturation = 0.0f; - _light_ct = ct; LightSetColorTemp(ct); } } else { - _light_ct = 0; light_hue = hue; light_saturation = sat; light_brightness = bri; @@ -1245,7 +1232,6 @@ void LightSetHsb(float hue, float sat, float bri, uint16_t ct, bool gotct) if (ct > 0) { light_hue = 0.0f; light_saturation = 0.0f; - _light_ct = ct; LightSetColorTemp(ct); } LightPreparePower(); diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 75e81a6a8..6ed6d6bf8 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -471,7 +471,6 @@ const char HUE_LIGHTS_STATUS_JSON1[] PROGMEM = "{light_status}" "\"alert\":\"none\"," "\"effect\":\"none\"," - "\"colormode\":\"{m}\"," "\"reachable\":true}"; const char HUE_LIGHTS_STATUS_JSON2[] PROGMEM = ",\"type\":\"Extended color light\"," @@ -594,14 +593,20 @@ void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) //*bri = Y; } +// store previously set values from the Alexa app +uint16_t prev_hue = 0; +uint8_t prev_sat = 0; +uint8_t prev_bri = 254; +uint16_t prev_ct = 254; + void HueLightStatus1(uint8_t device, String *response) { - float hue = 0; - float sat = 0; - float bri = 254; uint16_t ct = 0; // default xy color to white D65, https://en.wikipedia.org/wiki/Illuminant_D65 String light_status = ""; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; // force ct mode for LST_COLDWARM if (LST_COLDWARM == light_subtype) { @@ -609,26 +614,56 @@ void HueLightStatus1(uint8_t device, String *response) } if (light_type) { - LightGetHsb(&hue, &sat, &bri, g_gotct); + float hhue, hsat, hbri; + LightGetHsb(&hhue, &hsat, &hbri, g_gotct); + + bri = 254.0f * hbri + 0.5f; + if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254 + if (bri < 1) bri = 1; + if ((bri > prev_bri ? bri - prev_bri : prev_bri - bri) < 2) + bri = prev_bri; + + sat = 254.0f * hsat + 0.5f; // 0..254 + if (sat > 254) sat = 254; // Philips Hue only accepts 254 as max hue + if ((sat > prev_sat ? sat - prev_sat : prev_sat - sat) < 2) + sat = prev_sat; + + hue = 65535.0f * hhue + 0.5f; // 0..65535 + if ((hue > prev_hue ? hue - prev_hue : prev_hue - hue) < 700) + hue = prev_hue; + ct = LightGetColorTemp(); + if (ct < 100) ct = 284; + if ((ct > prev_ct ? ct - prev_ct : prev_ct - ct) < 5) + ct = prev_ct; + + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("HueLightStatus1 HSB (%d, %d, %d) Prev_HSB (%d, %d, %d)"), + // hue, sat, bri, prev_hue, prev_sat, prev_bri); } + *response += FPSTR(HUE_LIGHTS_STATUS_JSON1); response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false"); - light_status += "\"bri\":" + String((uint8_t)(254.0f * bri + 0.5f)) + ","; + if (LST_SINGLE <= light_subtype) { + light_status += "\"bri\":" + String(bri) + ","; + } + if (LST_COLDWARM <= light_subtype) { + light_status += "\"colormode\":\"" + String(g_gotct ? "ct" : "hs") + "\","; + } + if (LST_RGB <= light_subtype) { // colors float x, y; + RgbToXy(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], &x, &y); light_status += "\"xy\":[" + String(x) + ", " + String(y) + "],"; - light_status += "\"hue\":" + String((uint16_t)(65535.0f * hue + 0.5f)) + ","; - light_status += "\"sat\":" + String((uint8_t)(254.0f * sat + 0.5f)) + ","; + light_status += "\"hue\":" + String(hue) + ","; + light_status += "\"sat\":" + String(sat) + ","; } if (LST_COLDWARM == light_subtype || LST_RGBWC == light_subtype) { // white temp // ct = 0 is non valid, so we put 284 as default value (medium white) - light_status += "\"ct\":" + String( (ct < 100) ? 284 : ct) + ","; + light_status += "\"ct\":" + String(ct) + ","; } response->replace("{light_status}", light_status); - response->replace("{m}", g_gotct?"ct":"hs"); } void HueLightStatus2(uint8_t device, String *response) @@ -739,6 +774,7 @@ void HueLights(String *path) if (hue_json.containsKey("bri")) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. tmp = hue_json["bri"]; + prev_bri = tmp; // store command value tmp = tmax(tmp, 1); tmp = tmin(tmp, 254); bri = (float)tmp / 254.0f; @@ -754,6 +790,7 @@ void HueLights(String *path) } if (hue_json.containsKey("hue")) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue. tmp = hue_json["hue"]; + prev_hue = tmp; // store command value hue = (float)tmp / 65535.0f; if (resp) { response += ","; @@ -768,6 +805,7 @@ void HueLights(String *path) } if (hue_json.containsKey("sat")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). tmp = hue_json["sat"]; + prev_sat = tmp; // store command value tmp = tmax(tmp, 0); tmp = tmin(tmp, 254); sat = (float)tmp / 254.0f; @@ -784,6 +822,7 @@ void HueLights(String *path) } if (hue_json.containsKey("ct")) { // Color temperature 153 (Cold) to 500 (Warm) ct = hue_json["ct"]; + prev_ct = ct; // store commande value if (resp) { response += ","; }