Browse code

minor revision

Gandolf authored on 07/06/2021 21:04:25
Showing 1 changed files
... ...
@@ -324,6 +324,7 @@ var chartPeriod = 1;
324 324
 var objBlink;
325 325
 
326 326
 function main() {
327
+    httpRequest.timeout = 3000;
327 328
 
328 329
     if (location.hostname.match(/.local/g) == null) {
329 330
         customSelector.style.visibility = "hidden";
... ...
@@ -338,6 +339,7 @@ function main() {
338 339
             displayOfflineStatus();
339 340
         }
340 341
     };
342
+
341 343
     httpRequest.ontimeout = function(e) {
342 344
         displayOfflineStatus();
343 345
     };
... ...
@@ -350,8 +352,7 @@ function main() {
350 352
 }
351 353
 
352 354
 function getSensorData() {
353
-    httpRequest.open("GET", SENSOR_DATA_URL, true);
354
-    httpRequest.timeout = 3000;
355
+    httpRequest.open("POST", SENSOR_DATA_URL, true);
355 356
     httpRequest.send();
356 357
 }
357 358
 
Browse code

minor revisions

Gandolf authored on 06/23/2021 20:00:08
Showing 1 changed files
... ...
@@ -40,6 +40,21 @@ p {
40 40
     color: red;
41 41
     /*border: 1px solid black;*/
42 42
 }
43
+#logoBox {
44
+    display:inline-block;
45
+    width:20%;
46
+    /*border:1px solid black;*/
47
+}
48
+#logoInfo {
49
+    padding-left: 20px;
50
+    padding-top: 10px;
51
+    display:inline-block;
52
+    width:45%;
53
+    font:14px arial,sans-serif;
54
+    text-align:left;
55
+    vertical-align: top;
56
+   /*border:1px solid black;*/
57
+}
43 58
 .rowContainer {
44 59
     display: table;
45 60
     width: 100%;
... ...
@@ -64,10 +79,12 @@ p {
64 79
 }
65 80
 .chartContainer {
66 81
     padding: 2px;
67
-    border: 1px solid black;
82
+    /*border: 1px solid black;8?
68 83
 }
69 84
 img.chart {
70 85
     width: 100%;
86
+    height: 233px;
87
+    /*border: 1px solid black;*/
71 88
 }
72 89
 span.chartNav {
73 90
     margin: auto;
... ...
@@ -106,17 +123,21 @@ text.chartNav {
106 123
 
107 124
 <div id="mainContainer">
108 125
 
109
-<h2><a href="https://github.com/fractalxaos/ham/tree/master/nodepower" 
126
+<div id="logoBox">
127
+<a href="https://github.com/fractalxaos/ham/tree/master/nodepower" 
110 128
 style="text-decoration:none" target="_new">
111
-Node Power</a></h2>
112
-<h4>WA7ABU Victor Point AREDN Mesh Installation</h4>
129
+<img src="static/npwlogo.png"></a>
130
+</div>
113 131
 
114
-<div style="width:60%; text-align:left; font:14px arial, sans-serif;">
115
-This web page shows the power consumption of above node(s).  Charts below
116
-provide a historical glimpse of node power consumption, battery and ambient
117
-temperatures.
132
+<div id="logoInfo">
133
+This web page shows the power consumption of the node installation at the site listed below.  The charts provide a historical glimpse for different time periods
134
+of node power consumption, battery and ambient temperatures.
118 135
 </div>
119 136
 
137
+<a href="http://wa7abu-pb400-vp-0ccd.local.mesh:8080/cgi-bin/status"
138
+ style="text-decoration:none" target="_new">
139
+<h4>WA7ABU Victor Point AREDN Mesh Installation</h4></a>
140
+
120 141
 <div id="datetime">
121 142
 <text id="date"></text>
122 143
 &nbsp;&nbsp;
... ...
@@ -237,7 +258,7 @@ Status:
237 258
 <b>NOTES:</b>
238 259
 <ul>
239 260
 <li>Node sensor project plans and software available at
240
-<a href="https://github.com/fractalxaos/ham/tree/master/nodepower" target="_new">
261
+<a href="https://github.com/fractalxaos/ham/archive/master.zip" target="_new">
241 262
 <i>Github.com</i>
242 263
 </a>.</li>
243 264
 <li>Project plans and software also available via the mesh
Browse code

minor revisions

Gandolf authored on 06/23/2021 01:42:11
Showing 1 changed files
... ...
@@ -240,6 +240,10 @@ Status:
240 240
 <a href="https://github.com/fractalxaos/ham/tree/master/nodepower" target="_new">
241 241
 <i>Github.com</i>
242 242
 </a>.</li>
243
+<li>Project plans and software also available via the mesh
244
+<a href="http://ka7jlo-web.local.mesh/file-manager/files/KA7JLO/Apps/nodepower/nodepower.zip" target="_new">
245
+<i>here</i>
246
+</a>.</li>
243 247
 <li>Project plans include detailed instructions on how to use a Raspberry
244 248
     Pi Zero to add power bus and battery temperature monitoring 
245 249
     for your AREDN node.</li>
Browse code

improve readibility

Gandolf authored on 06/20/2021 00:49:50
Showing 1 changed files
... ...
@@ -7,7 +7,6 @@
7 7
 <title>Node Power</title>
8 8
 <meta charset="UTF-8">
9 9
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
10
-<link rel="shortcut icon" href="/favicon.png" type="image/x-icon" />
11 10
 <style>
12 11
 body {
13 12
     background-image: url("static/chalk.jpg");
... ...
@@ -387,7 +386,7 @@ function displayData(dataItem) {
387 386
     battemp_t.innerHTML = battemp.toFixed(2).toString();
388 387
     ambtemp_t.innerHTML = ambtemp.toFixed(2).toString();
389 388
 
390
-    period_t.innerHTML = dataItem.period / 60;
389
+    period_t.innerHTML = dataItem.chartUpdateInterval / 60;
391 390
     status_t.innerHTML = "online";
392 391
     status_t.style.color = "green";
393 392
 }
Browse code

added custom charts

Gandolf authored on 06/12/2021 06:28:37
Showing 1 changed files
... ...
@@ -95,6 +95,11 @@ text.chartNav {
95 95
     display: inline-block;
96 96
     padding: 8px 12px;
97 97
 }
98
+#iframe_a {
99
+    border:none;
100
+    width:100%;
101
+    height:1075px;
102
+}
98 103
 </style>
99 104
 </head>
100 105
 
... ...
@@ -179,10 +184,34 @@ Status:
179 184
 4 weeks</text></li>
180 185
 <li class="chartNav"><text class="chartNav" onclick="setChartPeriod(3)">
181 186
 12 months</text></li>
187
+
188
+<li id="customSelector" class="chartNav">
189
+<text  class="chartNav"
190
+ onclick="setChartPeriod(0)">Custom…</text></li>
191
+
182 192
 </ul>
183 193
 </span>
194
+
195
+<div class="rowContainer" id="customChartsContainer" style="display:none;">
196
+<div class="currentDataCell">
197
+<form id="fmDateSelector" action="power.php" method="post"
198
+ target="iframe_a">
199
+<label for="beginDate">Begin Date: </label>
200
+<input id="beginDate" name="beginDate" type="date" value="mm/dd/yyyy" />
201
+<label for="endDate">End Date: </label>
202
+<input id="endDate" name="endDate" type="date" value="mm/dd/yyyy" />
203
+<br><br>
204
+<input type="button" onclick="getCustomCharts()" value="Get Charts">
205
+</form>
206
+<span id="errorMsg"></span><br>
207
+<iframe id="iframe_a" name="iframe_a"></iframe>
208
+</div>
209
+</div>
210
+
184 211
 <br>
185 212
 
213
+<div class="rowContainer" id="stockChartsContainer">
214
+
186 215
 <div class="chartContainer">
187 216
 <img class="chart" id="current_g">
188 217
 </div>
... ...
@@ -203,6 +232,8 @@ Status:
203 232
 <img class="chart" id="ambtemp_g">
204 233
 </div>
205 234
 
235
+</div>
236
+
206 237
 <div id="notes">
207 238
 <b>NOTES:</b>
208 239
 <ul>
... ...
@@ -253,16 +284,27 @@ var status_t = document.getElementById("status");
253 284
 var period_t = document.getElementById("period");
254 285
 var alertmsg_t = document.getElementById("alertmsg");   
255 286
 
287
+var customChartsContainer = document.getElementById("customChartsContainer");
288
+var stockChartsContainer = document.getElementById("stockChartsContainer");
289
+var fmDateSelector = document.getElementById("fmDateSelector");
290
+var errorMsg = document.getElementById("errorMsg");
291
+var customSelector = document.getElementById("customSelector");
292
+
256 293
 /* Global objects */
257 294
 
258 295
 var httpRequest = new XMLHttpRequest();
259 296
 
260 297
 /* Global variables */
261 298
 
262
-var graphPeriod;
299
+var chartPeriod = 1;
263 300
 var objBlink;
264 301
 
265 302
 function main() {
303
+
304
+    if (location.hostname.match(/.local/g) == null) {
305
+        customSelector.style.visibility = "hidden";
306
+    }
307
+
266 308
     // Register call back function to process client http requests
267 309
     httpRequest.onreadystatechange = function() {
268 310
         if (httpRequest.readyState == 4 && httpRequest.status == 200) {
... ...
@@ -276,8 +318,8 @@ function main() {
276 318
         displayOfflineStatus();
277 319
     };
278 320
 
321
+    initializeDateSelector();
279 322
     getSensorData();
280
-    graphPeriod = 1;
281 323
     getSensorGraphs();
282 324
     setInterval(getSensorData, 2000);
283 325
     setInterval(getSensorGraphs, 600000);
... ...
@@ -290,15 +332,22 @@ function getSensorData() {
290 332
 }
291 333
 
292 334
 function setChartPeriod(n) {
293
-    graphPeriod = n;
294
-    getSensorGraphs();
335
+    chartPeriod = n;
336
+    if (n == 0) {
337
+        customChartsContainer.style.display = "block";
338
+        stockChartsContainer.style.display = "none";
339
+    } else {
340
+        customChartsContainer.style.display = "none";
341
+        stockChartsContainer.style.display = "block";
342
+        getSensorGraphs();
343
+    }
295 344
 }
296 345
 
297 346
 function getSensorGraphs() {
298 347
     var d = new Date;
299 348
     var pfx;
300 349
 
301
-    switch(graphPeriod) {
350
+    switch(chartPeriod) {
302 351
         case 1:
303 352
             pfx = "24hr_";
304 353
             break;
... ...
@@ -403,6 +452,23 @@ function displayHostTime() {
403 452
         "  <small>(UTC" + timeZoneShift + localTimeZone + ")</small>";
404 453
 }
405 454
 
455
+function initializeDateSelector() {
456
+    var d = new Date();
457
+
458
+    var dEnd = new Date(d.getFullYear(),
459
+               d.getMonth(), d.getDate() - 0);
460
+
461
+    var dBegin = new Date(d.getFullYear(),
462
+               d.getMonth(), d.getDate() - 1);
463
+
464
+    document.getElementById("beginDate").valueAsDate = dBegin;
465
+    document.getElementById("endDate").valueAsDate = dEnd;
466
+}
467
+
468
+function getCustomCharts() {
469
+    fmDateSelector.submit();
470
+}
471
+
406 472
 </script>
407 473
 
408 474
 </body>
Browse code

modify charts

Gandolf authored on 06/07/2021 20:50:02
Showing 1 changed files
... ...
@@ -36,6 +36,11 @@ p {
36 36
     text-align: left;
37 37
     padding: 10px;
38 38
 }
39
+#alert {
40
+    font: bold 22px arial, sans-serif;
41
+    color: red;
42
+    /*border: 1px solid black;*/
43
+}
39 44
 .rowContainer {
40 45
     display: table;
41 46
     width: 100%;
... ...
@@ -60,7 +65,7 @@ p {
60 65
 }
61 66
 .chartContainer {
62 67
     padding: 2px;
63
-    /*border: 1px solid black;*/
68
+    border: 1px solid black;
64 69
 }
65 70
 img.chart {
66 71
     width: 100%;
... ...
@@ -114,6 +119,11 @@ temperatures.
114 119
 <text id="time"></text>
115 120
 </div>
116 121
 
122
+<div id="alert">
123
+<text id="alertmsg" style="opacity: 0;">
124
+Warning: Low Battery Voltage</text>
125
+</div>
126
+
117 127
 <div class="rowContainer">
118 128
 
119 129
 <div class="currentDataCell">
... ...
@@ -122,10 +132,10 @@ Current:<br>
122 132
 Voltage:<br>
123 133
 Power:
124 134
 </div>
125
-<div class="dataItems" style="width: 30%;">
135
+<div class="dataItems" style="width: 40%;">
126 136
 <text id="current"></text> mA<br>
127 137
 <text id="voltage"></text> V<br>
128
-<text id="power"></text> mW
138
+<text id="power"></text> W
129 139
 </div>
130 140
 </div>
131 141
 
... ...
@@ -215,10 +225,12 @@ Status:
215 225
 </div>
216 226
 
217 227
 <script>
228
+"use strict";
218 229
 
219 230
 /* Global constants */
220 231
 
221
-var sensorDataUrl = "dynamic/powerData.js";
232
+var SENSOR_DATA_URL = "dynamic/powerData.js";
233
+var CRITICAL_VOLTAGE = 11.0;
222 234
 
223 235
 /* Global DOM objects */
224 236
 
... ...
@@ -238,7 +250,8 @@ var power_t = document.getElementById("power");
238 250
 var battemp_t = document.getElementById("battemp");    
239 251
 var ambtemp_t = document.getElementById("ambtemp");    
240 252
 var status_t = document.getElementById("status");
241
-var period_t = document.getElementById("period");    
253
+var period_t = document.getElementById("period");
254
+var alertmsg_t = document.getElementById("alertmsg");   
242 255
 
243 256
 /* Global objects */
244 257
 
... ...
@@ -247,6 +260,7 @@ var httpRequest = new XMLHttpRequest();
247 260
 /* Global variables */
248 261
 
249 262
 var graphPeriod;
263
+var objBlink;
250 264
 
251 265
 function main() {
252 266
     // Register call back function to process client http requests
... ...
@@ -270,7 +284,7 @@ function main() {
270 284
 }
271 285
 
272 286
 function getSensorData() {
273
-    httpRequest.open("GET", sensorDataUrl, true);
287
+    httpRequest.open("GET", SENSOR_DATA_URL, true);
274 288
     httpRequest.timeout = 3000;
275 289
     httpRequest.send();
276 290
 }
... ...
@@ -303,17 +317,27 @@ function getSensorGraphs() {
303 317
 }
304 318
 
305 319
 function displayData(dataItem) {
320
+    var voltage, current, power, ambtemp, battemp, powerWatts
321
+
322
+    voltage = Number(dataItem.voltage);
323
+    current = Number(dataItem.current);
324
+    power = Number(dataItem.power)/1000.0;
325
+    battemp = Number(dataItem.battemp);
326
+    ambtemp = Number(dataItem.ambtemp);
327
+
328
+    if (voltage < CRITICAL_VOLTAGE) {
329
+        displayAlert();
330
+    } else {
331
+        clearAlert();
332
+    }
333
+
306 334
     displayTime(dataItem);
307
-    current_t.innerHTML = 
308
-        (Number(dataItem.current).toFixed(2)).toString();
309
-    voltage_t.innerHTML = 
310
-        (Number(dataItem.voltage).toFixed(2)).toString();
311
-    power_t.innerHTML = 
312
-        (Number(dataItem.power).toFixed(2)).toString();
313
-    battemp_t.innerHTML = 
314
-        (Number(dataItem.battemp).toFixed(2)).toString();
315
-    ambtemp_t.innerHTML = 
316
-        (Number(dataItem.ambtemp).toFixed(2)).toString();
335
+    current_t.innerHTML = current.toFixed(2).toString(); 
336
+    voltage_t.innerHTML = voltage.toFixed(2).toString();
337
+    power_t.innerHTML = power.toFixed(2).toString();
338
+    battemp_t.innerHTML = battemp.toFixed(2).toString();
339
+    ambtemp_t.innerHTML = ambtemp.toFixed(2).toString();
340
+
317 341
     period_t.innerHTML = dataItem.period / 60;
318 342
     status_t.innerHTML = "online";
319 343
     status_t.style.color = "green";
... ...
@@ -331,6 +355,20 @@ function displayOfflineStatus() {
331 355
     status_t.style.color = "red";
332 356
 }
333 357
 
358
+function displayAlert() {
359
+    if (typeof objBlink == "undefined") {
360
+    objBlink = setInterval(function() {
361
+            alertmsg_t.style.opacity = 
362
+                (alertmsg_t.style.opacity == 0 ? 1 : 0);
363
+    }, 1000);
364
+    }
365
+}
366
+
367
+function clearAlert() {
368
+    clearInterval(objBlink);
369
+    alertmsg_t.style.opacity = 0;
370
+}
371
+
334 372
 function displayTime(dataItem) {
335 373
     var date, time, hourminute;
336 374
     var localDateObj, localTimeZone, timeZoneShift;
Browse code

base release

Gandolf authored on 06/02/2021 02:54:40
Showing 1 changed files
... ...
@@ -100,10 +100,10 @@ text.chartNav {
100 100
 <h2><a href="https://github.com/fractalxaos/ham/tree/master/nodepower" 
101 101
 style="text-decoration:none" target="_new">
102 102
 Node Power</a></h2>
103
-<h4>YOUR NODE DESCRIPTION AND CALL SIGN</h4>
103
+<h4>WA7ABU Victor Point AREDN Mesh Installation</h4>
104 104
 
105 105
 <div style="width:60%; text-align:left; font:14px arial, sans-serif;">
106
-This web page shows the power consumption of the above node.  Charts below
106
+This web page shows the power consumption of above node(s).  Charts below
107 107
 provide a historical glimpse of node power consumption, battery and ambient
108 108
 temperatures.
109 109
 </div>
... ...
@@ -135,8 +135,8 @@ Battery Temperature:<br>
135 135
 Ambient Temperature:
136 136
 </div>
137 137
 <div class="dataItems"  style="width: 30%;">
138
-<text id="battemp"></text> &#8451;<br>
139
-<text id="ambtemp"></text> &#8451;<br>
138
+<text id="battemp"></text> &#8457;<br>
139
+<text id="ambtemp"></text> &#8457;<br>
140 140
 </div>
141 141
 </div>
142 142
 </div>
Browse code

Initial release

Gandolf authored on 05/29/2021 20:13:39
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,371 @@
1
+<!DOCTYPE html>
2
+<!-- Courtesy ruler for editing this file
3
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
4
+-->
5
+<html>
6
+<head>
7
+<title>Node Power</title>
8
+<meta charset="UTF-8">
9
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+<link rel="shortcut icon" href="/favicon.png" type="image/x-icon" />
11
+<style>
12
+body {
13
+    background-image: url("static/chalk.jpg");
14
+}
15
+h2 {
16
+    font: bold 24px arial, sans-serif;
17
+}
18
+h4 {
19
+    font: bold 16px arial, sans-serif;
20
+}
21
+p {
22
+    font: normal 14px arial, sans-serif;
23
+}
24
+#mainContainer {
25
+    width: 740px;
26
+    text-align: center;
27
+    margin: auto;
28
+}
29
+#datetime {
30
+    padding: 10px;
31
+    font: bold 22px arial, sans-serif;
32
+    /*border: 1px solid black;*/
33
+}
34
+#notes {
35
+    font: 17px arial, sans-serif;
36
+    text-align: left;
37
+    padding: 10px;
38
+}
39
+.rowContainer {
40
+    display: table;
41
+    width: 100%;
42
+    /*border: 1px solid black;*/
43
+}
44
+.currentDataCell {
45
+    width: 50%;
46
+    padding: 10px;
47
+    font: bold 18px arial, sans-serif;
48
+    text-align: center;
49
+    display: table-cell;
50
+    vertical-align: middle;
51
+    /*border: 1px solid black;*/
52
+}
53
+.dataItems {
54
+    padding: 2px;
55
+    text-align: left;
56
+    line-height: 130%;
57
+    display: inline-block;
58
+    vertical-align: middle;
59
+    /*border: 1px solid black;*/
60
+}
61
+.chartContainer {
62
+    padding: 2px;
63
+    /*border: 1px solid black;*/
64
+}
65
+img.chart {
66
+    width: 100%;
67
+}
68
+span.chartNav {
69
+    margin: auto;
70
+}
71
+ul.chartNav {
72
+    list-style-type: none;
73
+    margin: 10px;
74
+    padding: 0;
75
+    overflow: hidden;
76
+    background-color: #bbb;
77
+    text-align: center;
78
+}
79
+li.chartNav {
80
+    display: inline-block;
81
+    font: bold 18px arial, sans-serif;
82
+    color: black;
83
+}
84
+text.chartNav:hover {
85
+    background-color: #333;
86
+    cursor: pointer;
87
+    color: white;
88
+}
89
+text.chartNav {
90
+    display: inline-block;
91
+    padding: 8px 12px;
92
+}
93
+</style>
94
+</head>
95
+
96
+<body onload="main()">
97
+
98
+<div id="mainContainer">
99
+
100
+<h2><a href="https://github.com/fractalxaos/ham/tree/master/nodepower" 
101
+style="text-decoration:none" target="_new">
102
+Node Power</a></h2>
103
+<h4>YOUR NODE DESCRIPTION AND CALL SIGN</h4>
104
+
105
+<div style="width:60%; text-align:left; font:14px arial, sans-serif;">
106
+This web page shows the power consumption of the above node.  Charts below
107
+provide a historical glimpse of node power consumption, battery and ambient
108
+temperatures.
109
+</div>
110
+
111
+<div id="datetime">
112
+<text id="date"></text>
113
+&nbsp;&nbsp;
114
+<text id="time"></text>
115
+</div>
116
+
117
+<div class="rowContainer">
118
+
119
+<div class="currentDataCell">
120
+<div class="dataItems">
121
+Current:<br>
122
+Voltage:<br>
123
+Power:
124
+</div>
125
+<div class="dataItems" style="width: 30%;">
126
+<text id="current"></text> mA<br>
127
+<text id="voltage"></text> V<br>
128
+<text id="power"></text> mW
129
+</div>
130
+</div>
131
+
132
+<div class="currentDataCell">
133
+<div class="dataItems">
134
+Battery Temperature:<br>
135
+Ambient Temperature:
136
+</div>
137
+<div class="dataItems"  style="width: 30%;">
138
+<text id="battemp"></text> &#8451;<br>
139
+<text id="ambtemp"></text> &#8451;<br>
140
+</div>
141
+</div>
142
+</div>
143
+
144
+<div class="rowContainer">
145
+<div class="currentDataCell">
146
+<div class="dataItems">
147
+Charts update every
148
+</div>
149
+<div class="dataItems">
150
+<text id="period"></text> minutes.<br>
151
+</div>
152
+</div>
153
+<div class="currentDataCell">
154
+<div class="dataItems">
155
+Status:
156
+</div>
157
+<div class="dataItems">
158
+<text id="status"></text><br>
159
+</div>
160
+</div>
161
+</div>
162
+
163
+<span class="chartNav">
164
+<ul class="chartNav">
165
+<li class="chartNav">Select charts:</li>
166
+<li class="chartNav"><text class="chartNav" onclick="setChartPeriod(1)">
167
+24 hours</text></li>
168
+<li class="chartNav"><text class="chartNav" onclick="setChartPeriod(2)">
169
+4 weeks</text></li>
170
+<li class="chartNav"><text class="chartNav" onclick="setChartPeriod(3)">
171
+12 months</text></li>
172
+</ul>
173
+</span>
174
+<br>
175
+
176
+<div class="chartContainer">
177
+<img class="chart" id="current_g">
178
+</div>
179
+
180
+<div class="chartContainer">
181
+<img class="chart" id="voltage_g">
182
+</div>
183
+
184
+<div class="chartContainer">
185
+<img class="chart" id="power_g">
186
+</div>
187
+
188
+<div class="chartContainer">
189
+<img class="chart" id="battemp_g">
190
+</div>
191
+
192
+<div class="chartContainer">
193
+<img class="chart" id="ambtemp_g">
194
+</div>
195
+
196
+<div id="notes">
197
+<b>NOTES:</b>
198
+<ul>
199
+<li>Node sensor project plans and software available at
200
+<a href="https://github.com/fractalxaos/ham/tree/master/nodepower" target="_new">
201
+<i>Github.com</i>
202
+</a>.</li>
203
+<li>Project plans include detailed instructions on how to use a Raspberry
204
+    Pi Zero to add power bus and battery temperature monitoring 
205
+    for your AREDN node.</li>
206
+<li>Displayed data may be delayed by as much as 2 seconds from
207
+ time of actual measurement.</li>
208
+<li>Project sponsored by
209
+ <a href=https://willamettevalleymesh.net/ TARGET="_NEW">
210
+ <i>Willamette Valley Mesh Network</i></a>, Salem, Oregon.</li>
211
+<li>Designed by Jeff Owrey, KA7JLO, 2021.</li>
212
+<li> Released under Creative Commons License.</li>
213
+</ul>
214
+</div><br><br>
215
+</div>
216
+
217
+<script>
218
+
219
+/* Global constants */
220
+
221
+var sensorDataUrl = "dynamic/powerData.js";
222
+
223
+/* Global DOM objects */
224
+
225
+// Chart Elements
226
+var current_g = document.getElementById("current_g");
227
+var voltage_g = document.getElementById("voltage_g");
228
+var power_g = document.getElementById("power_g");
229
+var battemp_g = document.getElementById("battemp_g");
230
+var ambtemp_g = document.getElementById("ambtemp_g");
231
+
232
+// Text Elements
233
+var date_t = document.getElementById("date");    
234
+var time_t = document.getElementById("time");    
235
+var current_t = document.getElementById("current");    
236
+var voltage_t = document.getElementById("voltage");    
237
+var power_t = document.getElementById("power");    
238
+var battemp_t = document.getElementById("battemp");    
239
+var ambtemp_t = document.getElementById("ambtemp");    
240
+var status_t = document.getElementById("status");
241
+var period_t = document.getElementById("period");    
242
+
243
+/* Global objects */
244
+
245
+var httpRequest = new XMLHttpRequest();
246
+
247
+/* Global variables */
248
+
249
+var graphPeriod;
250
+
251
+function main() {
252
+    // Register call back function to process client http requests
253
+    httpRequest.onreadystatechange = function() {
254
+        if (httpRequest.readyState == 4 && httpRequest.status == 200) {
255
+            var dataArray = JSON.parse(httpRequest.responseText);
256
+            displayData(dataArray[0]);
257
+        } else if (httpRequest.readyState == 4 && httpRequest.status == 404) {
258
+            displayOfflineStatus();
259
+        }
260
+    };
261
+    httpRequest.ontimeout = function(e) {
262
+        displayOfflineStatus();
263
+    };
264
+
265
+    getSensorData();
266
+    graphPeriod = 1;
267
+    getSensorGraphs();
268
+    setInterval(getSensorData, 2000);
269
+    setInterval(getSensorGraphs, 600000);
270
+}
271
+
272
+function getSensorData() {
273
+    httpRequest.open("GET", sensorDataUrl, true);
274
+    httpRequest.timeout = 3000;
275
+    httpRequest.send();
276
+}
277
+
278
+function setChartPeriod(n) {
279
+    graphPeriod = n;
280
+    getSensorGraphs();
281
+}
282
+
283
+function getSensorGraphs() {
284
+    var d = new Date;
285
+    var pfx;
286
+
287
+    switch(graphPeriod) {
288
+        case 1:
289
+            pfx = "24hr_";
290
+            break;
291
+        case 2:
292
+            pfx = "4wk_";
293
+            break;
294
+       case 3:
295
+            pfx = "12m_";
296
+            break;
297
+    }
298
+    current_g.src = "dynamic/" + pfx + "current.png?ver=" + d.getTime();
299
+    voltage_g.src = "dynamic/" + pfx + "voltage.png?ver=" + d.getTime();
300
+    power_g.src = "dynamic/" + pfx + "power.png?ver=" + d.getTime();
301
+    battemp_g.src = "dynamic/" + pfx + "battemp.png?ver=" + d.getTime();
302
+    ambtemp_g.src = "dynamic/" + pfx + "ambtemp.png?ver=" + d.getTime();
303
+}
304
+
305
+function displayData(dataItem) {
306
+    displayTime(dataItem);
307
+    current_t.innerHTML = 
308
+        (Number(dataItem.current).toFixed(2)).toString();
309
+    voltage_t.innerHTML = 
310
+        (Number(dataItem.voltage).toFixed(2)).toString();
311
+    power_t.innerHTML = 
312
+        (Number(dataItem.power).toFixed(2)).toString();
313
+    battemp_t.innerHTML = 
314
+        (Number(dataItem.battemp).toFixed(2)).toString();
315
+    ambtemp_t.innerHTML = 
316
+        (Number(dataItem.ambtemp).toFixed(2)).toString();
317
+    period_t.innerHTML = dataItem.period / 60;
318
+    status_t.innerHTML = "online";
319
+    status_t.style.color = "green";
320
+}
321
+
322
+function displayOfflineStatus() {
323
+    displayHostTime();
324
+    current_t.innerHTML = "";
325
+    voltage_t.innerHTML = "";
326
+    power_t.innerHTML = "";
327
+    battemp_t.innerHTML = "";
328
+    ambtemp_t.innerHTML = "";
329
+    period_t.innerHTML = " -n/a- ";
330
+    status_t.innerHTML = "offline";
331
+    status_t.style.color = "red";
332
+}
333
+
334
+function displayTime(dataItem) {
335
+    var date, time, hourminute;
336
+    var localDateObj, localTimeZone, timeZoneShift;
337
+    date = dataItem.time.split(" ")[0];
338
+    time = dataItem.time.split(" ")[1];
339
+    hourminute = time.split(":")[0] + ":" + time.split(":")[1];
340
+    localDateObj = new Date();
341
+    localTimeZone = localDateObj.getTimezoneOffset() / 60;
342
+    if (Math.sign(localTimeZone)) {
343
+        timeZoneShift = "-";
344
+    } else {
345
+        timeZoneShift = "+"
346
+    }
347
+    date_t.innerHTML = date;    
348
+    time_t.innerHTML = hourminute + "  <small>(UTC" + timeZoneShift +
349
+                       localTimeZone + ")</small>";
350
+}
351
+
352
+function displayHostTime() {
353
+    var d = new Date();
354
+    var localTimeZone, timeZoneShift;
355
+
356
+    localTimeZone = d.getTimezoneOffset() / 60;
357
+    if (Math.sign(localTimeZone)) {
358
+       timeZoneShift = "-";
359
+    } else {
360
+       timeZoneShift = "+"
361
+    }
362
+    date_t.innerHTML = d.getMonth() + "/" + d.getDate() + "/" +    
363
+        d.getFullYear();    
364
+    time_t.innerHTML = d.getHours() + ":" + d.getMinutes() + 
365
+        "  <small>(UTC" + timeZoneShift + localTimeZone + ")</small>";
366
+}
367
+
368
+</script>
369
+
370
+</body>
371
+</html>