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">
 <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;*/
 }
769aeab2
 #logoBox {
     display:inline-block;
     width:20%;
     /*border:1px solid black;*/
 }
 #logoInfo {
     padding-left: 20px;
     padding-top: 10px;
     display:inline-block;
     width:45%;
     font:14px arial,sans-serif;
     text-align:left;
     vertical-align: top;
    /*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;
769aeab2
     /*border: 1px solid black;8?
8623d74e
 }
 img.chart {
     width: 100%;
769aeab2
     height: 233px;
     /*border: 1px solid black;*/
8623d74e
 }
 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;
 }
8d25b7b0
 #iframe_a {
     border:none;
     width:100%;
     height:1075px;
 }
8623d74e
 </style>
 </head>
 
 <body onload="main()">
 
 <div id="mainContainer">
 
769aeab2
 <div id="logoBox">
 <a href="https://github.com/fractalxaos/ham/tree/master/nodepower" 
8623d74e
 style="text-decoration:none" target="_new">
769aeab2
 <img src="static/npwlogo.png"></a>
 </div>
8623d74e
 
769aeab2
 <div id="logoInfo">
 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
 of node power consumption, battery and ambient temperatures.
8623d74e
 </div>
 
769aeab2
 <a href="http://wa7abu-pb400-vp-0ccd.local.mesh:8080/cgi-bin/status"
  style="text-decoration:none" target="_new">
 <h4>WA7ABU Victor Point AREDN Mesh Installation</h4></a>
 
8623d74e
 <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>
8d25b7b0
 
 <li id="customSelector" class="chartNav">
 <text  class="chartNav"
  onclick="setChartPeriod(0)">Custom…</text></li>
 
8623d74e
 </ul>
 </span>
8d25b7b0
 
 <div class="rowContainer" id="customChartsContainer" style="display:none;">
 <div class="currentDataCell">
 <form id="fmDateSelector" action="power.php" method="post"
  target="iframe_a">
 <label for="beginDate">Begin Date: </label>
 <input id="beginDate" name="beginDate" type="date" value="mm/dd/yyyy" />
 <label for="endDate">End Date: </label>
 <input id="endDate" name="endDate" type="date" value="mm/dd/yyyy" />
 <br><br>
 <input type="button" onclick="getCustomCharts()" value="Get Charts">
 </form>
 <span id="errorMsg"></span><br>
 <iframe id="iframe_a" name="iframe_a"></iframe>
 </div>
 </div>
 
8623d74e
 <br>
 
8d25b7b0
 <div class="rowContainer" id="stockChartsContainer">
 
8623d74e
 <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>
 
8d25b7b0
 </div>
 
8623d74e
 <div id="notes">
 <b>NOTES:</b>
 <ul>
 <li>Node sensor project plans and software available at
769aeab2
 <a href="https://github.com/fractalxaos/ham/archive/master.zip" target="_new">
8623d74e
 <i>Github.com</i>
 </a>.</li>
98865bbb
 <li>Project plans and software also available via the mesh
 <a href="http://ka7jlo-web.local.mesh/file-manager/files/KA7JLO/Apps/nodepower/nodepower.zip" target="_new">
 <i>here</i>
 </a>.</li>
8623d74e
 <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
 
8d25b7b0
 var customChartsContainer = document.getElementById("customChartsContainer");
 var stockChartsContainer = document.getElementById("stockChartsContainer");
 var fmDateSelector = document.getElementById("fmDateSelector");
 var errorMsg = document.getElementById("errorMsg");
 var customSelector = document.getElementById("customSelector");
 
8623d74e
 /* Global objects */
 
 var httpRequest = new XMLHttpRequest();
 
 /* Global variables */
 
8d25b7b0
 var chartPeriod = 1;
d65ec281
 var objBlink;
8623d74e
 
 function main() {
cd727c58
     httpRequest.timeout = 3000;
8d25b7b0
 
     if (location.hostname.match(/.local/g) == null) {
         customSelector.style.visibility = "hidden";
     }
 
8623d74e
     // 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();
         }
     };
cd727c58
 
8623d74e
     httpRequest.ontimeout = function(e) {
         displayOfflineStatus();
     };
 
8d25b7b0
     initializeDateSelector();
8623d74e
     getSensorData();
     getSensorGraphs();
     setInterval(getSensorData, 2000);
     setInterval(getSensorGraphs, 600000);
 }
 
 function getSensorData() {
cd727c58
     httpRequest.open("POST", SENSOR_DATA_URL, true);
8623d74e
     httpRequest.send();
 }
 
 function setChartPeriod(n) {
8d25b7b0
     chartPeriod = n;
     if (n == 0) {
         customChartsContainer.style.display = "block";
         stockChartsContainer.style.display = "none";
     } else {
         customChartsContainer.style.display = "none";
         stockChartsContainer.style.display = "block";
         getSensorGraphs();
     }
8623d74e
 }
 
 function getSensorGraphs() {
     var d = new Date;
     var pfx;
 
8d25b7b0
     switch(chartPeriod) {
8623d74e
         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();
 
76a2df75
     period_t.innerHTML = dataItem.chartUpdateInterval / 60;
8623d74e
     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>";
 }
 
8d25b7b0
 function initializeDateSelector() {
     var d = new Date();
 
     var dEnd = new Date(d.getFullYear(),
                d.getMonth(), d.getDate() - 0);
 
     var dBegin = new Date(d.getFullYear(),
                d.getMonth(), d.getDate() - 1);
 
     document.getElementById("beginDate").valueAsDate = dBegin;
     document.getElementById("endDate").valueAsDate = dEnd;
 }
 
 function getCustomCharts() {
     fmDateSelector.submit();
 }
 
8623d74e
 </script>
 
 </body>
 </html>