 89afdd2b17
			
		
	
	89afdd2b17
	
	
	
		
			
			LEDs now turn on instantly after boot Support Hardware ESP32 RMT NeopixelBus library Added setting to force old LED boot behavior (init after WiFi) Fixed overlay switching bug Fixed ? characters in usused macros Added transitionDelay to notifier packet
		
			
				
	
	
		
			208 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			208 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Sync to Philips hue lights
 | |
|  */
 | |
| 
 | |
| 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(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 {
 | |
|     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
 | |
|   }
 | |
|   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;
 | |
|   byte 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 = briLast;
 | |
|     }
 | |
|   } 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 = briLast;
 | |
|     }
 | |
|     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 "";
 | |
| }
 | |
| 
 |