Browse Source

0.8.47

* started to have german translations of all variants (environments) #925 #1199
pull/1330/head
lumapu 1 year ago
parent
commit
b765916a2a
  1. 23
      .github/workflows/compile_development.yml
  2. 39
      scripts/convertHtml.py
  3. 54
      scripts/getVersion.py
  4. 1
      src/CHANGES.md
  5. 4
      src/config/config.h
  6. 144
      src/platformio.ini
  7. 2
      src/web/html/includes/header.html
  8. 10
      src/web/html/includes/nav.html
  9. 53
      src/web/html/index.html
  10. 8
      src/web/html/save.html
  11. 288
      src/web/html/setup.html
  12. 36
      src/web/html/system.html
  13. 8
      src/web/html/update.html
  14. 136
      src/web/html/visualization.html
  15. 28
      src/web/html/wizard.html
  16. 1334
      src/web/lang.json

23
.github/workflows/compile_development.yml

@ -43,7 +43,28 @@ jobs:
pip install --upgrade platformio
- name: Run PlatformIO
run: pio run -d src --environment esp8266 --environment esp8266-prometheus --environment esp8285 --environment esp32-wroom32 --environment esp32-wroom32-prometheus --environment esp32-wroom32-ethernet --environment esp32-s2-mini --environment esp32-c3-mini --environment opendtufusion --environment opendtufusion-ethernet
run: >
pio run -d src
--environment esp8266
--environment esp8266-prometheus
--environment esp8285
--environment esp32-wroom32
--environment esp32-wroom32-prometheus
--environment esp32-wroom32-ethernet
--environment esp32-s2-mini
--environment esp32-c3-mini
--environment opendtufusion
--environment opendtufusion-ethernet
--environment esp8266-de
--environment esp8266-prometheus-de
--environment esp8285-de
--environment esp32-wroom32-de
--environment esp32-wroom32-prometheus-de
--environment esp32-wroom32-ethernet-de
--environment esp32-s2-mini-de
--environment esp32-c3-mini-de
--environment opendtufusion-de
--environment opendtufusion-ethernet-de
- name: Copy boot_app0.bin
run: cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin src/.pio/build/opendtufusion/ota.bin

39
scripts/convertHtml.py

@ -3,6 +3,7 @@ import os
import gzip
import glob
import shutil
import json
from datetime import date
from pathlib import Path
import subprocess
@ -33,7 +34,7 @@ def readVersion(path):
ver += line[p+13:].rstrip() + "."
return ver[:-1]
def htmlParts(file, header, nav, footer, version):
def htmlParts(file, header, nav, footer, version, lang):
p = "";
f = open(file, "r")
lines = f.readlines()
@ -64,6 +65,8 @@ def htmlParts(file, header, nav, footer, version):
# remove if - endif ESP32
p = checkIf(p)
p = translate(file, p, lang)
p = translate("general", p, lang) # menu / header / footer
f = open("tmp/" + file, "w")
f.write(p);
@ -94,7 +97,30 @@ def checkIf(data):
return data
def convert2Header(inFile, version):
def findLang(file):
with open('../lang.json') as j:
lang = json.load(j)
for l in lang["files"]:
if l["name"] == file:
return l
return None
def translate(file, data, lang="de"):
json = findLang(file)
if None != json:
matches = re.findall(r'\{\#([A-Z0-9_]+)\}', data)
for x in matches:
for e in json["list"]:
if x == e["token"]:
#print("replace " + "{#" + x + "}" + " with " + e[lang])
data = data.replace("{#" + x + "}", e[lang])
return data
def convert2Header(inFile, version, lang):
fileType = inFile.split(".")[1]
define = inFile.split(".")[0].upper()
define2 = inFile.split(".")[1].upper()
@ -114,7 +140,7 @@ def convert2Header(inFile, version):
f.close()
else:
if fileType == "html":
data = htmlParts(inFile, "includes/header.html", "includes/nav.html", "includes/footer.html", version)
data = htmlParts(inFile, "includes/header.html", "includes/nav.html", "includes/footer.html", version, lang)
else:
f = open(inFile, "r")
data = f.read()
@ -169,6 +195,11 @@ Path("tmp").mkdir(exist_ok=True) # created to check if webpages are valid with a
shutil.copyfile("style.css", "tmp/style.css")
version = readVersion("../../defines.h")
# get language from environment
lang = "en"
if env['PIOENV'][-3:] == "-de":
lang = "de"
# go throw the array
for val in files_grabbed:
convert2Header(val, version)
convert2Header(val, version, lang)

54
scripts/getVersion.py

@ -59,6 +59,7 @@ def readVersion(path, infile):
os.mkdir(path + "firmware/ESP32-S3-ETH/")
sha = os.getenv("SHA",default="sha")
## ENGLISH VERSIONS
versionout = version[:-1] + "_" + sha + "_esp8266.bin"
src = path + ".pio/build/esp8266/firmware.bin"
dst = path + "firmware/ESP8266/" + versionout
@ -110,6 +111,59 @@ def readVersion(path, infile):
dst = path + "firmware/ESP32-S3-ETH/" + versionout
os.rename(src, dst)
## GERMAN VERSIONS
versionout = version[:-1] + "_" + sha + "_esp8266-de.bin"
src = path + ".pio/build/esp8266-de/firmware.bin"
dst = path + "firmware/ESP8266/" + versionout
os.rename(src, dst)
versionout = version[:-1] + "_" + sha + "_esp8266_prometheus-de.bin"
src = path + ".pio/build/esp8266-prometheus-de/firmware.bin"
dst = path + "firmware/ESP8266/" + versionout
os.rename(src, dst)
versionout = version[:-1] + "_" + sha + "_esp8285-de.bin"
src = path + ".pio/build/esp8285-de/firmware.bin"
dst = path + "firmware/ESP8285/" + versionout
os.rename(src, dst)
gzip_bin(dst, dst + ".gz")
versionout = version[:-1] + "_" + sha + "_esp32-de.bin"
src = path + ".pio/build/esp32-wroom32-de/firmware.bin"
dst = path + "firmware/ESP32/" + versionout
os.rename(src, dst)
versionout = version[:-1] + "_" + sha + "_esp32_prometheus-de.bin"
src = path + ".pio/build/esp32-wroom32-prometheus-de/firmware.bin"
dst = path + "firmware/ESP32/" + versionout
os.rename(src, dst)
versionout = version[:-1] + "_" + sha + "_esp32_ethernet-de.bin"
src = path + ".pio/build/esp32-wroom32-ethernet-de/firmware.bin"
dst = path + "firmware/ESP32/" + versionout
os.rename(src, dst)
versionout = version[:-1] + "_" + sha + "_esp32s2-mini-de.bin"
src = path + ".pio/build/esp32-s2-mini-de/firmware.bin"
dst = path + "firmware/ESP32-S2/" + versionout
os.rename(src, dst)
versionout = version[:-1] + "_" + sha + "_esp32c3-mini-de.bin"
src = path + ".pio/build/esp32-c3-mini-de/firmware.bin"
dst = path + "firmware/ESP32-C3/" + versionout
os.rename(src, dst)
versionout = version[:-1] + "_" + sha + "_esp32s3-de.bin"
src = path + ".pio/build/opendtufusion-de/firmware.bin"
dst = path + "firmware/ESP32-S3/" + versionout
os.rename(src, dst)
versionout = version[:-1] + "_" + sha + "_esp32s3_ethernet-de.bin"
src = path + ".pio/build/opendtufusion-ethernet-de/firmware.bin"
dst = path + "firmware/ESP32-S3-ETH/" + versionout
os.rename(src, dst)
## BOOTLOADER AND PARTITIONS
# other ESP32 bin files
src = path + ".pio/build/esp32-wroom32/"
dst = path + "firmware/ESP32/"

1
src/CHANGES.md

@ -6,6 +6,7 @@
* updated espressif32 platform to `6.5.0`
* updated U8g2 to `2.35.9`
* started to convert deprecated functions of new ArduinoJson `7.0.0`
* started to have german translations of all variants (environments) #925 #1199
## 0.8.46 - 2024-01-06
* improved communication

4
src/config/config.h

@ -1,6 +1,6 @@
//-----------------------------------------------------------------------------
// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
// 2024 Ahoy, https://www.mikrocontroller.net/topic/525778
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#ifndef __CONFIG_H__

144
src/platformio.ini

