Initial async hue client

This commit is contained in:
cschwinne 2019-02-18 22:34:21 +01:00
parent aa315f8472
commit c34ddb2bc3
5 changed files with 102 additions and 117 deletions

View File

@ -80,7 +80,7 @@
//version code in format yymmddb (b = daily build) //version code in format yymmddb (b = daily build)
#define VERSION 1902173 #define VERSION 1902181
char versionString[] = "0.8.4-dev"; char versionString[] = "0.8.4-dev";
@ -272,7 +272,6 @@ unsigned long nightlightStartTime;
byte briNlT = 0; //current nightlight brightness byte briNlT = 0; //current nightlight brightness
//brightness //brightness
bool offMode = false;
unsigned long lastOnTime = 0; unsigned long lastOnTime = 0;
byte bri = briS; byte bri = briS;
byte briOld = 0; byte briOld = 0;
@ -307,13 +306,16 @@ bool showWelcomePage = false;
//hue //hue
char hueError[25] = "Inactive"; char hueError[25] = "Inactive";
uint16_t hueFailCount = 0; //uint16_t hueFailCount = 0;
float hueXLast=0, hueYLast=0; float hueXLast=0, hueYLast=0;
uint16_t hueHueLast=0, hueCtLast=0; uint16_t hueHueLast=0, hueCtLast=0;
byte hueSatLast=0, hueBriLast=0; byte hueSatLast=0, hueBriLast=0;
unsigned long hueLastRequestSent = 0; unsigned long hueLastRequestSent = 0;
unsigned long huePollIntervalMsTemp = huePollIntervalMs; bool hueAuthRequired = false;
bool hueAttempt = false; bool hueReceived = false;
bool hueStoreAllowed = false, hueNewKey = false;
//unsigned long huePollIntervalMsTemp = huePollIntervalMs;
//bool hueAttempt = false;
//overlays //overlays
byte overlayCurrent = overlayDefault; byte overlayCurrent = overlayDefault;
@ -401,13 +403,9 @@ bool doReboot = false; //flag to initiate reboot from async handlers
//server library objects //server library objects
AsyncWebServer server(80); AsyncWebServer server(80);
HTTPClient* hueClient = NULL; AsyncClient* hueClient = NULL;
AsyncMqttClient* mqtt = NULL; AsyncMqttClient* mqtt = NULL;
#ifndef WLED_DISABLE_OTA
//ESP8266HTTPUpdateServer httpUpdater;
#endif
//udp interface objects //udp interface objects
WiFiUDP notifierUdp, rgbUdp; WiFiUDP notifierUdp, rgbUdp;
WiFiUDP ntpUdp; WiFiUDP ntpUdp;
@ -528,18 +526,7 @@ void loop() {
handleBlynk(); handleBlynk();
} }
if (briT) lastOnTime = millis(); if (briT) lastOnTime = millis();
if (millis() - lastOnTime < 600) if (millis() - lastOnTime < 600) strip.service();
{
offMode = false;
strip.service();
} else if (!offMode)
{
/*#if LEDPIN == 2 //turn off onboard LED
pinMode(2, OUTPUT);
digitalWrite(2, HIGH);
#endif*/
offMode = true;
}
} }
//DEBUG serial logging //DEBUG serial logging

View File

@ -193,15 +193,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
hueApplyOnOff = request->hasArg("HO"); hueApplyOnOff = request->hasArg("HO");
hueApplyBri = request->hasArg("HB"); hueApplyBri = request->hasArg("HB");
hueApplyColor = request->hasArg("HC"); hueApplyColor = request->hasArg("HC");
if (request->hasArg("HP")) huePollingEnabled = request->hasArg("HP");
{ hueStoreAllowed = true;
if (!huePollingEnabled) hueAttempt = true; reconnectHue();
if (!setupHue()) hueAttempt = true;
} else
{
huePollingEnabled = false;
strcpy(hueError,"Inactive");
}
} }
//TIME //TIME
@ -494,7 +488,7 @@ bool handleSet(AsyncWebServerRequest *request, String req)
if (id > 0) if (id > 0)
{ {
if (id < 100) huePollLightId = id; if (id < 100) huePollLightId = id;
setupHue(); reconnectHue();
} else { } else {
huePollingEnabled = false; huePollingEnabled = false;
} }

