mirror of
https://github.com/lumapu/ahoy.git
synced 2025-07-13 14:37:16 +02:00
0.7.64
* moved active power control to modal in `live` view (per inverter) by click on current APC state
This commit is contained in:
parent
ebb79d1d09
commit
1bbe979bcd
7 changed files with 130 additions and 118 deletions
|
@ -1,5 +1,8 @@
|
|||
# Development Changes
|
||||
|
||||
## 0.7.64 - 2023-10-02
|
||||
* moved active power control to modal in `live` view (per inverter) by click on current APC state
|
||||
|
||||
## 0.7.63 - 2023-10-01
|
||||
* fix NRF24 communication #1200
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//-------------------------------------
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 7
|
||||
#define VERSION_PATCH 63
|
||||
#define VERSION_PATCH 64
|
||||
|
||||
//-------------------------------------
|
||||
typedef struct {
|
||||
|
|
|
@ -105,6 +105,8 @@ class RestApi {
|
|||
getIvVersion(root, request->url().substring(22).toInt());
|
||||
else if(path.substring(0, 19) == "inverter/radiostat/")
|
||||
getIvStatistis(root, request->url().substring(24).toInt());
|
||||
else if(path.substring(0, 16) == "inverter/pwrack/")
|
||||
getIvPowerLimitAck(root, request->url().substring(21).toInt());
|
||||
else
|
||||
getNotFound(root, F("http://") + request->host() + F("/api/"));
|
||||
}
|
||||
|
@ -276,7 +278,7 @@ class RestApi {
|
|||
void getHtmlSystem(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getSysInfo(request, obj.createNestedObject(F("system")));
|
||||
getGeneric(request, obj.createNestedObject(F("generic")));
|
||||
obj[F("html")] = F("<a href=\"/factory\" class=\"btn\">Factory Reset</a><br/><br/><a href=\"/reboot\" class=\"btn\">Reboot</a>");
|
||||
obj[F("html")] = F("<a href=\"/factory\" class=\"btn\">AhoyFactory Reset</a><br/><br/><a href=\"/reboot\" class=\"btn\">Reboot</a>");
|
||||
}
|
||||
|
||||
void getHtmlLogout(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
|
@ -322,6 +324,15 @@ class RestApi {
|
|||
obj[F("retransmits")] = iv->radioStatistics.retransmits;
|
||||
}
|
||||
|
||||
void getIvPowerLimitAck(JsonObject obj, uint8_t id) {
|
||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||
if(NULL == iv) {
|
||||
obj[F("error")] = F("inverter not found!");
|
||||
return;
|
||||
}
|
||||
obj["ack"] = (bool)iv->powerLimitAck;
|
||||
}
|
||||
|
||||
void getInverterList(JsonObject obj) {
|
||||
JsonArray invArr = obj.createNestedArray(F("inverter"));
|
||||
|
||||
|
@ -389,10 +400,10 @@ class RestApi {
|
|||
ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0;
|
||||
}
|
||||
} else {
|
||||
for (uint8_t fld = 0; fld < sizeof(acList); fld++) {
|
||||
pos = (iv->getPosByChFld(CH0, acList[fld], rec));
|
||||
ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0;
|
||||
}
|
||||
for (uint8_t fld = 0; fld < sizeof(acList); fld++) {
|
||||
pos = (iv->getPosByChFld(CH0, acList[fld], rec));
|
||||
ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// DC
|
||||
|
@ -652,6 +663,7 @@ class RestApi {
|
|||
jsonOut[F("error")] = F("inverter index invalid: ") + jsonIn[F("id")].as<String>();
|
||||
return false;
|
||||
}
|
||||
jsonOut[F("id")] = jsonIn[F("id")];
|
||||
|
||||
if(F("power") == jsonIn[F("cmd")])
|
||||
accepted = iv->setDevControlRequest((jsonIn[F("val")] == 1) ? TurnOn : TurnOff);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</a>
|
||||
<div id="topnav" class="mobile">
|
||||
<a id="nav3" class="hide" href="/live?v={#VERSION}">Live</a>
|
||||
<a id="nav4" class="hide" href="/serial?v={#VERSION}">Serial / Control</a>
|
||||
<a id="nav4" class="hide" href="/serial?v={#VERSION}">Webserial</a>
|
||||
<a id="nav5" class="hide" href="/setup?v={#VERSION}">Settings</a>
|
||||
<span class="seperator"></span>
|
||||
<a id="nav6" class="hide" href="/update?v={#VERSION}">Update</a>
|
||||
|
|
|
@ -12,53 +12,13 @@
|
|||
<textarea id="serial" class="mt-3" cols="80" rows="20" readonly></textarea>
|
||||
</div>
|
||||
<div class="row my-3">
|
||||
<div class="col-3">connected: <span class="dot" id="connected"></span></div>
|
||||
<div class="col-3">console active: <span class="dot" id="active"></span></div>
|
||||
<div class="col-3 col-sm-4 my-3">Uptime: <span id="uptime"></span></div>
|
||||
<div class="col-6 col-sm-4">
|
||||
<div class="col-6 col-sm-4 a-r">
|
||||
<input type="button" value="clear" class="btn" id="clear"/>
|
||||
<input type="button" value="autoscroll" class="btn" id="scroll"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hr my-3"></div>
|
||||
<div class="row mb-3">
|
||||
<h3>Commands</h3>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-12 col-sm-3 my-2">Select Inverter</div>
|
||||
<div class="col-12 col-sm-9"><select name="iv" id="InvID"></select></div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-12 col-sm-3 my-2">Power Limit Command</div>
|
||||
<div class="col-12 col-sm-9">
|
||||
<select name="pwrlimctrl">
|
||||
<option value="" selected disabled hidden>select the unit and persistence</option>
|
||||
<option value="limit_nonpersistent_absolute">absolute non persistent [W]</option>
|
||||
<option value="limit_nonpersistent_relative">relative non persistent [%]</option>
|
||||
<option value="limit_persistent_absolute">absolute persistent [W]</option>
|
||||
<option value="limit_persistent_relative">relative persistent [%]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-12 col-sm-3 my-2">Power Limit Value</div>
|
||||
<div class="col-12 col-sm-9"><input type="number" name="pwrlimval" maxlength="4"/></div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-12 col-sm-3"></div>
|
||||
<div class="col-12 col-sm-9"><input type="button" value="Send Power Limit" class="btn" id="sendpwrlim"/></div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-12 col-sm-3 my-2">Control Inverter</div>
|
||||
<div class="col-12 col-sm-9" id="power">
|
||||
<input type="button" value="Restart" class="btn" id="restart"/>
|
||||
<input type="button" value="Turn Off" class="btn" id="power_off"/>
|
||||
<input type="button" value="Turn On" class="btn" id="power_on"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="col-3 my-2">Ctrl result</div>
|
||||
<div class="col-9 my-2"><span id="result">n/a</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#HTML_FOOTER}
|
||||
|
@ -84,23 +44,11 @@
|
|||
parseESP(obj);
|
||||
window.setInterval("getAjax('/api/generic', parseGeneric)", 10000);
|
||||
exeOnce = false;
|
||||
getAjax("/api/inverter/list", parse);
|
||||
setTimeOffset();
|
||||
}
|
||||
}
|
||||
|
||||
function parse(root) {
|
||||
select = document.getElementById('InvID');
|
||||
|
||||
if(null == root) return;
|
||||
root = root.inverter;
|
||||
for(var i = 0; i < root.length; i++) {
|
||||
inv = root[i];
|
||||
var opt = document.createElement('option');
|
||||
opt.value = inv.id;
|
||||
opt.innerHTML = inv.name;
|
||||
select.appendChild(opt);
|
||||
}
|
||||
|
||||
function setTimeOffset() {
|
||||
// set time offset for serial console
|
||||
var obj = new Object();
|
||||
obj.cmd = "serial_utc_offset";
|
||||
|
@ -119,12 +67,12 @@
|
|||
if (!!window.EventSource) {
|
||||
var source = new EventSource('/events');
|
||||
source.addEventListener('open', function(e) {
|
||||
document.getElementById("connected").style.backgroundColor = "#0c0";
|
||||
document.getElementById("active").style.backgroundColor = "#0c0";
|
||||
}, false);
|
||||
|
||||
source.addEventListener('error', function(e) {
|
||||
if (e.target.readyState != EventSource.OPEN) {
|
||||
document.getElementById("connected").style.backgroundColor = "#f00";
|
||||
document.getElementById("active").style.backgroundColor = "#f00";
|
||||
}
|
||||
}, false);
|
||||
|
||||
|
@ -135,56 +83,6 @@
|
|||
}, false);
|
||||
}
|
||||
|
||||
|
||||
function ctrlCb(obj) {
|
||||
var e = document.getElementById("result");
|
||||
if(obj["success"])
|
||||
e.innerHTML = "ok";
|
||||
else
|
||||
e.innerHTML = "Error: " + obj["error"];
|
||||
}
|
||||
|
||||
function get_selected_iv() {
|
||||
var e = document.getElementById("InvID");
|
||||
return parseInt(e.value);
|
||||
}
|
||||
|
||||
const wrapper = document.getElementById('power');
|
||||
|
||||
wrapper.addEventListener('click', (event) => {
|
||||
var obj = new Object();
|
||||
obj.id = get_selected_iv();
|
||||
obj.cmd = "power";
|
||||
|
||||
switch (event.target.value) {
|
||||
default:
|
||||
case "Turn On":
|
||||
obj.val = 1;
|
||||
break;
|
||||
case "Turn Off":
|
||||
obj.val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj));
|
||||
});
|
||||
|
||||
document.getElementById("sendpwrlim").addEventListener("click", function() {
|
||||
var val = parseInt(document.getElementsByName('pwrlimval')[0].value);
|
||||
var cmd = document.getElementsByName('pwrlimctrl')[0].value;
|
||||
|
||||
if(isNaN(val)) {
|
||||
document.getElementById("result").textContent = "value is missing";
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = new Object();
|
||||
obj.id = get_selected_iv();
|
||||
obj.cmd = cmd;
|
||||
obj.val = val;
|
||||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj));
|
||||
});
|
||||
|
||||
getAjax("/api/generic", parseGeneric);
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -683,7 +683,7 @@
|
|||
if(!obj["pwd_set"])
|
||||
e.value = "";
|
||||
var d = document.getElementById("prot_mask");
|
||||
var a = ["Index", "Live", "Serial / Console", "Settings", "Update", "System"];
|
||||
var a = ["Index", "Live", "Webserial", "Settings", "Update", "System"];
|
||||
var el = [];
|
||||
for(var i = 0; i < 6; i++) {
|
||||
var chk = ((obj["prot_mask"] & (1 << i)) == (1 << i));
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
var mIvHtml = [];
|
||||
var mNum = 0;
|
||||
var total = Array(5).fill(0);
|
||||
var tPwrAck;
|
||||
|
||||
function parseGeneric(obj) {
|
||||
if(true == exeOnce){
|
||||
|
@ -113,8 +114,10 @@
|
|||
ml("div", {class: "col mx-2 mx-md-1"}, ml("span", { class: "pointer", onclick: function() {
|
||||
getAjax("/api/inverter/version/" + obj.id, parseIvVersion);
|
||||
}}, obj.name)),
|
||||
ml("div", {class: "col a-c d-none d-sm-block"}, "Active Power Control: " + pwrLimit),
|
||||
ml("div", {class: "col a-c d-block d-sm-none"}, "APC: " + pwrLimit),
|
||||
ml("div", {class: "col a-c", onclick: function() {limitModal(obj)}}, [
|
||||
ml("span", {class: "d-none d-sm-block pointer"}, "Active Power Control: " + pwrLimit),
|
||||
ml("span", {class: "d-block d-sm-none pointer"}, "APC: " + pwrLimit)
|
||||
]),
|
||||
ml("div", {class: "col a-c"}, ml("span", { class: "pointer", onclick: function() {
|
||||
getAjax("/api/inverter/alarm/" + obj.id, parseIvAlarm);
|
||||
}}, ("Alarms: " + obj.alarm_cnt))),
|
||||
|
@ -305,6 +308,102 @@
|
|||
modal("Radio statistics for inverter " + obj.name, ml("div", {}, html));
|
||||
}
|
||||
|
||||
function limitModal(obj) {
|
||||
var opt = [["pct", "%"], ["watt", "W"]];
|
||||
var html = ml("div", {}, [
|
||||
ml("div", {class: "row mb-3"}, [
|
||||
ml("div", {class: "col-12 col-sm-5 my-2"}, "Limit Value"),
|
||||
ml("div", {class: "col-8 col-sm-5"}, ml("input", {name: "limit", type: "number"}, "")),
|
||||
ml("div", {class: "col-4 col-sm-2"}, sel("type", opt, "pct"))
|
||||
]),
|
||||
ml("div", {class: "row mb-3"}, [
|
||||
ml("div", {class: "col-8 col-sm-5"}, "Keep limit over inverter restart"),
|
||||
ml("div", {class: "col-4 col-sm-7"}, ml("input", {type: "checkbox", name: "keep"}))
|
||||
]),
|
||||
ml("div", {class: "row my-3"},
|
||||
ml("div", {class: "col a-r"}, ml("input", {type: "button", value: "Apply", class: "btn", onclick: function() {
|
||||
applyLimit(obj.id);
|
||||
}}, null))
|
||||
),
|
||||
ml("div", {class: "row my-4"}, [
|
||||
ml("div", {class: "col-12 col-sm-5 my-2"}, "Control"),
|
||||
ml("div", {class: "col col-sm-7 a-r"}, [
|
||||
ml("input", {type: "button", value: "restart", class: "btn", onclick: function() {
|
||||
applyCtrl(obj.id, "restart");
|
||||
}}, null),
|
||||
ml("input", {type: "button", value: "turn off", class: "btn mx-1", onclick: function() {
|
||||
applyCtrl(obj.id, "power", 0);
|
||||
}}, null),
|
||||
ml("input", {type: "button", value: "turn on", class: "btn", onclick: function() {
|
||||
applyCtrl(obj.id, "power", 1);
|
||||
}}, null)
|
||||
])
|
||||
]),
|
||||
ml("div", {class: "row mt-1"}, [
|
||||
ml("div", {class: "col-12 col-sm-5 my-2"}, "Result"),
|
||||
ml("div", {class: "col-sm-7 my-2"}, ml("span", {name: "pwrres"}, "-"))
|
||||
])
|
||||
]);
|
||||
modal("Active Power Control for inverter " + obj.name, html);
|
||||
}
|
||||
|
||||
function applyLimit(id) {
|
||||
var cmd = "limit_";
|
||||
if(!document.getElementsByName("keep")[0].checked)
|
||||
cmd += "non";
|
||||
cmd += "persistent_";
|
||||
if(document.getElementsByName("type")[0].value == "pct")
|
||||
cmd += "relative";
|
||||
else
|
||||
cmd += "absolute";
|
||||
|
||||
var val = document.getElementsByName("limit")[0].value;
|
||||
if(isNaN(val))
|
||||
val = 100;
|
||||
|
||||
var obj = new Object();
|
||||
obj.id = id;
|
||||
obj.cmd = cmd;
|
||||
obj.val = val;
|
||||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj));
|
||||
}
|
||||
|
||||
function applyCtrl(id, cmd, val=0) {
|
||||
var obj = new Object();
|
||||
obj.id = id;
|
||||
obj.cmd = cmd;
|
||||
obj.val = val;
|
||||
getAjax("/api/ctrl", ctrlCb2, "POST", JSON.stringify(obj));
|
||||
}
|
||||
|
||||
function ctrlCb(obj) {
|
||||
var e = document.getElementsByName("pwrres")[0];
|
||||
if(obj.success) {
|
||||
e.innerHTML = "received command, waiting for inverter acknowledge ...";
|
||||
tPwrAck = window.setInterval("getAjax('/api/inverter/pwrack/" + obj.id + "', updatePwrAck)", 1000);
|
||||
}
|
||||
else
|
||||
e.innerHTML = "Error: " + obj["error"];
|
||||
}
|
||||
|
||||
function ctrlCb2(obj) {
|
||||
var e = document.getElementsByName("pwrres")[0];
|
||||
if(obj.success)
|
||||
e.innerHTML = "command received";
|
||||
else
|
||||
e.innerHTML = "Error: " + obj["error"];
|
||||
}
|
||||
|
||||
function updatePwrAck(obj) {
|
||||
if(!obj.ack)
|
||||
return;
|
||||
var e = document.getElementsByName("pwrres")[0];
|
||||
clearInterval(tPwrAck);
|
||||
if(null == e)
|
||||
return;
|
||||
e.innerHTML = "inverter acknowledged active power control command";
|
||||
}
|
||||
|
||||
function parse(obj) {
|
||||
if(null != obj) {
|
||||
parseGeneric(obj["generic"]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue