forked from d0ughb0y/Chauvet16
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.htm
2168 lines (2159 loc) · 76.4 KB
/
index.htm
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
<!--Copyright (c) 2013 by Jerry Sy aka d0ughb0y-->
<!--mail-to: j3rry5y@gmail.com-->
<!--Commercial use of this software without-->
<!--permission is absolutely prohibited.-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css"/>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://omnipotent.net/jquery.sparkline/2.1.2/jquery.sparkline.min.js"></script>
<script type="text/javascript">
$(document).bind("mobileinit", function () {
$.mobile.page.prototype.options.addBackBtn=true;
$.mobile.page.prototype.options.backBtnTheme="c";
$.mobile.page.prototype.options.headerTheme="a";
$.mobile.page.prototype.options.contentTheme="c";
$.mobile.page.prototype.options.footerTheme="a";
$.mobile.listview.prototype.options.theme="d";
$.mobile.collapsible.prototype.options.theme="b";
$.mobile.collapsible.prototype.options.contentTheme="c";
$.mobile.defaultPageTransition="slide";
$.event.special.swipe.horizontalDistanceThreshold=100;
if (localStorage.length<=1) {
localStorage.setItem('timeout', 8000);
localStorage.setItem('httpretries', 1);
}
$.ajaxSetup({
timeout:localStorage.getItem('timeout'),
cache:true
});
});
</script>
<style type="text/css">
#Outlets .ui-li, #Inputs .ui-li, #outloglist .ui-li, #sensorloglist .ui-li, #loglist .ui-li {
padding-bottom: 0;
padding-top: 0;
font-weight: normal;
}
#sensorloglist .ui-block-a, #outloglist .ui-block-a { width: 55%; }
#outloglist .ui-block-b { width: 35%; }
#outloglist .ui-block-c { width: 10%; }
#loglist .ui-li { font-size: small; }
#csform label.ui-select, #csform label.ui-input-text {
width: 20% !important; display: inline-block !important;
}
#csform .ui-select, #csform .ui-input-text {
width: 70% !important; display: inline-block !important;
}
.tooltip {
display: none;
font-size: 10pt;
position: absolute;
border: 1px solid #000000;
background-color: #FFFFE0;
padding: 2px 6px;
color: #990000;
}
.sensor {
width: 70%;
min-width: 200px;
max-width: 400px;
height: 65px;
float: left;
}
.sensor_name {
height: 20px;
font-size: large;
}
.sensor_spark {
height: 30px;
}
.sensor_mma {
height: 15px;
font-size: small;
}
.sensor2 {
width: 80px;
height: 65px;
float: right;
}
.sensor_value {
height: 50px;
font-size: 36px;
line-height: 60px;
text-align: center;
}
.sensor_sg {
height: 15px;
font-size: small;
text-align: center;
}
.pump {
width: 90%;
height: 65px;
}
.pump_info {
height: 20px;
font-size: large;
}
.pump_spark {
height: 30px;
}
.pump_footer {
height: 15px;
font-size: small;
}
#perc {
width: 120px;
height: 20px;
border: 1px solid blue;
position: relative;
}
#perc_in {
position: absolute;
background: #0FF;
top: 0;
left: 0;
height: 100%;
}
#perc_txt {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
font-size: medium;
text-align: center;
z-index: 100;
}
</style>
<script src="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.js"></script>
<meta content="width=device-width, minimum-scale=1, maximum-scale=1" name="viewport"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
</head>
<body>
<div data-role="page" id="page">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<a href="#setup" data-role="button" data-icon="gear" data-iconpos="notext" class="ui-btn-left" data-iconshadow="false"></a>
<h1 id="hostname"></h1>
<a data-role="button" data-icon="refresh" data-iconpos="notext" data-iconshadow="false" class="ui-btn-right" onClick="lastupdatetime=new Date(0);updateStatus(true);"></a>
</div>
<div data-role="content">
<div data-role="collapsible-set" data-inset="false">
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false" id="SensorBar" data-collapsed="false">
<h2>Sensors</h2>
<ul data-role="listview" id="Sensors"></ul>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false" id="OutletsBar">
<h2>Outlets</h2>
<ul data-role="listview" id="Outlets"></ul>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false" id="InputsBar">
<h2>Inputs</h2>
<ul data-role="listview" id="Inputs"></ul>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false"id="PWMPumpsBar">
<h2>PWM Pumps</h2>
<ul data-role="listview" id="PWMPumps"></ul>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false"id="DosersBar">
<h2>Dosing Pumps</h2>
<ul data-role="listview" id="Dosers"><li>Coming soon ...</li></ul>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false"id="LEDsBar">
<h2>LEDs</h2>
<ul data-role="listview" id="LEDs"><li>Coming soon ...</li></ul>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false" style="text-decoration:none" id="MacrosBar">
<h2>Feed</h2>
<div data-role="controlgroup">
<button onClick="setFeed(0);" value="Feed A" id="feed1"/>
<button onClick="setFeed(1);" value="Feed B" id="feed2"/>
<button onClick="setFeed(2);" value="Feed C" id="feed3"/>
<button onClick="setFeed(3);" value="Feed D" id="feed4"/>
<button onClick="setFeed(4);" value="Feed Cancel" id="feedcancel"/>
</div>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false" id="webcam">
<h2>WebCam</h2>
<img id="camImg" width="290px" src=""/>
<button onClick="startPlay();" id="playbtn">Play</button>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false">
<h2>Logs and Setup</h2>
<ul data-role="listview">
<li><a href="#outlog">Outlet Log</a></li>
<li><a href="#sensorlog">Sensor Log</a></li>
<li><a href="#log">Controller Log</a></li>
<li><a href="#-" id="filemanager">File Manager</a></li>
<li><a href="#csetup">Controller Setup</a></li>
</ul>
</div>
<div data-role="collapsible" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" data-inset="false">
<h2>Apex Info</h2>
<div data-role="listview">
<li>Software<span class="ui-li-aside" id="software">1.0</span></li>
<li>Hardware<span class="ui-li-aside" id="hardware">1.0</span></li>
<li>Serial<span class="ui-li-aside" id="serial">1.0</span></li>
<li>Timezone<span class="ui-li-aside" id="timezone">1.0</span></li>
<li data-role="list-divider">Power</li>
<li>Failed<span class="ui-li-aside" id="powerfailed">none</span></li>
<li>Restored<span class="ui-li-aside" id="powerrestored">none</span></li>
</div>
</div>
</div>
</div>
<div data-role="footer" data-position="fixed" data-tap-toggle="false">
<a href="#info" data-role="button" data-icon="info" data-iconpos="notext" class="ui-btn-left" data-iconshadow="false"></a>
<h1 id="footertxt"></h1>
<a href="#doc" data-role="button" data-icon="search" data-iconpos="notext" class="ui-btn-right" data-iconshadow="false"></a>
</div>
</div>
<div data-role="page" id="outlog">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Outlet Log</h1>
</div>
<div data-role="content">
<ul data-role="listview" data-filter="true" id="outloglist"></ul>
</div>
</div>
<div data-role="page" id="sensorlog">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Sensor Log</h1>
</div>
<div data-role="content">
<ul data-role="listview" data-filter="true" id="sensorloglist"></ul>
</div>
</div>
<div data-role="page" id="doc">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Documentation</h1>
</div>
<div data-role="content" id="doccontents">
<p>m-apex (or mobile apex) was written as a supplement to the Apex app for smartphones. You can edit the html source
to your liking.</p>
<p>Unlike the Apex app, this does not continuously connect to the apex to get data. Other than the initial
connection to get the html page and load the initial data, this program does not consume any data bandwidth until
you click on the refresh button or view a section or log. This works better for smartphones without unlimited data
plan.</p>
<div data-role="collapsible-set">
<div data-role="collapsible">
<h2>Setup</h2>
<p>This program will work as is with the default Setup values.</p>
<p>If you use a webcam on your aquarium, you can enter the URL to retrieve a jpg or mjpeg image.</p>
<p>For Foscam compatible cameras, use this URL for image http://host:port/snapshot.cgi and this URL for server
push mjpeg http://host:port/videostream.cgi?resolution=8&rate=11</p>
<p><strong>Do not use server push if you do not have unlimited data plan on your mobile device!!!</strong></p>
<p>The Connection Timeout is the number of seconds before the browser will disconnect if Apex does not respond,
and Connection Retries is the number of times to retry in case of a failed connection. A spinner status will
be displayed during a connection, and the retry attempt number is displayed if applicable.</p>
<p>You must click on the Save button at the top right of the page to save the values you entered.</p>
</div>
<div data-role="collapsible">
<h2>Header</h2>
<p>The header section displays the Apex Host name in the center, with the Setup button to the left, and Refresh
button to the right. The Refresh button updates the Sensors and Outlets section if they are expanded.</p>
</div>
<div data-role="collapsible">
<h2>Sensors</h2>
<p>This section displays all your Apex sensors (probes) and their values. A chart of the last 24 hours is
displayed, with the min, max and avg values shown below. If you have a Conductivity probe and it returns
salinity values, the calculated specific gravity value will be displayed under the salinity value.</p>
</div>
<div data-role="collapsible">
<h2>Outlets</h2>
<p>This displays all the outlets defined on your Apex and allows you to manually turn On or Off the outlet.</p>
<p>The outlet name is color coded as follows: green = On, red = off, blue = Unknown and orange = Profile.</p>
<p>For outlets running on a profile, if you tap the outlet name, the profile name will display as a tooltip.</p>
<p>When you click on On or Off, only the outlet command is sent to the Apex.</p>
<p>If you click on the Auto button, an Update command is sent 600ms later to retrieve the outlet status so the
appropriate outlet name color can be applied.</p>
</div>
<div data-role="collapsible">
<h2>Inputs</h2>
<p>This section displays all the input switch states.</p>
</div>
<div data-role="collapsible">
<h2>Feed</h2>
<p>This section allows you to execute the Feed modes defined on the Apex. When you exectue a Feed program, the
feed buttons will be disabled and a countdown timer will display the remaining time on the feed program
currently running.</p>
</div>
<div data-role="collapsible">
<h2>WebCam</h2>
<p>If you entered a webcam image url in the Setup section, this will display an image from the webcam.</p>
<p>If you click on the Play button, it will play 15 frames 500ms apart to simulate a short video clip if the url
is to a jpg image. If the url is a videostream, then it starts playing the live webcam video.</p>
</div>
<div data-role="collapsible">
<h2>Apex Logs and Setup</h2>
<p>This section allows you to view Apex Outlet Log and Sensor Data Log. The log list is filterable to show only
lines that matches the search characters you type in.</p>
<p>For Outlet Log, switch your smartphone to landscape orientation if you have outlets running on profiles to
see the profile name. </p>
<p>You can also open the Apex Outlets setup page, the Apex Profiles setup page and the full Apex page in a new
browser window in this section.</p>
</div>
<div data-role="collapsible">
<h2>Apex Info</h2>
<p>This section displays miscellaneous information about your Apex device.</p>
</div>
<div data-role="collapsible">
<h2>Footer</h2>
<p>The center displays the timestamp the Outlets section was updated. The Info button is on the left and
Documentation button is on the right.</p>
</div>
</div>
</div>
</div>
<div data-role="page" id="info">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>m-apex v1.05</h1>
</div>
<div data-role="content">
<h2>Software License Agreement</h2>
<p>BEFORE INSTALLING THIS SOFTWARE, YOU SHOULD CAREFULLY READ THE FOLLOWING TERMS AND CONDITIONS. INSTALLING THE
SOFTWARE INDICATES YOUR ACCEPTANCE OF THE TERMS AND CONDITIONS. IF YOU DO NOT AGREE TO THE TERMS OF THIS
AGREEMENT, DO NOT INSTALL THE SOFTWARE. </p>
<p>The owner of m-apex licenses its software (hereinafter referred to as the "Software", including any accompanying
documentation) to you only upon the condition that you accept all of the terms contained in this license agreement
("Agreement"). </p>
<div data-role="collapsible-set">
<div data-role="collapsible">
<h2>GRANT OF USE.</h2>
<p>Software is copyrighted and title to Software and all associated intellectual property rights are retained by
Jerry Sy (hereinafter referred to as the "owner"). Software is confidential information of the owner.<BR> <BR>
The owner grants you a non-exclusive license to use the software in personal or non commercial purposes.<BR>
<BR> You may not remove or obscure any copyright and trademark notices relating to the Software.<BR> <BR> You
may not reverse engineer, decompile, or disassemble the Software, except and only to the extent that such
activity is expressly permitted by applicable law notwithstanding this limitation.<BR> <BR> You may not
sub-license, transfer, assign or distribute the Software or any of your rights or obligations under this
License Agreement, in whole or in part. </p>
</div>
<div data-role="collapsible">
<h2>TERM AND TERMINATiON</h2>
<p>This Agreement is effective until terminated. You may terminate this Agreement at any time by destroying all
copies of Software. This Agreement will terminate immediately without notice from the owner if you fail to
comply with any provision of this Agreement. Upon termination, you must destroy all copies of Software. </p>
</div>
<div data-role="collapsible">
<h2>DiSCLAIMER OF WARRANTY</h2>
<p>The Software is provided "AS IS" WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE IMPLIED WARRANTY OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. </p>
</div>
<div data-role="collapsible">
<h2>LIMITATION OF LIABILITY</h2>
<p><strong>NO LIABILITY FOR CONSEQUENTIAL DAMAGES.</strong> THE ENTIRE RISK ARISING OUT OF THE USE OR
PERFORMANCE OF THIS SOFTWARE AND DOCUMENTATION REMAINS WITH YOU. IN NO EVENT WILL THE OWNER BE LIABLE FOR ANY
LOST PROFITS, LOST SAVINGS, INCIDENTAL OR INDIRECT DAMAGES OR OTHER ECONOMIC CONSEQUENTIAL DAMAGES, EVEN IF
THE OWNER OR ITS AUTHORIZED SUPPLER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ADDITION THE OWNER
AND ITS SUPPLIERS WILL NOT BE LIABLE FOR ANY DAMAGES CLAIMED BY YOU BASED ON ANY THIRD PARTY CLAIM.<BR> <BR>
Some jurisdictions do not allow the exclusion of implied warranties, or the limitation for consequential
damages, so the above may not apply to you. </p>
</div>
<div data-role="collapsible">
<h2>COPYRIGHT</h2>
<p>Contents of this file, program code, concepts, and user interfaces are © 2013 and beyond by Jerry
Sy.<BR> <br> Nepture Apex is trademarked and copyrighted by Neptune Systems.<BR>
</p>
</div>
<div data-role="collapsible">
<h2>CREDITS</h2>
<p>Thanks to Russ Michel for providing access to his Apex for testing and development.<br> <br> This program
uses jQuery, jQuery Mobile, sparkline.</p>
</div>
<div data-role="collapsible">
<h2>AUTHOR</h2>
<p>You may contact the author to provide comments, suggestions and bug report about this program or request
program customization. Contact via <a href="mailto:j3rry5y@gmail.com">email</a> or via IM at
<a target="_blank" href="http://www.reefcentral.com/forums/member.php?u=186039">d0ughb0y@ReefCentral</a> or
<a target="_blank" href="http://www.reef2reef.com/forums/member.php?u=11889">doughboy@Reef2Reef.</a>
</p>
</div>
</div>
</div>
</div>
<div data-role="page" id="setup">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h2>Setup</h2>
<a data-role="button" class="ui-btn-right" data-theme="c" data-icon="check" data-iconpos="right" onClick="saveSetup();">Save</a>
</div>
<div data-role="content">
<div data-role="fieldcontain">
<label for="webcamurl">Webcam Image URL:</label>
<input type="url" name="textinput2" id="webcamurl" value="" data-mini="true"/>
</div>
<div data-role="fieldcontain">
<label for="webcamname">Webcam Login:</label>
<input type="text" name="textinput3" id="webcamname" value="" data-mini="true"/>
</div>
<div data-role="fieldcontain">
<label for="webcampass">Webcam Password:</label>
<input type="password" name="passwordinput2" id="webcampass" value="" data-mini="true"/>
</div>
<div data-role="fieldcontain">
<label for="httptimeout">Connection Timeout in seconds:</label>
<input type="range" name="slider" id="httptimeout" value="10" min="5" max="15" data-highlight="false" data-mini="true"/>
</div>
<div data-role="fieldcontain">
<label for="httpretries">Connection Retries:</label>
<input type="range" name="slider1" id="httpretries" value="1" min="0" max="5" data-highlight="false" data-mini="true"/>
</div>
</div>
</div>
<div data-role="page" id="log">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Controller Log</h1>
</div>
<div data-role="content">
<ul data-role="listview" id="loglist" data-filter="true"></ul>
</div>
<div data-role="footer" data-position="fixed" data-tap-toggle="false">
<h1 id="logfooter"></h1>
</div>
</div>
<div data-role="page" id="csetup">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Controller Setup</h1>
</div>
<div data-role="content">
<ul data-role="listview" id="csetupitemlist">
<li><a href="#csform">Outlets</a></li>
<li><a href="#csform">Macros</a> </li>
<li><a href="#csform">Actions</a></li>
<li><a href="#csform">PWM Pumps</a></li>
<li><a href="#csform">Dosers</a></li>
<li><a href="#csform">LEDs</a></li>
<li><a href="#csform">Misc</a></li>
</ul>
</div>
</div>
<div data-role="page" id="csform">
<div data-role="header" data-position="fixed" data-tap-toggle="true">
<a data-role="button" class="ui-btn-right" data-theme="c" data-icon="check" data-iconpos="right" onClick="saveconfig();">Save</a>
<h1 id="csformhdr">Controller Setup</h1>
</div>
<div data-role="content">
<div id="contentoutlets">
<label for="selectrec" class="select">Name:</label>
<select name="selectmenu" id="selectrec" data-mini="true"></select> <label for="initoff">Off:</label>
<input type="text" name="initoff" id="initoff" value="" data-mini="true"/> <label for="ontime">On:</label>
<input type="text" name="ontime" id="ontime" value="" data-mini="true"/> <label for="offtime">Off:</label>
<input type="text" name="offtime" id="offtime" value="" data-mini="true"/> <label for="cycletime">Cycle:</label>
<input type="text" name="cycletime" id="cycletime" value="" readonly data-mini="true"/>
<label for="days" class="select">Days:</label>
<select name="days" id="days" multiple data-native-menu="false" data-mini="true">
<option value="1">Sun</option>
<option value="2">Mon</option>
<option value="3">Tue</option>
<option value="4">Wed</option>
<option value="5">Thu</option>
<option value="6">Fri</option>
<option value="7">Sat</option>
</select> <label for="mode" class="select">Mode:</label> <select name="mode" id="mode" data-mini="true">
<option value="0">Auto</option>
<option value="1" disabled>Manual</option>
<option value="2">Disabled</option>
<option value="3" disabled>Macro</option>
</select>
</div>
<div id="contentactions">
<label for="selectmacro" class="select">Name:</label>
<select name="selectmacro" id="selectmacro" data-mini="true"> </select>
<ul data-role="listview" id="actionlist" style="margin-top:10px"></ul>
</div>
<div id="contentpumps">
<div data-role="navbar" id="pumpnav" data-mini="true">
<ul>
<li><a href="#" class="ui-btn-active ui-state-persist" id="pumpnav0">Pump 0</a> </li>
<li><a href="#" id="pumpnav1">Pump 1</a> </li>
</ul>
</div>
<div data-role="content">
<div>
<ul data-role="listview" id="pumpprogramlist">
</ul>
</div>
</div>
</div>
<div id="contentdosers">
<h1>Coming Soon...</h1>
</div>
<div id="contentleds">
<h1>Coming Soon...</h1>
</div>
<div id="contentmisc">
<h4 style="text-align:center">pH Calibration</h4>
<section class="ui-grid-b">
<div class="ui-block-a">
<button id="ph7btn" data-mini="true" data-theme="c" onClick="phcal({'cal':'s'});"/>
</div>
<div class="ui-block-b">
<button id="ph10btn" data-mini="true" data-theme="c" onClick="phcal({'cal':'t'})"/>
</div>
<div class="ui-block-c"><input type="text" readonly id="phval" data-mini="true" value=""/></div>
</section>
<h4 style="text-align:center">Temp Control</h4>
<section class="ui-grid-b">
<div class="ui-block-a">Heater:</div>
<div class="ui-block-b"><input type="number" id="htrlow" data-mini="true" value=""/></div>
<div class="ui-block-c"><input type="number" id="htrhigh" data-mini="true" value=""/></div>
<div class="ui-block-a">Fan:</div>
<div class="ui-block-b"><input type="number" id="fanlow" data-mini="true" value=""/></div>
<div class="ui-block-c"><input type="number" id="fanhigh" data-mini="true" value=""/></div>
</section>
<h4 style="text-align:center">Sonar Alert</h4>
<section class="ui-grid-b">
<div class="ui-block-a">Range:</div>
<div class="ui-block-b"><input type="number" id="sonarlow" data-mini="true" value=""/></div>
<div class="ui-block-c"><input type="number" id="sonarhigh" data-mini="true" value=""/></div>
<div class="ui-block-a">Low Alert Value:</div>
<div class="ui-block-b"><input type="number" id="sonaralertval" data-mini="true" value=""/></div>
<div class="ui-block-c"><input type="text" readonly id="sonarval" data-mini="true" value=""/></div>
</section>
<div><label for="sonaralert">Sonar Alert</label><input type="checkbox" id="sonaralert"/></div>
<h4 style="text-align:center">Alerts</h4>
<section class="ui-grid-b">
<div class="ui-block-a">Temp:</div>
<div class="ui-block-b"><input type="number" id="alerttemplow" data-mini="true" value="74.0"/></div>
<div class="ui-block-c"><input type="number" id="alerttemphigh" data-mini="true" value="82.0"/></div>
<div class="ui-block-a">pH:</div>
<div class="ui-block-b"><input type="number" id="alertphlow" data-mini="true" value="7.6"/></div>
<div class="ui-block-c"><input type="number" id="alertphhigh" data-mini="true" value="8.6"/></div>
</section>
<section class="ui-grid-a">
<div class="ui-block-a"><label for="sndalert">Sound</label><input type="checkbox" id="sndalert"/></div>
<div class="ui-block-b"><label for="emailalert">Email</label><input type="checkbox" id="emailalert"/>
</div>
</section>
<label for="resetconf">Reset:</label> <input type="number" id="resetconf" data-mini="true" value=""/>
</div>
</div>
</div>
<div data-role="page" id="editaction">
<div data-role="header">
<h1>Edit Action</h1>
</div>
<div data-role="content">
<label for="editoutlet">Outlet:</label>
<input type="number" name="editoutlet" id="editoutlet" value="" data-mini="true"/> <label for="editoff">Init
Off:</label> <input type="number" name="editoff" id="editoff" value="" data-mini="true"/> <label for="editon">On
Time:</label> <input type="number" name="editon" id="editon" value="" data-mini="true"/>
<button onClick="saveaction();" data-mini="true">Save</button>
</div>
</div>
<div data-role="page" id="pumpprogramedit">
<div data-role="header">
<h1>Pump Program</h1>
</div>
<div data-role="content">
<label for="_wavemode" class="select">Wave Mode:</label>
<select name="_wavemode" id="_wavemode" data-mini="true"> </select>
<label for="_syncmode" class="select">Sync Mode:</label>
<select name="_syncemode" id="_syncmode" data-mini="true">
<option value="0">Master</option>
<option value="1">Sync</option>
<option value="2">Anti-Sync</option>
</select>
<div data-role="fieldcontain">
<label for="_level">Max Speed:</label>
<input type="range" name="_slider2" id="_level" value="50" min="0" max="100" step="1" data-highlight="true"
data-mini="true"/>
</div>
<div data-role="fieldcontain">
<label for="pulsewidth">Pulse Width:</label>
<input type="range" name="_slider3" id="_pulsewidth" value="10" min="0" max="10" step="1" data-highlight="true"
data-mini="true"/>
</div>
<button onclick="savePumpProgram();" data-mini="true">Save</button>
</div>
</div>
<div data-role="page" id="pwmpage">
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h1 id="pumpname"></h1>
</div>
<div data-role="content" id="pwmpagecontent">
<label for="wavemode" class="select">Wave Mode:</label>
<select name="wavemode" id="wavemode" data-mini="true"> </select>
<label for="syncmode" class="select">Sync Mode:</label>
<select name="syncemode" id="syncmode" data-mini="true">
<option value="0">Master</option>
<option value="1">Sync</option>
<option value="2">Anti-Sync</option>
</select>
<div data-role="fieldcontain">
<label for="level">Max Speed:</label>
<input type="range" name="slider2" id="level" value="50" min="0" max="100" step="1" data-highlight="true" data-mini="true"/>
</div>
<div data-role="fieldcontain">
<label for="pulsewidth">Pulse Width:</label>
<input type="range" name="slider3" id="pulsewidth" value="10" min="0" max="10" step="1" data-highlight="true" data-mini="true"/>
</div>
<div data-role="controlgroup" data-type="horizontal" data-mini="true" style="text-align: center">
<input name="pumpauto" id="pumpauto" value="1" checked="checked" type="radio" data-mini="true">
<label for="pumpauto">Auto</label>
<input name="pumpauto" id="pumpmanual" value="0" type="radio" data-mini="true">
<label for="pumpmanual">Manual</label>
</div>
</div>
</div>
<script type="text/javascript">
var retry=0;
var maxretries=localStorage.getItem('httpretries');
var initialized=false;
var lastupdatetime=new Date(0);
var sensoropen=false;
var imageupdatecount=0;
var sensordata={names:[], record:[]};
var outlogdata={record:[]};
var feedactive=false;
var feedexpanded=false;
var feedcounter=0;
var feedname=0;
var feedtimeoutfunction;
var feedactivefunction;
var logpath="";
var config={};
var macros=[];
var outletname=[];
var action=[];
var pwmpumpsopen=false;
var updatewavefunction;
var pwmbarwidth=6;
var pwmpumpsconfig=[];
var pwmpumpsidx=0;
var pwmpumpspgmitem=-1;
var wavedef=[];
var csitem=-1;
$("#page").on('pageinit',function(e){
initHome();
}).on('pagebeforeshow',function(e){
if (initialized) updateStatus();
}).on("pageshow", function(e){});
$('#setup').on("pagebeforeshow", function (e) {
$('#httptimeout').val(localStorage.getItem('timeout')/1000).slider('refresh');
$('#httpretries').val(localStorage.getItem('httpretries')).slider('refresh');
$('#webcamurl').val(localStorage.getItem('webcamurl'));
$('#webcamname').val(localStorage.getItem('webcamname'));
$('#webcampass').val(localStorage.getItem('webcampass'));
});
$('#sensorlog').on("pagebeforeshow",function (e) {
$('#sensorloglist').empty().listview('refresh');
$('input[data-type="search"]').val("");
$('input[data-type="search"]').trigger("change");
}).on("pageshow", function () {
setTimeout(function () {
getsensordata(loadsensor);
}, 0);
});
$('#outlog').on("pagebeforeshow",function (e) {
$('#outloglist').empty().listview('refresh');
$('input[data-type="search"]').val("");
$('input[data-type="search"]').trigger("change");
}).on("pageshow", function (e) {
if (logpath.length==19)
loadfileoutlog(); else
loadoutlog();
});
$('#pwmpage').on('pageinit', function(e){
$('#syncmode').selectmenu();
$('#wavemode').selectmenu();
$('#level').slider();
$('#pulsewidth').slider();
}).on("pagebeforeshow", function(e){
$('#pumpname').html('Pump '+pwmpumpsidx);
$('#level').on('slidestop', function(e){
pwmpagechange(e);
});
$('#pulsewidth').on('slidestop', function(e){
pwmpagechange(e);
});
updatepwmpagefields();
clearTimeout(updatewavefunction);
}).on("pageshow",function(){
}).on('pagebeforehide', function(e){
lastupdatetime=new Date(0);
updateStatus();
}).on('change', '#pulsewidth, #level, #wavemode, #syncmode, #pumpauto, #pumpmanual', function (e,ui){
if (e.target.id=='pulsewidth' ||e.target.id=='level') {
if (e.originalEvent === undefined) return;
}
pwmpagechange(e);
});
function pwmpagechange(e) {
var tmp=e.target.value;
if (e.target.id=='level') {
if (tmp==100) tmp=255; else tmp=parseInt(tmp)*255/100;
tmp=tmp.toFixed(0);
if (tmp==pwmpumpsconfig[pwmpumpsidx].level)return;
pwmpumpsconfig[pwmpumpsidx].level=tmp;
} else if (e.target.id=='pulsewidth') {
if (tmp==pwmpumpsconfig[pwmpumpsidx].pulseWidth) return;
pwmpumpsconfig[pwmpumpsidx].pulseWidth=tmp;
} else if (e.target.id=='wavemode') {
if (tmp==pwmpumpsconfig[pwmpumpsidx].waveMode) return;
pwmpumpsconfig[pwmpumpsidx].waveMode=tmp;
} else if (e.target.id=='syncmode') {
if (tmp==pwmpumpsconfig[pwmpumpsidx].syncMode) return;
pwmpumpsconfig[pwmpumpsidx].syncMode=tmp;
} else if (e.target.id=='pumpauto') {
if (tmp==pwmpumpsconfig[pwmpumpsidx].pumpAuto) return;
pwmpumpsconfig[pwmpumpsidx].pumpAuto=tmp;
} else if (e.target.id=='pumpmanual') {
if (tmp==pwmpumpsconfig[pwmpumpsidx].pumpAuto) return;
pwmpumpsconfig[pwmpumpsidx].pumpAuto=tmp;
} else return;
$.ajax('pwmset.json', {
data:"{\"channel\":\""+pwmpumpsidx+"\",\""+e.target.id+"\":\""+tmp+"\"}",
contentType:'application/json',
type:'POST'}).always(function () {
}).done(function (data) {
pwmpumpsconfig=data.pwmpumps;
updatepwmpagefields();
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log(textStatus);
});
}
$('#Outlets').on("change", $("input[type='radio']"), function (event, ui) {
var olet=event.target;
var val=$(olet).val();
var name=olet.name;
var dev=name.replace('_state', '');
var s=name+"="+val+"&noResponse=1";
$.mobile.loading('show', {text:'Sending command...', textVisible:true});
$.post("/cgi-bin/status.cgi", s,function (data) {
if (val=="0") {
lastupdatetime=new Date(0);
setTimeout(updateStatus, 600);
} else if (val=="1") {
$('#'+dev).attr('style', 'color:#F00').addClass('refresh');
} else if (val=="2") {
$('#'+dev).attr('style', 'color:#090').addClass('refresh');
}
}).always(function () {
$.mobile.loading('hide');
}).fail(function (jqXHR, textStatus, errorThrown) {
alert(textStatus);
updateStatus();
});
});
$('#webcam').on('collapse',function () {
imageupdatecount=0;
$('#camImg').off('load error');
$('#camImg').attr('src', '');
$.mobile.loading('hide');
}).on('expand', function (e) {
var wcurl=getwebcamurl();
var videopush=false;
if (wcurl!="") {
if (wcurl.indexOf("videostream.cgi")!= -1) {
videopush=true;
if ($(window).width()>320)
$('#camImg').removeAttr('width'); else
$('#camImg').attr('width', '290px');
} else {
$('#camImg').attr('width', '290px');
}
$.mobile.loading('show', {text:'Loading Image...', textVisible:true});
var firstparam=wcurl.indexOf('?')== -1;
$('#camImg').attr('src', wcurl+(firstparam?'?':'&')+'_='+Math.random());
$('#camImg').one('load error', function (e) {
$.mobile.loading('hide');
if (e.type=='error') {
alert('Unable to load image.');
$('#playbtn').button('disable');
} else {
$('#playbtn').button(videopush?'disable':'enable');
}
});
} else {
$('#camImg').attr('src', '');
$('#playbtn').button('disable');
}
});
$('#SensorBar').on('collapse',function () {
sensoropen=false;
}).on('expand', function (e) {
if (!sensoropen) {
sensoropen=true;
if (initialized)
setTimeout(function () {
updateStatus(true);
}, 0);
}
});
$('#OutletsBar').on('expand',function (e) {
if (initialized) {
if ($('#Outlets li').length>12) {
$('#page [data-role=header], #page [data-role=footer]').fixedtoolbar("hide");
}
setTimeout(updateStatus, 0);
}
}).on('collapse', function () {
if ($('#Outlets li').length>12) {
$('#page [data-role=header], #page [data-role=footer]').fixedtoolbar("show");
}
});
$('#InputsBar').on('expand', function (e) {
setTimeout(updateStatus, 0);
});
$('#PWMPumpsBar').on('expand',function(e){
if (!pwmpumpsopen) {
pwmpumpsopen=true;
if (initialized) {
setTimeout(function() {updateStatus(0);},0);
// updatewavefunction = setTimeout("getpwmpumpdata();",0);
}
}
}).on('collapse',function(){
pwmpumpsopen=false;
clearTimeout(updatewavefunction);
});
$('#MacrosBar').on('expand',function (e) {
if (!feedexpanded) {
feedexpanded=true;
updateFeedTime();
}
}).on('collapse', function () {
feedexpanded=false;
clearTimeout(feedtimeoutfunction);
});
$('#outloglist').on('orientationchange', function (e) {
if (e.orientation=='landscape') {
$('#outloglist .ui-block-a').css('width', '40%');
$('#outloglist .ui-block-b').css('width', '35%');
$('#outloglist .ui-block-c').css('width', '25%');
} else if (e.orientation=='portrait') {
$('#outloglist .ui-block-a').css('width', '55%');
$('#outloglist .ui-block-b').css('width', '35%');
$('#outloglist .ui-block-c').css('width', '10%');
}
$('#outloglist').listview('refresh');
});
$('#Sensors').on('orientationchange', function (e) {
if (e.orientation=='landscape') {
$('.sensor').css('width', '80%');
} else if (e.orientation=='portrait') {
$('.sensor').css('width', '70%');
}
getSparkData();
});
$('#PWMPumps').on('orientationchange', function(e){
if (e.orientation=='landscape'){
pwmbarwidth=10;
} else if (e.orientation=='portrait'){
pwmbarwidth=6;
}
}).on('click', 'li a', function(e){
pwmpumpsidx = $(this).closest('li').index();
});
$('#csetupitemlist').on('click','li a', function(e){
csitem = $(this).closest('li').index();
})
$(document).on('mouseenter', '.outlettext',function (e) {
var toolTip=$(this).attr('tooltip');
$('<span class="tooltip"></span>').text(toolTip).appendTo('body').css('top', (e.pageY-10)+'px').css('left', (e.pageX+20)+'px').fadeIn('slow');
}).on('mouseleave', '.outlettext',function () {
$('.tooltip').remove();
}).on('mousemove', '.outlettext', function (e) {
$('.tooltip').css('top', (e.pageY-10)+'px').css('left', (e.pageX+20)+'px');
});
function getpwmpumpdata() {
$.getJSON('pwmpumpdata.json',function(data){
for (var i in data.pwmpumpdata) {
var x = data.pwmpumpdata[i].data;
$(".pump"+i+"_gr").sparkline(x, { type: 'bar', height:"100%",chartRangeMin:"0",chartRangeMax:"255",barWidth:pwmbarwidth, barSpacing:"1",
disableInteraction:true, barColor:(pwmpumpsconfig[i].pumpAuto=='1'?'blue':'gray')});
}
}).always(function(){
if (pwmpumpsopen && $.mobile.activePage[0].id=='page'){
clearTimeout(updatewavefunction);
updatewavefunction=setTimeout('getpwmpumpdata();',1000);
}
}).fail(function(jqXHR, textStatus,errorThrown){});
}
function getsensordata(callback) {
var initstring="Updating";
if (retry>0) {
initstring=initstring+" "+retry;
}
initstring+="...";
if (initialized)
$.mobile.loading('show', {text:initstring, textVisible:true});
var timenow=new Date();
var timeincr=0;
timenow.setDate(timenow.getDate()-1);
if (sensordata.record.length>0) {
timeincr=calculateLogInterval();
$.each(sensordata.record, function (i, r) {
var currentTime=new Date(r.date);
if (currentTime.getTime()>=timenow.getTime()) {
for (var x=0; x<i-1; x++)
sensordata.record.shift();
if (sensordata.record.length>0) {
timenow=new Date(sensordata.record[sensordata.record.length-1].date);
timenow.setTime(timenow.getTime()+timeincr);
}
return false;
}
});
}
if (timenow.getTime()>(new Date()).getTime()) {
if (typeof callback=="function")
callback();
$.mobile.loading('hide');
return;
}
var sdate=(timenow.getFullYear()+' ').substring(2, 4)+checkTime(timenow.getMonth()+1)+checkTime(timenow.getDate())+checkTime(timenow.getHours())+checkTime(timenow.getMinutes());
$.get("/cgi-bin/datalog.xml?sdate="+sdate+"&days=31",function (data) {
var records=data.getElementsByTagName("record");
if (records.length>0&&sensordata.names.length==0) {
var names=records[0].getElementsByTagName("name");
for (var i=0; i<names.length; i++) {
sensordata.names.push(names[i].childNodes[0].nodeValue);
}
}
$.each(records, function (i, rec) {
var probes=rec.getElementsByTagName("probe");
var rec={'date':rec.getElementsByTagName("date")[0].childNodes[0].nodeValue, data:[]};
$.each(probes, function (j, probe) {
rec.data.push(probe.getElementsByTagName("value")[0].childNodes[0].nodeValue);
});
sensordata.record.push(rec);
});
if (typeof callback=="function") callback();
}).fail(function (jqXHR, textStatus, errorThrown) {
if (retry>=maxretries) {
alert('unable to connect to apex.');
retry=0;
return;
}
retry++;
console.log(textStatus);
setTimeout(getsensordata, 1);
}).always(function () {
if (initialized)
$.mobile.loading('hide');
}).done(function (f) {
retry=0;
});
}
function loadoutlog() {
var initstring="Loading";
if (retry>0) {
initstring=initstring+" "+retry;
}
initstring+="...";
$.mobile.loading('show', {text:initstring, textVisible:true});
var timenow=new Date();
timenow.setDate(timenow.getDate()-1);
$.each(outlogdata.record, function (i, r) {
var currentTime=new Date(r.date);
if (currentTime.getTime()>=timenow.getTime()) {
for (var x=0; x<i-1; x++)
outlogdata.record.shift();
if (outlogdata.record.length>0) {
timenow=new Date(outlogdata.record[outlogdata.record.length-1].date);
timenow.setTime(timenow.getTime()+60000);
}
return false;
}
});
var sdate=(timenow.getFullYear()+' ').substring(2, 4)+checkTime(timenow.getMonth()+1)+checkTime(timenow.getDate())+checkTime(timenow.getHours())+checkTime(timenow.getMinutes());
$.get("/cgi-bin/outlog.xml?sdate="+sdate+"&days=31",function (data) {
var records=data.getElementsByTagName("record");
$.each(records, function (i, rec) {
var datetxt=rec.getElementsByTagName("date")[0].childNodes[0].nodeValue;
var name=rec.getElementsByTagName("name")[0].childNodes[0].nodeValue;
var value=rec.getElementsByTagName("value")[0].childNodes[0].nodeValue;
outlogdata.record.push({'date':datetxt, 'name':name, 'value':value});
});
$.each(outlogdata.record, function (i, rec) {
var divblocka=$('<div class="ui-block-a">'+rec.date+'</div>');
var divblockb=$('<div class="ui-block-b">'+rec.name+'</div>');
var divblockc=$('<div class="ui-block-c">'+rec.value+'</div>');
var sec=$('<section class="ui-grid-b">');
sec.append(divblocka, divblockb, divblockc);
var olist=$('<li/>');
olist.append(sec);
$('#outloglist').append(olist);
});
$('#outloglist').listview('refresh');
}).always(function () {
$.mobile.loading('hide');
}).fail(function (jqXHR, textStatus, errorThrown) {
if (retry>=maxretries) {
alert('unable to connect to apex.');
retry=0;
history.back();
return;
}
retry++;
setTimeout(loadoutlog, 1);
}).done(function (f) {
retry=0;
});
}