-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtesting.lua
More file actions
1639 lines (1546 loc) · 56 KB
/
testing.lua
File metadata and controls
1639 lines (1546 loc) · 56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
module("L_PhilipsHue2", package.seeall)
local dkjson = require("dkjson")
local socket = require("socket")
local http = require("socket.http")
local ltn12 = require("ltn12")
local log = luup.log
local params = {
mode = "client",
protocol = "tlsv1",
verify = {"none"},
options = {"no_compression"},
ciphers = "ALL",
}
-- Flags
local DEBUG_MODE = false
local FAILED_STATUS_REPORT = true
local FLAGS = {
LAMPS = false,
BRIDGE = false,
HANDLE_GROUPS = false
}
-- Constants
local DEVICE_FILES = {
MOTION_SENSOR = "D_MotionSensor1.xml"
}
local DEVICE_TYPES = {
BINARY_LIGHT = "urn:schemas-upnp-org:device:BinaryLight:1",
}
local SID = {
HUE = "urn:micasaverde-com:serviceId:PhilipsHue1",
SWP = "urn:upnp-org:serviceId:SwitchPower1",
DIM = "urn:upnp-org:serviceId:Dimming1"
}
local TASK = {
ERROR = 2,
ERROR_ALARM = -2,
ERROR_STOP = -4,
SUCCESS = 4,
BUSY = 1
}
-- LastUpdate
local DISPLAY_SECONDS = 20
local POLLING_RATE = 7 -- 30 --"PollFrequency"
-- Globals
local lug_device = nil
local g_appendPtr -- The pointer passed to luup.chdev.append
local g_taskHandle = -1
local g_lastTask = os.time() -- The time when the status message was last updated.
local g_ipAddress = ""
local g_UUID = nil
local g_hueURL = ""
local g_username = "testuser"
local g_lastState = ""
local g_lampNumber = 0
local g_groupNumber = 0
local g_sceneNumber = 0
local g_lamps = {
}
local g_groups = {
-- .id
-- .name
-- ...
}
local g_scenes = {
-- .id
-- .name
-- ...
}
local LANGUAGE_TOKENS = {
["fr"] = {
["Please press the link button on the Bridge and hit the Pair button again!"] = "Veuillez appuyer sur le bouton lien localisé sur le pont et cliquez a nouveau sur le bouton 'associer avec le pont'!",
["Philips Hue Connected!"] = "Philips Hue est connecté!",
["Philips Hue Disconnected!"] = "Philips Hue est déconnecté!",
["Connection with the Bridge could not be established! Check IP or the wired connection!"] = "La connexion de pont n'a pas été établie! Veuillez vérifier l'adresse IP ou la connexion par câble!",
["IP address could not be automatically set! Please add it in IP field, save and reload the engine!"] = "L'adresse IP ne peut pas être initialisée automatiquement! Veuillez l'ajouter dans le champ IP et appuyer sur le bouton 'sauvegarder le numéro d'IP'!",
["Startup successful!"] = "Démarrage réussi",
["Startup ERROR : Connection with the Bridge could not be established!"] = "Luup ERROR : La connexion de pont n'a pas été établie!",
["Philips Hue Bridge has been discovered!"] = "Philips Hue a été découvert!",
["Philips Hue Bridge could not be found!"] = "Philips Hue n'a pas été trouvé!",
["Linking ERROR occurred: "] = "Une erreur de liaison est survenue:",
["Starting up..."] = "Démarrage ..."
},
["en"] = {
["Please press the link button on the Bridge and hit the Pair button again!"] = "Please press the link button on the Bridge and hit the Pair button again!",
["Philips Hue Connected!"] = "Philips Hue Connected!",
["Philips Hue Disconnected!"] = "Philips Hue Disconnected!",
["Connection with the Bridge could not be established! Check IP or the wired connection!"] = "Connection with the Bridge could not be established! Check IP or the wired connection!",
["IP address could not be automatically set! Please add it in IP field, save and reload the engine!"] = "IP address could not be automatically set! Please add it in IP field and click on 'Save' button!",
["Startup successful!"] = "Startup successful!",
["Startup ERROR : Connection with the Bridge could not be established!"] = "Startup ERROR : Connection with the Bridge could not be established!",
["Philips Hue Bridge has been discovered!"] = "Philips Hue Bridge has been discovered!",
["Philips Hue Bridge could not be found!"] = "Philips Hue Bridge could not be found!",
["Linking ERROR occurred: "] = "Linking ERROR occurred: ",
["Starting up..."] = "Starting up..."
}
}
local lug_language
---------------------------------------------------------
-----------------Generic Utils---------------------------
---------------------------------------------------------
local function debug (text)
-- if (DEBUG_MODE == true) then
log(text)
-- end
end
function clearTask()
if (os.time() - g_lastTask >= DISPLAY_SECONDS) then
if lug_language == "fr" then
luup.task("Effancer...", TASK.SUCCESS, "Philips Hue", g_taskHandle)
else
luup.task("Clearing...", TASK.SUCCESS, "Philips Hue", g_taskHandle)
end
end
debug("(Hue2 Plugin)::(clearTask) : Clearing task... ")
end
local function displayMessage (text, mode)
if LANGUAGE_TOKENS[lug_language] and LANGUAGE_TOKENS[lug_language][text] then
text = LANGUAGE_TOKENS[lug_language][text]
end
if mode == TASK.ERROR_ALARM or mode == TASK.ERROR_STOP then
luup.task(text, TASK.ERROR, "Philips Hue", g_taskHandle)
if mode == TASK.ERROR_STOP then
luup.set_failure(1, lug_device)
end
return
end
luup.task(text, mode, "Philips Hue", g_taskHandle)
-- Set message timeout.
g_lastTask = os.time()
luup.call_delay("clearTask", DISPLAY_SECONDS)
end
local function GetLanguage()
local file = io.open("/etc/cmh/language")
if file then
local language = file:read("*a")
file:close()
language = language:match("%a+")
debug("(Hue2 Plugin)::(GetLanguage) : Got language: ".. language)
return language
else
debug("(Hue2 Plugin)::(GetLanguage) : Cannot open /etc/cmh/language, returning default language!")
return "en"
end
end
local function UrlEncode (s)
s = s:gsub("\n", "\r\n")
s = s:gsub("([^%w])", function (c)
return string.format("%%%02X", string.byte(c))
end)
return s
end
local function DEC_HEX(IN)
return string.format("%02X",IN) or "00"
end
function round(x)
return math.floor(x+0.5)
end
local function clamp(x,min,max)
if x < min then
return round(min)
end
if x > max then
return round(max)
end
return round(x)
end
local function mirekToKelvin(value)
local mirek = 6500-(value-153)*12.9682997118
return mirek
end
local function convertColorTemperatureToHex(colortemperature)
local kelvin = 6500 - (colortemperature - 153) * 12.9682997118
local temp = kelvin / 100
local red, green, blue
if temp <= 66 then
red = 255
green = temp
green = 99.4708025861 * math.log(green) - 161.1195681661
if temp <= 19 then
blue = 0
else
blue = temp - 10
blue = 138.5177312231 * math.log(blue) - 305.0447927307
end
else
red = temp - 60
red = 329.698727446 * math.pow(red, -0.1332047592)
green = temp - 60
green = 288.1221695283 * math.pow(green, -0.0755148492)
blue = 255
end
return "#" .. DEC_HEX(clamp(red, 0, 255)) .. DEC_HEX(clamp(green, 0, 255)) .. DEC_HEX(clamp(blue, 0, 255))
end
local function HueToRgb(p,q,t)
if t < 0 then
t = t + 1
elseif t > 1 then
t = t - 1
end
if t < 1/6 then
return p + (q - p) * 6 * t
elseif t < 1/2 then
return q;
elseif t < 2/3 then
return p + (q - p) * (2/3 - t) * 6
end
return p
end
local function convertHslToHex(h,s)
local l = 0.7 - (s - 200 ) * 0.0036363636363
local r,g,b
h = h/65535
s = s/255
if s == 0 then
r = 1
g = 1
b = 1
else
local q
if l < 0.5 then
q = l * (1 + s)
else
q = l + s - l * s
end
local p = 2 * l - q
r = HueToRgb(p, q, h + 1/3)
g = HueToRgb(p, q, h)
b = HueToRgb(p, q, h - 1/3)
end
return "#" .. DEC_HEX(clamp(round(r * 255),0,255)) .. DEC_HEX(clamp(round(g * 255),0,255)) .. DEC_HEX(clamp(round(b * 255),0,255))
end
---------------------------------------------------------
---------------Action Implementations--------------------
---------------------------------------------------------
function bridgeConnect(lul_device)
log("(Hue2 Plugin)::(bridgeConnect) : Linking with the Bridge device - lug_device = "..(lug_device or "NIL"))
local deviceType = "Vera" .. luup.pk_accesspoint
local jsondata = { devicetype = deviceType}
local postdata = dkjson.encode(jsondata)
local body, status, headers = http.request(g_hueURL, postdata)
local json_response = dkjson.decode(body)
local linkError = false
local otherError = false
local errorDescription = ""
for key, value in pairs(json_response) do
if value.error ~= nil then
if value.error.type == 101 then
linkError = true
break
else
otherError = true
errorDescription = value.error.description
break
end
end
if value.success then
local username = value.success.username
luup.attr_set("username", username, lug_device)
break
end
end
if linkError then
luup.variable_set(SID.HUE, "Status", LANGUAGE_TOKENS[lug_language]["Please press the link button on the Bridge and hit the Pair button again!"], lug_device)
luup.variable_set(SID.HUE, "BridgeLink", "0", lug_device)
displayMessage("Please press the link button on the Bridge and hit the Pair button again!", TASK.BUSY)
log( "(Hue2 Plugin)::(bridgeConnect) : Please press the link button on the Bridge and hit the Pair button again!" )
elseif otherError then
luup.variable_set(SID.HUE, "Status", LANGUAGE_TOKENS[lug_language]["Linking ERROR occurred: "] .. errorDescription , lug_device)
log( "(Hue2 Plugin)::(bridgeConnect) : Linking ERROR occurred: " .. errorDescription )
else
local bridgeLink = luup.variable_get(SID.HUE, "BridgeLink", lug_device) or ""
if bridgeLink == "0" then
luup.variable_set(SID.HUE, "BridgeLink", "1", lug_device)
luup.reload()
end
luup.variable_set(SID.HUE, "Status",LANGUAGE_TOKENS[lug_language]["Philips Hue Connected!"], lug_device)
displayMessage("Philips Hue Connected!", TASK.BUSY)
log( "(Hue2 Plugin)::(bridgeConnect) : Philips Hue Connected!" )
end
end
local function getIconVal(colormode, value)
if colormode == "hs" or colormode == "xy" then
if value >= 0 and value <= 3900 then
return "R"
elseif value > 3900 and value <= 8500 then
return "O"
elseif value > 8500 and value <= 13700 then
return "Y"
elseif value > 13700 and value <= 29500 then
return "G"
elseif value > 29500 and value <= 34700 then
return "C"
elseif value > 34700 and value <= 47500 then
return "B"
elseif value > 47500 and value <= 49100 then
return "V"
elseif value > 49100 and value <= 62250 then
return "M"
elseif value > 62250 and value <= 65535 then
return "R"
else
return "W"
end
elseif colormode == "ct" then
if value > 0 and value <= 300 then
return "ABB"
elseif value > 300 and value <= 350 then
return "ABW"
elseif value >350 and value <= 500 then
return "ABY"
else
return "ABW"
end
else
return "ABW"
end
end
function setLoadLevelTarget(lul_settings, device)
local newLoadlevelTarget
if lul_settings.newTargetValue then
if tonumber(lul_settings.newTargetValue) > 0 then
newLoadlevelTarget = "100"
else
newLoadlevelTarget = "0"
end
elseif lul_settings.newLoadlevelTarget then
newLoadlevelTarget = lul_settings.newLoadlevelTarget
else
debug("(Hue2 Plugin)::(setLoadLevelTarget) : We shouldn't be here!!!")
return false
end
-- Philips Hue Color Temperatures
local colors = {
energize = {hue = 34495, sat = 232, ct = 155, name = 'Energize'},
concentrate = {hue = 33849, sat = 44, ct = 234, name = 'Concentrate'},
reading = {hue = 15331, sat = 121, ct = 343, name = 'Reading'},
warm = {hue = 14563, sat = 160, ct = 385, name = 'Warm'},
natural = {hue = 15223, sat = 127, ct = 349, name = 'Natural'},
relax = {hue = 13198, sat = 209, ct = 463, name = 'Relax'},
}
-- Check for UI7 All On/Off Command
if lul_settings.Category then
local isGroupOnOff = false
for k,v in pairs(g_groups) do
if tonumber(v.veraid) == device then
isGroupOnOff = true
end
end
if tonumber(lul_settings.Category) == 999 and isGroupOnOff == false then
if tonumber(lul_settings.newTargetValue) > 0 then
newLoadlevelTarget = 50
--setColorTemperature(colors.relax.ct, device)
else
newLoadlevelTarget = 0
end
else
debug("(Hue2 Plugin)::(setLoadLevelTarget) : Group is not affected by All On/Off command, returning ...")
return false
end
end
luup.variable_set(SID.DIM, "LoadLevelStatus", newLoadlevelTarget, device)
luup.variable_set(SID.DIM, "LoadLevelTarget", newLoadlevelTarget, device)
if tonumber(newLoadlevelTarget) > 0 then
luup.variable_set(SID.SWP, "Status", "1", device)
else
luup.variable_set(SID.SWP, "Status", "0", device)
end
local brightness = math.floor(tonumber(newLoadlevelTarget) * 254 / 100 + 0.5)
local lampID = ""
local isGroup = false
for key, val in pairs(g_lamps) do
if tonumber(val.veraid) == device then
lampID = val.hueid
end
end
if lampID == "" then
for key, val in pairs(g_groups) do
if tonumber(val.veraid) == device then
lampID = val.hueid
isGroup = true
end
end
end
if tostring(newLoadlevelTarget) == "0" then
if isGroup then
if setLampValues(lampID, "group", "on", false, "bri", brightness) then
return true
else
return false
end
else
if setLampValues(lampID, "light", "on", false, "bri", brightness) then
return true
else
return false
end
end
else
if isGroup then
if setLampValues(lampID, "group", "on", true, "bri", brightness) then
return true
else
return false
end
else
if setLampValues(lampID, "light", "on", true, "bri", brightness) then
return true
else
return false
end
end
end
end
function turnOffLamp(lamp)
setLampValues(lamp, "light", "on", false)
end
function setStateForAll(state, device)
local data = {}
if state == "0" then
data["on"] = false
elseif state == "1" then
data["on"] = true
else
debug("(Hue2 Plugin)::(setStateForAll) : We shouldn't be here!")
return false
end
local senddata = dkjson.encode(data)
local body = putToHue(senddata, 0, "group")
local json = dkjson.decode(body)
local flagError = false
for key, value in pairs(json) do
if (value.error ~= nil) then
debug( "(Hue2 Plugin)::(setStateForAll) : Setting state for all bulbs ERROR occurred : " .. value.error.type .. " with description : " .. value.error.description)
flagError = true
end
end
if flagError == false then
debug("(Hue2 Plugin)::(setStateForAll) : Successfully changed state for all hue bulbs!")
luup.variable_set(SID.HUE, "StateForAll", state, device)
return true
else
log("(Hue2 Plugin)::(setStateForAll) : Please check error/s above!")
return false
end
end
function setHueAndSaturation(hue, saturation, device)
debug("(Hue2 Plugin)::(setHueAndSaturation) : Starting...")
local lampID = ""
local on_val
local isGroup = false
for key, val in pairs(g_lamps) do
if tonumber(val.veraid) == device then
lampID = val.hueid
on_val = val.on
end
end
if lampID == "" then
for key, val in pairs(g_groups) do
if tonumber(val.veraid) == device then
lampID = val.hueid
on_val = val.on
isGroup = true
end
end
end
local value = "hue:".. hue .. ";sat:" .. saturation
luup.variable_set(SID.HUE, "LampValues", value, device)
luup.variable_set(SID.HUE, "LampHexValue", convertHslToHex(hue,saturation), device)
debug("(Hue2 Plugin)::(setHueAndSaturation) : on_val = ".. tostring(on_val))
if on_val then
if isGroup then
if setLampValues(lampID, "group", "hue", tonumber(hue), "sat", tonumber(saturation)) then
return true
else
return false
end
else
if setLampValues(lampID, "light", "hue", tonumber(hue), "sat", tonumber(saturation)) then
return true
else
return false
end
end
else
if isGroup then
if setLampValues(lampID, "group", "on", true, "hue", tonumber(hue), "sat", tonumber(saturation)) then
return true
else
return false
end
else
if setLampValues(lampID, "light", "on", true, "hue", tonumber(hue), "sat", tonumber(saturation)) then
return true
else
return false
end
end
--luup.call_delay( "turnOffLamp", 5, lampID)
end
end
function setColorTemperature(colortemperature, device)
debug("(Hue2 Plugin)::(setColorTemperature) : CT = " .. colortemperature)
local lampID = ""
local on_val
local isGroup = false
for key, val in pairs(g_lamps) do
if tonumber(val.veraid) == device then
lampID = val.hueid
on_val = val.on
end
end
if lampID == "" then
for key, val in pairs(g_groups) do
if tonumber(val.veraid) == device then
lampID = val.hueid
on_val = val.on
isGroup = true
end
end
end
luup.variable_set(SID.HUE, "LampValues", "ct:" .. colortemperature, device)
luup.variable_set(SID.HUE, "LampHexValue", convertColorTemperatureToHex(colortemperature), device)
debug("(Hue2 Plugin)::(setColorTemperature) : on_val = ".. tostring(on_val))
if on_val then
if isGroup then
if setLampValues(lampID, "group", "ct", tonumber(colortemperature)) then
return true
else
return false
end
else
if setLampValues(lampID, "light", "ct", tonumber(colortemperature)) then
return true
else
return false
end
end
else
if isGroup then
if setLampValues(lampID, "group", "on", true, "ct", tonumber(colortemperature)) then
return true
else
return false
end
else
if setLampValues(lampID, "light", "on", true, "ct", tonumber(colortemperature)) then
return true
else
return false
end
end
--luup.call_delay( "turnOffLamp", 5, lampID)
end
end
function putToHue(data, hueid, hueStructure)
debug("Hue2 Plugin)::(putToHue):data=" .. data)
local len = string.len(data)
local URL = ""
if hueStructure == "group" then
URL = g_hueURL .. "/" .. g_username .. "/groups/" .. hueid .. "/action"
elseif hueStructure == "light" then
URL = g_hueURL .. "/" .. g_username .. "/lights/" .. hueid .. "/state"
elseif hueStructure == "scene" then
URL = g_hueURL .. "/" .. g_username .. "/scenes/" .. hueid
elseif hueStructure == "Mscene" then
local sceneName = hueid:match("^(.*),")
local lightID = hueid:match(",(.*)$")
URL = g_hueURL .. "/" .. g_username .. "/scenes/" .. sceneName .. "/lights/" .. lightID .. "/state"
end
local bodyparts = { }
local x, status, headers = http.request {
url = URL,
headers = {["content-length"] = len},
source = ltn12.source.string(data),
sink = ltn12.sink.table(bodyparts),
method = "PUT"
}
local body = table.concat(bodyparts)
return body
end
function setLampValues(light_id, hueStructure, ...)
local lampID = tonumber(light_id, 10)
local deviceID
local arg = {...}
if #arg % 2 ~= 0 then
log( "(Hue2 Plugin)::(setLampValues) : ERROR : Wrong number of arguments!")
return false
end
local data = {}
for i = 1,#arg,2 do
data[arg[i]] = arg[i+1]
end
if hueStructure == "group" then
deviceID = g_groups[lampID].veraid
else
deviceID = g_lamps[lampID].veraid
end
for key,val in pairs(data) do
if key == "bri" then
if val == 0 then
data[key] = 1
if hueStructure == "group" then
g_groups[lampID].bri = 1
elseif hueStructure == "light" then
g_lamps[lampID].bri = 1
end
end
end
if key == "hue" then
local iconVal = getIconVal("hs", val)
if hueStructure == "group" then
luup.variable_set(SID.HUE, "IconValue", iconVal, g_groups[lampID].veraid)
else
luup.variable_set(SID.HUE, "IconValue", iconVal, g_lamps[lampID].veraid)
end
end
if key == "ct" then
local iconVal = getIconVal("ct", val)
if hueStructure == "group" then
luup.variable_set(SID.HUE, "IconValue", iconVal, g_groups[lampID].veraid)
else
luup.variable_set(SID.HUE, "IconValue", iconVal, g_lamps[lampID].veraid)
end
end
end
local senddata = dkjson.encode(data)
local body = putToHue(senddata, light_id, hueStructure)
local json = dkjson.decode(body)
local flagError = false
for key, value in pairs(json) do
if (value.error ~= nil) then
log( "(Hue2 Plugin)::(setLampValues) : Changing lamp/group status ERROR occurred : " .. value.error.type .. " with description : " .. value.error.description)
flagError = true
end
end
if flagError == false then
debug("(Hue2 Plugin)::(setLampValues) : Successfully changed lamp/group status for device " .. deviceID .. " !")
return true
else
log("(Hue2 Plugin)::(setLampValues) : Please check error/s above!")
return false
end
end
local function getLightName(light_id)
for k,v in pairs(g_lamps) do
if tostring(v.hueid) == tostring(light_id) then
return v.name .. "," .. v.veraid
end
end
end
local function getGroupLights(group_hue_id)
local groupLights = ""
for key,val in pairs(g_groups) do
if val.hueid == group_hue_id then
for k,v in pairs(val.lights) do
groupLights = groupLights .. v .. "," .. getLightName(v) .. ";"
end
end
end
return groupLights:sub(1,#groupLights - 1)
end
local function appendLamps()
debug("(Hue2 Plugin)::(appendLamps) : Verifying... ")
local count = 0
for i, v in pairs(g_lamps) do
debug("(Hue2 Plugin)::(appendLamps) : Appending Lamp ".. i ..".")
luup.log("appendLamps: processing type["..(v.huetype or "NIL").."] manufacturer ["..(v.manufacturer or "NIL").."] model ["..(v.modelid or "NIL").."]")
if v.manufacturer == 'philips' then
if v.huetype == "Dimmable light" then
luup.chdev.append(lug_device, g_appendPtr, "hueLamp_"..v.hueid, "HueLux ".. v.hueid ..": ".. v.name, "urn:schemas-micasaverde-com:device:PhilipsHueLuxLamp:1", "D_PhilipsHueLuxLamp2.xml", nil, "urn:micasaverde-com:serviceId:PhilipsHue1,BulbModelID=" .. v.modelid .. "\nurn:upnp-org:serviceId:Dimming1,TurnOnBeforeDim=0", false)
count = count + 1
else
luup.chdev.append(lug_device, g_appendPtr, "hueLamp_"..v.hueid, "HueLamp ".. v.hueid ..": ".. v.name, "urn:schemas-micasaverde-com:device:PhilipsHueLamp:1", "D_PhilipsHueLamp2.xml", nil, "urn:micasaverde-com:serviceId:PhilipsHue1,BulbModelID=" .. v.modelid .. "\nurn:upnp-org:serviceId:Dimming1,TurnOnBeforeDim=0", false)
count = count + 1
end
elseif v.manufacturer == 'cree' then
if v.modelid == "Connected" then
luup.chdev.append(lug_device, g_appendPtr, "hueLamp_"..v.hueid, "CreeConnected ".. v.hueid ..": ".. v.name, "urn:schemas-micasaverde-com:device:PhilipsHueLuxLamp:1", "D_PhilipsHueLuxLamp2.xml", nil, "urn:micasaverde-com:serviceId:PhilipsHue1,BulbModelID=" .. v.modelid .. "\nurn:upnp-org:serviceId:Dimming1,TurnOnBeforeDim=0", false)
count = count + 1
end
end
if count > 80 then
debug("(Hue2 Plugin)::(appendLamps) : Possible error in generating lamps function, more then 80 devices were generated!!!")
return
end
end
end
local function appendGroups()
debug("(Hue2 Plugin)::(appendGroups) : Verifying... ")
if #g_groups > 0 then
local count = 0
for i, v in pairs(g_groups) do
if v.huetype == "LightGroup" then
debug("(Hue2 Plugin)::(appendGroups) : Appending Group ".. i ..".")
groupType = "NLG"
luup.chdev.append(lug_device, g_appendPtr, "hueGroup_".. v.hueid, "HueGroup ".. v.hueid ..": ".. v.name, "urn:schemas-micasaverde-com:device:PhilipsHueMultisourceLuminaireLamp:1", "D_PhilipsHueMultisourceLuminaireLamp2.xml", nil, "urn:micasaverde-com:serviceId:PhilipsHue1,GroupType=NLG\nurn:upnp-org:serviceId:Dimming1,TurnOnBeforeDim=0", false)
count = count + 1
-- debug("(Hue2 Plugin)::(appendGroups) : Not handled!")
elseif v.huetype == "Luminaire" then
debug("(Hue2 Plugin)::(appendGroups) : Appending Luminaire Group ".. i ..".")
local GroupType = ""
if v.modelid == "HML001" or v.modelid == "HML002" or v.modelid == "HML003" or v.modelid == "HML007" then
GroupType = "CTM"
else
GroupType = "CLM"
end
local services = "urn:micasaverde-com:serviceId:PhilipsHue1,GroupType=" .. GroupType .. "\nurn:micasaverde-com:serviceId:PhilipsHue1,BulbModelID=" .. v.modelid .. "\nurn:upnp-org:serviceId:Dimming1,TurnOnBeforeDim=0"
luup.chdev.append(lug_device, g_appendPtr, "hueGroup_".. v.hueid, "HueLuminaire ".. v.hueid ..": ".. v.name, "urn:schemas-micasaverde-com:device:PhilipsHueMultisourceLuminaireLamp:1", "D_PhilipsHueMultisourceLuminaireLamp2.xml", nil, services, false)
count = count + 1
end
if count > 50 then
debug("(Hue2 Plugin)::(appendGroups) : Possible error in generating lamps function, more then 50 devices were generated!!!")
return
end
end
else
debug("(Hue2 Plugin)::(appendGroups) : No supported groups found!")
end
end
---------------------------------------------------------
---------------Initialization Functions------------------
---------------------------------------------------------
local function getChildDevices(device)
for dev, attr in pairs(luup.devices) do
if (attr.device_num_parent == device) then
local LampNo = attr.id:match("^hueLamp_(%d+)")
if LampNo then
for k,v in pairs(g_lamps) do
if LampNo == tostring(v.hueid) then
g_lamps[tonumber(v.hueid)].veraid = dev
end
end
end
local GroupNo = attr.id:match("^hueGroup_(%d+)")
if GroupNo then
for k,v in pairs(g_groups) do
if GroupNo == tostring(v.hueid) then
g_groups[tonumber(v.hueid)].veraid = dev
end
end
end
end
end
end
local function findBridge()
-- Try to get the bridge IP via nupnp
log("(Hue2 Plugin)::(findBridge) : Trying to get IP via NUPNP...")
local https = require("ssl.https")
local content, status = https.request("https://www.meethue.com/api/nupnp")
-- local status, content = luup.inet.wget("https://www.meethue.com/api/nupnp")
if content then
uuid = content:match("\"id\":\"(.-)\"")
ipAddress = content:match("\"internalipaddress\":\"(.-)\"")
if ipAddress then
log("(Hue2 Plugin)::(findBridge) : (NUPNP) : Philips Hue Bridge found with IP address: " .. ipAddress)
g_ipAddress = ipAddress
g_UUID = uuid
luup.variable_set(SID.HUE, "UUID", g_UUID, lug_device)
luup.attr_set("ip", g_ipAddress, lug_device)
luup.variable_set(SID.HUE, "Status", LANGUAGE_TOKENS[lug_language]["Philips Hue Bridge has been discovered!"], lug_device)
FLAGS.BRIDGE = true
return true
else
log("(Hue2 Plugin)::(findBridge) : (NUPNP) : Philips Hue Bridge could not be found!")
luup.variable_set(SID.HUE, "Status", LANGUAGE_TOKENS[lug_language]["Philips Hue Bridge could not be found!"], lug_device)
FLAGS.BRIDGE = false
return false
end
else
log("(Hue2 Plugin)::(findBridge) : Philips Hue Bridge could not be found!")
luup.variable_set(SID.HUE, "Status", LANGUAGE_TOKENS[lug_language]["Philips Hue Bridge could not be found!"], lug_device)
FLAGS.BRIDGE = false
return false
end
end
local function getLength(var)
local i = 0
for k,v in pairs(var) do
i = i + 1
end
return i
end
local function getDevices(json)
local length = 0
-- Get LampS Info
if json.lights then
length = getLength(json.lights)
if length > 0 then
g_lampNumber = length
for key, val in pairs(json.lights) do
local k = tonumber(key)
local manufacturer = "philips"
if val.manufacturername then
local manufacturer = string.gsub(string.lower(val.manufacturername), '%s+', '')
end
if manufacturer == "philips" then
if val.type == "Dimmable light" then
g_lamps[k] = {}
g_lamps[k].manufacturer = manufacturer
g_lamps[k].hueid = key
g_lamps[k].on = val.state.on
g_lamps[k].bri = val.state.bri or 0
g_lamps[k].huetype = val.type
g_lamps[k].name = val.name
g_lamps[k].modelid = val.modelid
g_lamps[k].reachable = val.state.reachable or false
else
g_lamps[k] = {}
g_lamps[k].manufacturer = manufacturer
g_lamps[k].hueid = key
g_lamps[k].on = val.state.on
g_lamps[k].bri = val.state.bri or 0
if val.state.hue then g_lamps[k].hue = val.state.hue or 0 end
if val.state.sat then g_lamps[k].sat = val.state.sat or 0 end
if val.state.xy[1] then g_lamps[k].x = val.state.xy[1] or 0 end
if val.state.xy[2] then g_lamps[k].y = val.state.xy[2] or 0 end
if val.state.ct then g_lamps[k].ct = val.state.ct or 0 end
if val.state.colormode then g_lamps[k].colormode = val.state.colormode or "hs" end
g_lamps[k].huetype = val.type
g_lamps[k].name = val.name
g_lamps[k].modelid = val.modelid
g_lamps[k].uniqueid = val.uniqueid
g_lamps[k].swversion = val.swversion
end
elseif manufacturer == "cree" or manufacturer == "ge_appliances" then
if val.type == "Dimmable light" then
g_lamps[k] = {}
g_lamps[k].manufacturer = manufacturer
g_lamps[k].hueid = key
g_lamps[k].on = val.state.on
g_lamps[k].bri = val.state.bri or 0
g_lamps[k].huetype = val.type
g_lamps[k].name = val.name
g_lamps[k].modelid = val.modelid
g_lamps[k].reachable = val.state.reachable or false
else
g_lamps[k] = {}
g_lamps[k].manufacturer = manufacturer
g_lamps[k].hueid = key
g_lamps[k].on = val.state.on
g_lamps[k].bri = val.state.bri or 0
if val.state.hue then g_lamps[k].hue = val.state.hue or 0 end
if val.state.sat then g_lamps[k].sat = val.state.sat or 0 end
if val.state.xy[1] then g_lamps[k].x = val.state.xy[1] or 0 end
if val.state.xy[2] then g_lamps[k].y = val.state.xy[2] or 0 end
if val.state.ct then g_lamps[k].ct = val.state.ct or 0 end
if val.state.colormode then g_lamps[k].colormode = val.state.colormode or "hs" end
g_lamps[k].huetype = val.type
g_lamps[k].name = val.name
g_lamps[k].modelid = val.modelid
g_lamps[k].uniqueid = val.uniqueid
g_lamps[k].swversion = val.swversion
end
end
end
debug("(Hue2 Plugin)::(getDevices) : Lights values saved!")
else
debug("(Hue2 Plugin)::(getDevices) : There are no lights set on the Bridge!")
end
else
debug("(Hue2 Plugin)::(getDevices) : Possible error, 'lights' tag is not there!")
end
-- Get Groups Info
length = 0
if json.groups then
length = getLength(json.groups)
if length > 0 then
g_groupNumber = length
for key, val in pairs(json.groups) do
if val.type and ((val.type == "Luminaire") or (val.type == "LightGroup")) then
local k = tonumber(key)
g_groups[k] = {}
g_groups[k].lights = {}
g_groups[k].hueid = key
g_groups[k].name = val.name
for i = 1,getLength(val.lights) do
g_groups[k].lights[i] = val.lights[i]
end
g_groups[k].huetype = val.type
if val.modelid then g_groups[k].modelid = val.modelid end
g_groups[k].on = val.action.on
g_groups[k].bri = val.action.bri or 0
if val.action.hue then g_groups[k].hue = val.action.hue or 0 end
if val.action.sat then g_groups[k].sat = val.action.sat or 0 end
if val.action.xy then g_groups[k].x = val.action.xy[1] end
if val.action.xy then g_groups[k].y = val.action.xy[2] end
if val.action.effect then g_groups[k].effect = val.action.effect end
if val.action.ct then g_groups[k].ct = val.action.ct or 0 end
if val.action.alert then g_groups[k].alert = val.action.alert or "none" end
if val.action.colormode then g_groups[k].colormode = val.action.colormode or "hs" end
end
end
if #g_groups > 0 then
debug("(Hue2 Plugin)::(getDevices) : Groups values saved!")
end
else
debug("(Hue2 Plugin)::(getDevices) : There are no groups set on the Bridge!")
end
else
debug("(Hue2 Plugin)::(getDevices) : Possible error, 'groups' tag is not there!")
end
-- Get Scenes Info
local bridgeScenes = {}
length = 0
if json.scenes then
length = getLength(json.scenes)
if length > 0 then
g_sceneNumber = length
for key, val in pairs(json.scenes) do
local k = key
g_scenes[k] = {}
g_scenes[k].sceneID = key
g_scenes[k].lights = {}
g_scenes[k].name = val.name
for i = 1,getLength(val.lights) do
g_scenes[k].lights[i] = val.lights[i]
end
g_scenes[k].active = val.active
-- update scenes json for web and mobile
bridgeScenes[k] = {}
bridgeScenes[k].name = val.name:match("(.+)%s+o[nf]+ %d*") or val.name:match("(.+)")
bridgeScenes[k].lights = {}
for i = 1,getLength(val.lights) do
bridgeScenes[k].lights[i] = val.lights[i]
end
bridgeScenes[k].active = val.active
end
debug("(Hue2 Plugin)::(getDevices) : Scenes values saved!")
else
debug("(Hue2 Plugin)::(getDevices) : There are no Scenes set on the Bridge!")
end
else
debug("(Hue2 Plugin)::(getDevices) : Possible error, 'scenes' tag is not there!")
end
local scenejson = dkjson.encode(bridgeScenes)
luup.variable_set(SID.HUE, "BridgeScenes", scenejson, lug_device)
end
function pollHueDevice(pollType)
local onLightsNumber = 0
if pollType == "true" then
debug("(Hue2 Plugin)::(pollHueDevice) : Action poll performed!")
else
debug("(Hue2 Plugin)::(pollHueDevice) : Normal poll performed!")
end
local length = 0
local url = g_hueURL .. "/" .. g_username