mirror of
https://github.com/wled/WLED.git
synced 2025-07-26 12:16:40 +00:00
Added Philips Hue synchronization
Added HU and SA API calls to set color via Hue and Saturation values
This commit is contained in:
parent
1b0d735e50
commit
e7e11b8bd2
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -196,27 +196,25 @@ Receive <input type="checkbox" name="NRCBR">Brightness, <input type="checkbox" n
|
|||||||
Send notifications on direct change: <input type="checkbox" name="NSDIR"> <br>
|
Send notifications on direct change: <input type="checkbox" name="NSDIR"> <br>
|
||||||
Send notifications on button press: <input type="checkbox" name="NSBTN"> <br>
|
Send notifications on button press: <input type="checkbox" name="NSBTN"> <br>
|
||||||
Send Alexa notifications: <input type="checkbox" name="NSALX"> <br>
|
Send Alexa notifications: <input type="checkbox" name="NSALX"> <br>
|
||||||
<!--Send Philips Hue change notifications: <input type="checkbox" name="NSHUE">-->
|
Send Philips Hue change notifications: <input type="checkbox" name="NSHUE">
|
||||||
<h3>Alexa Voice Assistant</h3>
|
<h3>Alexa Voice Assistant</h3>
|
||||||
Emulate Alexa device: <input type="checkbox" name="ALEXA"> <br>
|
Emulate Alexa device: <input type="checkbox" name="ALEXA"> <br>
|
||||||
Alexa invocation name: <input name="AINVN" maxlength="32"><br>
|
Alexa invocation name: <input name="AINVN" maxlength="32"><br>
|
||||||
<h3>Philips Hue</h3>
|
<h3>Philips Hue</h3>
|
||||||
Coming soon! Not yet implemented!
|
<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br>
|
||||||
<!--<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br>
|
|
||||||
Hue Bridge IP:<br>
|
Hue Bridge IP:<br>
|
||||||
<input name="HUIP0" type="number" min="0" max="255" required> .
|
<input name="HUIP0" type="number" min="0" max="255" required> .
|
||||||
<input name="HUIP1" type="number" min="0" max="255" required> .
|
<input name="HUIP1" type="number" min="0" max="255" required> .
|
||||||
<input name="HUIP2" type="number" min="0" max="255" required> .
|
<input name="HUIP2" type="number" min="0" max="255" required> .
|
||||||
<input name="HUIP3" type="number" min="0" max="255" required> <br>
|
<input name="HUIP3" type="number" min="0" max="255" required> <br>
|
||||||
<b>For successful pairing, press the pushlink button on the bridge, then save this page!</b><br>
|
<b>Press the pushlink button on the bridge, after that save this page!</b><br>
|
||||||
(when first connecting)<br>
|
(when first connecting)<br>
|
||||||
<i> Use 0 for group and light to turn off sending/receiving </i><br>
|
<!--Update Hue group <input name="HUEGR" type="number" min="0" max="99" required> <br>
|
||||||
Update Hue group <input name="HUEGR" type="number" min="0" max="99" required> <br>
|
Send <input type="checkbox" name="HUEIO"> On/Off, <input type="checkbox" name="HUEBR"> Brightness, and <input type="checkbox" name="HUECL"> Color<br>-->
|
||||||
Send <input type="checkbox" name="HUEIO"> On/Off, <input type="checkbox" name="HUEBR"> Brightness, and <input type="checkbox" name="HUECL"> Color<br>
|
Poll Hue light <input name="HUELI" type="number" min="1" max="99" required> every <input name="HUEPI" type="number" min="100" max="65000" required> ms: <input type="checkbox" name="HUEPL"><br>
|
||||||
Poll Hue light <input name="HUELI" type="number" min="0" max="99" required> every <input name="HUEPL" type="number" min="100" max="62000" required> ms<br>
|
|
||||||
Then, receive <input type="checkbox" name="HURIO"> On/Off, <input type="checkbox" name="HURBR"> Brightness, and <input type="checkbox" name="HURCL"> Color<br>
|
Then, receive <input type="checkbox" name="HURIO"> On/Off, <input type="checkbox" name="HURBR"> Brightness, and <input type="checkbox" name="HURCL"> Color<br>
|
||||||
After device color update, ignore Hue updates for <input name="HUELI" type="number" min="0" max="255" required> minutes<br>
|
<!--After device color update, ignore Hue updates for <input name="HUELI" type="number" min="0" max="255" required> minutes<br>-->
|
||||||
Hue status: <span class="hms"> Internal ESP error! </span>-->
|
Hue status: <span class="hms"> Internal ESP Error! </span>
|
||||||
<hr>
|
<hr>
|
||||||
<button type="button" onclick="B()">Back</button><button type="submit" name="SUBM">Save</button>
|
<button type="button" onclick="B()">Back</button><button type="submit" name="SUBM">Save</button>
|
||||||
</form>
|
</form>
|
||||||
@ -333,7 +331,7 @@ HTTP traffic is not encrypted. An attacker in the same network could intercept f
|
|||||||
<button type="button" onclick="U()">Manual OTA Update</button><br>
|
<button type="button" onclick="U()">Manual OTA Update</button><br>
|
||||||
Enable ArduinoOTA: <input type="checkbox" name="AROTA"><br>
|
Enable ArduinoOTA: <input type="checkbox" name="AROTA"><br>
|
||||||
<h3>About</h3>
|
<h3>About</h3>
|
||||||
<a href="https://github.com/Aircoookie/WLED">WLED</a> version 0.5.0<br>
|
<a href="https://github.com/Aircoookie/WLED">WLED</a> version 0.5.1<br>
|
||||||
(c) 2016-2018 Christian Schwinne <br>
|
(c) 2016-2018 Christian Schwinne <br>
|
||||||
<i>Licensed under the MIT license</i><br><br>
|
<i>Licensed under the MIT license</i><br><br>
|
||||||
<i>Uses libraries:</i><br>
|
<i>Uses libraries:</i><br>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* @title WLED project sketch
|
* @title WLED project sketch
|
||||||
* @version 0.5.0
|
* @version 0.5.1
|
||||||
* @author Christian Schwinne
|
* @author Christian Schwinne
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -12,10 +12,12 @@
|
|||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <ESPmDNS.h>
|
#include <ESPmDNS.h>
|
||||||
#include "src/dependencies/webserver/WebServer.h"
|
#include "src/dependencies/webserver/WebServer.h"
|
||||||
|
#include <HTTPClient.h>
|
||||||
#else
|
#else
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESP8266mDNS.h>
|
#include <ESP8266mDNS.h>
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
|
#include <ESP8266HTTPClient.h>
|
||||||
#endif
|
#endif
|
||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
@ -30,8 +32,8 @@
|
|||||||
#include "WS2812FX.h"
|
#include "WS2812FX.h"
|
||||||
|
|
||||||
//version in format yymmddb (b = daily build)
|
//version in format yymmddb (b = daily build)
|
||||||
#define VERSION 1802251
|
#define VERSION 1802273
|
||||||
const String versionString = "0.5.0";
|
const String versionString = "0.5.1";
|
||||||
|
|
||||||
//AP and OTA default passwords (change them!)
|
//AP and OTA default passwords (change them!)
|
||||||
String appass = "wled1234";
|
String appass = "wled1234";
|
||||||
@ -152,6 +154,22 @@ unsigned long countdownTime = 1514764800L;
|
|||||||
|
|
||||||
double transitionResolution = 0.011;
|
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
|
//Internal vars
|
||||||
byte col[]{0, 0, 0};
|
byte col[]{0, 0, 0};
|
||||||
byte col_old[]{0, 0, 0};
|
byte col_old[]{0, 0, 0};
|
||||||
@ -241,20 +259,13 @@ WebServer server(80);
|
|||||||
#else
|
#else
|
||||||
ESP8266WebServer server(80);
|
ESP8266WebServer server(80);
|
||||||
#endif
|
#endif
|
||||||
|
HTTPClient hueClient;
|
||||||
ESP8266HTTPUpdateServer httpUpdater;
|
ESP8266HTTPUpdateServer httpUpdater;
|
||||||
WiFiUDP notifierUdp;
|
WiFiUDP notifierUdp;
|
||||||
WiFiUDP ntpUdp;
|
WiFiUDP ntpUdp;
|
||||||
|
|
||||||
WS2812FX strip = WS2812FX(LEDCOUNT);
|
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
|
#ifdef DEBUG
|
||||||
#define DEBUG_PRINT(x) Serial.print (x)
|
#define DEBUG_PRINT(x) Serial.print (x)
|
||||||
#define DEBUG_PRINTLN(x) Serial.println (x)
|
#define DEBUG_PRINTLN(x) Serial.println (x)
|
||||||
@ -333,6 +344,7 @@ void loop() {
|
|||||||
handleAlexa();
|
handleAlexa();
|
||||||
if (!arlsTimeout) //block stuff if WARLS is enabled
|
if (!arlsTimeout) //block stuff if WARLS is enabled
|
||||||
{
|
{
|
||||||
|
handleHue();
|
||||||
handleNightlight();
|
handleNightlight();
|
||||||
#ifdef USEOVERLAYS
|
#ifdef USEOVERLAYS
|
||||||
handleOverlays();
|
handleOverlays();
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
* Methods to handle saving and loading to non-volatile memory
|
* 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()
|
void clearEEPROM()
|
||||||
{
|
{
|
||||||
@ -143,6 +153,24 @@ void saveSettingsToEEPROM()
|
|||||||
{
|
{
|
||||||
EEPROM.write(i, cssFont.charAt(i-950));
|
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();
|
EEPROM.commit();
|
||||||
}
|
}
|
||||||
@ -297,6 +325,26 @@ void loadSettingsFromEEPROM(bool first)
|
|||||||
receiveNotificationEffects = receiveNotificationBrightness;
|
receiveNotificationEffects = receiveNotificationBrightness;
|
||||||
}
|
}
|
||||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
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);
|
bootPreset = EEPROM.read(389);
|
||||||
wifiLock = EEPROM.read(393);
|
wifiLock = EEPROM.read(393);
|
||||||
@ -315,6 +363,9 @@ void loadSettingsFromEEPROM(bool first)
|
|||||||
|
|
||||||
//custom macro memory (16 slots/ each 64byte)
|
//custom macro memory (16 slots/ each 64byte)
|
||||||
//1024-2047 reserved
|
//1024-2047 reserved
|
||||||
|
|
||||||
|
//user MOD memory
|
||||||
|
//2944 - 3071 reserved
|
||||||
|
|
||||||
useHSB = useHSBDefault;
|
useHSB = useHSBDefault;
|
||||||
|
|
||||||
|
@ -187,9 +187,21 @@ String getSettings(uint8_t subPage)
|
|||||||
resp += ds + "NRCFX" + c + receiveNotificationEffects +";";
|
resp += ds + "NRCFX" + c + receiveNotificationEffects +";";
|
||||||
resp += ds + "NSDIR" + c + notifyDirectDefault +";";
|
resp += ds + "NSDIR" + c + notifyDirectDefault +";";
|
||||||
resp += ds + "NSBTN" + c + notifyButton +";";
|
resp += ds + "NSBTN" + c + notifyButton +";";
|
||||||
|
resp += ds + "NSHUE" + c + notifyHue +";";
|
||||||
resp += ds + "ALEXA" + c + alexaEnabled +";";
|
resp += ds + "ALEXA" + c + alexaEnabled +";";
|
||||||
resp += ds + "AINVN" + v + "\"" + alexaInvocationName + "\";";
|
resp += ds + "AINVN" + v + "\"" + alexaInvocationName + "\";";
|
||||||
resp += ds + "NSALX" + c + alexaNotify +";";
|
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)
|
if (subPage == 5)
|
||||||
|
@ -281,6 +281,37 @@ void handleSettingsSet(uint8_t subPage)
|
|||||||
alexaEnabled = server.hasArg("ALEXA");
|
alexaEnabled = server.hasArg("ALEXA");
|
||||||
if (server.hasArg("AINVN")) alexaInvocationName = server.arg("AINVN");
|
if (server.hasArg("AINVN")) alexaInvocationName = server.arg("AINVN");
|
||||||
alexaNotify = server.hasArg("NSALX");
|
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
|
//TIME
|
||||||
@ -358,6 +389,19 @@ boolean handleSet(String req)
|
|||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
bri = req.substring(pos + 3).toInt();
|
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
|
//set red value
|
||||||
pos = req.indexOf("&R=");
|
pos = req.indexOf("&R=");
|
||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
@ -480,6 +524,19 @@ boolean handleSet(String req)
|
|||||||
effectUpdated = true;
|
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)
|
//set default control mode (0 - RGB, 1 - HSB)
|
||||||
pos = req.indexOf("MD=");
|
pos = req.indexOf("MD=");
|
||||||
|
@ -55,6 +55,13 @@ void wledInit()
|
|||||||
DEBUG_PRINTLN("");
|
DEBUG_PRINTLN("");
|
||||||
DEBUG_PRINT("Connected! IP address: ");
|
DEBUG_PRINT("Connected! IP address: ");
|
||||||
DEBUG_PRINTLN(WiFi.localIP());
|
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:
|
// Set up mDNS responder:
|
||||||
if (cmdns != NULL && !onlyAP && !MDNS.begin(cmdns.c_str())) {
|
if (cmdns != NULL && !onlyAP && !MDNS.begin(cmdns.c_str())) {
|
||||||
@ -147,7 +154,13 @@ void wledInit()
|
|||||||
|
|
||||||
server.on("/settings/sync", HTTP_POST, [](){
|
server.on("/settings/sync", HTTP_POST, [](){
|
||||||
handleSettingsSet(4);
|
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, [](){
|
server.on("/settings/time", HTTP_POST, [](){
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file allows you to add own functionality to WLED more easily
|
* 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()
|
void userBeginPreConnection()
|
||||||
|
@ -13,6 +13,7 @@ void notify(uint8_t callMode)
|
|||||||
case 2: if (!notifyButton) return; break;
|
case 2: if (!notifyButton) return; break;
|
||||||
case 4: if (!notifyDirect) return; break;
|
case 4: if (!notifyDirect) return; break;
|
||||||
case 6: if (!notifyDirect) return; break; //fx change
|
case 6: if (!notifyDirect) return; break; //fx change
|
||||||
|
case 7: if (!notifyHue) return; break;
|
||||||
default: return;
|
default: return;
|
||||||
}
|
}
|
||||||
byte udpOut[WLEDPACKETSIZE];
|
byte udpOut[WLEDPACKETSIZE];
|
||||||
|
@ -51,7 +51,7 @@ bool colorChanged()
|
|||||||
|
|
||||||
void colorUpdated(int callMode)
|
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 (!colorChanged())
|
||||||
{
|
{
|
||||||
if (callMode == 6) notify(6);
|
if (callMode == 6) notify(6);
|
||||||
|
@ -3,25 +3,118 @@
|
|||||||
*/
|
*/
|
||||||
void colorCTtoRGB(uint16_t mired, uint8_t* rgb) //white spectrum to rgb
|
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)
|
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 z = 1.0f - x - y;
|
||||||
//float Y = 1.0f; // Brightness, we handle this separately
|
|
||||||
float X = (1.0f / y) * x;
|
float X = (1.0f / y) * x;
|
||||||
float Z = (1.0f / y) * z;
|
float Z = (1.0f / y) * z;
|
||||||
rgb[0] = (int)(X * 1.656492f - 0.354851f - Z * 0.255038f);
|
float r = (int)255*(X * 1.656492f - 0.354851f - Z * 0.255038f);
|
||||||
rgb[1] = (int)(-X * 0.707196f + 1.655397f + Z * 0.036152f);
|
float g = (int)255*(-X * 0.707196f + 1.655397f + Z * 0.036152f);
|
||||||
rgb[2] = (int)(X * 0.051713f - 0.121364f + Z * 1.011530f);
|
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
|
void colorRGBtoRGBW(uint8_t* rgb, uint8_t* rgbw){} //rgb to rgbw, not imlemented yet
|
||||||
|
@ -1,4 +1,207 @@
|
|||||||
/*
|
/*
|
||||||
* Sync to Philips hue lights
|
* 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 "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user