nodepower/html/power.html
8623d74e
 <!DOCTYPE html>
 <!-- Courtesy ruler for editing this file
 12345678901234567890123456789012345678901234567890123456789012345678901234567890
 -->
 <html>
 <head>
 <title>Node Power</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="shortcut icon" href="/favicon.png" type="image/x-icon" />
 <style>
 body {
     background-image: url("static/chalk.jpg");
 }
 h2 {
     font: bold 24px arial, sans-serif;
 }
 h4 {
     font: bold 16px arial, sans-serif;
 }
 p {
     font: normal 14px arial, sans-serif;
 }
 #mainContainer {
     width: 740px;
     text-align: center;
     margin: auto;
 }
 #datetime {
     padding: 10px;
     font: bold 22px arial, sans-serif;
     /*border: 1px solid black;*/
 }
 #notes {
     font: 17px arial, sans-serif;
     text-align: left;
     padding: 10px;
 }
d65ec281
 #alert {
     font: bold 22px arial, sans-serif;
     color: red;
     /*border: 1px solid black;*/
 }
8623d74e
 .rowContainer {
     display: table;
     width: 100%;
     /*border: 1px solid black;*/
 }
 .currentDataCell {
     width: 50%;
     padding: 10px;
     font: bold 18px arial, sans-serif;
     text-align: center;
     display: table-cell;
     vertical-align: middle;
     /*border: 1px solid black;*/
 }
 .dataItems {
     padding: 2px;
     text-align: left;
     line-height: 130%;
     display: inline-block;
     vertical-align: middle;
     /*border: 1px solid black;*/
 }
 .chartContainer {
     padding: 2px;
d65ec281
     border: 1px solid black;
8623d74e
 }
 img.chart {
     width: 100%;
 }
 span.chartNav {
     margin: auto;
 }
 ul.chartNav {
     list-style-type: none;
     margin: 10px;
     padding: 0;
     overflow: hidden;
     background-color: #bbb;
     text-align: center;
 }
 li.chartNav {
     display: inline-block;
     font: bold 18px arial, sans-serif;
     color: black;
 }
 text.chartNav:hover {
     background-color: #333;
     cursor: pointer;
     color: white;
 }
 text.chartNav {
     display: inline-block;
     padding: 8px 12px;
 }
 </style>
 </head>
 
 <body onload="main()">
 
 <div id="mainContainer">
 
 <h2><a href="https://github.com/fractalxaos/ham/tree/master/nodepower" 
 style="text-decoration:none" target="_new">
 Node Power</a></h2>
58e58c0d
 <h4>WA7ABU Victor Point AREDN Mesh Installation</h4>
8623d74e
 
 <div style="width:60%; text-align:left; font:14px arial, sans-serif;">
58e58c0d
 This web page shows the power consumption of above node(s).  Charts below
8623d74e
 provide a historical glimpse of node power consumption, battery and ambient
 temperatures.
 </div>
 
 <div id="datetime">
 <text id="date"></text>
 &nbsp;&nbsp;
 <text id="time"></text>
 </div>
 
d65ec281
 <div id="alert">
 <text id="alertmsg" style="opacity: 0;">
 Warning: Low Battery Voltage</text>
 </div>
 
8623d74e
 <div class="rowContainer">
 
 <div class="currentDataCell">
 <div class="dataItems">
 Current:<br>
 Voltage:<br>
 Power:
 </div>
d65ec281
 <div class="dataItems" style="width: 40%;">
8623d74e
 <text id="current"></text> mA<br>
 <text id="voltage"></text> V<br>
d65ec281
 <text id="power"></text> W
8623d74e
 </div>
 </div>
 
 <div class="currentDataCell">
 <div class="dataItems">
 Battery Temperature:<br>
 Ambient Temperature:
 </div>
 <div class="dataItems"  style="width: 30%;">
58e58c0d
 <text id="battemp"></text> &#8457;<br>
 <text id="ambtemp"></text> &#8457;<br>
