Compare commits

..

20 Commits

Author SHA1 Message Date
Yurii 54217fbbc1 chore(git): fix update notes for dev release 2026-05-21 13:37:51 +03:00
Yurii 69116aa3d2 chore(git): fix update of dev tag 2026-05-21 13:27:38 +03:00
Yurii bb6b3f1854 chore(git): update build workflow 2026-05-21 13:16:30 +03:00
Yurii a4ca20f9a9 fix(portal): fix i18n name for extPump.invertState 2026-05-19 23:25:24 +03:00
Weblate (bot) f5e08f20a2 refactor(translation): update translation files (#249)
* feat(translation): added Estonian language

* refactor(translation): updated Chinese (Simplified Han script) language

Currently translated at 100.0% (355 of 355 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/zh_Hans/

* refactor(translation): updated Russian language

Currently translated at 100.0% (355 of 355 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/ru/

* refactor(translation): updated Dutch language

Currently translated at 100.0% (355 of 355 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/nl/

* refactor(translation): updated Italian language

Currently translated at 100.0% (355 of 355 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/it/

---------

Co-authored-by: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>
Co-authored-by: Yurii <34578544+Laxilef@users.noreply.github.com>
2026-05-19 21:55:14 +03:00
Yurii 07650b4878 refactor: improved dashboard & restore settings 2026-05-19 21:39:29 +03:00
Yurii ffcacb3d48 fix: translation for volume unit 2026-05-19 19:42:29 +03:00
Yurii bdfa793c49 chore: update build workflow 2026-05-18 00:36:30 +03:00
Yurii 25280bf9b1 chore: added build workflow 2026-05-17 19:32:27 +03:00
Yurii 9c2e420ec7 refactor: use localtime_r(...) for thread safety 2026-05-17 04:46:47 +03:00
Yurii af2d4624b3 chore: fix cpplint 2026-05-17 04:38:53 +03:00
Yurii 14d2262d2f chore: added CI workflow 2026-05-17 03:57:59 +03:00
Yurii db99746ee9 feat: added choice of averaging type for indoor and outdoor temperatures (#244)
* feat: added a choice of averaging type for indoor and outdoor temperatures

* refactor: locales updated

* refactor: some changes
2026-05-16 17:58:13 +03:00
Yurii bfc1cc1118 chore: update README 2026-05-16 17:14:00 +03:00
Yurii 01a70b3288 refactor(translation): updated Dutch language
Currently translated at 100.0% (346 of 346 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/nl/
2026-05-16 16:40:25 +03:00
Yurii 5d053690c6 refactor(translation): updated Chinese (Simplified Han script) language
Currently translated at 100.0% (346 of 346 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/zh_Hans/
2026-05-16 16:40:25 +03:00
Yurii ea95b5fa7d refactor(translation): updated Italian language
Currently translated at 100.0% (346 of 346 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/it/
2026-05-16 16:40:25 +03:00
Yurii dd9cb0b88f refactor(translation): updated English language
Currently translated at 100.0% (346 of 346 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/en/
2026-05-16 16:40:25 +03:00
Weblate (bot) ec7a24ce0c refactor(translation): updated Russian language (#245)
Currently translated at 100.0% (346 of 346 strings)

Translation: OTGateway/Portal
Translate-URL: https://hosted.weblate.org/projects/otgateway/portal/ru/

Co-authored-by: Yurii <laxilef@gmail.com>
2026-05-16 16:00:23 +03:00
Yurii 44fb0afffa refactor: reformat locales 2026-05-16 13:42:28 +03:00
39 changed files with 1419 additions and 705 deletions
+169
View File
@@ -0,0 +1,169 @@
name: Build
on:
workflow_dispatch:
push:
paths-ignore:
- "assets/**"
- "tools/**"
- "**/*.md"
- "LICENSE"
pull_request:
paths-ignore:
- "assets/**"
- "tools/**"
- "**/*.md"
- "LICENSE"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
prepare:
name: Prepare (get envs)
runs-on: ubuntu-latest
outputs:
envs: ${{ steps.envs.outputs.envs }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Python
uses: actions/setup-python@v6
with:
python-version: "3.x"
- name: Install PlatformIO Core
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
pip install --upgrade intelhex
- name: Get environments
id: envs
shell: bash
run: |
ENVS=$(
pio project config --json-output \
| jq -c '
.[]
| select(type == "array")
| .[0]
| select(startswith("env:"))
| sub("^env:"; "")
' | jq -s -c '.'
)
echo "envs=$ENVS" >> "$GITHUB_OUTPUT"
echo "$ENVS"
build:
name: Build ${{ matrix.env }}
runs-on: ubuntu-latest
needs: prepare
strategy:
fail-fast: false
matrix:
env: ${{ fromJson(needs.prepare.outputs.envs) }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Python
uses: actions/setup-python@v6
with:
python-version: "3.x"
- name: Install Node.js
uses: actions/setup-node@v5
with:
node-version: "22"
- name: Install PlatformIO Core
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
pip install --upgrade intelhex
- name: Full clean
run: |
pio run -e ${{ matrix.env }} -t fullclean
- name: Build firmware & filesystem
env:
PIOENV: ${{ matrix.env }}
run: |
pio run -e ${{ matrix.env }}
- name: Upload artifacts
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.env }}
if-no-files-found: error
path: |
build/*.bin
build/*.elf
release:
name: Release Dev
runs-on: ubuntu-latest
needs: build
permissions:
contents: write
if: ${{ github.repository_owner == 'Laxilef' && github.ref == 'refs/heads/async' }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Move dev tag
run: |
git fetch --tags
git tag -f dev
git push origin refs/tags/dev --force
- name: Download artifacts
uses: actions/download-artifact@v5
with:
path: artifacts
merge-multiple: true
- name: Delete old release assets
uses: mknejp/delete-release-assets@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: dev
fail-if-no-assets: false
fail-if-no-release: false
assets: |
*.bin
*.elf
- name: Clear release body
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release edit dev --notes ""
- name: Publish Dev release
uses: softprops/action-gh-release@v3
with:
tag_name: dev
target_commitish: async
name: Dev (unstable)
prerelease: true
make_latest: false
generate_release_notes: true
files: |
artifacts/*.bin
artifacts/*.elf
+110
View File
@@ -0,0 +1,110 @@
name: CI
on:
workflow_dispatch:
push:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
validate-json:
name: Validate JSON
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install jq
run: sudo apt-get update && sudo apt-get install -y jq
- name: Validate
shell: bash
run: |
set -euo pipefail
echo "Searching JSON files in src_data..."
files=$(find src_data -type f -iname "*.json")
if [ -z "$files" ]; then
echo "No JSON files found."
exit 0
fi
failed=0
for file in $files; do
echo "Validating: $file"
if ! jq empty "$file" >/dev/null 2>&1; then
echo "❌ Invalid JSON: $file"
failed=1
else
echo "✅ Valid JSON: $file"
fi
done
if [ "$failed" -ne 0 ]; then
echo ""
echo "JSON validation failed."
exit 1
fi
echo ""
echo "All JSON files are valid."
cpplint:
name: cpplint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Cache
uses: actions/cache@v5
with:
key: ${{ runner.os }}-cpplint
path: ~/.cache/pip
- name: Python
uses: actions/setup-python@v6
with:
python-version: "3.x"
- name: Install cpplint
run: |
python -m pip install --upgrade pip
pip install --upgrade cpplint
- name: cpplint
run: |
FILTERS="-legal/copyright,\
-runtime/int,\
-runtime/references,\
-runtime/indentation_namespace,\
-runtime/arrays,\
-runtime/printf,\
-whitespace/line_length,\
-whitespace/braces,\
-whitespace/comments,\
-whitespace/indent,\
-whitespace/newline,\
-whitespace/parens,\
-whitespace/blank_line,\
-readability/braces,\
-readability/todo,\
-readability/namespace,\
-build/header_guard,\
-build/include_subdir,\
-build/include_what_you_use,\
-build/namespaces,\
-build/c++11"
cpplint \
--repository=. \
--recursive \
--quiet \
--filter="$FILTERS" \
include lib src
+9
View File
@@ -2,6 +2,7 @@
[![GitHub version](https://img.shields.io/github/release/Laxilef/OTGateway.svg?include_prereleases)](https://github.com/Laxilef/OTGateway/releases)
[![GitHub download](https://img.shields.io/github/downloads/Laxilef/OTGateway/total.svg)](https://github.com/Laxilef/OTGateway/releases/latest)
[![Translations](https://hosted.weblate.org/widget/otgateway/svg-badge.svg)](https://hosted.weblate.org/engage/otgateway/)
[![License](https://img.shields.io/github/license/Laxilef/OTGateway.svg)](LICENSE.txt)
[![Telegram](https://img.shields.io/badge/Telegram-Channel-33A8E3)](https://t.me/otgateway)
@@ -71,6 +72,14 @@ All available information and instructions can be found in the wiki:
* [Connection](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#connection)
* [Leds on board](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#leds-on-board)
## Translations
The project uses [Weblate](https://hosted.weblate.org/new-lang/otgateway/) to manage translations of the interface.<br />
Anyone is welcome to add their native language as a [new localization](https://hosted.weblate.org/new-lang/otgateway/portal/). All contributions are welcome!
<a href="https://hosted.weblate.org/engage/otgateway/">
<img src="https://hosted.weblate.org/widget/otgateway/multi-auto.svg" alt="Translation status" />
</a>
## Gratitude
* To the developers of the libraries used: [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library), [ESP8266Scheduler](https://github.com/nrwiersma/ESP8266Scheduler), [ArduinoJson](https://github.com/bblanchon/ArduinoJson), [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino), [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient), [ESPTelnet](https://github.com/LennartHennigs/ESPTelnet), [FileData](https://github.com/GyverLibs/FileData), [GyverPID](https://github.com/GyverLibs/GyverPID), [GyverBlinker](https://github.com/GyverLibs/GyverBlinker), [OneWireNg](https://github.com/pstolarz/OneWireNg) & [OneWire](https://github.com/PaulStoffregen/OneWire)
* To the [PlatformIO](https://platformio.org/) Team
+2 -2
View File
@@ -1,9 +1,9 @@
class BufferedWebServer {
public:
BufferedWebServer(WebServer* webServer, size_t bufferSize = 64) {
explicit BufferedWebServer(WebServer* webServer, size_t bufferSize = 64) {
this->webServer = webServer;
this->bufferSize = bufferSize;
this->buffer = (uint8_t*)malloc(bufferSize * sizeof(*this->buffer));
this->buffer = static_cast<uint8_t*>(malloc(bufferSize * sizeof(*this->buffer)));
}
~BufferedWebServer() {
+1 -1
View File
@@ -7,7 +7,7 @@ public:
typedef std::function<void(unsigned long, uint8_t)> BeforeSendRequestCallback;
typedef std::function<void(unsigned long, unsigned long, OpenThermResponseStatus, uint8_t)> AfterSendRequestCallback;
CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false, bool alwaysReceive = false) : OpenTherm(inPin, outPin, isSlave, alwaysReceive) {}
explicit CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false, bool alwaysReceive = false) : OpenTherm(inPin, outPin, isSlave, alwaysReceive) {}
~CustomOpenTherm() {}
CustomOpenTherm* setDelayCallback(DelayCallback callback = nullptr) {
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#ifndef PROGMEM
#define PROGMEM
#define PROGMEM // NOLINT
#endif
const char HA_ENTITY_BINARY_SENSOR[] PROGMEM = "binary_sensor";
+2 -2
View File
@@ -12,10 +12,10 @@ public:
typedef std::function<void(const char*, size_t, size_t, bool)> PublishEventCallback;
typedef std::function<void(size_t, size_t)> FlushEventCallback;
MqttWriter(MqttClient* client, size_t bufferSize = 64) {
explicit MqttWriter(MqttClient* client, size_t bufferSize = 64) {
this->client = client;
this->bufferSize = bufferSize;
this->buffer = (uint8_t*) malloc(bufferSize * sizeof(*this->buffer));
this->buffer = static_cast<uint8_t*>(malloc(bufferSize * sizeof(*this->buffer)));
#ifdef ARDUINO_ARCH_ESP32
this->mutex = new std::mutex();
-224
View File
@@ -1,224 +0,0 @@
#include <FS.h>
class DynamicPage : public RequestHandler {
public:
typedef std::function<bool(HTTPMethod, const String&)> CanHandleCallback;
typedef std::function<bool()> BeforeSendCallback;
typedef std::function<String(const char*)> TemplateCallback;
DynamicPage(const char* uri, FS* fs, const char* path, const char* cacheHeader = nullptr) {
this->uri = uri;
this->fs = fs;
this->path = path;
this->cacheHeader = cacheHeader;
}
DynamicPage* setCanHandleCallback(CanHandleCallback callback = nullptr) {
this->canHandleCallback = callback;
return this;
}
DynamicPage* setBeforeSendCallback(BeforeSendCallback callback = nullptr) {
this->beforeSendCallback = callback;
return this;
}
DynamicPage* setTemplateCallback(TemplateCallback callback = nullptr) {
this->templateCallback = callback;
return this;
}
#if defined(ARDUINO_ARCH_ESP32)
bool canHandle(WebServer &server, HTTPMethod method, const String &uri) override {
return this->canHandle(method, uri);
}
#endif
bool canHandle(HTTPMethod method, const String& uri) override {
return uri.equals(this->uri) && (!this->canHandleCallback || this->canHandleCallback(method, uri));
}
bool handle(WebServer& server, HTTPMethod method, const String& uri) override {
if (!this->canHandle(method, uri)) {
return false;
}
if (this->beforeSendCallback && !this->beforeSendCallback()) {
return true;
}
File file = this->fs->open(this->path, "r");
if (!file) {
return false;
} else if (file.isDirectory()) {
file.close();
return false;
}
if (this->cacheHeader != nullptr) {
server.sendHeader(F("Cache-Control"), this->cacheHeader);
}
#ifdef ARDUINO_ARCH_ESP8266
if (!server.chunkedResponseModeStart(200, F("text/html"))) {
server.send(505, F("text/html"), F("HTTP1.1 required"));
return true;
}
#else
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", emptyString);
#endif
uint8_t* argStartPos = nullptr;
uint8_t* argEndPos = nullptr;
uint8_t argName[16];
size_t sizeArgName = 0;
bool argNameProcess = false;
while (file.available()) {
uint8_t buf[64];
size_t length = file.read(buf, sizeof(buf));
size_t offset = 0;
if (argNameProcess) {
argEndPos = (uint8_t*) memchr(buf, '}', length);
if (argEndPos != nullptr) {
size_t fullSizeArgName = sizeArgName + (argEndPos - buf);
if (fullSizeArgName < sizeof(argName)) {
// copy full arg name
if (argEndPos - buf > 0) {
memcpy(argName + sizeArgName, buf, argEndPos - buf);
}
argName[fullSizeArgName] = '\0';
// send arg value
String argValue = this->templateCallback((const char*) argName);
if (argValue.length()) {
server.sendContent(argValue.c_str());
} else if (fullSizeArgName > 0) {
server.sendContent("{");
server.sendContent((const char*) argName);
server.sendContent("}");
}
offset = size_t(argEndPos - buf + 1);
sizeArgName = 0;
argNameProcess = false;
}
}
if (argNameProcess) {
server.sendContent("{");
if (sizeArgName > 0) {
argName[sizeArgName] = '\0';
server.sendContent((const char*) argName);
}
argNameProcess = false;
}
}
do {
uint8_t* currentBuf = buf + offset;
size_t currentLength = length - offset;
argStartPos = (uint8_t*) memchr(currentBuf, '{', currentLength);
// send all content
if (argStartPos == nullptr) {
if (currentLength > 0) {
server.sendContent((const char*) currentBuf, currentLength);
}
break;
}
argEndPos = (uint8_t*) memchr(argStartPos, '}', length - (argStartPos - buf));
if (argEndPos != nullptr) {
sizeArgName = argEndPos - argStartPos - 1;
// send all content if arg len > space
if (sizeArgName >= sizeof(argName)) {
if (currentLength > 0) {
server.sendContent((const char*) currentBuf, currentLength);
}
break;
}
// arg name
memcpy(argName, argStartPos + 1, sizeArgName);
argName[sizeArgName] = '\0';
// send arg value
String argValue = this->templateCallback((const char*) argName);
if (argValue.length()) {
// send content before var
if (argStartPos - buf > 0) {
server.sendContent((const char*) currentBuf, argStartPos - buf);
}
server.sendContent(argValue.c_str());
} else {
server.sendContent((const char*) currentBuf, argEndPos - currentBuf + 1);
}
offset = size_t(argEndPos - currentBuf + 1);
} else {
sizeArgName = length - size_t(argStartPos - currentBuf) - 1;
// send all content if arg len > space
if (sizeArgName >= sizeof(argName)) {
if (currentLength) {
server.sendContent((const char*) currentBuf, currentLength);
}
break;
}
// send content before var
if (argStartPos - buf > 0) {
server.sendContent((const char*) currentBuf, argStartPos - buf);
}
// copy arg name chunk
if (sizeArgName > 0) {
memcpy(argName, argStartPos + 1, sizeArgName);
}
argNameProcess = true;
break;
}
} while(true);
}
file.close();
#ifdef ARDUINO_ARCH_ESP8266
server.chunkedResponseFinalize();
#else
server.sendContent(emptyString);
#endif
return true;
}
protected:
FS* fs = nullptr;
CanHandleCallback canHandleCallback;
BeforeSendCallback beforeSendCallback;
TemplateCallback templateCallback;
const char* uri = nullptr;
const char* path = nullptr;
const char* cacheHeader = nullptr;
};
+5 -5
View File
@@ -29,9 +29,9 @@ public:
typedef std::function<bool(AsyncWebServerRequest *request, UpgradeType)> BeforeUpgradeCallback;
typedef std::function<void(AsyncWebServerRequest *request, const UpgradeResult&, const UpgradeResult&)> AfterUpgradeCallback;
UpgradeHandler(AsyncURIMatcher uri) : uri(uri) {}
explicit UpgradeHandler(AsyncURIMatcher uri) : uri(uri) {}
bool canHandle(AsyncWebServerRequest *request) const override final {
bool canHandle(AsyncWebServerRequest *request) const final {
if (!request->isHTTP()) {
return false;
}
@@ -51,7 +51,7 @@ public:
return this;
}
void handleRequest(AsyncWebServerRequest *request) override final {
void handleRequest(AsyncWebServerRequest *request) final {
if (this->afterUpgradeCallback) {
this->afterUpgradeCallback(request, this->firmwareResult, this->filesystemResult);
}
@@ -63,7 +63,7 @@ public:
this->filesystemResult.error.clear();
}
void handleUpload(AsyncWebServerRequest *request, const String &fileName, size_t index, uint8_t *data, size_t dataLength, bool isFinal) override final {
void handleUpload(AsyncWebServerRequest *request, const String &fileName, size_t index, uint8_t *data, size_t dataLength, bool isFinal) final {
UpgradeResult* result = nullptr;
if (!request->hasParam(asyncsrv::T_name, true, true)) {
@@ -194,7 +194,7 @@ public:
}
}
bool isRequestHandlerTrivial() const override final {
bool isRequestHandlerTrivial() const final {
return false;
}
+4 -4
View File
@@ -35,9 +35,9 @@ namespace CrashRecorder {
} ext_t;
__NOINIT_ATTR volatile static backtrace_t backtrace;
__NOINIT_ATTR volatile static epc_t epc;
__NOINIT_ATTR volatile static ext_t ext;
__NOINIT_ATTR volatile static backtrace_t backtrace; // NOLINT
__NOINIT_ATTR volatile static epc_t epc; // NOLINT
__NOINIT_ATTR volatile static ext_t ext; // NOLINT
uint8_t backtraceMaxLength = sizeof(backtrace.data) / sizeof(*backtrace.data);
uint8_t epcMaxLength = sizeof(epc.data) / sizeof(*epc.data);
@@ -98,7 +98,7 @@ extern "C" void custom_crash_callback(struct rst_info *info, uint32_t stack, uin
CrashRecorder::backtrace.continues = false;
uint32_t value;
for (uint32_t i = stack; i < stack_end; i += 4) {
value = *((uint32_t*) i);
value = *((uint32_t*) i); // NOLINT
// keep only addresses in code area
if ((value >= 0x40000000) && (value < 0x40300000)) {
+4 -4
View File
@@ -10,7 +10,7 @@ extern FileData fsNetworkSettings, fsSettings, fsSensorsSettings;
class MainTask : public Task {
public:
MainTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {
explicit MainTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {
this->blinker = new Blinker();
network->setDelayCallback([this](unsigned int time) {
@@ -225,7 +225,7 @@ protected:
uint8_t availableSensors = 0;
if (Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) {
auto value = Sensors::getMeanValueByPurpose(Sensors::Purpose::INDOOR_TEMP, Sensors::ValueType::PRIMARY);
auto value = Sensors::getMeanValueByPurpose(Sensors::Purpose::INDOOR_TEMP, Sensors::ValueType::PRIMARY, settings.heating.indoorTempAvgType);
if (value < lowTemp) {
lowTemp = value;
}
@@ -253,7 +253,7 @@ protected:
if (availableSensors) {
if (vars.master.heating.freezing) {
if (lowTemp - (float) settings.heating.freezeProtection.highTemp + 0.0001f >= 0.0f) {
if (lowTemp - static_cast<float>(settings.heating.freezeProtection.highTemp) + 0.0001f >= 0.0f) {
vars.master.heating.freezing = false;
Log.sinfoln(
@@ -264,7 +264,7 @@ protected:
}
} else {
if ((float) settings.heating.freezeProtection.lowTemp - lowTemp + 0.0001f >= 0.0f) {
if (static_cast<float>(settings.heating.freezeProtection.lowTemp) - lowTemp + 0.0001f >= 0.0f) {
vars.master.heating.freezing = true;
if (!settings.heating.enabled) {
+2 -2
View File
@@ -8,7 +8,7 @@ extern FileData fsSettings;
class MqttTask : public Task {
public:
MqttTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {
explicit MqttTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {
this->wifiClient = new MqttWiFiClient();
this->client = new MqttClient(this->wifiClient);
this->writer = new MqttWriter(this->client, 256);
@@ -443,7 +443,7 @@ protected:
} else if (payload[i] == 10) {
Log.print(F("\r\n> "));
} else {
Log.print((char) payload[i]);
Log.print(static_cast<char>(payload[i]));
}
}
Log.print(F("\r\n\n"));
+7 -7
View File
@@ -3,7 +3,7 @@ extern FileData fsSettings;
class OpenThermTask : public Task {
public:
OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
explicit OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
~OpenThermTask() {
delete this->instance;
@@ -1401,7 +1401,7 @@ protected:
);
if (vars.master.heating.overheat) {
if ((float) settings.heating.overheatProtection.lowTemp - highTemp + 0.0001f >= 0.0f) {
if (static_cast<float>(settings.heating.overheatProtection.lowTemp) - highTemp + 0.0001f >= 0.0f) {
vars.master.heating.overheat = false;
Log.sinfoln(
@@ -1411,7 +1411,7 @@ protected:
}
} else if (vars.slave.heating.active) {
if (highTemp - (float) settings.heating.overheatProtection.highTemp + 0.0001f >= 0.0f) {
if (highTemp - static_cast<float>(settings.heating.overheatProtection.highTemp) + 0.0001f >= 0.0f) {
vars.master.heating.overheat = true;
Log.swarningln(
@@ -1441,7 +1441,7 @@ protected:
);
if (vars.master.dhw.overheat) {
if ((float) settings.dhw.overheatProtection.lowTemp - highTemp + 0.0001f >= 0.0f) {
if (static_cast<float>(settings.dhw.overheatProtection.lowTemp) - highTemp + 0.0001f >= 0.0f) {
vars.master.dhw.overheat = false;
Log.sinfoln(
@@ -1451,7 +1451,7 @@ protected:
}
} else if (vars.slave.dhw.active) {
if (highTemp - (float) settings.dhw.overheatProtection.highTemp + 0.0001f >= 0.0f) {
if (highTemp - static_cast<float>(settings.dhw.overheatProtection.highTemp) + 0.0001f >= 0.0f) {
vars.master.dhw.overheat = true;
Log.swarningln(
@@ -2432,7 +2432,7 @@ protected:
return false;
}
const float value = (float) CustomOpenTherm::getInt(response);
const float value = CustomOpenTherm::getInt(response);
if (!isValidTemp(value, settings.opentherm.unitSystem, -40, 500)) {
return false;
}
@@ -2456,7 +2456,7 @@ protected:
return false;
}
const float value = (float) CustomOpenTherm::getInt(response);
const float value = CustomOpenTherm::getInt(response);
if (value <= 0) {
return false;
}
+7 -3
View File
@@ -18,7 +18,7 @@ extern WebSerial* webSerial;
class PortalTask : public LeanTask {
public:
PortalTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {
explicit PortalTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {
this->webServer = new AsyncWebServer(80);
this->dnsServer = new DNSServer();
}
@@ -810,9 +810,13 @@ protected:
static void getFilename(char* filename, size_t maxSizeFilename, const char* type) {
const time_t now = time(nullptr);
const tm* localNow = localtime(&now);
tm localNow {};
localtime_r(&now, &localNow);
char localNowValue[20];
strftime(localNowValue, sizeof(localNowValue), PSTR("%Y-%m-%d-%H-%M-%S"), localNow);
strftime(localNowValue, sizeof(localNowValue), PSTR("%Y-%m-%d-%H-%M-%S"), &localNow);
snprintf_P(filename, maxSizeFilename, PSTR("%s_%s_%s.json"), networkSettings.hostname, localNowValue, type);
}
};
+1 -1
View File
@@ -5,7 +5,7 @@ GyverPID pidRegulator(0, 0, 0);
class RegulatorTask : public LeanTask {
public:
RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
explicit RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
protected:
float prevHeatingTarget = 0.0f;
+47 -18
View File
@@ -77,6 +77,12 @@ public:
RSSI = 3
};
enum class AverageType : uint8_t {
MEAN = 0,
MINIMUM = 1,
MAXIMUM = 2
};
typedef struct {
bool enabled = false;
char name[33];
@@ -330,38 +336,61 @@ public:
return updated;
}
static float getMeanValueByPurpose(Purpose purpose, const ValueType valueType, bool onlyConnected = true) {
static float getMeanValueByPurpose(Purpose purpose, const ValueType valueType, const AverageType avgType = AverageType::MEAN, const bool onlyConnected = true, const float defaultValue = NAN) {
if (settings == nullptr || results == nullptr) {
return 0.0f;
return defaultValue;
}
uint8_t valueId = (uint8_t) valueType;
if (!isValidValueId(valueId)) {
return 0.0f;
return defaultValue;
}
float value = 0.0f;
uint8_t amount = 0;
for (uint8_t id = 0; id <= getMaxSensorId(); id++) {
auto& sSensor = settings[id];
auto& rSensor = results[id];
if (avgType == AverageType::MEAN) {
float sum = 0.0f;
for (uint8_t id = 0; id <= getMaxSensorId(); id++) {
auto& sSensor = settings[id];
auto& rSensor = results[id];
if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) {
value += rSensor.values[valueId];
amount++;
if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) {
sum += rSensor.values[valueId];
amount++;
}
}
value = amount == 1 ? sum : (sum / amount);
} else if (avgType == AverageType::MINIMUM) {
for (uint8_t id = 0; id <= getMaxSensorId(); id++) {
auto& sSensor = settings[id];
auto& rSensor = results[id];
if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) {
if (amount == 0 || rSensor.values[valueId] < value) {
value = rSensor.values[valueId];
amount++;
}
}
}
} else if (avgType == AverageType::MAXIMUM) {
for (uint8_t id = 0; id <= getMaxSensorId(); id++) {
auto& sSensor = settings[id];
auto& rSensor = results[id];
if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) {
if (amount == 0 || rSensor.values[valueId] > value) {
value = rSensor.values[valueId];
amount++;
}
}
}
}
if (!amount) {
return 0.0f;
} else if (amount == 1) {
return value;
} else {
return value / amount;
}
return amount > 0 ? value : defaultValue;
}
static bool existsConnectedSensorsByPurpose(Purpose purpose) {
+47 -11
View File
@@ -74,7 +74,7 @@ public:
}
static bool parseAtcData(const NimBLEAdvertisedDevice* device, uint8_t sensorId) {
NimBLEUUID serviceUuid((uint16_t) 0x181A);
NimBLEUUID serviceUuid(static_cast<uint16_t>(0x181A));
auto serviceData = device->getServiceData(serviceUuid);
if (!serviceData.size()) {
@@ -125,7 +125,7 @@ public:
}
static bool parsePvvxData(const NimBLEAdvertisedDevice* device, uint8_t sensorId) {
NimBLEUUID serviceUuid((uint16_t) 0x181A);
NimBLEUUID serviceUuid(static_cast<uint16_t>(0x181A));
auto serviceData = device->getServiceData(serviceUuid);
if (!serviceData.size()) {
@@ -178,7 +178,7 @@ public:
}
static bool parseBTHomeData(const NimBLEAdvertisedDevice* device, uint8_t sensorId) {
NimBLEUUID serviceUuid((uint16_t) 0xFCD2);
NimBLEUUID serviceUuid(static_cast<uint16_t>(0xFCD2));
auto serviceData = device->getServiceData(serviceUuid);
if (!serviceData.size()) {
@@ -304,7 +304,7 @@ public:
class SensorsTask : public LeanTask {
public:
SensorsTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {
explicit SensorsTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {
this->gpioLastPollingTime.reserve(2);
// OneWire
@@ -423,14 +423,50 @@ protected:
}
void updateMasterValues() {
vars.master.heating.outdoorTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::OUTDOOR_TEMP, Sensors::ValueType::PRIMARY);
vars.master.heating.indoorTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::INDOOR_TEMP, Sensors::ValueType::PRIMARY);
vars.master.heating.indoorTemp = Sensors::getMeanValueByPurpose(
Sensors::Purpose::INDOOR_TEMP,
Sensors::ValueType::PRIMARY,
settings.heating.indoorTempAvgType,
true,
0.0f
);
vars.master.heating.outdoorTemp = Sensors::getMeanValueByPurpose(
Sensors::Purpose::OUTDOOR_TEMP,
Sensors::ValueType::PRIMARY,
settings.heating.outdoorTempAvgType,
true,
0.0f
);
vars.master.heating.currentTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::HEATING_TEMP, Sensors::ValueType::PRIMARY);
vars.master.heating.returnTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::HEATING_RETURN_TEMP, Sensors::ValueType::PRIMARY);
vars.master.heating.currentTemp = Sensors::getMeanValueByPurpose(
Sensors::Purpose::HEATING_TEMP,
Sensors::ValueType::PRIMARY,
Sensors::AverageType::MEAN,
true,
0.0f
);
vars.master.heating.returnTemp = Sensors::getMeanValueByPurpose(
Sensors::Purpose::HEATING_RETURN_TEMP,
Sensors::ValueType::PRIMARY,
Sensors::AverageType::MEAN,
true,
0.0f
);
vars.master.dhw.currentTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::DHW_TEMP, Sensors::ValueType::PRIMARY);
vars.master.dhw.returnTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::DHW_RETURN_TEMP, Sensors::ValueType::PRIMARY);
vars.master.dhw.currentTemp = Sensors::getMeanValueByPurpose(
Sensors::Purpose::DHW_TEMP,
Sensors::ValueType::PRIMARY,
Sensors::AverageType::MEAN,
true,
0.0f
);
vars.master.dhw.returnTemp = Sensors::getMeanValueByPurpose(
Sensors::Purpose::DHW_RETURN_TEMP,
Sensors::ValueType::PRIMARY,
Sensors::AverageType::MEAN,
true,
0.0f
);
}
void makeDallasInstances() {
@@ -794,7 +830,7 @@ protected:
}
const float sensorResistance = value > 1
? DEFAULT_NTC_REF_RESISTANCE / (DEFAULT_NTC_VREF / (float) value - 1.0f)
? DEFAULT_NTC_REF_RESISTANCE / (DEFAULT_NTC_VREF / static_cast<float>(value) - 1.0f)
: 0.0f;
const float rawTemp = 1.0f / (
1.0f / (DEFAULT_NTC_NOMINAL_TEMP + 273.15f) +
+2
View File
@@ -107,6 +107,8 @@ struct Settings {
uint8_t minTemp = DEFAULT_HEATING_MIN_TEMP;
uint8_t maxTemp = DEFAULT_HEATING_MAX_TEMP;
uint8_t maxModulation = 100;
Sensors::AverageType indoorTempAvgType = Sensors::AverageType::MEAN;
Sensors::AverageType outdoorTempAvgType = Sensors::AverageType::MEAN;
struct {
bool enabled = true;
+1 -1
View File
@@ -147,7 +147,7 @@
#endif
#ifndef PROGMEM
#define PROGMEM
#define PROGMEM // NOLINT
#endif
#ifdef ARDUINO_ARCH_ESP32
+3 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#ifndef PROGMEM
#define PROGMEM
#define PROGMEM // NOLINT
#endif
const char L_SETTINGS[] PROGMEM = "SETTINGS";
@@ -115,6 +115,7 @@ const char S_IGNORE_DIAG_STATE[] PROGMEM = "ignoreDiagState";
const char S_IMMERGAS_FIX[] PROGMEM = "immergasFix";
const char S_ALWAYS_SEND_INDOOR_TEMP[] PROGMEM = "alwaysSendIndoorTemp";
const char S_INDOOR_TEMP[] PROGMEM = "indoorTemp";
const char S_INDOOR_TEMP_AVG_TYPE[] PROGMEM = "indoorTempAvgType";
const char S_INDOOR_TEMP_CONTROL[] PROGMEM = "indoorTempControl";
const char S_IN_GPIO[] PROGMEM = "inGpio";
const char S_INPUT[] PROGMEM = "input";
@@ -155,6 +156,7 @@ const char S_ON_LOSS_CONNECTION[] PROGMEM = "onLossConnection"
const char S_OPENTHERM[] PROGMEM = "opentherm";
const char S_OPTIONS[] PROGMEM = "options";
const char S_OUTDOOR_TEMP[] PROGMEM = "outdoorTemp";
const char S_OUTDOOR_TEMP_AVG_TYPE[] PROGMEM = "outdoorTempAvgType";
const char S_OUT_GPIO[] PROGMEM = "outGpio";
const char S_OUTPUT[] PROGMEM = "output";
const char S_OVERHEAT[] PROGMEM = "overheat";
+40 -2
View File
@@ -139,7 +139,7 @@ inline bool isValidTemp(const float value, UnitSystem unit, const float min = 0.
float roundf(float value, uint8_t decimals = 2) {
if (decimals == 0) {
return (int)(value + 0.5f);
return static_cast<int>(value + 0.5f);
} else if (abs(value) < 0.00000001f) {
return 0.0f;
@@ -147,7 +147,7 @@ float roundf(float value, uint8_t decimals = 2) {
float multiplier = pow10(decimals);
value += 0.5f / multiplier * (value < 0.0f ? -1.0f : 1.0f);
return (int)(value * multiplier) / multiplier;
return static_cast<int>(value * multiplier) / multiplier;
}
inline size_t getTotalHeap() {
@@ -498,6 +498,8 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
heating[FPSTR(S_MIN_TEMP)] = src.heating.minTemp;
heating[FPSTR(S_MAX_TEMP)] = src.heating.maxTemp;
heating[FPSTR(S_MAX_MODULATION)] = src.heating.maxModulation;
heating[FPSTR(S_INDOOR_TEMP_AVG_TYPE)] = static_cast<uint8_t>(src.heating.indoorTempAvgType);
heating[FPSTR(S_OUTDOOR_TEMP_AVG_TYPE)] = static_cast<uint8_t>(src.heating.outdoorTempAvgType);
auto heatingOverheatProtection = heating[FPSTR(S_OVERHEAT_PROTECTION)].to<JsonObject>();
heatingOverheatProtection[FPSTR(S_HIGH_TEMP)] = src.heating.overheatProtection.highTemp;
@@ -1393,6 +1395,42 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}
if (!src[FPSTR(S_HEATING)][FPSTR(S_INDOOR_TEMP_AVG_TYPE)].isNull()) {
uint8_t value = src[FPSTR(S_HEATING)][FPSTR(S_INDOOR_TEMP_AVG_TYPE)].as<uint8_t>();
switch (value) {
case static_cast<uint8_t>(Sensors::AverageType::MEAN):
case static_cast<uint8_t>(Sensors::AverageType::MINIMUM):
case static_cast<uint8_t>(Sensors::AverageType::MAXIMUM):
if (static_cast<uint8_t>(dst.heating.indoorTempAvgType) != value) {
dst.heating.indoorTempAvgType = static_cast<Sensors::AverageType>(value);
changed = true;
}
break;
default:
break;
}
}
if (!src[FPSTR(S_HEATING)][FPSTR(S_OUTDOOR_TEMP_AVG_TYPE)].isNull()) {
uint8_t value = src[FPSTR(S_HEATING)][FPSTR(S_OUTDOOR_TEMP_AVG_TYPE)].as<uint8_t>();
switch (value) {
case static_cast<uint8_t>(Sensors::AverageType::MEAN):
case static_cast<uint8_t>(Sensors::AverageType::MINIMUM):
case static_cast<uint8_t>(Sensors::AverageType::MAXIMUM):
if (static_cast<uint8_t>(dst.heating.outdoorTempAvgType) != value) {
dst.heating.outdoorTempAvgType = static_cast<Sensors::AverageType>(value);
changed = true;
}
break;
default:
break;
}
}
if (!src[FPSTR(S_HEATING)][FPSTR(S_OVERHEAT_PROTECTION)][FPSTR(S_HIGH_TEMP)].isNull()) {
unsigned char value = src[FPSTR(S_HEATING)][FPSTR(S_OVERHEAT_PROTECTION)][FPSTR(S_HIGH_TEMP)].as<unsigned char>();
+33 -55
View File
@@ -19,9 +19,10 @@
"rpm": "RPM",
"ppm": "ppm",
"byte": "byte",
"mbyte": "MB"
"mbyte": "MB",
"liter": "升",
"gallon": "加仑"
},
"button": {
"upgrade": "固件升级",
"restart": "重启",
@@ -36,15 +37,12 @@
"success": "成功",
"error": "错误"
},
"index": {
"title": "OpenTherm Gateway",
"section": {
"network": "网络",
"system": "系统"
},
"system": {
"build": {
"title": "Build",
@@ -56,8 +54,8 @@
},
"uptime": "运行时间",
"memory": {
"heap": "Heap",
"psram": "Psram",
"heap": "内存 (heap)",
"psram": "内存 (PSRAM)",
"maxFreeBlock": "max free block",
"min": "min"
},
@@ -74,26 +72,22 @@
"lastResetReason": "上次重置原因"
}
},
"dashboard": {
"name": "仪表盘",
"title": "仪表盘 - OpenTherm Gateway",
"section": {
"control": "调节",
"states": "状态",
"sensors": "传感器",
"diag": "OpenTherm 诊断"
},
"thermostat": {
"heating": "供暖",
"dhw": "生活热水",
"temp.current": "当前温度",
"currentTemp": "当前温度",
"enable": "启用",
"turbo": "Turbo 模式"
},
"notify": {
"fault": {
"title": "锅炉报警状态已激活!",
@@ -105,7 +99,6 @@
},
"reset": "点击复位"
},
"states": {
"mNetworkConnected": "网络连接状态",
"mMqttConnected": "MQTT服务器连接状态",
@@ -113,7 +106,6 @@
"mExtPumpState": "外置循环泵",
"mCascadeControlInput": "Cascade 控制 (input)",
"mCascadeControlOutput": "Cascade 控制 (output)",
"sConnected": "OpenTherm 通讯状态",
"sFlame": "火焰",
"sCoolingActive": "制冷",
@@ -122,7 +114,6 @@
"sFaultCode": "报警代码",
"sDiagActive": "诊断状态",
"sDiagCode": "诊断代码",
"mHeatEnabled": "供暖功能已启用",
"mHeatBlocking": "供暖",
"mHeatOverheat": "供暖超热保护",
@@ -134,7 +125,6 @@
"mHeatRetTemp": "供暖回水温度",
"mHeatIndoorTemp": "供暖,室内温度",
"mHeatOutdoorTemp": "供暖,室外温度",
"mDhwEnabled": "生活热水功能已启用",
"mDhwOverheat": "生活热水超热保护",
"sDhwActive": "生活热水激活",
@@ -142,7 +132,6 @@
"mDhwCurrTemp": "生活热水当前出水温度",
"mDhwRetTemp": "生活热水回水温度"
},
"sensors": {
"values": {
"temp": "温度",
@@ -152,23 +141,19 @@
}
}
},
"network": {
"title": "网络 - OpenTherm Gateway",
"name": "网络设置",
"section": {
"static": "静态设置",
"availableNetworks": "可用网络",
"staSettings": "WiFi 设置",
"apSettings": "AP 设置"
},
"scan": {
"pos": "#",
"info": "Info"
},
"wifi": {
"ssid": "SSID",
"password": "密码",
@@ -176,7 +161,6 @@
"signal": "信号强度",
"connected": "已连接"
},
"params": {
"hostname": "Hostname",
"dhcp": "自动 (DHCP)",
@@ -186,16 +170,15 @@
"gateway": "网关",
"dns": "DNS 服务器"
},
"sta": {
"channel.note": "自动选择设置为0"
"channel": {
"note": "自动选择设置为0"
}
}
},
"sensors": {
"title": "传感器设置 - OpenTherm Gateway",
"name": "传感器设置",
"enabled": "启用",
"sensorName": {
"title": "传感器名称",
@@ -252,7 +235,6 @@
"otHeatingPumpHours": "OpenTherm, number of pump operating hours (heating)",
"otDhwPumpHours": "OpenTherm, number of pump operating hours (DHW)",
"otCoolingHours": "OpenTherm, number of cooling hours",
"ntcTemp": "NTC 传感器",
"dallasTemp": "DALLAS 传感器",
"bluetooth": "BLE 传感器",
@@ -284,11 +266,9 @@
}
}
},
"settings": {
"title": "设置 - OpenTherm Gateway",
"name": "设置",
"section": {
"portal": "Portal 设置",
"system": "系统设置",
@@ -303,18 +283,21 @@
"extPump": "外置循环泵设置",
"cascadeControl": "Cascade 级联控制设置"
},
"enable": "启用",
"note": {
"restart": "更改这些设置后,必须重启设备以使变更生效",
"blankNotUse": "空白 - 未使用",
"bleDevice": "BLE设备仅支持搭载BLE功能的特定ESP32开发板使用!"
},
"temp": {
"min": "最低温度",
"max": "最高温度"
},
"avgType": {
"mean": "平均温度",
"min": "最低温度",
"max": "最高温度"
},
"maxModulation": "最大调制范围",
"ohProtection": {
"title": "超温保护",
@@ -340,14 +323,12 @@
"note": "强制开启加热的阈值"
}
},
"portal": {
"login": "登录",
"password": "密码",
"auth": "需身份验证",
"mdns": "使用 mDNS"
},
"system": {
"unit": "单位",
"metric": "公制 <small>(摄氏度、升、巴)</small>",
@@ -367,7 +348,6 @@
"timezonePresets": "选择预设配置..."
}
},
"heating": {
"hyst": {
"title": "滞回",
@@ -379,19 +359,24 @@
"set0target": "设置空目标"
}
},
"turboFactor": "Turbo 模式系数"
"turboFactor": "Turbo 模式系数",
"indoorTempAvgType": {
"title": "室内温度平均类型",
"desc": "使用两个或更多室内温度传感器时可能有用(使用 «Equitherm» 和/或 «PID» 时)。"
},
"outdoorTempAvgType": {
"title": "室外温度平均类型",
"desc": "使用两个或更多室外温度传感器时可能有用(使用 «Equitherm» 时)。"
}
},
"emergency": {
"desc": "紧急模式会在以下情况自动激活(当PID或气候补偿无法计算热媒设定值时):<br />启用气候补偿但室外温度传感器断开连接;<br />启用PID或 OpenTherm 选项中启用<i>原生供暖控制</i>但室内温度传感器断开连接。<br /><b>注意:</b> 网络故障或MQTT 服务器连接故障时,类型为<i>通过MQTT/API手动控制<i>的传感器将显示为断开连接状态。",
"desc": "紧急模式会在以下情况自动激活(当PID或气候补偿无法计算热媒设定值时):<br />启用气候补偿但室外温度传感器断开连接;<br />启用PID或 OpenTherm 选项中启用<i>原生供暖控制</i>但室内温度传感器断开连接。<br /><b>注意:</b> 网络故障或MQTT 服务器连接故障时,类型为<i>通过MQTT/API手动控制</i>的传感器将显示为断开连接状态。",
"target": {
"title": "目标温度",
"note": "<b>重要提示:</b> 若启用OpenTherm选项 <i>«原生供暖控制»</i>,此处设定值为<u>目标室内温度</u><br />其他所有情况下,此处设定值为 <u>目标热媒出水温度</u>."
},
"treshold": "阈值时间 <small>(秒)</small>"
},
"equitherm": {
"slope": {
"title": "斜率",
@@ -415,7 +400,6 @@
"outdoorTemp": "室外温度"
}
},
"pid": {
"p": "P 系数",
"i": "I 系数",
@@ -435,7 +419,6 @@
"thresholdLow": "Threshold low"
}
},
"ot": {
"advanced": "高级设置",
"inGpio": "In GPIO",
@@ -449,9 +432,8 @@
},
"maxPower": {
"title": "最大锅炉功率 <small>(kW)</small>",
"note": "<b>0</b> - 自动检测,通常在锅炉参数设置中的\"最大有效热输出\"。 "
"note": "<b>0</b> - 自动检测,通常在锅炉参数设置中的\"最大有效热输出\"。"
},
"options": {
"title": "选项(附加设置)",
"desc": "附加设置选项可调整锅炉的运行逻辑。由于协议未完整记录所有选项,同一选项在不同锅炉上可能产生不同效果。<br /><b>注意:</b>若系统运行正常,无需修改设置。",
@@ -473,13 +455,11 @@
"immergasFix": "针对Immergas锅炉的兼容性修复",
"alwaysSendIndoorTemp": "向锅炉发送当前室内温度"
},
"nativeOTC": {
"title": "原生热载体温度计算模式",
"note": "仅在锅炉处于 OTC 模式时<u>才</u>工作:需要并接受目标室内温度,并基于内置曲线模式自行调节热载体温度。与 PID 和 Equitherm 不兼容。"
}
},
"mqtt": {
"homeAssistantDiscovery": "Home Assistant 自动发现",
"server": "服务器地址",
@@ -489,7 +469,6 @@
"prefix": "Prefix 前缀",
"interval": "发布间隔 <small>(秒)</small>"
},
"extPump": {
"use": "使用外置循环泵",
"gpio": "继电器 GPIO引脚",
@@ -498,7 +477,6 @@
"antiStuckInterval": "防卡死间隔时间<small>(天)</small>",
"antiStuckTime": "防卡死运行时长<small>(分钟)</small>"
},
"cascadeControl": {
"input": {
"desc": "仅当另一台锅炉发生故障时启用本锅炉加热。另一台锅炉的控制器需在故障发生时切换GPIO输入状态以触发本功能。",
@@ -522,23 +500,23 @@
}
}
},
"upgrade": {
"title": "固件升级 - OpenTherm Gateway",
"name": "固件升级",
"section": {
"backupAndRestore": "备份与恢复",
"backupAndRestore.desc": "本功能支持备份恢复全部设置",
"upgrade": "升级",
"upgrade.desc": "本模块支持升级设备的固件与系统文件。<br />可从以下地址下载最新版本 <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Releases page</a> 。"
"backupAndRestore": {
"title": "备份恢复",
"desc": "本功能支持备份和恢复全部设置"
},
"upgrade": {
"title": "升级",
"desc": "本模块支持升级设备的固件与系统文件。<br />可从以下地址下载最新版本 <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Releases page</a> 。"
}
},
"note": {
"disclaimer1": "升级系统文件成功后,所有设置将恢复为默认值!升级前请务必备份配置。",
"disclaimer2": "升级成功后,设备将在15秒后自动重启。"
},
"settingsFile": "设置文件",
"fw": "Firmware",
"fs": "Filesystem"
+31 -53
View File
@@ -19,9 +19,10 @@
"rpm": "RPM",
"ppm": "ppm",
"byte": "byte",
"mbyte": "MB"
"mbyte": "MB",
"liter": "L",
"gallon": "gal"
},
"button": {
"upgrade": "Upgrade",
"restart": "Restart",
@@ -36,15 +37,12 @@
"success": "Success",
"error": "Error"
},
"index": {
"title": "OpenTherm Gateway",
"section": {
"network": "Network",
"system": "System"
},
"system": {
"build": {
"title": "Build",
@@ -56,8 +54,8 @@
},
"uptime": "Uptime",
"memory": {
"heap": "Heap",
"psram": "Psram",
"heap": "Memory (heap)",
"psram": "Memory (PSRAM)",
"maxFreeBlock": "max free block",
"min": "min"
},
@@ -74,26 +72,22 @@
"lastResetReason": "Last reset reason"
}
},
"dashboard": {
"name": "Dashboard",
"title": "Dashboard - OpenTherm Gateway",
"section": {
"control": "Control",
"states": "States",
"sensors": "Sensors",
"diag": "OpenTherm diagnostic"
},
"thermostat": {
"heating": "Heating",
"dhw": "DHW",
"temp.current": "Current",
"currentTemp": "Current",
"enable": "Enable",
"turbo": "Turbo mode"
},
"notify": {
"fault": {
"title": "Boiler Fault state is active!",
@@ -105,7 +99,6 @@
},
"reset": "Try reset"
},
"states": {
"mNetworkConnected": "Network connection",
"mMqttConnected": "MQTT connection",
@@ -113,7 +106,6 @@
"mExtPumpState": "External pump",
"mCascadeControlInput": "Cascade control (input)",
"mCascadeControlOutput": "Cascade control (output)",
"sConnected": "OpenTherm connection",
"sFlame": "Flame",
"sCoolingActive": "Cooling",
@@ -122,7 +114,6 @@
"sFaultCode": "Fault code",
"sDiagActive": "Diagnostic",
"sDiagCode": "Diagnostic code",
"mHeatEnabled": "Heating enabled",
"mHeatBlocking": "Heating blocked",
"mHeatOverheat": "Heating overheat",
@@ -134,7 +125,6 @@
"mHeatRetTemp": "Heating return temp",
"mHeatIndoorTemp": "Heating, indoor temp",
"mHeatOutdoorTemp": "Heating, outdoor temp",
"mDhwEnabled": "DHW enabled",
"mDhwOverheat": "DHW overheat",
"sDhwActive": "DHW active",
@@ -142,7 +132,6 @@
"mDhwCurrTemp": "DHW current temp",
"mDhwRetTemp": "DHW return temp"
},
"sensors": {
"values": {
"temp": "Temperature",
@@ -152,23 +141,19 @@
}
}
},
"network": {
"title": "Network - OpenTherm Gateway",
"name": "Network settings",
"section": {
"static": "Static settings",
"availableNetworks": "Available networks",
"staSettings": "WiFi settings",
"apSettings": "AP settings"
},
"scan": {
"pos": "#",
"info": "Info"
},
"wifi": {
"ssid": "SSID",
"password": "Password",
@@ -176,7 +161,6 @@
"signal": "Signal",
"connected": "Connected"
},
"params": {
"hostname": "Hostname",
"dhcp": "Use DHCP",
@@ -186,16 +170,15 @@
"gateway": "Gateway",
"dns": "DNS"
},
"sta": {
"channel.note": "set 0 for auto select"
"channel": {
"note": "set 0 for auto select"
}
}
},
"sensors": {
"title": "Sensors settings - OpenTherm Gateway",
"name": "Sensors settings",
"enabled": "Enabled",
"sensorName": {
"title": "Sensor name",
@@ -252,7 +235,6 @@
"otHeatingPumpHours": "OpenTherm, number of pump operating hours (heating)",
"otDhwPumpHours": "OpenTherm, number of pump operating hours (DHW)",
"otCoolingHours": "OpenTherm, number of cooling hours",
"ntcTemp": "NTC sensor",
"dallasTemp": "DALLAS sensor",
"bluetooth": "BLE sensor",
@@ -284,11 +266,9 @@
}
}
},
"settings": {
"title": "Settings - OpenTherm Gateway",
"name": "Settings",
"section": {
"portal": "Portal settings",
"system": "System settings",
@@ -303,18 +283,21 @@
"extPump": "External pump settings",
"cascadeControl": "Cascade control settings"
},
"enable": "Enable",
"note": {
"restart": "After changing these settings, the device must be restarted for the changes to take effect.",
"blankNotUse": "blank - not use",
"bleDevice": "BLE device can be used <u>only</u> with some ESP32 boards with BLE support!"
},
"temp": {
"min": "Minimum temperature",
"max": "Maximum temperature"
},
"avgType": {
"mean": "Mean temperature",
"min": "Minimum temperature",
"max": "Maximum temperature"
},
"maxModulation": "Max modulation level",
"ohProtection": {
"title": "Overheating protection",
@@ -340,14 +323,12 @@
"note": "Threshold when heating is forced to turn on"
}
},
"portal": {
"login": "Login",
"password": "Password",
"auth": "Require authentication",
"mdns": "Use mDNS"
},
"system": {
"unit": "Unit system",
"metric": "Metric <small>(celsius, liters, bar)</small>",
@@ -367,7 +348,6 @@
"timezonePresets": "Select preset..."
}
},
"heating": {
"hyst": {
"title": "Hysteresis",
@@ -379,19 +359,24 @@
"set0target": "Set null target"
}
},
"turboFactor": "Turbo mode coeff."
"turboFactor": "Turbo mode coeff.",
"indoorTempAvgType": {
"title": "Indoor temp. averaging type",
"desc": "May be useful when using two or more indoor temp. sensors (when using «Equitherm» and/or «PID»)."
},
"outdoorTempAvgType": {
"title": "Outdoor temp. averaging type",
"desc": "May be useful when using two or more outdoor temp. sensors (when using «Equitherm»)."
}
},
"emergency": {
"desc": "Emergency mode is activated automatically when «PID» or «Equitherm» cannot calculate the heat carrier setpoint:<br />- if «Equitherm» is enabled and the outdoor temperature sensor is disconnected;<br />- if «PID» or OT option <i>«Native heating control»</i> is enabled and the indoor temperature sensor is disconnected.<br /><b>Note:</b> On network fault or MQTT fault, sensors with <i>«Manual via MQTT/API»</i> type will be in DISCONNECTED state.",
"target": {
"title": "Target temperature",
"note": "<b>Important:</b> <u>Target indoor temperature</u> if OT option <i>«Native heating control»</i> is enabled.<br />In all other cases, the <u>target heat carrier temperature</u>."
},
"treshold": "Treshold time <small>(sec)</small>"
},
"equitherm": {
"slope": {
"title": "Slope",
@@ -415,7 +400,6 @@
"outdoorTemp": "Outdoor temperature"
}
},
"pid": {
"p": "P factor",
"i": "I factor",
@@ -435,7 +419,6 @@
"thresholdLow": "Threshold low"
}
},
"ot": {
"advanced": "Advanced Settings",
"inGpio": "In GPIO",
@@ -451,7 +434,6 @@
"title": "Max boiler power <small>(kW)</small>",
"note": "<b>0</b> - try detect automatically. Typically found in the boiler specification as \"maximum useful heat output\"."
},
"options": {
"title": "Options (additional settings)",
"desc": "Options can change the logic of the boiler. Not all options are documented in the protocol, so the same option can have different effects on different boilers.<br /><b>Note:</b> There is no need to change anything if everything works well.",
@@ -473,13 +455,11 @@
"immergasFix": "Fix for Immergas boilers",
"alwaysSendIndoorTemp": "Send current indoor temp to boiler"
},
"nativeOTC": {
"title": "Native OTC mode",
"note": "Works <u>ONLY</u> if the boiler is in OTC mode: requires and accepts the target indoor temperature and self-regulates the heat carrier temperature based on the built-in curves mode. Incompatible with PID and Equitherm."
}
},
"mqtt": {
"homeAssistantDiscovery": "Home Assistant Discovery",
"server": "Server",
@@ -489,7 +469,6 @@
"prefix": "Prefix",
"interval": "Publish interval <small>(sec)</small>"
},
"extPump": {
"use": "Use external pump",
"gpio": "Relay GPIO",
@@ -498,7 +477,6 @@
"antiStuckInterval": "Anti stuck interval <small>(days)</small>",
"antiStuckTime": "Anti stuck time <small>(min)</small>"
},
"cascadeControl": {
"input": {
"desc": "Can be used to turn on the heating only if another boiler is faulty. The other boiler controller must change the state of the GPIO input in the event of a fault.",
@@ -522,23 +500,23 @@
}
}
},
"upgrade": {
"title": "Upgrade - OpenTherm Gateway",
"name": "Upgrade",
"section": {
"backupAndRestore": "Backup & restore",
"backupAndRestore.desc": "In this section you can save and restore a backup of ALL settings.",
"upgrade": "Upgrade",
"upgrade.desc": "In this section you can upgrade the firmware and filesystem of your device.<br />Latest releases can be downloaded from the <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Releases page</a> of the project repository."
"backupAndRestore": {
"title": "Backup & restore",
"desc": "In this section you can save and restore a backup of ALL settings."
},
"upgrade": {
"title": "Upgrade",
"desc": "In this section you can upgrade the firmware and filesystem of your device.<br />Latest releases can be downloaded from the <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Releases page</a> of the project repository."
}
},
"note": {
"disclaimer1": "After a successful upgrade the filesystem, ALL settings will be reset to default values! Save backup before upgrading.",
"disclaimer2": "After a successful upgrade, the device will automatically reboot after 15 seconds."
},
"settingsFile": "Settings file",
"fw": "Firmware",
"fs": "Filesystem"
+523
View File
@@ -0,0 +1,523 @@
{
"values": {
"logo": "",
"nav": {
"license": "",
"source": "",
"help": "",
"issues": "",
"releases": ""
},
"units": {
"days": "",
"hours": "",
"min": "",
"sec": "",
"dbm": "",
"mhz": "",
"kw": "",
"rpm": "",
"ppm": "",
"byte": "",
"mbyte": ""
},
"button": {
"upgrade": "",
"restart": "",
"save": "",
"saved": "",
"refresh": "",
"restore": "",
"restored": "",
"backup": "",
"wait": "",
"uploading": "",
"success": "",
"error": ""
},
"index": {
"title": "",
"section": {
"network": "",
"system": ""
},
"system": {
"build": {
"title": "",
"version": "",
"commit": "",
"date": "",
"core": "",
"sdk": ""
},
"uptime": "",
"memory": {
"heap": "",
"psram": "",
"maxFreeBlock": "",
"min": ""
},
"board": "",
"chip": {
"model": "",
"cores": "",
"freq": ""
},
"flash": {
"size": "",
"realSize": ""
},
"lastResetReason": ""
}
},
"dashboard": {
"name": "",
"title": "",
"section": {
"control": "",
"states": "",
"sensors": "",
"diag": ""
},
"thermostat": {
"heating": "",
"dhw": "",
"currentTemp": "",
"enable": "",
"turbo": ""
},
"notify": {
"fault": {
"title": "",
"note": ""
},
"diag": {
"title": "",
"note": ""
},
"reset": ""
},
"states": {
"mNetworkConnected": "",
"mMqttConnected": "",
"mEmergencyState": "",
"mExtPumpState": "",
"mCascadeControlInput": "",
"mCascadeControlOutput": "",
"sConnected": "",
"sFlame": "",
"sCoolingActive": "",
"sCoolingSetpoint": "",
"sFaultActive": "",
"sFaultCode": "",
"sDiagActive": "",
"sDiagCode": "",
"mHeatEnabled": "",
"mHeatBlocking": "",
"mHeatOverheat": "",
"mHeatFreezing": "",
"sHeatActive": "",
"mHeatSetpointTemp": "",
"mHeatTargetTemp": "",
"mHeatCurrTemp": "",
"mHeatRetTemp": "",
"mHeatIndoorTemp": "",
"mHeatOutdoorTemp": "",
"mDhwEnabled": "",
"mDhwOverheat": "",
"sDhwActive": "",
"mDhwTargetTemp": "",
"mDhwCurrTemp": "",
"mDhwRetTemp": ""
},
"sensors": {
"values": {
"temp": "",
"humidity": "",
"battery": "",
"rssi": ""
}
}
},
"network": {
"title": "",
"name": "",
"section": {
"static": "",
"availableNetworks": "",
"staSettings": "",
"apSettings": ""
},
"scan": {
"pos": "",
"info": ""
},
"wifi": {
"ssid": "",
"password": "",
"channel": "",
"signal": "",
"connected": ""
},
"params": {
"hostname": "",
"dhcp": "",
"mac": "",
"ip": "",
"subnet": "",
"gateway": "",
"dns": ""
},
"sta": {
"channel": {
"note": ""
}
}
},
"sensors": {
"title": "",
"name": "",
"enabled": "",
"sensorName": {
"title": "",
"note": ""
},
"purpose": "",
"purposes": {
"outdoorTemp": "",
"indoorTemp": "",
"heatTemp": "",
"heatRetTemp": "",
"dhwTemp": "",
"dhwRetTemp": "",
"dhwFlowRate": "",
"exhaustTemp": "",
"modLevel": "",
"number": "",
"powerFactor": "",
"power": "",
"fanSpeed": "",
"co2": "",
"pressure": "",
"humidity": "",
"temperature": "",
"notConfigured": ""
},
"type": "",
"types": {
"otOutdoorTemp": "",
"otHeatTemp": "",
"otHeatRetTemp": "",
"otDhwTemp": "",
"otDhwTemp2": "",
"otDhwFlowRate": "",
"otCh2Temp": "",
"otExhaustTemp": "",
"otHeatExchangerTemp": "",
"otPressure": "",
"otModLevel": "",
"otCurrentPower": "",
"otExhaustCo2": "",
"otExhaustFanSpeed": "",
"otSupplyFanSpeed": "",
"otSolarStorageTemp": "",
"otSolarCollectorTemp": "",
"otFanSpeedSetpoint": "",
"otFanSpeedCurrent": "",
"otBurnerStarts": "",
"otDhwBurnerStarts": "",
"otHeatingPumpStarts": "",
"otDhwPumpStarts": "",
"otBurnerHours": "",
"otDhwBurnerHours": "",
"otHeatingPumpHours": "",
"otDhwPumpHours": "",
"otCoolingHours": "",
"ntcTemp": "",
"dallasTemp": "",
"bluetooth": "",
"dht11": "",
"dht22": "",
"heatSetpointTemp": "",
"manual": "",
"notConfigured": ""
},
"gpio": "",
"address": {
"title": "",
"note": ""
},
"correction": {
"desc": "",
"offset": "",
"factor": ""
},
"filtering": {
"desc": "",
"enabled": {
"title": "",
"note": ""
},
"factor": {
"title": "",
"note": ""
}
}
},
"settings": {
"title": "",
"name": "",
"section": {
"portal": "",
"system": "",
"diag": "",
"heating": "",
"dhw": "",
"emergency": "",
"equitherm": "",
"pid": "",
"ot": "",
"mqtt": "",
"extPump": "",
"cascadeControl": ""
},
"enable": "",
"note": {
"restart": "",
"blankNotUse": "",
"bleDevice": ""
},
"temp": {
"min": "",
"max": ""
},
"avgType": {
"mean": "",
"min": "",
"max": ""
},
"maxModulation": "",
"ohProtection": {
"title": "",
"desc": "",
"highTemp": {
"title": "",
"note": ""
},
"lowTemp": {
"title": "",
"note": ""
}
},
"freezeProtection": {
"title": "",
"desc": "",
"highTemp": {
"title": "",
"note": ""
},
"lowTemp": {
"title": "",
"note": ""
}
},
"portal": {
"login": "",
"password": "",
"auth": "",
"mdns": ""
},
"system": {
"unit": "",
"metric": "",
"imperial": "",
"statusLedGpio": "",
"logLevel": "",
"serial": {
"enable": "",
"baud": ""
},
"webSerial": {
"enable": ""
},
"ntp": {
"server": "",
"timezone": "",
"timezonePresets": ""
}
},
"heating": {
"hyst": {
"title": "",
"desc": "",
"value": "",
"action": {
"title": "",
"disableHeating": "",
"set0target": ""
}
},
"turboFactor": "",
"indoorTempAvgType": {
"title": "",
"desc": ""
},
"outdoorTempAvgType": {
"title": "",
"desc": ""
}
},
"emergency": {
"desc": "",
"target": {
"title": "",
"note": ""
},
"treshold": ""
},
"equitherm": {
"slope": {
"title": "",
"note": ""
},
"exponent": {
"title": "",
"note": ""
},
"shift": {
"title": "",
"note": ""
},
"targetDiffFactor": {
"title": "",
"note": ""
},
"chart": {
"targetTemp": "",
"setpointTemp": "",
"outdoorTemp": ""
}
},
"pid": {
"p": "",
"i": "",
"d": "",
"dt": "",
"limits": {
"title": "",
"note": ""
},
"deadband": {
"title": "",
"note": "",
"p_multiplier": "",
"i_multiplier": "",
"d_multiplier": "",
"thresholdHigh": "",
"thresholdLow": ""
}
},
"ot": {
"advanced": "",
"inGpio": "",
"outGpio": "",
"ledGpio": "",
"memberId": "",
"flags": "",
"minPower": {
"title": "",
"note": ""
},
"maxPower": {
"title": "",
"note": ""
},
"options": {
"title": "",
"desc": "",
"dhwSupport": "",
"coolingSupport": "",
"summerWinterMode": "",
"heatingStateToSummerWinterMode": "",
"ch2AlwaysEnabled": "",
"heatingToCh2": "",
"dhwToCh2": "",
"dhwBlocking": "",
"dhwStateAsDhwBlocking": "",
"maxTempSyncWithTargetTemp": "",
"getMinMaxTemp": "",
"ignoreDiagState": "",
"autoFaultReset": "",
"autoDiagReset": "",
"setDateAndTime": "",
"immergasFix": "",
"alwaysSendIndoorTemp": ""
},
"nativeOTC": {
"title": "",
"note": ""
}
},
"mqtt": {
"homeAssistantDiscovery": "",
"server": "",
"port": "",
"user": "",
"password": "",
"prefix": "",
"interval": ""
},
"extPump": {
"use": "",
"gpio": "",
"invertState": "",
"postCirculationTime": "",
"antiStuckInterval": "",
"antiStuckTime": ""
},
"cascadeControl": {
"input": {
"desc": "",
"enable": "",
"gpio": "",
"invertState": "",
"thresholdTime": ""
},
"output": {
"desc": "",
"enable": "",
"gpio": "",
"invertState": "",
"thresholdTime": "",
"events": {
"desc": "",
"onFault": "",
"onLossConnection": "",
"onEnabledHeating": ""
}
}
}
},
"upgrade": {
"title": "",
"name": "",
"section": {
"backupAndRestore": {
"title": "",
"desc": ""
},
"upgrade": {
"title": "",
"desc": ""
}
},
"note": {
"disclaimer1": "",
"disclaimer2": ""
},
"settingsFile": "",
"fw": "",
"fs": ""
}
}
}
+31 -53
View File
@@ -19,9 +19,10 @@
"rpm": "RPM",
"ppm": "ppm",
"byte": "byte",
"mbyte": "MB"
"mbyte": "MB",
"liter": "litro",
"gallon": "gal"
},
"button": {
"upgrade": "Aggiorna",
"restart": "Riavvia",
@@ -36,15 +37,12 @@
"success": "Riuscito",
"error": "Errore"
},
"index": {
"title": "OpenTherm Gateway",
"section": {
"network": "Rete",
"system": "Sistema"
},
"system": {
"build": {
"title": "Build",
@@ -56,8 +54,8 @@
},
"uptime": "Tempo di attività",
"memory": {
"heap": "Heap",
"psram": "Psram",
"heap": "Memoria (heap)",
"psram": "Memoria (PSRAM)",
"maxFreeBlock": "max free block",
"min": "min"
},
@@ -74,26 +72,22 @@
"lastResetReason": "Motivo ultimo Reset"
}
},
"dashboard": {
"name": "Pannello",
"title": "Pannello - OpenTherm Gateway",
"section": {
"control": "Controlli",
"states": "Stato",
"sensors": "Sensori",
"diag": "Diagnostica OpenTherm"
},
"thermostat": {
"heating": "Riscaldamento",
"dhw": "ACS",
"temp.current": "Attuale",
"currentTemp": "Attuale",
"enable": "Attiva",
"turbo": "Turbo"
},
"notify": {
"fault": {
"title": "Rilevamento guasti caldiaia attivo!",
@@ -105,7 +99,6 @@
},
"reset": "Prova a resettare"
},
"states": {
"mNetworkConnected": "Connessione Rete",
"mMqttConnected": "Connessione MQTT",
@@ -113,7 +106,6 @@
"mExtPumpState": "Pompa esterna",
"mCascadeControlInput": "Controllo a cascata (input)",
"mCascadeControlOutput": "Controllo a cascata (output)",
"sConnected": "Connessione OpenTherm",
"sFlame": "Fiamma",
"sCoolingActive": "Raffrescamento",
@@ -122,7 +114,6 @@
"sFaultCode": "Codice anomalia",
"sDiagActive": "Diagnostica",
"sDiagCode": "Codice Diagnostica",
"mHeatEnabled": "Riscaldamento attivato",
"mHeatBlocking": "Riscaldamento bloccato",
"mHeatOverheat": "Riscaldamento surriscaldamento",
@@ -134,7 +125,6 @@
"mHeatRetTemp": "Temp ritorno riscaldamento",
"mHeatIndoorTemp": "Riscaldamento, temp interna",
"mHeatOutdoorTemp": "Riscaldamento, temp esterna",
"mDhwEnabled": "ACS attivata",
"mDhwOverheat": "ACS surriscaldamento",
"sDhwActive": "ACS attiva",
@@ -142,7 +132,6 @@
"mDhwCurrTemp": "ACS temp attuale",
"mDhwRetTemp": "ACS temp ricircolo"
},
"sensors": {
"values": {
"temp": "Temperatura",
@@ -152,23 +141,19 @@
}
}
},
"network": {
"title": "Rete - OpenTherm Gateway",
"name": "Impostazioni rete",
"section": {
"static": "Impostazioni statico",
"availableNetworks": "Reti disponibili",
"staSettings": "Impostazioni WiFi",
"apSettings": "Impostazioni AP"
},
"scan": {
"pos": "#",
"info": "Info"
},
"wifi": {
"ssid": "SSID",
"password": "Password",
@@ -176,7 +161,6 @@
"signal": "Segnale",
"connected": "Connesso"
},
"params": {
"hostname": "Hostname",
"dhcp": "Usa DHCP",
@@ -186,16 +170,15 @@
"gateway": "Gateway",
"dns": "DNS"
},
"sta": {
"channel.note": "Metti 0 per auto selezione"
"channel": {
"note": "Metti 0 per auto selezione"
}
}
},
"sensors": {
"title": "Impostazione sensori - OpenTherm Gateway",
"name": "Impostazione sensori",
"enabled": "Attivato",
"sensorName": {
"title": "Nome sensore",
@@ -252,7 +235,6 @@
"otHeatingPumpHours": "OpenTherm, numero di ore di funzionamento della pompa (riscaldamento)",
"otDhwPumpHours": "OpenTherm, numero di ore di funzionamento della pompa (ACS)",
"otCoolingHours": "OpenTherm, numero di ore di funzionamento della cooling",
"ntcTemp": "Sensore NTC",
"dallasTemp": "Sensore DALLAS",
"bluetooth": "Sensore BLE",
@@ -284,11 +266,9 @@
}
}
},
"settings": {
"title": "Impostazioni - OpenTherm Gateway",
"name": "Impostazioni",
"section": {
"portal": "Impostazioni Accesso",
"system": "Impostazioni sistema",
@@ -303,18 +283,21 @@
"extPump": "Impostazioni pompa esterna",
"cascadeControl": "Impostazioni controllo a cascata"
},
"enable": "Attiva",
"note": {
"restart": "Dopo aver cambiato queste impostazioni, il sistema sarà riavviato perchè i cambiamenti abbiano effetto.",
"blankNotUse": "vuoto - non usare",
"bleDevice": "Dispositivi BLE possono essere usati <u>solo</u> con alcune schede ESP32 che supportano il bluetooth!"
},
"temp": {
"min": "Temperatura minima",
"max": "Temperatura massima"
},
"avgType": {
"mean": "Temperatura media",
"min": "Temperatura minima",
"max": "Temperatura massima"
},
"maxModulation": "Max livello modulazione",
"ohProtection": {
"title": "Protezione contro il surriscaldamento",
@@ -340,14 +323,12 @@
"note": "Soglia quando il riscaldamento viene forzatamente attivato"
}
},
"portal": {
"login": "Login",
"password": "Password",
"auth": "Richiede autenticazione",
"mdns": "Usa mDNS"
},
"system": {
"unit": "Unità di misura",
"metric": "Metrico <small>(celsius, litri, bar)</small>",
@@ -367,7 +348,6 @@
"timezonePresets": "Seleziona preimpostato..."
}
},
"heating": {
"hyst": {
"title": "Isteresi",
@@ -379,19 +359,24 @@
"set0target": "Imposta target nullo"
}
},
"turboFactor": "Turbo mode coeff."
"turboFactor": "Turbo mode coeff.",
"indoorTempAvgType": {
"title": "Tipo di media temperatura interna",
"desc": "Utile con due o più sensori di temperatura interna (quando si usa «Equitherm» e/o «PID»)."
},
"outdoorTempAvgType": {
"title": "Tipo di media temperatura esterna",
"desc": "Utile con due o più sensori di temperatura esterna (quando si usa «Equitherm»)."
}
},
"emergency": {
"desc": "Il modo emergenza è attivato automaticamente quando «PID» o «Equitherm» non possono calcolare il setpoint:<br />- se «Equitherm» è attivato e il sensore della temperatura esternare è disconnesso;<br />- se «PID» o l'opzione OT <i>«Impostazioni riscaldamento native»</i> è attiva e il sensore di temperatura interno è disconnesso.<br /><b>Nota:</b> In mancanza di rete o MQTT, sensore di tipo <i>«Manuale via MQTT/API»</i> è in stato Disconnesso.",
"target": {
"title": "Temperatura impostata",
"note": "<b>Importante:</b> <u>Temperatura interna impostata</u> se l'opzione OT <i>«Controllo riscaldamento interno»</i> è attivato.<br />In tutti gli altri casi, la <u>target heat carrier temperature</u>."
},
"treshold": "Tempo di soglia <small>(sec)</small>"
},
"equitherm": {
"slope": {
"title": "Pendenza",
@@ -415,7 +400,6 @@
"outdoorTemp": "Temperatura esterna"
}
},
"pid": {
"p": "Fattore P",
"i": "Fattore I",
@@ -435,7 +419,6 @@
"thresholdLow": "Soglia inferiore"
}
},
"ot": {
"advanced": "Impostazioni avanzate",
"inGpio": "In GPIO",
@@ -451,7 +434,6 @@
"title": "Potenza massima caldaia <small>(kW)</small>",
"note": "<b>0</b> - prova a rilevarla automaticamente. Di solito si trova nelle specifiche delle caldaia come \"potenza massima disponibile\"."
},
"options": {
"title": "Opzioni (impostazioni aggiuntive)",
"desc": "Le opzioni possono modificare la logica della caldaia. Non tutte le opzioni sono documentate nel protocollo, quindi la stessa opzione può avere effetti diversi su caldaie diverse.<br /><b>Nota:</b> Non è necessario modificare nulla se tutto funziona correttamente.",
@@ -473,13 +455,11 @@
"immergasFix": "Fix per caldiaie Immergas",
"alwaysSendIndoorTemp": "Invia la temp attuale interna alla caldaia"
},
"nativeOTC": {
"title": "Modalità nativa di calcolo della temperatura del vettore termico",
"note": "Funziona <u>SOLO</u> se la caldaia è in modalità OTC: richiede e accetta la temperatura interna target e regola autonomamente la temperatura del vettore termico basata sulla modalità curve integrata. Incompatibile con PID e Equitherm."
}
},
"mqtt": {
"homeAssistantDiscovery": "Home Assistant Discovery",
"server": "Server",
@@ -489,7 +469,6 @@
"prefix": "Prefisso",
"interval": "Intervallo invio <small>(sec)</small>"
},
"extPump": {
"use": "Usa pompa/circolatore esterno",
"gpio": "GPIO relè",
@@ -498,7 +477,6 @@
"antiStuckInterval": "Intervallo antiblocco <small>(days)</small>",
"antiStuckTime": "Tempo antiblocco <small>(min)</small>"
},
"cascadeControl": {
"input": {
"desc": "Può essere attivata la caldaia se un'altra ha fallito. Il controllo dell'altra caldaia cambia lo stato dell'ingresso del GPIO in caso di errore.",
@@ -522,23 +500,23 @@
}
}
},
"upgrade": {
"title": "Aggiornamenti - OpenTherm Gateway",
"name": "Aggiornamenti",
"section": {
"backupAndRestore": "Backup & restore",
"backupAndRestore.desc": "In questa sezione puoi salvare e recuperare un backup di tutte le impostazioni.",
"upgrade": "Aggiorna",
"upgrade.desc": "In questa sezione puoi aggiornare il firmware il filesystem del tuo dispositivo.<br />L'ultimo aggiornamento può essere scaricato da <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Releases page</a> del progetto."
"backupAndRestore": {
"title": "Backup & restore",
"desc": "In questa sezione puoi salvare e recuperare un backup di tutte le impostazioni."
},
"upgrade": {
"title": "Aggiorna",
"desc": "In questa sezione puoi aggiornare il firmware il filesystem del tuo dispositivo.<br />L'ultimo aggiornamento può essere scaricato da <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Releases page</a> del progetto."
}
},
"note": {
"disclaimer1": "Dopo un aggiornamento riuscito del filesystem, tutte le impostazioni sono impostate di default! Salva un backup prima di aggiornare.",
"disclaimer2": "Dopo un aggiornamento riuscito, il sistema viene automaticamente riavviato dopo 15 secondi."
},
"settingsFile": "Settings file",
"fw": "Firmware",
"fs": "Filesystem"
+31 -13
View File
@@ -19,7 +19,9 @@
"rpm": "RPM",
"ppm": "ppm",
"byte": "byte",
"mbyte": "MB"
"mbyte": "MB",
"liter": "L",
"gallon": "gal"
},
"button": {
"upgrade": "Upgraden",
@@ -52,8 +54,8 @@
},
"uptime": "Uptime",
"memory": {
"heap": "Heap",
"psram": "Psram",
"heap": "Geheugen (heap)",
"psram": "Geheugen (PSRAM)",
"maxFreeBlock": "max. vrij blok",
"min": "min"
},
@@ -82,7 +84,7 @@
"thermostat": {
"heating": "Verwarming",
"dhw": "Warm water",
"temp.current": "Huidig",
"currentTemp": "Huidig",
"enable": "Inschakelen",
"turbo": "Turbomodus"
},
@@ -169,7 +171,9 @@
"dns": "DNS"
},
"sta": {
"channel.note": "zet op 0 voor automatische selectie"
"channel": {
"note": "zet op 0 voor automatische selectie"
}
}
},
"sensors": {
@@ -231,7 +235,6 @@
"otHeatingPumpHours": "OpenTherm, aantal pompuren (verwarming)",
"otDhwPumpHours": "OpenTherm, aantal pompuren (warm water)",
"otCoolingHours": "OpenTherm, aantal cooling",
"ntcTemp": "NTC-sensor",
"dallasTemp": "DALLAS-sensor",
"bluetooth": "BLE-sensor",
@@ -290,6 +293,11 @@
"min": "Minimumtemperatuur",
"max": "Maximumtemperatuur"
},
"avgType": {
"mean": "Gemiddelde temperatuur",
"min": "Minimum temperatuur",
"max": "Maximum temperatuur"
},
"maxModulation": "Max. modulatieniveau",
"ohProtection": {
"title": "Oververhittingsbeveiliging",
@@ -315,7 +323,6 @@
"note": "Drempel wanneer de verwarming geforceerd wordt ingeschakeld"
}
},
"portal": {
"login": "Gebruikersnaam",
"password": "Wachtwoord",
@@ -352,7 +359,15 @@
"set0target": "Stel null target in"
}
},
"turboFactor": "Turbomodus coëff."
"turboFactor": "Turbomodus coëff.",
"indoorTempAvgType": {
"title": "Binnentemperatuur gemiddelde type",
"desc": "Nuttig bij twee of meer binnentemperatuursensoren (bij gebruik van «Equitherm» en/of «PID»)."
},
"outdoorTempAvgType": {
"title": "Buitentemperatuur gemiddelde type",
"desc": "Nuttig bij twee of meer buitensensoren (bij gebruik van «Equitherm»)."
}
},
"emergency": {
"desc": "Noodmodus wordt automatisch geactiveerd wanneer «PID» of «Equitherm» het instelpunt van de warmtedrager niet kan berekenen:<br />- als «Equitherm» is ingeschakeld en de buitentemperatuursensor is losgekoppeld;<br />- als «PID» of OT-optie <i>«Natuurlijke verwarmingsregeling»</i> is ingeschakeld en de binnentemperatuursensor is losgekoppeld.<br /><b>Let op:</b> Bij een netwerk- of MQTT-storing krijgen sensoren van het type <i>«Handmatig via MQTT/API»</i> de status ONVERBONDEN.",
@@ -440,7 +455,6 @@
"immergasFix": "Fix voor Immergas-ketels",
"alwaysSendIndoorTemp": "Stuur huidige binnentemp naar ketel"
},
"nativeOTC": {
"title": "Native warmtedrager temperatuur berekeningsmodus",
"note": "Werkt <u>ALLEEN</u> als de ketel in OTC-modus is: vereist en accepteert de doel binnentemperatuur en regelt zelf de warmtedrager temperatuur op basis van de ingebouwde curves modus. Incompatibel met PID en Equitherm."
@@ -490,10 +504,14 @@
"title": "Upgrade - OpenTherm Gateway",
"name": "Upgrade",
"section": {
"backupAndRestore": "Back-up & herstel",
"backupAndRestore.desc": "In deze sectie kunt u een back-up van ALLE instellingen opslaan en herstellen.",
"upgrade": "Upgrade",
"upgrade.desc": "In deze sectie kunt u de firmware en het bestandssysteem van uw apparaat upgraden.<br />De nieuwste releases kunnen worden gedownload van de <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Releases-pagina</a> van de projectrepository."
"backupAndRestore": {
"title": "Back-up & herstel",
"desc": "In deze sectie kunt u een back-up van ALLE instellingen opslaan en herstellen."
},
"upgrade": {
"title": "Upgrade",
"desc": "In deze sectie kunt u de firmware en het bestandssysteem van uw apparaat upgraden.<br />De nieuwste releases kunnen worden gedownload van de <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Releases-pagina</a> van de projectrepository."
}
},
"note": {
"disclaimer1": "Na een succesvolle upgrade van het bestandssysteem worden ALLE instellingen teruggezet naar de standaardwaarden! Sla een back-up op voordat u gaat upgraden.",
+31 -54
View File
@@ -8,7 +8,6 @@
"issues": "Проблемы и вопросы",
"releases": "Релизы"
},
"units": {
"days": "д.",
"hours": "ч.",
@@ -20,9 +19,10 @@
"rpm": "RPM",
"ppm": "ppm",
"byte": "байт",
"mbyte": "мбайт"
"mbyte": "мбайт",
"liter": "Л.",
"gallon": "гал."
},
"button": {
"upgrade": "Обновить",
"restart": "Перезагрузка",
@@ -37,15 +37,12 @@
"success": "Успешно",
"error": "Ошибка"
},
"index": {
"title": "OpenTherm Gateway",
"section": {
"network": "Сеть",
"system": "Система"
},
"system": {
"build": {
"title": "Билд",
@@ -57,8 +54,8 @@
},
"uptime": "Аптайм",
"memory": {
"heap": "Heap",
"psram": "Psram",
"heap": "Память (heap)",
"psram": "Память (PSRAM)",
"maxFreeBlock": "макс. блок",
"min": "мин."
},
@@ -75,26 +72,22 @@
"lastResetReason": "Причина перезагрузки"
}
},
"dashboard": {
"name": "Дашборд",
"title": "Дашборд - OpenTherm Gateway",
"section": {
"control": "Управление",
"states": "Состояние",
"sensors": "Сенсоры",
"diag": "Диагностика OpenTherm"
},
"thermostat": {
"heating": "Отопление",
"dhw": "ГВС",
"temp.current": "Текущая",
"currentTemp": "Текущая",
"enable": "Вкл",
"turbo": "Турбо"
},
"notify": {
"fault": {
"title": "Состояние неисправности котла активно!",
@@ -106,7 +99,6 @@
},
"reset": "Сбросить"
},
"states": {
"mNetworkConnected": "Подключение к сети",
"mMqttConnected": "Подключение к MQTT",
@@ -114,7 +106,6 @@
"mExtPumpState": "Внешний насос",
"mCascadeControlInput": "Каскадное управление (вход)",
"mCascadeControlOutput": "Каскадное управление (выход)",
"sConnected": "Подключение к OpenTherm",
"sFlame": "Пламя",
"sCoolingActive": "Охлаждение",
@@ -123,7 +114,6 @@
"sFaultCode": "Код ошибки",
"sDiagActive": "Диагностика",
"sDiagCode": "Диагностический код",
"mHeatEnabled": "Отопление",
"mHeatBlocking": "Блокировка отопления",
"mHeatOverheat": "Отопление, перегрев",
@@ -135,7 +125,6 @@
"mHeatRetTemp": "Отопление, температура обратки",
"mHeatIndoorTemp": "Отопление, внутренняя темп.",
"mHeatOutdoorTemp": "Отопление, наружная темп.",
"mDhwEnabled": "ГВС",
"mDhwOverheat": "ГВС, перегрев",
"sDhwActive": "Активность ГВС",
@@ -143,7 +132,6 @@
"mDhwCurrTemp": "ГВС, текущая температура",
"mDhwRetTemp": "ГВС, температура обратки"
},
"sensors": {
"values": {
"temp": "Температура",
@@ -153,23 +141,19 @@
}
}
},
"network": {
"title": "Сеть - OpenTherm Gateway",
"name": "Настройки сети",
"section": {
"static": "Статические параметры",
"availableNetworks": "Доступные сети",
"staSettings": "Настройки подключения",
"apSettings": "Настройки точки доступа"
},
"scan": {
"pos": "#",
"info": "Инфо"
},
"wifi": {
"ssid": "Имя сети",
"password": "Пароль",
@@ -177,7 +161,6 @@
"signal": "Сигнал",
"connected": "Подключено"
},
"params": {
"hostname": "Имя хоста",
"dhcp": "Использовать DHCP",
@@ -187,16 +170,15 @@
"gateway": "Адрес шлюза",
"dns": "DNS адрес"
},
"sta": {
"channel.note": "установите 0 для автоматического выбора"
"channel": {
"note": "установите 0 для автоматического выбора"
}
}
},
"sensors": {
"title": "Настройки сенсоров - OpenTherm Gateway",
"name": "Настройки сенсоров",
"enabled": "Включить и использовать",
"sensorName": {
"title": "Имя сенсора",
@@ -253,7 +235,6 @@
"otHeatingPumpHours": "OpenTherm, кол-во часов работы насоса (отопление)",
"otDhwPumpHours": "OpenTherm, кол-во часов работы насоса (ГВС)",
"otCoolingHours": "OpenTherm, кол-во часов работы охлаждения",
"ntcTemp": "NTC датчик",
"dallasTemp": "DALLAS датчик",
"bluetooth": "BLE датчик",
@@ -285,11 +266,9 @@
}
}
},
"settings": {
"title": "Настройки - OpenTherm Gateway",
"name": "Настройки",
"section": {
"portal": "Настройки портала",
"system": "Системные настройки",
@@ -304,18 +283,21 @@
"extPump": "Настройки дополнительного насоса",
"cascadeControl": "Настройки каскадного управления"
},
"enable": "Вкл",
"note": {
"restart": "После изменения этих настроек устройство необходимо перезагрузить, чтобы изменения вступили в силу.",
"blankNotUse": "пусто - не использовать",
"bleDevice": "BLE устройство можно использовать <u>только</u> с некоторыми платами ESP32, которые поддерживают BLE!"
},
"temp": {
"min": "Мин. температура",
"max": "Макс. температура"
},
"avgType": {
"mean": "Средняя температура",
"min": "Минимальная температура",
"max": "Максимальная температура"
},
"maxModulation": "Макс. уровень модуляции",
"ohProtection": {
"title": "Защита от перегрева",
@@ -341,14 +323,12 @@
"note": "Порог, при котором отопление будет принудительно включено"
}
},
"portal": {
"login": "Логин",
"password": "Пароль",
"auth": "Требовать аутентификацию",
"mdns": "Использовать mDNS"
},
"system": {
"unit": "Система единиц",
"metric": "Метрическая <small>(цельсии, литры, бары)</small>",
@@ -368,7 +348,6 @@
"timezonePresets": "Выберите пресет..."
}
},
"heating": {
"hyst": {
"title": "Гистерезис",
@@ -380,19 +359,24 @@
"set0target": "Установить 0 в качестве целевой темп."
}
},
"turboFactor": "Коэфф. турбо режима"
"turboFactor": "Коэфф. турбо режима",
"indoorTempAvgType": {
"title": "Тип усреднения внутренней темп.",
"desc": "Полезно при использовании двух и более датчиков внутренней температуры (при использовании «Equitherm» и/или «PID»)."
},
"outdoorTempAvgType": {
"title": "Тип усреднения наружнной темп.",
"desc": "Полезно при использовании двух и более датчиков наружной температуры (при использовании «Equitherm»)."
}
},
"emergency": {
"desc": "Аварийный режим активируется автоматически, если «ПИД» или «ПЗА» не могут рассчитать уставку теплоносителя:<br />- если «ПЗА» включен и датчик наружной температуры отключен;<br />- если включен «ПИД» или OT опция <i>«Передать управление отоплением котлу»</i> и датчик внутренней температуры отключен.<br /><b>Примечание:</b> При сбое сети или MQTT датчики с типом <i>«Вручную через MQTT/API»</i> будут находиться в состоянии ОТКЛЮЧЕН.",
"target": {
"title": "Целевая температура",
"note": "<b>Важно:</b> <u>Целевая температура в помещении</u>, если включена ОТ опция <i>«Передать управление отоплением котлу»</i>.<br />Во всех остальных случаях <u>целевая температура теплоносителя</u>."
},
"treshold": "Пороговое время включения <small>(сек)</small>"
},
"equitherm": {
"slope": {
"title": "Наклон",
@@ -416,7 +400,6 @@
"outdoorTemp": "Наружная температура"
}
},
"pid": {
"p": "Коэффициент P",
"i": "Коэффициент I",
@@ -436,7 +419,6 @@
"thresholdLow": "Нижний порог"
}
},
"ot": {
"advanced": "Дополнительные настройки",
"inGpio": "Вход GPIO",
@@ -452,7 +434,6 @@
"title": "Макс. мощность котла <small>(кВт)</small>",
"note": "<b>0</b> - попробовать определить автоматически. Обычно можно найти в спецификации котла как \"максимальная полезная тепловая мощность\"."
},
"options": {
"title": "Опции (дополнительные настройки)",
"desc": "Опции могут менять логику работы котла. Не все опции задокументированы в протоколе, поэтому одна и та же опция может иметь разный эффект на разных котлах.<br /><b>Примечание:</b> Нет необходимости что-то менять, если всё работает хорошо.",
@@ -474,13 +455,11 @@
"immergasFix": "Фикс для котлов Immergas",
"alwaysSendIndoorTemp": "Передавать текущую темп. в помещении котлу"
},
"nativeOTC": {
"title": "Нативный режим OTC (расчёт температуры теплоносителя)",
"note": "Работает <u>ТОЛЬКО</u> если котел в режиме OTC: требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД и ПЗА."
}
},
"mqtt": {
"homeAssistantDiscovery": "Home Assistant Discovery",
"server": "Адрес сервера",
@@ -490,7 +469,6 @@
"prefix": "Префикс",
"interval": "Интервал публикации <small>(сек)</small>"
},
"extPump": {
"use": "Использовать доп. насос",
"gpio": "GPIO реле",
@@ -499,7 +477,6 @@
"antiStuckInterval": "Интервал защиты от блокировки <small>(в днях)</small>",
"antiStuckTime": "Время работы насоса <small>(в минутах)</small>"
},
"cascadeControl": {
"input": {
"desc": "Может использоваться для включения отопления только при неисправности другого котла. Контроллер другого котла должен изменить состояние входа GPIO в случае неисправности.",
@@ -523,23 +500,23 @@
}
}
},
"upgrade": {
"title": "Обновление - OpenTherm Gateway",
"name": "Обновление",
"section": {
"backupAndRestore": "Резервное копирование и восстановление",
"backupAndRestore.desc": "В этом разделе вы можете сохранить и восстановить резервную копию ВСЕХ настроек.",
"upgrade": "Обновление",
"upgrade.desc": "В этом разделе вы можете обновить прошивку и файловую систему вашего устройства.<br />Последнюю версию можно загрузить со страницы <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Релизы</a> в репозитории проекта."
"backupAndRestore": {
"title": "Резервное копирование и восстановление",
"desc": "В этом разделе вы можете сохранить и восстановить резервную копию ВСЕХ настроек."
},
"upgrade": {
"title": "Обновление",
"desc": "В этом разделе вы можете обновить прошивку и файловую систему вашего устройства.<br />Последнюю версию можно загрузить со страницы <a href=\"https://github.com/Laxilef/OTGateway/releases\" target=\"_blank\">Релизы</a> в репозитории проекта."
}
},
"note": {
"disclaimer1": "После успешного обновления файловой системы ВСЕ настройки будут сброшены на стандартные! Создайте резервную копию ПЕРЕД обновлением.",
"disclaimer2": "После успешного обновления устройство автоматически перезагрузится через 15 секунд."
},
"settingsFile": "Файл настроек",
"fw": "Прошивка",
"fs": "Файловая система"
+36 -8
View File
@@ -47,7 +47,7 @@
<div class="thermostat-header" data-i18n>dashboard.thermostat.heating</div>
<div class="thermostat-temp">
<div class="thermostat-temp-target"><span class="targetTemp"></span> <span class="tempUnit"></span></div>
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.temp.current</span>: <span id="tHeatCurrentTemp"></span> <span class="tempUnit"></span></div>
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.currentTemp</span>: <span id="tHeatCurrentTemp"></span> <span class="tempUnit"></span></div>
</div>
<div class="thermostat-minus">
<button class="tAction outline" data-action="decrement"><i class="icons-down"></i></button>
@@ -68,7 +68,7 @@
<div class="thermostat-header" data-i18n>dashboard.thermostat.dhw</div>
<div class="thermostat-temp">
<div class="thermostat-temp-target"><span class="targetTemp"></span> <span class="tempUnit"></span></div>
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.temp.current</span>: <span id="tDhwCurrentTemp"></span> <span class="tempUnit"></span></div>
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.currentTemp</span>: <span id="tDhwCurrentTemp"></span> <span class="tempUnit"></span></div>
</div>
<div class="thermostat-minus">
<button class="tAction outline" data-action="decrement"><i class="icons-down"></i></button>
@@ -351,9 +351,14 @@
value = -(step);
}
newSettings[purpose].target = parseFloat(constrain(newSettings[purpose].target + value, minTemp, maxTemp).toFixed(2));
modifiedTime = Date.now();
setValue('.targetTemp', newSettings[purpose].target, tContainer);
const newValue = parseFloat(constrain(newSettings[purpose].target + value, minTemp, maxTemp).toFixed(2));
if (newSettings[purpose].target != newValue) {
newSettings[purpose].target = newValue;
modifiedTime = Date.now();
setValue('.targetTemp', newValue, tContainer);
tContainer.querySelector('.thermostat-header').setAttribute("aria-busy", "true");
}
}
});
}
@@ -383,27 +388,46 @@
value = -(bigStep);
}
newSettings[purpose].target = parseFloat(constrain(newSettings[purpose].target + value, minTemp, maxTemp).toFixed(2));
modifiedTime = Date.now();
const newValue = parseFloat(constrain(newSettings[purpose].target + value, minTemp, maxTemp).toFixed(2));
if (newSettings[purpose].target != newValue) {
newSettings[purpose].target = newValue;
modifiedTime = Date.now();
setValue('.targetTemp', newSettings[purpose].target, tContainer);
setValue('.targetTemp', newSettings[purpose].target, tContainer);
tContainer.querySelector('.thermostat-header').setAttribute("aria-busy", "true");
}
}, 500);
});
});
document.querySelector('#tHeatEnabled').addEventListener('change', (event) => {
if (newSettings.heating.enabled == event.currentTarget.checked) {
return;
}
modifiedTime = Date.now();
newSettings.heating.enabled = event.currentTarget.checked;
document.querySelector('.tHeat .thermostat-header').setAttribute("aria-busy", "true");
});
document.querySelector('#tHeatTurbo').addEventListener('change', (event) => {
if (newSettings.heating.turbo == event.currentTarget.checked) {
return;
}
modifiedTime = Date.now();
newSettings.heating.turbo = event.currentTarget.checked;
document.querySelector('.tHeat .thermostat-header').setAttribute("aria-busy", "true");
});
document.querySelector('#tDhwEnabled').addEventListener('change', (event) => {
if (newSettings.dhw.enabled == event.currentTarget.checked) {
return;
}
modifiedTime = Date.now();
newSettings.dhw.enabled = event.currentTarget.checked;
document.querySelector('.tDhw .thermostat-header').setAttribute("aria-busy", "true");
});
document.querySelector('.notify-fault .reset').addEventListener('click', async (event) => {
@@ -531,6 +555,10 @@
setValue('.pressureUnit', pressureUnit(unitSystem));
setValue('.volumeUnit', volumeUnit(unitSystem));
document.querySelectorAll('.thermostat-header').forEach((item) => {
item.setAttribute("aria-busy", "false");
});
} catch (error) {
console.log(error);
}
+25 -1
View File
@@ -198,6 +198,28 @@
</label>
</div>
<div class="grid">
<label>
<span data-i18n>settings.heating.indoorTempAvgType.title</span>
<select name="heating[indoorTempAvgType]">
<option value="0" data-i18n>settings.avgType.mean</option>
<option value="1" data-i18n>settings.avgType.min</option>
<option value="2" data-i18n>settings.avgType.max</option>
</select>
<small data-i18n>settings.heating.indoorTempAvgType.desc</small>
</label>
<label>
<span data-i18n>settings.heating.outdoorTempAvgType.title</span>
<select name="heating[outdoorTempAvgType]">
<option value="0" data-i18n>settings.avgType.mean</option>
<option value="1" data-i18n>settings.avgType.min</option>
<option value="2" data-i18n>settings.avgType.max</option>
</select>
<small data-i18n>settings.heating.outdoorTempAvgType.desc</small>
</label>
</div>
<hr />
<details>
@@ -785,7 +807,7 @@
<label>
<input type="checkbox" name="externalPump[invertState]" value="true">
<span data-i18n>settings.externalPump.invertState</span>
<span data-i18n>settings.extPump.invertState</span>
</label>
</fieldset>
@@ -1171,6 +1193,8 @@
setSelectValue("[name='heating[hysteresis][action]']", data.heating.hysteresis.action);
setInputValue("[name='heating[turboFactor]']", data.heating.turboFactor);
setInputValue("[name='heating[maxModulation]']", data.heating.maxModulation);
setSelectValue("[name='heating[indoorTempAvgType]']", data.heating.indoorTempAvgType);
setSelectValue("[name='heating[outdoorTempAvgType]']", data.heating.outdoorTempAvgType);
setInputValue("[name='heating[overheatProtection][highTemp]']", data.heating.overheatProtection.highTemp, {
"min": 0,
"max": data.system.unitSystem == 0 ? 100 : 212
+2 -2
View File
@@ -35,7 +35,7 @@
<article>
<div>
<hgroup>
<h2 data-i18n>upgrade.section.backupAndRestore</h2>
<h2 data-i18n>upgrade.section.backupAndRestore.title</h2>
<p data-i18n>upgrade.section.backupAndRestore.desc</p>
</hgroup>
@@ -56,7 +56,7 @@
<article>
<div>
<hgroup>
<h2 data-i18n>upgrade.section.upgrade</h2>
<h2 data-i18n>upgrade.section.upgrade.title</h2>
<p data-i18n>upgrade.section.upgrade.desc</p>
</hgroup>
+39 -4
View File
@@ -318,7 +318,7 @@ const setupRestoreBackupForm = (formSelector) => {
console.log("Backup: ", data);
if (data.settings != undefined) {
for (var key in data.settings) {
for (const key in data.settings) {
let response = await fetch(url, {
method: "POST",
cache: "no-cache",
@@ -338,6 +338,41 @@ const setupRestoreBackupForm = (formSelector) => {
return;
}
}
let finalPayload = {};
if (data.settings.emergency?.target !== undefined) {
finalPayload.emergency ??= {};
finalPayload.emergency.target = data.settings.emergency.target;
}
if (data.settings.heating?.target !== undefined) {
finalPayload.heating ??= {};
finalPayload.heating.target = data.settings.heating.target;
}
if (data.settings.dhw?.target !== undefined) {
finalPayload.dhw ??= {};
finalPayload.dhw.target = data.settings.dhw.target;
}
if (Object.keys(finalPayload).length) {
let response = await fetch(url, {
method: "POST",
cache: "no-cache",
credentials: "include",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
"settings": finalPayload
})
});
if (!response.ok) {
onFailed();
return;
}
}
}
if (data.sensors != undefined) {
@@ -765,8 +800,8 @@ const pressureUnit = (unitSystem) => {
const volumeUnit = (unitSystem) => {
return unit2str(unitSystem, {
0: "L",
1: "gal"
0: i18n('units.liter'),
1: i18n('units.gallon')
});
}
@@ -780,7 +815,7 @@ const purposeUnit = (purpose, unitSystem) => {
3: tUnit,
4: tUnit,
5: tUnit,
6: `${volumeUnit(unitSystem)}/${i18n('time.min')}`,
6: `${volumeUnit(unitSystem)}/${i18n('units.min')}`,
7: tUnit,
8: "%",
248: "%",