View File

@ -118,8 +118,7 @@ void wledInit()
initBlynk(blynkApiKey); initBlynk(blynkApiKey);
initE131(); initE131();
reconnectHue();
hueClient = new HTTPClient();
} else { } else {
e131Enabled = false; e131Enabled = false;
} }

View File

@ -4,75 +4,90 @@
#ifndef WLED_DISABLE_HUESYNC #ifndef WLED_DISABLE_HUESYNC
void handleHue() 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 (WiFi.status() != WL_CONNECTED || !huePollingEnabled) return;
{ DEBUG_PRINTLN("Hue reconnect");
if (strlen(hueApiKey)>20) //api key is probably ok if (hueClient == nullptr) {
{ hueClient = new AsyncClient();
if (sendHuePoll(false)) hueClient->onConnect(&onHueConnect, hueClient);
{ hueClient->onData(&onHueData, hueClient);
huePollingEnabled = true; hueClient->onError(&onHueError, hueClient);
return true; hueAuthRequired = (strlen(hueApiKey)<20);
}
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;
} }
else return false; if (hueClient->connecting()) return; //don't start multiple connections before timeout
return true; 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; DEBUG_PRINTLN("Hue err");
hueClient->setReuse(true); strcpy(hueError,"Request timeout");
hueClient->setTimeout(450); }
String hueURL = "http://";
hueURL += hueIP.toString(); void onHueConnect(void* arg, AsyncClient* client)
hueURL += "/api/"; {
if (!sAuth) { DEBUG_PRINTLN("Hue connect");
hueURL += hueApiKey; sendHuePoll();
hueURL += "/lights/" + String(huePollLightId); }
}
hueClient->begin(hueURL); void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
int httpCode = (sAuth)? hueClient->POST("{\"devicetype\":\"wled#esp\"}"):hueClient->GET(); {
//TODO this request may block operation for ages if (len) handleHueResponse(String((char*)data));
}
if (httpCode>0){
st = handleHueResponse(hueClient->getString(),sAuth); void sendHuePoll()
} else { {
strcpy(hueError,"Request timed out"); if (hueClient == nullptr || !hueClient->connected()) return;
st = false; String req = "";
} if (hueAuthRequired)
if (!st){ //error {
if (huePollIntervalMsTemp<300000) huePollIntervalMsTemp*=2; // only poll every ~5min when unable to connect req += "POST /api HTTP/1.1\r\nHost: ";
hueFailCount++; req += hueIP.toString();
if (hueFailCount > 150) huePollingEnabled = false; //disable after many hours offline 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(); hueLastRequestSent = millis();
return st;
} }
bool handleHueResponse(String hueResp, bool isAuth) void handleHueResponse(String hueResp)
{ {
DEBUG_PRINTLN(hueApiKey); DEBUG_PRINTLN(hueApiKey);
DEBUG_PRINTLN(hueResp); DEBUG_PRINTLN(hueResp);
@ -81,36 +96,36 @@ bool handleHueResponse(String hueResp, bool isAuth)
int hueErrorCode = getJsonValue(&hueResp,"type").toInt(); int hueErrorCode = getJsonValue(&hueResp,"type").toInt();
switch (hueErrorCode) switch (hueErrorCode)
{ {
case 1: strcpy(hueError,"Unauthorized"); break; case 1: strcpy(hueError,"Unauthorized"); hueAuthRequired = true; break;
case 3: strcpy(hueError,"Invalid light ID"); break; case 3: strcpy(hueError,"Invalid light ID"); huePollingEnabled = false; break;
case 101: strcpy(hueError,"Link button not pressed"); break; case 101: strcpy(hueError,"Link button not pressed"); hueAuthRequired = true; break;
default: default:
char coerr[18]; char coerr[18];
sprintf(coerr,"Bridge Error %i",hueErrorCode); sprintf(coerr,"Bridge Error %i",hueErrorCode);
strcpy(hueError,coerr); strcpy(hueError,coerr);
} }
return false; return;
} }
if (isAuth) if (hueAuthRequired)
{ {
String tempApi = getJsonValue(&hueResp,"username"); String tempApi = getJsonValue(&hueResp,"username");
if (tempApi.length()>0) if (tempApi.length()>0)
{ {
strcpy(hueApiKey,tempApi.c_str()); strcpy(hueApiKey,tempApi.c_str());
return true; hueAuthRequired = false;
hueNewKey = true;
} }
strcpy(hueError,"Invalid response"); return;
return false;
} }
float hueX=0, hueY=0; float hueX=0, hueY=0;
uint16_t hueHue=0, hueCt=0; uint16_t hueHue=0, hueCt=0;
byte hueBri=0, hueSat=0, hueColormode=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 if (tempV.length()>0) //Dimmable device
{ {
hueBri = (tempV.toInt())+1; hueBri = (tempV.toInt())+1;
@ -129,12 +144,12 @@ bool handleHueResponse(String hueResp, bool isAuth)
} }
} else if (tempV.charAt(0) == 'h') //hs mode } else if (tempV.charAt(0) == 'h') //hs mode
{ {
tempV = getJsonValue(&hueResp,"hue"); tempV = getJsonValue(&hueResp,"\"hue");
if (tempV.length()>0) //valid if (tempV.length()>0) //valid
{ {
hueColormode = 2; hueColormode = 2;
hueHue = tempV.toInt(); hueHue = tempV.toInt();
tempV = getJsonValue(&hueResp,"sat"); tempV = getJsonValue(&hueResp,"\"sat");
if (tempV.length()>0) //valid if (tempV.length()>0) //valid
{ {
hueSat = tempV.toInt(); hueSat = tempV.toInt();
@ -142,7 +157,7 @@ bool handleHueResponse(String hueResp, bool isAuth)
} }
} else //ct mode } else //ct mode
{ {
tempV = getJsonValue(&hueResp,"\"ct"); //dirty hack to not get effect value instead tempV = getJsonValue(&hueResp,"\"ct");
if (tempV.length()>0) //valid if (tempV.length()>0) //valid
{ {
hueColormode = 3; hueColormode = 3;
@ -158,9 +173,7 @@ bool handleHueResponse(String hueResp, bool isAuth)
{ {
hueBri = 0; hueBri = 0;
} }
hueFailCount = 0; strcpy(hueError,"Active");
huePollIntervalMsTemp = huePollIntervalMs;
strcpy(hueError,"Connected");
//applying vals //applying vals
if (hueBri != hueBriLast) if (hueBri != hueBriLast)
{ {
@ -184,8 +197,7 @@ bool handleHueResponse(String hueResp, bool isAuth)
case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,col); hueCtLast = hueCt; break; case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,col); hueCtLast = hueCt; break;
} }
} }
colorUpdated(7); hueReceived = true;
return true;
} }
String getJsonValue(String* req, String key) String getJsonValue(String* req, String key)
@ -208,5 +220,5 @@ String getJsonValue(String* req, String key)
} }
#else #else
void handleHue(){} void handleHue(){}
bool setupHue(){return false;} bool reconnectHue(){}
#endif #endif

