From c34ddb2bc3eecb4cb98b790d7c43b4e81db4ee9d Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 18 Feb 2019 22:34:21 +0100 Subject: [PATCH] Initial async hue client --- wled00/wled00.ino | 31 +++----- wled00/wled03_set.ino | 14 +--- wled00/wled05_init.ino | 3 +- wled00/wled15_hue.ino | 160 +++++++++++++++++++++------------------ wled00/wled18_server.ino | 11 +-- 5 files changed, 102 insertions(+), 117 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index d0cfb708..7c5c0fe2 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -80,7 +80,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1902173 +#define VERSION 1902181 char versionString[] = "0.8.4-dev"; @@ -272,7 +272,6 @@ unsigned long nightlightStartTime; byte briNlT = 0; //current nightlight brightness //brightness -bool offMode = false; unsigned long lastOnTime = 0; byte bri = briS; byte briOld = 0; @@ -307,13 +306,16 @@ bool showWelcomePage = false; //hue char hueError[25] = "Inactive"; -uint16_t hueFailCount = 0; +//uint16_t hueFailCount = 0; float hueXLast=0, hueYLast=0; uint16_t hueHueLast=0, hueCtLast=0; byte hueSatLast=0, hueBriLast=0; unsigned long hueLastRequestSent = 0; -unsigned long huePollIntervalMsTemp = huePollIntervalMs; -bool hueAttempt = false; +bool hueAuthRequired = false; +bool hueReceived = false; +bool hueStoreAllowed = false, hueNewKey = false; +//unsigned long huePollIntervalMsTemp = huePollIntervalMs; +//bool hueAttempt = false; //overlays byte overlayCurrent = overlayDefault; @@ -401,13 +403,9 @@ bool doReboot = false; //flag to initiate reboot from async handlers //server library objects AsyncWebServer server(80); -HTTPClient* hueClient = NULL; +AsyncClient* hueClient = NULL; AsyncMqttClient* mqtt = NULL; -#ifndef WLED_DISABLE_OTA -//ESP8266HTTPUpdateServer httpUpdater; -#endif - //udp interface objects WiFiUDP notifierUdp, rgbUdp; WiFiUDP ntpUdp; @@ -528,18 +526,7 @@ void loop() { handleBlynk(); } if (briT) lastOnTime = millis(); - if (millis() - lastOnTime < 600) - { - offMode = false; - strip.service(); - } else if (!offMode) - { - /*#if LEDPIN == 2 //turn off onboard LED - pinMode(2, OUTPUT); - digitalWrite(2, HIGH); - #endif*/ - offMode = true; - } + if (millis() - lastOnTime < 600) strip.service(); } //DEBUG serial logging diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index cf035152..0ec1a606 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -193,15 +193,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) hueApplyOnOff = request->hasArg("HO"); hueApplyBri = request->hasArg("HB"); hueApplyColor = request->hasArg("HC"); - if (request->hasArg("HP")) - { - if (!huePollingEnabled) hueAttempt = true; - if (!setupHue()) hueAttempt = true; - } else - { - huePollingEnabled = false; - strcpy(hueError,"Inactive"); - } + huePollingEnabled = request->hasArg("HP"); + hueStoreAllowed = true; + reconnectHue(); } //TIME @@ -494,7 +488,7 @@ bool handleSet(AsyncWebServerRequest *request, String req) if (id > 0) { if (id < 100) huePollLightId = id; - setupHue(); + reconnectHue(); } else { huePollingEnabled = false; } diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 08904430..d8fe3559 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -118,8 +118,7 @@ void wledInit() initBlynk(blynkApiKey); initE131(); - - hueClient = new HTTPClient(); + reconnectHue(); } else { e131Enabled = false; } diff --git a/wled00/wled15_hue.ino b/wled00/wled15_hue.ino index f8ce9206..6efd6611 100644 --- a/wled00/wled15_hue.ino +++ b/wled00/wled15_hue.ino @@ -4,75 +4,90 @@ #ifndef WLED_DISABLE_HUESYNC void handleHue() { - if (huePollingEnabled && WiFi.status() == WL_CONNECTED && hueClient != NULL) + if (hueClient != nullptr && millis() - hueLastRequestSent > huePollIntervalMs && WiFi.status() == WL_CONNECTED) { - if (millis() - hueLastRequestSent > huePollIntervalMsTemp) + hueLastRequestSent = millis(); + if (huePollingEnabled) { - sendHuePoll(false); + reconnectHue(); + } else { + hueClient->close(); + if (hueError[0] == 'A') strcpy(hueError,"Inactive"); + } + } + if (hueReceived) + { + colorUpdated(7); hueReceived = false; + if (hueStoreAllowed && hueNewKey) + { + saveSettingsToEEPROM(); //save api key + hueStoreAllowed = false; + hueNewKey = false; } } } -bool setupHue() +void reconnectHue() { - if (WiFi.status() == WL_CONNECTED) //setup needed - { - if (strlen(hueApiKey)>20) //api key is probably ok - { - if (sendHuePoll(false)) - { - huePollingEnabled = true; - return true; - } - if (hueError[0] == 'R' || hueError[0] == 'I') return false; //can't connect - delay(20); - } - sendHuePoll(true); //new API key - if (hueError[0] != 'C') return false; //still some error - delay(20); - if (sendHuePoll(false)) - { - huePollingEnabled = true; - return true; - } - return false; + if (WiFi.status() != WL_CONNECTED || !huePollingEnabled) return; + DEBUG_PRINTLN("Hue reconnect"); + if (hueClient == nullptr) { + hueClient = new AsyncClient(); + hueClient->onConnect(&onHueConnect, hueClient); + hueClient->onData(&onHueData, hueClient); + hueClient->onError(&onHueError, hueClient); + hueAuthRequired = (strlen(hueApiKey)<20); } - else return false; - return true; + if (hueClient->connecting()) return; //don't start multiple connections before timeout + if (hueClient->connected()) + { + if (hueClient->getRemoteAddress() == uint32_t(hueIP) && huePollingEnabled) {sendHuePoll(); return;} //already connected + hueClient->close(); return; + } + hueClient->connect(hueIP, 80); } -bool sendHuePoll(bool sAuth) +void onHueError(void* arg, AsyncClient* client, int8_t error) { - bool st; - hueClient->setReuse(true); - hueClient->setTimeout(450); - 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 { - strcpy(hueError,"Request timed out"); - st = false; - } - if (!st){ //error - if (huePollIntervalMsTemp<300000) huePollIntervalMsTemp*=2; // only poll every ~5min when unable to connect - hueFailCount++; - if (hueFailCount > 150) huePollingEnabled = false; //disable after many hours offline + DEBUG_PRINTLN("Hue err"); + strcpy(hueError,"Request timeout"); +} + +void onHueConnect(void* arg, AsyncClient* client) +{ + DEBUG_PRINTLN("Hue connect"); + sendHuePoll(); +} + +void onHueData(void* arg, AsyncClient* client, void *data, size_t len) +{ + if (len) handleHueResponse(String((char*)data)); +} + +void sendHuePoll() +{ + if (hueClient == nullptr || !hueClient->connected()) return; + String req = ""; + if (hueAuthRequired) + { + req += "POST /api HTTP/1.1\r\nHost: "; + req += hueIP.toString(); + req += "\r\nContent-Length: 25\r\n\r\n{\"devicetype\":\"wled#esp\"}"; + } else + { + req += "GET /api/"; + req += hueApiKey; + req += "/lights/" + String(huePollLightId); + req += " HTTP/1.1\r\nHost: "; + req += hueIP.toString(); + req += "\r\n\r\n"; } + hueClient->add(req.c_str(), req.length()); + hueClient->send(); hueLastRequestSent = millis(); - return st; } -bool handleHueResponse(String hueResp, bool isAuth) +void handleHueResponse(String hueResp) { DEBUG_PRINTLN(hueApiKey); DEBUG_PRINTLN(hueResp); @@ -81,36 +96,36 @@ bool handleHueResponse(String hueResp, bool isAuth) int hueErrorCode = getJsonValue(&hueResp,"type").toInt(); switch (hueErrorCode) { - case 1: strcpy(hueError,"Unauthorized"); break; - case 3: strcpy(hueError,"Invalid light ID"); break; - case 101: strcpy(hueError,"Link button not pressed"); break; + case 1: strcpy(hueError,"Unauthorized"); hueAuthRequired = true; break; + case 3: strcpy(hueError,"Invalid light ID"); huePollingEnabled = false; break; + case 101: strcpy(hueError,"Link button not pressed"); hueAuthRequired = true; break; default: char coerr[18]; sprintf(coerr,"Bridge Error %i",hueErrorCode); strcpy(hueError,coerr); } - return false; + return; } - - if (isAuth) + + if (hueAuthRequired) { String tempApi = getJsonValue(&hueResp,"username"); if (tempApi.length()>0) { strcpy(hueApiKey,tempApi.c_str()); - return true; + hueAuthRequired = false; + hueNewKey = true; } - strcpy(hueError,"Invalid response"); - return false; + return; } float hueX=0, hueY=0; uint16_t hueHue=0, hueCt=0; byte hueBri=0, hueSat=0, hueColormode=0; - if (getJsonValue(&hueResp,"on").charAt(0) == 't') + if (getJsonValue(&hueResp,"\"on").charAt(0) == 't') { - String tempV = getJsonValue(&hueResp,"bri"); + String tempV = getJsonValue(&hueResp,"\"bri"); if (tempV.length()>0) //Dimmable device { hueBri = (tempV.toInt())+1; @@ -129,12 +144,12 @@ bool handleHueResponse(String hueResp, bool isAuth) } } else if (tempV.charAt(0) == 'h') //hs mode { - tempV = getJsonValue(&hueResp,"hue"); + tempV = getJsonValue(&hueResp,"\"hue"); if (tempV.length()>0) //valid { hueColormode = 2; hueHue = tempV.toInt(); - tempV = getJsonValue(&hueResp,"sat"); + tempV = getJsonValue(&hueResp,"\"sat"); if (tempV.length()>0) //valid { hueSat = tempV.toInt(); @@ -142,7 +157,7 @@ bool handleHueResponse(String hueResp, bool isAuth) } } else //ct mode { - tempV = getJsonValue(&hueResp,"\"ct"); //dirty hack to not get effect value instead + tempV = getJsonValue(&hueResp,"\"ct"); if (tempV.length()>0) //valid { hueColormode = 3; @@ -158,9 +173,7 @@ bool handleHueResponse(String hueResp, bool isAuth) { hueBri = 0; } - hueFailCount = 0; - huePollIntervalMsTemp = huePollIntervalMs; - strcpy(hueError,"Connected"); + strcpy(hueError,"Active"); //applying vals if (hueBri != hueBriLast) { @@ -184,8 +197,7 @@ bool handleHueResponse(String hueResp, bool isAuth) case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,col); hueCtLast = hueCt; break; } } - colorUpdated(7); - return true; + hueReceived = true; } String getJsonValue(String* req, String key) @@ -208,5 +220,5 @@ String getJsonValue(String* req, String key) } #else void handleHue(){} -bool setupHue(){return false;} +bool reconnectHue(){} #endif diff --git a/wled00/wled18_server.ino b/wled00/wled18_server.ino index 72c33924..096b02ca 100644 --- a/wled00/wled18_server.ino +++ b/wled00/wled18_server.ino @@ -50,13 +50,7 @@ void initServer() server.on("/settings/sync", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 4); - if (hueAttempt) - { - serveMessage(request, 200,"Hue setup result",hueError,253); - } else { - serveMessage(request, 200,"Sync settings saved.","Redirecting...",1); - } - hueAttempt = false; + serveMessage(request, 200,"Sync settings saved.","Redirecting...",1); }); server.on("/settings/time", HTTP_POST, [](AsyncWebServerRequest *request){ @@ -190,8 +184,7 @@ void initServer() //called when the url is not defined here, ajax-in; get-settings server.onNotFound([](AsyncWebServerRequest *request){ DEBUG_PRINTLN("Not-Found HTTP call:"); - DEBUG_PRINTLN("URI: " + server->uri()); - DEBUG_PRINTLN("Body: " + server->arg(0)); + DEBUG_PRINTLN("URI: " + request->url()); //make API CORS compatible if (request->method() == HTTP_OPTIONS)