From bc125ad76c2b9d85248a6aaf13bb7d8c214ba269 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Fri, 1 Mar 2019 17:10:42 +0100 Subject: [PATCH] Updated Espalexa to v2.4.0 --- wled00/WS2812FX.h | 7 +- wled00/WS2812FX_fcn.cpp | 3 +- wled00/src/dependencies/espalexa/Espalexa.h | 261 +++++++++++------- .../dependencies/espalexa/EspalexaDevice.cpp | 171 ++++++++++-- .../dependencies/espalexa/EspalexaDevice.h | 49 +++- wled00/wled00.ino | 4 +- wled00/wled03_set.ino | 23 +- wled00/wled08_led.ino | 9 +- wled00/wled12_alexa.ino | 18 +- wled00/wled17_mqtt.ino | 4 +- 10 files changed, 393 insertions(+), 156 deletions(-) diff --git a/wled00/WS2812FX.h b/wled00/WS2812FX.h index 6b703cea..ad530217 100644 --- a/wled00/WS2812FX.h +++ b/wled00/WS2812FX.h @@ -169,8 +169,11 @@ class WS2812FX { uint8_t speed; uint8_t intensity; uint8_t palette; - uint8_t mode; - uint8_t options; + uint8_t mode; + uint8_t options; + //uint8_t clone; + //bool reverse; + //uint8_t grouping; uint32_t colors[NUM_COLORS]; } segment; diff --git a/wled00/WS2812FX_fcn.cpp b/wled00/WS2812FX_fcn.cpp index 5d2ed710..2148660d 100644 --- a/wled00/WS2812FX_fcn.cpp +++ b/wled00/WS2812FX_fcn.cpp @@ -312,8 +312,7 @@ void WS2812FX::setSecondaryColor(uint32_t c) { void WS2812FX::setBrightness(uint8_t b) { if (_brightness == b) return; _brightness = b; - bus->SetBrightness(_brightness); - show(); + if (SEGMENT_RUNTIME.next_time > millis() + 20) show(); //apply brightness change immeadiately if no refresh soon } uint8_t WS2812FX::getMode(void) { diff --git a/wled00/src/dependencies/espalexa/Espalexa.h b/wled00/src/dependencies/espalexa/Espalexa.h index 831b754b..b55f3b25 100644 --- a/wled00/src/dependencies/espalexa/Espalexa.h +++ b/wled00/src/dependencies/espalexa/Espalexa.h @@ -10,7 +10,7 @@ */ /* * @title Espalexa library - * @version 2.3.4 + * @version 2.4.0 * @author Christian Schwinne * @license MIT * @contributors d-999 @@ -21,6 +21,9 @@ //you can use these defines for library config in your sketch. Just use them before #include //#define ESPALEXA_ASYNC +//in case this is unwanted in your application (will disable the /espalexa value page) +//#define ESPALEXA_NO_SUBPAGE + #ifndef ESPALEXA_MAXDEVICES #define ESPALEXA_MAXDEVICES 10 //this limit only has memory reasons, set it higher should you need to #endif @@ -46,7 +49,7 @@ #include #ifdef ESPALEXA_DEBUG - #pragma message "Espalexa 2.3.4 debug mode" + #pragma message "Espalexa 2.4.0 debug mode" #define EA_DEBUG(x) Serial.print (x) #define EA_DEBUGLN(x) Serial.println (x) #else @@ -56,6 +59,7 @@ #include "EspalexaDevice.h" + class Espalexa { private: //private member vars @@ -69,6 +73,7 @@ private: ESP8266WebServer* server; #endif uint8_t currentDeviceCount = 0; + bool discoverable = true; EspalexaDevice* devices[ESPALEXA_MAXDEVICES] = {}; //Keep in mind that Device IDs go from 1 to DEVICES, cpp arrays from 0 to DEVICES-1!! @@ -80,44 +85,99 @@ private: String escapedMac=""; //lowercase mac address //private member functions + String boolString(bool st) + { + return(st)?"true":"false"; + } + + String modeString(EspalexaColorMode m) + { + if (m == EspalexaColorMode::xy) return "xy"; + if (m == EspalexaColorMode::hs) return "hs"; + return "ct"; + } + + String typeString(EspalexaDeviceType t) + { + switch (t) + { + case EspalexaDeviceType::dimmable: return "Dimmable light"; + case EspalexaDeviceType::whitespectrum: return "Color temperature light"; + case EspalexaDeviceType::color: return "Color light"; + case EspalexaDeviceType::extendedcolor: return "Extended color light"; + } + return "Light"; + } + + String modelidString(EspalexaDeviceType t) + { + switch (t) + { + case EspalexaDeviceType::dimmable: return "LWB010"; + case EspalexaDeviceType::whitespectrum: return "LWT010"; + case EspalexaDeviceType::color: return "LST001"; + case EspalexaDeviceType::extendedcolor: return "LCT015"; + } + return "Plug 01"; + } + //device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001) String deviceJsonString(uint8_t deviceId) { if (deviceId < 1 || deviceId > currentDeviceCount) return "{}"; //error EspalexaDevice* dev = devices[deviceId-1]; - String json = "{\"type\":\""; - json += dev->isColorDevice() ? "Extended color light" : "Dimmable light"; - json += "\",\"manufacturername\":\"OpenSource\",\"swversion\":\"0.1\",\"name\":\""; - json += dev->getName(); - json += "\",\"uniqueid\":\""+ WiFi.macAddress() +"-"+ (deviceId+1) ; - json += "\",\"modelid\":\""; - json += dev->isColorDevice() ? "LCT015" : "LWB010"; - json += "\",\"state\":{\"on\":"; - json += boolString(dev->getValue()) +",\"bri\":"+ (String)(dev->getLastValue()-1) ; - if (dev->isColorDevice()) + + String json = "{\"state\":{\"on\":"; + json += boolString(dev->getValue()); + if (dev->getType() != EspalexaDeviceType::onoff) //bri support { - json += ",\"xy\":[0.00000,0.00000],\"colormode\":\""; - json += (dev->isColorTemperatureMode()) ? "ct":"hs"; - json += "\",\"effect\":\"none\",\"ct\":" + (String)(dev->getCt()) + ",\"hue\":" + (String)(dev->getHue()) + ",\"sat\":" + (String)(dev->getSat()); + json += ",\"bri\":" + String(dev->getLastValue()-1); + if (static_cast(dev->getType()) > 2) //color support + { + json += ",\"hue\":" + String(dev->getHue()) + ",\"sat\":" + String(dev->getSat()); + json += ",\"effect\":\"none\",\"xy\":[" + String(dev->getX()) + "," + String(dev->getY()) + "]"; + } + if (static_cast(dev->getType()) > 1 && dev->getType() != EspalexaDeviceType::color) //white spectrum support + { + json += ",\"ct\":" + String(dev->getCt()); + } } - json +=",\"alert\":\"none\",\"reachable\":true}}"; + json += ",\"alert\":\"none"; + if (static_cast(dev->getType()) > 1) json += "\",\"colormode\":\"" + modeString(dev->getColorMode()); + json += "\",\"mode\":\"homeautomation\",\"reachable\":true},"; + json += "\"type\":\"" + typeString(dev->getType()); + json += "\",\"name\":\"" + dev->getName(); + json += "\",\"modelid\":\"" + modelidString(dev->getType()); + json += "\",\"manufacturername\":\"Espalexa\",\"productname\":\"E" + String(static_cast(dev->getType())); + json += "\",\"uniqueid\":\""+ WiFi.macAddress() +"-"+ (deviceId+1); + json += "\",\"swversion\":\"2.4.0\"}"; + return json; } //Espalexa status page /espalexa + #ifndef ESPALEXA_NO_SUBPAGE void servePage() { EA_DEBUGLN("HTTP Req espalexa ...\n"); String res = "Hello from Espalexa!\r\n\r\n"; for (int i=0; igetName() + "): " + String(devices[i]->getValue()) + "\r\n"; + EspalexaDevice* dev = devices[i]; + res += "Value of device " + String(i+1) + " (" + dev->getName() + "): " + String(dev->getValue()) + " (" + typeString(dev->getType()); + if (static_cast(dev->getType()) > 1) //color support + { + res += ", colormode=" + modeString(dev->getColorMode()) + ", r=" + String(dev->getR()) + ", g=" + String(dev->getG()) + ", b=" + String(dev->getB()); + res +=", ct=" + String(dev->getCt()) + ", hue=" + String(dev->getHue()) + ", sat=" + String(dev->getSat()) + ", x=" + String(dev->getX()) + ", y=" + String(dev->getY()); + } + res += ")\r\n"; } res += "\r\nFree Heap: " + (String)ESP.getFreeHeap(); res += "\r\nUptime: " + (String)millis(); - res += "\r\n\r\nEspalexa library v2.3.4 by Christian Schwinne 2019"; + res += "\r\n\r\nEspalexa library v2.4.0 by Christian Schwinne 2019"; server->send(200, "text/plain", res); } + #endif //not found URI (only if internal webserver is used) void serveNotFound() @@ -194,7 +254,9 @@ private: EA_DEBUG("Received body: "); EA_DEBUGLN(body); }); + #ifndef ESPALEXA_NO_SUBPAGE serverAsync->on("/espalexa", HTTP_GET, [=](AsyncWebServerRequest *request){server = request; servePage();}); + #endif serverAsync->on("/description.xml", HTTP_GET, [=](AsyncWebServerRequest *request){server = request; serveDescription();}); serverAsync->begin(); @@ -208,57 +270,14 @@ private: server->onNotFound([=](){serveNotFound();}); } + #ifndef ESPALEXA_NO_SUBPAGE server->on("/espalexa", HTTP_GET, [=](){servePage();}); + #endif server->on("/description.xml", HTTP_GET, [=](){serveDescription();}); server->begin(); #endif } - //called when Alexa sends ON command - void alexaOn(uint8_t deviceId) - { - devices[deviceId-1]->setValue(devices[deviceId-1]->getLastValue()); - devices[deviceId-1]->setPropertyChanged(1); - devices[deviceId-1]->doCallback(); - } - - //called when Alexa sends OFF command - void alexaOff(uint8_t deviceId) - { - devices[deviceId-1]->setValue(0); - devices[deviceId-1]->setPropertyChanged(2); - devices[deviceId-1]->doCallback(); - } - - //called when Alexa sends BRI command - void alexaDim(uint8_t deviceId, uint8_t briL) - { - if (briL == 255) - { - devices[deviceId-1]->setValue(255); - } else { - devices[deviceId-1]->setValue(briL+1); - } - devices[deviceId-1]->setPropertyChanged(3); - devices[deviceId-1]->doCallback(); - } - - //called when Alexa sends HUE command - void alexaCol(uint8_t deviceId, uint16_t hue, uint8_t sat) - { - devices[deviceId-1]->setColor(hue, sat); - devices[deviceId-1]->setPropertyChanged(4); - devices[deviceId-1]->doCallback(); - } - - //called when Alexa sends CT command (color temperature) - void alexaCt(uint8_t deviceId, uint16_t ct) - { - devices[deviceId-1]->setColor(ct); - devices[deviceId-1]->setPropertyChanged(5); - devices[deviceId-1]->doCallback(); - } - //respond to UDP SSDP M-SEARCH void respondToSearch() { @@ -286,11 +305,6 @@ private: espalexaUdp.endPacket(); } - String boolString(bool st) - { - return(st)?"true":"false"; - } - public: Espalexa(){} @@ -348,10 +362,11 @@ public: packetBuffer[len] = 0; } espalexaUdp.flush(); - + if (!discoverable) return; //do not reply to M-SEARCH if not discoverable + String request = packetBuffer; - EA_DEBUGLN(request); if(request.indexOf("M-SEARCH") >= 0) { + EA_DEBUGLN(request); if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0) { EA_DEBUGLN("Responding search req..."); respondToSearch(); @@ -364,12 +379,15 @@ public: EA_DEBUG("Adding device "); EA_DEBUGLN((currentDeviceCount+1)); if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false; + if (d == nullptr) return false; + d->setId(currentDeviceCount); devices[currentDeviceCount] = d; currentDeviceCount++; return true; } - - bool addDevice(String deviceName, CallbackBriFunction callback, uint8_t initialValue = 0) + + //deprecated brightness-only callback + bool addDevice(String deviceName, BrightnessCallbackFunction callback, uint8_t initialValue = 0) { EA_DEBUG("Constructing device "); EA_DEBUGLN((currentDeviceCount+1)); @@ -378,12 +396,12 @@ public: return addDevice(d); } - bool addDevice(String deviceName, CallbackColFunction callback, uint8_t initialValue = 0) + bool addDevice(String deviceName, DeviceCallbackFunction callback, EspalexaDeviceType t = EspalexaDeviceType::dimmable, uint8_t initialValue = 0) { EA_DEBUG("Constructing device "); EA_DEBUGLN((currentDeviceCount+1)); if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false; - EspalexaDevice* d = new EspalexaDevice(deviceName, callback, initialValue); + EspalexaDevice* d = new EspalexaDevice(deviceName, callback, t, initialValue); return addDevice(d); } @@ -413,32 +431,78 @@ public: { EA_DEBUGLN("devType"); body = ""; - server->send(200, "application/json", "[{\"success\":{\"username\": \"2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr\"}}]"); + server->send(200, "application/json", "[{\"success\":{\"username\":\"2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr\"}}]"); return true; } if (req.indexOf("state") > 0) //client wants to control light { server->send(200, "application/json", "[{\"success\":true}]"); //short valid response - - int tempDeviceId = req.substring(req.indexOf("lights")+7).toInt(); - EA_DEBUG("ls"); EA_DEBUGLN(tempDeviceId); - if (body.indexOf("false")>0) {alexaOff(tempDeviceId); return true;} - if (body.indexOf("bri")>0 ) {alexaDim(tempDeviceId, body.substring(body.indexOf("bri") +5).toInt()); return true;} - if (body.indexOf("hue")>0 ) {alexaCol(tempDeviceId, body.substring(body.indexOf("hue") +5).toInt(), body.substring(body.indexOf("sat") +5).toInt()); return true;} - if (body.indexOf("ct") >0 ) {alexaCt (tempDeviceId, body.substring(body.indexOf("ct") +4).toInt()); return true;} - alexaOn(tempDeviceId); + + int devId = req.substring(req.indexOf("lights")+7).toInt(); + EA_DEBUG("ls"); EA_DEBUGLN(devId); + devices[devId-1]->setPropertyChanged(EspalexaDeviceProperty::none); + if (body.indexOf("false")>0) //OFF command + { + devices[devId-1]->setValue(0); + devices[devId-1]->setPropertyChanged(EspalexaDeviceProperty::off); + devices[devId-1]->doCallback(); + return true; + } + + if (body.indexOf("true") >0) //ON command + { + devices[devId-1]->setValue(devices[devId-1]->getLastValue()); + devices[devId-1]->setPropertyChanged(EspalexaDeviceProperty::on); + } + + if (body.indexOf("bri") >0) //BRIGHTNESS command + { + uint8_t briL = body.substring(body.indexOf("bri") +5).toInt(); + if (briL == 255) + { + devices[devId-1]->setValue(255); + } else { + devices[devId-1]->setValue(briL+1); + } + devices[devId-1]->setPropertyChanged(EspalexaDeviceProperty::bri); + } + + if (body.indexOf("xy") >0) //COLOR command (XY mode) + { + devices[devId-1]->setColorXY(body.substring(body.indexOf("[") +1).toFloat(), body.substring(body.indexOf(",0") +1).toFloat()); + devices[devId-1]->setPropertyChanged(EspalexaDeviceProperty::xy); + } + + if (body.indexOf("hue") >0) //COLOR command (HS mode) + { + devices[devId-1]->setColor(body.substring(body.indexOf("hue") +5).toInt(), body.substring(body.indexOf("sat") +5).toInt()); + devices[devId-1]->setPropertyChanged(EspalexaDeviceProperty::hs); + } + + if (body.indexOf("ct") >0) //COLOR TEMP command (white spectrum) + { + devices[devId-1]->setColor(body.substring(body.indexOf("ct") +4).toInt()); + devices[devId-1]->setPropertyChanged(EspalexaDeviceProperty::ct); + } + + devices[devId-1]->doCallback(); + + #ifdef ESPALEXA_DEBUG + if (devices[devId-1]->getLastChangedProperty() == EspalexaDeviceProperty::none) + EA_DEBUGLN("STATE REQ WITHOUT BODY (likely Content-Type issue #6)"); + #endif return true; } int pos = req.indexOf("lights"); if (pos > 0) //client wants light info { - int tempDeviceId = req.substring(pos+7).toInt(); - EA_DEBUG("l"); EA_DEBUGLN(tempDeviceId); + int devId = req.substring(pos+7).toInt(); + EA_DEBUG("l"); EA_DEBUGLN(devId); - if (tempDeviceId == 0) //client wants all lights + if (devId == 0) //client wants all lights { EA_DEBUGLN("lAll"); String jsonTemp = "{"; @@ -450,9 +514,9 @@ public: } jsonTemp += "}"; server->send(200, "application/json", jsonTemp); - } else //client wants one light (tempDeviceId) + } else //client wants one light (devId) { - server->send(200, "application/json", deviceJsonString(tempDeviceId)); + server->send(200, "application/json", deviceJsonString(devId)); } return true; @@ -463,21 +527,26 @@ public: return true; } + //set whether Alexa can discover any devices + void setDiscoverable(bool d) + { + discoverable = d; + } + + //get EspalexaDevice at specific index + EspalexaDevice* getDevice(uint8_t index) + { + if (index >= currentDeviceCount) return nullptr; + return devices[index]; + } + //is an unique device ID String getEscapedMac() { return escapedMac; } - //convert brightness (0-255) to percentage - uint8_t toPercent(uint8_t bri) - { - uint16_t perc = bri * 100; - return perc / 255; - } - ~Espalexa(){delete devices;} //note: Espalexa is NOT meant to be destructed }; #endif - diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.cpp b/wled00/src/dependencies/espalexa/EspalexaDevice.cpp index 70f5cdb2..f440b800 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.cpp +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.cpp @@ -4,34 +4,40 @@ EspalexaDevice::EspalexaDevice(){} -EspalexaDevice::EspalexaDevice(String deviceName, CallbackBriFunction gnCallback, uint8_t initialValue) { //constructor +EspalexaDevice::EspalexaDevice(String deviceName, BrightnessCallbackFunction gnCallback, uint8_t initialValue) { //constructor _deviceName = deviceName; _callback = gnCallback; _val = initialValue; _val_last = _val; + _type = EspalexaDeviceType::dimmable; } -EspalexaDevice::EspalexaDevice(String deviceName, CallbackColFunction gnCallback, uint8_t initialValue) { //constructor for color device +EspalexaDevice::EspalexaDevice(String deviceName, DeviceCallbackFunction gnCallback, EspalexaDeviceType t, uint8_t initialValue) { //constructor for color device _deviceName = deviceName; - _callbackCol = gnCallback; + _callbackDev = gnCallback; _callback = nullptr; + _type = t; _val = initialValue; _val_last = _val; } EspalexaDevice::~EspalexaDevice(){/*nothing to destruct*/} -bool EspalexaDevice::isColorDevice() +uint8_t EspalexaDevice::getId() { - //if brightness-only callback is null, we have color device - return (_callback == nullptr); + return _id; } -bool EspalexaDevice::isColorTemperatureMode() +EspalexaColorMode EspalexaDevice::getColorMode() { - return _ct; + return _mode; +} + +EspalexaDeviceType EspalexaDevice::getType() +{ + return _type; } String EspalexaDevice::getName() @@ -39,7 +45,7 @@ String EspalexaDevice::getName() return _deviceName; } -uint8_t EspalexaDevice::getLastChangedProperty() +EspalexaDeviceProperty EspalexaDevice::getLastChangedProperty() { return _changed; } @@ -49,6 +55,17 @@ uint8_t EspalexaDevice::getValue() return _val; } +uint8_t EspalexaDevice::getPercent() +{ + uint16_t perc = _val * 100; + return perc / 255; +} + +uint8_t EspalexaDevice::getDegrees() +{ + return getPercent(); +} + uint16_t EspalexaDevice::getHue() { return _hue; @@ -59,17 +76,37 @@ uint8_t EspalexaDevice::getSat() return _sat; } +float EspalexaDevice::getX() +{ + return _x; +} + +float EspalexaDevice::getY() +{ + return _y; +} + uint16_t EspalexaDevice::getCt() { if (_ct == 0) return 500; return _ct; } -uint32_t EspalexaDevice::getColorRGB() +uint32_t EspalexaDevice::getKelvin() { + if (_ct == 0) return 2000; + return 1000000/_ct; +} + +uint32_t EspalexaDevice::getRGB() +{ + if (_rgb != 0) return _rgb; //color has not changed uint8_t rgb[3]; + float r, g, b; - if (isColorTemperatureMode()) + if (_mode == EspalexaColorMode::none) return 0; + + if (_mode == EspalexaColorMode::ct) { //TODO tweak a bit to match hue lamp characteristics //based on https://gist.github.com/paulkaplan/5184275 @@ -97,9 +134,8 @@ uint32_t EspalexaDevice::getColorRGB() rgb[0] = (byte)constrain(r,0.1,255.1); rgb[1] = (byte)constrain(g,0.1,255.1); rgb[2] = (byte)constrain(b,0.1,255.1); - } - else - { //hue + sat mode + } else if (_mode == EspalexaColorMode::hs) + { float h = ((float)_hue)/65535.0; float s = ((float)_sat)/255.0; byte i = floor(h*6); @@ -115,8 +151,79 @@ uint32_t EspalexaDevice::getColorRGB() case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break; case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q; } + } else if (_mode == EspalexaColorMode::xy) + { + //Source: https://www.developers.meethue.com/documentation/color-conversions-rgb-xy + float z = 1.0f - _x - _y; + float X = (1.0f / _y) * _x; + float Z = (1.0f / _y) * z; + float r = (int)255*(X * 1.656492f - 0.354851f - Z * 0.255038f); + float g = (int)255*(-X * 0.707196f + 1.655397f + Z * 0.036152f); + float b = (int)255*(X * 0.051713f - 0.121364f + Z * 1.011530f); + if (r > b && r > g && r > 1.0f) { + // red is too big + g = g / r; + b = b / r; + r = 1.0f; + } else if (g > b && g > r && g > 1.0f) { + // green is too big + r = r / g; + b = b / g; + g = 1.0f; + } else if (b > r && b > g && b > 1.0f) { + // blue is too big + r = r / b; + g = g / b; + b = 1.0f; + } + // Apply gamma correction + r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f; + g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f; + b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f; + + if (r > b && r > g) { + // red is biggest + if (r > 1.0f) { + g = g / r; + b = b / r; + r = 1.0f; + } + } else if (g > b && g > r) { + // green is biggest + if (g > 1.0f) { + r = r / g; + b = b / g; + g = 1.0f; + } + } else if (b > r && b > g) { + // blue is biggest + if (b > 1.0f) { + r = r / b; + g = g / b; + b = 1.0f; + } + } + rgb[0] = 255.0*r; + rgb[1] = 255.0*g; + rgb[2] = 255.0*b; } - return ((rgb[0] << 16) | (rgb[1] << 8) | (rgb[2])); + _rgb = ((rgb[0] << 16) | (rgb[1] << 8) | (rgb[2])); + return _rgb; +} + +uint8_t EspalexaDevice::getR() +{ + return (getRGB() >> 16) & 0xFF; +} + +uint8_t EspalexaDevice::getG() +{ + return (getRGB() >> 8) & 0xFF; +} + +uint8_t EspalexaDevice::getB() +{ + return getRGB() & 0xFF; } uint8_t EspalexaDevice::getLastValue() @@ -125,12 +232,16 @@ uint8_t EspalexaDevice::getLastValue() return _val_last; } -void EspalexaDevice::setPropertyChanged(uint8_t p) +void EspalexaDevice::setPropertyChanged(EspalexaDeviceProperty p) { - //0: initial 1: on 2: off 3: bri 4: col 5: ct _changed = p; } +void EspalexaDevice::setId(uint8_t id) +{ + _id = id; +} + //you need to re-discover the device for the Alexa name to change void EspalexaDevice::setName(String name) { @@ -158,19 +269,41 @@ void EspalexaDevice::setPercent(uint8_t perc) setValue(val); } +void EspalexaDevice::setColorXY(float x, float y) +{ + _x = x; + _y = y; + _rgb = 0; + _mode = EspalexaColorMode::xy; +} + void EspalexaDevice::setColor(uint16_t hue, uint8_t sat) { _hue = hue; _sat = sat; - _ct = 0; + _rgb = 0; + _mode = EspalexaColorMode::hs; } void EspalexaDevice::setColor(uint16_t ct) { _ct = ct; + _rgb = 0; + _mode =EspalexaColorMode::ct; +} + +void EspalexaDevice::setColor(uint8_t r, uint8_t g, uint8_t b) +{ + float X = r * 0.664511f + g * 0.154324f + b * 0.162028f; + float Y = r * 0.283881f + g * 0.668433f + b * 0.047685f; + float Z = r * 0.000088f + g * 0.072310f + b * 0.986039f; + _x = X / (X + Y + Z); + _y = Y / (X + Y + Z); + _rgb = ((r << 16) | (g << 8) | b); + _mode = EspalexaColorMode::xy; } void EspalexaDevice::doCallback() { - (_callback != nullptr) ? _callback(_val) : _callbackCol(_val, getColorRGB()); + (_callback != nullptr) ? _callback(_val) : _callbackDev(this); } \ No newline at end of file diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.h b/wled00/src/dependencies/espalexa/EspalexaDevice.h index 0974ff3a..07708d07 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.h +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.h @@ -3,40 +3,63 @@ #include "Arduino.h" -typedef void (*CallbackBriFunction) (uint8_t br); -typedef void (*CallbackColFunction) (uint8_t br, uint32_t col); +typedef class EspalexaDevice; + +typedef void (*BrightnessCallbackFunction) (uint8_t b); +typedef void (*DeviceCallbackFunction) (EspalexaDevice* d); + +enum class EspalexaColorMode : uint8_t { none = 0, ct = 1, hs = 2, xy = 3 }; +enum class EspalexaDeviceType : uint8_t { onoff = 0, dimmable = 1, whitespectrum = 2, color = 3, extendedcolor = 4 }; +enum class EspalexaDeviceProperty : uint8_t { none = 0, on = 1, off = 2, bri = 3, hs = 4, ct = 5, xy = 6 }; class EspalexaDevice { private: String _deviceName; - CallbackBriFunction _callback; - CallbackColFunction _callbackCol; + BrightnessCallbackFunction _callback; + DeviceCallbackFunction _callbackDev; uint8_t _val, _val_last, _sat = 0; uint16_t _hue = 0, _ct = 0; - uint8_t _changed = 0; + float _x = 0, _y = 0; + uint32_t _rgb = 0; + uint8_t _id = 0; + EspalexaDeviceType _type; + EspalexaDeviceProperty _changed = EspalexaDeviceProperty::none; + EspalexaColorMode _mode; public: EspalexaDevice(); ~EspalexaDevice(); - EspalexaDevice(String deviceName, CallbackBriFunction gnCallback, uint8_t initialValue =0); - EspalexaDevice(String deviceName, CallbackColFunction gnCallback, uint8_t initialValue =0); + EspalexaDevice(String deviceName, BrightnessCallbackFunction bcb, uint8_t initialValue =0); + EspalexaDevice(String deviceName, DeviceCallbackFunction dcb, EspalexaDeviceType t =EspalexaDeviceType::dimmable, uint8_t initialValue =0); - bool isColorDevice(); - bool isColorTemperatureMode(); String getName(); - uint8_t getLastChangedProperty(); + uint8_t getId(); + EspalexaDeviceProperty getLastChangedProperty(); uint8_t getValue(); + uint8_t getPercent(); + uint8_t getDegrees(); uint16_t getHue(); uint8_t getSat(); uint16_t getCt(); - uint32_t getColorRGB(); + uint32_t getKelvin(); + float getX(); + float getY(); + uint32_t getRGB(); + uint8_t getR(); + uint8_t getG(); + uint8_t getB(); + EspalexaColorMode getColorMode(); + EspalexaDeviceType getType(); - void setPropertyChanged(uint8_t p); + void setId(uint8_t id); + void setPropertyChanged(EspalexaDeviceProperty p); void setValue(uint8_t bri); void setPercent(uint8_t perc); void setName(String name); - void setColor(uint16_t hue, uint8_t sat); void setColor(uint16_t ct); + void setColor(uint16_t hue, uint8_t sat); + void setColorXY(float x, float y); + void setColor(uint8_t r, uint8_t g, uint8_t b); void doCallback(); diff --git a/wled00/wled00.ino b/wled00/wled00.ino index ec04bb39..c3953a9a 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -86,7 +86,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1902251 +#define VERSION 1903011 char versionString[] = "0.8.4-dev"; @@ -146,7 +146,7 @@ byte nightlightDelayMins = 60; bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over bool fadeTransition = true; //enable crossfading color transition bool enableSecTransition = true; //also enable transition for secondary color -uint16_t transitionDelay = 900; //default crossfade duration in ms +uint16_t transitionDelay = 750; //default crossfade duration in ms bool reverseMode = false; //flip entire LED strip (reverses all effect directions) bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index f92d9ae2..5b0ce433 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -586,6 +586,10 @@ bool handleSet(AsyncWebServerRequest *request, String req) default: toggleOnOff(); //toggle } } + + //Segment reverse + //pos = req.indexOf("SR="); + //if (pos > 0) strip.getSegment().reverse = (req.charAt(pos+3) != '0'); //deactivate nightlight if target brightness is reached if (bri == nightlightTargetBri) nightlightActive = false; @@ -641,21 +645,13 @@ bool handleSet(AsyncWebServerRequest *request, String req) } //cronixie + #ifndef WLED_DISABLE_CRONIXIE pos = req.indexOf("NX="); //sets digits to code if (pos > 0) { strcpy(cronixieDisplay,req.substring(pos + 3, pos + 9).c_str()); setCronixie(); } - pos = req.indexOf("NM="); //mode, 1 countdown - if (pos > 0) { - countdownMode = true; - if (req.indexOf("NM=0") > 0) - { - countdownMode = false; - } - } - if (req.indexOf("NB=") > 0) //sets backlight { cronixieBacklight = true; @@ -666,6 +662,15 @@ bool handleSet(AsyncWebServerRequest *request, String req) if (overlayCurrent == 3) strip.setCronixieBacklight(cronixieBacklight); overlayRefreshedTime = 0; } + #endif + pos = req.indexOf("NM="); //mode, 1 countdown + if (pos > 0) { + countdownMode = true; + if (req.indexOf("NM=0") > 0) + { + countdownMode = false; + } + } pos = req.indexOf("U0="); //user var 0 if (pos > 0) { diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index 36ff0465..00476d4c 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -134,9 +134,6 @@ void colorUpdated(int callMode) } if (callMode == 8) return; - #ifndef WLED_DISABLE_ALEXA - if (espalexaDevice != nullptr) espalexaDevice->setValue(bri); - #endif //only update Blynk and mqtt every 2 seconds to reduce lag if (millis() - lastInterfaceUpdate <= 2000) { @@ -149,6 +146,12 @@ void colorUpdated(int callMode) void updateInterfaces(uint8_t callMode) { + #ifndef WLED_DISABLE_ALEXA + if (espalexaDevice != nullptr && callMode != 10) { + espalexaDevice->setValue(bri); + espalexaDevice->setColor(col[0], col[1], col[2]); + } + #endif if (callMode != 9 && callMode != 5) updateBlynk(); publishMqtt(); lastInterfaceUpdate = millis(); diff --git a/wled00/wled12_alexa.ino b/wled00/wled12_alexa.ino index 4f249680..c9aa5fc5 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/wled12_alexa.ino @@ -12,7 +12,7 @@ void prepareIds() { } #ifndef WLED_DISABLE_ALEXA -void onAlexaChange(byte b, uint32_t color); +void onAlexaChange(EspalexaDevice* dev); void alexaInit() { @@ -20,7 +20,7 @@ void alexaInit() { if (espalexaDevice == nullptr) //only init once { - espalexaDevice = new EspalexaDevice(alexaInvocationName, onAlexaChange); + espalexaDevice = new EspalexaDevice(alexaInvocationName, onAlexaChange, EspalexaDeviceType::extendedcolor); espalexa.addDevice(espalexaDevice); espalexa.begin(&server); } else { @@ -35,11 +35,12 @@ void handleAlexa() espalexa.loop(); } -void onAlexaChange(byte b, uint32_t color) +void onAlexaChange(EspalexaDevice* dev) { - byte m = espalexaDevice->getLastChangedProperty(); + EspalexaDeviceProperty m = espalexaDevice->getLastChangedProperty(); - if (m == 1){ //ON + if (m == EspalexaDeviceProperty::on) + { if (!macroAlexaOn) { if (bri == 0) @@ -48,7 +49,7 @@ void onAlexaChange(byte b, uint32_t color) colorUpdated(10); } } else applyMacro(macroAlexaOn); - } else if (m == 2) //OFF + } else if (m == EspalexaDeviceProperty::off) { if (!macroAlexaOff) { @@ -59,12 +60,13 @@ void onAlexaChange(byte b, uint32_t color) colorUpdated(10); } } else applyMacro(macroAlexaOff); - } else if (m == 3) //brightness + } else if (m == EspalexaDeviceProperty::bri) { - bri = b; + bri = espalexaDevice->getValue(); colorUpdated(10); } else //color { + uint32_t color = espalexaDevice->getRGB(); col[0] = ((color >> 16) & 0xFF); col[1] = ((color >> 8) & 0xFF); col[2] = (color & 0xFF); diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino index 14bdf5f7..b09a3e58 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/wled17_mqtt.ino @@ -6,8 +6,8 @@ void parseMQTTBriPayload(char* payload) { - if (strcmp(payload, "ON") == 0) {bri = briLast; colorUpdated(1);} - else if (strcmp(payload, "T" ) == 0) {toggleOnOff(); colorUpdated(1);} + if (strcmp(payload, "ON") == 0 || strcmp(payload, "on") == 0) {bri = briLast; colorUpdated(1);} + else if (strcmp(payload, "T" ) == 0 || strcmp(payload, "t" ) == 0) {toggleOnOff(); colorUpdated(1);} else { uint8_t in = strtoul(payload, NULL, 10); if (in == 0 && bri > 0) briLast = bri;