Skip to content

Commit 2ac1a6b

Browse files
authored
Merge pull request diyhue#14 from mariusmotea/develop
two-way light sync + minor improvements
2 parents 09d33ed + 4052c45 commit 2ac1a6b

File tree

9 files changed

+365
-265
lines changed

9 files changed

+365
-265
lines changed

BridgeEmulator/HueEmulator.py

+36-13
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,14 @@ def rules_processor(scheduler=False):
161161
for action in bridge_config["rules"][rule]["actions"]:
162162
Thread(target=sendRequest, args=["/api/" + bridge_config["rules"][rule]["owner"] + action["address"], action["method"], json.dumps(action["body"])]).start()
163163

164-
def sendRequest(url, method, data):
164+
def sendRequest(url, method, data, time_out=3):
165165
if not url.startswith( 'http://' ):
166166
url = "http://127.0.0.1" + url
167167
opener = urllib2.build_opener(urllib2.HTTPHandler)
168168
request = urllib2.Request(url, data=data)
169169
request.add_header("Content-Type",'application/json')
170170
request.get_method = lambda: method
171-
response = opener.open(request, timeout=3).read()
171+
response = opener.open(request, timeout=time_out).read()
172172
return response
173173

174174
def convert_xy(x, y, bri): #needed for milight hub that don't work with xy values
@@ -246,7 +246,7 @@ def sendLightRequest(light, data):
246246
else:
247247
url += "&" + key + "=" + str(value)
248248
elif lights_address[light]["protocol"] == "hue": #Original Hue light
249-
url = "http://" + lights_address[light]["ip"] + "/api/" + lights_address[light]["username"] + "/lights/" + lights_address[light]["light_id"] + "/state";
249+
url = "http://" + lights_address[light]["ip"] + "/api/" + lights_address[light]["username"] + "/lights/" + lights_address[light]["light_id"] + "/state"
250250
method = 'PUT'
251251
payload = data
252252
elif lights_address[light]["protocol"] == "milight": #MiLight bulb
@@ -358,9 +358,21 @@ def scan_for_lights(): #scan for ESP8266 lights and strips
358358
except Exception, e:
359359
print(ip + " is unknow device " + str(e))
360360

361-
def syncWithTradfri(): #update Hue Bridge lights states from Ikea Tradfri gateway
361+
def syncWithLights(): #update Hue Bridge lights states
362362
for light in lights_address:
363-
if lights_address[light]["protocol"] == "ikea_tradfri":
363+
if lights_address[light]["protocol"] == "native":
364+
try:
365+
light_data = json.loads(sendRequest("http://" + lights_address[light]["ip"] + "/get?light=" + str(lights_address[light]["light_nr"]), "GET", "{}", 0.5))
366+
except:
367+
bridge_config["lights"][light]["state"]["reachable"] = False
368+
print("request error")
369+
else:
370+
bridge_config["lights"][light]["state"]["reachable"] = True
371+
bridge_config["lights"][light]["state"].update(light_data)
372+
elif lights_address[light]["protocol"] == "hue":
373+
light_data = json.loads(sendRequest("http://" + lights_address[light]["ip"] + "/api/" + lights_address[light]["username"] + "/lights/" + lights_address[light]["light_id"] + "/state"), "GET", "{}", 1))
374+
bridge_config["lights"][light]["state"].update(light_data)
375+
elif lights_address[light]["protocol"] == "ikea_tradfri":
364376
light_stats = json.loads(check_output("./coap-client-linux -m get -u \"Client_identity\" -k \"" + lights_address[light]["security_code"] + "\" \"coaps://" + lights_address[light]["ip"] + ":5684/15001/" + str(lights_address[light]["device_id"]) +"\"", shell=True).split("\n")[3])
365377
bridge_config["lights"][light]["state"]["on"] = bool(light_stats["3311"][0]["5850"])
366378
bridge_config["lights"][light]["state"]["bri"] = light_stats["3311"][0]["5851"]
@@ -371,7 +383,6 @@ def syncWithTradfri(): #update Hue Bridge lights states from Ikea Tradfri gatewa
371383
elif light_stats["3311"][0]["5706"] == "efd275":
372384
bridge_config["lights"][light]["state"]["ct"] = 470
373385