@ -50,6 +50,16 @@ build_flags = ${env.build_flags}
monitor_filters =
esp8266_exception_decoder
[env:esp8266-de]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
-DLANG_DE
;-Wl,-Map,output.map
monitor_filters =
esp8266_exception_decoder
[env:esp8266-prometheus]
platform = espressif8266
@ -61,6 +71,17 @@ build_flags = ${env.build_flags}
monitor_filters =
esp8266_exception_decoder
[env:esp8266-prometheus-de]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DENABLE_PROMETHEUS_EP
-DEMC_MIN_FREE_MEMORY=4096
-DLANG_DE
monitor_filters =
esp8266_exception_decoder
[env:esp8285]
platform = espressif8266
board = esp8285
@ -71,6 +92,17 @@ build_flags = ${env.build_flags}
monitor_filters =
esp8266_exception_decoder
[env:esp8285-de]
platform = espressif8266
board = esp8285
board_build.ldscript = eagle.flash.1m64.ld
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
-DLANG_DE
monitor_filters =
esp8266_exception_decoder
[env:esp32-wroom32]
platform = espressif32@6.5.0
board = lolin_d32
@ -79,6 +111,15 @@ build_flags = ${env.build_flags}
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-de]
platform = espressif32@6.5.0
board = lolin_d32
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
-DLANG_DE
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-prometheus]
platform = espressif32@6.5.0
board = lolin_d32
@ -88,6 +129,16 @@ build_flags = ${env.build_flags}
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-prometheus-de]
platform = espressif32@6.5.0
board = lolin_d32
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
-DENABLE_PROMETHEUS_EP
-DLANG_DE
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-ethernet]
platform = espressif32
board = esp32dev
@ -110,6 +161,29 @@ build_flags = ${env.build_flags}
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-ethernet-de]
platform = espressif32
board = esp32dev
lib_deps =
khoih-prog/AsyncWebServer_ESP32_W5500
khoih-prog/AsyncUDP_ESP32_W5500
nrf24/RF24 @ ^1.4.8
paulstoffregen/Time @ ^1.6.1
https://github.com/bertmelis/espMqttClient#v1.5.0
bblanchon/ArduinoJson @ ^6.21.3
https://github.com/JChristensen/Timezone @ ^1.2.4
olikraus/U8g2 @ ^2.35.9
https://github.com/zinggjm/GxEPD2 @ ^1.5.3
build_flags = ${env.build_flags}
-D ETHERNET
-DRELEASE
-DUSE_HSPI_FOR_EPD
-DLANG_DE
-DLOG_LOCAL_LEVEL=ESP_LOG_INFO
-DDEBUG_LEVEL=DBG_INFO
monitor_filters =
esp32_exception_decoder
[env:esp32-s2-mini]
platform = espressif32@6.5.0
board = lolin_s2_mini
@ -124,6 +198,21 @@ build_flags = ${env.build_flags}
monitor_filters =
esp32_exception_decoder
[env:esp32-s2-mini-de]
platform = espressif32@6.5.0
board = lolin_s2_mini
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
-DDEF_NRF_CS_PIN=12
-DDEF_NRF_CE_PIN=3
-DDEF_NRF_IRQ_PIN=5
-DDEF_NRF_MISO_PIN=9
-DDEF_NRF_MOSI_PIN=11
-DDEF_NRF_SCLK_PIN=7
-DLANG_DE
monitor_filters =
esp32_exception_decoder
[env:esp32-c3-mini]
platform = espressif32@6.5.0
board = lolin_c3_mini
@ -138,6 +227,20 @@ build_flags = ${env.build_flags}
monitor_filters =
esp32_exception_decoder
[env:esp32-c3-mini-de]
platform = espressif32@6.5.0
board = lolin_c3_mini
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
-DDEF_NRF_CS_PIN=5
-DDEF_NRF_CE_PIN=0
-DDEF_NRF_IRQ_PIN=1
-DDEF_NRF_MISO_PIN=3
-DDEF_NRF_MOSI_PIN=4
-DDEF_NRF_SCLK_PIN=2
-DLANG_DE
monitor_filters =
esp32_exception_decoder
[env:opendtufusion]
platform = espressif32@6.5.0
@ -162,6 +265,30 @@ build_flags = ${env.build_flags}
monitor_filters =
esp32_exception_decoder, colorize
[env:opendtufusion-de]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
upload_protocol = esp-builtin
build_flags = ${env.build_flags}
-DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38
-DDEF_NRF_IRQ_PIN=47
-DDEF_NRF_MISO_PIN=48
-DDEF_NRF_MOSI_PIN=35
-DDEF_NRF_SCLK_PIN=36
-DDEF_CMT_CSB=4
-DDEF_CMT_FCSB=21
-DDEF_CMT_IRQ=8
-DDEF_CMT_SDIO=5
-DDEF_CMT_SCLK=6
-DDEF_LED0=18
-DDEF_LED1=17
-DLED_ACTIVE_HIGH
-DARDUINO_USB_MODE=1
-DLANG_DE
monitor_filters =
esp32_exception_decoder, colorize
[env:opendtufusion-ethernet]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
@ -204,11 +331,12 @@ build_flags = ${env.build_flags}
monitor_filters =
esp32_exception_decoder, colorize
[env:opendtufusion-dev]
[env:opendtufusion-ethernet-de]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer
khoih-prog/AsyncWebServer_ESP32_W5500
khoih-prog/AsyncUDP_ESP32_W5500
https://github.com/nrf24/RF24 @ ^1.4.8
paulstoffregen/Time @ ^1.6.1
https://github.com/bertmelis/espMqttClient#v1.5.0
@ -218,6 +346,14 @@ lib_deps =
https://github.com/zinggjm/GxEPD2 @ ^1.5.3
upload_protocol = esp-builtin
build_flags = ${env.build_flags}
-DETHERNET
-DSPI_HAL
-DDEF_ETH_CS_PIN=42
-DDEF_ETH_SCK_PIN=39
-DDEF_ETH_MISO_PIN=41
-DDEF_ETH_MOSI_PIN=40
-DDEF_ETH_IRQ_PIN=44
-DDEF_ETH_RST_PIN=43
-DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38
-DDEF_NRF_IRQ_PIN=47
@ -233,7 +369,7 @@ build_flags = ${env.build_flags}
-DDEF_LED1=17
-DLED_ACTIVE_HIGH
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
-DSPI_HAL
#-DARDUINO_USB_CDC_ON_BOOT=1
-DLANG_DE
monitor_filters =
esp32_exception_decoder, colorize

2
src/web/html/includes/header.html

@ -1,6 +1,6 @@
<link rel="stylesheet" type="text/css" href="style.css?v={#VERSION}"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<meta charset="utf-8">
<script type="text/javascript" src="api.js?v={#VERSION}"></script>
<link rel="stylesheet" type="text/css" href="colors.css?v={#VERSION}"/>
<meta name="robots" content="noindex, nofollow" />

10
src/web/html/includes/nav.html

