mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-10 07:26:38 +02:00
* built visualization as xhr
* converted /update to xhr * started web serial console /serial * /save does not work yet - not debugged
This commit is contained in:
parent
47c782e3f6
commit
8ae78842a8
16 changed files with 307 additions and 392 deletions
|
@ -54,7 +54,7 @@ void ahoywifi::setup(uint32_t timeout, bool settingValid) {
|
|||
if(mApActive)
|
||||
DBGPRINTLN(F("192.168.1.1"));
|
||||
else
|
||||
DBGPRINTLN(WiFi.localIP());
|
||||
DBGPRINTLN(WiFi.localIP().toString());
|
||||
DPRINTLN(DBG_INFO, F("to configure your device"));
|
||||
DPRINTLN(DBG_INFO, F("----------------------------------------\n"));
|
||||
}
|
||||
|
|
|
@ -108,9 +108,6 @@
|
|||
// default MQTT topic
|
||||
#define DEF_MQTT_TOPIC "inverter"
|
||||
|
||||
// changes the style of "/setup" page, visualized = nicer
|
||||
#define LIVEDATA_VISUALIZED
|
||||
|
||||
#if __has_include("config_override.h")
|
||||
#include "config_override.h"
|
||||
#endif
|
||||
|
|
3
tools/esp8266/dbg.cpp
Normal file
3
tools/esp8266/dbg.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
#include "dbg.h"
|
||||
|
||||
DBG_CB mCb = NULL;
|
|
@ -16,10 +16,12 @@ function getAjax(url, ptr) {
|
|||
http.send(null);
|
||||
}
|
||||
function p() {
|
||||
if(http.readyState == 4)
|
||||
if(http.readyState == 4) {
|
||||
if(null != http.responseText)
|
||||
ptr(JSON.parse(http.responseText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function des(val) {
|
||||
e = document.createElement('p');
|
||||
|
@ -60,6 +62,13 @@ function sel(name, opt, selId) {
|
|||
|
||||
function div(cl) {
|
||||
e = document.createElement('div');
|
||||
e.classList.add(cl);
|
||||
e.classList.add(...cl);
|
||||
return e;
|
||||
}
|
||||
|
||||
function span(val, cl) {
|
||||
e = document.createElement('span');
|
||||
e.innerHTML = val;
|
||||
e.classList.add(...cl);
|
||||
return e;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ def convert2Header(inFile, compress):
|
|||
|
||||
convert2Header("index.html", True)
|
||||
convert2Header("setup.html", True)
|
||||
convert2Header("visualization.html", False)
|
||||
convert2Header("update.html", False)
|
||||
convert2Header("visualization.html", True)
|
||||
convert2Header("update.html", True)
|
||||
convert2Header("serial.html", True)
|
||||
convert2Header("style.css", True)
|
||||
convert2Header("api.js", True)
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
<h1>AHOY</h1>
|
||||
<div id="content" class="content">
|
||||
<p>
|
||||
<a href="/visualization">Visualization</a><br/>
|
||||
<a href="/live">Visualization</a><br/>
|
||||
<br/>
|
||||
<a href="/setup">Setup</a><br/>
|
||||
<a href="/serial">Serial Console</a><br/>
|
||||
</p>
|
||||
<p><span class="des">Uptime: </span><span id="uptime"></span></p>
|
||||
<p><span class="des">ESP-Time: </span><span id="date"></span></p>
|
||||
|
@ -80,9 +81,11 @@
|
|||
html += "producing\n";
|
||||
|
||||
if(false == i["is_avail"]) {
|
||||
if(i["ts_last_success"] > 0) {
|
||||
var date = new Date(i["ts_last_success"] * 1000);
|
||||
html += "-> last successful transmission: " + date.toLocaleString('de-DE', {timeZone: 'UTC'});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
document.getElementById("iv").innerHTML = html;
|
||||
|
@ -100,13 +103,19 @@
|
|||
}
|
||||
|
||||
function parse(obj) {
|
||||
if(null != obj) {
|
||||
parseSys(obj["system"]);
|
||||
parseStat(obj["statistics"]);
|
||||
parseIv(obj["inverter"]);
|
||||
parseWarnInfo(obj["warnings"], obj["infos"]);
|
||||
document.getElementById("refresh").innerHTML = obj["refresh_interval"];
|
||||
if(false == intervalSet)
|
||||
if(false == intervalSet) {
|
||||
window.setInterval("getAjax('/api/index', parse)", obj["refresh_interval"] * 1000);
|
||||
intervalSet = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
document.getElementById("refresh").innerHTML = "n/a";
|
||||
}
|
||||
|
||||
getAjax("/api/index", parse);
|
||||
|
|
50
tools/esp8266/html/serial.html
Normal file
50
tools/esp8266/html/serial.html
Normal file
|
@ -0,0 +1,50 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Serial Console</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script type="text/javascript" src="api.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Serial Console</h1>
|
||||
<div id="content" class="content">
|
||||
<textarea rows="20" cols="90" id="serial" readonly></textarea>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p class="left">© 2022</p>
|
||||
<p class="left"><a href="/">Home</a></p>
|
||||
<p class="right" id="version"></p>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
function parseSys(obj) {
|
||||
document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"];
|
||||
}
|
||||
var con = document.getElementById("serial");
|
||||
if (!!window.EventSource) {
|
||||
var source = new EventSource('/events');
|
||||
|
||||
source.addEventListener('open', function(e) {
|
||||
//console.log("Events Connected");
|
||||
}, false);
|
||||
|
||||
source.addEventListener('error', function(e) {
|
||||
if (e.target.readyState != EventSource.OPEN) {
|
||||
//console.log("Events Disconnected");
|
||||
}
|
||||
}, false);
|
||||
|
||||
source.addEventListener('serial', function(e) {
|
||||
//var ascii = "";
|
||||
//for(i = 0; i < e.data.length; i++)
|
||||
// ascii += e.data.charCodeAt(i).toString(16) + " ";
|
||||
//console.log(ascii);
|
||||
con.value += e.data.replace(/\<rn\>/g, '\r\n');
|
||||
con.scrollTop = con.scrollHeight;
|
||||
}, false);
|
||||
}
|
||||
|
||||
getAjax("/api/system", parseSys);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -262,6 +262,7 @@
|
|||
}
|
||||
|
||||
function parse(root) {
|
||||
if(null != root) {
|
||||
parseSys(root["system"]);
|
||||
parseIv(root["inverter"]);
|
||||
parseMqtt(root["mqtt"]);
|
||||
|
@ -270,6 +271,7 @@
|
|||
parseRadio(root["radio"]);
|
||||
parseSerial(root["serial"]);
|
||||
}
|
||||
}
|
||||
|
||||
getAjax("/api/setup", parse);
|
||||
</script>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<title>Update</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
{#HEAD}
|
||||
<script type="text/javascript" src="api.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Update</h1>
|
||||
|
@ -13,13 +13,21 @@
|
|||
Make sure that you have noted all or settings before starting an update. New versions maybe changed their memory layout which remains in default settings.
|
||||
</div>
|
||||
<br/><br/>
|
||||
{#CONTENT}
|
||||
<form method="POST" action="/update" enctype="multipart/form-data" accept-charset="utf-8">
|
||||
<input type="file" name="update"><input type="submit" value="Update">
|
||||
</form>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p class="left">© 2022</p>
|
||||
<p class="left"><a href="{#IP}/">Home</a></p>
|
||||
<p class="right">AHOY :: {#VERSION}</p>
|
||||
<p class="right"><a href="/reboot">Reboot</a></p>
|
||||
<p class="left"><a href="/">Home</a></p>
|
||||
<p class="right" id="version"></p>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
function parseSys(obj) {
|
||||
document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"];
|
||||
}
|
||||
|
||||
getAjax("/api/system", parseSys);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,43 +1,116 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Index - {DEVICE}</title>
|
||||
<title>Live</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<script type="text/javascript">
|
||||
getAjax('/livedata', 'livedata');
|
||||
window.setInterval("getAjax('/livedata', 'livedata')", {JS_TS});
|
||||
|
||||
function getAjax(url, resid) {
|
||||
var http = null;
|
||||
http = new XMLHttpRequest();
|
||||
if(http != null) {
|
||||
http.open("GET", url, true);
|
||||
http.onreadystatechange = print;
|
||||
http.send(null);
|
||||
}
|
||||
|
||||
function print() {
|
||||
if(http.readyState == 4) {
|
||||
document.getElementById(resid).innerHTML = http.responseText;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/css">
|
||||
</style>
|
||||
<script type="text/javascript" src="api.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>AHOY - {DEVICE}</h1>
|
||||
<h1>AHOY</h1>
|
||||
<div id="content" class="content">
|
||||
<div id="livedata"></div>
|
||||
<p>Every {TS}seconds the values are updated</p>
|
||||
<div id="live"></div>
|
||||
<p>Every <span id="refresh"></span> seconds the values are updated</p>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p class="left">© 2022</p>
|
||||
<p class="left"><a href="/">Home</a></p>
|
||||
<p class="right">AHOY :: {VERSION}</p>
|
||||
<p class="right" id="version"></p>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var intervalSet = false;
|
||||
function parseSys(obj) {
|
||||
document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"];
|
||||
}
|
||||
|
||||
function parseIv(obj, root) {
|
||||
var ivHtml = [];
|
||||
|
||||
var tDiv = div(["ch-all", "iv"]);
|
||||
tDiv.appendChild(span("Total", ["head"]));
|
||||
var total = new Array(root.ch0_fld_names.length).fill(0);
|
||||
if(obj.length > 1)
|
||||
ivHtml.push(tDiv);
|
||||
|
||||
for(var iv of obj) {
|
||||
main = div(["iv"]);
|
||||
var ch0 = div(["ch-iv"]);
|
||||
var ctrl = (iv["power_limit_active"]) ? "" : " (not controlled)";
|
||||
ch0.appendChild(span(iv["name"] + " Limit " + iv["power_limit_read"] + "%" + ctrl + " | last Alarm: " + iv["last_alarm"], ["head"]));
|
||||
|
||||
for(var j = 0; j < root.ch0_fld_names.length; j++) {
|
||||
var val = Math.round(iv["ch"][0][j] * 100) / 100;
|
||||
if(val > 0) {
|
||||
var sub = div(["subgrp"]);
|
||||
sub.appendChild(span(val + " " + span(root["ch0_fld_units"][j], ["unit"]).innerHTML, ["value"]));
|
||||
sub.appendChild(span(root["ch0_fld_names"][j], ["info"]));
|
||||
ch0.appendChild(sub);
|
||||
|
||||
switch(j) {
|
||||
case 2: total[j] += val; break;
|
||||
case 6: total[j] += val; break;
|
||||
case 7: total[j] += val; break;
|
||||
case 8: total[j] += val; break;
|
||||
case 10: total[j] += val; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
main.appendChild(ch0);
|
||||
|
||||
|
||||
for(var i = 1; i < 5; i++) {
|
||||
var ch = div(["ch"]);
|
||||
ch.appendChild(span(("" == iv["ch_names"][i]) ? ("CHANNEL " + i) : iv["ch_names"][i], ["head"]));
|
||||
|
||||
for(var j = 0; j < root.fld_names.length; j++) {
|
||||
var val = Math.round(iv["ch"][i][j] * 100) / 100;
|
||||
if(val > 0) {
|
||||
ch.appendChild(span(val + " " + span(root["fld_units"][j], ["unit"]).innerHTML, ["value"]));
|
||||
ch.appendChild(span(root["fld_names"][j], ["info"]));
|
||||
}
|
||||
}
|
||||
main.appendChild(ch);
|
||||
}
|
||||
|
||||
var ts = div(["ts"]);
|
||||
var date = new Date(iv["ts_last_success"] * 1000);
|
||||
ts.innerHTML = "Last received data requested at: " + date.toLocaleString('de-DE', {timeZone: 'UTC'});
|
||||
main.appendChild(ts);
|
||||
ivHtml.push(main);
|
||||
}
|
||||
|
||||
// total
|
||||
if(obj.length > 1) {
|
||||
for(var j = 0; j < root.ch0_fld_names.length; j++) {
|
||||
var val = total[j];
|
||||
if(val > 0) {
|
||||
var sub = div(["subgrp"]);
|
||||
sub.appendChild(span(val + " " + span(root["ch0_fld_units"][j], ["unit"]).innerHTML, ["value"]));
|
||||
sub.appendChild(span(root["ch0_fld_names"][j], ["info"]));
|
||||
tDiv.appendChild(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("live").replaceChildren(...ivHtml);
|
||||
}
|
||||
|
||||
function parse(obj) {
|
||||
if(null != obj) {
|
||||
parseSys(obj["system"]);
|
||||
parseIv(obj["inverter"], obj);
|
||||
document.getElementById("refresh").innerHTML = obj["refresh_interval"];
|
||||
if(false == intervalSet) {
|
||||
window.setInterval("getAjax('/api/live', parse)", obj["refresh_interval"] * 1000);
|
||||
intervalSet = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
document.getElementById("refresh").innerHTML = "n/a";
|
||||
}
|
||||
|
||||
getAjax("/api/live", parse);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define F(sl) (sl)
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
//-----------------------------------------------------------------------------
|
||||
// available levels
|
||||
#define DBG_ERROR 1
|
||||
|
@ -32,14 +33,22 @@
|
|||
#define DBGPRINTLN(str)
|
||||
#else
|
||||
#ifdef ARDUINO
|
||||
#define DBG_CB std::function<void(String)>
|
||||
extern DBG_CB mCb;
|
||||
//static DBG_CB mCb;
|
||||
|
||||
inline void registerDebugCb(DBG_CB cb) {
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
#ifndef DSERIAL
|
||||
#define DSERIAL Serial
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
inline void DBGPRINT(T str) { DSERIAL.print(str); }
|
||||
template <class T>
|
||||
inline void DBGPRINTLN(T str) { DBGPRINT(str); DBGPRINT(F("\r\n")); }
|
||||
//template <class T>
|
||||
inline void DBGPRINT(String str) { DSERIAL.print(str); if(NULL != mCb) mCb(str); }
|
||||
//template <class T>
|
||||
inline void DBGPRINTLN(String str) { DBGPRINT(str); DBGPRINT(F("\r\n")); }
|
||||
inline void DHEX(uint8_t b) {
|
||||
if( b<0x10 ) DSERIAL.print('0');
|
||||
DSERIAL.print(b,HEX);
|
||||
|
@ -60,6 +69,8 @@
|
|||
else if( b<0x10000000 ) DSERIAL.print(F("0"));
|
||||
DSERIAL.print(b,HEX);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
|
||||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef __TMPL_PROC__
|
||||
#define __TMPL_PROC__
|
||||
|
||||
// HTML template processor, searches keywords and calls callback
|
||||
// inspired by: https://github.com/plapointe6/EspHtmlTemplateProcessor
|
||||
|
||||
#include "dbg.h"
|
||||
#include <string.h>
|
||||
#include <functional>
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
#define MAX_BUFFER_SIZE 256
|
||||
#define MAX_KEY_LEN 20
|
||||
enum { ST_NONE = 0, ST_BUF, ST_PROC, ST_START, ST_KEY };
|
||||
|
||||
typedef std::function<String(char* key)> TmplCb;
|
||||
|
||||
class tmplProc {
|
||||
public:
|
||||
tmplProc(AsyncWebServerRequest *request, uint32_t bufStartSize = 1000) {
|
||||
// Note: don't choose the bufStartSize to small. A too small start
|
||||
// size will result in fractioned memory and maybe in a zero
|
||||
// increase -> fail (Arduino - cbuf.cpp)
|
||||
mRequest = request;
|
||||
mResponse = request->beginResponseStream("text/html", bufStartSize);
|
||||
}
|
||||
|
||||
~tmplProc() {}
|
||||
|
||||
void process(const char* tmpl, const uint32_t tmplLen, TmplCb cb) {
|
||||
char* buf = new char[MAX_BUFFER_SIZE];
|
||||
char* p = buf;
|
||||
uint32_t len = 0, pos = 0, i = 0;
|
||||
uint8_t state = ST_BUF;
|
||||
bool complete = false;
|
||||
|
||||
while (i < tmplLen) {
|
||||
switch (state) {
|
||||
default:
|
||||
DPRINTLN(DBG_DEBUG, F("unknown state"));
|
||||
break;
|
||||
case ST_BUF:
|
||||
if(0 != i) {
|
||||
buf[pos] = '\0';
|
||||
mResponse->print(p);
|
||||
}
|
||||
pos = 0;
|
||||
len = ((tmplLen - i) > MAX_BUFFER_SIZE) ? MAX_BUFFER_SIZE : (tmplLen - i);
|
||||
if((len + i) == tmplLen)
|
||||
complete = true;
|
||||
memcpy_P(buf, &tmpl[i], len);
|
||||
if(len < MAX_BUFFER_SIZE)
|
||||
buf[len] = '\0';
|
||||
p = buf;
|
||||
state = ST_PROC;
|
||||
break;
|
||||
|
||||
case ST_PROC:
|
||||
if(((pos + MAX_KEY_LEN) >= len) && !complete)
|
||||
state = ST_BUF;
|
||||
else if(buf[pos] == '{')
|
||||
state = ST_START;
|
||||
break;
|
||||
|
||||
case ST_START:
|
||||
if(buf[pos] == '#') {
|
||||
if(pos != 0)
|
||||
buf[pos-1] = '\0';
|
||||
mResponse->print(p);
|
||||
p = &buf[pos+1];
|
||||
state = ST_KEY;
|
||||
}
|
||||
else
|
||||
state = ST_PROC;
|
||||
break;
|
||||
|
||||
case ST_KEY:
|
||||
if(buf[pos] == '}') {
|
||||
buf[pos] = '\0';
|
||||
mResponse->print((cb)(p));
|
||||
p = &buf[pos+1];
|
||||
state = ST_PROC;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(ST_BUF != state) {
|
||||
pos++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
mResponse->print(p);
|
||||
delete[] buf;
|
||||
mRequest->send(mResponse);
|
||||
}
|
||||
|
||||
private:
|
||||
AsyncWebServerRequest *mRequest;
|
||||
AsyncResponseStream *mResponse;
|
||||
};
|
||||
|
||||
#endif /*__TMPL_PROC__*/
|
|
@ -17,6 +17,7 @@
|
|||
#include "html/h/setup_html.h"
|
||||
#include "html/h/visualization_html.h"
|
||||
#include "html/h/update_html.h"
|
||||
#include "html/h/serial_html.h"
|
||||
|
||||
const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq"};
|
||||
|
||||
|
@ -28,7 +29,7 @@ web::web(app *main, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, c
|
|||
mStat = stat;
|
||||
mVersion = version;
|
||||
mWeb = new AsyncWebServer(80);
|
||||
//mEvts = new AsyncEventSource("/events");
|
||||
mEvts = new AsyncEventSource("/events");
|
||||
|
||||
mApi = new webApi(mWeb, main, sysCfg, config, stat, version);
|
||||
}
|
||||
|
@ -51,19 +52,22 @@ void web::setup(void) {
|
|||
mWeb->on("/setup", HTTP_GET, std::bind(&web::onSetup, this, std::placeholders::_1));
|
||||
mWeb->on("/save", HTTP_ANY, std::bind(&web::showSave, this, std::placeholders::_1));
|
||||
|
||||
mWeb->on("/visualization", HTTP_ANY, std::bind(&web::showVisualization, this, std::placeholders::_1));
|
||||
mWeb->on("/livedata", HTTP_ANY, std::bind(&web::showLiveData, this, std::placeholders::_1));
|
||||
mWeb->on("/live", HTTP_ANY, std::bind(&web::onLive, this, std::placeholders::_1));
|
||||
mWeb->on("/api1", HTTP_POST, std::bind(&web::showWebApi, this, std::placeholders::_1));
|
||||
|
||||
|
||||
mWeb->on("/update", HTTP_GET, std::bind(&web::showUpdateForm, this, std::placeholders::_1));
|
||||
mWeb->on("/update", HTTP_GET, std::bind(&web::onUpdate, this, std::placeholders::_1));
|
||||
mWeb->on("/update", HTTP_POST, std::bind(&web::showUpdate, this, std::placeholders::_1),
|
||||
std::bind(&web::showUpdate2, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6));
|
||||
mWeb->on("/serial", HTTP_GET, std::bind(&web::onSerial, this, std::placeholders::_1));
|
||||
|
||||
//mEvts->onConnect(std::bind(&web::onConnect, this, std::placeholders::_1));
|
||||
//mWeb->addHandler(mEvts);
|
||||
|
||||
mEvts->onConnect(std::bind(&web::onConnect, this, std::placeholders::_1));
|
||||
mWeb->addHandler(mEvts);
|
||||
|
||||
mApi->setup();
|
||||
|
||||
registerDebugCb(std::bind(&web::serialCb, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
|
||||
|
@ -74,14 +78,14 @@ void web::loop(void) {
|
|||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/*void web::onConnect(AsyncEventSourceClient *client) {
|
||||
DPRINTLN(DBG_INFO, "onConnect");
|
||||
void web::onConnect(AsyncEventSourceClient *client) {
|
||||
DPRINTLN(DBG_VERBOSE, "onConnect");
|
||||
|
||||
if(client->lastId())
|
||||
DPRINTLN(DBG_INFO, "Client reconnected! Last message ID that it got is: " + String(client->lastId()));
|
||||
DPRINTLN(DBG_VERBOSE, "Client reconnected! Last message ID that it got is: " + String(client->lastId()));
|
||||
|
||||
client->send("hello!", NULL, millis(), 1000);
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -91,11 +95,6 @@ void web::onIndex(AsyncWebServerRequest *request) {
|
|||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html"), index_html, index_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
|
||||
/*
|
||||
html.replace(F("{TS}"), String(mConfig->sendInterval) + " ");
|
||||
html.replace(F("{JS_TS}"), String(mConfig->sendInterval * 1000));
|
||||
request->send(200, "text/html", html);*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -318,149 +317,17 @@ void web::showSave(AsyncWebServerRequest *request) {
|
|||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void web::showVisualization(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("web::showVisualization"));
|
||||
String html = FPSTR(visualization_html);
|
||||
html.replace(F("{DEVICE}"), mSysCfg->deviceName);
|
||||
html.replace(F("{VERSION}"), mVersion);
|
||||
html.replace(F("{TS}"), String(mConfig->sendInterval) + " ");
|
||||
html.replace(F("{JS_TS}"), String(mConfig->sendInterval * 1000));
|
||||
request->send(200, F("text/html"), html);
|
||||
void web::onLive(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onLive"));
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html"), visualization_html, visualization_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void web::showLiveData(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("web::showLiveData"));
|
||||
|
||||
String modHtml, totalModHtml;
|
||||
float totalYield = 0, totalYieldToday = 0, totalActual = 0;
|
||||
uint8_t count = 0;
|
||||
|
||||
for (uint8_t id = 0; id < mMain->mSys->getNumInverters(); id++) {
|
||||
count++;
|
||||
|
||||
Inverter<> *iv = mMain->mSys->getInverterByPos(id);
|
||||
if (NULL != iv) {
|
||||
#ifdef LIVEDATA_VISUALIZED
|
||||
uint8_t modNum, pos;
|
||||
switch (iv->type) {
|
||||
default:
|
||||
case INV_TYPE_1CH: modNum = 1; break;
|
||||
case INV_TYPE_2CH: modNum = 2; break;
|
||||
case INV_TYPE_4CH: modNum = 4; break;
|
||||
}
|
||||
|
||||
modHtml += F("<div class=\"iv\">"
|
||||
"<div class=\"ch-iv\"><span class=\"head\">")
|
||||
+ String(iv->name) + F(" Limit ")
|
||||
+ String(iv->actPowerLimit) + F("%");
|
||||
if(NoPowerLimit == iv->powerLimit[1])
|
||||
modHtml += F(" (not controlled)");
|
||||
modHtml += F(" | last Alarm: ") + iv->lastAlarmMsg + F("</span>");
|
||||
|
||||
uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PCT, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_PRA, FLD_ALARM_MES_ID};
|
||||
|
||||
for (uint8_t fld = 0; fld < 11; fld++) {
|
||||
pos = (iv->getPosByChFld(CH0, list[fld]));
|
||||
|
||||
if(fld == 6){
|
||||
totalYield += iv->getValue(pos);
|
||||
}
|
||||
|
||||
if(fld == 7){
|
||||
totalYieldToday += iv->getValue(pos);
|
||||
}
|
||||
|
||||
if(fld == 2){
|
||||
totalActual += iv->getValue(pos);
|
||||
}
|
||||
|
||||
if (0xff != pos) {
|
||||
modHtml += F("<div class=\"subgrp\">");
|
||||
modHtml += F("<span class=\"value\">") + String(iv->getValue(pos));
|
||||
modHtml += F("<span class=\"unit\">") + String(iv->getUnit(pos)) + F("</span></span>");
|
||||
modHtml += F("<span class=\"info\">") + String(iv->getFieldName(pos)) + F("</span>");
|
||||
modHtml += F("</div>");
|
||||
}
|
||||
}
|
||||
modHtml += "</div>";
|
||||
|
||||
for (uint8_t ch = 1; ch <= modNum; ch++) {
|
||||
modHtml += F("<div class=\"ch\"><span class=\"head\">");
|
||||
if (iv->chName[ch - 1][0] == 0)
|
||||
modHtml += F("CHANNEL ") + String(ch);
|
||||
else
|
||||
modHtml += String(iv->chName[ch - 1]);
|
||||
modHtml += F("</span>");
|
||||
for (uint8_t j = 0; j < 6; j++) {
|
||||
switch (j) {
|
||||
default: pos = (iv->getPosByChFld(ch, FLD_UDC)); break;
|
||||
case 1: pos = (iv->getPosByChFld(ch, FLD_IDC)); break;
|
||||
case 2: pos = (iv->getPosByChFld(ch, FLD_PDC)); break;
|
||||
case 3: pos = (iv->getPosByChFld(ch, FLD_YD)); break;
|
||||
case 4: pos = (iv->getPosByChFld(ch, FLD_YT)); break;
|
||||
case 5: pos = (iv->getPosByChFld(ch, FLD_IRR)); break;
|
||||
}
|
||||
if (0xff != pos) {
|
||||
modHtml += F("<span class=\"value\">") + String(iv->getValue(pos));
|
||||
modHtml += F("<span class=\"unit\">") + String(iv->getUnit(pos)) + F("</span></span>");
|
||||
modHtml += F("<span class=\"info\">") + String(iv->getFieldName(pos)) + F("</span>");
|
||||
}
|
||||
}
|
||||
modHtml += "</div>";
|
||||
}
|
||||
modHtml += F("<div class=\"ts\">Last received data requested at: ") + mMain->getDateTimeStr(iv->ts) + F("</div>");
|
||||
modHtml += F("</div>");
|
||||
#else
|
||||
// dump all data to web frontend
|
||||
modHtml = F("<pre>");
|
||||
char topic[30], val[10];
|
||||
for (uint8_t i = 0; i < iv->listLen; i++) {
|
||||
snprintf(topic, 30, "%s/ch%d/%s", iv->name, iv->assign[i].ch, iv->getFieldName(i));
|
||||
snprintf(val, 10, "%.3f %s", iv->getValue(i), iv->getUnit(i));
|
||||
modHtml += String(topic) + ": " + String(val) + "\n";
|
||||
}
|
||||
modHtml += F("</pre>");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(count > 1){
|
||||
totalModHtml += F("<div class=\"iv\">"
|
||||
"<div class=\"ch-all\"><span class=\"head\">Gesamt</span>");
|
||||
|
||||
totalModHtml += F("<div class=\"subgrp\">");
|
||||
totalModHtml += F("<span class=\"value\">") + String(totalActual);
|
||||
totalModHtml += F("<span class=\"unit\">W</span></span>");
|
||||
totalModHtml += F("<span class=\"info\">P_AC All</span>");
|
||||
totalModHtml += F("</div>");
|
||||
|
||||
totalModHtml += F("<div class=\"subgrp\">");
|
||||
totalModHtml += F("<span class=\"value\">") + String(totalYieldToday);
|
||||
totalModHtml += F("<span class=\"unit\">Wh</span></span>");
|
||||
totalModHtml += F("<span class=\"info\">YieldDayAll</span>");
|
||||
totalModHtml += F("</div>");
|
||||
|
||||
totalModHtml += F("<div class=\"subgrp\">");
|
||||
totalModHtml += F("<span class=\"value\">") + String(totalYield);
|
||||
totalModHtml += F("<span class=\"unit\">kWh</span></span>");
|
||||
totalModHtml += F("<span class=\"info\">YieldTotalAll</span>");
|
||||
totalModHtml += F("</div>");
|
||||
|
||||
totalModHtml += F("</div>");
|
||||
totalModHtml += F("</div>");
|
||||
request->send(200, F("text/html"), modHtml);
|
||||
} else {
|
||||
request->send(200, F("text/html"), modHtml);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void web::showWebApi(AsyncWebServerRequest *request)
|
||||
{
|
||||
void web::showWebApi(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("web::showWebApi"));
|
||||
DPRINTLN(DBG_DEBUG, request->arg("plain"));
|
||||
const size_t capacity = 200; // Use arduinojson.org/assistant to compute the capacity.
|
||||
|
@ -531,9 +398,13 @@ void web::showWebApi(AsyncWebServerRequest *request)
|
|||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void web::showUpdateForm(AsyncWebServerRequest *request) {
|
||||
tmplProc *proc = new tmplProc(request, 850);
|
||||
proc->process(update_html, update_html_len, std::bind(&web::showUpdateFormCb, this, std::placeholders::_1));
|
||||
void web::onUpdate(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onUpdate"));
|
||||
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html"), update_html, update_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
|
@ -580,27 +451,17 @@ void web::showUpdate2(AsyncWebServerRequest *request, String filename, size_t in
|
|||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
String web::replaceHtmlGenericKeys(char *key) {
|
||||
if(0 == strncmp(key, "VERSION", 7)) return mVersion;
|
||||
else if(0 == strncmp(key, "DEVICE", 6)) return mSysCfg->deviceName;
|
||||
else if(0 == strncmp(key, "IP", 2)) {
|
||||
if(mMain->getWifiApActive()) return F("http://192.168.1.1");
|
||||
else return (F("http://") + String(WiFi.localIP().toString()));
|
||||
}
|
||||
void web::onSerial(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onSerial"));
|
||||
|
||||
return "";
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html"), serial_html, serial_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
String web::showUpdateFormCb(char *key) {
|
||||
String generic = replaceHtmlGenericKeys(key);
|
||||
if(generic.length() == 0) {
|
||||
if(0 == strncmp(key, "CONTENT", 7))
|
||||
return F("<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>");
|
||||
else if(0 == strncmp(key, "HEAD", 4))
|
||||
return "";
|
||||
}
|
||||
|
||||
return generic;
|
||||
void web::serialCb(String msg) {
|
||||
msg.replace("\r\n", "<rn>");
|
||||
mEvts->send(msg.c_str(), "serial", millis());
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "ESPAsyncWebServer.h"
|
||||
#include "app.h"
|
||||
#include "webApi.h"
|
||||
#include "tmplProc.h"
|
||||
|
||||
class app;
|
||||
class webApi;
|
||||
|
@ -37,17 +36,17 @@ class web {
|
|||
void onSetup(AsyncWebServerRequest *request);
|
||||
void showSave(AsyncWebServerRequest *request);
|
||||
|
||||
void showVisualization(AsyncWebServerRequest *request);
|
||||
void showLiveData(AsyncWebServerRequest *request);
|
||||
void onLive(AsyncWebServerRequest *request);
|
||||
void showWebApi(AsyncWebServerRequest *request);
|
||||
|
||||
void showUpdateForm(AsyncWebServerRequest *request);
|
||||
void onUpdate(AsyncWebServerRequest *request);
|
||||
void showUpdate(AsyncWebServerRequest *request);
|
||||
void showUpdate2(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||
|
||||
void serialCb(String msg);
|
||||
|
||||
private:
|
||||
String replaceHtmlGenericKeys(char *key);
|
||||
String showUpdateFormCb(char* key);
|
||||
void onSerial(AsyncWebServerRequest *request);
|
||||
|
||||
AsyncWebServer *mWeb;
|
||||
AsyncEventSource *mEvts;
|
||||
|
|
|
@ -59,6 +59,23 @@ void webApi::onApi(AsyncWebServerRequest *request) {
|
|||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::getNotFound(JsonObject obj, String url) {
|
||||
JsonObject ep = obj.createNestedObject("avail_endpoints");
|
||||
ep[F("system")] = url + F("system");
|
||||
ep[F("statistics")] = url + F("statistics");
|
||||
ep[F("inverter/list")] = url + F("inverter/list");
|
||||
ep[F("mqtt")] = url + F("mqtt");
|
||||
ep[F("ntp")] = url + F("ntp");
|
||||
ep[F("pinout")] = url + F("pinout");
|
||||
ep[F("radio")] = url + F("radio");
|
||||
ep[F("serial")] = url + F("serial");
|
||||
ep[F("index")] = url + F("index");
|
||||
ep[F("setup")] = url + F("setup");
|
||||
ep[F("live")] = url + F("live");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::getSystem(JsonObject obj) {
|
||||
obj[F("ssid")] = mSysCfg->stationSsid;
|
||||
|
@ -148,23 +165,6 @@ void webApi::getSerial(JsonObject obj) {
|
|||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::getNotFound(JsonObject obj, String url) {
|
||||
JsonObject ep = obj.createNestedObject("avail_endpoints");
|
||||
ep[F("system")] = url + F("system");
|
||||
ep[F("statistics")] = url + F("statistics");
|
||||
ep[F("inverter/list")] = url + F("inverter/list");
|
||||
ep[F("mqtt")] = url + F("mqtt");
|
||||
ep[F("ntp")] = url + F("ntp");
|
||||
ep[F("pinout")] = url + F("pinout");
|
||||
ep[F("radio")] = url + F("radio");
|
||||
ep[F("serial")] = url + F("serial");
|
||||
ep[F("index")] = url + F("index");
|
||||
ep[F("setup")] = url + F("setup");
|
||||
ep[F("live")] = url + F("live");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::getIndex(JsonObject obj) {
|
||||
getSystem(obj.createNestedObject(F("system")));
|
||||
|
@ -216,7 +216,9 @@ void webApi::getSetup(JsonObject obj) {
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::getLive(JsonObject obj) {
|
||||
getSystem(obj.createNestedObject(F("system")));
|
||||
JsonArray invArr = obj.createNestedArray(F("inverter"));
|
||||
obj["refresh_interval"] = SEND_INTERVAL;
|
||||
|
||||
uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PCT, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_PRA, FLD_ALARM_MES_ID};
|
||||
|
||||
|
@ -235,18 +237,16 @@ void webApi::getLive(JsonObject obj) {
|
|||
|
||||
JsonArray ch = obj2.createNestedArray("ch");
|
||||
JsonArray ch0 = ch.createNestedArray();
|
||||
obj2[F("ch_names")][0] = "AC";
|
||||
for (uint8_t fld = 0; fld < 11; fld++) {
|
||||
pos = (iv->getPosByChFld(CH0, list[fld]));
|
||||
if (0xff != pos) {
|
||||
JsonObject dat = ch0.createNestedObject();
|
||||
dat[F("value")] = iv->getValue(pos);
|
||||
dat[F("unit")] = String(iv->getUnit(pos));
|
||||
dat[F("name")] = String(iv->getFieldName(pos));
|
||||
}
|
||||
ch0[fld] = (0xff != pos) ? iv->getValue(pos) : 0.0;
|
||||
obj[F("ch0_fld_units")][fld] = (0xff != pos) ? String(iv->getUnit(pos)) : F("n/a");
|
||||
obj[F("ch0_fld_names")][fld] = (0xff != pos) ? String(iv->getFieldName(pos)) : F("n/a");
|
||||
}
|
||||
|
||||
for(uint8_t j = 1; j <= iv->channels; j ++) {
|
||||
obj2[F("ch_names")][j-1] = iv->chName[j];
|
||||
obj2[F("ch_names")][j] = String(iv->chName[j-1]);
|
||||
JsonArray cur = ch.createNestedArray();
|
||||
for (uint8_t k = 0; k < 6; k++) {
|
||||
switch(k) {
|
||||
|
@ -258,9 +258,9 @@ void webApi::getLive(JsonObject obj) {
|
|||
case 5: pos = (iv->getPosByChFld(j, FLD_IRR)); break;
|
||||
}
|
||||
cur[k] = (0xff != pos) ? iv->getValue(pos) : 0.0;
|
||||
if((0 == j) && (0xff != pos)) {
|
||||
obj2[F("fld_units")][k] = String(iv->getUnit(pos));
|
||||
obj2[F("fld_names")][k] = String(iv->getFieldName(pos));
|
||||
if(1 == j) {
|
||||
obj[F("fld_units")][k] = (0xff != pos) ? String(iv->getUnit(pos)) : F("n/a");
|
||||
obj[F("fld_names")][k] = (0xff != pos) ? String(iv->getFieldName(pos)) : F("n/a");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ class webApi {
|
|||
|
||||
private:
|
||||
void onApi(AsyncWebServerRequest *request);
|
||||
void getNotFound(JsonObject obj, String url);
|
||||
|
||||
void getSystem(JsonObject obj);
|
||||
void getStatistics(JsonObject obj);
|
||||
|
@ -29,7 +30,6 @@ class webApi {
|
|||
void getRadio(JsonObject obj);
|
||||
void getSerial(JsonObject obj);
|
||||
|
||||
void getNotFound(JsonObject obj, String url);
|
||||
void getIndex(JsonObject obj);
|
||||
void getSetup(JsonObject obj);
|
||||
void getLive(JsonObject obj);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue