Added Philips Hue synchronization

Added HU and SA API calls to set color via Hue and Saturation values
This commit is contained in:
cschwinne 2018-02-28 00:27:10 +01:00
parent 1b0d735e50
commit e7e11b8bd2
13 changed files with 477 additions and 36 deletions

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -196,27 +196,25 @@ Receive <input type="checkbox" name="NRCBR">Brightness, <input type="checkbox" n
Send notifications on direct change: <input type="checkbox" name="NSDIR"> <br>
Send notifications on button press: <input type="checkbox" name="NSBTN"> <br>
Send Alexa notifications: <input type="checkbox" name="NSALX"> <br>
<!--Send Philips Hue change notifications: <input type="checkbox" name="NSHUE">-->
Send Philips Hue change notifications: <input type="checkbox" name="NSHUE">
<h3>Alexa Voice Assistant</h3>
Emulate Alexa device: <input type="checkbox" name="ALEXA"> <br>
Alexa invocation name: <input name="AINVN" maxlength="32"><br>
<h3>Philips Hue</h3>
Coming soon! Not yet implemented!
<!--<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br>
<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br>
Hue Bridge IP:<br>
<input name="HUIP0" type="number" min="0" max="255" required> .
<input name="HUIP1" type="number" min="0" max="255" required> .
<input name="HUIP2" type="number" min="0" max="255" required> .
<input name="HUIP3" type="number" min="0" max="255" required> <br>
<b>For successful pairing, press the pushlink button on the bridge, then save this page!</b><br>
<b>Press the pushlink button on the bridge, after that save this page!</b><br>
(when first connecting)<br>
<i> Use 0 for group and light to turn off sending/receiving </i><br>
Update Hue group <input name="HUEGR" type="number" min="0" max="99" required> <br>
Send <input type="checkbox" name="HUEIO"> On/Off, <input type="checkbox" name="HUEBR"> Brightness, and <input type="checkbox" name="HUECL"> Color<br>
Poll Hue light <input name="HUELI" type="number" min="0" max="99" required> every <input name="HUEPL" type="number" min="100" max="62000" required> ms<br>
<!--Update Hue group <input name="HUEGR" type="number" min="0" max="99" required> <br>
Send <input type="checkbox" name="HUEIO"> On/Off, <input type="checkbox" name="HUEBR"> Brightness, and <input type="checkbox" name="HUECL"> Color<br>-->
Poll Hue light <input name="HUELI" type="number" min="1" max="99" required> every <input name="HUEPI" type="number" min="100" max="65000" required> ms: <input type="checkbox" name="HUEPL"><br>
Then, receive <input type="checkbox" name="HURIO"> On/Off, <input type="checkbox" name="HURBR"> Brightness, and <input type="checkbox" name="HURCL"> Color<br>
After device color update, ignore Hue updates for <input name="HUELI" type="number" min="0" max="255" required> minutes<br>
Hue status: <span class="hms"> Internal ESP error! </span>-->
<!--After device color update, ignore Hue updates for <input name="HUELI" type="number" min="0" max="255" required> minutes<br>-->
Hue status: <span class="hms"> Internal ESP Error! </span>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit" name="SUBM">Save</button>
</form>
@ -333,7 +331,7 @@ HTTP traffic is not encrypted. An attacker in the same network could intercept f
<button type="button" onclick="U()">Manual OTA Update</button><br>
Enable ArduinoOTA: <input type="checkbox" name="AROTA"><br>
<h3>About</h3>
<a href="https://github.com/Aircoookie/WLED">WLED</a> version 0.5.0<br>
<a href="https://github.com/Aircoookie/WLED">WLED</a> version 0.5.1<br>
(c) 2016-2018 Christian Schwinne <br>
<i>Licensed under the MIT license</i><br><br>
<i>Uses libraries:</i><br>

View File

