Browse Source

0.8.50

* merge PR: added history charts to web #1336
pull/1347/head
lumapu 1 year ago
parent
commit
fa2028e479
  1. 3
      src/CHANGES.md
  2. 2
      src/defines.h
  3. 2
      src/web/RestApi.h
  4. 222
      src/web/html/history.html
  5. 2
      src/web/html/save.html
  6. 2
      src/web/html/serial.html
  7. 12
      src/web/html/style.css
  8. 2
      src/web/html/visualization.html
  9. 2
      src/web/html/wizard.html
  10. 52
      src/web/lang.json

3
src/CHANGES.md

@ -1,5 +1,8 @@
# Development Changes
## 0.8.50 - 2024-01-09
* merge PR: added history charts to web #1336
## 0.8.49 - 2024-01-08
* fix send total values if inverter state is different from `OFF` #1331
* fix german language issues #1335

2
src/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 8
#define VERSION_PATCH 49
#define VERSION_PATCH 50
//-------------------------------------
typedef struct {

2
src/web/RestApi.h

@ -794,7 +794,6 @@ class RestApi {
void getPowerHistory(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("refresh")] = mConfig->inst.sendInterval;
obj[F("datapoints")] = HISTORY_DATA_ARR_LENGTH;
uint16_t max = 0;
for (uint16_t fld = 0; fld < HISTORY_DATA_ARR_LENGTH; fld++) {
uint16_t value = mApp->getHistoryValue((uint8_t)HistoryStorageType::POWER, fld);
@ -809,7 +808,6 @@ class RestApi {
void getYieldDayHistory(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("refresh")] = 86400; // 1 day
obj[F("datapoints")] = HISTORY_DATA_ARR_LENGTH;
uint16_t max = 0;
for (uint16_t fld = 0; fld < HISTORY_DATA_ARR_LENGTH; fld++) {
uint16_t value = mApp->getHistoryValue((uint8_t)HistoryStorageType::YIELD, fld);

222
src/web/html/history.html

@ -1,135 +1,113 @@
<!doctype html>
<html>
<head>
<title>{#NAV_HISTORY}</title>
{#HTML_HEADER}
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<head>
<title>History</title>
{#HTML_HEADER}
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
</head>
</head>
<body>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<h3>Total Power history</h3>
<div class="chartDivContainer">
<div class="chartDiv" id="phHistoryChart"> </div>
<p class="center" style="margin:0px;border:0px;">
Maximum day: <span id="phMaximumDay"></span> W. Last value: <span id="phActual"></span> W.<br />
Maximum graphics: <span id="phMaximum"></span> W. Updated every <span id="phRefresh"></span> seconds </p>
</div>
<h3>Yield per day history</h3>
<div class="chartDivContainer">
<div class="chartDiv" id="ydHistoryChart"> </div>
<p class="center" style="margin:0px;border:0px;">
Maximum value: <span id="ydMaximum"></span> Wh<br />
Updated every <span id="ydRefresh"></span> seconds </p>
<body>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<h3>{#TOTAL_POWER}</h3>
<div>
<div class="chartDiv" id="pwrChart"> </div>
<p>
{#MAX_DAY}: <span id="pwrMaxDay"></span> W. {#LAST_VALUE}: <span id="pwrLast"></span> W.<br />
{#MAXIMUM}: <span id="pwrMax"></span> W. {#UPDATED} <span id="pwrRefresh"></span> {#SECONDS}
</p>
</div>
<h3>{#TOTAL_YIELD_PER_DAY}</h3>
<div>
<div class="chartDiv" id="ydChart"> </div>
<p>
{#MAXIMUM}: <span id="ydMax"></span> Wh<br />
{#UPDATED} <span id="ydRefresh"></span> {#SECONDS}
</p>
</div>
</div>
</div>
{#HTML_FOOTER}
<h4 style="margin-bottom:0px;">Insert data into Yield per day history</h4>
<fieldset style="padding: 1px;">
<legend class="des" style="margin-top: 0px;">Insert data (*.json) i.e. from a saved "/api/yieldDayHistory" call </legend>
<form id="form" method="POST" action="/api/insertYieldDayHistory" enctype="multipart/form-data" accept-charset="utf-8">
<input type="button" class="btn my-4" style="padding: 3px;margin: 3px;" value="Insert" onclick="submit()">
<input type="file" name="insert" style="width: 80%;">
</form>
</fieldset>
<p></p>
</div>
</div>
{#HTML_FOOTER}
<script type="text/javascript">
const svgns = "http://www.w3.org/2000/svg";
var pwrExeOnce = true;
var ydExeOnce = true;
// make a simple rectangle
var mRefresh = 60;
var mLastValue = 0;
const mChartHeight = 250;
<script type="text/javascript">
const svgns = "http://www.w3.org/2000/svg";
var phExeOnce = true;
var ydExeOnce = true;
// make a simple rectangle
var mRefresh = 60;
var phDatapoints = 512;
var mMaximum = 0;
var mLastValue = 0;
var mDataValues = [];
const mChartHight = 250;
function parseHistory(obj, namePrefix, execOnce) {
mRefresh = obj.refresh
var data = Object.assign({}, obj.value)
var numDataPts = data.length
function parseHistory(obj, namePrefix, execOnce) {
mRefresh = obj["refresh"];
phDatapoints = obj["datapoints"];
mDataValues = Object.assign({}, obj["value"]);
mMaximum = obj["maximum"];
// generate svg
if (true == execOnce) {
let svg = document.createElementNS(svgns, "svg");
svg.setAttribute("class", "chart");
svg.setAttribute("width", String((phDatapoints+2) * 2));
svg.setAttribute("height", String(mChartHight) + "");
svg.setAttribute("aria-labelledby", "title desc");
svg.setAttribute("role", "img");
t = ml("title");
t.innerHTML = "History of day";
svg.appendChild(t);
let g = document.createElementNS(svgns, "g");
svg.appendChild(g);
for (var i = 0; i < phDatapoints; i++) {
val = mDataValues[i];
let rect = document.createElementNS(svgns, "rect");
rect.setAttribute("id", namePrefix+"Rect" + i);
rect.setAttribute("x", String(i * 2) + "");
rect.setAttribute("width", String(2) + "");
g.appendChild(rect);
if (true == execOnce) {
let s = svg(null, (numDataPts + 2) * 2, mChartHeight, "chart");
s.setAttribute("role", "img");
let g = document.createElementNS(svgns, "g");
s.appendChild(g);
for (var i = 0; i < numDataPts; i++) {
val = data[i];
let rect = document.createElementNS(svgns, "rect");
rect.setAttribute("id", namePrefix+"Rect" + i);
rect.setAttribute("x", String(i * 2) + "");
rect.setAttribute("width", String(2) + "");
g.appendChild(rect);
}
document.getElementById(namePrefix+"Chart").appendChild(s);
}
document.getElementById(namePrefix+"HistoryChart").appendChild(svg);
}
// normalize data to chart
let divider = mMaximum / mChartHight;
if (divider == 0)
divider = 1;
for (var i = 0; i < phDatapoints; i++) {
val = mDataValues[i];
if (val>0)
mLastValue = val
val = val / divider
rect = document.getElementById(namePrefix+"Rect" + i);
rect.setAttribute("height", val);
rect.setAttribute("y", mChartHight - val);
}
document.getElementById(namePrefix + "Maximum").innerHTML = mMaximum;
if (mRefresh < 5)
mRefresh = 5;
document.getElementById(namePrefix + "Refresh").innerHTML = mRefresh;
}
function parsePowerHistory(obj){
if (null != obj) {
parseNav(obj["generic"]);
parseHistory(obj,"ph", phExeOnce)
let maximumDay = obj["maximumDay"];
document.getElementById("phActual").innerHTML = mLastValue;
document.getElementById("phMaximumDay").innerHTML = maximumDay;
}
if (true == phExeOnce) {
phExeOnce = false;
window.setInterval("getAjax('/api/powerHistory', parsePowerHistory)", mRefresh * 1000);
// one after the other
setTimeout(() => {
getAjax("/api/yieldDayHistory", parseYieldDayHistory);
} , 20);
}
}
function parseYieldDayHistory(obj) {
if (null != obj) {
parseNav(obj["generic"]);
parseHistory(obj, "yd", ydExeOnce)
}
if (true == ydExeOnce) {
ydExeOnce = false;
window.setInterval("getAjax('/api/yieldDayHistory', parseYieldDayHistory)", mRefresh * 500);
// normalize data to chart
let divider = obj.max / mChartHeight;
if (divider == 0)
divider = 1;
for (var i = 0; i < numDataPts; i++) {
val = data[i];
if (val > 0)
mLastValue = val
val = val / divider
rect = document.getElementById(namePrefix+"Rect" + i);
rect.setAttribute("height", val);
rect.setAttribute("y", mChartHeight - val);
}
document.getElementById(namePrefix + "Max").innerHTML = obj.max;
if (mRefresh < 5)
mRefresh = 5;
document.getElementById(namePrefix + "Refresh").innerHTML = mRefresh;
}
}
getAjax("/api/powerHistory", parsePowerHistory);
</script>
function parsePowerHistory(obj){
if (null != obj) {
parseHistory(obj,"pwr", pwrExeOnce)
document.getElementById("pwrLast").innerHTML = mLastValue
document.getElementById("pwrMaxDay").innerHTML = obj.maxDay
}
if (pwrExeOnce) {
pwrExeOnce = false;
window.setInterval("getAjax('/api/powerHistory', parsePowerHistory)", mRefresh * 1000);
</body>
setTimeout(() => {
getAjax("/api/yieldDayHistory", parseYieldDayHistory);
} , 20);
}
}
function parseYieldDayHistory(obj) {
if (null != obj) {
parseNav(obj.generic);
parseHistory(obj, "yd", ydExeOnce)
}
if (ydExeOnce) {
ydExeOnce = false;
window.setInterval("getAjax('/api/yieldDayHistory', parseYieldDayHistory)", mRefresh * 500);
}
}
</html>
getAjax("/api/powerHistory", parsePowerHistory);
</script>
</body>
</html>

2
src/web/html/save.html

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>Save</title>
<title>{#NAV_SAVE}</title>
{#HTML_HEADER}
</head>
<body>

2
src/web/html/serial.html

@ -1,7 +1,7 @@
<!doctype html>
<html lang="en">
<head>
<title>Serial Console</title>
<title>{#NAV_WEBSERIAL}</title>
{#HTML_HEADER}
</head>
<body>

12
src/web/html/style.css

@ -36,21 +36,17 @@ textarea {
svg rect {fill: #0000AA;}
svg.chart {
background: #f2f2f2;
border: 2px solid gray;
padding: 1px;
border: 2px solid gray;
padding: 1px;
}
div.chartDivContainer {
padding: 1px;
margin: 1px;
padding: 1px;
margin: 1px;
}
div.chartdivContainer span {
color: var(--fg2);
}
div.chartDiv {
padding: 0px;
margin: 0px;
}
.topnav {

2
src/web/html/visualization.html

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>Live</title>
<title>{#NAV_LIVE}</title>
{#HTML_HEADER}
<meta name="apple-mobile-web-app-capable" content="yes">
</head>

2
src/web/html/wizard.html

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>Setup Wizard</title>
<title>{#NAV_WIZARD}</title>
{#HTML_HEADER}
</head>
<body>

52
src/web/lang.json

@ -3,13 +3,18 @@
{
"name": "general",
"list": [
{
"token": "NAV_WIZARD",
"en": "Setup Wizard",
"de": "Daten"
},
{
"token": "NAV_LIVE",
"en": "Live",
"de": "Daten"
},
{
"token": "{#NAV_HISTORY}",
"token": "NAV_HISTORY",
"en": "History",
"de": "Verlauf"
},
@ -28,6 +33,11 @@
"en": "Documentation",
"de": "Dokumentation"
},
{
"token": "NAV_SAVE",
"en": "save",
"de": "speichern"
},
{
"token": "NAV_ABOUT",
"en": "About",
@ -1334,6 +1344,46 @@
"de": "Fehler beim Speichern"
}
]
},
{
"name": "history.html",
"list": [
{
"token": "TOTAL_POWER",
"en": "Total Power",
"de": "Gesamtleistung"
},
{
"token": "TOTAL_YIELD_PER_DAY",
"en": "Total Yield per day",
"de": "Gesamtertrag pro Tag"
},
{
"token": "MAX_DAY",
"en": "maximum day",
"de": "Tagesmaximum"
},
{
"token": "LAST_VALUE",
"en": "last value",
"de": "letzter Wert"
},
{
"token": "MAXIMUM",
"en": "maximum value",
"de": "Maximalwert"
},
{
"token": "UPDATED",
"en": "Updated every",
"de": "aktualisiert alle"
},
{
"token": "SECONDS",
"en": "seconds",
"de": "Sekunden"
}
]
}
]
}

Loading…
Cancel
Save