Updated Espalexa to v2.4.0

This commit is contained in:
cschwinne 2019-03-01 17:10:42 +01:00
parent 62a2246448
commit bc125ad76c
10 changed files with 393 additions and 156 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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 <Espalexa.h>
//#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 <WiFiUdp.h>
#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<uint8_t>(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<uint8_t>(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<uint8_t>(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<uint8_t>(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; i<currentDeviceCount; i++)
{
res += "Value of device " + String(i+1) + " (" + devices[i]->getName() + "): " + 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<uint8_t>(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

View File

@ -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);
}

View File

@ -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();

View File

@ -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)

View File

@ -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) {

View File

@ -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();

View File

@ -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);

View File

@ -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;