Initial async hue client
This commit is contained in:
parent
aa315f8472
commit
c34ddb2bc3
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -118,8 +118,7 @@ void wledInit()
|
|||||||
|
|
||||||
initBlynk(blynkApiKey);
|
initBlynk(blynkApiKey);
|
||||||
initE131();
|
initE131();
|
||||||
|
reconnectHue();
|
||||||
hueClient = new HTTPClient();
|
|
||||||
} else {
|
} else {
|
||||||
e131Enabled = false;
|
e131Enabled = false;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user