@ -3,7 +3,7 @@
*/
/*
* @title WLED project sketch
* @version 0.5.0
* @version 0.5.1
* @author Christian Schwinne
*/
@ -12,10 +12,12 @@
#include <WiFi.h>
#include <ESPmDNS.h>
#include "src/dependencies/webserver/WebServer.h"
#include <HTTPClient.h>
#else
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#endif
#include <EEPROM.h>
#include <ArduinoOTA.h>
@ -30,8 +32,8 @@
#include "WS2812FX.h"
//version in format yymmddb (b = daily build)
#define VERSION 1802251
const String versionString = "0.5.0";
#define VERSION 1802273
const String versionString = "0.5.1";
//AP and OTA default passwords (change them!)
String appass = "wled1234";
@ -152,6 +154,22 @@ unsigned long countdownTime = 1514764800L;
double transitionResolution = 0.011;
//hue
long hueLastRequestSent = 0;
bool huePollingEnabled = false, hueAttempt = false;
uint16_t huePollIntervalMs = 2500;
uint32_t huePollIntervalMsTemp = 2500;
String hueApiKey = "api";
uint8_t huePollLightId = 1;
IPAddress hueIP = (0,0,0,0);
bool notifyHue = true;
bool hueApplyOnOff = true, hueApplyBri = true, hueApplyColor = true;
String hueError = "Inactive";
uint16_t hueFailCount = 0;
float hueXLast=0, hueYLast=0;
uint16_t hueHueLast=0, hueCtLast=0;
uint8_t hueSatLast=0, hueBriLast=0;
//Internal vars
byte col[]{0, 0, 0};
byte col_old[]{0, 0, 0};
@ -241,20 +259,13 @@ WebServer server(80);
#else
ESP8266WebServer server(80);
#endif
HTTPClient hueClient;
ESP8266HTTPUpdateServer httpUpdater;
WiFiUDP notifierUdp;
WiFiUDP ntpUdp;
WS2812FX strip = WS2812FX(LEDCOUNT);
//eeprom Version code, enables default settings instead of 0 init on update
#define EEPVER 4
//0 -> old version, default
//1 -> 0.4p 1711272 and up
//2 -> 0.4p 1711302 and up
//3 -> 0.4 1712121 and up
//4 -> 0.5.0 and up
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print (x)
#define DEBUG_PRINTLN(x) Serial.println (x)
@ -333,6 +344,7 @@ void loop() {
handleAlexa();
if (!arlsTimeout) //block stuff if WARLS is enabled
{
handleHue();
handleNightlight();
#ifdef USEOVERLAYS
handleOverlays();

View File

@ -1,8 +1,18 @@
/*
* Methods to handle saving and loading to non-volatile memory
* EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map
*/
#define EEPSIZE 2048
#define EEPSIZE 3072
//eeprom Version code, enables default settings instead of 0 init on update
#define EEPVER 5
//0 -> old version, default
//1 -> 0.4p 1711272 and up
//2 -> 0.4p 1711302 and up
//3 -> 0.4 1712121 and up
//4 -> 0.5.0 and up
//5 -> 0.5.1 and up
void clearEEPROM()
{
@ -144,6 +154,24 @@ void saveSettingsToEEPROM()
EEPROM.write(i, cssFont.charAt(i-950));
}
EEPROM.write(2048, huePollingEnabled);
//EEPROM.write(2049, hueUpdatingEnabled);
for (int i = 2050; i < 2054; ++i)
{
EEPROM.write(i, hueIP[i-2050]);
}
for (int i = 2054; i < 2100; ++i)
{
EEPROM.write(i, hueApiKey.charAt(i-2054));
}
EEPROM.write(2100, (huePollIntervalMs >> 0) & 0xFF);
EEPROM.write(2101, (huePollIntervalMs >> 8) & 0xFF);
EEPROM.write(2102, notifyHue);
EEPROM.write(2103, hueApplyOnOff);
EEPROM.write(2104, hueApplyBri);
EEPROM.write(2105, hueApplyColor);
EEPROM.write(2106, huePollLightId);
EEPROM.commit();
}
@ -297,6 +325,26 @@ void loadSettingsFromEEPROM(bool first)
receiveNotificationEffects = receiveNotificationBrightness;
}
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
if (lastEEPROMversion > 4) {
huePollingEnabled = EEPROM.read(2048);
//hueUpdatingEnabled = EEPROM.read(2049);
for (int i = 2050; i < 2054; ++i)
{
hueIP[i-2050] = EEPROM.read(i);
}
hueApiKey = "";
for (int i = 2054; i < 2100; ++i)
{
if (EEPROM.read(i) == 0) break;
hueApiKey += char(EEPROM.read(i));
}
huePollIntervalMs = ((EEPROM.read(2100) << 0) & 0xFF) + ((EEPROM.read(2101) << 8) & 0xFF00);
notifyHue = EEPROM.read(2102);
hueApplyOnOff = EEPROM.read(2103);
hueApplyBri = EEPROM.read(2104);
hueApplyColor = EEPROM.read(2105);
huePollLightId = EEPROM.read(2106);
}
bootPreset = EEPROM.read(389);
wifiLock = EEPROM.read(393);
@ -316,6 +364,9 @@ void loadSettingsFromEEPROM(bool first)
//custom macro memory (16 slots/ each 64byte)
//1024-2047 reserved
//user MOD memory
//2944 - 3071 reserved
useHSB = useHSBDefault;
strip.setMode(effectCurrent);