@ -6,16 +6,16 @@
<span></span>
</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}">Webserial</a>
<a id="nav5" class="hide" href="/setup?v={#VERSION}">Settings</a>
<a id="nav3" class="hide" href="/live?v={#VERSION}">{#NAV_LIVE}</a>
<a id="nav4" class="hide" href="/serial?v={#VERSION}">{#NAV_WEBSERIAL}</a>
<a id="nav5" class="hide" href="/setup?v={#VERSION}">{#NAV_SETTINGS}</a>
<span class="seperator"></span>
<a id="nav6" class="hide" href="/update?v={#VERSION}">Update</a>
<a id="nav7" class="hide" href="/system?v={#VERSION}">System</a>
<span class="seperator"></span>
<a id="nav8" href="/api" target="_blank">REST API</a>
<a id="nav9" href="https://ahoydtu.de" target="_blank">Documentation</a>
<a id="nav10" href="/about?v={#VERSION}">About</a>
<a id="nav9" href="https://ahoydtu.de" target="_blank">{#NAV_DOCUMENTATION}</a>
<a id="nav10" href="/about?v={#VERSION}">{#NAV_ABOUT}</a>
<span class="seperator"></span>
<a id="nav0" class="hide" href="/login">Login</a>
<a id="nav1" class="hide" href="/logout">Logout</a>

53
src/web/html/index.html

@ -20,18 +20,15 @@
</p>
<div id="note">
<h3>Support this project:</h3>
<h3>{#SUPPORT}:</h3>
<ul>
<li><a href="https://github.com/lumapu/ahoy/blob/main/src/CHANGES.md" target="_blank">Changelog</a></li>
<li>Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a></li>
<li>Report <a href="https://github.com/lumapu/ahoy/issues" target="_blank">issues</a></li>
<li>Contribute to <a href="https://github.com/lumapu/ahoy/blob/main/User_Manual.md" target="_blank">documentation</a></li>
<li><a href="https://nightly.link/lumapu/ahoy/workflows/compile_development/development03/ahoydtu_dev.zip" target="_blank">Download</a> & Test development firmware, <a href="https://github.com/lumapu/ahoy/blob/development03/src/CHANGES.md" target="_blank">Development Changelog</a></li>
<li>make a <a href="https://paypal.me/lupusch" target="_blank">donation</a></li>
<li><a href="https://github.com/lumapu/ahoy/blob/main/src/CHANGES.md" target="_blank">{#CHANGELOG}</a></li>
<li>{#DISCUSS} <a href="https://discord.gg/WzhxEY62mB">Discord</a></li>
<li>{#REPORT} <a href="https://github.com/lumapu/ahoy/issues" target="_blank">{#ISSUES}</a></li>
<li>{#CONTRIBUTE} <a href="https://github.com/lumapu/ahoy/blob/main/User_Manual.md" target="_blank">{#DOCUMENTATION}</a></li>
<li><a href="https://nightly.link/lumapu/ahoy/workflows/compile_development/development03/ahoydtu_dev.zip" target="_blank">Download</a> & Test {#DEV_FIRMWARE}, <a href="https://github.com/lumapu/ahoy/blob/development03/src/CHANGES.md" target="_blank">{#DEV_CHANGELOG}</a></li>
<li>{#DON_MAKE} <a href="https://paypal.me/lupusch" target="_blank">{#DONATION}</a></li>
</ul>
<p class="lic">
This project was started from <a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion. (Mikrocontroller.net)</a>
</p>
</div>
</div>
</div>
@ -46,11 +43,11 @@
function apiCb(obj) {
var e = document.getElementById("apiResult");
if(obj.success) {
e.innerHTML = " command executed";
e.innerHTML = " {#COMMAND_EXE}";
getAjax("/api/index", parse);
}
else
e.innerHTML = " Error: " + obj.error;
e.innerHTML = " {#ERROR}: " + obj.error;
}
function setTime() {
@ -92,7 +89,7 @@
else {
dSpan.innerHTML = "";
var e = inp("set", "sync from browser", 0, ["btn"], "set", "button");
dSpan.appendChild(span("NTP timeserver unreachable. "));
dSpan.appendChild(span("{#NTP_UNREACH}. "));
dSpan.appendChild(e);
dSpan.appendChild(span("", ["span"], "apiResult"));
e.addEventListener("click", setTime);
@ -101,15 +98,15 @@
if(obj.disNightComm) {
if(((obj.ts_sunrise + obj.ts_offsSr) < obj.ts_now)
&& ((obj.ts_sunset + obj.ts_offsSs) > obj.ts_now)) {
commInfo = "Polling inverter(s), will pause at sunset " + (new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE'));
commInfo = "{#POLLING_STOP} " + (new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE'));
}
else {
commInfo = "Night time, inverter polling disabled, ";
commInfo = "{#NIGHT_TIME}, ";
if(obj.ts_now > (obj.ts_sunrise + obj.ts_offsSr)) {
commInfo += "paused at " + (new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE'));
commInfo += "{#PAUSED_AT} " + (new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE'));
}
else {
commInfo += "will start polling at " + (new Date((obj.ts_sunrise + obj.ts_offsSr) * 1000).toLocaleString('de-DE'));
commInfo += "{#START_AT} " + (new Date((obj.ts_sunrise + obj.ts_offsSr) * 1000).toLocaleString('de-DE'));
}
}
}
@ -124,33 +121,33 @@
if(false == i["enabled"]) {
icon = iconWarn;
cl = "icon-warn";
avail = "disabled";
avail = "{#DISABLED}";
} else if((false == i["is_avail"]) || (0 == ts)) {
icon = iconInfo;
cl = "icon-info";
avail = "not yet available";
avail = "{#NOT_YET_AVAIL}";
} else if(0 == i["ts_last_success"]) {
avail = "available but no data was received until now";
avail = "{#AVAIL_NO_DATA}";
} else {
avail = "available and is ";
avail = "{#AVAIL} ";
if(false == i["is_producing"])
avail += "not producing";
avail += "{#NOT_PRODUCING}";
else {
icon = iconSuccessFull;
avail += "producing " + i.cur_pwr + "W";
avail += "{#PRODUCING} " + i.cur_pwr + "W";
}
}
p.append(
svg(icon, 30, 30, "icon " + cl),
span("Inverter #" + i["id"] + ": " + i["name"] + " is " + avail),
span("{#INVERTER} #" + i["id"] + ": " + i["name"] + " {#IS} " + avail),
br()
);
if(false == i["is_avail"]) {
if(i["ts_last_success"] > 0) {
var date = new Date(i["ts_last_success"] * 1000);
p.append(span("-> last successful transmission: " + toIsoDateStr(date)), br());
p.append(span("-> {#LAST_SUCCESS}: " + toIsoDateStr(date)), br());
}
}
}
@ -168,11 +165,11 @@
if(null != release) {
if(getVerInt("{#VERSION}") < getVerInt(release))
p.append(svg(iconInfo, 30, 30, "icon icon-info"), span("Update available, current released version: " + release), br());
p.append(svg(iconInfo, 30, 30, "icon icon-info"), span("{#UPDATE_AVAIL}: " + release), br());
else if(getVerInt("{#VERSION}") > getVerInt(release))
p.append(svg(iconInfo, 30, 30, "icon icon-info"), span("You are using development version {#VERSION}. In case of issues you may want to try the current stable release: " + release), br());
p.append(svg(iconInfo, 30, 30, "icon icon-info"), span("{#USING_DEV_VERSION} {#VERSION}. {#DEV_ISSUE_RELEASE_VERSION}: " + release), br());
else
p.append(svg(iconInfo, 30, 30, "icon icon-info"), span("You are using the current stable release: " + release), br());
p.append(svg(iconInfo, 30, 30, "icon icon-info"), span("{#RELEASE_INSTALLED}: " + release), br());
}
document.getElementById("warn_info").replaceChildren(p);

8
src/web/html/save.html

@ -8,7 +8,7 @@
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<div id="html" class="mt-3 mb-3">Saving settings...</div>
<div id="html" class="mt-3 mb-3">{#SAVE_SETTINGS}</div>
</div>
</div>
{#HTML_FOOTER}
@ -31,15 +31,15 @@
var meta = document.createElement('meta');
meta.httpEquiv = "refresh"
if(!obj.reboot) {
html = "Settings successfully saved. Automatic page reload in 3 seconds.";
html = "{#SUCCESS_SAVED_RELOAD}";
meta.content = 3;
} else {
html = "Settings successfully saved. Rebooting. Automatic redirect in " + obj.reload + " seconds.";
html = "{#SUCCESS_SAVED_REBOOT} " + obj.reload + " {#SECONDS}.";
meta.content = obj.reload + "; URL=/";
}
document.getElementsByTagName('head')[0].appendChild(meta);
} else {
html = "Failed saving settings.";
html = "{#FAILED_SAVE}.";
}
}
document.getElementById("html").innerHTML = html;

288
src/web/html/setup.html

@ -1,7 +1,7 @@
<!doctype html>
<html lang="en">
<head>
<title>Setup</title>
<title>{#SETTINGS}</title>
{#HTML_HEADER}
</head>
<body>
@ -10,77 +10,74 @@
<div id="content">
<form method="post" action="/save" id="settings">
<fieldset>
<button type="button" class="s_collapsible mt-4">System Config</button>
<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>
<legend class="des">{#DEVICE_NAME}</legend>
<div class="row mb-3">
<div class="col-12 col-sm-3">Device Name</div>
<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-8 col-sm-3">{#REBOOT_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-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 class="col-12">{#DARK_MODE_NOTE}</div>
</div>
</fieldset>
<fieldset class="mb-4">
<legend class="des">System Config</legend>
<legend class="des">{#PINOUT_CONFIGURATION}</legend>
<p class="des">Status LEDs</p>
<div id="pinout"></div>
<p class="des">Radio (NRF24L01+)</p>
<p class="des">{#RADIO} (NRF24L01+)</p>
<div id="rf24"></div>
<!--IF_ESP32-->
<p class="des">Radio (CMT2300A)</p>
<div id="cmt"><div class="col-12">(ESP32 only)</div></div>
<p class="des">{#RADIO} (CMT2300A)</p>
<div id="cmt"></div>
<!--ENDIF_ESP32-->
<p class="des">Serial Console</p>
<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-8 col-sm-3">{#LOG_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-8 col-sm-3">{#LOG_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-8 col-sm-3">Privacy Mode</div>
<div class="col-8 col-sm-3">{#LOG_PRIVACY_MODE}</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="priv"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Print whole traces in Log</div>
<div class="col-8 col-sm-3">{#LOG_PRINT_TRACES}</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="wholeTrace"/></div>
</div>
</fieldset>
</div>
<button type="button" class="s_collapsible">Network</button>
<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-3 my-2">{#AP_PWD}</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 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="{#BTN_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-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>
<option value="-1" selected disabled hidden>{#NETWORK_NOT_SCANNED}</option>
</select>
</div>
</div>
@ -89,26 +86,25 @@
<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-3">{#SSID_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-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>
<legend class="des">{#STATIC_IP}</legend>
<p>
Leave fields blank for DHCP<br/>
The following fields are parsed in this format: 192.168.4.1
{#NETWORK_HINT_BLANK}
</p>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">IP Address</div>
<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-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">
@ -126,58 +122,58 @@
</fieldset>
</div>
<button type="button" class="s_collapsible">Protection</button>
<button type="button" class="s_collapsible">{#PROTECTION}</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des mx-2">Protection</legend>
<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-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>
<p>{#PROTECTION_NOTE}</p>
<div id="prot_mask"></div>
</fieldset>
</div>
<button type="button" class="s_collapsible">Inverter</button>
<button type="button" class="s_collapsible">{#INVERTER}</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des">Inverter</legend>
<legend class="des">{#INVERTER}</legend>
<div id="inverter"></div>
<div class="row mb-3">
<div class="col-8 my-2">Interval [s]</div>
<div class="col-8 my-2">{#INTERVAL}</div>
<div class="col-4"><input type="number" name="invInterval" title="Invalid input"/></div>
</div>
<div class="row mb-3">
<div class="col-8 my-2">Inverter Gap [ms]</div>
<div class="col-8 my-2">{#INV_GAP}</div>
<div class="col-4"><input type="number" name="invGap" title="Invalid input"/></div>
</div>
<div class="row mb-3">
<div class="col-8 mb-2">Reset values and YieldDay at midnight</div>
<div class="col-8 mb-2">{#INV_RESET_MIDNIGHT}</div>
<div class="col-4"><input type="checkbox" name="invRstMid"/></div>
</div>
<div class="row mb-3">
<div class="col-8 mb-2">Reset values when inverter polling pauses at sunset</div>
<div class="col-8 mb-2">{#INV_PAUSE_SUNSET}</div>
<div class="col-4"><input type="checkbox" name="invRstComStop"/></div>
</div>
<div class="row mb-3">
<div class="col-8">Reset values when inverter status is 'not available'</div>
<div class="col-8">{#INV_RESET_NOT_AVAIL}</div>
<div class="col-4"><input type="checkbox" name="invRstNotAvail"/></div>
</div>
<div class="row mb-3">
<div class="col-8">Reset 'max' values at midnight</div>
<div class="col-8">{#INV_RESET_MAX_MIDNIGHT}</div>
<div class="col-4"><input type="checkbox" name="invRstMaxMid"/></div>
</div>
<div class="row mb-3">
<div class="col-8">Start without time sync (useful in AP-Only-Mode)</div>
<div class="col-8">{#INV_START_WITHOUT_TIME}</div>
<div class="col-4"><input type="checkbox" name="strtWthtTm"/></div>
</div>
<div class="row mb-3">
<div class="col-8">Read Grid Profile</div>
<div class="col-8">{#INV_READ_GRID_PROFILE}</div>
<div class="col-4"><input type="checkbox" name="rdGrid"/></div>
</div>
<div class="row mb-3">
<div class="col-8">Yield Efficiency (Standard 1.0)</div>
<div class="col-8">{#INV_YIELD_EFF}</div>
<div class="col-4"><input type="number" name="yldEff" step="any"/></div>
</div>
</fieldset>
@ -196,42 +192,42 @@
<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-3 my-2">{#NTP_INTERVAL}</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-3 my-2">{#NTP_SET_SYS_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/>
<input type="button" name="ntpBtn" id="ntpBtn" class="btn" value="{#BTN_FROM_BROWSER}" onclick="setTime()"/>
<input type="button" name="ntpSync" id="ntpSync" class="btn" value="{#BTN_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-3 my-2">{#NTP_SYS_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>
<button type="button" class="s_collapsible">{#SUNRISE_SUNSET}</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des">Sunrise & Sunset</legend>
<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-3 my-2">{#LATITUDE}</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-3 my-2">{#LONGITUDE}</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 (sunrise)</div>
<div class="col-12 col-sm-3 my-2">{#OFFSET_SUNRISE}</div>
<div class="col-12 col-sm-9"><select name="sunOffsSr"></select></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Offset (sunset)</div>
<div class="col-12 col-sm-3 my-2">{#OFFSET_SUNSET}</div>
<div class="col-12 col-sm-9"><select name="sunOffsSs"></select></div>
</div>
</fieldset>
@ -254,20 +250,20 @@
<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-3 my-2">{#MQTT_USER}</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-3 my-2">{MQTT_PASSWORD}</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>
<p class="des">{#MQTT_NOTE}</p>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Interval [s]</div>
<div class="col-12 col-sm-3 my-2">{#INTERVAL}</div>
<div class="col-12 col-sm-9"><input type="number" name="mqttInterval" title="Invalid input" /></div>
</div>
<div class="row mb-3">
@ -280,29 +276,29 @@
</fieldset>
</div>
<button type="button" class="s_collapsible">Display Config</button>
<button type="button" class="s_collapsible">{#DISPLAY_CONFIG}</button>
<div class="s_content">
<fieldset class="mb-4">
<legend class="des">Display Config</legend>
<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-8 col-sm-3">{#DISP_OFF_INV}</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="disp_pwr"/></div>
</div>
<div id="screenSaver"></div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Luminance</div>
<div class="col-12 col-sm-3 my-2">{#DISP_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>
<p class="des">{#DISP_PINOUT}</p>
<div id="dispPins"></div>
<div id="pirPin"></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-8 col-sm-3">{#BTN_REBOOT_SUCCESSFUL_SAVE}</div>
<div class="col-4 col-sm-9">
<input type="checkbox" name="reboot" checked />
<input type="submit" value="save" class="btn right"/>
@ -311,11 +307,11 @@
</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>
<a class="btn" href="/erase">{#BTN_ERASE}</a>
<fieldset class="mb-4">
<legend class="des">Import / Export JSON Settings</legend>
<legend class="des">{#IM_EXPORT}</legend>
<div class="row mb-4 mt-4">
<div class="col-12 col-sm-3">Import</div>
<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">
@ -326,9 +322,9 @@
</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-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>
<a class="btn" href="/get_setup" target="_blank">{#BTN_EXPORT}</a><span> {#EXPORT_NOTE}</span>
</div>
</div>
</fieldset>
@ -341,7 +337,7 @@
var ts = 0;
var esp8266pins = [
[255, "off / default"],
[255, "{#PIN_OFF}"],
[0, "D3 (GPIO0)"],
[1, "TX (GPIO1)"],
[2, "D4 (GPIO2)"],
@ -358,12 +354,12 @@
[13, "D7 (GPIO13)"],
[14, "D5 (GPIO14)"],
[15, "D8 (GPIO15)"],
[16, "D0 (GPIO16 - no IRQ!)"]
[16, "D0 (GPIO16 - {#PIN_NO_IRQ})"]
];
/*IF_ESP32*/
var esp32pins = [
[255, "off / default"],
[255, "{#PIN_OFF}"],
[0, "GPIO0"],
[1, "TX (GPIO1)"],
[2, "GPIO2 (LED)"],
@ -386,14 +382,14 @@
[27, "GPIO27"],
[32, "GPIO32"],
[33, "GPIO33"],
[34, "GPIO34 (in only)"],
[35, "GPIO35 (in only)"],
[36, "VP (GPIO36, in only)"],
[39, "VN (GPIO39, in only)"]
[34, "GPIO34 ({#PIN_INPUT_ONLY})"],
[35, "GPIO35 ({#PIN_INPUT_ONLY})"],
[36, "VP (GPIO36, {#PIN_INPUT_ONLY})"],
[39, "VN (GPIO39, {#PIN_INPUT_ONLY})"]
];
var esp32s3pins = [
[255, "off / default"],
[0, "GPIO0 (DONT USE - BOOT)"],
[0, "GPIO0 ({#PIN_DONT_USE} - BOOT)"],
[1, "GPIO1"],
[2, "GPIO2"],
[3, "GPIO3"],
@ -412,16 +408,16 @@
[16, "GPIO16"],
[17, "GPIO17"],
[18, "GPIO18"],
[19, "GPIO19 (DONT USE - USB-)"],
[20, "GPIO20 (DONT USE - USB+)"],
[19, "GPIO19 ({#PIN_DONT_USE} - USB-)"],
[20, "GPIO20 ({#PIN_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)"],
[26, "GPIO26 (PSRAM - {#PIN_NOT_AVAIL})"],
[27, "GPIO27 (FLASH - {#PIN_NOT_AVAIL})"],
[28, "GPIO28 (FLASH - {#PIN_NOT_AVAIL})"],
[29, "GPIO29 (FLASH - {#PIN_NOT_AVAIL})"],
[30, "GPIO30 (FLASH - {#PIN_NOT_AVAIL})"],
[31, "GPIO31 (FLASH - {#PIN_NOT_AVAIL})"],
[32, "GPIO32 (FLASH - {#PIN_NOT_AVAIL})"],
[33, "GPIO33 (not exposed on WROOM modules)"],
[34, "GPIO34 (not exposed on WROOM modules)"],
[35, "GPIO35"],
@ -434,8 +430,8 @@
[42, "GPIO42"],
[43, "GPIO43"],
[44, "GPIO44"],
[45, "GPIO45 (DONT USE - STRAPPING PIN)"],
[46, "GPIO46 (DONT USE - STRAPPING PIN)"],
[45, "GPIO45 ({#PIN_DONT_USE} - STRAPPING PIN)"],
[46, "GPIO46 ({#PIN_DONT_USE} - STRAPPING PIN)"],
[47, "GPIO47"],
[48, "GPIO48"],
];
@ -459,17 +455,17 @@
[15, "GPIO15 (PSRAM/FLASH)"],
[16, "GPIO16 (PSRAM/FLASH)"],
[17, "GPIO17 (PSRAM/FLASH)"],
[18, "GPIO18 (DONT USE - USB-)"],
[19, "GPIO19 (DONT USE - USB+)"],
[18, "GPIO18 ({#PIN_DONT_USE} - USB-)"],
[19, "GPIO19 ({#PIN_DONT_USE} - USB+)"],
[20, "GPIO20 (RX)"],
[21, "GPIO21 (TX)"],
];
/*ENDIF_ESP32*/
var nrfPa = [
[0, "MIN (recommended)"],
[0, "MIN ({#PIN_RECOMMENDED})"],
[1, "LOW"],
[2, "HIGH"],
[3, "MAX (experimental)"]
[3, "MAX ({#PIN_EXPERIMENTAL})"]
];
var esp32cmtPa = [];
var esp32cmtFreq = [];
@ -487,8 +483,8 @@
}
/*ENDIF_ESP32*/
var led_high_active = [
[0, "low active"],
[1, "high active"],
[0, "{#PIN_LOW_ACTIVE}"],
[1, "{#PIN_HIGH_ACTIVE}"],
];
window.onload = function() {
@ -513,31 +509,31 @@
var e = document.getElementById("networks");
selDelAllOpt(e);
if(obj["success"])
e.appendChild(opt("-1", "scanning ..."))
e.appendChild(opt("-1", "{#NETWORK_SCANNING}"))
else
e.appendChild(opt("-1", "Error: " + obj["error"]));
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 ...";
e.innerHTML = "{#NTP_COMMAND_EXE}";
else
e.innerHTML = "Error: " + obj["error"];
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";
e.innerHTML = "{#NTP_SYNCED_AT}: " + toIsoDateStr(date) + ", {#NTP_DIFF}: " + (ts - obj["ts_now"]) + "ms";
}
function apiCbMqtt(obj) {
var e = document.getElementById("apiResultMqtt");
if(obj["success"])
e.innerHTML = "command executed";
e.innerHTML = "{#MQTT_EXE}";
else
e.innerHTML = "Error: " + obj["error"];
e.innerHTML = "{#ERROR} " + obj["error"];
}
function setTime() {
@ -572,7 +568,7 @@
function hide() {
document.getElementById("form").submit();
var e = document.getElementById("content");
e.replaceChildren(span("upload started"));
e.replaceChildren(span("{#IMPORT_UPLOAD_STARTED}"));
}
function delIv() {
@ -626,7 +622,7 @@
if(!obj["pwd_set"])
e.value = "";
var d = document.getElementById("prot_mask");
var a = ["Index", "Live", "Webserial", "Settings", "Update", "System"];
var a = ["Index", "{#NAV_LIVE}", "{#NAV_WEBSERIAL}", "{#NAV_SETTINGS}", "Update", "System"];
var el = [];
for(var i = 0; i < 6; i++) {
var chk = ((obj["prot_mask"] & (1 << i)) == (1 << i));
@ -657,13 +653,13 @@
ml("th", {style: "width: 10%; text-align: center;"}, ""),
ml("th", {}, "Name"),
ml("th", {}, "Serial"),
ml("th", {style: "width: 10%; text-align: center;"}, "Edit"),
ml("th", {style: "width: 10%; text-align: center;"}, "Delete")
ml("th", {style: "width: 10%; text-align: center;"}, "{#INV_EDIT}"),
ml("th", {style: "width: 10%; text-align: center;"}, "{#INV_DELETE}")
]));
for(let i = 0; i < obj.inverter.length; i++) {
lines.push(ml("tr", {}, [
ml("td", {}, badge(obj.inverter[i].enabled, (obj.inverter[i].enabled) ? "enabled" : "disabled")),
ml("td", {}, badge(obj.inverter[i].enabled, (obj.inverter[i].enabled) ? "{#ENABLED}" : "{#DISABLED}")),
ml("td", {}, obj.inverter[i].name),
ml("td", {}, String(obj.inverter[i].serial)),
ml("td", {style: "text-align: center;", onclick: function() {ivModal(obj.inverter[i]);}}, svg(iconGear, 25, 25, "icon icon-fg pointer")),
@ -686,7 +682,7 @@
e.innerHTML = ""; // remove all childs
e.append(ml("table", {class: "table"}, ml("tbody", {}, lines)));
if(obj.max_num_inverters > obj.inverter.length)
e.append(ml("div", {class: "row my-3"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", value: "add Inverter", class: "btn", onclick: function() { ivModal(add); }}, null))));
e.append(ml("div", {class: "row my-3"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", value: "{#BTN_INV_ADD}", class: "btn", onclick: function() { ivModal(add); }}, null))));
ivGlob(obj);
}
@ -694,10 +690,10 @@
function ivModal(obj) {
var lines = [];
lines.push(ml("tr", {}, [
ml("th", {style: "width: 10%;"}, "Input"),
ml("th", {}, "Max Module Power [Wp]"),
ml("th", {style: "width: 10%;"}, "{#INV_INPUT}"),
ml("th", {}, "{#INV_MAX_MODULE_POWER} [Wp]"),
ml("th", {}, "Name (optional)"),
ml("th", {}, "Yield Correction [kWh] (optional)")
ml("th", {}, "{#INV_YIELD_CORR} [kWh] (optional)")
]));
for(let i = 0; i < 6; i++) {
@ -718,14 +714,14 @@
var ser = ml("input", {name: "ser", class: "text", type: "number", max: 138999999999, value: obj.serial}, null);
var html = ml("div", {}, [
tabs(["General", "Inputs", "Radio", "Advanced"]),
ml("div", {id: "divGeneral", class: "tab-content"}, [
tabs(["{#TAB_GENERAL}", "{#TAB_INPUTS}", "{#TAB_RADIO}", "{#TAB_ADVANCED}"]),
ml("div", {id: "div{#TAB_GENERAL}", class: "tab-content"}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-2"}, "Enable"),
ml("div", {class: "col-2"}, "{#INV_ENABLE}"),
ml("div", {class: "col-10"}, cbEn)
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-2 mt-2"}, "Serial"),
ml("div", {class: "col-2 mt-2"}, "{#INV_SERIAL}"),
ml("div", {class: "col-10"}, ser)
]),
ml("div", {class: "row mb-3"}, [
@ -733,45 +729,45 @@
ml("div", {class: "col-10"}, ml("input", {name: "name", class: "text", type: "text", value: obj.name}, null))
])
]),
ml("div", {id: "divInputs", class: "tab-content hide"}, [
ml("div", {id: "div{#TAB_INPUTS}", class: "tab-content hide"}, [
ml("div", {class: "row mb-3"},
ml("table", {class: "table"},
ml("tbody", {}, lines)
)
)
]),
ml("div", {id: "divRadio", class: "tab-content hide"}, [
ml("div", {id: "div{#TAB_RADIO}", class: "tab-content hide"}, [
ml("input", {type: "hidden", name: "isnrf"}, null),
ml("div", {id: "setcmt"}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-3 mt-2"}, "Frequency"),
ml("div", {class: "col-3 mt-2"}, "{#INV_FREQUENCY}"),
ml("div", {class: "col-9"}, sel("freq", esp32cmtFreq, obj.freq))
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-3 mt-2"}, "Power Level"),
ml("div", {class: "col-3 mt-2"}, "{#INV_POWER_LEVEL}"),
ml("div", {class: "col-9"}, sel("cmtpa", esp32cmtPa, obj.pa))
]),
]),
ml("div", {id: "setnrf"},
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-3 mt-2"}, "Power Level"),
ml("div", {class: "col-3 mt-2"}, "{#INV_POWER_LEVEL}"),
ml("div", {class: "col-9"}, sel("nrfpa", nrfPa, obj.pa))
]),
),
]),
ml("div", {id: "divAdvanced", class: "tab-content hide"}, [
ml("div", {id: "div{#TAB_ADVANCED}", class: "tab-content hide"}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-10"}, "Pause communication during night (lat. and lon. need to be set)"),
ml("div", {class: "col-10"}, "{#INV_PAUSE_DURING_NIGHT}"),
ml("div", {class: "col-2"}, cbDisNightCom)
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-10"}, "Include inverter to sum of total (should be checked by default, MqTT only)"),
ml("div", {class: "col-10"}, "{#INV_INCLUDE_MQTT_SUM}"),
ml("div", {class: "col-2"}, cbAddTotal)
])
]),
ml("div", {class: "row mt-5"}, [
ml("div", {class: "col-8", id: "res"}, ""),
ml("div", {class: "col-4 a-r"}, ml("input", {type: "button", value: "save", class: "btn", onclick: function() { ivSave(); }}, null))
ml("div", {class: "col-4 a-r"}, ml("input", {type: "button", value: "{#BTN_SAVE}", class: "btn", onclick: function() { ivSave(); }}, null))
])
]);
@ -808,7 +804,7 @@
})
});
modal("Edit inverter " + obj.name, html);
modal("{#INV_EDIT_MODAL}: " + obj.name, html);
ser.dispatchEvent(new Event('change'));
function ivSave() {
@ -839,7 +835,7 @@
function cb(obj2) {
var e = document.getElementById("res");
if(!obj2.success)
e.innerHTML = "error: " + obj2.error;
e.innerHTML = "{#ERROR}" + obj2.error;
else {
modalClose();
getAjax("/api/inverter/list", parseIv);
@ -849,10 +845,10 @@
function ivDel(obj) {
var html = ml("div", {class: "row"}, [
ml("div", {class: "col-9"}, "do you realy want to delete inverter " + obj.name + "?"),
ml("div", {class: "col-3 a-r"}, ml("div", {class: "col-4 a-r"}, ml("input", {type: "button", value: "yes", class: "btn", onclick: function() { del(); }}, null)))
ml("div", {class: "col-9"}, "{#INV_DELETE_SURE} (" + obj.name + ")"),
ml("div", {class: "col-3 a-r"}, ml("div", {class: "col-4 a-r"}, ml("input", {type: "button", value: "{#BTN_YES}", class: "btn", onclick: function() { del(); }}, null)))
]);
modal("Delete inverter " + obj.name, html);
modal("{#INV_DELETE_MODAL}: " + obj.name, html);
function del() {
var o = new Object();
@ -896,7 +892,7 @@
for(p of [["sunOffsSr", "offsSr"], ["sunOffsSs", "offsSs"]]) {
const sel = document.getElementsByName(p[0])[0];
for(var i = -60; i <= 60; i++) {
sel.appendChild(opt(i, i + " minutes", (i == (obj[p[1]] / 60))));
sel.appendChild(opt(i, i + " {#NTP_MINUTES}", (i == (obj[p[1]] / 60))));
}
}
}
@ -909,7 +905,7 @@
if ("ESP32-S3" == system.chip_model) pinList = esp32s3pins;
else if("ESP32-C3" == system["chip_model"]) pinList = esp32c3pins;
/*ENDIF_ESP32*/
pins = [['led0', 'pinLed0', 'At least one inverter is producing'], ['led1', 'pinLed1', 'MqTT connected'], ['led2', 'pinLed2', 'Night time']];
pins = [['led0', 'pinLed0', '{#LED_AT_LEAST_ONE_PRODUCING}'], ['led1', 'pinLed1', '{#LED_MQTT_CONNECTED}'], ['led2', 'pinLed2', '{#LED_NIGHT_TIME}']];
for(p of pins) {
e.append(
ml("div", {class: "row mb-3"}, [
@ -922,13 +918,13 @@
}
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-3 my-2" }, "{#LED_POLARITY}"),
ml("div", { class: "col-12 col-sm-9" },
sel('pinLedHighActive', led_high_active, obj.led_high_active)
)
]),
ml("div", { class: "row mb-3" }, [
ml("div", { class: "col-12 col-sm-3 my-2" }, "LED luminance (0-255)"),
ml("div", { class: "col-12 col-sm-3 my-2" }, "{#LED_LUMINANCE} (0-255)"),
ml("div", { class: "col-12 col-sm-9" }, ml("input", {class: "text", type: "number", name: "pinLedLum", value: obj.led_lum, min: 0, max: 255}, null))
])
)
@ -948,7 +944,7 @@
e.replaceChildren (
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8 col-sm-3 my-2"}, "NRF24 Enable"),
ml("div", {class: "col-8 col-sm-3 my-2"}, "{#NRF24_ENABLE}"),
ml("div", {class: "col-4 col-sm-9"}, en)
])
);
@ -982,7 +978,7 @@
e.replaceChildren (
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8 col-sm-3 my-2"}, "CMT2300A Enable"),
ml("div", {class: "col-8 col-sm-3 my-2"}, "{#CMT_ENABLE}"),
ml("div", {class: "col-4 col-sm-9"}, en)
])
);
@ -1033,7 +1029,7 @@
);
}
// keep display types grouped
var opts = [[0, "None"],
var opts = [[0, "{#DISP_NONE}"],
[2, "SH1106 128x64 (1.3\")"],
[5, "SSD1306 64x48 (0.66\" Wemos OLED Shield)"],
[4, "SSD1306 128x32 (0.91\")"],
@ -1046,7 +1042,7 @@
var dtype_sel = 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-3 my-2"}, "{#DISP_TYPE}"),
ml("div", {class: "col-12 col-sm-9"}, dtype_sel)
])
);
@ -1057,22 +1053,22 @@
/*ENDIF_ESP32*/
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-3 my-2"}, "{#DISP_ROTATION}"),
ml("div", {class: "col-12 col-sm-9"}, sel("disp_rot", opts, obj["disp_rot"]))
])
);
var opts1 = [[0, "off"], [1, "pixel shift"], [2, "motion sensor"]];
var opts1 = [[0, "{#DISP_SCREENSAVER_OFF}"], [1, "{#DISP_PIXEL_SHIFT}"], [2, "{#DISP_MOTION_SENS}"]];
var screensaver_sel = sel("disp_screensaver", opts1, obj["disp_screensaver"]);
screensaver_sel.id = 'disp_screensaver';
document.getElementById("screenSaver").append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, "Screensaver (OLED only)"),
ml("div", {class: "col-12 col-sm-3 my-2"}, "{#DISP_SCREENSAVER}"),
ml("div", {class: "col-12 col-sm-9"}, screensaver_sel)
])
);
var esp8266pirpins = [[255, "off / default"],
var esp8266pirpins = [[255, "{#PIN_OFF}"],
[17, "A0"]];
document.getElementById("pirPin").append(
ml("div", {class: "row mb-3"}, [
@ -1155,12 +1151,12 @@
var s = document.getElementById("networks");
selDelAllOpt(s);
if(root["networks"].length > 0) {
s.appendChild(opt("-1", "please select network"));
s.appendChild(opt("-1", "{#NETWORK_PLEASE_SELECT}"));
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"));
s.appendChild(opt("-1", "{#NO_NETWORK_FOUND}"));
}
function selNet() {

36
src/web/html/system.html

@ -46,7 +46,7 @@
function irqBadge(state) {
switch(state) {
case 0: return badge(false, "unknown", "warning"); break;
case 0: return badge(false, "{#UNKNOWN}", "warning"); break;
case 1: return badge(true, "true"); break;
default: return badge(false, "false"); break;
}
@ -57,16 +57,16 @@
if(obj.radioNrf.en) {
lines = [
tr("NRF24L01", badge(obj.radioNrf.isconnected, ((obj.radioNrf.isconnected) ? "" : "not ") + "connected")),
tr("Interrupt Pin working", irqBadge(obj.radioNrf.irqOk)),
tr("NRF24 Data Rate", dr[obj.radioNrf.dataRate] + "bps"),
tr("NRF24L01", badge(obj.radioNrf.isconnected, ((obj.radioNrf.isconnected) ? "" : "{#NOT} ") + "{#CONNECTED}")),
tr("{#IRQ_WORKING}", irqBadge(obj.radioNrf.irqOk)),
tr("{#NRF24_DATA_RATE}", dr[obj.radioNrf.dataRate] + "bps"),
tr("DTU Radio ID", obj.radioNrf.sn)
];
} else
lines = [tr("NRF24L01", badge(false, "not enabled"))];
lines = [tr("NRF24L01", badge(false, "{#NOT_ENABLED}"))];
document.getElementById("info").append(
headline("Radio NRF"),
headline("{#NRF24_RADIO}"),
ml("table", {class: "table"},
ml("tbody", {}, lines)
)
@ -75,15 +75,15 @@
/*IF_ESP32*/
if(obj.radioCmt.en) {
cmt = [
tr("CMT2300A", badge(obj.radioCmt.isconnected, ((obj.radioCmt.isconnected) ? "" : "not ") + "connected")),
tr("Interrupt Pin working", irqBadge(obj.radioCmt.irqOk)),
tr("CMT2300A", badge(obj.radioCmt.isconnected, ((obj.radioCmt.isconnected) ? "" : "{#NOT} ") + "{#CONNECTED}")),
tr("{#IRQ_WORKING}", irqBadge(obj.radioCmt.irqOk)),
tr("DTU Radio ID", obj.radioCmt.sn)
];
} else
cmt = [tr("CMT2300A", badge(false, "not enabled"))];
cmt = [tr("CMT2300A", badge(false, "{#NOT_ENABLED}"))];
document.getElementById("info").append(
headline("Radio CMT"),
headline("{#CMT_RADIO}"),
ml("table", {class: "table"},
ml("tbody", {}, cmt)
)
@ -94,13 +94,13 @@
function parseMqtt(obj) {
if(obj.enabled) {
lines = [
tr("connected", badge(obj.connected, ((obj.connected) ? "true" : "false"))),
tr("{#CONNECTED}", badge(obj.connected, ((obj.connected) ? "{#TRUE}" : "{#FALSE}"))),
tr("#TX", obj.tx_cnt),
tr("#RX", obj.rx_cnt)
];
} else
lines = tr("enabled", badge(false, "false"));
lines = tr("{#ENABLED}", badge(false, "{#FALSE}"));
document.getElementById("info").append(
headline("MqTT"),
@ -113,14 +113,14 @@
function parseIndex(obj) {
if(obj.ts_sunrise > 0) {
document.getElementById("info").append(
headline("Sun"),
headline("{#SUN}"),
ml("table", {class: "table"},
ml("tbody", {}, [
tr("Sunrise", new Date(obj.ts_sunrise * 1000).toLocaleString('de-DE')),
tr("Sunset", new Date(obj.ts_sunset * 1000).toLocaleString('de-DE')),
tr("Communication start", new Date((obj.ts_sunrise + obj.ts_offsSr) * 1000).toLocaleString('de-DE')),
tr("Communication stop", new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE')),
tr("Night behaviour", badge(obj.disNightComm, ((obj.disNightComm) ? "not" : "") + " communicating", "warning"))
tr("{#SUNRISE}", new Date(obj.ts_sunrise * 1000).toLocaleString('de-DE')),
tr("{#SUNSET}", new Date(obj.ts_sunset * 1000).toLocaleString('de-DE')),
tr("{#COMMUNICATION_START}", new Date((obj.ts_sunrise + obj.ts_offsSr) * 1000).toLocaleString('de-DE')),
tr("{#COMMUNICATION_STTOP}", new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE')),
tr("{#NIGHT_BEHAVE}", badge(obj.disNightComm, ((obj.disNightComm) ? "{#NOT}" : "") + " {#COMMUNICATING}", "warning"))
])
)
);

8
src/web/html/update.html

@ -9,14 +9,14 @@
<div id="wrapper">
<div id="content">
<fieldset>
<legend class="des">Select firmware file (*.bin)</legend>
<legend class="des">{#SELECT_FILE} (*.bin)</legend>
<form id="form" method="POST" action="/update" enctype="multipart/form-data" accept-charset="utf-8">
<input type="file" name="update">
<input type="button" class="btn my-4" value="Update" onclick="hide()">
<input type="button" class="btn my-4" value="{#BTN_UPDATE}" onclick="hide()">
</form>
</fieldset>
<div class="row mt-4">
<a href="https://fw.ahoydtu.de" target="_blank">Download latest Release and Development versions<a/>
<a href="https://fw.ahoydtu.de" target="_blank">{#DOWNLOADS}<a/>
</div>
</div>
</div>
@ -31,7 +31,7 @@
function hide() {
document.getElementById("form").submit();
var e = document.getElementById("content");
e.replaceChildren(span("update started"));
e.replaceChildren(span("{#UPDATE_STARTED}"));
}
getAjax("/api/generic", parseGeneric);

136
src/web/html/visualization.html

@ -10,7 +10,7 @@
<div id="wrapper">
<div id="content">
<div id="live"></div>
<p>Every <span id="refresh"></span> seconds the values are updated</p>
<p>{#EVERY} <span id="refresh"></span> {#UPDATE_SECS}</p>
</div>
</div>
{#HTML_FOOTER}
@ -70,20 +70,20 @@
ml("div", {class: "col"}, [
ml("div", {class: "p-2 total-h"},
ml("div", {class: "row"},
ml("div", {class: "col mx-2 mx-md-1"}, "TOTAL")
ml("div", {class: "col mx-2 mx-md-1"}, "{#TOTAL}")
),
),
ml("div", {class: "p-2 total-bg"}, [
ml("div", {class: "row"}, [
numBig(total[0], "W", "AC Power"),
numBig(total[1], "Wh", "Yield Day"),
numBig(total[2], "kWh", "Yield Total")
numBig(total[0], "W", "{#AC_POWER}"),
numBig(total[1], "Wh", "{#YIELD_DAY}"),
numBig(total[2], "kWh", "{#YIELD_TOTAL}")
]),
ml("div", {class: "hr"}),
ml("div", {class: "row"}, [
numMid(total[3], "W", "Max Power"),
numMid(total[4], "W", "DC Power"),
numMid(total[5], "var", "Reactive Power")
numMid(total[3], "W", "{#MAX_POWER}"),
numMid(total[4], "W", "{#DC_POWER}"),
numMid(total[5], "var", "{#REACTIVE_POWER}")
])
])
])
@ -119,31 +119,31 @@
getAjax("/api/inverter/version/" + obj.id, parseIvVersion);
}}, obj.name)),
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("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))),
}}, ("{#ALARMS}: " + obj.alarm_cnt))),
ml("div", {class: "col a-r mx-2 mx-md-1"}, String(obj.ch[0][5].toFixed(1)) + t.innerText)
])
),
ml("div", {class: "p-2 " + clbg}, [
ml("div", {class: "row"},[
numBig(obj.ch[0][2], "W", "AC Power"),
numBig(obj.ch[0][7], "Wh", "Yield Day"),
numBig(obj.ch[0][6], "kWh", "Yield Total")
numBig(obj.ch[0][2], "W", "{#AC_POWER}"),
numBig(obj.ch[0][7], "Wh", "{#YIELD_DAY}"),
numBig(obj.ch[0][6], "kWh", "{#YIELD_TOTAL}")
]),
ml("div", {class: "hr"}),
ml("div", {class: "row mt-2"},[
numMid(obj.ch[0][11], "W", "Max AC Power", {class: "fs-6 tooltip", data: maxAcPwr}),
numMid(obj.ch[0][8], "W", "DC Power"),
numMid(obj.ch[0][0], "V", "AC Voltage"),
numMid(obj.ch[0][1], "A", "AC Current"),
numMid(obj.ch[0][3], "Hz", "Frequency"),
numMid(obj.ch[0][9], "%", "Efficiency"),
numMid(obj.ch[0][10], "var", "Reactive Power"),
numMid(obj.ch[0][4], "", "Power Factor")
numMid(obj.ch[0][11], "W", "{#MAX_AC_POWER}", {class: "fs-6 tooltip", data: maxAcPwr}),
numMid(obj.ch[0][8], "W", "{#DC_POWER}"),
numMid(obj.ch[0][0], "V", "{#DC_VOLTAGE}"),
numMid(obj.ch[0][1], "A", "{#AC_CURRENT}"),
numMid(obj.ch[0][3], "Hz", "{#FREQUENCY}"),
numMid(obj.ch[0][9], "%", "{#EFFICIENCY}"),
numMid(obj.ch[0][10], "var", "{#REACTIVE_POWER}"),
numMid(obj.ch[0][4], "", "{#POWER_FACTOR}")
])
])
])
@ -172,26 +172,26 @@
ml("div", {class: "p-2 a-c " + clh}, name),
ml("div", {class: "p-2 " + clbg}, [
ml("div", {class: "row"}, [
numCh(vals[2], units[2], "DC Power"),
numCh(vals[6], units[2], "Max Power"),
numCh(vals[5], units[5], "Irradiation"),
numCh(vals[3], units[3], "Yield Day"),
numCh(vals[4], units[4], "Yield Total"),
numCh(vals[0], units[0], "DC Voltage"),
numCh(vals[1], units[1], "DC Current")
numCh(vals[2], units[2], "{#DC_POWER}"),
numCh(vals[6], units[2], "{#MAX_POWER}"),
numCh(vals[5], units[5], "{#IRRADIATION}"),
numCh(vals[3], units[3], "{#YIELD_DAY}"),
numCh(vals[4], units[4], "{#YIELD_TOTAL}"),
numCh(vals[0], units[0], "{#DC_VOLTAGE}"),
numCh(vals[1], units[1], "{#DC_CURRENT}")
])
])
]);
}
function tsInfo(obj) {
var ageInfo = "Last received data requested at: ";
var ageInfo = "{#LAST_RECEIVED}: ";
if(obj.ts_last_success > 0) {
var date = new Date(obj.ts_last_success * 1000);
ageInfo += toIsoDateStr(date);
}
else
ageInfo += "nothing received";
ageInfo += "{#NOTHING_RECEIVED}";
if(obj.rssi > -127) {
if(obj.generation < 2)
@ -252,10 +252,10 @@
var offs = new Date().getTimezoneOffset() * -60;
html.push(
ml("div", {class: "row"}, [
ml("div", {class: "col"}, ml("strong", {}, "Event")),
ml("div", {class: "col"}, ml("strong", {}, "{#EVENT}")),
ml("div", {class: "col"}, ml("strong", {}, "ID")),
ml("div", {class: "col"}, ml("strong", {}, "Start")),
ml("div", {class: "col"}, ml("strong", {}, "End"))
ml("div", {class: "col"}, ml("strong", {}, "{#END}"))
])
);
@ -271,7 +271,7 @@
);
}
}
modal("Alarms of inverter " + obj.iv_name, ml("div", {}, html));
modal("{#ALARMS_MODAL}: " + obj.iv_name, ml("div", {}, html));
}
function parseIvVersion(obj) {
@ -283,7 +283,7 @@
case 3: model = "HMT-"; break;
default: model = "???-"; break;
}
model += String(obj.max_pwr) + " (Serial: " + obj.serial + ")";
model += String(obj.max_pwr) + " ({#SERIAL}: " + obj.serial + ")";
var html = ml("table", {class: "table"}, [
@ -291,15 +291,15 @@
tr("Model", model),
tr("Firmware Version / Build", String(obj.fw_ver) + " (build: " + String(obj.fw_date) + " " + String(obj.fw_time) + ")"),
tr("Hardware Version / Build", (obj.hw_ver/100).toFixed(2) + " (build: " + String(obj.prod_cw) + "/" + String(obj.prod_year) + ")"),
tr("Hardware Number", obj.part_num.toString(16)),
tr("{#HW_NUMBER}", obj.part_num.toString(16)),
tr("Bootloader Version", (obj.boot_ver/100).toFixed(2)),
tr("Grid Profile", ml("input", {type: "button", value: "show", class: "btn", onclick: function() {
tr("Grid Profile", ml("input", {type: "button", value: "{#BTN_SHOW}", class: "btn", onclick: function() {
modalClose();
getAjax("/api/inverter/grid/" + obj.id, showGridProfile);
}}, null))
])
])
modal("Info for inverter " + obj.name, ml("div", {}, html))
modal("{#INV_INFO}: " + obj.name, ml("div", {}, html))
}
function getGridValue(g) {
@ -332,9 +332,9 @@
))
content.push(ml("div", {class: "row my-2"}, [
ml("div", {class: "col-4"}, ml("b", {}, "Name")),
ml("div", {class: "col-3"}, ml("b", {}, "Value")),
ml("div", {class: "col-3"}, ml("b", {}, "Range")),
ml("div", {class: "col-2"}, ml("b", {}, "Default"))
ml("div", {class: "col-3"}, ml("b", {}, "{#VALUE}")),
ml("div", {class: "col-3"}, ml("b", {}, "{#RANGE}")),
ml("div", {class: "col-2"}, ml("b", {}, "{#DEFAULT}"))
]))
for(e of g.info.group) {
if(Array.isArray(e[id])) {
@ -362,10 +362,10 @@
var v = getGridValue(glob);
if(null === g) {
if(0 == obj.grid.length) {
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("p", {}, "Profile was not read until now, maybe turned off?"))))
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("p", {}, "{#PROFILE_NOT_READ}?"))))
} else {
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("h5", {}, "Unknown Profile"))))
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("p", {}, "Please open a new issue at https://github.com/lumapu/ahoy and copy the raw data into it."))))
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("h5", {}, "{#UNKNOWN_PROFILE}"))))
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("p", {}, "{#OPEN_ISSUE}."))))
content.push(ml("div", {class: "row"}, ml("div", {class: "col my-2"}, ml("pre", {}, obj.grid))))
}
} else {
@ -378,7 +378,7 @@
}
}
modal("Grid Profile for inverter " + obj.name, ml("div", {}, ml("div", {class: "col mb-2"}, [...content])))
modal("{#PROFILE_MODAL}: " + obj.name, ml("div", {}, ml("div", {class: "col mb-2"}, [...content])))
})
}
@ -386,56 +386,56 @@
function parseIvRadioStats(obj) {
var html = ml("table", {class: "table"}, [
ml("tbody", {}, [
tr2(["TX count", obj.tx_cnt, ""]),
tr2(["RX success", obj.rx_success, String(Math.round(obj.rx_success / obj.tx_cnt * 10000) / 100) + "&nbsp;%"]),
tr2(["RX fail", obj.rx_fail, String(Math.round(obj.rx_fail / obj.tx_cnt * 10000) / 100) + "&nbsp;%"]),
tr2(["RX no answer", obj.rx_fail_answer, String(Math.round(obj.rx_fail_answer / obj.tx_cnt * 10000) / 100) + "&nbsp;%"]),
tr2(["RX fragments", obj.frame_cnt, ""]),
tr2(["TX retransmits", obj.retransmits, ""]),
tr2(["Inverter loss rate", "lost " + obj.ivLoss + " of " + obj.ivSent, String(Math.round(obj.ivLoss / obj.ivSent * 10000) / 100) + "&nbsp;%"]),
tr2(["DTU loss rate", "lost " + obj.dtuLoss + " of " + obj.dtuSent, String(Math.round(obj.dtuLoss / obj.dtuSent * 10000) / 100) + "&nbsp;%"])
tr2(["{#TX_COUNT}", obj.tx_cnt, ""]),
tr2(["{#RX_SUCCESS}", obj.rx_success, String(Math.round(obj.rx_success / obj.tx_cnt * 10000) / 100) + "&nbsp;%"]),
tr2(["{#RX_FAIL}", obj.rx_fail, String(Math.round(obj.rx_fail / obj.tx_cnt * 10000) / 100) + "&nbsp;%"]),
tr2(["{#RX_NO_ANSWER}", obj.rx_fail_answer, String(Math.round(obj.rx_fail_answer / obj.tx_cnt * 10000) / 100) + "&nbsp;%"]),
tr2(["{#RX_FRAGMENTS}", obj.frame_cnt, ""]),
tr2(["{#TX_RETRANSMITS}", obj.retransmits, ""]),
tr2(["{#INV_LOSS_RATE}", "{#LOST_1} " + obj.ivLoss + " {#LOST_2} " + obj.ivSent + " {#LOST_3}", String(Math.round(obj.ivLoss / obj.ivSent * 10000) / 100) + "&nbsp;%"]),
tr2(["{#DTU_LOSS_RATE}", "{#LOST_1} " + obj.dtuLoss + " {#LOST_2} " + obj.dtuSent + " {#LOST_3}", String(Math.round(obj.dtuLoss / obj.dtuSent * 10000) / 100) + "&nbsp;%"])
])
])
modal("Radio statistics for inverter " + obj.name, ml("div", {}, html))
modal("{#RADIO_STAT_MODAL}: " + 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-12 col-sm-5 my-2"}, "{#LIMIT_VALUE}"),
ml("div", {class: "col-8 col-sm-5"}, ml("input", {name: "limit", type: "number", step: "0.1", min: 1}, "")),
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-8 col-sm-5"}, "{#KEEP_LIMIT}"),
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() {
ml("div", {class: "col a-r"}, ml("input", {type: "button", value: "{#BTN_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-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() {
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() {
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() {
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-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);
modal("{#POWER_LIMIT_MODAL}: " + obj.name, html);
}
function applyLimit(id) {
@ -470,19 +470,19 @@
function ctrlCb(obj) {
var e = document.getElementsByName("pwrres")[0];
if(obj.success) {
e.innerHTML = "received command, waiting for inverter acknowledge ...";
e.innerHTML = "{#CMD_RECEIVED_WAIT_ACK}";
tPwrAck = window.setInterval("getAjax('/api/inverter/pwrack/" + obj.id + "', updatePwrAck)", 1000);
}
else
e.innerHTML = "Error: " + obj["error"];
e.innerHTML = "{#ERROR}: " + obj["error"];
}
function ctrlCb2(obj) {
var e = document.getElementsByName("pwrres")[0];
if(obj.success)
e.innerHTML = "command received";
e.innerHTML = "{#COMMAND_RECEIVED}";
else
e.innerHTML = "Error: " + obj["error"];
e.innerHTML = "{#ERROR}: " + obj["error"];
}
function updatePwrAck(obj) {
@ -492,7 +492,7 @@
clearInterval(tPwrAck);
if(null == e)
return;
e.innerHTML = "inverter acknowledged active power control command";
e.innerHTML = "{#INV_ACK}";
}
function parse(obj) {

28
src/web/html/wizard.html

@ -24,23 +24,23 @@
function wifi() {
return ml("div", {}, [
ml("div", {class: "row my-5"}, ml("div", {class: "col"}, ml("span", {class: "fs-1"}, "Welcome to AhoyDTU"))),
ml("div", {class: "row"}, ml("div", {class: "col"}, ml("span", {class: "fs-5"}, "Network Setup"))),
sect("Choose your WiFi Network", ml("select", {id: "net", onchange: () => {if(found) clearInterval(v)}}, ml("option", {value: "-1"}, "---"))),
sect("... or name it manually", ml("input", {id: "man", type: "text"})),
sect("WiFi Password", ml("input", {id: "pwd", type: "password"})),
ml("div", {class: "row my-4"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", class:"btn", value: "next", onclick: () => {saveWifi()}}, null))),
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/"}, "stop wizard")))
ml("div", {class: "row my-5"}, ml("div", {class: "col"}, ml("span", {class: "fs-1"}, "{#WELCOME}"))),
ml("div", {class: "row"}, ml("div", {class: "col"}, ml("span", {class: "fs-5"}, "{#NETWORK_SETUP}"))),
sect("{#CHOOSE_WIFI}", ml("select", {id: "net", onchange: () => {if(found) clearInterval(v)}}, ml("option", {value: "-1"}, "---"))),
sect("{#WIFI_MANUAL}", ml("input", {id: "man", type: "text"})),
sect("{#WIFI_PASSWORD}", ml("input", {id: "pwd", type: "password"})),
ml("div", {class: "row my-4"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", class:"btn", value: "{#BTN_NEXT}", onclick: () => {saveWifi()}}, null))),
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/"}, "{#STOP_WIZARD}")))
])
}
function checkWifi() {
c.replaceChildren(
ml("div", {class: "row my-5"}, ml("div", {class: "col"}, ml("span", {class: "fs-1"}, "Welcome to AhoyDTU"))),
ml("div", {class: "row"}, ml("div", {class: "col"}, ml("span", {class: "fs-5"}, "Test Connection"))),
sect("AhoyDTU is trying to connect to your WiFi", ml("span", {id: "state"}, "connecting ...")),
ml("div", {class: "row my-4"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", class:"btn hide", id: "btn", value: "Finish", onclick: () => {redirect()}}, null))),
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/"}, "stop wizard")))
ml("div", {class: "row my-5"}, ml("div", {class: "col"}, ml("span", {class: "fs-1"}, "{#WELCOME}"))),
ml("div", {class: "row"}, ml("div", {class: "col"}, ml("span", {class: "fs-5"}, "{#TEST_CONNECTION}"))),
sect("{#TRY_TO_CONNECT}", ml("span", {id: "state"}, "{#CONNECTING}")),
ml("div", {class: "row my-4"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", class:"btn hide", id: "btn", value: "{#BTN_FINISH}", onclick: () => {redirect()}}, null))),
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/"}, "{#STOP_WIZARD}")))
)
v = setInterval(() => {getAjax('/api/setup/getip', printIp)}, 2500);
}
@ -53,7 +53,7 @@
if("0.0.0.0" != obj["ip"]) {
clearInterval(v)
setHide("btn", false)
document.getElementById("state").innerHTML = "success, got following IP in your network: " + obj.ip
document.getElementById("state").innerHTML = "{#NETWORK_SUCCESS}" + obj.ip
}
}
@ -68,7 +68,7 @@
var e = document.getElementById("net");
if(obj.networks.length > 0) {
var a = []
a.push(ml("option", {value: -1}, obj.networks.length + " Network(s) found"))
a.push(ml("option", {value: -1}, obj.networks.length + " {#NUM_NETWORKS_FOUND}"))
for(n of obj.networks) {
a.push(ml("option", {value: n.ssid}, n.ssid + " (" + n.rssi + "dBm)"))
found = true;

1334
src/web/lang.json

File diff suppressed because it is too large
Loading…
Cancel
Save