374-
375386
def description():
376387
return """<root xmlns=\"urn:schemas-upnp-org:device-1-0\">
377388
<specVersion>
@@ -502,7 +513,11 @@ def update_all_lights():
502513
payload = {}
503514
payload["on"] = bridge_config["lights"][light]["state"]["on"]
504515
payload["bri"] = bridge_config["lights"][light]["state"]["bri"]
505-
payload[bridge_config["lights"][light]["state"]["colormode"]] = bridge_config["lights"][light]["state"][bridge_config["lights"][light]["state"]["colormode"]]
516+
if bridge_config["lights"][light]["state"]["colormode"] in ["xy", "ct"]:
517+
payload[bridge_config["lights"][light]["state"]["colormode"]] = bridge_config["lights"][light]["state"][bridge_config["lights"][light]["state"]["colormode"]]
518+
elif bridge_config["lights"][light]["state"]["colormode"] == "hs":
519+
payload["hue"] = bridge_config["lights"][light]["state"]["hue"]
520+
payload["sat"] = bridge_config["lights"][light]["state"]["sat"]
506521
Thread(target=sendLightRequest, args=[light, payload]).start()
507522
sleep(0.5)
508523
print("update status for light " + light)
@@ -518,7 +533,7 @@ def do_GET(self):
518533
if self.path == '/description.xml':
519534
self.wfile.write(description())
520535
elif self.path == '/favicon.ico':
521-
self.send_response(404)
536+
self.wfile.write("file not found")
522537
elif self.path.startswith("/tradfri"): #setup Tradfri gateway
523538
get_parameters = parse_qs(urlparse(self.path).query)
524539
if "code" in get_parameters:
@@ -623,7 +638,7 @@ def do_GET(self):
623638
self.wfile.write(json.dumps(bridge_config))
624639
elif len(url_pices) == 4: #print specified object config
625640
if url_pices[3] == "lights": #add changes from IKEA Tradfri gateway to bridge
626-
syncWithTradfri()
641+
syncWithLights()
627642
self.wfile.write(json.dumps(bridge_config[url_pices[3]]))
628643
elif len(url_pices) == 5:
629644
if url_pices[4] == "new": #return new lights and sensors only
@@ -728,7 +743,13 @@ def do_PUT(self):
728743
del bridge_config["scenes"][url_pices[4]]["lightstates"][light]["ct"]
729744
elif "hue" in bridge_config["scenes"][url_pices[4]]["lightstates"][light]:
730745
del bridge_config["scenes"][url_pices[4]]["lightstates"][light]["hue"]
731-
bridge_config["scenes"][url_pices[4]]["lightstates"][light][bridge_config["lights"][light]["state"]["colormode"]] = bridge_config["lights"][light]["state"][bridge_config["lights"][light]["state"]["colormode"]]
746+
del bridge_config["scenes"][url_pices[4]]["lightstates"][light]["sat"]
747+
if bridge_config["lights"][light]["state"]["colormode"] in ["ct", "xy"]:
748+
bridge_config["scenes"][url_pices[4]]["lightstates"][light][bridge_config["lights"][light]["state"]["colormode"]] = bridge_config["lights"][light]["state"][bridge_config["lights"][light]["state"]["colormode"]]
749+
elif bridge_config["lights"][light]["state"]["colormode"] == "hs":
750+
bridge_config["scenes"][url_pices[4]]["lightstates"][light]["hue"] = bridge_config["lights"][light]["state"]["hue"]
751+
bridge_config["scenes"][url_pices[4]]["lightstates"][light]["sat"] = bridge_config["lights"][light]["state"]["sat"]
752+
732753
if url_pices[3] == "sensors":
733754
for key, value in put_dictionary.iteritems():
734755
bridge_config[url_pices[3]][url_pices[4]][key].update(value)
@@ -744,8 +765,8 @@ def do_PUT(self):
744765
bridge_config["lights"][light]["state"]["colormode"] = "xy"
745766
elif "ct" in bridge_config["scenes"][put_dictionary["scene"]]["lightstates"][light]:
746767
bridge_config["lights"][light]["state"]["colormode"] = "ct"
747-
elif "hue" in bridge_config["scenes"][put_dictionary["scene"]]["lightstates"][light]:
748-
bridge_config["lights"][light]["state"]["colormode"] = "hue"
768+
elif "hue" or "sat" in bridge_config["scenes"][put_dictionary["scene"]]["lightstates"][light]:
769+
bridge_config["lights"][light]["state"]["colormode"] = "hs"
749770
Thread(target=sendLightRequest, args=[light, bridge_config["scenes"][put_dictionary["scene"]]["lightstates"][light]]).start()
750771
update_group_stats(light)
751772
elif "bri_inc" in put_dictionary:
@@ -779,8 +800,10 @@ def do_PUT(self):
779800
elif url_pices[3] == "lights": #state is applied to a light
780801
Thread(target=sendLightRequest, args=[url_pices[4], put_dictionary]).start()
781802
for key in put_dictionary.iterkeys():
782-
if key in ["ct", "xy", "hue"]: #colormode must be set by bridge
803+
if key in ["ct", "xy"]: #colormode must be set by bridge
783804
bridge_config["lights"][url_pices[4]]["state"]["colormode"] = key
805+
elif key in ["hue", "sat"]:
806+
bridge_config["lights"][url_pices[4]]["state"]["colormode"] = "hs"
784807
update_group_stats(url_pices[4])
785808
if not url_pices[4] == "0": #group 0 is virtual, must not be saved in bridge configuration
786809
try:

Images/hue-map.png

304 KB
Loading

Lights/Arduino/GenericWifiHueLight/GenericWifiHueLight.ino

+40-28
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,29 @@ void apply_scene(uint8_t new_scene) {
218218
}
219219
}
220220

221+
void lightEngine() {
222+
for (uint8_t color = 0; color < pwm_channels; color++) {
223+
if (light_state) {
224+
if (rgbw[color] != current_rgbw[color] ) {
225+
in_transition = true;
226+
current_rgbw[color] += step_level[color];
227+
if ((step_level[color] > 0.0f && current_rgbw[color] > rgbw[color]) || (step_level[color] < 0.0f && current_rgbw[color] < rgbw[color])) current_rgbw[color] = rgbw[color];
228+
analogWrite(pins[color], (int)(current_rgbw[color] * 4));
229+
}
230+
} else {
231+
if (current_rgbw[color] != 0) {
232+
in_transition = true;
233+
current_rgbw[color] -= step_level[color];
234+
if (current_rgbw[color] < 0.0f) current_rgbw[color] = 0;
235+
analogWrite(pins[color], (int)(current_rgbw[color] * 4));
236+
}
237+
}
238+
}
239+
if (in_transition) {
240+
delay(6);
241+
in_transition = false;
242+
}
243+
}
221244

222245
void setup() {
223246
EEPROM.begin(512);
@@ -226,8 +249,6 @@ void setup() {
226249
pins[1] = green_pin;
227250
pins[2] = blue_pin;
228251
pins[3] = white_pin;
229-
WiFiManager wifiManager;
230-
wifiManager.autoConnect("New Hue Light");
231252
analogWriteRange(1024);
232253
analogWriteFreq(4096);
233254

@@ -238,7 +259,13 @@ void setup() {
238259

239260
if (EEPROM.read(1) == 1 || (EEPROM.read(1) == 0 && EEPROM.read(0) == 1)) {
240261
light_state = true;
241-
} else {
262+
for (uint8_t i = 0; i < 200; i++) {
263+
lightEngine();
264+
}
265+
}
266+
WiFiManager wifiManager;
267+
wifiManager.autoConnect("New Hue Light");
268+
if (! light_state) {
242269
while (WiFi.status() != WL_CONNECTED) {
243270
analogWrite(pins[0], 10);
244271
delay(250);
@@ -413,7 +440,16 @@ void setup() {
413440
});
414441

415442
server.on("/get", []() {
416-
server.send(200, "text/plain", "{\"R\":" + (String)rgbw[0] + ", \"G\": " + (String)rgbw[1] + ", \"B\":" + (String)rgbw[2] + ", \"W\":" + (String)rgbw[3] + ", \"bri\":" + (String)bri + ", \"xy\": [" + (String)x + "," + (String)y + "], \"ct\":" + (String)ct + ", \"sat\": " + (String)sat + ", \"hue\": " + (String)hue + ", \"colormode\":" + color_mode + "}");
443+
String colormode;
444+
String power_status;
445+
power_status = light_state ? "true" : "false";
446+
if (color_mode == 1)
447+
colormode = "xy";
448+
else if (color_mode == 2)
449+
colormode = "ct";
450+
else if (color_mode == 3)
451+
colormode = "hs";
452+
server.send(200, "text/plain", "{\"on\": " + power_status + ", \"bri\": " + (String)bri + ", \"xy\": [" + (String)x + ", " + (String)y + "], \"ct\":" + (String)ct + ", \"sat\": " + (String)sat + ", \"hue\": " + (String)hue + ", \"colormode\": \"" + colormode + "\"}");
417453
});
418454

419455
server.on("/detect", []() {
@@ -582,30 +618,6 @@ void setup() {
582618
server.begin();
583619
}
584620

585-
void lightEngine() {
586-
for (uint8_t color = 0; color < pwm_channels; color++) {
587-
if (light_state) {
588-
if (rgbw[color] != current_rgbw[color] ) {
589-
in_transition = true;
590-
current_rgbw[color] += step_level[color];
591-
if ((step_level[color] > 0.0f && current_rgbw[color] > rgbw[color]) || (step_level[color] < 0.0f && current_rgbw[color] < rgbw[color])) current_rgbw[color] = rgbw[color];
592-
analogWrite(pins[color], (int)(current_rgbw[color] * 4));
593-
}
594-
} else {
595-
if (current_rgbw[color] != 0) {
596-
in_transition = true;
597-
current_rgbw[color] -= step_level[color];
598-
if (current_rgbw[color] < 0.0f) current_rgbw[color] = 0;
599-
analogWrite(pins[color], (int)(current_rgbw[color] * 4));
600-
}
601-
}
602-
}
603-
if (in_transition) {
604-
delay(6);
605-
in_transition = false;
606-
}
607-
}
608-
609621
void loop() {
610622
ArduinoOTA.handle();
611623
server.handleClient();

Lights/Arduino/SK6812HueStrip/SK6812HueStrip.ino

+54-46
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,47 @@ void apply_scene(uint8_t new_scene, uint8_t light) {
233233
}
234234
}
235235

236+
void lightEngine() {
237+
for (int i = 0; i < lightsCount; i++) {
238+
if (light_state[i]) {
239+
if (rgbw[i][0] != current_rgbw[i][0] || rgbw[i][1] != current_rgbw[i][1] || rgbw[i][2] != current_rgbw[i][2] || rgbw[i][3] != current_rgbw[i][3]) {
240+
in_transition = true;
241+
for (uint8_t k = 0; k <= 3; k++) {
242+
if (rgbw[i][k] != current_rgbw[i][k]) current_rgbw[i][k] += step_level[i][k];
243+
if ((step_level[i][k] > 0.0 && current_rgbw[i][k] > rgbw[i][k]) || (step_level[i][k] < 0.0 && current_rgbw[i][k] < rgbw[i][k])) current_rgbw[i][k] = rgbw[i][k];
244+
}
245+
for (int j = 0; j < pixelCount / lightsCount ; j++)
246+
{
247+
strip.SetPixelColor(j + i * pixelCount / lightsCount, RgbwColor((int)current_rgbw[i][0], (int)current_rgbw[i][1], (int)current_rgbw[i][2], (int)current_rgbw[i][3]));
248+
}
249+
strip.Show();
250+
}
251+
} else {
252+
if (current_rgbw[i][0] != 0 || current_rgbw[i][1] != 0 || current_rgbw[i][2] != 0 || current_rgbw[i][3] != 0) {
253+
in_transition = true;
254+
for (uint8_t k = 0; k <= 3; k++) {
255+
if (current_rgbw[i][k] != 0) current_rgbw[i][k] -= step_level[i][k];
256+
if (current_rgbw[i][k] < 0) current_rgbw[i][k] = 0;
257+
}
258+
for (int j = 0; j < pixelCount / lightsCount ; j++)
259+
{
260+
strip.SetPixelColor(j + i * pixelCount / lightsCount, RgbwColor((int)current_rgbw[i][0], (int)current_rgbw[i][1], (int)current_rgbw[i][2], (int)current_rgbw[i][3]));
261+
}
262+
strip.Show();
263+
}
264+
}
265+
}
266+
if (in_transition) {
267+
delay(6);
268+
in_transition = false;
269+
}
270+
}
236271

237272
void setup() {
238273
strip.Begin();
239274
strip.Show();
240275
EEPROM.begin(512);
241276

242-
WiFiManager wifiManager;
243-
wifiManager.autoConnect("New Hue Light");
244-
245277
//WiFi.config(strip_ip, gateway_ip, subnet_mask);
246278

247279
for (uint8_t light = 0; light < lightsCount; light++) {
@@ -256,7 +288,13 @@ void setup() {
256288
for (int i = 0; i < lightsCount; i++) {
257289
light_state[i] = true;
258290
}
259-
} else {
291+
for (int j = 0; j < 200; j++) {
292+
lightEngine();
293+
}
294+
WiFiManager wifiManager;
295+
wifiManager.autoConnect("New Hue Light");
296+
}
297+
if (! light_state[0]) {
260298
infoLight(white);
261299
while (WiFi.status() != WL_CONNECTED) {
262300
infoLight(red);
@@ -436,12 +474,18 @@ void setup() {
436474

437475
server.on("/get", []() {
438476
uint8_t light;
439-
for (uint8_t i = 0; i < server.args(); i++) {
440-
if (server.argName(i) == "light") {
441-
light = server.arg(i).toInt() - 1;
442-
}
443-
}
444-
server.send(200, "text/plain", "{\"R\":" + (String)current_rgbw[light][0] + ", \"G\": " + (String)current_rgbw[light][1] + ", \"B\":" + (String)current_rgbw[light][2] + ", \"W\":" + (String)current_rgbw[light][3] + ", \"bri\":" + (String)bri[light] + ", \"xy\": [" + (String)x[light] + "," + (String)y[light] + "], \"ct\":" + (String)ct[light] + ", \"sat\": " + (String)sat[light] + ", \"hue\": " + (String)hue[light] + ", \"colormode\":" + color_mode[light] + "}");
477+
if (server.hasArg("light"))
478+
light = server.arg("light").toInt() - 1;
479+
String colormode;
480+
String power_status;
481+
power_status = light_state[light] ? "true" : "false";
482+
if (color_mode[light] == 1)
483+
colormode = "xy";
484+
else if (color_mode[light] == 2)
485+
colormode = "ct";
486+
else if (color_mode[light] == 3)
487+
colormode = "hs";
488+
server.send(200, "text/plain", "{\"on\": " + power_status + ", \"bri\": " + (String)bri[light] + ", \"xy\": [" + (String)x[light] + ", " + (String)y[light] + "], \"ct\":" + (String)ct[light] + ", \"sat\": " + (String)sat[light] + ", \"hue\": " + (String)hue[light] + ", \"colormode\": \"" + colormode + "\"}");
445489
});
446490

447491
server.on("/detect", []() {
@@ -611,42 +655,6 @@ void setup() {
611655
server.begin();
612656
}
613657

614-
void lightEngine() {
615-
for (int i = 0; i < lightsCount; i++) {
616-
if (light_state[i]) {
617-
if (rgbw[i][0] != current_rgbw[i][0] || rgbw[i][1] != current_rgbw[i][1] || rgbw[i][2] != current_rgbw[i][2] || rgbw[i][3] != current_rgbw[i][3]) {
618-
in_transition = true;
619-
for (uint8_t k = 0; k <= 3; k++) {
620-
if (rgbw[i][k] != current_rgbw[i][k]) current_rgbw[i][k] += step_level[i][k];
621-
if ((step_level[i][k] > 0.0 && current_rgbw[i][k] > rgbw[i][k]) || (step_level[i][k] < 0.0 && current_rgbw[i][k] < rgbw[i][k])) current_rgbw[i][k] = rgbw[i][k];
622-
}
623-
for (int j = 0; j < pixelCount / lightsCount ; j++)
624-
{
625-
strip.SetPixelColor(j + i * pixelCount / lightsCount, RgbwColor((int)current_rgbw[i][0], (int)current_rgbw[i][1], (int)current_rgbw[i][2], (int)current_rgbw[i][3]));
626-
}
627-
strip.Show();
628-
}
629-
} else {
630-
if (current_rgbw[i][0] != 0 || current_rgbw[i][1] != 0 || current_rgbw[i][2] != 0 || current_rgbw[i][3] != 0) {
631-
in_transition = true;
632-
for (uint8_t k = 0; k <= 3; k++) {
633-
if (current_rgbw[i][k] != 0) current_rgbw[i][k] -= step_level[i][k];
634-
if (current_rgbw[i][k] < 0) current_rgbw[i][k] = 0;
635-
}
636-
for (int j = 0; j < pixelCount / lightsCount ; j++)
637-
{
638-
strip.SetPixelColor(j + i * pixelCount / lightsCount, RgbwColor((int)current_rgbw[i][0], (int)current_rgbw[i][1], (int)current_rgbw[i][2], (int)current_rgbw[i][3]));
639-
}
640-
strip.Show();
641-
}
642-
}
643-
}
644-
if (in_transition) {
645-
delay(6);
646-
in_transition = false;
647-
}
648-
}
649-
650658
void loop() {
651659
ArduinoOTA.handle();
652660
server.handleClient();

0 commit comments

Comments
 (0)