You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

940 lines
46 KiB

<!doctype html>
<html lang="en">
<head>
<title>Setup</title>
{#HTML_HEADER}
</head>
<body>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<form method="post" action="/save" id="settings">
<fieldset>
<button type="button" class="s_collapsible mt-4">System Config</button>
<div class="s_content">
<fieldset class="mb-2">
<legend class="des">Device Host Name</legend>
<div class="row mb-3">
<div class="col-12 col-sm-3">Device Name</div>
<div class="col-12 col-sm-9"><input type="text" name="device"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Reboot Ahoy at midnight</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="schedReboot"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Dark Mode</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="darkMode"/></div>
<div class="col-12">(empty browser cache or use CTRL + F5 after reboot to apply this setting)</div>
</div>
</fieldset>
<fieldset class="mb-4">
<legend class="des">System Config</legend>
<p class="des">Pinout</p>
<div id="pinout"></div>
<p class="des">Radio (NRF24L01+)</p>
<div id="rf24"></div>
<p class="des">Radio (CMT2300A)</p>
<div id="cmt"><div class="col-12">(ESP32 only)</div></div>
<p class="des">Serial Console</p>
<div class="row mb-3">
<div class="col-8 col-sm-3">print inverter data</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="serEn"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Serial Debug</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="serDbg"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Interval [s]</div>
<div class="col-12 col-sm-9"><input type="text" name="serIntvl" pattern="[0-9]+" title="Invalid input"/></div>
</div>
</fieldset>
</div>
<button type="button" class="s_collapsible">Network</button>
<div class="s_content">
<fieldset class="mb-2">
<legend class="des">WiFi</legend>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">AP Password (min. length: 8)</div>
<div class="col-12 col-sm-9"><input type="text" name="ap_pwd" minlength="8" /></div>
</div>
<p>Enter the credentials to your preferred WiFi station. After rebooting the device tries to connect with this information.</p>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Search Networks</div>
<div class="col-12 col-sm-9"><input type="button" name="scanbtn" id="scanbtn" class="btn" value="scan" onclick="scan()"/></div>
</div>
<div class="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3 my-2">Avail Networks</div>
<div class="col-12 col-sm-9">
<select name="networks" id="networks" onChange="selNet()">
<option value="-1" selected disabled hidden>not scanned</option>
</select>
</div>
</div>
<div class="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3 my-2">SSID</div>
<div class="col-12 col-sm-9"><input type="text" name="ssid"/></div>
</div>
<div class="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3">SSID is hidden</div>
<div class="col-12 col-sm-9"><input type="checkbox" name="hidd"/></div>
</div>
<div class="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3 my-2">Password</div>
<div class="col-12 col-sm-9"><input type="password" name="pwd" value="{PWD}"/></div>
</div>
</fieldset>
<fieldset class="mb-4">
<legend class="des">Static IP (optional)</legend>
<p>
Leave fields blank for DHCP<br/>
The following fields are parsed in this format: 192.168.4.1
</p>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">IP Address</div>
<div class="col-12 col-sm-9"><input type="text" name="ipAddr" maxlength="15" /></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Submask</div>
<div class="col-12 col-sm-9"><input type="text" name="ipMask" maxlength="15" /></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">DNS 1</div>
<div class="col-12 col-sm-9"><input type="text" name="ipDns1" maxlength="15" /></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">DNS 2</div>
<div class="col-12 col-sm-9"><input type="text" name="ipDns2" maxlength="15" /></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Gateway</div>
<div class="col-12 col-sm-9"><input type="text" name="ipGateway" maxlength="15" /></div>
</div>
</fieldset>
</div>
<button type="button" class="s_collapsible">Protection</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des mx-2">Protection</legend>
<div class="row mb-3">
<div class="col-12 col-sm-3 mb-2 mt-2">Admin Password</div>
<div class="col-12 col-sm-9"><input type="password" name="adminpwd" value="{PWD}"/></div>
</div>
<p>Select pages which should be protected by password</p>
<div id="prot_mask"></div>
</fieldset>
</div>
<button type="button" class="s_collapsible">Inverter</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des">Inverter</legend>
<div id="inverter"></div>
<div class="row mb-2">
<div class="col-12 col-sm-3"></div>
<div class="col-12 col-sm-9"><input type="button" id="btnAdd" class="btn" value="Add Inverter"/></div>
</div>
<div class="row mb-2">
<div class="col-12 col-sm-3"><p class="subdes">Note</p></div>
<div class="col-12 col-sm-9"><p>A 'max module power' value of '0' disables the channel in 'live' view</p></div>
</div>
<div class="row mb-2">
<div class="col-12 col-sm-3"><p class="subdes">General</p></div>
<div class="col-12 col-sm-9"></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Interval [s]</div>
<div class="col-12 col-sm-9"><input type="number" name="invInterval" title="Invalid input"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Max retries per Payload</div>
<div class="col-12 col-sm-9"><input type="number" name="invRetry"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3 mb-2">Reset values and YieldDay at midnight</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstMid"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3 mb-2">Reset values when inverter polling pauses at sunset</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstComStop"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Reset values when inverter status is 'not available'</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstNotAvail"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Reset 'max' values at midnight</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstMaxMid"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Start without time sync (useful in AP-Only-Mode)</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="strtWthtTm"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Yield Efficiency (should be between 0.95 and 0.96)</div>
<div class="col-4 col-sm-9"><input type="number" name="yldEff" step="any"/></div>
</div>
</fieldset>
</div>
<button type="button" class="s_collapsible">NTP Server</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des">NTP Server</legend>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">NTP Server / IP</div>
<div class="col-12 col-sm-9"><input type="text" name="ntpAddr"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">NTP Port</div>
<div class="col-12 col-sm-9"><input type="number" name="ntpPort"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">NTP Interval (in Minutes, min. 5 Minutes)</div>
<div class="col-12 col-sm-9"><input type="number" name="ntpIntvl"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">set System time</div>
<div class="col-12 col-sm-9">
<input type="button" name="ntpBtn" id="ntpBtn" class="btn" value="from browser" onclick="setTime()"/>
<input type="button" name="ntpSync" id="ntpSync" class="btn" value="sync NTP" onclick="syncTime()"/><br/>
<span id="apiResultNtp"></span>
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">System Time</div>
<div class="col-12 col-sm-9 my-2"><span id="date"></span></div>
</div>
</fieldset>
</div>
<button type="button" class="s_collapsible">Sunrise & Sunset</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des">Sunrise & Sunset</legend>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Latitude (decimal)</div>
<div class="col-12 col-sm-9"><input type="number" name="sunLat" step="any"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Longitude (decimal)</div>
<div class="col-12 col-sm-9"><input type="number" name="sunLon" step="any"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Offset (pre sunrise, post sunset)</div>
<div class="col-12 col-sm-9"><select name="sunOffs"></select></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Pause polling inverters during night</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="sunDisNightCom"/></div>
</div>
</fieldset>
</div>
<button type="button" class="s_collapsible">MQTT</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des">MQTT</legend>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Broker / Server IP</div>
<div class="col-12 col-sm-9"><input type="text" name="mqttAddr"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Port</div>
<div class="col-12 col-sm-9"><input type="number" name="mqttPort"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Client Id (optional)</div>
<div class="col-12 col-sm-9"><input type="text" name="mqttClientId"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Username (optional)</div>
<div class="col-12 col-sm-9"><input type="text" name="mqttUser"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Password (optional)</div>
<div class="col-12 col-sm-9"><input type="password" name="mqttPwd"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Topic</div>
<div class="col-12 col-sm-9"><input type="text" name="mqttTopic" pattern="[A-Za-z0-9./#$%&=+_-]+" title="Invalid input" /></div>
</div>
<p class="des">Send Inverter data in a fixed interval, even if there is no change. A value of '0' disables the fixed interval. The data is published once it was successfully received from inverter. (default: 0)</p>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Interval [s]</div>
<div class="col-12 col-sm-9"><input type="number" name="mqttInterval" title="Invalid input" /></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Discovery Config (homeassistant)</div>
<div class="col-12 col-sm-9">
<input type="button" name="mqttDiscovery" id="mqttDiscovery" class="btn" value="send" onclick="sendDiscoveryConfig()"/>
<span id="apiResultMqtt"></span>
</div>
</div>
</fieldset>
</div>
<button type="button" class="s_collapsible">Display Config</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des">Display Config</legend>
<div id="dispType"></div>
<div id="dispRot"></div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Turn off while inverters are offline</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="disp_pwr"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Enable Screensaver (pixel shifting, OLED only)</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="disp_pxshift"/></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Luminance</div>
<div class="col-12 col-sm-9"><input type="number" name="disp_cont" min="0" max="255"></select></div>
</div>
<p class="des">Pinout</p>
<div id="dispPins"></div>
</fieldset>
</div>
<div class="row mb-4 mt-4">
<div class="col-8 col-sm-3">Reboot device after successful save</div>
<div class="col-4 col-sm-9">
<input type="checkbox" name="reboot" checked />
<input type="submit" value="save" class="btn right"/>
</div>
</div>
</form>
<div class="hr mb-3 mt-3"></div>
<div class="mb-4 mt-4">
<a class="btn" href="/erase">ERASE SETTINGS (not WiFi)</a>
<fieldset class="mb-4">
<legend class="des">Import / Export JSON Settings</legend>
<div class="row mb-4 mt-4">
<div class="col-12 col-sm-3">Import</div>
<div class="col-12 col-sm-9">
<form id="form" method="POST" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
<div class="row">
<div class="col-12 col-sm-8 my-2"><input type="file" name="upload"></div>
<div class="col-12 col-sm-4 my-2"><input type="button" class="btn" value="Import" onclick="hide()"></div>
</div>
</form>
</div>
</div>
<div class="row mb-4 mt-4">
<div class="col-12 col-sm-3 my-2">Export</div>
<div class="col-12 col-sm-9">
<a class="btn" href="/get_setup" target="_blank">Export settings (JSON file)</a><span> (only values, passwords will be removed!)</span>
</div>
</div>
</fieldset>
</div>
</div>
</div>
{#HTML_FOOTER}
<script type="text/javascript">
var highestId = 0;
var maxInv = 0;
var ts = 0;
var esp8266pins = [
[255, "off / default"],
[0, "D3 (GPIO0)"],
[1, "TX (GPIO1)"],
[2, "D4 (GPIO2)"],
[3, "RX (GPIO3)"],
[4, "D2 (GPIO4, SDA)"],
[5, "D1 (GPIO5, SCL)"],
[6, "GPIO6"],
[7, "GPIO7"],
[8, "GPIO8"],
[9, "GPIO9"],
[10, "GPIO10"],
[11, "GPIO11"],
[12, "D6 (GPIO12)"],
[13, "D7 (GPIO13)"],
[14, "D5 (GPIO14)"],
[15, "D8 (GPIO15)"],
[16, "D0 (GPIO16 - no IRQ!)"]
];
var esp32pins = [
[255, "off / default"],
[0, "GPIO0"],
[1, "TX (GPIO1)"],
[2, "GPIO2 (LED)"],
[3, "RX (GPIO3)"],
[4, "GPIO4"],
[5, "GPIO5"],
[12, "GPIO12"],
[13, "GPIO13"],
[14, "GPIO14"],
[15, "GPIO15"],
[16, "GPIO16"],
[17, "GPIO17"],
[18, "GPIO18"],
[19, "GPIO19"],
[21, "GPIO21 (SDA)"],
[22, "GPIO22 (SCL)"],
[23, "GPIO23"],
[25, "GPIO25"],
[26, "GPIO26"],
[27, "GPIO27"],
[32, "GPIO32"],
[33, "GPIO33"],
[34, "GPIO34"],
[35, "GPIO35"],
[36, "VP (GPIO36)"],
[39, "VN (GPIO39)"]
];
var esp32s3pins = [
[255, "off / default"],
[0, "GPIO0 (DONT USE - BOOT)"],
[1, "GPIO1"],
[2, "GPIO2"],
[3, "GPIO3"],
[4, "GPIO4"],
[5, "GPIO5"],
[6, "GPIO6"],
[7, "GPIO7"],
[8, "GPIO8"],
[9, "GPIO9"],
[10, "GPIO10"],
[11, "GPIO11"],
[12, "GPIO12"],
[13, "GPIO13"],
[14, "GPIO14"],
[15, "GPIO15"],
[16, "GPIO16"],
[17, "GPIO17"],
[18, "GPIO18"],
[19, "GPIO19 (DONT USE - USB-)"],
[20, "GPIO20 (DONT USE - USB+)"],
[21, "GPIO21"],
[26, "GPIO26 (PSRAM - not available)"],
[27, "GPIO27 (FLASH - not available)"],
[28, "GPIO28 (FLASH - not available)"],
[29, "GPIO29 (FLASH - not available)"],
[30, "GPIO30 (FLASH - not available)"],
[31, "GPIO31 (FLASH - not available)"],
[32, "GPIO32 (FLASH - not available)"],
[33, "GPIO33 (not exposed on WROOM modules)"],
[34, "GPIO34 (not exposed on WROOM modules)"],
[35, "GPIO35"],
[36, "GPIO36"],
[37, "GPIO37"],
[38, "GPIO38"],
[39, "GPIO39"],
[40, "GPIO40"],
[41, "GPIO41"],
[42, "GPIO42"],
[43, "GPIO43"],
[44, "GPIO44"],
[45, "GPIO45 (DONT USE - STRAPPING PIN)"],
[46, "GPIO46 (DONT USE - STRAPPING PIN)"],
[47, "GPIO47"],
[48, "GPIO48"],
];
var led_high_active = [
[0, "low active"],
[1, "high active"],
];
const re = /1[1,3][2,4,6,8][1,2,4].*/;
window.onload = function() {
for(it of document.getElementsByClassName("s_collapsible")) {
it.addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
content.style.display = (content.style.display === "block") ? "none" : "block";
});
}
document.getElementById("settings").addEventListener("submit", function() {
var inputs = document.querySelectorAll("input[type='number']");
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].value.indexOf(",") != -1)
inputs[i].value = inputs[i].value.replace(",", ".");
}
});
}
document.getElementById("btnAdd").addEventListener("click", function() {
if(highestId <= (maxInv-1)) {
ivHtml(JSON.parse('{"enabled":true,"name":"","serial":"","channels":6,"ch_max_pwr":[0,0,0,0,0,0],"ch_name":["","","","","",""],"ch_yield_cor":[0,0,0,0,0,0]}'), highestId);
}
});
function apiCbWifi(obj) {
var e = document.getElementById("networks");
selDelAllOpt(e);
if(obj["success"])
e.appendChild(opt("-1", "scanning ..."))
else
e.appendChild(opt("-1", "Error: " + obj["error"]));
}
function apiCbNtp(obj) {
var e = document.getElementById("apiResultNtp");
if(obj["success"])
e.innerHTML = "command executed, set new time ...";
else
e.innerHTML = "Error: " + obj["error"];
}
function apiCbNtp2(obj) {
var e = document.getElementById("apiResultNtp");
var date = new Date(obj["ts_now"] * 1000);
e.innerHTML = "synced at: " + toIsoDateStr(date) + ", difference: " + (ts - obj["ts_now"]) + "ms";
}
function apiCbMqtt(obj) {
var e = document.getElementById("apiResultMqtt");
if(obj["success"])
e.innerHTML = "command executed";
else
e.innerHTML = "Error: " + obj["error"];
}
function setTime() {
var date = new Date();
var obj = new Object();
obj.cmd = "set_time";
obj.val = parseInt(date.getTime() / 1000);
getAjax("/api/setup", apiCbNtp, "POST", JSON.stringify(obj));
setTimeout(function() {getAjax('/api/index', apiCbNtp2)}, 2000);
}
function scan() {
var obj = new Object();
obj.cmd = "scan_wifi";
getAjax("/api/setup", apiCbWifi, "POST", JSON.stringify(obj));
setTimeout(function() {getAjax('/api/setup/networks', listNetworks)}, 5000);
}
function syncTime() {
var obj = new Object();
obj.cmd = "sync_ntp";
getAjax("/api/setup", apiCbNtp, "POST", JSON.stringify(obj));
setTimeout(function() {getAjax('/api/index', apiCbNtp2)}, 2000);
}
function sendDiscoveryConfig() {
var obj = new Object();
obj.cmd = "discovery_cfg";
getAjax("/api/setup", apiCbMqtt, "POST", JSON.stringify(obj));
}
function hide() {
document.getElementById("form").submit();
var e = document.getElementById("content");
e.replaceChildren(span("upload started"));
}
function delIv() {
var id = this.id.substring(0,4);
var e = document.getElementsByName(id + "Addr")[0];
e.value = "";
e.dispatchEvent(new Event("keyup"));
e.dispatchEvent(new Event("change"));
document.getElementsByName(id + "Name")[0].value = "";
}
function mlCb(id, des, chk=false) {
var cb = ml("input", {type: "checkbox", id: id, name: id}, "");
if(chk)
cb.checked = true;
return ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8 col-sm-3"}, des),
ml("div", {class: "col-4 col-sm-9"}, cb)
]);
}
function mlE(des, e) {
return ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, des),
ml("div", {class: "col-12 col-sm-9"}, e)
]);
}
function ivHtml(obj, id) {
highestId = id + 1;
if(highestId == maxInv)
setHide("btnAdd", true);
var iv = document.getElementById("inverter");
iv.appendChild(des("Inverter " + id));
id = "inv" + id;
var addr = inp(id + "Addr", obj["serial"], 12, ["text"], null, "text", "[0-9]+", "Invalid input");
iv.append(
mlCb(id + "Enable", "Communication Enable", obj["enabled"]),
mlE("Serial Number (12 digits)*", addr)
);
['keyup', 'change'].forEach(function(evt) {
addr.addEventListener(evt, (e) => {
var serial = addr.value.substring(0,4);
var max = 0;
for(var i=0;i<6;i++) {
setHide(id+"ModPwr"+i, true);
setHide(id+"ModName"+i, true);
setHide(id+"YieldCor"+i, true);
}
setHide("row"+id+"ModPwr", true);
setHide("row"+id+"ModName", true);
setHide("row"+id+"YieldCor", true);
if(serial.charAt(0) == 1) {
if((serial.charAt(1) == 0) || (serial.charAt(1) == 1) || (serial.charAt(1) == 3)) {
if((serial.charAt(3) == 1) || (serial.charAt(3) == 2) || (serial.charAt(3) == 4)) {
switch(serial.charAt(2)) {
case "2": max = 1; break;
case "4": max = 2; break;
case "6": max = 4; break;
case "8": max = 6; break;
}
}
}
}
if(max != 0) {
for(var i=0;i<max;i++) {
setHide(id+"ModPwr"+i, false);
setHide(id+"ModName"+i, false);
setHide(id+"YieldCor"+i, false);
}
setHide("row"+id+"ModPwr", false);
setHide("row"+id+"ModName", false);
setHide("row"+id+"YieldCor", false);
}
})
});
iv.append(mlE("Name*", inp(id + "Name", obj["name"], 16, ["text"], null, "text", "[A-Za-z0-9./#$%&=+_-]+", "Invalid input")));
for(var j of [
["ModPwr", "ch_max_pwr", "Max Module Power (Wp)", 4, "[0-9]+"],
["ModName", "ch_name", "Module Name", 15, null],
["YieldCor", "ch_yield_cor", "Yield Total Correction [kWh]", 8, "[0-9-\.]+"]]) {
var cl = (re.test(obj["serial"])) ? "" : " hide";
i = 0;
arrIn = [];
for(it of obj[j[1]]) {
arrIn.push(ml("div", {class: "col-3 "},
inp(id + j[0] + i, it, j[3], [], id + j[0] + i, "text", j[4], "Invalid input")
));
i++;
}
iv.append(
ml("div", {class: "row mb-2 mb-sm-3" + cl, id: "row" + id + j[0]}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, j[2]),
ml("div", {class: "col-12 col-sm-9"},
ml("div", {class: "row"}, arrIn)
)
])
);
}
var del = inp(id+"del", "X", 0, ["btn", "btnDel"], id+"del", "button");
del.addEventListener("click", delIv);
iv.append(mlE("Delete", del));
}
function ivGlob(obj) {
for(var i of [["invInterval", "interval"], ["invRetry", "retries"], ["yldEff", "yldEff"]])
document.getElementsByName(i[0])[0].value = obj[i[1]];
for(var i of ["Mid", "ComStop", "NotAvail", "MaxMid"])
document.getElementsByName("invRst"+i)[0].checked = obj["rst" + i];
document.getElementsByName("strtWthtTm")[0].checked = obj["strtWthtTm"];
}
function parseSys(obj) {
for(var i of [["device", "device_name"], ["ssid", "ssid"], ["ap_pwd", "ap_pwd"]])
document.getElementsByName(i[0])[0].value = obj[i[1]];
document.getElementsByName("hidd")[0].checked = obj["hidd"];
document.getElementsByName("darkMode")[0].checked = obj["dark_mode"];
document.getElementsByName("schedReboot")[0].checked = obj["sched_reboot"];
e = document.getElementsByName("adminpwd")[0];
if(!obj["pwd_set"])
e.value = "";
var d = document.getElementById("prot_mask");
var a = ["Index", "Live", "Serial / Console", "Settings", "Update", "System"];
var el = [];
for(var i = 0; i < 6; i++) {
var chk = ((obj["prot_mask"] & (1 << i)) == (1 << i));
el.push(mlCb("protMask" + i, a[i], chk))
}
d.append(...el);
}
function parseGeneric(obj) {
parseNav(obj);
parseESP(obj);
parseRssi(obj);
ts = obj["ts_now"];
window.setInterval("tick()", 1000);
}
function parseStaticIp(obj) {
for(var i of [["ipAddr", "ip"], ["ipMask", "mask"], ["ipDns1", "dns1"], ["ipDns2", "dns2"], ["ipGateway", "gateway"]])
if(null != obj[i[1]])
document.getElementsByName(i[0])[0].value = obj[i[1]];
}
function parseIv(obj) {
for(var i = 0; i < obj.inverter.length; i++)
ivHtml(obj.inverter[i], i);
ivGlob(obj);
maxInv = obj["max_num_inverters"];
}
function parseMqtt(obj) {
for(var i of [["Addr", "broker"], ["Port", "port"], ["ClientId", "clientId"], ["User", "user"], ["Pwd", "pwd"], ["Topic", "topic"], ["Interval", "interval"]])
document.getElementsByName("mqtt"+i[0])[0].value = obj[i[1]];
}
function parseNtp(obj) {
for(var i of [["ntpAddr", "addr"], ["ntpPort", "port"], ["ntpIntvl", "interval"]])
document.getElementsByName(i[0])[0].value = obj[i[1]];
}
function parseSun(obj) {
document.getElementsByName("sunLat")[0].value = obj["lat"];
document.getElementsByName("sunLon")[0].value = obj["lon"];
document.getElementsByName("sunDisNightCom")[0].checked = obj["disnightcom"];
const sel = document.getElementsByName("sunOffs")[0];
for(var i = 0; i <= 60; i++) {
sel.appendChild(opt(i, i + " minutes", (i == (obj["offs"] / 60))));
}
}
function parsePinout(obj, type, system) {
var e = document.getElementById("pinout");
pins = [['led0', 'pinLed0'], ['led1', 'pinLed1']];
for(p of pins) {
e.append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, p[0].toUpperCase()),
ml("div", {class: "col-12 col-sm-9"},
sel(p[1], ("ESP8266" == type) ? esp8266pins : ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, obj[p[0]])
)
])
);
}
e.append(
ml("div", { class: "row mb-3" }, [
ml("div", { class: "col-12 col-sm-3 my-2" }, "LED polarity"),
ml("div", { class: "col-12 col-sm-9" },
sel('pinLedHighActive', led_high_active, obj['led_high_active'])
)
])
)
}
function parseNrfRadio(obj, objPin, type, system) {
var e = document.getElementById("rf24");
var en = inp("nrfEnable", null, null, ["cb"], "nrfEnable", "checkbox");
en.checked = obj["en"];
e.replaceChildren (
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8 col-sm-3 my-2"}, "NRF24 Enable"),
ml("div", {class: "col-4 col-sm-9"}, en)
])
);
if ("ESP8266" == type) {
pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq']];
} else {
pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq'], ['sclk', 'pinSclk'], ['mosi', 'pinMosi'], ['miso', 'pinMiso']];
}
for(p of pins) {
e.append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, p[0].toUpperCase()),
ml("div", {class: "col-12 col-sm-9"},
sel(p[1], ("ESP8266" == type) ? esp8266pins : ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, objPin[p[0]])
)
])
);
}
e.append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, "Power Level"),
ml("div", {class: "col-12 col-sm-9"},
sel("rf24Power", [
[0, "MIN (recommended)"],
[1, "LOW"],
[2, "HIGH"],
[3, "MAX (experimental)"]
], obj["power_level"])
)
])
);
}
function parseCmtRadio(obj, type, system) {
var e = document.getElementById("cmt");
var en = inp("cmtEnable", null, null, ["cb"], "cmtEnable", "checkbox");
en.checked = obj["en"];
e.replaceChildren (
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8 col-sm-3 my-2"}, "CMT2300A Enable"),
ml("div", {class: "col-4 col-sm-9"}, en)
])
);
pins = [['csb', 'pinCsb'], ['fcsb', 'pinFcsb'], ['irq', 'pinGpio3']];
for(p of pins) {
e.append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, p[0].toUpperCase()),
ml("div", {class: "col-12 col-sm-9"},
sel(p[1], ("ESP8266" == type) ? esp8266pins : ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, obj[p[0]])
)
])
);
}
}
function parseSerial(obj) {
for(var i of [["serEn", "show_live_data"], ["serDbg", "debug"]])
document.getElementsByName(i[0])[0].checked = obj[i[1]];
document.getElementsByName("serIntvl")[0].value = obj["interval"];
}
function parseDisplay(obj, type, system) {
for(var i of ["disp_pwr", "disp_pxshift"])
document.getElementsByName(i)[0].checked = obj[i];
var e = document.getElementById("dispPins");
//KEEP this order !!!
var pins = [['clock', 'disp_clk'], ['data', 'disp_data'], ['cs', 'disp_cs'], ['dc', 'disp_dc'], ['reset', 'disp_rst']];
if("ESP32" == type)
pins.push(['busy', 'disp_bsy']);
for(p of pins) {
e.append(
ml("div", {class: "row mb-3", id: "row_" + p[1]}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, p[0].toUpperCase()),
ml("div", {class: "col-12 col-sm-9"},
sel(p[1], ("ESP8266" == type) ? esp8266pins : ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, obj[p[1]])
)
])
);
}
// keep display types grouped
var opts = [[0, "None"], [2, "SH1106 1.3\" 128X64"], [5, "SSD1306 0.66\" 64X48 (Wemos OLED Shield)"], [4, "SSD1306 0.91\" 128X32"], [1, "SSD1306 0.96\" 128X64"], [3, "Nokia5110"]];
if("ESP32" == type)
opts.push([10, "ePaper"]);
var dispType = sel("disp_typ", opts, obj["disp_typ"]);
document.getElementById("dispType").append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, "Type"),
ml("div", {class: "col-12 col-sm-9"}, dispType)
])
);
dispType.addEventListener('change', (e) => {
hideDispPins(pins, parseInt(e.target.value))
});
opts = [[0, "0&deg;"], [2, "180&deg;"]];
if("ESP32" == type) {
opts.push([1, "90&deg;"]);
opts.push([3, "270&deg;"]);
}
document.getElementById("dispRot").append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, "Rotation"),
ml("div", {class: "col-12 col-sm-9"}, sel("disp_rot", opts, obj["disp_rot"]))
])
);
document.getElementsByName("disp_cont")[0].value = obj["disp_cont"];
hideDispPins(pins, obj.disp_typ);
}
function hideDispPins(pins, dispType) {
// create pin map for each display type.
// It depends on fix pin array (see var pins)
// var pins = [['clock', 'disp_clk'], ['data', 'disp_data'], ['cs', 'disp_cs'], ['dc', 'disp_dc'], ['reset', 'disp_rst']];
const pinMap = new Map([
[0, [0,0,0,0,0,0]], //none
[1, [1,1,0,0,0,0]], //SSD1306_128X64
[2, [1,1,0,0,0,0]], //SH1106_128X64
[3, [1,1,1,1,0,0]], //PCD8544_84X48 /nokia5110
[4, [1,1,0,0,0,0]], //SSD1306_128X32
[5, [1,1,0,0,0,0]], //SSD1306_64X48
[10, [1,1,1,1,1,1]] //ePaper
])
for(var i = 0; i < pins.length; i++) {
var cl = document.getElementById("row_" + pins[i][1]).classList;
if(pinMap.get(dispType)[i]) {
cl.remove("hide");
}
else {
cl.add("hide");
}
}
}
function tick() {
document.getElementById("date").innerHTML = toIsoDateStr((new Date((++ts) * 1000)));
}
function parse(root) {
if(null != root) {
parseGeneric(root["generic"]);
parseSys(root["system"]);
parseStaticIp(root["static_ip"]);
parseMqtt(root["mqtt"]);
parseNtp(root["ntp"]);
parseSun(root["sun"]);
parsePinout(root["pinout"], root["system"]["esp_type"], root["system"]);
parseNrfRadio(root["radioNrf"], root["pinout"], root["system"]["esp_type"], root["system"]);
if(root["generic"]["esp_type"] == "ESP32")
parseCmtRadio(root["radioCmt"], root["system"]["esp_type"], root["system"]);
parseSerial(root["serial"]);
parseDisplay(root["display"], root["system"]["esp_type"], root["system"]);
getAjax("/api/inverter/list", parseIv);
}
}
function listNetworks(root) {
var s = document.getElementById("networks");
selDelAllOpt(s);
if(root["networks"].length > 0) {
s.appendChild(opt("-1", "please select network"));
for(i = 0; i < root["networks"].length; i++) {
s.appendChild(opt(root["networks"][i]["ssid"], root["networks"][i]["ssid"] + " (" + root["networks"][i]["rssi"] + " dBm)"));
}
} else
s.appendChild(opt("-1", "no network found"));
}
function selNet() {
var s = document.getElementById("networks");
var e = document.getElementsByName("ssid")[0];
if(-1 != s.value)
e.value = s.value;
}
getAjax("/api/setup", parse);
</script>
</body>
</html>