Updated Espalexa to v2.4.0
This commit is contained in:
parent
62a2246448
commit
bc125ad76c
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user