<!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; } #alert { font: bold 22px arial, sans-serif; color: red; /*border: 1px solid black;*/ } .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; border: 1px solid black; } 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; } #iframe_a { border:none; width:100%; height:1075px; } </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> <h4>WA7ABU Victor Point AREDN Mesh Installation</h4> <div style="width:60%; text-align:left; font:14px arial, sans-serif;"> This web page shows the power consumption of above node(s). Charts below provide a historical glimpse of node power consumption, battery and ambient temperatures. </div> <div id="datetime"> <text id="date"></text> <text id="time"></text> </div> <div id="alert"> <text id="alertmsg" style="opacity: 0;"> Warning: Low Battery Voltage</text> </div> <div class="rowContainer"> <div class="currentDataCell"> <div class="dataItems"> Current:<br> Voltage:<br> Power: </div> <div class="dataItems" style="width: 40%;"> <text id="current"></text> mA<br> <text id="voltage"></text> V<br> <text id="power"></text> W </div> </div> <div class="currentDataCell"> <div class="dataItems"> Battery Temperature:<br> Ambient Temperature: </div> <div class="dataItems" style="width: 30%;"> <text id="battemp"></text> ℉<br> <text id="ambtemp"></text> ℉<br> </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> <li id="customSelector" class="chartNav"> <text class="chartNav" onclick="setChartPeriod(0)">Custom…</text></li> </ul> </span> <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> <br> <div class="rowContainer" id="stockChartsContainer"> <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> <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> "use strict"; /* Global constants */ var SENSOR_DATA_URL = "dynamic/powerData.js"; var CRITICAL_VOLTAGE = 11.0; /* 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"); var period_t = document.getElementById("period"); var alertmsg_t = document.getElementById("alertmsg"); 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"); /* Global objects */ var httpRequest = new XMLHttpRequest(); /* Global variables */ var chartPeriod = 1; var objBlink; function main() { if (location.hostname.match(/.local/g) == null) { customSelector.style.visibility = "hidden"; } // 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(); }; initializeDateSelector(); getSensorData(); getSensorGraphs(); setInterval(getSensorData, 2000); setInterval(getSensorGraphs, 600000); } function getSensorData() { httpRequest.open("GET", SENSOR_DATA_URL, true); httpRequest.timeout = 3000; httpRequest.send(); } function setChartPeriod(n) { chartPeriod = n; if (n == 0) { customChartsContainer.style.display = "block"; stockChartsContainer.style.display = "none"; } else { customChartsContainer.style.display = "none"; stockChartsContainer.style.display = "block"; getSensorGraphs(); } } function getSensorGraphs() { var d = new Date; var pfx; switch(chartPeriod) { 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) { 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(); } displayTime(dataItem); 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(); period_t.innerHTML = dataItem.chartUpdateInterval / 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"; } 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; } 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>"; } 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(); } </script> </body> </html>