From b765916a2af3f0229eeae1da8d93277566c6dfb4 Mon Sep 17 00:00:00 2001 From: lumapu Date: Sun, 7 Jan 2024 02:36:19 +0100 Subject: [PATCH] 0.8.47 * started to have german translations of all variants (environments) #925 #1199 --- .github/workflows/compile_development.yml | 23 +- scripts/convertHtml.py | 39 +- scripts/getVersion.py | 54 + src/CHANGES.md | 1 + src/config/config.h | 4 +- src/platformio.ini | 144 ++- src/web/html/includes/header.html | 2 +- src/web/html/includes/nav.html | 10 +- src/web/html/index.html | 53 +- src/web/html/save.html | 8 +- src/web/html/setup.html | 288 +++-- src/web/html/system.html | 36 +- src/web/html/update.html | 8 +- src/web/html/visualization.html | 136 +-- src/web/html/wizard.html | 28 +- src/web/lang.json | 1334 +++++++++++++++++++++ 16 files changed, 1869 insertions(+), 299 deletions(-) create mode 100644 src/web/lang.json diff --git a/.github/workflows/compile_development.yml b/.github/workflows/compile_development.yml index 6eba4879..4b688fca 100644 --- a/.github/workflows/compile_development.yml +++ b/.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 diff --git a/scripts/convertHtml.py b/scripts/convertHtml.py index 6eaa92a3..4e01a875 100644 --- a/scripts/convertHtml.py +++ b/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) diff --git a/scripts/getVersion.py b/scripts/getVersion.py index cdb39ae8..ce34d26e 100644 --- a/scripts/getVersion.py +++ b/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/" diff --git a/src/CHANGES.md b/src/CHANGES.md index e91179f7..c403f8b0 100644 --- a/src/CHANGES.md +++ b/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 diff --git a/src/config/config.h b/src/config/config.h index fb05a1cc..40cb5b76 100644 --- a/src/config/config.h +++ b/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__ diff --git a/src/platformio.ini b/src/platformio.ini index 9b204f2e..e9eb282b 100644 --- a/src/platformio.ini +++ b/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 diff --git a/src/web/html/includes/header.html b/src/web/html/includes/header.html index ab3b0545..c363304c 100644 --- a/src/web/html/includes/header.html +++ b/src/web/html/includes/header.html @@ -1,6 +1,6 @@ - + diff --git a/src/web/html/includes/nav.html b/src/web/html/includes/nav.html index 91de5047..c05099b7 100644 --- a/src/web/html/includes/nav.html +++ b/src/web/html/includes/nav.html @@ -6,16 +6,16 @@
- Live - Webserial - Settings + {#NAV_LIVE} + {#NAV_WEBSERIAL} + {#NAV_SETTINGS} Update System REST API - Documentation - About + {#NAV_DOCUMENTATION} + {#NAV_ABOUT} Login Logout diff --git a/src/web/html/index.html b/src/web/html/index.html index cfdaeee6..6ad2c787 100644 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -20,18 +20,15 @@

-

Support this project:

+

{#SUPPORT}:

-

- This project was started from this discussion. (Mikrocontroller.net) -

@@ -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); diff --git a/src/web/html/save.html b/src/web/html/save.html index 9b5b4864..e5d5a67d 100644 --- a/src/web/html/save.html +++ b/src/web/html/save.html @@ -8,7 +8,7 @@ {#HTML_NAV}
-
Saving settings...
+
{#SAVE_SETTINGS}
{#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; diff --git a/src/web/html/setup.html b/src/web/html/setup.html index 782c23be..4903db16 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -1,7 +1,7 @@ - Setup + {#SETTINGS} {#HTML_HEADER} @@ -10,77 +10,74 @@
- +
- Device Host Name + {#DEVICE_NAME}
-
Device Name
+
{#DEVICE_NAME}
-
Reboot Ahoy at midnight
+
{#REBOOT_AT_MIDNIGHT}
-
Dark Mode
+
{#DARK_MODE}
-
(empty browser cache or use CTRL + F5 after reboot to apply this setting)
+
{#DARK_MODE_NOTE}
- System Config + {#PINOUT_CONFIGURATION}

Status LEDs

-

Radio (NRF24L01+)

+

{#RADIO} (NRF24L01+)

-

Radio (CMT2300A)

-
(ESP32 only)
+

{#RADIO} (CMT2300A)

+
-

Serial Console

+

{#SERIAL_CONSOLE}

-
print inverter data
+
{#LOG_PRINT_INVERTER_DATA}
-
Serial Debug
+
{#LOG_SERIAL_DEBUG}
-
Privacy Mode
+
{#LOG_PRIVACY_MODE}
-
Print whole traces in Log
+
{#LOG_PRINT_TRACES}
- +
WiFi
-
AP Password (min. length: 8)
+
{#AP_PWD}
- -

Enter the credentials to your preferred WiFi station. After rebooting the device tries to connect with this information.

-
-
Search Networks
-
+
{#SEARCH_NETWORKS}
+
-
Avail Networks
+
{#AVAIL_NETWORKS}
@@ -89,26 +86,25 @@
-
SSID is hidden
+
{#SSID_HIDDEN}
-
Password
+
{#PASSWORD}
- Static IP (optional) + {#STATIC_IP}

- Leave fields blank for DHCP
- The following fields are parsed in this format: 192.168.4.1 + {#NETWORK_HINT_BLANK}

-
IP Address
+
{#IP_ADDRESS}
-
Submask
+
{#SUBMASK}
@@ -126,58 +122,58 @@
- +
- Protection + {#PROTECTION}
-
Admin Password
+
{#ADMIN_PASSWORD}
-

Select pages which should be protected by password

+

{#PROTECTION_NOTE}

- +
- Inverter + {#INVERTER}
-
Interval [s]
+
{#INTERVAL}
-
Inverter Gap [ms]
+
{#INV_GAP}
-
Reset values and YieldDay at midnight
+
{#INV_RESET_MIDNIGHT}
-
Reset values when inverter polling pauses at sunset
+
{#INV_PAUSE_SUNSET}
-
Reset values when inverter status is 'not available'
+
{#INV_RESET_NOT_AVAIL}
-
Reset 'max' values at midnight
+
{#INV_RESET_MAX_MIDNIGHT}
-
Start without time sync (useful in AP-Only-Mode)
+
{#INV_START_WITHOUT_TIME}
-
Read Grid Profile
+
{#INV_READ_GRID_PROFILE}
-
Yield Efficiency (Standard 1.0)
+
{#INV_YIELD_EFF}
@@ -196,42 +192,42 @@
-
NTP Interval (in Minutes, min. 5 Minutes)
+
{#NTP_INTERVAL}
-
set System time
+
{#NTP_SET_SYS_TIME}
- -
+ +
-
System Time
+
{#NTP_SYS_TIME}
- +
- Sunrise & Sunset + {#SUNRISE_SUNSET}
-
Latitude (decimal)
+
{#LATITUDE}
-
Longitude (decimal)
+
{#LONGITUDE}
-
Offset (sunrise)
+
{#OFFSET_SUNRISE}
-
Offset (sunset)
+
{#OFFSET_SUNSET}
@@ -254,20 +250,20 @@
-
Username (optional)
+
{#MQTT_USER}
-
Password (optional)
+
{MQTT_PASSWORD}
Topic
-

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)

+

{#MQTT_NOTE}

-
Interval [s]
+
{#INTERVAL}
@@ -280,29 +276,29 @@
- +
- Display Config + {#DISPLAY_CONFIG}
-
Turn off while inverters are offline
+
{#DISP_OFF_INV}
-
Luminance
+
{#DISP_LUMINANCE}
-

Pinout

+

{#DISP_PINOUT}

-
Reboot device after successful save
+
{#BTN_REBOOT_SUCCESSFUL_SAVE}
@@ -311,11 +307,11 @@
- ERASE SETTINGS (not WiFi) + {#BTN_ERASE}
- Import / Export JSON Settings + {#IM_EXPORT}
-
Import
+
{#IMPORT}
@@ -326,9 +322,9 @@
-
Export
+
{#EXPORT}
- Export settings (JSON file) (only values, passwords will be removed!) + {#BTN_EXPORT} {#EXPORT_NOTE}
@@ -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() { diff --git a/src/web/html/system.html b/src/web/html/system.html index f400db41..16c6343e 100644 --- a/src/web/html/system.html +++ b/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")) ]) ) ); diff --git a/src/web/html/update.html b/src/web/html/update.html index 4e56b1ad..f0e39fe7 100644 --- a/src/web/html/update.html +++ b/src/web/html/update.html @@ -9,14 +9,14 @@
- Select firmware file (*.bin) + {#SELECT_FILE} (*.bin) - +
@@ -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); diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index 27f5da35..8099539c 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -10,7 +10,7 @@
-

Every seconds the values are updated

+

{#EVERY} {#UPDATE_SECS}

{#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) + " %"]), - tr2(["RX fail", obj.rx_fail, String(Math.round(obj.rx_fail / obj.tx_cnt * 10000) / 100) + " %"]), - tr2(["RX no answer", obj.rx_fail_answer, String(Math.round(obj.rx_fail_answer / obj.tx_cnt * 10000) / 100) + " %"]), - 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) + " %"]), - tr2(["DTU loss rate", "lost " + obj.dtuLoss + " of " + obj.dtuSent, String(Math.round(obj.dtuLoss / obj.dtuSent * 10000) / 100) + " %"]) + tr2(["{#TX_COUNT}", obj.tx_cnt, ""]), + tr2(["{#RX_SUCCESS}", obj.rx_success, String(Math.round(obj.rx_success / obj.tx_cnt * 10000) / 100) + " %"]), + tr2(["{#RX_FAIL}", obj.rx_fail, String(Math.round(obj.rx_fail / obj.tx_cnt * 10000) / 100) + " %"]), + tr2(["{#RX_NO_ANSWER}", obj.rx_fail_answer, String(Math.round(obj.rx_fail_answer / obj.tx_cnt * 10000) / 100) + " %"]), + 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) + " %"]), + tr2(["{#DTU_LOSS_RATE}", "{#LOST_1} " + obj.dtuLoss + " {#LOST_2} " + obj.dtuSent + " {#LOST_3}", String(Math.round(obj.dtuLoss / obj.dtuSent * 10000) / 100) + " %"]) ]) ]) - 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) { diff --git a/src/web/html/wizard.html b/src/web/html/wizard.html index 1ebfb65f..674823b2 100644 --- a/src/web/html/wizard.html +++ b/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; diff --git a/src/web/lang.json b/src/web/lang.json new file mode 100644 index 00000000..d6c76ffc --- /dev/null +++ b/src/web/lang.json @@ -0,0 +1,1334 @@ +{ + "files": [ + { + "name": "general", + "list": [ + { + "token": "NAV_LIVE", + "en": "Live", + "de": "Daten" + }, + { + "token": "NAV_WEBSERIAL", + "en": "Webserial", + "de": "Web Konsole" + }, + { + "token": "NAV_SETTINGS", + "en": "Settings", + "de": "Einstellungen" + }, + { + "token": "NAV_DOCUMENTATION", + "en": "Documentation", + "de": "Dokumentation" + }, + { + "token": "NAV_ABOUT", + "en": "About", + "de": "Über" + } + ] + }, + { + "name": "wizard.html", + "list": [ + { + "token": "WELCOME", + "en": "Welcome to AhoyDTU", + "de": "Willkommen zu AhoyDTU" + }, + { + "token": "NETWORK_SETUP", + "en": "Network Setup", + "de": "Netzwerkeinstellung" + }, + { + "token": "CHOOSE_WIFI", + "en": "Choose your WiFi Network", + "de": "Wähle dein WiFi Netzwerk aus" + }, + { + "token": "WIFI_MANUAL", + "en": "... or name it manually", + "de": "... oder nenne es hier" + }, + { + "token": "WIFI_PASSWORD", + "en": "WiFi Password", + "de": "WiFi Passwort" + }, + { + "token": "STOP_WIZARD", + "en": "stop wizard", + "de": "Einrichtung beenden" + }, + { + "token": "BTN_NEXT", + "en": "next >>", + "de": "prüfen >>" + }, + { + "token": "TEST_CONNECTION", + "en": "Test Connection", + "de": "Verbindung wird überprüft" + }, + { + "token": "TRY_TO_CONNECT", + "en": "AhoyDTU is trying to connect to your WiFi", + "de": "AhoyDTU versucht eine Verindung mit deinem Netzwerk herzustellen" + }, + { + "token": "CONNECTING", + "en": "connecting ...", + "de": "verbinde ..." + }, + { + "token": "NETWORK_SUCCESS", + "en": "success, got following IP in your network: ", + "de": "Verindung erfolgreich. AhoyDTU hat die folgende IP bekommen: " + }, + { + "token": "BTN_FINISH", + "en": "Finish >>", + "de": "Fertig >>" + }, + { + "token": "NUM_NETWORKS_FOUND", + "en": "Network(s) found", + "de": "Netzwerk(e) gefunden" + } + ] + }, + { + "name": "setup.html", + "list": [ + { + "token": "SETTINGS", + "en": "Settings", + "de": "Einstellungen" + }, + { + "token": "SYSTEM_CONFIG", + "en": "System Config", + "de": "Systemkonfiguration" + }, + { + "token": "DEVICE_NAME", + "en": "Device Host Name", + "de": "Name der DTU" + }, + { + "token": "REBOOT_AT_MIDNIGHT", + "en": "Reboot Ahoy at midnight", + "de": "um Mitternacht neu starten" + }, + { + "token": "DARK_MODE", + "en": "Dark Mode", + "de": "dunkler Modus" + }, + { + "token": "DARK_MODE_NOTE", + "en": "(empty browser cache or use CTRL + F5 after reboot to apply this setting)", + "de": "(der Browser-Cache muss geleert oder STRG + F5 gedrückt werden, um diese Einstellung zu aktivieren)" + }, + { + "token": "PINOUT_CONFIGURATION", + "en": "Pinout Configuration", + "de": "Anschlusseinstellungen" + }, + { + "token": "RADIO", + "en": "Radio", + "de": "Funkmodul" + }, + { + "token": "SERIAL_CONSOLE", + "en": "Serial console", + "de": "Serielle Konsole" + }, + { + "token": "LOG_PRINT_INVERTER_DATA", + "en": "print inverter data", + "de": "Livedaten ausgeben" + }, + { + "token": "LOG_SERIAL_DEBUG", + "en": "Serial Debug", + "de": "Entwicklerinformationen ausgeben" + }, + { + "token": "LOG_PRIVACY_MODE", + "en": "Privacy Mode", + "de": "Privatsphärenmodus" + }, + { + "token": "LOG_PRINT_TRACES", + "en": "Print whole traces in Log", + "de": "alle Informationen in Log schreiben" + }, + { + "token": "NETWORK", + "en": "Network", + "de": "Netzwerk" + }, + { + "token": "AP_PWD", + "en": "AP Password (min. length: 8)", + "de": "AP Passwort (min. Länge: 8 Zeichen)" + }, + { + "token": "SEARCH_NETWORKS", + "en": "Search Networks", + "de": "Netzwerke suchen" + }, + { + "token": "BTN_SCAN", + "en": "scan", + "de": "Suche starten" + }, + { + "token": "AVAIL_NETWORKS", + "en": "Avail Networks", + "de": "Verfügbare Netzwerke" + }, + { + "token": "NETWORK_NOT_SCANNED", + "en": "not scanned", + "de": "nicht gesucht" + }, + { + "token": "SSID_HIDDEN", + "en": "SSID is hidden", + "de": "SSID ist versteckt" + }, + { + "token": "PASSWORD", + "en": "Password", + "de": "Passwort" + }, + { + "token": "STATIC_IP", + "en": "Static IP (optional)", + "de": "Statische IP (optional)" + }, + { + "token": "NETWORK_HINT_BLANK", + "en": "Leave fields blank for DHCP
The following fields are parsed in this format: 192.168.4.1", + "de": "Felder leer lassen um in den DHCP Modus zu wechseln.
Das Format der IP Adressen ist: 192.168.4.1" + }, + { + "token": "IP_ADDRESS", + "en": "IP Address", + "de": "IP Adresse" + }, + { + "token": "SUBMASK", + "en": "Submask", + "de": "Subnetzmaske" + }, + { + "token": "PROTECTION", + "en": "Protection", + "de": "Zugriffsschutz" + }, + { + "token": "ADMIN_PASSWORD", + "en": "Admin Password", + "de": "Administratorpasswort" + }, + { + "token": "PROTECTION_NOTE", + "en": "Select pages which should be protected by password", + "de": "Auswählen, welche Bereiche passwortgeschützt werden sollen" + }, + { + "token": "INVERTER", + "en": "Inverter", + "de": "Wechselrichter" + }, + { + "token": "INTERVAL", + "en": "Interval [s]", + "de": "Intervall [s]" + }, + { + "token": "INV_GAP", + "en": "Communication Gap [ms]", + "de": "Kommunikationslücke" + }, + { + "token": "INV_RESET_MIDNIGHT", + "en": "Reset values and YieldDay at midnight", + "de": "Werte und Gesamtertrag um Mitternacht zurücksetzen" + }, + { + "token": "INV_PAUSE_SUNSET", + "en": "Reset values at sunset", + "de": "Werte bei Sonnenuntergang zurücksetzen" + }, + { + "token": "INV_RESET_NOT_AVAIL", + "en": "Reset values when inverter status is 'not available'", + "de": "Werte zurücksetzen, sobald der Wechselrichter nicht erreichbar ist" + }, + { + "token": "INV_RESET_MAX_MIDNIGHT", + "en": "Reset 'max' values at midnight", + "de": "Maximalwerte bei Sonnenuntergang zurücksetzen" + }, + { + "token": "INV_START_WITHOUT_TIME", + "en": "Start without time sync (useful in AP-Only-Mode)", + "de": "Kommunikation starten ohne gültige Zeit (sinnvoll im AP Modus)" + }, + { + "token": "INV_READ_GRID_PROFILE", + "en": "Read Grid Profile", + "de": "Grid-Profil auslesen" + }, + { + "token": "INV_YIELD_EFF", + "en": "Yield Efficiency (default 1.0)", + "de": "Ertragseffizienz (Standard 1.0)" + }, + { + "token": "NTP_INTERVAL", + "en": "NTP Interval (in minutes, min. 5 minutes)", + "de": "NTP Intervall (in Minuten, min. 5 Minuten)" + }, + { + "token": "NTP_SET_SYS_TIME", + "en": "set system time", + "de": "Systemzeit setzten" + }, + { + "token": "BTN_FROM_BROWSER", + "en": "from browser", + "de": "vom Browser übernehmen" + }, + { + "token": "BTN_SYNC_NTP", + "en": "sync NTP", + "de": "NTP syncchronisieren" + }, + { + "token": "NTP_SYS_TIME", + "en": "System Time", + "de": "Systemzeit" + }, + { + "token": "SUNRISE_SUNSET", + "en": "Sunrise & Sunset", + "de": "Sonnenaufgang & -untergang" + }, + { + "token": "LATITUDE", + "en": "Latitude (decimal)", + "de": "Breitengrad (dezimal)" + }, + { + "token": "LONGITUDE", + "en": "Longitude (decimal)", + "de": "Längengrad (dezimal)" + }, + { + "token": "OFFSET_SUNRISE", + "en": "Offset (sunrise)", + "de": "Zeitversatz (Sonnenaufgang)" + }, + { + "token": "OFFSET_SUNSET", + "en": "Offset (sunset)", + "de": "Zeitversatz (Sonnenuntergang)" + }, + { + "token": "MQTT_USER", + "en": "Username (optional)", + "de": "Benutzername (optional)" + }, + { + "token": "MQTT_PASSWORD", + "en": "Password (optional)", + "de": "Passwort (optional)" + }, + { + "token": "MQTT_NOTE", + "en": "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)", + "de": "Wechselrichterdaten in fixem Intervall schicken, auch wenn es keine Änderung gab. Ein Wert von '0' deaktiviert das fixe Intervall, die Wechselrichterdaten werden übertragen, sobald neue zur Verfügung stehen. (Standard: 0)" + }, + { + "token": "DISPLAY_CONFIG", + "en": "Display Config", + "de": "Display Konfiguration" + }, + { + "token": "DISP_OFF_INV", + "en": "Turn off while inverters are offline", + "de": "schalte das Display aus, während die Wechselrichter aus sind" + }, + { + "token": "DISP_LUMINANCE", + "en": "Luminance", + "de": "Helligkeit" + }, + { + "token": "DISP_PINOUT", + "en": "Pinout", + "de": "Anschlusseinstellungen" + }, + { + "token": "BTN_REBOOT_SUCCESSFUL_SAVE", + "en": "Reboot device after successful save", + "de": "Nach erfolgreichem Speichern AhoyDTU neu starten" + }, + { + "token": "BTN_ERASE", + "en": "ERASE SETTINGS (not WiFi)", + "de": "Einstellungen zurücksetzen (nicht WiFi)" + }, + { + "token": "IM_EXPORT", + "en": "Import / Export JSON Settings", + "de": "Import / Export JSON Einstellungen" + }, + { + "token": "IMPORT", + "en": "Import", + "de": "Import" + }, + { + "token": "EXPORT", + "en": "Export", + "de": "Export" + }, + { + "token": "BTN_EXPORT", + "en": "Export settings (JSON file)", + "de": "Einstellungen exportieren (JSON Datei)" + }, + { + "token": "EXPORT_NOTE", + "en": "(only values, passwords will be skipped!)", + "de": "(nur Werte, Passwörter werden nicht mit exportiert!)" + }, + { + "token": "PIN_OFF", + "en": "off / default", + "de": "aus / Standard" + }, + { + "token": "PIN_NO_IRQ", + "en": "no IRQ!", + "de": "kein Interrupt!" + }, + { + "token": "PIN_INPUT_ONLY", + "en": "in only", + "de": "nur Eingang" + }, + { + "token": "PIN_DONT_USE", + "en": "DONT USE", + "de": "nicht benutzen" + }, + { + "token": "PIN_NOT_AVAIL", + "en": "not available", + "de": "nicht verfügbar" + }, + { + "token": "PIN_RECOMMENDED", + "en": "recommended", + "de": "empfohlen" + }, + { + "token": "PIN_EXPERIMENTAL", + "en": "experimental", + "de": "experimentell" + }, + { + "token": "PIN_LOW_ACTIVE", + "en": "low active", + "de": "low aktiv" + }, + { + "token": "PIN_HIGH_ACTIVE", + "en": "high active", + "de": "high aktiv" + }, + { + "token": "NETWORK_SCANNING", + "en": "scanning ...", + "de": "suche ..." + }, + { + "token": "ERROR", + "en": "Error:", + "de": "Fehler:" + }, + { + "token": "NTP_COMMAND_EXE", + "en": "command executed, set new time ...", + "de": "Befehl ausgeführt, setze neue Zeit ..." + }, + { + "token": "NTP_SYNCED_AT", + "en": "synced at", + "de": "syncchronisiert um" + }, + { + "token": "NTP_DIFF", + "en": "difference", + "de": "Unterschied" + }, + { + "token": "MQTT_EXE", + "en": "command executed", + "de": "Befehl ausgeführt" + }, + { + "token": "IMPORT_UPLOAD_STARTED", + "en": "upload started", + "de": "hochladen gestartet" + }, + { + "token": "INV_EDIT", + "en": "Edit", + "de": "Bearbeiten" + }, + { + "token": "INV_DELETE", + "en": "Delete", + "de": "Löschen" + }, + { + "token": "ENABLED", + "en": "enabled", + "de": "aktiviert" + }, + { + "token": "DISABLED", + "en": "disabled", + "de": "deaktiviert" + }, + { + "token": "BTN_INV_ADD", + "en": "add Inverter", + "de": "Wechselrichter hinzufuegen" + }, + { + "token": "INV_INPUT", + "en": "Input", + "de": "Eingang" + }, + { + "token": "INV_MAX_MODULE_POWER", + "en": "Max Module Power", + "de": "Maximale Panelleistung" + }, + { + "token": "INV_YIELD_CORR", + "en": "Yield Correction", + "de": "Ertragskorrektur" + }, + { + "token": "TAB_GENERAL", + "en": "General", + "de": "Allgemein" + }, + { + "token": "TAB_INPUTS", + "en": "Inputs", + "de": "Eingaenge" + }, + { + "token": "TAB_RADIO", + "en": "Radio", + "de": "Funkmodul" + }, + { + "token": "TAB_ADVANCED", + "en": "Advanced", + "de": "Erweitert" + }, + { + "token": "INV_ENABLE", + "en": "Enabled", + "de": "aktiviert" + }, + { + "token": "INV_SERIAL", + "en": "Serial", + "de": "Seriennummer" + }, + { + "token": "INV_FREQUENCY", + "en": "Frequency", + "de": "Frequenz" + }, + { + "token": "INV_POWER_LEVEL", + "en": "Power Level", + "de": "Sendeleistung" + }, + { + "token": "INV_PAUSE_DURING_NIGHT", + "en": "Pause communication during night (lat. and lon. need to be set)", + "de": "Kommunikation während der Nacht pausieren (Breiten- und Längengrad müssen gesetzt sein" + }, + { + "token": "INV_INCLUDE_MQTT_SUM", + "en": "Include inverter to sum of total (should be checked by default, MqTT only)", + "de": "Wechselrichter in Liste der aufzuaddierenden Wechselrichter aufnehmen (nur MqTT)" + }, + { + "token": "BTN_SAVE", + "en": "save", + "de": "speichern" + }, + { + "token": "INV_EDIT_MODAL", + "en": "Edit inverter", + "de": "Wechselrichter editieren" + }, + { + "token": "INV_DELETE_SURE", + "en": "do you realy want to delete inverter?", + "de": "Willst du den Wechselrichter wirklich löschen?" + }, + { + "token": "BTN_YES", + "en": "yes", + "de": "ja" + }, + { + "token": "INV_DELETE_MODAL", + "en": "Delete inverter", + "de": "Wechselrichter löschen " + }, + { + "token": "NTP_MINUTES", + "en": "minutes", + "de": "Minuten" + }, + { + "token": "LED_AT_LEAST_ONE_PRODUCING", + "en": "At least one inverter is producing", + "de": "mindestens ein Wechselrichter produziert" + }, + { + "token": "LED_MQTT_CONNECTED", + "en": "MqTT connected", + "de": "MqTT verbunden" + }, + { + "token": "LED_NIGHT_TIME", + "en": "Night time", + "de": "Nachtschaltung" + }, + { + "token": "LED_POLARITY", + "en": "LED polarity", + "de": "LED Polarität" + }, + { + "token": "LED_LUMINANCE", + "en": "LED luminance", + "de": "LED Helligkeit" + }, + { + "token": "NRF24_ENABLE", + "en": "NRF24 radio enable", + "de": "NRF24 Funkmodul aktivieren" + }, + { + "token": "CMT_ENABLE", + "en": "CMT2300A radio enable", + "de": "CMT2300A Funkmodul aktivieren" + }, + { + "token": "DISP_NONE", + "en": "None", + "de": "keins" + }, + { + "token": "DISP_TYPE", + "en": "Type", + "de": "Typ" + }, + { + "token": "DISP_ROTATION", + "en": "Rotation", + "de": "Drehung" + }, + { + "token": "DISP_SCREENSAVER_OFF", + "en": "off", + "de": "aus" + }, + { + "token": "DISP_PIXEL_SHIFT", + "en": "pixel shift", + "de": "Bildversatz" + }, + { + "token": "DISP_MOTION_SENS", + "en": "motion sensor", + "de": "Bewegungssensor" + }, + { + "token": "DISP_SCREENSAVER", + "en": "Screensaver (OLED only)", + "de": "Bildschrimschoner (nur OLED)" + }, + { + "token": "NETWORK_PLEASE_SELECT", + "en": "please select network", + "de": "bitte Netzwerk auswählen" + }, + { + "token": "NO_NETWORK_FOUND", + "en": "no network found", + "de": "kein Netzwerk gefunden" + } + ] + }, + { + "name": "system.html", + "list": [ + { + "token": "UNKNOWN", + "en": "unknown", + "de": "unbekannt" + }, + { + "token": "CONNECTED", + "en": "connected", + "de": "verbunden" + }, + { + "token": "NOT", + "en": "not", + "de": "nicht" + }, + { + "token": "IRQ_WORKING", + "en": "Interrupt Pin working", + "de": "Interrupt Pin funktoniert" + }, + { + "token": "NRF24_DATA_RATE", + "en": "NRF24 Data Rate", + "de": "NRF24 Geschwindigkeit" + }, + { + "token": "NOT_ENABLED", + "en": "not enabled", + "de": "nicht aktiviert" + }, + { + "token": "NRF24_RADIO", + "en": "Radio NRF24", + "de": "NRF24 Funkmodul" + }, + { + "token": "CMT_RADIO", + "en": "Radio CMT", + "de": "CMT2300A Funkmodul" + }, + { + "token": "TRUE", + "en": "true", + "de": "ja" + }, + { + "token": "FALSE", + "en": "false", + "de": "nein" + }, + { + "token": "ENABLED", + "en": "enabled", + "de": "aktiviert" + }, + { + "token": "SUN", + "en": "Sun", + "de": "Sonne" + }, + { + "token": "SUNRISE", + "en": "sunrise", + "de": "Sonnenaufgang" + }, + { + "token": "SUNSET", + "en": "sunset", + "de": "Sonnenuntergang" + }, + { + "token": "COMMUNICATION_START", + "en": "Communication start", + "de": "Start der Kommunikation" + }, + { + "token": "COMMUNICATION_STOP", + "en": "Communication stop", + "de": "Ende der Kommunikation" + }, + { + "token": "NIGHT_BEHAVE", + "en": "Night behaviour", + "de": "Verhalten bei Nacht" + }, + { + "token": "COMMUNICATING", + "en": "communicating", + "de": "aktiv" + } + ] + }, + { + "name": "index.html", + "list": [ + { + "token": "SUPPORT", + "en": "Support this project", + "de": "Dieses Projekt unterstützen" + }, + { + "token": "CHANGELOG", + "en": "Changelog", + "de": "Änderungshistorie" + }, + { + "token": "DISCUSS", + "en": "Discuss with us on", + "de": "Diskutiere mit uns bei" + }, + { + "token": "REPORT", + "en": "Report", + "de": "Melde " + }, + { + "token": "ISSUES", + "en": "Issues", + "de": "Fehler " + }, + { + "token": "CONTRIBUTE", + "en": "Contribute to", + "de": "Unterstütze bei" + }, + { + "token": "DOCUMENTATION", + "en": "documentation", + "de": "der Dokumentation" + }, + { + "token": "DEV_FIRMWARE", + "en": "development firmware", + "de": "Entwicklerversionen der Firmware" + }, + { + "token": "DEV_CHANGELOG", + "en": "Development Changelog", + "de": "Änderungshistorie der Entwicklerversionen" + }, + { + "token": "DON_MAKE", + "en": "make a", + "de": "Unterstüze mit" + }, + { + "token": "DON_MAKE", + "en": "make a", + "de": "Unterstüze z.B. mit" + }, + { + "token": "DONATION", + "en": "donation", + "de": "Paypal" + }, + { + "token": "COMMAND_EXE", + "en": "command executed", + "de": "Befehl ausgeführt" + }, + { + "token": "ERROR", + "en": "Error", + "de": "Fehler" + }, + { + "token": "NTP_UNREACH", + "en": "NTP timeserver unreachable", + "de": "NTP Zeitserver nicht erreichbar" + }, + { + "token": "POLLING_STOP", + "en": "Polling inverter(s), will pause at sunset", + "de": "Abfrage der Wechselrichter wird zum Sonnenuntergang stoppen" + }, + { + "token": "NIGHT_TIME", + "en": "Night time, inverter polling disabled", + "de": "Nacht, Wechselrichterabfrage deaktivert" + }, + { + "token": "PAUSED_AT", + "en": "paused at", + "de": "pausiert um" + }, + { + "token": "START_AT", + "en": "will start polling at", + "de": "wird starten um" + }, + { + "token": "DISABLED", + "en": "disabled", + "de": "deaktiviert" + }, + { + "token": "NOT_YET_AVAIL", + "en": "not yet available", + "de": "gerade nicht verfügbar" + }, + { + "token": "AVAIL", + "en": "available and is", + "de": "verfügbar und ist" + }, + { + "token": "AVAIL_NO_DATA", + "en": "available but no data was received until now", + "de": "verfügbar, aber bis jetzt keine Daten empfangen" + }, + { + "token": "NOT_PRODUCING", + "en": "not producing", + "de": "produziert nicht" + }, + { + "token": "PRODUCING", + "en": "producing", + "de": "produziert" + }, + { + "token": "INVERTER", + "en": "Inverter", + "de": "Wechselrichter" + }, + { + "token": "IS", + "en": "is", + "de": "ist" + }, + { + "token": "LAST_SUCCESS", + "en": "last successful transmission", + "de": "letzte erfolgreiche Übertragung" + }, + { + "token": "UPDATE_AVAIL", + "en": "Update available, current released version", + "de": "Update verfügbar, aktuell veröffentlichte Version" + }, + { + "token": "USING_DEV_VERSION", + "en": "You are using development version", + "de": "Du verwendest eine Entwicklerversion" + }, + { + "token": "DEV_ISSUE_RELEASE_VERSION", + "en": "In case of issues you may want to try the current stable release", + "de": "Wenn du Fehler feststellst solltest du die aktuelle Releaseversion verwenden" + }, + { + "token": "RELEASE_INSTALLED", + "en": "You are using the current stable release", + "de": "Du verwendest das aktuelle Release" + } + ] + }, + { + "name": "update.html", + "list": [ + { + "token": "SELECT_FILE", + "en": "Select firmware file", + "de": "Firmware Datei auswählen" + }, + { + "token": "BTN_UPDATE", + "en": "Update", + "de": "aktualisieren" + }, + { + "token": "BTN_UPDATE", + "en": "Update", + "de": "aktualisieren" + }, + { + "token": "DOWNLOADS", + "en": "Download latest Release and Development versions (without login)", + "de": "Lade die letzte Releaseversion oder Entwicklerversion herunter (ohne Login)" + }, + { + "token": "UPDATE_STARTED", + "en": "update started", + "de": "Aktualisierung gestartet" + } + ] + }, + { + "name": "visualization.html", + "list": [ + { + "token": "EVERY", + "en": "Every", + "de": "Alle" + }, + { + "token": "UPDATE_SECS", + "en": "seconds the values are updated", + "de": "Sekunden werden die Daten aktualisiert" + }, + { + "token": "TOTAL", + "en": "TOTAL", + "de": "GESAMT" + }, + { + "token": "AC_POWER", + "en": "AC Power", + "de": "AC Leistung" + }, + { + "token": "YIELD_DAY", + "en": "Yield Day", + "de": "Tagesertrag" + }, + { + "token": "YIELD_TOTAL", + "en": "Yield Total", + "de": "Gesamtertrag" + }, + { + "token": "MAX_POWER", + "en": "Max Power", + "de": "Maximale Leistung" + }, + { + "token": "DC_POWER", + "en": "DC Power", + "de": "DC Leistung" + }, + { + "token": "REACTIVE_POWER", + "en": "Reactive Power", + "de": "Blindleistung" + }, + { + "token": "ACTIVE_POWER_CONTROL", + "en": "Active Power Control", + "de": "Leistungsbegrenzung" + }, + { + "token": "APC", + "en": "APC", + "de": "Begr." + }, + { + "token": "ALARMS", + "en": "Alarms", + "de": "Meldungen" + }, + { + "token": "MAX_AC_POWER", + "en": "Max AC Power", + "de": "Max AC Leistung" + }, + { + "token": "DC_POWER", + "en": "DC Power", + "de": "DC Leistung" + }, + { + "token": "DC_VOLTAGE", + "en": "DC Voltage", + "de": "DC Spannung" + }, + { + "token": "AC_CURRENT", + "en": "AC Current", + "de": "AC Strom" + }, + { + "token": "FREQUENCY", + "en": "Frequency", + "de": "Frequenz" + }, + { + "token": "EFFICIENCY", + "en": "Efficiency", + "de": "Effizienz" + }, + { + "token": "POWER_FACTOR", + "en": "Power Factor", + "de": "Leistungsfaktor" + }, + { + "token": "IRRADIATION", + "en": "Irradiation", + "de": "Einstrahlung" + }, + { + "token": "DC_CURRENT", + "en": "DC Current", + "de": "DC Strom" + }, + { + "token": "LAST_RECEIVED", + "en": "Last received data requested at", + "de": "Zuletzt empfangene Daten wurden angefragt um" + }, + { + "token": "NOTHING_RECEIVED", + "en": "nothing received", + "de": "nichts empfangen" + }, + { + "token": "ALARMS_MODAL", + "en": "Alarms of inverter", + "de": "Meldungen von Wechselrichter" + }, + { + "token": "SERIAL", + "en": "Serial", + "de": "Seriennummer" + }, + { + "token": "BTN_SHOW", + "en": "show", + "de": "anzeigen" + }, + { + "token": "EVENT", + "en": "Event", + "de": "Ereignis" + }, + { + "token": "END", + "en": "End", + "de": "Ende" + }, + { + "token": "HW_NUMBER", + "en": "Hardware Number", + "de": "Hardware Nummer" + }, + { + "token": "INV_INFO", + "en": "Info for inverter", + "de": "Info für Wechselrichter" + }, + { + "token": "VALUE", + "en": "Value", + "de": "Wert" + }, + { + "token": "RANGE", + "en": "Range", + "de": "Bereich" + }, + { + "token": "DEFAULT", + "en": "Default", + "de": "Standard" + }, + { + "token": "PROFILE_NOT_READ", + "en": "Profile was not read until now, maybe turned off", + "de": "Grid-Profil wurde bis jetzt noch nicht gelesen oder das Auslesen ist nicht aktiv" + }, + { + "token": "UNKNOWN_PROFILE", + "en": "Unknown Profile", + "de": "Unbekanntes Profil" + }, + { + "token": "OPEN_ISSUE", + "en": "Please open a new issue at https://github.com/lumapu/ahoy and copy the raw data into it", + "de": "Bitte erstelle einen neuen Issue auf https://github.com/lumapu/ahoy und kopiere die Rohdaten hinein" + }, + { + "token": "PROFILE_MODAL", + "en": "Grid Profile for inverter", + "de": "Grid-Profil für Wechselrichter" + }, + { + "token": "TX_COUNT", + "en": "TX count", + "de": "Sendezähler" + }, + { + "token": "RX_SUCCESS", + "en": "RX success", + "de": "erfolgreicher Empfang" + }, + { + "token": "RX_FAIL", + "en": "RX fail", + "de": "fehlgeschlagener Empfang" + }, + { + "token": "RX_NO_ANSWER", + "en": "RX no answer", + "de": "keine Antwort" + }, + { + "token": "RX_FRAGMENTS", + "en": "RX fragments", + "de": "empfangene Fragmente" + }, + { + "token": "TX_RETRANSMITS", + "en": "TX retransmits", + "de": "erneute Sendeversuche" + }, + { + "token": "INV_LOSS_RATE", + "en": "Inverter loss rate", + "de": "Wechselrichter Empfangsqualität" + }, + { + "token": "DTU_LOSS_RATE", + "en": "DTU loss rate", + "de": "DTU Empfangsqualität" + }, + { + "token": "RADIO_STAT_MODAL", + "en": "Radio statistics for inverter", + "de": "Funkstatistik für Wechselrichter" + }, + { + "token": "LOST_1", + "en": "lost", + "de": "" + }, + { + "token": "LOST_2", + "en": "of", + "de": "von" + }, + { + "token": "LOST_3", + "en": "", + "de": "verloren" + }, + { + "token": "LIMIT_VALUE", + "en": "Limit Value", + "de": "Wert der Leistungsbegrenzung" + }, + { + "token": "KEEP_LIMIT", + "en": "Keep limit over inverter restart", + "de": "Leistungsbegrenzung dauerhaft speichern (im Wechselrichter)" + }, + { + "token": "BTN_APPLY", + "en": "Apply", + "de": "anwenden" + }, + { + "token": "CONTROL", + "en": "Control", + "de": "Ansteuern" + }, + { + "token": "RESTART", + "en": "restart", + "de": "neustarten" + }, + { + "token": "TURN_OFF", + "en": "turn off", + "de": "ausschalten" + }, + { + "token": "TURN_ON", + "en": "turn on", + "de": "anschalten" + }, + { + "token": "RESULT", + "en": "Result", + "de": "Status" + }, + { + "token": "POWER_LIMIT_MODAL", + "en": "Active Power Control for inverter", + "de": "Leistungsbegrenzung für Wechselrichter" + }, + { + "token": "CMD_RECEIVED_WAIT_ACK", + "en": "received command, waiting for inverter acknowledge ...", + "de": "Befehl erhalten, warte auf Bestäigung von Wechselrichter ..." + }, + { + "token": "COMMAND_RECEIVED", + "en": "command received", + "de": "Befehl erhalten" + }, + { + "token": "ERROR", + "en": "Error", + "de": "Fehler" + }, + { + "token": "INV_ACK", + "en": "inverter acknowledged active power control command", + "de": "Wechselrichter hat die Leistungsbegrenzung akzeptiert" + } + ] + }, + { + "name": "save.html", + "list": [ + { + "token": "SAVE_SETTINGS", + "en": "Saving settings...", + "de": "Einstellungen werden gespeichert ..." + }, + { + "token": "SUCCESS_SAVED_RELOAD", + "en": "Settings successfully saved. Automatic page reload in 3 seconds.", + "de": "Einstellungen erfolgreich gespeichert. Automatische Weiterleitung in 3 Sekunden" + }, + { + "token": "SUCCESS_SAVED_REBOOT", + "en": "Settings successfully saved. Rebooting. Automatic redirect in", + "de": "Einstellungen erfolgreich gespeichert. Automatische Weiterleitung nach Reboot in" + }, + { + "token": "SECONDS", + "en": "seconds", + "de": "Sekunden" + }, + { + "token": "FAILED_SAVE", + "en": "Failed saving settings", + "de": "Fehler beim Speichern" + } + ] + } + ] +}