View File

@ -50,13 +50,7 @@ void initServer()
server.on("/settings/sync", HTTP_POST, [](AsyncWebServerRequest *request){ server.on("/settings/sync", HTTP_POST, [](AsyncWebServerRequest *request){
handleSettingsSet(request, 4); handleSettingsSet(request, 4);
if (hueAttempt) serveMessage(request, 200,"Sync settings saved.","Redirecting...",1);
{
serveMessage(request, 200,"Hue setup result",hueError,253);
} else {
serveMessage(request, 200,"Sync settings saved.","Redirecting...",1);
}
hueAttempt = false;
}); });
server.on("/settings/time", HTTP_POST, [](AsyncWebServerRequest *request){ 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 //called when the url is not defined here, ajax-in; get-settings
server.onNotFound([](AsyncWebServerRequest *request){ server.onNotFound([](AsyncWebServerRequest *request){
DEBUG_PRINTLN("Not-Found HTTP call:"); DEBUG_PRINTLN("Not-Found HTTP call:");
DEBUG_PRINTLN("URI: " + server->uri()); DEBUG_PRINTLN("URI: " + request->url());
DEBUG_PRINTLN("Body: " + server->arg(0));
//make API CORS compatible //make API CORS compatible
if (request->method() == HTTP_OPTIONS) if (request->method() == HTTP_OPTIONS)