View File

@ -187,9 +187,21 @@ String getSettings(uint8_t subPage)
resp += ds + "NRCFX" + c + receiveNotificationEffects +";";
resp += ds + "NSDIR" + c + notifyDirectDefault +";";
resp += ds + "NSBTN" + c + notifyButton +";";
resp += ds + "NSHUE" + c + notifyHue +";";
resp += ds + "ALEXA" + c + alexaEnabled +";";
resp += ds + "AINVN" + v + "\"" + alexaInvocationName + "\";";
resp += ds + "NSALX" + c + alexaNotify +";";
resp += ds + "HUIP0" + v + hueIP[0] +";";
resp += ds + "HUIP1" + v + hueIP[1] +";";
resp += ds + "HUIP2" + v + hueIP[2] +";";
resp += ds + "HUIP3" + v + hueIP[3] +";";
resp += ds + "HUELI" + v + huePollLightId +";";
resp += ds + "HUEPI" + v + huePollIntervalMs +";";
resp += ds + "HUEPL" + c + huePollingEnabled +";";
resp += ds + "HURIO" + c + hueApplyOnOff +";";
resp += ds + "HURBR" + c + hueApplyBri +";";
resp += ds + "HURCL" + c + hueApplyColor +";";
resp += dg + "(\"hms\")[0]" + ih + "\"" + hueError + "\";";
}
if (subPage == 5)

View File

