-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathhousepanel.php
5897 lines (5274 loc) · 250 KB
/
housepanel.php
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
<?php
/*
* House Panel application for SmartThings and Hubitat
* author: Ken Washington (c) 2017, 2018, 2019
*
* Must be paired with housepanel.groovy on the SmartThings or Hubitat side
* HousePanel now obtains all auth information from the setup step upon first run
*
* Revision History
*/
$devhistory = "
2.119 Minor bug fixes
2.118 Fix bug that prevented user from changing custom tile count
2.117 Load jquery locally and include files in the distro
2.116 Tweaks to enable floor plan skins and bug fixes
2.115 Finalize audio track refresh feature and remove bugs
- handle music tiles properly and remove test bug
2.114 Allow track to update on hub refresh for audio devices
- updated modern skin to work with new Sonos DH
2.113 Remove bogus line in groovy code
2.112 Added audioNotification capability for new Sonos DH (draft)
- fixed up login again and added feature to disable pw's
2.111 Minor bugfixes to 2.110 hub auth separation
2.110 Major rewrite of auth flow to move options to options page
- username and password are now on the options page
- bug fixes in timer refresh logic
- bug fix to tile width to include slider for bulbs and switches
- add hub filter to options and tile catalog drag pages
2.109 Add options parameter to enable or disable rules since it can be slow
2.108 Modify Rule to enable multiple actions and require 'if: ' to flag if
2.107 New Rule feature that allows non-visual triggers to be added to any tile
2.106 Macro feature tested and fine tuned to return results in console log
- tile editor name update fixed to prevent spurious page reloads
- returns name on switches now for viewing with API; GUI still ignores
- protect from returning password and name in the GUI everywhere
2.105 Minor bugfix for leak sensors that dont support wet/dry commands
2.104 Bug Fixes and API improvements
- enable auto search for correct hub if omitted in API calls
- fix spurious hub creation when reauthorization performed
- enable blink properly when waiting for authorization
- fix tile editor list and tile customizer for weather tiles
2.103 link tile query fix and media art fine tune
- add default icons for water sensors and enable water actions
2.100 User specific skin support
- add custom tiles to user account
- now save user account files in true json format
- fix query to linked items
- improve album art search and support tunein items
2.092 Major update to documentation on housepanel.net
- tweak info window when inspected near right edge
- enable album art upon first change in song
2.091 Fix LINK for custom tile actions; bugfix album art for grouped speakers
2.090 Add curl call to gather usage statistics on central server
2.087 Minor formatting cleanup in show info routine
2.086 Update install script to support user skins and updates easily
- remove hubtype from main array to save load time as it wasn't used
2.085 Clean up handling of custom names
2.084 Bugfix auth code to handle PHP installs without builtin functions
- change minimum username length to 3 and look for admin name
- drag drop name fix
2.083 Properly load things and options for use in GUI and other bug fixes
2.082 Fixed snarky bug in auth that reset hubpush ports and other things
- did more cleanup and robusting of auth flow
2.081 Security lock down - no longer accept blanks to make new bogus user
- reauth request via api if not logged in will return to login page
- default user name not set to admin rather set to blank now
- reauth page still available if options file is missing
- reset code will also launch to auth page all the time if enabled
2.080 Remove blank customtile.css files to avoid overwriting user version
- LINK customizer bugfix
- minor bug fix of weather tile name
- custom field image default CSS fix, misc code cleanup
- show status when click on tiles that typically have no actions
- speed up initial load after refresh page
2.078 Bugfixes to 2.076 and 2.077 - skin missing from tileeditor
- fix long standing bug of duplicate Node.js clients
- properly close sockets upon disconnect and remove dups
2.077 Remove http requirement for URL entries to enable intent links
2.076 Various password updates and fixes
- add password support for tiles using the custom field feature
- change main password from simple hash to strong algorithm
- fix bug in the action buttons and links in clock tiles
- remove reserved fields from hub push results
- enabled return and cancel keys in popup dialog boxes
2.075 js Time bugfixes
- finish implementing the sorting feature for user fields
- speedup by avoiding reading options on each tile make
2.073 Major speedup in Tile Customizer (customize.js)
- prep work for sorting feature - not yet implemented
- minor bug fixes
2.072 Honor time format in js updates every second
- merge in README clean up pull request
- enable multiple things in a query request
- minor bugfix for auto of non-groovy tiles
- update hpapi.py demo to work with current version
2.071 Bypass cache for updated frames and other special tiles
- minor bug fix to tile editor for tile name setting
- fix bug where special tile count was not being saved
- fix bug that screwed up max number of custom tiles
- fix bug for page changes not sticking
2.070 Bugfixes to beta 2.065, code cleanup, ignore DeviceWatch-Enroll
- includes error checking for bogus hub calls
- also fixed hidden check in tile editor for fields that match type
- handled obscure cases for refreshing special tiles properly
2.065 Migrate image and blank tiles over to php server side
- provide user way to select city in AccuWeather
- but user must find the Location Code first
2.064 Fix music control siblings and improve Album Art reliability
2.063 Implement music icons for native Echo Speaks and Generic music
2.062 Retain edit and custom names upon refresh; minor bug fixes
2.061 Custom frame and video tile name bugfix
2.060 Auto detect and grab artist, album title, and album art image
2.057 Minor cleanup including proper detection of hidden status in editor
2.056 Groovy file update only to specify event date format
2.055 Update version number in Groovy file and more error checking
2.054 Clean up groovy file; add direct mode action buttons
2.053 Misc bug fixes: LINK on/off; tile editor tweaks
- new feature in Tile Editor to pick inline/blcok & absolute/relative
2.052 Really fixed clobber this time (in hubpush). Added portrait CSS support
2.051 Another run at fixing name clobber; update modern skin for flash
2.050 Fix cloberred custom names; fix Hubitat event reporting; add timezone
2.049 Time zone fix for real time javascript digital clock
- add version number to main screen
2.048 Visual cue for clicking on any tile for 3/4 of a second
2.047 Clean up SHM and HSM to deliver similar display fields and bug fixes
2.046 Avoid fatal error if prefix not given, fix Routine bug in groovy, etc
2.045 Merge groovy files into one with conditional hub detector
2.042 Minor tweak to CSS default for showing history only on some things
- add dev history to show info and auto create version info from this
- add on and off toggle icons from modern to the default skin
- doc images update
2.040 Four event fields added to most tiles for reporting (ST only for now)
2.031 Use custom name for head title and name field
2.030 Fix HSM and SHM bugs and piston styling for modern skin
2.020 Macro rule graduate from beta to tested feature - still no gui
2.010 Grid snap feature and fix catalog for modern skin
2.000 Release of rule feature as non beta. Fixed level and other tweaks
1.998 Macro rules implemented as beta feature. No easy GUI provided yet
1.997 Improve crude rule feature to only do push from last client
minor performance and aesthetic improvements in push Node code
1.996 Fix hubId bug in push file
implement crude rule capability triggered by custom tile use
- if a motion sensor is added to a light it will trigger it on
- if a contact is added to a light, open will turn on, close off
- if another switch is added to a light, it will trigger it too
1.995 Update install script to properly implement push service setup
remove .service file because install script makes this
clean up hubid usage to use the real id for each hub consistently
refresh screen automatically after user reorders tiles
1.992 Bugfix for swapping skins to enable new skin's customtiles
this also changes the custom tiles comments to avoid dups
minor tweaks to the modern skin and controller look
1.991 New modern skin and include door in classes from tile names
1.990 Final cleanup before public release of hubpush bugfixes
move housepanel-push to subfolder beneath main files
update housepanel-push to include more robust error checking
Fixed bug in housepanel-push service causing it to crash
Corrected and cleaned up install.sh script to work with hubpush
1.989 Continued bug fixing hubpush and auth flow stuff
1.988 Major bugfix to auth flow for new users without a cfg file
1.987 Bugfix for broken hubpush after implementing hubId indexing
publish updated housepanel-push.js Node.js program
1.986 Minor fix to use proper hub name and type in info tables
1.985 Finish implementing hub removal feature
- added messages to inform user during long hub processes in auth
- position delete confirm box near the tile
- minor bug fixes
1.983 2019-02-14
bugfix in auth page where default hub was messed up
1.982 2019-02-14
change hubnum to use hubId so we can remove hubs without damage
1.981 Upgrade to install.sh script and enable hub removal
1.980 Update tiles using direct push from hub using Node.js middleman
1.972 Add ability to tailor fast polling to include any tile
by adding a refresh user field with name fast, slow, or never
- also added built-in second refresh for clock tiles
- two new floor lamp icons added to main skin
- fix bug so that hidden items in editor now indicate hidden initially
1.971 Fix clicking on linked tiles so it updates the linked to tile
- also fixes an obscure bug with user linked query tiles
1.970 Tidy up customizer dialog to give existing info
1.966 Enable duplicate LINK items and add power meter things
1.965 Restored weather icons using new mapping info
1.964 Updated documentation and tweak CSS for Edge browser
1.963 Improved user guidance for Hubitat installations
1.962 Bring Hubitat and SmartThigns groovy files into sync with each other
and in the process found a few minor bugs and fixed them
1.961 Important bug fixes to groovy code for switches, locks, valves
1.960 New username feature and change how auth dialog box works
- fixed error in door controller
1.953 Fix room delete bug - thanks to @hefman for flagging this
1.952 Finalize GUI for tile customization (wicked cool)
- fix bug in Music player for controls
- revert to old light treatment in Hubitat
1.951 Bug fixes while testing major 1.950 update
- fix bug that made kiosk mode setting not work in the Options page
- fix bug that broke skin media in tile edit while in kiosk mode
- use the user config date formats before setting up clock in a refresh
1.950 Major new update with general customizations for any tile
- this is a major new feature that gives any tile the ability to
add any element from any other tile or any user provided text
so basically all tiles now behave like custom tiles in addition
to their native behavior. You can even replace existing elements
For example, the analog clock skin can be changed now by user
User provided URL links and web service POST calls also supported
Any URL link provided when clicked will open in a new tab/window
- fix weird bug in processing names for class types
- added ability to customize time formats leveraging custom feature
- now refresh frames so their content stays current
- include blanks, clocks, and custom tiles in fast non-hub refresh
- enable frame html file names to be specified as name in TileEdit
- lots of other cleanups and bug fixes
1.941 Added config tile for performing various options from a tile
- also fixed a bug in cache file reload for customtiles
1.940 Fix bug in Tile Editor for rotating icon setting and slower timers
1.930 Fix thermostat and video tag obscure bugs and more
- chnage video to inherit size
- change tile editor to append instead of prepend to avoid overlaps
- increase default polling speed
- first release of install script install.sh
1.928 Disallow hidden whole tiles and code cleanup
1.927 Added flourescent graphic to default skin, fix edit of active tile
1.926 Doc update to describe video tiles and minor tweaks, added help button
1.925 Various patches and hub tweaks
- Hub name retrieval from hub
- Show user auth activation data
- Hack to address Hubitat bug for Zwave generic dimmers
- Added border styling to TileEditor
1.924 Update custom tile status to match linked tiles
Added option to select number of custom tiles to use (beta)
1.923 TileEditor updates
- new option to align icons left, center or right
- added images of Sonos speakers to media library
- fixed bug where header invert option was always clicked
- renamed Text Width/Height to Item Width/Height
1.922 Updated default skin to make custom reflect originals in more places
1.921 Hybrid custom tile support using hmoptions user provided input
1.920 CSS cleanup and multiple new features
- enable skin editing on the main page
- connect customtiles to each skin to each one has its own
this means all customizations are saved in the skin directory too
- migrated fixed portions of skin to tileedit.css
- fix plain skin to use as skin swapping demo
- various bug fixes and performance improvements
1.910 Clean up CSS files to prepare for new skin creation
1.900 Refresh when done auth and update documentation to ccurrent version
1.809 Fix disappearing things in Hubitat bug - really this time...
1.808 Clean up page tile editing and thermostat bug fix
1.807 Fix brain fart mistake with 1.806 update
1.806 Multi-tile editing and major upgrade to page editing
1.805 Updates to tile editor and change outside image; other bug fixes
1.804 Fix invert icon in TileEditor, update plain skin to work
1.803 Fix http missing bug on hubHost, add custom POST, and other cleanup
1.802 Password option implemented - leave blank to bypass
1.801 Squashed a bug when tile instead of id was used to invoke the API
1.80 Merged multihub with master that included multi-tile api calls
1.793 Cleaned up auth page GUI, bug fixes, added hub num & type to tiles
1.792 Updated but still beta update to multiple ST and HE hub support
1.791 Multiple ST hub support and Analog Clock
1.79 More bug fixes
- fix icon setting on some servers by removing backslashes
- added separate option for timers and action disable
1.78 Activate multiple things for API calls using comma separated lists
to use this you mugit stst have useajax=doaction or useajax=dohubitat
and list all the things to control in the API call with commas separating
1.77 More bug fixes
- fix accidental delete of icons in hubitat version
- incorporate initial width and height values in tile editor
1.76 Misc cleanup for first production release
- fixed piston graphic in tileeditor
- fix music tile status to include stop state in tileeditor
- added ?v=hash to js and css files to force reload upon change
- removed old comments and dead code
1.75 Page name editing, addition, and removal function and reorder bug fixes
1.74 Add 8 custom tiles, zindex bugfix, and more tile editor updates
1.73 Updated tile editor to include whole tile backgrounds, custom names, and more
1.72 Timezone bug fix and merge into master
1.71 Bug fixes and draft page edit commented out until fixed
1.7 New authentication approach for easier setup and major code cleanup
1.622 Updated info dump to include json dump of variables
1.621 ***IMPT**bugfix to prior 1.62 update resolving corrupt config files
1.62 New ability to use only a Hubitat hubg
1.61 Bugfixes to TileEditor
1.60 Major rewrite of TileEditor
1.53 Drag and drop tile addition and removal and bug fixes
1.52 Bugfix for disappearing rooms, add Cancel in options, SmartHomeMonitor add
1.51 Integrate skin-material from @vervallsweg to v1.0.0 to work with sliders
1.50 Enable Hubitat devices when on same local network as HP
1.49 sliderhue branch to implement slider and draft color picker
1.48 Integrate @nitwitgit (Nick) TileEdit V3.2
1.47 Integrate Nick's color picker and custom dialog
1.46 Free form drag and drop of tiles
1.45 Merge in custom tile editing from Nick ngredient-master branch
1.44 Tab row hide/show capabilty in kiosk and regular modes
Added 4 generally customizable tiles to each page for styling
Fix 1 for bugs in hue lights based on testing thanks to @cwwilson08
1.43 Added colorTemperature, hue, and saturation support - not fully tested
Fixed bug in thermostat that caused fan and mode to fail
Squashed more bugs
1.42 Clean up CSS file to show presence and other things correctly
Change blank and image logic to read from Groovy code
Keep session updated for similar things when they change
-- this was done in the js file by calling refreshTile
Fix default size for switch tiles with power meter and level
-- by default will be larger but power can be disabled in CSS
1.41 Added filters on the Options page
Numerous bug fixes including default Kiosk set to false
Automatically add newly identified things to rooms per base logic
Fix tablet alignment of room tabs
Add hack to force background to show on near empty pages
1.4 Official merge with Open-Dash
Misc bug fixes in CSS and javascript files
Added kiosk mode flag to options file for hiding options button
1.32 Added routines capabilities and cleaned up default icons
1.31 Minor bug fixes - fixed switchlevel to include switch class
1.3 Intelligent class filters and force feature
user can add any class to a thing using <<custom>>
or <<!custom>> the only difference being ! signals
to avoid putting custom in the name of the tile
Note - it will still look really ugly in the ST app
Also adds first three words of the thing name to class
this is the preferred customizing approach
1.2 Cleaned up the Groovy file and streamlined a few things
Added smoke, illuminance, and doors (for Garages)
Reorganized categories to be more logical when selecting things
1.1 beta Added cool piston graph for Webcore tiles
Added png icons for browser and Apple products
Show all fields supported - some hidden via CSS
Battery display on battery powered sensors
Support Valves - only tested with Rachio sprinklers
Weather tile changed to show actual and feels like side by side
Power and Energy show up now in metered plugs
Fix name of web page in title
Changed backgrounds to jpg to make them smaller and load faster
Motion sensor with temperature readings now show temperature too
0.8 beta Many fixes based on alpha user feedback - first beta release
Includes webCoRE integration, Modes, and Weather tile reformatting
Also includes a large time tile in the default skin file
Squashed a few bugs including a typo in file usage
0.7-alpha Enable a skinning feature by moving all CSS and graphics into a
directory. Added parameter for API calls to support EU
0.6-alpha Minor tweaks to above - this is the actual first public version
0.5-alpha First public test version
0.2 Cleanup including fixing unsafe GET and POST calls
Removed history call and moved to javascript side
put reading and writing of options into function calls
replaced main page bracket from table to div
0.1 Implement new architecture for files to support sortable jQuery
0.0 Initial release
";
ini_set('max_execution_time', 300);
ini_set('max_input_vars', 20);
// grab the version number from the latest history entry
$version = trim(substr($devhistory,1,10));
define('HPVERSION', $version);
define('APPNAME', 'HousePanel V' . HPVERSION);
define('CRYPTSALT','HP$by%KW');
define('BYPASSPW', false);
// developer debug options
// options 2 and 4 will stop the flow and must be reset to continue normal operation
// option3 can stay on and will just print lots of stuff on each page
define('DEBUG', false); // turns on debugs 3 and 5
define('DEBUG2', false); // authentication flow debug and stop
define('DEBUG3', false); // room display debug - show all things
define('DEBUG4', false); // options processing debug and stop
define('DEBUG5', false); // debug print included in output table
define('DEBUG6', false); // show devices and stop
define('DEBUG7', false); // show what getallthings returns and stop
define('DEBUG8', false); // debug custom development - add info to doaction return
define("DONATE", true); // turn on or off the donate button
// turns on ?code=reset to invoke api - this is not secure but it will
// allow you to go to the reauth page without manually deleting cookies
define('ENABLERESET', true);
// set error reporting to just show fatal errors
error_reporting(E_ERROR);
// header and footer
function htmlHeader($skin="skin-housepanel") {
$tc = '<!DOCTYPE html>';
$tc.= '<html><head><title>House Panel</title>';
$tc.= '<meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type">';
// specify icon and color for windows machines
$tc.= '<meta name="msapplication-TileColor" content="#2b5797">';
$tc.= '<meta name="msapplication-TileImage" content="media/mstile-144x144.png">';
// specify icons for browsers and apple
$tc.= '<link rel="icon" type="image/png" href="media/favicon-16x16.png" sizes="16x16"> ';
$tc.= '<link rel="icon" type="image/png" href="media/favicon-32x32.png" sizes="32x32"> ';
$tc.= '<link rel="icon" type="image/png" href="media/favicon-96x96.png" sizes="96x96"> ';
$tc.= '<link rel="apple-touch-icon" href="media/apple-touch-icon.png">';
$tc.= '<link rel="shortcut icon" href="media/favicon.ico">';
// load jQuery and themes
// now load these locally and include them in the HP source for speed
// and to keep everything local
// $tc.= '<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">';
// $tc.= '<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>';
// $tc.= '<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>';
$tc.= '<link rel="stylesheet" href="jquery-ui.css">';
$tc.= '<script src="jquery-1.12.4.min.js"></script>';
$tc.= '<script src="jquery-ui.min.js"></script>';
// md5 function
$tc.= '<script src="md5.min.js"></script>';
// include hack from touchpunch.furf.com to enable touch punch through for tablets
$tc.= '<script src="jquery.ui.touch-punch.min.js"></script>';
// minicolors library
$tc.= '<script src="jquery.minicolors.min.js"></script>';
$tc.= '<link rel="stylesheet" href="jquery.minicolors.css">';
// analog clock support
$tc.= '<!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->';
$tc.= '<script type="text/javascript" src="coolclock.js"></script>';
// load main script file
$jshash = md5_file("housepanel.js");
$tc.= '<script type="text/javascript" src="housepanel.js?v=' . $jshash . '"></script>';
// check for valid skin folder
if (!$skin || !file_exists("$skin/housepanel.css")) {
$skin = "skin-housepanel";
}
// if this theme has a helper js then load it
if ( file_exists( $skin . "/housepanel-theme.js") ) {
$helperhash = md5_file($skin . "/housepanel-theme.js");
$tc.= "<script type=\"text/javascript\" src=\"$skin/housepanel-theme.js?v=" . $helperhash . "\"></script>";
}
// load tile editor fixed css file with cutomization helpers
$tejshash = md5_file("tileeditor.js");
$tecsshash = md5_file("tileeditor.css");
$tc.= "<script type=\"text/javascript\" src=\"tileeditor.js?v=" . $tejshash . "\"></script>";
$tc.= "<link id=\"tileeditor\" rel=\"stylesheet\" type=\"text/css\" href=\"tileeditor.css?v=" . $tecsshash . "\">";
// load tile customizer
$cm_hash = md5_file("customize.js");
$tc.= "<script type=\"text/javascript\" src=\"customize.js?v=" . $cm_hash . "\"></script>";
// load the main css file
$csshash = md5_file("$skin/housepanel.css");
$tc.= "<link rel=\"stylesheet\" type=\"text/css\" href=\"$skin/housepanel.css?v=" . $csshash . "\">";
// load the custom tile sheet if it exists
// replaced logic to make customizations skin specific
if (file_exists("$skin/customtiles.css")) {
$customhash = md5_file("$skin/customtiles.css");
$tc.= "<link id=\"customtiles\" rel=\"stylesheet\" type=\"text/css\" href=\"$skin/customtiles.css?v=". $customhash ."\">";
}
// begin creating the main page
$tc.= '</head><body>';
$tc.= '<div class="maintable">';
return $tc;
}
function htmlFooter() {
$tc = "";
$tc.= "</div>";
$tc.= "</body></html>";
return $tc;
}
// helper function to put a hidden field inside a form
function hidden($pname, $pvalue, $id = false) {
$inpstr = "<input type='hidden' name='$pname' value='$pvalue'";
if ($id) { $inpstr .= " id='$id'"; }
$inpstr .= " />";
return $inpstr;
}
function putdiv($value, $class) {
$tc = "<div class=\"" . $class . "\">" . $value . "</div>";
return $tc;
}
// function to make a curl call
function curl_call($host, $headertype="", $nvpstr="", $calltype="GET", $webcall=false)
{
//setting the curl parameters for GET to tack onto host
if ( $calltype==="GET" && $nvpstr) {
$host = $host . "?$nvpstr";
$nvpstr = "";
}
// tell curl where to go
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $host);
// add header setting if provided
if ($headertype) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headertype);
}
//turning off peer verification
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
if ($calltype==="POST") {
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_PUT, FALSE);
} else if ($calltype==="PUT") {
curl_setopt($ch, CURLOPT_POST, FALSE);
curl_setopt($ch, CURLOPT_PUT, TRUE);
} else {
curl_setopt($ch, CURLOPT_POST, FALSE);
curl_setopt($ch, CURLOPT_PUT, FALSE);
if ( $calltype!=="GET" ) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $calltype);
}
}
if ($nvpstr) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpstr);
}
//getting response from server
try {
$response = curl_exec($ch);
} catch (Exception $e) {
$response = false;
}
curl_close($ch);
// handle errors
if ($response===false) {
$nvpResArray = false;
} else {
// convert json returned by Groovy into associative array
if ( $webcall ) {
$nvpResArray = $response;
} else {
$nvpResArray = json_decode($response, TRUE);
// convert errors into false
if ( array_key_exists("error",$nvpResArray) && $nvpResArray["error"]===true ) {
$nvpResArray = false;
}
}
}
return $nvpResArray;
}
function getAlbumArt($artist, $album) {
$query = "https://www.google.com/search";
$nvpstr = "as_st=y&tbm=isch&as_epq=" . urlencode("$album by $artist");
$tc = curl_call($query . "?$nvpstr", false, "", "GET", true);
// get the location of the first image returned by Google
$ipos = strpos($tc, "table class=\"images_table\"");
if ( $ipos ) {
$imgpos = strpos($tc, "<img", $ipos);
$imgend = strpos($tc, ">", $imgpos);
$image = substr($tc, $imgpos, $imgend - $imgpos + 1);
} else {
$image = "";
}
return $image;
}
// return all devices in one call
// TODO: Implement logic to read Wink, OpenHab, and Vera hubs
function getDevices($allthings, $options, $hubnum, $hubType, $hubAccess, $hubEndpt, $clientId, $clientSecret) {
// we now always get all things at once
$specialtiles = getSpecials();
$host = $hubEndpt . "/getallthings";
$headertype = array("Authorization: Bearer " . $hubAccess);
$nvpreq = "client_secret=" . urlencode($clientSecret) . "&scope=app&client_id=" . urlencode($clientId);
$response = curl_call($host, $headertype, $nvpreq, "POST");
if (DEBUG6) {
echo "<br>Response<br><pre>";
print_r($response);
echo "</pre>";
exit(0);
}
// configure returned array with the "id"
if ($response && is_array($response) && count($response)) {
foreach ($response as $k => $content) {
$thetype = $content["type"];
$thevalue = $content["value"];
$id = $content["id"];
$thename = $content["name"];
// ** IMPT ** Users can hack the code here and add other tiles to fast poll
// like what is shown in comments below will fast poll Office Light
// be careful with this feature because polling is cloud intensive
// so you should only activate a small number of devices
// this hack is obsolete because any tile can change refresh
// to fast by changing the text of refresh in the customizer
// but this is still available
$reftype = "normal";
// if ( $thename==="Office Light" ) {
// $reftype = "fast";
// }
// make a unique index for this thing based on id and type
// new to this array is the hub number and hub type
// we also skip making any legacy type special tiles
if ( !array_key_exists($thetype, $specialtiles) ) {
$idx = $thetype . "|" . $id;
$allthings[$idx] = array("id" => $id, "name" => $thename,
"hubnum" => $hubnum,
"type" => $thetype,
"refresh"=>$reftype, "value" => $thevalue );
}
}
}
return $allthings;
}
// new function to get name from hub
function getName($hubAccess, $hubEndpt, $clientId, $clientSecret) {
$host = $hubEndpt . "/gethubinfo";
$headertype = array("Authorization: Bearer " . $hubAccess);
$nvpreq = "client_secret=" . urlencode($clientSecret) . "&scope=app&client_id=" . urlencode($clientId);
$response = curl_call($host, $headertype, $nvpreq, "POST");
return array( $response["sitename"], $response["hubId"] );
}
function fixHost($stweb) {
if ( substr(strtolower($stweb),0,4) !== "http" ) {
if ( preg_match("/{1,3}\d\.{1,3}\d\.{1,3}\d\.{1,3}\d/", $stweb) ) {
$stweb = is_ssl() . $stweb;
} else {
$stweb = "https://" . $stweb;
}
}
return $stweb;
}
// function to get authorization code
// this does a redirect back here with results
// this is the first step of the oauth flow
// the new logic works for both SmartThings and Hubitat
// TODO: Implement logic to obtain Wink and Vera auth codes
function getAuthCode($returl, $stweb, $clientId, $hubType) {
$nvpreq="response_type=code&client_id=" . urlencode($clientId) . "&scope=app&redirect_uri=" . urlencode($returl);
$location = $stweb . "/oauth/authorize?" . $nvpreq;
header("Location: $location");
}
// return access token from oauth flow
// this should work for both SmartThings and HousePanel
// TODO: Implement logic to read Wink and Vera hub access tokens
function getAccessToken($returl, $code, $stweb, $clientId, $clientSecret, $hubType) {
$host = $stweb . "/oauth/token";
$ctype = "application/x-www-form-urlencoded";
$headertype = array('Content-Type: ' . $ctype);
$nvpreq = "grant_type=authorization_code&code=" . urlencode($code) . "&client_id=" . urlencode($clientId) .
"&client_secret=" . urlencode($clientSecret) . "&redirect_uri=" . $returl;
$response = curl_call($host, $headertype, $nvpreq, "POST");
// save the access token
if ($response) {
$token = $response["access_token"];
} else {
$token = false;
}
return $token;
}
// changed this routine to only get endpoint
// since we now get the location name separately
// this only works if the clientid within theendpoint matches our auth version
// TODO: Implement logic to read Wink and Vera hub end points
function getEndpoint($access_token, $stweb, $clientId, $hubType) {
if ( $hubType==="SmartThings" ) {
$host = $stweb . "/api/smartapps/endpoints";
} else if ( $hubType ==="Hubitat" ) {
$host = $stweb . "/apps/api/endpoints";
} else {
$host = $stweb . "/api/smartapps/endpoints";
}
$headertype = array("Authorization: Bearer " . $access_token);
$response = curl_call($host, $headertype);
$endpt = false;
if ($response) {
if ( is_array($response) ) {
$endclientid = $response[0]["oauthClient"]["clientId"];
if ($endclientid === $clientId) {
$endpt = $response[0]["uri"];
}
} else {
$endclientid = $response["oauthClient"]["clientId"];
if ($endclientid === $clientId) {
$endpt = $response["uri"];
}
}
}
return $endpt;
}
// this used to create input blocks for auth page
// it was modified for use now on the options page
function tsk($timezone, $skin, $uname, $port, $webSocketServerPort, $fast_timer, $slow_timer) {
$tc= "";
$tc.= "<div class='inp'><label class=\"startupinp\">Skin: </label>";
$tc.= "<input id=\"skinid\" class=\"startupinp\" name=\"skin\" width=\"80\" type=\"text\" value=\"$skin\"/></div>";
$tc.= "<div><label class=\"startupinp\">Timezone: </label>";
$tc.= "<input id=\"newtimezone\" class=\"startupinp\" name=\"timezone\" width=\"80\" type=\"text\" value=\"$timezone\"/></div>";
$tc.= "<div><label class=\"startupinp\">Listen On Port: </label>";
$tc.= "<input id=\"newport\" class=\"startupinp\" name=\"port\" width=\"20\" type=\"text\" value=\"$port\"/></div>";
$tc.= "<div><label class=\"startupinp\">WebSocket Port: </label>";
$tc.= "<input id=\"newsocketport\" class=\"startupinp\" name=\"webSocketServerPort\" width=\"20\" type=\"text\" value=\"$webSocketServerPort\"/></div>";
$tc.= "<div><label class=\"startupinp\">Fast Timer: </label>";
$tc.= "<input id=\"newfast_timer\" class=\"startupinp\" name=\"fast_timer\" width=\"20\" type=\"text\" value=\"$fast_timer\"/></div>";
$tc.= "<div><label class=\"startupinp\">Slow Timer: </label>";
$tc.= "<input id=\"newslow_timer\" class=\"startupinp\" name=\"slow_timer\" width=\"20\" type=\"text\" value=\"$slow_timer\"/></div>";
$tc.= "<div><label for=\"uname\" class=\"startupinp\">Username: </label>";
$tc.= "<input id=\"uname\" class=\"startupinp\" name=\"uname\" width=\"20\" type=\"text\" value=\"$uname\"/></div>";
$tc.= "<div><label for=\"pword\" class=\"startupinp\">Set New Password: </label>";
$tc.= "<input id=\"pword\" class=\"startupinp\" name=\"pword\" width=\"80\" type=\"password\" value=\"\"/></div>";
$tc.= "<div><label></label><span class='indent typeopt'>(blank to keep prior)</span></div>";
$tc.= "<div></div><br />";
return $tc;
}
function getLoginPage($returnURL, $uname) {
$tc = "";
$tc.= "<h2>" . APPNAME . "</h2>";
$tc.= "<br /><br />";
$tc.= "<form name=\"login\" action=\"$returnURL\" method=\"POST\">";
$tc.= hidden("returnURL", $returnURL);
$tc.= hidden("pagename", "login");
$tc.= hidden("useajax", "dologin");
$tc.= hidden("id", "none");
$tc.= hidden("type", "none");
$tc.= "<div>";
$tc.= "<label for=\"uname\" class=\"startupinp\">Username: </label>";
$tc.= "<input id=\"uname\" name=\"uname\" width=\"20\" type=\"text\" value=\"$uname\"/>";
$tc.= "<br /><br />";
$tc.= "<label for=\"pword\" class=\"startupinp\">Password: </label>";
$tc.= "<input id=\"pword\" name=\"pword\" width=\"40\" type=\"password\" value=\"\"/>";
$tc.= "<br /><br />";
$tc.= "<input class=\"submitbutton\" value=\"Login\" name=\"submit\" type=\"submit\" />";
$tc.= "</div>";
$tc.= "</form>";
return $tc;
}
// screen that greets user and asks for authentication
function getAuthPage($returl, $hpcode, $hubset=null, $newthings=null) {
$tc = "";
$tc.= "<h2>" . APPNAME . "</h2>";
// provide welcome page with instructions for what to do
// this will show only if the user hasn't set up HP
// or if a reauth is requested or when converting old passwords
$tc.= "<div class=\"greeting\">";
$tc.="<p>Here is where you link a SmartThings or Hubitat hub to " .
"HousePanel to gain access to your smart home devices. " .
"You can link any number and combination of hubs. " .
"To link a hub you must have the following info: " .
"API URL, Client ID, and Client Secret. " .
"</p><br />";
$tc.= "<p><strong>*** IMPORTANT ***</strong> Information you provide here is secret and will be stored " .
"on your server in a configuration file called <i>hmoptions.cfg</i> " .
"This is why HousePanel should <strong>*** NOT ***</strong> be hosted on a public-facing website. " .
"A locally hosted website on a Raspberry Pi is the strongly recommended option. " .
"HousePanel does periodically store anonymized and encrypted use frequency data. " .
"By proceeding you are agreeing to this practice.</p>";
$tc.= "</div>";
if ( defined("DONATE") && DONATE===true ) {
$tc.= '<br /><h4>Donations appreciated for HousePanel support and continued improvement, but not required to proceed.</h4>
<br /><div><form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="XS7MHW7XPYJA4">
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form></div>';
}
// get the current settings from options file
// legacy file support removed
$options = readOptions();
$rewrite = false;
// first check for existence of options file
if ( !$options || !is_array($options) ) {
$options = array();
$configoptions = array();
$rewrite = true;
} else if ( !array_key_exists("config", $options) ) {
$configoptions = array();
$rewrite = true;
} else {
$configoptions = $options["config"];
}
// removed old legacy file handling since it was prone to errors
if ( array_key_exists("use_st", $configoptions) ||
array_key_exists("st_web", $configoptions) ||
array_key_exists("client_id", $configoptions) ||
array_key_exists("client_secret", $configoptions) ||
array_key_exists("hubitat_access", $configoptions) ||
array_key_exists("hubitat_endpt", $configoptions) ||
array_key_exists("use_he", $configoptions) ||
array_key_exists("hubitat_id", $configoptions) ||
array_key_exists("hubitat_host", $configoptions) ||
array_key_exists("use_he", $configoptions) ||
array_key_exists("user_sitename", $configoptions) )
{
$hubs = array();
$rewrite = true;
} else if ( array_key_exists("hubs", $configoptions) ) {
$hubs = $configoptions["hubs"];
} else {
$hubs = array();
$rewrite = true;
}
// get version and time info
// force rewrite of options if a new version
if ( array_key_exists("time", $options) ) {
$time = $options["time"];
$info = explode(" @ ", $time);
$version = $info[0];
$timestamp = $info[1];
$lastedit = date("M j, Y h:i:s a", $timestamp);
if ( $version != HPVERSION ) {
$rewrite = true;
}
} else {
$rewrite = true;
$lastedit = "Unknown";
$version = "Pre Version 1.7";
}
// removed clientinfo support - too old to make sense any more
// update the options with hub info
// all user provided skins are now tied to a user name under pwords
// last one given is saved here also in main area as the new default
if ( $rewrite ) {
$configoptions["hubs"] = $hubs;
$options["config"] = $configoptions;
writeOptions($options);
}
// add a new blank hub at the end for adding new ones
// note: the type must be "New" because js uses this to do stuff
$newhub = array("hubType"=>"New", "hubHost"=>"https://graph.api.smartthings.com",
"clientId"=>"", "clientSecret"=>"",
"userAccess"=>"", "userEndpt"=>"", "hubName"=>"", "hubId"=>"",
"hubTimer"=>120000, "hubAccess"=>"", "hubEndpt"=>"");
$hubs[] = $newhub;
$tc.= hidden("returnURL", $returl);
$tc.= hidden("pagename", "auth");
$tc.= "<div class=\"greetingopts\">";
$tc.= "<div><span class=\"startupinp\">Last update: $lastedit</span></div>";
// ------------------ general settings ----------------------------------
// $tc.= "<div id=\"tskwrapper\">";
// $tc.= tsk($timezone, $skin, $kiosk, $uname, $port, $webSocketServerPort, $fast_timer, $slow_timer);
// $tc.= "</div>";
if ( $hubset!==null && $newthings!==null && is_array($newthings) ) {
$defhub = strval($hubset);
$numnewthings = count($newthings);
$ntc= "Hub with hubId= $defhub was authorized and $numnewthings devices were retrieved.";
} else {
$defhub = strval($hubs[0]["hubId"]);
$ntc = "";
}
// $defhub = "1";
$tc.= "<div id=\"newthingcount\">$ntc</div>";
$tc.= "<div class='hubopt'><label for=\"pickhub\" class=\"startupinp\">Authorize which hub?</label>";
$tc.= "<select name=\"pickhub\" id=\"pickhub\" class=\"startupinp\">";
$i= 0;
foreach ($hubs as $hub) {
$hubName = $hub["hubName"];
$hubType = $hub["hubType"];
$hubId = strval($hub["hubId"]);
if ( $hubId === $defhub) {
$hubselected = "selected";
} else {
$hubselected = "";
}
$tc.= "<option value=\"$hubId\" $hubselected>Hub #$i ($hubType)</option>";
$i++;
}
$tc.= "</select></div>";
$tc.="<div id=\"authhubwrapper\">";
$i = 0;
foreach ($hubs as $hub) {
putStats($hub);
$hubType = $hub["hubType"];
$hubId = strval($hub["hubId"]);
if ( $hubId === $defhub) {
$hubclass = "authhub";
} else {
$hubclass = "authhub hidden";
}
$tc.="<div id=\"authhub_$hubId\" hubid=\"$hubId\" hubtype=\"$hubType\" class=\"$hubclass\">";
$tc.= "<form id=\"hubform_$hubId\" class=\"houseauth\" action=\"" . $returl . "\" method=\"POST\">";
$tc.= hidden("doauthorize", $hpcode);
// $tc.= hidden("hubnum", $i);
// we use this div below to grab the hub type dynamically chosen
$tc.= "<div id=\"hubdiv_$hubId\"><label class=\"startupinp\">Hub Type: </label>";
$tc.= "<select name=\"hubType\" class=\"startupinp\">";
$st_select = $he_select = $w_select = $v_select = $o_select = "";
if ( $hubType==="SmartThings" ) { $st_select = "selected"; }
if ( $hubType==="Hubitat" ) { $he_select = "selected"; }
// if ( $hubType==="Wink" ) { $w_select = "selected"; }
// if ( $hubType==="Vera" ) { $v_select = "selected"; }
// if ( $hubType==="OpenHab" ) { $o_select = "selected"; }
$tc.= "<option value=\"SmartThings\" $st_select>SmartThings</option>";
$tc.= "<option value=\"Hubitat\" $he_select>Hubitat</option>";
// $tc.= "<option value=\"Wink\" $w_select>Wink</option>";
// $tc.= "<option value=\"Vera\" $v_select>Vera</option>";
// $tc.= "<option value=\"OpenHab\" $o_select>OpenHab</option>";
$tc.= "</select></div>";
if ( !$hub["hubHost"] ) {
$hub["hubHost"] = "https://graph.api.smartthings.com";
}
$tc.= "<div><label class=\"startupinp required\">API Url: </label>";
$tc.= "<input class=\"startupinp\" title=\"Enter the hub OAUTH address here\" name=\"hubHost\" width=\"80\" type=\"text\" value=\"" . $hub["hubHost"] . "\"/></div>";
$tc.= "<div><label class=\"startupinp required\">Client ID: </label>";
$tc.= "<input class=\"startupinp\" name=\"clientId\" width=\"80\" type=\"text\" value=\"" . $hub["clientId"] . "\"/></div>";
$tc.= "<div><label class=\"startupinp required\">Client Secret: </label>";
$tc.= "<input class=\"startupinp\" name=\"clientSecret\" width=\"80\" type=\"text\" value=\"" . $hub["clientSecret"] . "\"/></div>";
$tc.= "<div><label class=\"startupinp\">Fixed Access Token: </label>";
$tc.= "<input class=\"startupinp\" name=\"userAccess\" width=\"80\" type=\"text\" value=\"" . $hub["userAccess"] . "\"/></div>";
$tc.= "<div><label class=\"startupinp\">Fixed Endpoint: </label>";
$tc.= "<input class=\"startupinp\" name=\"userEndpt\" width=\"80\" type=\"text\" value=\"" . $hub["userEndpt"] . "\"/></div>";
$tc.= "<div><label class=\"startupinp\">Hub Name: </label>";
$tc.= "<input class=\"startupinp\" name=\"hubName\" width=\"80\" type=\"text\" value=\"" . $hub["hubName"] . "\"/></div>";
$tc.= hidden("hubId", $hubId);
$tc.= "<div><label class=\"startupinp required\">Refresh Timer: </label>";
$tc.= "<input class=\"startupinp\" name=\"hubTimer\" width=\"10\" type=\"text\" value=\"" . $hub["hubTimer"] . "\"/></div>";
$tc.= "<input class=\"hidden\" name=\"hubAccess\" type=\"hidden\" value=\"" . $hub["hubAccess"] . "\"/>";
$tc.= "<input class=\"hidden\" name=\"hubEndpt\" type=\"hidden\" value=\"" . $hub["hubEndpt"] . "\"/>";
$tc.= "<div>";
$tc .= "<input hub=\"$i\" hubid=\"$hubId\" class=\"authbutton hubauth\" value=\"Authorize Hub #$i\" type=\"button\" />";
$tc .= "<input hub=\"$i\" hubid=\"$hubId\" class=\"authbutton hubdel\" value=\"Remove Hub #$i\" type=\"button\" />";
$tc.= "</div>";
$tc.= "</form>";
$tc.= "</div>";
$i++;
}
$tc.= "</div>";
$tc.= "<div id=\"authmessage\"></div>";
$tc.= "<input id=\"cancelauth\" class=\"authbutton\" value=\"Done Authorizing\" name=\"cancelauth\" type=\"button\" />";
return $tc;
}
function getCustomName($defname, $idx, $options) {
$rooms = $options["rooms"];