8623d74e
 </div>
 </div>
 </div>
 
 <div class="rowContainer">
 <div class="currentDataCell">
 <div class="dataItems">
 Charts update every
 </div>
 <div class="dataItems">
 <text id="period"></text> minutes.<br>
 </div>
 </div>
 <div class="currentDataCell">
 <div class="dataItems">
 Status:
 </div>
 <div class="dataItems">
 <text id="status"></text><br>
 </div>
 </div>
 </div>
 
 <span class="chartNav">
 <ul class="chartNav">
 <li class="chartNav">Select charts:</li>
 <li class="chartNav"><text class="chartNav" onclick="setChartPeriod(1)">
 24 hours</text></li>
 <li class="chartNav"><text class="chartNav" onclick="setChartPeriod(2)">
 4 weeks</text></li>
 <li class="chartNav"><text class="chartNav" onclick="setChartPeriod(3)">
 12 months</text></li>
 </ul>
 </span>
 <br>
 
 <div class="chartContainer">
 <img class="chart" id="current_g">
 </div>
 
 <div class="chartContainer">
 <img class="chart" id="voltage_g">
 </div>
 
 <div class="chartContainer">
 <img class="chart" id="power_g">
 </div>
 
 <div class="chartContainer">
 <img class="chart" id="battemp_g">
 </div>
 
 <div class="chartContainer">
 <img class="chart" id="ambtemp_g">
 </div>
 
 <div id="notes">
 <b>NOTES:</b>
 <ul>
 <li>Node sensor project plans and software available at
 <a href="https://github.com/fractalxaos/ham/tree/master/nodepower" target="_new">
 <i>Github.com</i>
 </a>.</li>
 <li>Project plans include detailed instructions on how to use a Raspberry
     Pi Zero to add power bus and battery temperature monitoring 
     for your AREDN node.</li>
 <li>Displayed data may be delayed by as much as 2 seconds from
  time of actual measurement.</li>
 <li>Project sponsored by
  <a href=https://willamettevalleymesh.net/ TARGET="_NEW">
  <i>Willamette Valley Mesh Network</i></a>, Salem, Oregon.</li>
 <li>Designed by Jeff Owrey, KA7JLO, 2021.</li>
 <li> Released under Creative Commons License.</li>
 </ul>
 </div><br><br>
 </div>
 
 <script>
d65ec281
 "use strict";
8623d74e
 
 /* Global constants */
 
d65ec281
 var SENSOR_DATA_URL = "dynamic/powerData.js";
 var CRITICAL_VOLTAGE = 11.0;
8623d74e
 
 /* Global DOM objects */
 
 // Chart Elements
 var current_g = document.getElementById("current_g");
 var voltage_g = document.getElementById("voltage_g");
 var power_g = document.getElementById("power_g");
 var battemp_g = document.getElementById("battemp_g");
 var ambtemp_g = document.getElementById("ambtemp_g");
 
 // Text Elements
 var date_t = document.getElementById("date");    
 var time_t = document.getElementById("time");    
 var current_t = document.getElementById("current");    
 var voltage_t = document.getElementById("voltage");    
 var power_t = document.getElementById("power");    
 var battemp_t = document.getElementById("battemp");    
 var ambtemp_t = document.getElementById("ambtemp");    
 var status_t = document.getElementById("status");
d65ec281
 var period_t = document.getElementById("period");
 var alertmsg_t = document.getElementById("alertmsg");   
8623d74e
 
 /* Global objects */
 
 var httpRequest = new XMLHttpRequest();
 
 /* Global variables */
 
 var graphPeriod;
d65ec281
 var objBlink;