@ -281,6 +281,37 @@ void handleSettingsSet(uint8_t subPage)
alexaEnabled = server.hasArg("ALEXA");
if (server.hasArg("AINVN")) alexaInvocationName = server.arg("AINVN");
alexaNotify = server.hasArg("NSALX");
notifyHue = server.hasArg("NSHUE");
for (int i=0;i<4;i++){
String a = "HUIP"+String(i);
if (server.hasArg(a))
{
int j = server.arg(a).toInt();
if (j >= 0 && j <= 255) hueIP[i] = j;
}
}
if (server.hasArg("HUELI"))
{
int i = server.arg("HUELI").toInt();
if (i > 0) huePollLightId = i;
}
if (server.hasArg("HUEPI"))
{
int i = server.arg("HUEPI").toInt();
if (i > 50) huePollIntervalMs = i;
}
hueApplyOnOff = server.hasArg("HURIO");
hueApplyBri = server.hasArg("HURBR");
hueApplyColor = server.hasArg("HURCL");
if (server.hasArg("HUEPL"))
{
if (!huePollingEnabled) hueAttempt = true;
if (!setupHue()) hueAttempt = true;
} else
{
huePollingEnabled = false;
hueError = "Inactive";
}
}
//TIME
@ -358,6 +389,19 @@ boolean handleSet(String req)
if (pos > 0) {
bri = req.substring(pos + 3).toInt();
}
//set hue
pos = req.indexOf("HU=");
if (pos > 0) {
uint16_t temphue = req.substring(pos + 3).toInt();
uint8_t tempsat = 255;
pos = req.indexOf("SA=");
if (pos > 0) {
tempsat = req.substring(pos + 3).toInt();
}
colorHStoRGB(temphue,tempsat,(req.indexOf("H2")>0)? col_sec:col);
}
//set red value
pos = req.indexOf("&R=");
if (pos > 0) {
@ -481,6 +525,19 @@ boolean handleSet(String req)
}
}
//set hue polling light: 0 -off
pos = req.indexOf("HP=");
if (pos > 0) {
int id = req.substring(pos + 3).toInt();
if (id > 0)
{
if (id < 100) huePollLightId = id;
setupHue();
} else {
huePollingEnabled = false;
}
}
//set default control mode (0 - RGB, 1 - HSB)
pos = req.indexOf("MD=");
if (pos > 0) {

View File

@ -56,6 +56,13 @@ void wledInit()
DEBUG_PRINT("Connected! IP address: ");
DEBUG_PRINTLN(WiFi.localIP());
if (hueIP[0] == 0)
{
hueIP[0] = WiFi.localIP()[0];
hueIP[1] = WiFi.localIP()[1];
hueIP[2] = WiFi.localIP()[2];
}
// Set up mDNS responder:
if (cmdns != NULL && !onlyAP && !MDNS.begin(cmdns.c_str())) {
DEBUG_PRINTLN("Error setting up MDNS responder!");
@ -147,7 +154,13 @@ void wledInit()
server.on("/settings/sync", HTTP_POST, [](){
handleSettingsSet(4);
if (hueAttempt)
{
serveMessage(200,"Hue setup result",hueError,253);
} else {
serveMessage(200,"Sync settings saved.","Redirecting...",1);
}
hueAttempt = false;
});
server.on("/settings/time", HTTP_POST, [](){

View File

@ -1,6 +1,7 @@
/*
* This file allows you to add own functionality to WLED more easily
*
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* EEPROM bytes 2944 to 3071 are reserved for your custom use case.
*/
void userBeginPreConnection()

View File

@ -13,6 +13,7 @@ void notify(uint8_t callMode)
case 2: if (!notifyButton) return; break;
case 4: if (!notifyDirect) return; break;
case 6: if (!notifyDirect) return; break; //fx change
case 7: if (!notifyHue) return; break;
default: return;
}
byte udpOut[WLEDPACKETSIZE];

View File

@ -51,7 +51,7 @@ bool colorChanged()
void colorUpdated(int callMode)
{
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (no not.) (NN)6: fx changed
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (NN)6: fx changed 7: hue
if (!colorChanged())
{
if (callMode == 6) notify(6);

View File

@ -3,25 +3,118 @@
*/
void colorCTtoRGB(uint16_t mired, uint8_t* rgb) //white spectrum to rgb
{
//this is only an approximation using WS2812B with gamma correction enabled
if (mired > 475)
{
rgb[0]=255;rgb[1]=199;rgb[2]=92;//500
} else if (mired > 425)
{
rgb[0]=255;rgb[1]=213;rgb[2]=118;//450
} else if (mired > 375)
{
rgb[0]=255;rgb[1]=216;rgb[2]=118;//400
} else if (mired > 325)
{
rgb[0]=255;rgb[1]=234;rgb[2]=140;//350
} else if (mired > 275)
{
rgb[0]=255;rgb[1]=243;rgb[2]=160;//300
} else if (mired > 225)
{
rgb[0]=250;rgb[1]=255;rgb[2]=188;//250
} else if (mired > 175)
{
rgb[0]=247;rgb[1]=255;rgb[2]=215;//200
} else
{
rgb[0]=237;rgb[1]=255;rgb[2]=239;//150
}
}
void colorHSBtoRGB(uint16_t hue, uint8_t sat, uint8_t bri, uint8_t* rgb) //hue, sat, bri to rgb
void colorHStoRGB(uint16_t hue, uint8_t sat, uint8_t* rgb) //hue, sat to rgb
{
float h = ((float)hue)/65535.0;
float s = ((float)sat)/255.0;
uint8_t i = floor(h*6);
float f = h * 6-i;
float p = 255 * (1-s);
float q = 255 * (1-f*s);
float t = 255 * (1-(1-f)*s);
switch (i%6) {
case 0: rgb[0]=255,rgb[1]=t,rgb[2]=p;break;
case 1: rgb[0]=q,rgb[1]=255,rgb[2]=p;break;
case 2: rgb[0]=p,rgb[1]=255,rgb[2]=t;break;
case 3: rgb[0]=p,rgb[1]=q,rgb[2]=255;break;
case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break;
case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q;
}
}
void colorXYtoRGB(float x, float y, uint8_t* rgb) //coordinates to rgb (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
{
float z = 1.0f - x - y;
//float Y = 1.0f; // Brightness, we handle this separately
float X = (1.0f / y) * x;
float Z = (1.0f / y) * z;
rgb[0] = (int)(X * 1.656492f - 0.354851f - Z * 0.255038f);
rgb[1] = (int)(-X * 0.707196f + 1.655397f + Z * 0.036152f);
rgb[2] = (int)(X * 0.051713f - 0.121364f + Z * 1.011530f);
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;
}
void colorRGBtoXY(uint8_t* rgb, float* xy){} //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
void colorRGBtoXY(uint8_t* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
{
float X = rgb[0] * 0.664511f + rgb[1] * 0.154324f + rgb[2] * 0.162028f;
float Y = rgb[0] * 0.283881f + rgb[1] * 0.668433f + rgb[2] * 0.047685f;
float Z = rgb[0] * 0.000088f + rgb[1] * 0.072310f + rgb[2] * 0.986039f;
xy[0] = X / (X + Y + Z);
xy[1] = Y / (X + Y + Z);
}
void colorRGBtoRGBW(uint8_t* rgb, uint8_t* rgbw){} //rgb to rgbw, not imlemented yet

View File

@ -1,4 +1,207 @@
/*
* Sync to Philips hue lights
*/
void foo(){}
void handleHue()
{
if (huePollingEnabled && WiFi.status() == WL_CONNECTED)
{
if (millis() - hueLastRequestSent > huePollIntervalMsTemp)
{
sendHuePoll(false);
}
}
}
bool setupHue()
{
if (WiFi.status() == WL_CONNECTED) //setup needed
{
if (hueApiKey.length()>20) //api key is probably ok
{
if (sendHuePoll(false))
{
huePollingEnabled = true;
return true;
}
if (hueError.charAt(0) == 'R' || hueError.charAt(0) == 'I') return false; //can't connect
delay(20);
}
sendHuePoll(true); //new API key
if (hueError.charAt(0) != 'C') return false; //still some error
delay(20);
if (sendHuePoll(false))
{
huePollingEnabled = true;
return true;
}
return false;
}
else return false;
return true;
}
bool sendHuePoll(bool sAuth)
{
bool st;
hueClient.setReuse(true);
hueClient.setTimeout(250);
String hueURL = "http://";
hueURL += hueIP.toString();
hueURL += "/api/";
if (!sAuth) {
hueURL += hueApiKey;
hueURL += "/lights/" + String(huePollLightId);
}
hueClient.begin(hueURL);
int httpCode = (sAuth)? hueClient.POST("{\"devicetype\":\"wled#esp\"}"):hueClient.GET();
//TODO this request may block operation for ages
if (httpCode>0){
st = handleHueResponse(hueClient.getString(),sAuth);
} else {
hueError = "Request timed out";
st = false;
}
if (!st){ //error
if (hueFailCount<7) huePollIntervalMsTemp*=2; // only poll every 5min when unable to connect
hueFailCount++;
if (hueFailCount > 150) huePollingEnabled = false; //disable after many hours offline
}
hueLastRequestSent = millis();
return st;
}
bool handleHueResponse(String hueResp, bool isAuth)
{
DEBUG_PRINTLN(hueApiKey);
DEBUG_PRINTLN(hueResp);
if (hueResp.indexOf("error")>0)//hue bridge returned error
{
int hueErrorCode = getJsonValue(&hueResp,"type").toInt();
switch (hueErrorCode)
{
case 1: hueError = "Unauthorized"; break;
case 3: hueError = "Invalid light ID"; break;
case 101: hueError = "Link button not pressed"; break;
default: hueError = "Bridge Error " + String(hueErrorCode);
}
return false;
}
if (isAuth)
{
String tempApi = getJsonValue(&hueResp,"username");
if (tempApi.length()>0)
{
hueApiKey = tempApi;
return true;
}
hueError = "Invalid response";
return false;
}
float hueX=0, hueY=0;
uint16_t hueHue=0, hueCt=0;
uint8_t hueBri=0, hueSat=0, hueColormode=0;
if (getJsonValue(&hueResp,"on").charAt(0) == 't')
{
String tempV = getJsonValue(&hueResp,"bri");
if (tempV.length()>0) //Dimmable device
{
hueBri = (tempV.toInt())+1;
tempV = getJsonValue(&hueResp,"colormode");
if (hueApplyColor && tempV.length()>0) //Color device
{
if (tempV.charAt(0) == 'x') //xy mode
{
tempV = getJsonValue(&hueResp,"xy");
if (tempV.length()>0) //valid
{
hueColormode = 1;
hueX = tempV.toFloat();
tempV = tempV.substring(tempV.indexOf(',')+1);
hueY = tempV.toFloat();
}
} else if (tempV.charAt(0) == 'h') //hs mode
{
tempV = getJsonValue(&hueResp,"hue");
if (tempV.length()>0) //valid
{
hueColormode = 2;
hueHue = tempV.toInt();
tempV = getJsonValue(&hueResp,"sat");
if (tempV.length()>0) //valid
{
hueSat = tempV.toInt();
}
}
} else //ct mode
{
tempV = getJsonValue(&hueResp,"\"ct"); //dirty hack to not get effect value instead
if (tempV.length()>0) //valid
{
hueColormode = 3;
hueCt = tempV.toInt();
}
}
}
} else //On/Off device
{
hueBri = bri_last;
}
} else
{
hueBri = 0;
}
hueFailCount = 0;
huePollIntervalMsTemp = huePollIntervalMs;
hueError = "Connected";
//applying vals
if (hueBri != hueBriLast)
{
bri = hueBri;
if (hueApplyOnOff)
{
if (hueBri==0) {bri = 0;}
else if (bri==0 && hueBri>0) bri = bri_last;
}
if (hueApplyBri)
{
if (hueBri>0) bri = hueBri;
}
hueBriLast = hueBri;
}
if (hueApplyColor)
{
switch(hueColormode)
{
case 1: if (hueX != hueXLast || hueY != hueYLast) colorXYtoRGB(hueX,hueY,col); hueXLast = hueX; hueYLast = hueY; break;
case 2: if (hueHue != hueHueLast || hueSat != hueSatLast) colorHStoRGB(hueHue,hueSat,col); hueHueLast = hueHue; hueSatLast = hueSat; break;
case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,col); hueCtLast = hueCt; break;
}
}
colorUpdated(7);
return true;
}
String getJsonValue(String* req, String key)
{
//TODO may replace with ArduinoJSON if too complex
//this is horribly inefficient and designed to work only in this case
uint16_t pos = req->indexOf(key);
String b = req->substring(pos + key.length()+2);
if (b.charAt(0)=='\"') //is string
{
return b.substring(1,b.substring(1).indexOf('\"')+1);
} else if (b.charAt(0)=='[') //is array
{
return b.substring(1,b.indexOf(']'));
} else //is primitive type
{
return b.substring(0,b.indexOf(',')); //this works only if value not last
}
return "";
}