Tentative for 'xy' color space control from Alexa app (Philips Hue emulation)

This commit is contained in:
Stephan Hadinger 2019-04-26 16:39:26 +02:00
parent e4173848b9
commit d1b073e01d
2 changed files with 66 additions and 19 deletions

View File

@ -438,9 +438,7 @@ class LightStateClass {
static void RgbToHsb(uint8_t r, uint8_t g, uint8_t b, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri);
static void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b);
static void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y);
#if 0
static void XyToRgb(float x, float y, float bri, float *r, float *g, float *b);
#endif
static void XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb);
};
@ -561,24 +559,39 @@ void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x,
if (r_y) *r_y = y;
}
#if 0
// We don't need XY to RGB right now, but code is ready - jst in case
void LightStateClass::XyToRgb(float x, float y, float bri, float *rr, float *rg, float *rb)
void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb)
{
x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x));
y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y));
float z = 1.0f - x - y;
float Y = bri;
float X = (Y / y) * x;
float Z = (Y / y) * z;
float r = X * 1.4628067 - Y * 0.1840623 - Z * 0.2743606;
float g = -X * 0.5217933 + Y * 1.4472381 + Z * 0.0677227;
float b = X * 0.0349342 - Y * 0.0968930 + Z * 1.2884099;
if (rr) { *rr = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * powf(r, (1.0f / 2.4f)) - 0.055f; }
if (rg) { *rg = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * powf(g, (1.0f / 2.4f)) - 0.055f; }
if (rb) { *rb = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * powf(b, (1.0f / 2.4f)) - 0.055f; }
//float Y = 1.0f;
float X = x / y;
float Z = z / y;
// float r = X * 1.4628067f - 0.1840623f - Z * 0.2743606f;
// float g = -X * 0.5217933f + 1.4472381f + Z * 0.0677227f;
// float b = X * 0.0349342f - 0.0968930f + Z * 1.2884099f;
float r = X * 3.2406f - 1.5372f - Z * 0.4986f;
float g = -X * 0.9689f + 1.8758f + Z * 0.0415f;
float b = X * 0.0557f - 0.2040f + Z * 1.0570f;
float max = (r > g && r > b) ? r : (g > b) ? g : b;
r = r / max; // normalize to max == 1.0
g = g / max;
b = b / max;
r = (r <= 0.0031308f) ? 12.92f * r : 1.055f * powf(r, (1.0f / 2.4f)) - 0.055f;
g = (g <= 0.0031308f) ? 12.92f * g : 1.055f * powf(g, (1.0f / 2.4f)) - 0.055f;
b = (b <= 0.0031308f) ? 12.92f * b : 1.055f * powf(b, (1.0f / 2.4f)) - 0.055f;
//
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, "XyToRgb XZ (%s %s) rgb (%s %s %s)",
// String(X,5).c_str(), String(Z,5).c_str(),
// String(r,5).c_str(), String(g,5).c_str(),String(b,5).c_str());
int32_t ir = r * 255.0f + 0.5f;
int32_t ig = g * 255.0f + 0.5f;
int32_t ib = b * 255.0f + 0.5f;
if (rr) { *rr = (ir > 255 ? 255: (ir < 0 ? 0 : ir)); }
if (rg) { *rg = (ig > 255 ? 255: (ig < 0 ? 0 : ig)); }
if (rb) { *rb = (ib > 255 ? 255: (ib < 0 ? 0 : ib)); }
}
#endif
class LightControllerClass {
LightStateClass *_state;

View File

@ -571,6 +571,10 @@ uint16_t prev_hue = 0;
uint8_t prev_sat = 0;
uint8_t prev_bri = 254;
uint16_t prev_ct = 254;
float prev_x = 0.31271f; // default to D65 white
float prev_y = 0.32902f; // https://en.wikipedia.org/wiki/Illuminant_D65
char prev_x_str[25] = "\0"; // store previously set xy by Alexa app
char prev_y_str[25] = "\0";
void HueLightStatus1(uint8_t device, String *response)
{
@ -631,9 +635,17 @@ void HueLightStatus1(uint8_t device, String *response)
light_status += "\"colormode\":\"" + String(g_gotct ? "ct" : "hs") + "\",";
}
if (LST_RGB <= light_subtype) { // colors
float x, y;
light_state.getXY(&x, &y);
light_status += "\"xy\":[" + String(x, 5) + "," + String(y, 5) + "],";
if (prev_x_str[0] && prev_y_str[0]) {
light_status += "\"xy\":[";
light_status += prev_x_str;
light_status += ",";
light_status += prev_y_str;
light_status += "],";
} else {
float x, y;
light_state.getXY(&x, &y);
light_status += "\"xy\":[" + String(x, 5) + "," + String(y, 5) + "],";
}
light_status += "\"hue\":" + String(hue) + ",";
light_status += "\"sat\":" + String(sat) + ",";
}
@ -752,6 +764,7 @@ void HueLights(String *path)
g_gotct = true;
}
}
prev_x_str[0] = prev_y_str[0] = 0; // reset xy string
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"];
@ -800,6 +813,27 @@ void HueLights(String *path)
}
resp = true;
}
if (hue_json.containsKey("xy")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white).
prev_x = hue_json["xy"][0];
prev_y = hue_json["xy"][1];
const String &x_str = hue_json["xy"][0];
const String &y_str = hue_json["xy"][1];
x_str.toCharArray(prev_x_str, sizeof(prev_x_str));
y_str.toCharArray(prev_y_str, sizeof(prev_y_str));
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, "XY (%s %s)", String(prev_x,5).c_str(), String(prev_y,5).c_str());
uint8_t rr,gg,bb;
LightStateClass::XyToRgb(prev_x, prev_y, &rr, &gg, &bb);
LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, "XY RGB (%d %d %d) HS (%d %d)", rr,gg,bb,hue,sat);
if (resp) { response += ","; }
response += FPSTR(HUE_LIGHT_RESPONSE_JSON);
response.replace("{id", String(device));
response.replace("{cm", "xy");
response.replace("{re", "[" + x_str + "," + y_str + "]");
g_gotct = false;
resp = true;
change = true;
}
if (hue_json.containsKey("ct")) { // Color temperature 153 (Cold) to 500 (Warm)
ct = hue_json["ct"];
prev_ct = ct; // store commande value