8623d74e
 
 function main() {
     // Register call back function to process client http requests
     httpRequest.onreadystatechange = function() {
         if (httpRequest.readyState == 4 && httpRequest.status == 200) {
             var dataArray = JSON.parse(httpRequest.responseText);
             displayData(dataArray[0]);
         } else if (httpRequest.readyState == 4 && httpRequest.status == 404) {
             displayOfflineStatus();
         }
     };
     httpRequest.ontimeout = function(e) {
         displayOfflineStatus();
     };
 
     getSensorData();
     graphPeriod = 1;
     getSensorGraphs();
     setInterval(getSensorData, 2000);
     setInterval(getSensorGraphs, 600000);
 }
 
 function getSensorData() {
d65ec281
     httpRequest.open("GET", SENSOR_DATA_URL, true);
8623d74e
     httpRequest.timeout = 3000;
     httpRequest.send();
 }
 
 function setChartPeriod(n) {
     graphPeriod = n;
     getSensorGraphs();
 }
 
 function getSensorGraphs() {
     var d = new Date;
     var pfx;
 
     switch(graphPeriod) {
         case 1:
             pfx = "24hr_";
             break;
         case 2:
             pfx = "4wk_";
             break;
        case 3:
             pfx = "12m_";
             break;
     }
     current_g.src = "dynamic/" + pfx + "current.png?ver=" + d.getTime();
     voltage_g.src = "dynamic/" + pfx + "voltage.png?ver=" + d.getTime();
     power_g.src = "dynamic/" + pfx + "power.png?ver=" + d.getTime();
     battemp_g.src = "dynamic/" + pfx + "battemp.png?ver=" + d.getTime();
     ambtemp_g.src = "dynamic/" + pfx + "ambtemp.png?ver=" + d.getTime();
 }
 
 function displayData(dataItem) {
d65ec281
     var voltage, current, power, ambtemp, battemp, powerWatts
 
     voltage = Number(dataItem.voltage);
     current = Number(dataItem.current);
     power = Number(dataItem.power)/1000.0;
     battemp = Number(dataItem.battemp);
     ambtemp = Number(dataItem.ambtemp);
 
     if (voltage < CRITICAL_VOLTAGE) {
         displayAlert();
     } else {
         clearAlert();
     }
 
8623d74e
     displayTime(dataItem);
d65ec281
     current_t.innerHTML = current.toFixed(2).toString(); 
     voltage_t.innerHTML = voltage.toFixed(2).toString();
     power_t.innerHTML = power.toFixed(2).toString();
     battemp_t.innerHTML = battemp.toFixed(2).toString();
     ambtemp_t.innerHTML = ambtemp.toFixed(2).toString();
 
8623d74e
     period_t.innerHTML = dataItem.period / 60;
     status_t.innerHTML = "online";
     status_t.style.color = "green";
 }
 
 function displayOfflineStatus() {
     displayHostTime();
     current_t.innerHTML = "";
     voltage_t.innerHTML = "";
     power_t.innerHTML = "";
     battemp_t.innerHTML = "";
     ambtemp_t.innerHTML = "";
     period_t.innerHTML = " -n/a- ";
     status_t.innerHTML = "offline";
     status_t.style.color = "red";
 }
 
d65ec281
 function displayAlert() {
     if (typeof objBlink == "undefined") {
     objBlink = setInterval(function() {
             alertmsg_t.style.opacity = 
                 (alertmsg_t.style.opacity == 0 ? 1 : 0);
     }, 1000);
     }
 }
 
 function clearAlert() {
     clearInterval(objBlink);
     alertmsg_t.style.opacity = 0;
 }
 
8623d74e
 function displayTime(dataItem) {
     var date, time, hourminute;
     var localDateObj, localTimeZone, timeZoneShift;
     date = dataItem.time.split(" ")[0];
     time = dataItem.time.split(" ")[1];
     hourminute = time.split(":")[0] + ":" + time.split(":")[1];
     localDateObj = new Date();
     localTimeZone = localDateObj.getTimezoneOffset() / 60;
     if (Math.sign(localTimeZone)) {
         timeZoneShift = "-";
     } else {
         timeZoneShift = "+"
     }
     date_t.innerHTML = date;    
     time_t.innerHTML = hourminute + "  <small>(UTC" + timeZoneShift +
                        localTimeZone + ")</small>";
 }
 
 function displayHostTime() {
     var d = new Date();
     var localTimeZone, timeZoneShift;
 
     localTimeZone = d.getTimezoneOffset() / 60;
     if (Math.sign(localTimeZone)) {
        timeZoneShift = "-";
     } else {
        timeZoneShift = "+"
     }
     date_t.innerHTML = d.getMonth() + "/" + d.getDate() + "/" +    
         d.getFullYear();    
     time_t.innerHTML = d.getHours() + ":" + d.getMinutes() + 
         "  <small>(UTC" + timeZoneShift + localTimeZone + ")</small>";
 }
 
 </script>
 
 </body>
 </html>