diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index 2c4380a89..94d96227c 100644 Binary files a/wled00/data/settings_sync.htm and b/wled00/data/settings_sync.htm differ diff --git a/wled00/htmls00.h b/wled00/htmls00.h index 116bbb2dc..eb783a1a7 100644 --- a/wled00/htmls00.h +++ b/wled00/htmls00.h @@ -7,7 +7,7 @@ const char PAGE_index0[] PROGMEM = R"=====( -WLED 0.5.0 +WLED 0.5.1 )====="; diff --git a/wled00/htmls01.h b/wled00/htmls01.h index acc1343c4..ad6285f45 100644 --- a/wled00/htmls01.h +++ b/wled00/htmls01.h @@ -196,27 +196,25 @@ Receive Brightness,
Send notifications on button press:
Send Alexa notifications:
- +Send Philips Hue change notifications:

Alexa Voice Assistant

Emulate Alexa device:
Alexa invocation name:

Philips Hue

-Coming soon! Not yet implemented! - +Poll Hue light every ms:
Then, receive On/Off, Brightness, and Color
-After device color update, ignore Hue updates for minutes
-Hue status: Internal ESP error! --> + +Hue status: Internal ESP Error!
@@ -333,7 +331,7 @@ HTTP traffic is not encrypted. An attacker in the same network could intercept f
Enable ArduinoOTA:

About

-WLED version 0.5.0
+WLED version 0.5.1
(c) 2016-2018 Christian Schwinne
Licensed under the MIT license

Uses libraries:
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 148122787..1350114a4 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -3,7 +3,7 @@ */ /* * @title WLED project sketch - * @version 0.5.0 + * @version 0.5.1 * @author Christian Schwinne */ @@ -12,10 +12,12 @@ #include #include #include "src/dependencies/webserver/WebServer.h" +#include #else #include #include #include +#include #endif #include #include @@ -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(); diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index b3e9d3e98..e75c3b2df 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -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() { @@ -143,6 +153,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); @@ -315,6 +363,9 @@ void loadSettingsFromEEPROM(bool first) //custom macro memory (16 slots/ each 64byte) //1024-2047 reserved + + //user MOD memory + //2944 - 3071 reserved useHSB = useHSBDefault; diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 5cbff75dc..e7d5d0042 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -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) diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 76ee6999f..56ca9d749 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -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) { @@ -480,6 +524,19 @@ boolean handleSet(String req) effectUpdated = true; } } + + //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="); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index b58513432..3776ee71a 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -55,6 +55,13 @@ void wledInit() DEBUG_PRINTLN(""); 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())) { @@ -147,7 +154,13 @@ void wledInit() server.on("/settings/sync", HTTP_POST, [](){ handleSettingsSet(4); - serveMessage(200,"Sync settings saved.","Redirecting...",1); + 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, [](){ diff --git a/wled00/wled06_usermod.ino b/wled00/wled06_usermod.ino index 54fe5d539..f60d2e4ff 100644 --- a/wled00/wled06_usermod.ino +++ b/wled00/wled06_usermod.ino @@ -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() diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index 5d2256d3c..f82a618b2 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -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]; diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index d5ff1ca95..0dd5fdcee 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -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); diff --git a/wled00/wled14_colors.ino b/wled00/wled14_colors.ino index 52a6e98fd..eb2d60d92 100644 --- a/wled00/wled14_colors.ino +++ b/wled00/wled14_colors.ino @@ -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 diff --git a/wled00/wled15_hue.ino b/wled00/wled15_hue.ino index 388ce0c8d..1c10faa77 100644 --- a/wled00/wled15_hue.ino +++ b/wled00/wled15_hue.ino @@ -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 ""; +} +