Compare commits
46 Commits
ed50208546
...
platformio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bef31da3d5 | ||
|
|
bae7770371 | ||
|
|
4e5a3e9da5 | ||
|
|
412e1594e9 | ||
|
|
9701e8c97b | ||
|
|
2fe546812c | ||
|
|
7efcbaa57e | ||
|
|
43c065b97a | ||
|
|
5c1e967fdc | ||
|
|
105a79f72c | ||
|
|
50280f6db3 | ||
|
|
c97e50669c | ||
|
|
43fd095714 | ||
|
|
1bb9b61017 | ||
|
|
1eb10563ed | ||
|
|
222ea4feaa | ||
|
|
ea1406c509 | ||
|
|
b20b450736 | ||
|
|
e8c7f58e67 | ||
|
|
2589020428 | ||
|
|
2bb771a4a7 | ||
|
|
f4af237472 | ||
|
|
5473034421 | ||
|
|
a5996cc93d | ||
|
|
9d6b6c18ab | ||
|
|
e6119dc7ee | ||
|
|
19feb85230 | ||
|
|
0d71a674b6 | ||
|
|
34eabca64a | ||
|
|
d3b28c5bfb | ||
|
|
640935a2b7 | ||
|
|
c0c631aab4 | ||
|
|
861db33765 | ||
|
|
b91266063b | ||
|
|
b087e6e6d3 | ||
|
|
b205f14bae | ||
|
|
7482eb8898 | ||
|
|
2ff84cbddf | ||
|
|
8b6e6be670 | ||
|
|
636872f72d | ||
|
|
c6df74f06e | ||
|
|
24a46f4c16 | ||
|
|
ddc9cf7c90 | ||
|
|
2d74d0c0ad | ||
|
|
e04462811b | ||
|
|
99d82657c0 |
69
README.md
@@ -1,40 +1,44 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||

|
|
||||||
<br>
|
|
||||||
[](https://github.com/Laxilef/OTGateway/releases)
|
[](https://github.com/Laxilef/OTGateway/releases)
|
||||||
[](https://github.com/Laxilef/OTGateway/releases/latest)
|
[](https://github.com/Laxilef/OTGateway/releases/latest)
|
||||||
[](LICENSE.txt)
|
[](LICENSE.txt)
|
||||||
[](https://t.me/otgateway)
|
[](https://t.me/otgateway)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Hot water temperature control
|
- DHW temperature control
|
||||||
- Heating temperature control
|
- Heating temperature control
|
||||||
- Smart heating temperature control modes:
|
- Smart heating temperature control modes:
|
||||||
- PID
|
- PID
|
||||||
- Equithermic curves - adjusts the temperature based on indoor and outdoor temperatures
|
- Equithermic curves - adjusts the temperature based on indoor and outdoor temperatures
|
||||||
- Hysteresis setting (for accurate maintenance of room temperature)
|
- Hysteresis setting _(for accurate maintenance of room temperature)_
|
||||||
- Ability to connect an external sensors to monitor outdoor and indoor temperature ([compatible sensors](https://github.com/Laxilef/OTGateway/wiki/Compatibility#temperature-sensors))
|
- Ability to connect [additional (external) sensors](https://github.com/Laxilef/OTGateway/wiki/Compatibility#temperature-sensors): Dallas (1-wire), NTC 10k, Bluetooth (BLE). Makes it possible to monitor indoor and outdoor temperatures, temperatures on pipes/heat exchangers/etc.
|
||||||
- Emergency mode. If the Wi-Fi connection is lost or the gateway cannot connect to the MQTT server, the mode will turn on. This mode will automatically maintain the set temperature and prevent your home from freezing. In this mode it is also possible to use equithermal curves (weather-compensated control).
|
- Emergency mode. In any dangerous situation _(loss of connection with Wifi, MQTT, sensors, etc)_ it will not let you and your home freeze.
|
||||||
- Automatic error reset (not with all boilers)
|
- Ability of remote fault reset _(not with all boilers)_
|
||||||
- Diagnostics:
|
- Diagnostics:
|
||||||
- The process of heating: works/does not work
|
- Displaying gateway status
|
||||||
- The process of heating water for hot water: working/not working
|
- Displaying the connection status to the boiler via OpenTherm
|
||||||
- Display of boiler errors
|
- Displaying the fault status and fault code
|
||||||
- Burner status (flame): on/off
|
- Displaying the diagnostic status & diagnostic code
|
||||||
- Burner modulation level in percent
|
- Display of the process of heating: works/does not work
|
||||||
- Pressure in the heating system
|
- Display of burner (flame) status: on/off
|
||||||
- Gateway status (depending on errors and connection status)
|
- Display of burner modulation level in percent
|
||||||
- Boiler connection status via OpenTherm interface
|
- Display of pressure in the heating system
|
||||||
- The current temperature of the heat carrier (usually the return heat carrier)
|
- Display of current temperature of the heat carrier
|
||||||
- Set heat carrier temperature (depending on the selected mode)
|
- Display of return temperature of the heat carrier
|
||||||
- Current hot water temperature
|
- Display of setpoint heat carrier temperature (useful when using PID or Equitherm)
|
||||||
|
- Display of the process of DHW: working/not working
|
||||||
|
- Display of current DHW temperature
|
||||||
|
- _And other information..._
|
||||||
- [Home Assistant](https://www.home-assistant.io/) integration via MQTT. The ability to create any automation for the boiler!
|
- [Home Assistant](https://www.home-assistant.io/) integration via MQTT. The ability to create any automation for the boiler!
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
All available information and instructions can be found in the wiki:
|
All available information and instructions can be found in the wiki:
|
||||||
|
|
||||||
@@ -52,6 +56,7 @@ All available information and instructions can be found in the wiki:
|
|||||||
* [Ratios](https://github.com/Laxilef/OTGateway/wiki#ratios)
|
* [Ratios](https://github.com/Laxilef/OTGateway/wiki#ratios)
|
||||||
* [Fit coefficients](https://github.com/Laxilef/OTGateway/wiki#fit-coefficients)
|
* [Fit coefficients](https://github.com/Laxilef/OTGateway/wiki#fit-coefficients)
|
||||||
* [PID mode](https://github.com/Laxilef/OTGateway/wiki#pid-mode)
|
* [PID mode](https://github.com/Laxilef/OTGateway/wiki#pid-mode)
|
||||||
|
* [Logs and debug](https://github.com/Laxilef/OTGateway/wiki#logs-and-debug)
|
||||||
* [Compatibility](https://github.com/Laxilef/OTGateway/wiki/Compatibility)
|
* [Compatibility](https://github.com/Laxilef/OTGateway/wiki/Compatibility)
|
||||||
* [Boilers](https://github.com/Laxilef/OTGateway/wiki/Compatibility#boilers)
|
* [Boilers](https://github.com/Laxilef/OTGateway/wiki/Compatibility#boilers)
|
||||||
* [Boards](https://github.com/Laxilef/OTGateway/wiki/Compatibility#boards)
|
* [Boards](https://github.com/Laxilef/OTGateway/wiki/Compatibility#boards)
|
||||||
@@ -64,21 +69,5 @@ All available information and instructions can be found in the wiki:
|
|||||||
* [Connection](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#connection)
|
* [Connection](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#connection)
|
||||||
* [Leds on board](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#leds-on-board)
|
* [Leds on board](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#leds-on-board)
|
||||||
|
|
||||||
|
___
|
||||||
|
This project is tested with BrowserStack.
|
||||||
## Dependencies
|
|
||||||
- [ESP8266Scheduler](https://github.com/nrwiersma/ESP8266Scheduler) (for ESP8266)
|
|
||||||
- [ESP32Scheduler](https://github.com/laxilef/ESP32Scheduler) (for ESP32)
|
|
||||||
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
|
|
||||||
- [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library)
|
|
||||||
- [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)
|
|
||||||
- [DallasTemperature](https://github.com/milesburton/Arduino-Temperature-Control-Library)
|
|
||||||
- [TinyLogger](https://github.com/laxilef/TinyLogger)
|
|
||||||
|
|
||||||
## Debug
|
|
||||||
To display DEBUG messages you must enable debug in settings (switch is disabled by default).
|
|
||||||
You can connect via Telnet to read messages. IP: ESP8266 ip, port: 23
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 261 KiB After Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 380 KiB |
|
Before Width: | Height: | Size: 684 KiB After Width: | Height: | Size: 849 KiB |
BIN
assets/BOM.xlsx
BIN
assets/CPL.csv
22533
assets/Schematic.pdf
BIN
assets/ha.png
|
Before Width: | Height: | Size: 190 KiB |
BIN
assets/poster-1.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
assets/poster-2.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
assets/poster-3.png
Normal file
|
After Width: | Height: | Size: 973 KiB |
17
gulpfile.js
@@ -6,6 +6,7 @@ const cssnano = require('cssnano');
|
|||||||
const terser = require('gulp-terser');
|
const terser = require('gulp-terser');
|
||||||
const jsonminify = require('gulp-jsonminify');
|
const jsonminify = require('gulp-jsonminify');
|
||||||
const htmlmin = require('gulp-html-minifier-terser');
|
const htmlmin = require('gulp-html-minifier-terser');
|
||||||
|
const replace = require('gulp-replace');
|
||||||
|
|
||||||
// Paths for tasks
|
// Paths for tasks
|
||||||
let paths = {
|
let paths = {
|
||||||
@@ -59,6 +60,10 @@ const styles = (cb) => {
|
|||||||
const items = paths.styles.bundles[name];
|
const items = paths.styles.bundles[name];
|
||||||
|
|
||||||
src(items)
|
src(items)
|
||||||
|
.pipe(replace(
|
||||||
|
"{BUILD_TIME}",
|
||||||
|
Math.floor(Date.now() / 1000)
|
||||||
|
))
|
||||||
.pipe(postcss([
|
.pipe(postcss([
|
||||||
cssnano({ preset: 'advanced' })
|
cssnano({ preset: 'advanced' })
|
||||||
]))
|
]))
|
||||||
@@ -77,6 +82,10 @@ const scripts = (cb) => {
|
|||||||
const items = paths.scripts.bundles[name];
|
const items = paths.scripts.bundles[name];
|
||||||
|
|
||||||
src(items)
|
src(items)
|
||||||
|
.pipe(replace(
|
||||||
|
"{BUILD_TIME}",
|
||||||
|
Math.floor(Date.now() / 1000)
|
||||||
|
))
|
||||||
.pipe(terser().on('error', console.error))
|
.pipe(terser().on('error', console.error))
|
||||||
.pipe(concat(name))
|
.pipe(concat(name))
|
||||||
.pipe(gzip({
|
.pipe(gzip({
|
||||||
@@ -93,6 +102,10 @@ const jsonFiles = (cb) => {
|
|||||||
const item = paths.json[i];
|
const item = paths.json[i];
|
||||||
|
|
||||||
src(item.src)
|
src(item.src)
|
||||||
|
.pipe(replace(
|
||||||
|
"{BUILD_TIME}",
|
||||||
|
Math.floor(Date.now() / 1000)
|
||||||
|
))
|
||||||
.pipe(jsonminify())
|
.pipe(jsonminify())
|
||||||
.pipe(gzip({
|
.pipe(gzip({
|
||||||
append: true
|
append: true
|
||||||
@@ -119,6 +132,10 @@ const staticFiles = (cb) => {
|
|||||||
|
|
||||||
const pages = () => {
|
const pages = () => {
|
||||||
return src(paths.pages.src)
|
return src(paths.pages.src)
|
||||||
|
.pipe(replace(
|
||||||
|
"{BUILD_TIME}",
|
||||||
|
Math.floor(Date.now() / 1000)
|
||||||
|
))
|
||||||
.pipe(htmlmin({
|
.pipe(htmlmin({
|
||||||
html5: true,
|
html5: true,
|
||||||
caseSensitive: true,
|
caseSensitive: true,
|
||||||
|
|||||||
@@ -77,10 +77,19 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->webServer->sendContent((const char*)this->buffer, this->bufferPos);
|
|
||||||
this->bufferPos = 0;
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto& client = this->webServer->client();
|
||||||
|
if (client.connected()) {
|
||||||
|
this->webServer->sendContent((const char*)this->buffer, this->bufferPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->bufferPos = 0;
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ const char HA_UNIT_OF_MEASUREMENT[] PROGMEM = "unit_of_measurement";
|
|||||||
const char HA_UNIT_OF_MEASUREMENT_C[] PROGMEM = "°C";
|
const char HA_UNIT_OF_MEASUREMENT_C[] PROGMEM = "°C";
|
||||||
const char HA_UNIT_OF_MEASUREMENT_F[] PROGMEM = "°F";
|
const char HA_UNIT_OF_MEASUREMENT_F[] PROGMEM = "°F";
|
||||||
const char HA_UNIT_OF_MEASUREMENT_PERCENT[] PROGMEM = "%";
|
const char HA_UNIT_OF_MEASUREMENT_PERCENT[] PROGMEM = "%";
|
||||||
|
const char HA_UNIT_OF_MEASUREMENT_L_MIN[] PROGMEM = "L/min";
|
||||||
|
const char HA_UNIT_OF_MEASUREMENT_GAL_MIN[] PROGMEM = "gal/min";
|
||||||
const char HA_ICON[] PROGMEM = "icon";
|
const char HA_ICON[] PROGMEM = "icon";
|
||||||
const char HA_MIN[] PROGMEM = "min";
|
const char HA_MIN[] PROGMEM = "min";
|
||||||
const char HA_MAX[] PROGMEM = "max";
|
const char HA_MAX[] PROGMEM = "max";
|
||||||
|
|||||||
@@ -218,7 +218,6 @@ protected:
|
|||||||
CanHandleCallback canHandleCallback;
|
CanHandleCallback canHandleCallback;
|
||||||
BeforeSendCallback beforeSendCallback;
|
BeforeSendCallback beforeSendCallback;
|
||||||
TemplateCallback templateCallback;
|
TemplateCallback templateCallback;
|
||||||
String eTag;
|
|
||||||
const char* uri = nullptr;
|
const char* uri = nullptr;
|
||||||
const char* path = nullptr;
|
const char* path = nullptr;
|
||||||
const char* cacheHeader = nullptr;
|
const char* cacheHeader = nullptr;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
#include <detail/mimetable.h>
|
#include <detail/mimetable.h>
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
#include <detail/RequestHandlersImpl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace mime;
|
using namespace mime;
|
||||||
|
|
||||||
@@ -47,21 +50,25 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
|
||||||
if (server._eTagEnabled) {
|
if (server._eTagEnabled) {
|
||||||
if (server._eTagFunction) {
|
if (this->eTag.isEmpty()) {
|
||||||
this->eTag = (server._eTagFunction)(*this->fs, this->path);
|
if (server._eTagFunction) {
|
||||||
|
this->eTag = (server._eTagFunction)(*this->fs, this->path);
|
||||||
|
|
||||||
} else if (this->eTag.isEmpty()) {
|
} else {
|
||||||
this->eTag = esp8266webserver::calcETag(*this->fs, this->path);
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
|
this->eTag = esp8266webserver::calcETag(*this->fs, this->path);
|
||||||
|
#elif defined(ARDUINO_ARCH_ESP32)
|
||||||
|
this->eTag = StaticRequestHandler::calcETag(*this->fs, this->path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.header(F("If-None-Match")).equals(this->eTag.c_str())) {
|
if (!this->eTag.isEmpty() && server.header(F("If-None-Match")).equals(this->eTag.c_str())) {
|
||||||
server.send(304);
|
server.send(304);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!this->path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !this->fs->exists(path)) {
|
if (!this->path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !this->fs->exists(path)) {
|
||||||
String pathWithGz = this->path + FPSTR(mimeTable[gz].endsWith);
|
String pathWithGz = this->path + FPSTR(mimeTable[gz].endsWith);
|
||||||
@@ -84,11 +91,11 @@ public:
|
|||||||
server.sendHeader(F("Cache-Control"), this->cacheHeader);
|
server.sendHeader(F("Cache-Control"), this->cacheHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
if (server._eTagEnabled && !this->eTag.isEmpty()) {
|
||||||
if (server._eTagEnabled && this->eTag.length() > 0) {
|
|
||||||
server.sendHeader(F("ETag"), this->eTag);
|
server.sendHeader(F("ETag"), this->eTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
server.streamFile(file, F("text/html"), method);
|
server.streamFile(file, F("text/html"), method);
|
||||||
#else
|
#else
|
||||||
server.streamFile(file, F("text/html"), 200);
|
server.streamFile(file, F("text/html"), 200);
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is needed by the Arduino IDE because the ino file needs to be named as the directory name.
|
|
||||||
Don't worry, the Arduino compiler will "merge" all files, including src/main.cpp
|
|
||||||
*/
|
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"gulp-html-minifier-terser": "^7.1.0",
|
"gulp-html-minifier-terser": "^7.1.0",
|
||||||
"gulp-jsonminify": "^1.1.0",
|
"gulp-jsonminify": "^1.1.0",
|
||||||
"gulp-postcss": "^10.0.0",
|
"gulp-postcss": "^10.0.0",
|
||||||
"gulp-terser": "^2.1.0"
|
"gulp-terser": "^2.1.0",
|
||||||
|
"gulp-replace": "^1.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ extra_configs = secrets.default.ini
|
|||||||
core_dir = .pio
|
core_dir = .pio
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
version = 1.5.0-alpha
|
version = 1.5.0
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson@^7.1.0
|
bblanchon/ArduinoJson@^7.1.0
|
||||||
@@ -30,9 +30,6 @@ lib_deps =
|
|||||||
laxilef/TinyLogger@^1.1.1
|
laxilef/TinyLogger@^1.1.1
|
||||||
build_type = ${secrets.build_type}
|
build_type = ${secrets.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
|
||||||
;-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
|
||||||
-D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
|
|
||||||
-mtext-section-literals
|
-mtext-section-literals
|
||||||
-D MQTT_CLIENT_STD_FUNCTION_CALLBACK=1
|
-D MQTT_CLIENT_STD_FUNCTION_CALLBACK=1
|
||||||
;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_HTTP_SERVER -D DEBUG_ESP_PORT=Serial
|
;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_HTTP_SERVER -D DEBUG_ESP_PORT=Serial
|
||||||
@@ -75,7 +72,11 @@ lib_ignore =
|
|||||||
extra_scripts =
|
extra_scripts =
|
||||||
post:tools/build.py
|
post:tools/build.py
|
||||||
build_type = ${env.build_type}
|
build_type = ${env.build_type}
|
||||||
build_flags = ${env.build_flags}
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
-D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
||||||
|
;-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||||
|
-D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
|
||||||
board_build.ldscript = eagle.flash.4m1m.ld
|
board_build.ldscript = eagle.flash.4m1m.ld
|
||||||
|
|
||||||
[esp32_defaults]
|
[esp32_defaults]
|
||||||
@@ -84,13 +85,13 @@ board_build.ldscript = eagle.flash.4m1m.ld
|
|||||||
;platform_packages =
|
;platform_packages =
|
||||||
; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5
|
; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5
|
||||||
; framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.1/esp32-arduino-libs-idf-release_v5.1-33fbade6.zip
|
; framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.1/esp32-arduino-libs-idf-release_v5.1-33fbade6.zip
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc2/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
|
||||||
platform_packages =
|
platform_packages =
|
||||||
board_build.partitions = esp32_partitions.csv
|
board_build.partitions = esp32_partitions.csv
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
laxilef/ESP32Scheduler@^1.0.1
|
laxilef/ESP32Scheduler@^1.0.1
|
||||||
nimble_lib = h2zero/NimBLE-Arduino@^1.4.2
|
nimble_lib = h2zero/NimBLE-Arduino@^2.1.0
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
post:tools/esp32.py
|
post:tools/esp32.py
|
||||||
|
|||||||
168
src/HaHelper.h
@@ -37,7 +37,7 @@ public:
|
|||||||
case Sensors::Purpose::DHW_RETURN_TEMP:
|
case Sensors::Purpose::DHW_RETURN_TEMP:
|
||||||
case Sensors::Purpose::EXHAUST_TEMP:
|
case Sensors::Purpose::EXHAUST_TEMP:
|
||||||
case Sensors::Purpose::TEMPERATURE:
|
case Sensors::Purpose::TEMPERATURE:
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
|
|
||||||
@@ -59,23 +59,33 @@ public:
|
|||||||
case Sensors::Purpose::DHW_FLOW_RATE:
|
case Sensors::Purpose::DHW_FLOW_RATE:
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("volume_flow_rate");
|
doc[FPSTR(HA_DEVICE_CLASS)] = F("volume_flow_rate");
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_L_MIN);
|
||||||
|
|
||||||
} else if (unit == UnitSystem::IMPERIAL) {
|
} else if (unit == UnitSystem::IMPERIAL) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_GAL_MIN);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::Purpose::MODULATION_LEVEL:
|
case Sensors::Purpose::MODULATION_LEVEL:
|
||||||
|
case Sensors::Purpose::POWER_FACTOR:
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("power_factor");
|
doc[FPSTR(HA_DEVICE_CLASS)] = F("power_factor");
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::Purpose::CURRENT_POWER:
|
case Sensors::Purpose::POWER:
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("power");
|
doc[FPSTR(HA_DEVICE_CLASS)] = F("power");
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("kW");
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("kW");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Sensors::Purpose::FAN_SPEED:
|
||||||
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("RPM");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Sensors::Purpose::CO2:
|
||||||
|
doc[FPSTR(HA_DEVICE_CLASS)] = F("carbon_dioxide");
|
||||||
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("ppm");
|
||||||
|
break;
|
||||||
|
|
||||||
case Sensors::Purpose::PRESSURE:
|
case Sensors::Purpose::PRESSURE:
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("pressure");
|
doc[FPSTR(HA_DEVICE_CLASS)] = F("pressure");
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
@@ -87,7 +97,7 @@ public:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::Purpose::HUMIDITY:
|
case Sensors::Purpose::HUMIDITY:
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("humidity");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_HUMIDITY);
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -137,10 +147,15 @@ public:
|
|||||||
doc[FPSTR(HA_ICON)] = F("mdi:fire-circle");
|
doc[FPSTR(HA_ICON)] = F("mdi:fire-circle");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::Purpose::CURRENT_POWER:
|
case Sensors::Purpose::POWER_FACTOR:
|
||||||
|
case Sensors::Purpose::POWER:
|
||||||
doc[FPSTR(HA_ICON)] = F("mdi:chart-bar");
|
doc[FPSTR(HA_ICON)] = F("mdi:chart-bar");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Sensors::Purpose::FAN_SPEED:
|
||||||
|
doc[FPSTR(HA_ICON)] = F("mdi:fan");
|
||||||
|
break;
|
||||||
|
|
||||||
case Sensors::Purpose::PRESSURE:
|
case Sensors::Purpose::PRESSURE:
|
||||||
doc[FPSTR(HA_ICON)] = F("mdi:gauge");
|
doc[FPSTR(HA_ICON)] = F("mdi:gauge");
|
||||||
break;
|
break;
|
||||||
@@ -156,23 +171,25 @@ public:
|
|||||||
String objId = Sensors::makeObjectId(sSensor.name);
|
String objId = Sensors::makeObjectId(sSensor.name);
|
||||||
|
|
||||||
// state topic
|
// state topic
|
||||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("sensors"), objId.c_str());
|
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(
|
||||||
|
F("sensors"),
|
||||||
|
objId.c_str()
|
||||||
|
);
|
||||||
|
|
||||||
// set device class, name, value template for bluetooth sensors
|
// set device class, name, value template for bluetooth sensors
|
||||||
// or name & value template for another sensors
|
// or name & value template for another sensors
|
||||||
String sName = sSensor.name;
|
|
||||||
|
|
||||||
if (sSensor.type == Sensors::Type::BLUETOOTH) {
|
if (sSensor.type == Sensors::Type::BLUETOOTH) {
|
||||||
// available state topic
|
// available state topic
|
||||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = doc[FPSTR(HA_STATE_TOPIC)];
|
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = doc[FPSTR(HA_STATE_TOPIC)];
|
||||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = AVAILABILITY_SENSOR_CONN;
|
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = AVAILABILITY_SENSOR_CONN;
|
||||||
|
|
||||||
|
String sName = sSensor.name;
|
||||||
switch (vType) {
|
switch (vType) {
|
||||||
case Sensors::ValueType::TEMPERATURE:
|
case Sensors::ValueType::TEMPERATURE:
|
||||||
objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("temp"));
|
Sensors::makeObjectIdWithSuffix(objId, sSensor.name, F("temp"));
|
||||||
sName += F(" temperature");
|
sName += F(" temperature");
|
||||||
|
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
|
|
||||||
@@ -184,20 +201,20 @@ public:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::ValueType::HUMIDITY:
|
case Sensors::ValueType::HUMIDITY:
|
||||||
objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("humidity"));
|
Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_HUMIDITY));
|
||||||
sName += F(" humidity");
|
sName += F(" humidity");
|
||||||
|
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("humidity");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_HUMIDITY);
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT);
|
||||||
doc[FPSTR(HA_NAME)] = sName;
|
doc[FPSTR(HA_NAME)] = sName;
|
||||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.humidity|float(0)|round(2) }}");
|
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.humidity|float(0)|round(2) }}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::ValueType::BATTERY:
|
case Sensors::ValueType::BATTERY:
|
||||||
objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("battery"));
|
Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_BATTERY));
|
||||||
sName += F(" battery");
|
sName += F(" battery");
|
||||||
|
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("battery");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_BATTERY);
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC);
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT);
|
||||||
doc[FPSTR(HA_NAME)] = sName;
|
doc[FPSTR(HA_NAME)] = sName;
|
||||||
@@ -205,7 +222,7 @@ public:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::ValueType::RSSI:
|
case Sensors::ValueType::RSSI:
|
||||||
objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("rssi"));
|
Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_RSSI));
|
||||||
sName += F(" RSSI");
|
sName += F(" RSSI");
|
||||||
|
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength");
|
doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength");
|
||||||
@@ -221,12 +238,17 @@ public:
|
|||||||
|
|
||||||
} else if (sSensor.type == Sensors::Type::MANUAL) {
|
} else if (sSensor.type == Sensors::Type::MANUAL) {
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
|
doc[FPSTR(HA_STEP)] = 0.01f;
|
||||||
doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX);
|
doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX);
|
||||||
|
|
||||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("sensors"), objId.c_str(), F("set"));
|
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(
|
||||||
|
F("sensors"),
|
||||||
|
objId.c_str(),
|
||||||
|
F("set")
|
||||||
|
);
|
||||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"value\": {{ value }}}");
|
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"value\": {{ value }}}");
|
||||||
|
|
||||||
doc[FPSTR(HA_NAME)] = sName;
|
doc[FPSTR(HA_NAME)] = sSensor.name;
|
||||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.value|float(0)|round(2) }}");
|
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.value|float(0)|round(2) }}");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -234,20 +256,15 @@ public:
|
|||||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = doc[FPSTR(HA_STATE_TOPIC)];
|
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = doc[FPSTR(HA_STATE_TOPIC)];
|
||||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = AVAILABILITY_SENSOR_CONN;
|
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = AVAILABILITY_SENSOR_CONN;
|
||||||
|
|
||||||
doc[FPSTR(HA_NAME)] = sName;
|
doc[FPSTR(HA_NAME)] = sSensor.name;
|
||||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.value|float(0)|round(2) }}");
|
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.value|float(0)|round(2) }}");
|
||||||
}
|
}
|
||||||
|
|
||||||
sName.clear();
|
|
||||||
|
|
||||||
// object id's
|
// object id's
|
||||||
{
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str());
|
||||||
String objIdWithPrefix = this->getObjectIdWithPrefix(objId.c_str());
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = objIdWithPrefix;
|
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = objIdWithPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
String configTopic = this->makeConfigTopic(
|
const String& configTopic = this->makeConfigTopic(
|
||||||
sSensor.type == Sensors::Type::MANUAL ? FPSTR(HA_ENTITY_NUMBER) : FPSTR(HA_ENTITY_SENSOR),
|
sSensor.type == Sensors::Type::MANUAL ? FPSTR(HA_ENTITY_NUMBER) : FPSTR(HA_ENTITY_SENSOR),
|
||||||
objId.c_str()
|
objId.c_str()
|
||||||
);
|
);
|
||||||
@@ -264,32 +281,35 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool deleteDynamicSensor(Sensors::Settings& sSensor, Sensors::ValueType vType = Sensors::ValueType::PRIMARY) {
|
bool deleteDynamicSensor(Sensors::Settings& sSensor, Sensors::ValueType vType = Sensors::ValueType::PRIMARY) {
|
||||||
String objId = Sensors::makeObjectId(sSensor.name);
|
String objId;
|
||||||
|
|
||||||
if (sSensor.type == Sensors::Type::BLUETOOTH) {
|
if (sSensor.type == Sensors::Type::BLUETOOTH) {
|
||||||
switch (vType) {
|
switch (vType) {
|
||||||
case Sensors::ValueType::TEMPERATURE:
|
case Sensors::ValueType::TEMPERATURE:
|
||||||
objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("temp"));
|
Sensors::makeObjectIdWithSuffix(objId, sSensor.name, F("temp"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::ValueType::HUMIDITY:
|
case Sensors::ValueType::HUMIDITY:
|
||||||
objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("humidity"));
|
Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_HUMIDITY));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::ValueType::BATTERY:
|
case Sensors::ValueType::BATTERY:
|
||||||
objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("battery"));
|
Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_BATTERY));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::ValueType::RSSI:
|
case Sensors::ValueType::RSSI:
|
||||||
objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("rssi"));
|
Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_RSSI));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Sensors::makeObjectId(objId, sSensor.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
String configTopic = this->makeConfigTopic(
|
const String& configTopic = this->makeConfigTopic(
|
||||||
sSensor.type == Sensors::Type::MANUAL ? FPSTR(HA_ENTITY_NUMBER) : FPSTR(HA_ENTITY_SENSOR),
|
sSensor.type == Sensors::Type::MANUAL ? FPSTR(HA_ENTITY_NUMBER) : FPSTR(HA_ENTITY_SENSOR),
|
||||||
objId.c_str()
|
objId.c_str()
|
||||||
);
|
);
|
||||||
@@ -303,18 +323,14 @@ public:
|
|||||||
String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("connected"));
|
String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("connected"));
|
||||||
|
|
||||||
// object id's
|
// object id's
|
||||||
{
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str());
|
||||||
String objIdWithPrefix = this->getObjectIdWithPrefix(objId.c_str());
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = objIdWithPrefix;
|
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = objIdWithPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
// state topic
|
// state topic
|
||||||
{
|
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(
|
||||||
String parentObjId = Sensors::makeObjectId(sSensor.name);
|
F("sensors"),
|
||||||
String stateTopic = this->getDeviceTopic(F("sensors"), parentObjId.c_str());
|
Sensors::makeObjectId(sSensor.name).c_str()
|
||||||
doc[FPSTR(HA_STATE_TOPIC)] = stateTopic;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// sensor name
|
// sensor name
|
||||||
{
|
{
|
||||||
@@ -325,7 +341,7 @@ public:
|
|||||||
doc[FPSTR(HA_NAME)] = sName;
|
doc[FPSTR(HA_NAME)] = sName;
|
||||||
}
|
}
|
||||||
|
|
||||||
String configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_BINARY_SENSOR), objId.c_str());
|
const String& configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_BINARY_SENSOR), objId.c_str());
|
||||||
objId.clear();
|
objId.clear();
|
||||||
|
|
||||||
|
|
||||||
@@ -341,9 +357,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool deleteConnectionDynamicSensor(Sensors::Settings& sSensor) {
|
bool deleteConnectionDynamicSensor(Sensors::Settings& sSensor) {
|
||||||
String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("connected"));
|
const String& configTopic = this->makeConfigTopic(
|
||||||
String configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_BINARY_SENSOR), objId.c_str());
|
FPSTR(HA_ENTITY_BINARY_SENSOR),
|
||||||
objId.clear();
|
Sensors::makeObjectIdWithSuffix(sSensor.name, F("connected")).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
return this->publish(configTopic.c_str());
|
return this->publish(configTopic.c_str());
|
||||||
}
|
}
|
||||||
@@ -353,18 +370,14 @@ public:
|
|||||||
String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality"));
|
String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality"));
|
||||||
|
|
||||||
// object id's
|
// object id's
|
||||||
{
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str());
|
||||||
String objIdWithPrefix = this->getObjectIdWithPrefix(objId.c_str());
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = objIdWithPrefix;
|
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = objIdWithPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
// state topic
|
// state topic
|
||||||
{
|
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(
|
||||||
String parentObjId = Sensors::makeObjectId(sSensor.name);
|
F("sensors"),
|
||||||
String stateTopic = this->getDeviceTopic(F("sensors"), parentObjId.c_str());
|
Sensors::makeObjectId(sSensor.name).c_str()
|
||||||
doc[FPSTR(HA_STATE_TOPIC)] = stateTopic;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// sensor name
|
// sensor name
|
||||||
{
|
{
|
||||||
@@ -375,7 +388,7 @@ public:
|
|||||||
doc[FPSTR(HA_NAME)] = sName;
|
doc[FPSTR(HA_NAME)] = sName;
|
||||||
}
|
}
|
||||||
|
|
||||||
String configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), objId.c_str());
|
const String& configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), objId.c_str());
|
||||||
objId.clear();
|
objId.clear();
|
||||||
|
|
||||||
|
|
||||||
@@ -395,9 +408,10 @@ public:
|
|||||||
|
|
||||||
bool deleteSignalQualityDynamicSensor(Sensors::Settings& sSensor) {
|
bool deleteSignalQualityDynamicSensor(Sensors::Settings& sSensor) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality"));
|
const String& configTopic = this->makeConfigTopic(
|
||||||
String configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), objId.c_str());
|
FPSTR(HA_ENTITY_SENSOR),
|
||||||
objId.clear();
|
Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality")).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
return this->publish(configTopic.c_str());
|
return this->publish(configTopic.c_str());
|
||||||
}
|
}
|
||||||
@@ -432,7 +446,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_hysteresis"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_hysteresis"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
|
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
@@ -488,7 +502,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_min_temp"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_min_temp"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
|
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
@@ -522,7 +536,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_max_temp"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_max_temp"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
|
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
@@ -557,7 +571,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_min_temp"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_min_temp"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
|
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
@@ -591,7 +605,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_max_temp"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_max_temp"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
|
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
@@ -742,7 +756,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_min_temp"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_min_temp"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
|
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
@@ -776,7 +790,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_max_temp"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_max_temp"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
||||||
|
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||||
@@ -1119,7 +1133,7 @@ public:
|
|||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("rssi"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(S_RSSI));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength");
|
doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength");
|
||||||
@@ -1132,7 +1146,7 @@ public:
|
|||||||
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), F("rssi")).c_str(), doc);
|
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), FPSTR(S_RSSI)).c_str(), doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishUptime(bool enabledByDefault = true) {
|
bool publishUptime(bool enabledByDefault = true) {
|
||||||
@@ -1190,7 +1204,7 @@ public:
|
|||||||
doc[FPSTR(HA_MODES)][1] = F("heat");
|
doc[FPSTR(HA_MODES)][1] = F("heat");
|
||||||
|
|
||||||
doc[FPSTR(HA_ACTION_TOPIC)] = this->stateTopic.c_str();
|
doc[FPSTR(HA_ACTION_TOPIC)] = this->stateTopic.c_str();
|
||||||
doc[FPSTR(HA_ACTION_TEMPLATE)] = F("{{ iif(value_json.master.heating.enabled, iif(value_json.slave.heating.active, 'heating', 'idle'), 'off') }}");
|
doc[FPSTR(HA_ACTION_TEMPLATE)] = F("{{ iif(value_json.slave.heating.active, 'heating', 'idle') }}");
|
||||||
|
|
||||||
doc[FPSTR(HA_PRESET_MODE_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
doc[FPSTR(HA_PRESET_MODE_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
||||||
doc[FPSTR(HA_PRESET_MODE_COMMAND_TEMPLATE)] = F("{% if value == 'boost' %}{\"heating\": {\"turbo\" : true}}"
|
doc[FPSTR(HA_PRESET_MODE_COMMAND_TEMPLATE)] = F("{% if value == 'boost' %}{\"heating\": {\"turbo\" : true}}"
|
||||||
@@ -1242,7 +1256,7 @@ public:
|
|||||||
doc[FPSTR(HA_MODES)][1] = F("heat");
|
doc[FPSTR(HA_MODES)][1] = F("heat");
|
||||||
|
|
||||||
doc[FPSTR(HA_ACTION_TOPIC)] = this->stateTopic.c_str();
|
doc[FPSTR(HA_ACTION_TOPIC)] = this->stateTopic.c_str();
|
||||||
doc[FPSTR(HA_ACTION_TEMPLATE)] = F("{{ iif(value_json.master.dhw.enabled, iif(value_json.slave.dhw.active, 'heating', 'idle'), 'off') }}");
|
doc[FPSTR(HA_ACTION_TEMPLATE)] = F("{{ iif(value_json.slave.dhw.active, 'heating', 'idle') }}");
|
||||||
|
|
||||||
doc[FPSTR(HA_MIN_TEMP)] = minTemp;
|
doc[FPSTR(HA_MIN_TEMP)] = minTemp;
|
||||||
doc[FPSTR(HA_MAX_TEMP)] = maxTemp;
|
doc[FPSTR(HA_MAX_TEMP)] = maxTemp;
|
||||||
@@ -1257,17 +1271,17 @@ public:
|
|||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("restart"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(S_RESTART));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("restart");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART);
|
||||||
doc[FPSTR(HA_NAME)] = F("Restart");
|
doc[FPSTR(HA_NAME)] = F("Restart");
|
||||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str();
|
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str();
|
||||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"restart\": true}}");
|
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"restart\": true}}");
|
||||||
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_BUTTON), F("restart")).c_str(), doc);
|
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_BUTTON), FPSTR(S_RESTART)).c_str(), doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishResetFaultButton(bool enabledByDefault = true) {
|
bool publishResetFaultButton(bool enabledByDefault = true) {
|
||||||
@@ -1280,7 +1294,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_fault"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_fault"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("restart");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART);
|
||||||
doc[FPSTR(HA_NAME)] = F("Reset fault");
|
doc[FPSTR(HA_NAME)] = F("Reset fault");
|
||||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str();
|
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str();
|
||||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"resetFault\": true}}");
|
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"resetFault\": true}}");
|
||||||
@@ -1300,7 +1314,7 @@ public:
|
|||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_diagnostic"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_diagnostic"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("restart");
|
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART);
|
||||||
doc[FPSTR(HA_NAME)] = F("Reset diagnostic");
|
doc[FPSTR(HA_NAME)] = F("Reset diagnostic");
|
||||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str();
|
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str();
|
||||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"resetDiagnostic\": true}}");
|
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"resetDiagnostic\": true}}");
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ protected:
|
|||||||
unsigned long lastHeapInfo = 0;
|
unsigned long lastHeapInfo = 0;
|
||||||
unsigned int minFreeHeap = 0;
|
unsigned int minFreeHeap = 0;
|
||||||
unsigned int minMaxFreeBlockHeap = 0;
|
unsigned int minMaxFreeBlockHeap = 0;
|
||||||
unsigned long restartSignalTime = 0;
|
bool restartSignalReceived = false;
|
||||||
|
unsigned long restartSignalReceivedTime = 0;
|
||||||
bool heatingEnabled = false;
|
bool heatingEnabled = false;
|
||||||
unsigned long heatingDisabledTime = 0;
|
unsigned long heatingDisabledTime = 0;
|
||||||
PumpStartReason extPumpStartReason = PumpStartReason::NONE;
|
PumpStartReason extPumpStartReason = PumpStartReason::NONE;
|
||||||
@@ -73,8 +74,15 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (vars.actions.restart) {
|
if (vars.actions.restart) {
|
||||||
|
this->restartSignalReceivedTime = millis();
|
||||||
|
this->restartSignalReceived = true;
|
||||||
vars.actions.restart = false;
|
vars.actions.restart = false;
|
||||||
this->restartSignalTime = millis();
|
|
||||||
|
Log.sinfoln(FPSTR(L_MAIN), F("Received restart signal"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vars.states.restarting && this->restartSignalReceived && millis() - this->restartSignalReceivedTime > 5000) {
|
||||||
|
vars.states.restarting = true;
|
||||||
|
|
||||||
// save settings
|
// save settings
|
||||||
fsSettings.updateNow();
|
fsSettings.updateNow();
|
||||||
@@ -87,7 +95,7 @@ protected:
|
|||||||
fsNetworkSettings.write();
|
fsNetworkSettings.write();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec."));
|
Log.sinfoln(FPSTR(L_MAIN), F("Restart scheduled in 10 sec."));
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.mqtt.connected = tMqtt->isConnected();
|
vars.mqtt.connected = tMqtt->isConnected();
|
||||||
@@ -147,8 +155,9 @@ protected:
|
|||||||
for (Stream* stream : Log.getStreams()) {
|
for (Stream* stream : Log.getStreams()) {
|
||||||
while (stream->available() > 0) {
|
while (stream->available() > 0) {
|
||||||
stream->read();
|
stream->read();
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,8 +167,9 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
// restart
|
// restart
|
||||||
if (this->restartSignalTime > 0 && millis() - this->restartSignalTime > 10000) {
|
if (this->restartSignalReceived && millis() - this->restartSignalReceivedTime > 15000) {
|
||||||
this->restartSignalTime = 0;
|
this->restartSignalReceived = false;
|
||||||
|
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,8 +178,10 @@ protected:
|
|||||||
unsigned int freeHeap = getFreeHeap();
|
unsigned int freeHeap = getFreeHeap();
|
||||||
unsigned int maxFreeBlockHeap = getMaxFreeBlockHeap();
|
unsigned int maxFreeBlockHeap = getMaxFreeBlockHeap();
|
||||||
|
|
||||||
if (!this->restartSignalTime && (freeHeap < 2048 || maxFreeBlockHeap < 2048)) {
|
// critical heap
|
||||||
this->restartSignalTime = millis();
|
if (!vars.states.restarting && (freeHeap < 2048 || maxFreeBlockHeap < 2048)) {
|
||||||
|
this->restartSignalReceivedTime = millis();
|
||||||
|
vars.states.restarting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.system.logLevel < TinyLogger::Level::VERBOSE) {
|
if (settings.system.logLevel < TinyLogger::Level::VERBOSE) {
|
||||||
@@ -206,13 +218,13 @@ protected:
|
|||||||
|
|
||||||
// set outdoor sensor flag
|
// set outdoor sensor flag
|
||||||
if (settings.equitherm.enabled) {
|
if (settings.equitherm.enabled) {
|
||||||
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) {
|
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP)) {
|
||||||
emergencyFlags |= 0b00000001;
|
emergencyFlags |= 0b00000001;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set indoor sensor flags
|
// set indoor sensor flags
|
||||||
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP)) {
|
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) {
|
||||||
if (!settings.equitherm.enabled && settings.pid.enabled) {
|
if (!settings.equitherm.enabled && settings.pid.enabled) {
|
||||||
emergencyFlags |= 0b00000010;
|
emergencyFlags |= 0b00000010;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->client->onMessage([this] (void*, size_t length) {
|
this->client->onMessage([this] (void*, size_t length) {
|
||||||
String topic = this->client->messageTopic();
|
const String& topic = this->client->messageTopic();
|
||||||
if (!length || length > 2048 || !topic.length()) {
|
if (!length || length > 2048 || !topic.length()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ protected:
|
|||||||
payload[i] = this->client->read();
|
payload[i] = this->client->read();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->onMessage(topic.c_str(), payload, length);
|
this->onMessage(topic, payload, length);
|
||||||
});
|
});
|
||||||
|
|
||||||
// writer settings
|
// writer settings
|
||||||
@@ -153,7 +153,7 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_MQTT), F("%s publish %u of %u bytes to topic: %s"), result ? F("Successfully") : F("Failed"), written, length, topic);
|
Log.straceln(FPSTR(L_MQTT), F("%s publish %u of %u bytes to topic: %s"), result ? F("Successfully") : F("Failed"), written, length, topic);
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//this->client->poll();
|
//this->client->poll();
|
||||||
@@ -162,13 +162,13 @@ protected:
|
|||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
this->writer->setFlushEventCallback([this] (size_t, size_t) {
|
this->writer->setFlushEventCallback([this] (size_t, size_t) {
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
|
|
||||||
if (this->wifiClient->connected()) {
|
if (this->wifiClient->connected()) {
|
||||||
this->wifiClient->flush();
|
this->wifiClient->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -184,6 +184,10 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
if (vars.states.restarting || vars.states.upgrading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->connected && !this->client->connected()) {
|
if (this->connected && !this->client->connected()) {
|
||||||
this->connected = false;
|
this->connected = false;
|
||||||
this->onDisconnect();
|
this->onDisconnect();
|
||||||
@@ -222,7 +226,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// publish variables and status
|
// publish variables and status
|
||||||
@@ -295,14 +299,14 @@ protected:
|
|||||||
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::TEMPERATURE);
|
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::TEMPERATURE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::Type::MANUAL: {
|
case Sensors::Type::MANUAL:
|
||||||
String topic = this->haHelper->getDeviceTopic(
|
this->client->unsubscribe(
|
||||||
F("sensors"),
|
this->haHelper->getDeviceTopic(
|
||||||
Sensors::makeObjectId(prevSettings.name).c_str(),
|
F("sensors"),
|
||||||
F("set")
|
Sensors::makeObjectId(prevSettings.name).c_str(),
|
||||||
|
F("set")
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
this->client->unsubscribe(topic.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::PRIMARY);
|
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::PRIMARY);
|
||||||
@@ -331,14 +335,14 @@ protected:
|
|||||||
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::Type::MANUAL: {
|
case Sensors::Type::MANUAL:
|
||||||
String topic = this->haHelper->getDeviceTopic(
|
this->client->subscribe(
|
||||||
F("sensors"),
|
this->haHelper->getDeviceTopic(
|
||||||
Sensors::makeObjectId(prevSettings.name).c_str(),
|
F("sensors"),
|
||||||
F("set")
|
Sensors::makeObjectId(prevSettings.name).c_str(),
|
||||||
|
F("set")
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
this->client->subscribe(topic.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem);
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem);
|
||||||
@@ -372,13 +376,13 @@ protected:
|
|||||||
Log.swarningln(FPSTR(L_MQTT), F("Disconnected (reason: %d uptime: %lu s.)"), this->client->connectError(), uptime);
|
Log.swarningln(FPSTR(L_MQTT), F("Disconnected (reason: %d uptime: %lu s.)"), this->client->connectError(), uptime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onMessage(const char* topic, uint8_t* payload, size_t length) {
|
void onMessage(const String& topic, uint8_t* payload, size_t length) {
|
||||||
if (!length) {
|
if (!length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.system.logLevel >= TinyLogger::Level::TRACE) {
|
if (settings.system.logLevel >= TinyLogger::Level::TRACE) {
|
||||||
Log.strace(FPSTR(L_MQTT_MSG), F("Topic: %s\r\n> "), topic);
|
Log.strace(FPSTR(L_MQTT_MSG), F("Topic: %s\r\n> "), topic.c_str());
|
||||||
if (Log.lock()) {
|
if (Log.lock()) {
|
||||||
for (size_t i = 0; i < length; i++) {
|
for (size_t i = 0; i < length; i++) {
|
||||||
if (payload[i] == 0) {
|
if (payload[i] == 0) {
|
||||||
@@ -409,31 +413,27 @@ protected:
|
|||||||
}
|
}
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
|
// delete topic
|
||||||
|
this->writer->publish(topic.c_str(), nullptr, 0, true);
|
||||||
|
|
||||||
if (this->haHelper->getDeviceTopic(F("state/set")).equals(topic)) {
|
if (this->haHelper->getDeviceTopic(F("state/set")).equals(topic)) {
|
||||||
this->writer->publish(topic, nullptr, 0, true);
|
|
||||||
|
|
||||||
if (jsonToVars(doc, vars)) {
|
if (jsonToVars(doc, vars)) {
|
||||||
this->resetPublishedVarsTime();
|
this->resetPublishedVarsTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (this->haHelper->getDeviceTopic(F("settings/set")).equals(topic)) {
|
} else if (this->haHelper->getDeviceTopic(F("settings/set")).equals(topic)) {
|
||||||
this->writer->publish(topic, nullptr, 0, true);
|
|
||||||
|
|
||||||
if (safeJsonToSettings(doc, settings)) {
|
if (safeJsonToSettings(doc, settings)) {
|
||||||
this->resetPublishedSettingsTime();
|
this->resetPublishedSettingsTime();
|
||||||
fsSettings.update();
|
fsSettings.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this->writer->publish(topic, nullptr, 0, true);
|
const String& sensorsTopic = this->haHelper->getDeviceTopic(F("sensors/"));
|
||||||
|
|
||||||
String _topic = topic;
|
|
||||||
String sensorsTopic = this->haHelper->getDeviceTopic(F("sensors/"));
|
|
||||||
auto stLength = sensorsTopic.length();
|
auto stLength = sensorsTopic.length();
|
||||||
|
|
||||||
if (_topic.startsWith(sensorsTopic) && _topic.endsWith(F("/set"))) {
|
if (topic.startsWith(sensorsTopic) && topic.endsWith(F("/set"))) {
|
||||||
if (_topic.length() > stLength + 4) {
|
if (topic.length() > stLength + 4) {
|
||||||
String name = _topic.substring(stLength, _topic.indexOf('/', stLength));
|
const String& name = topic.substring(stLength, topic.indexOf('/', stLength));
|
||||||
int16_t id = Sensors::getIdByObjectId(name.c_str());
|
int16_t id = Sensors::getIdByObjectId(name.c_str());
|
||||||
|
|
||||||
if (id == -1) {
|
if (id == -1) {
|
||||||
@@ -515,14 +515,14 @@ protected:
|
|||||||
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Sensors::Type::MANUAL: {
|
case Sensors::Type::MANUAL:
|
||||||
String topic = this->haHelper->getDeviceTopic(
|
this->client->subscribe(
|
||||||
F("sensors"),
|
this->haHelper->getDeviceTopic(
|
||||||
Sensors::makeObjectId(sSettings.name).c_str(),
|
F("sensors"),
|
||||||
F("set")
|
Sensors::makeObjectId(sSettings.name).c_str(),
|
||||||
|
F("set")
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
this->client->subscribe(topic.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem);
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem);
|
||||||
@@ -603,12 +603,14 @@ protected:
|
|||||||
sensorResultToJson(sensorId, doc);
|
sensorResultToJson(sensorId, doc);
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
String topic = this->haHelper->getDeviceTopic(
|
return this->writer->publish(
|
||||||
F("sensors"),
|
this->haHelper->getDeviceTopic(
|
||||||
Sensors::makeObjectId(sSettings.name).c_str()
|
F("sensors"),
|
||||||
|
Sensors::makeObjectId(sSettings.name).c_str()
|
||||||
|
).c_str(),
|
||||||
|
doc,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
return this->writer->publish(topic.c_str(), doc, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishVariables(const char* topic) {
|
bool publishVariables(const char* topic) {
|
||||||
|
|||||||
1103
src/OpenThermTask.h
224
src/PortalTask.h
@@ -1,5 +1,5 @@
|
|||||||
#define PORTAL_CACHE_TIME "max-age=86400"
|
//#define PORTAL_CACHE "max-age=86400"
|
||||||
#define PORTAL_CACHE (settings.system.logLevel >= TinyLogger::Level::TRACE ? nullptr : PORTAL_CACHE_TIME)
|
#define PORTAL_CACHE nullptr
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
#include <Updater.h>
|
#include <Updater.h>
|
||||||
@@ -72,9 +72,24 @@ protected:
|
|||||||
void setup() {
|
void setup() {
|
||||||
this->dnsServer->setTTL(0);
|
this->dnsServer->setTTL(0);
|
||||||
this->dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
|
this->dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
this->webServer->enableETag(true, [](FS &fs, const String &fName) -> const String {
|
||||||
this->webServer->enableETag(true);
|
char buf[32];
|
||||||
#endif
|
{
|
||||||
|
MD5Builder md5;
|
||||||
|
md5.begin();
|
||||||
|
md5.add(fName);
|
||||||
|
md5.add(" " BUILD_ENV " " BUILD_VERSION " " __DATE__ " " __TIME__);
|
||||||
|
md5.calculate();
|
||||||
|
md5.getChars(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
String etag;
|
||||||
|
etag.reserve(34);
|
||||||
|
etag += '\"';
|
||||||
|
etag.concat(buf, 32);
|
||||||
|
etag += '\"';
|
||||||
|
return etag;
|
||||||
|
});
|
||||||
|
|
||||||
// index page
|
// index page
|
||||||
/*auto indexPage = (new DynamicPage("/", &LittleFS, "/pages/index.html"))
|
/*auto indexPage = (new DynamicPage("/", &LittleFS, "/pages/index.html"))
|
||||||
@@ -93,8 +108,8 @@ protected:
|
|||||||
// dashboard page
|
// dashboard page
|
||||||
auto dashboardPage = (new StaticPage("/dashboard.html", &LittleFS, F("/pages/dashboard.html"), PORTAL_CACHE))
|
auto dashboardPage = (new StaticPage("/dashboard.html", &LittleFS, F("/pages/dashboard.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(BASIC_AUTH);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,11 +119,9 @@ protected:
|
|||||||
|
|
||||||
// restart
|
// restart
|
||||||
this->webServer->on(F("/restart.html"), HTTP_GET, [this]() {
|
this->webServer->on(F("/restart.html"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
this->webServer->requestAuthentication(BASIC_AUTH);
|
||||||
this->webServer->send(401);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.actions.restart = true;
|
vars.actions.restart = true;
|
||||||
@@ -119,8 +132,8 @@ protected:
|
|||||||
// network settings page
|
// network settings page
|
||||||
auto networkPage = (new StaticPage("/network.html", &LittleFS, F("/pages/network.html"), PORTAL_CACHE))
|
auto networkPage = (new StaticPage("/network.html", &LittleFS, F("/pages/network.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(BASIC_AUTH);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,8 +144,8 @@ protected:
|
|||||||
// settings page
|
// settings page
|
||||||
auto settingsPage = (new StaticPage("/settings.html", &LittleFS, F("/pages/settings.html"), PORTAL_CACHE))
|
auto settingsPage = (new StaticPage("/settings.html", &LittleFS, F("/pages/settings.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(BASIC_AUTH);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,8 +156,8 @@ protected:
|
|||||||
// sensors page
|
// sensors page
|
||||||
auto sensorsPage = (new StaticPage("/sensors.html", &LittleFS, F("/pages/sensors.html"), PORTAL_CACHE))
|
auto sensorsPage = (new StaticPage("/sensors.html", &LittleFS, F("/pages/sensors.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(BASIC_AUTH);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,8 +168,8 @@ protected:
|
|||||||
// upgrade page
|
// upgrade page
|
||||||
auto upgradePage = (new StaticPage("/upgrade.html", &LittleFS, F("/pages/upgrade.html"), PORTAL_CACHE))
|
auto upgradePage = (new StaticPage("/upgrade.html", &LittleFS, F("/pages/upgrade.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(BASIC_AUTH);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +179,7 @@ protected:
|
|||||||
|
|
||||||
// OTA
|
// OTA
|
||||||
auto upgradeHandler = (new UpgradeHandler("/api/upgrade"))->setCanUploadCallback([this](const String& uri) {
|
auto upgradeHandler = (new UpgradeHandler("/api/upgrade"))->setCanUploadCallback([this](const String& uri) {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
this->webServer->sendHeader(F("Connection"), F("close"));
|
this->webServer->sendHeader(F("Connection"), F("close"));
|
||||||
this->webServer->send(401);
|
this->webServer->send(401);
|
||||||
return false;
|
return false;
|
||||||
@@ -174,6 +187,11 @@ protected:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
})->setBeforeUpgradeCallback([](UpgradeHandler::UpgradeType type) -> bool {
|
})->setBeforeUpgradeCallback([](UpgradeHandler::UpgradeType type) -> bool {
|
||||||
|
if (vars.states.restarting) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vars.states.upgrading = true;
|
||||||
return true;
|
return true;
|
||||||
})->setAfterUpgradeCallback([this](const UpgradeHandler::UpgradeResult& fwResult, const UpgradeHandler::UpgradeResult& fsResult) {
|
})->setAfterUpgradeCallback([this](const UpgradeHandler::UpgradeResult& fwResult, const UpgradeHandler::UpgradeResult& fsResult) {
|
||||||
unsigned short status = 200;
|
unsigned short status = 200;
|
||||||
@@ -194,16 +212,16 @@ protected:
|
|||||||
response.concat(fsResult.error);
|
response.concat(fsResult.error);
|
||||||
response.concat(F("\"}}"));
|
response.concat(F("\"}}"));
|
||||||
this->webServer->send(status, F("application/json"), response);
|
this->webServer->send(status, F("application/json"), response);
|
||||||
|
|
||||||
|
vars.states.upgrading = false;
|
||||||
});
|
});
|
||||||
this->webServer->addHandler(upgradeHandler);
|
this->webServer->addHandler(upgradeHandler);
|
||||||
|
|
||||||
|
|
||||||
// backup
|
// backup
|
||||||
this->webServer->on(F("/api/backup/save"), HTTP_GET, [this]() {
|
this->webServer->on(F("/api/backup/save"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
@@ -226,13 +244,15 @@ protected:
|
|||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on(F("/api/backup/restore"), HTTP_POST, [this]() {
|
this->webServer->on(F("/api/backup/restore"), HTTP_POST, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String plain = this->webServer->arg(0);
|
if (vars.states.restarting) {
|
||||||
|
return this->webServer->send(503);
|
||||||
|
}
|
||||||
|
|
||||||
|
const String& plain = this->webServer->arg(0);
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/backup/restore %d bytes: %s"), plain.length(), plain.c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/backup/restore %d bytes: %s"), plain.length(), plain.c_str());
|
||||||
|
|
||||||
if (plain.length() < 5) {
|
if (plain.length() < 5) {
|
||||||
@@ -246,7 +266,6 @@ protected:
|
|||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, plain);
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
plain.clear();
|
|
||||||
|
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
this->webServer->send(400);
|
this->webServer->send(400);
|
||||||
@@ -254,12 +273,6 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
if (!doc[FPSTR(S_SETTINGS)].isNull() && jsonToSettings(doc[FPSTR(S_SETTINGS)], settings)) {
|
|
||||||
vars.actions.restart = true;
|
|
||||||
fsSettings.update();
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!doc[FPSTR(S_NETWORK)].isNull() && jsonToNetworkSettings(doc[FPSTR(S_NETWORK)], networkSettings)) {
|
if (!doc[FPSTR(S_NETWORK)].isNull() && jsonToNetworkSettings(doc[FPSTR(S_NETWORK)], networkSettings)) {
|
||||||
fsNetworkSettings.update();
|
fsNetworkSettings.update();
|
||||||
network->setHostname(networkSettings.hostname)
|
network->setHostname(networkSettings.hostname)
|
||||||
@@ -271,36 +284,47 @@ protected:
|
|||||||
networkSettings.staticConfig.gateway,
|
networkSettings.staticConfig.gateway,
|
||||||
networkSettings.staticConfig.subnet,
|
networkSettings.staticConfig.subnet,
|
||||||
networkSettings.staticConfig.dns
|
networkSettings.staticConfig.dns
|
||||||
)
|
);
|
||||||
->reconnect();
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doc[FPSTR(S_SETTINGS)].isNull() && jsonToSettings(doc[FPSTR(S_SETTINGS)], settings)) {
|
||||||
|
fsSettings.update();
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!doc[FPSTR(S_SENSORS)].isNull()) {
|
if (!doc[FPSTR(S_SENSORS)].isNull()) {
|
||||||
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
|
for (auto sensor : doc[FPSTR(S_SENSORS)].as<JsonObject>()) {
|
||||||
if (doc[FPSTR(S_SENSORS)][sensorId].isNull()) {
|
if (!isDigit(sensor.key().c_str())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sensorSettingsDoc = doc[FPSTR(S_SENSORS)][sensorId].to<JsonObject>();
|
int sensorId = atoi(sensor.key().c_str());
|
||||||
if (jsonToSensorSettings(sensorId, sensorSettingsDoc, Sensors::settings[sensorId])){
|
if (sensorId < 0 || sensorId > 255 || !Sensors::isValidSensorId(sensorId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonToSensorSettings(sensorId, sensor.value(), Sensors::settings[sensorId])) {
|
||||||
|
fsSensorsSettings.update();
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.clear();
|
doc.clear();
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
vars.actions.restart = true;
|
||||||
|
}
|
||||||
|
|
||||||
this->webServer->send(changed ? 201 : 200);
|
this->webServer->send(changed ? 201 : 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
// network
|
// network
|
||||||
this->webServer->on(F("/api/network/settings"), HTTP_GET, [this]() {
|
this->webServer->on(F("/api/network/settings"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
@@ -311,13 +335,15 @@ protected:
|
|||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on(F("/api/network/settings"), HTTP_POST, [this]() {
|
this->webServer->on(F("/api/network/settings"), HTTP_POST, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
}
|
||||||
}
|
|
||||||
|
if (vars.states.restarting) {
|
||||||
|
return this->webServer->send(503);
|
||||||
}
|
}
|
||||||
|
|
||||||
String plain = this->webServer->arg(0);
|
const String& plain = this->webServer->arg(0);
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/network/settings %d bytes: %s"), plain.length(), plain.c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/network/settings %d bytes: %s"), plain.length(), plain.c_str());
|
||||||
|
|
||||||
if (plain.length() < 5) {
|
if (plain.length() < 5) {
|
||||||
@@ -331,7 +357,6 @@ protected:
|
|||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, plain);
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
plain.clear();
|
|
||||||
|
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
this->webServer->send(400);
|
this->webServer->send(400);
|
||||||
@@ -367,11 +392,8 @@ protected:
|
|||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on(F("/api/network/scan"), HTTP_GET, [this]() {
|
this->webServer->on(F("/api/network/scan"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
this->webServer->send(401);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto apCount = WiFi.scanComplete();
|
auto apCount = WiFi.scanComplete();
|
||||||
@@ -390,7 +412,7 @@ protected:
|
|||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
for (short int i = 0; i < apCount; i++) {
|
for (short int i = 0; i < apCount; i++) {
|
||||||
String ssid = WiFi.SSID(i);
|
const String& ssid = WiFi.SSID(i);
|
||||||
doc[i][FPSTR(S_SSID)] = ssid;
|
doc[i][FPSTR(S_SSID)] = ssid;
|
||||||
doc[i][FPSTR(S_BSSID)] = WiFi.BSSIDstr(i);
|
doc[i][FPSTR(S_BSSID)] = WiFi.BSSIDstr(i);
|
||||||
doc[i][FPSTR(S_SIGNAL_QUALITY)] = NetworkMgr::rssiToSignalQuality(WiFi.RSSI(i));
|
doc[i][FPSTR(S_SIGNAL_QUALITY)] = NetworkMgr::rssiToSignalQuality(WiFi.RSSI(i));
|
||||||
@@ -413,10 +435,8 @@ protected:
|
|||||||
|
|
||||||
// settings
|
// settings
|
||||||
this->webServer->on(F("/api/settings"), HTTP_GET, [this]() {
|
this->webServer->on(F("/api/settings"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
@@ -427,13 +447,15 @@ protected:
|
|||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on(F("/api/settings"), HTTP_POST, [this]() {
|
this->webServer->on(F("/api/settings"), HTTP_POST, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
}
|
||||||
}
|
|
||||||
|
if (vars.states.restarting) {
|
||||||
|
return this->webServer->send(503);
|
||||||
}
|
}
|
||||||
|
|
||||||
String plain = this->webServer->arg(0);
|
const String& plain = this->webServer->arg(0);
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/settings %d bytes: %s"), plain.length(), plain.c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/settings %d bytes: %s"), plain.length(), plain.c_str());
|
||||||
|
|
||||||
if (plain.length() < 5) {
|
if (plain.length() < 5) {
|
||||||
@@ -447,7 +469,6 @@ protected:
|
|||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, plain);
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
plain.clear();
|
|
||||||
|
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
this->webServer->send(400);
|
this->webServer->send(400);
|
||||||
@@ -475,10 +496,8 @@ protected:
|
|||||||
|
|
||||||
// sensors list
|
// sensors list
|
||||||
this->webServer->on(F("/api/sensors"), HTTP_GET, [this]() {
|
this->webServer->on(F("/api/sensors"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool detailed = false;
|
bool detailed = false;
|
||||||
@@ -490,6 +509,7 @@ protected:
|
|||||||
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
|
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
|
||||||
if (detailed) {
|
if (detailed) {
|
||||||
auto& sSensor = Sensors::settings[sensorId];
|
auto& sSensor = Sensors::settings[sensorId];
|
||||||
|
doc[sensorId][FPSTR(S_ENABLED)] = sSensor.enabled;
|
||||||
doc[sensorId][FPSTR(S_NAME)] = sSensor.name;
|
doc[sensorId][FPSTR(S_NAME)] = sSensor.name;
|
||||||
doc[sensorId][FPSTR(S_PURPOSE)] = static_cast<uint8_t>(sSensor.purpose);
|
doc[sensorId][FPSTR(S_PURPOSE)] = static_cast<uint8_t>(sSensor.purpose);
|
||||||
sensorResultToJson(sensorId, doc[sensorId]);
|
sensorResultToJson(sensorId, doc[sensorId]);
|
||||||
@@ -505,10 +525,8 @@ protected:
|
|||||||
|
|
||||||
// sensor settings
|
// sensor settings
|
||||||
this->webServer->on(F("/api/sensor"), HTTP_GET, [this]() {
|
this->webServer->on(F("/api/sensor"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->webServer->hasArg(F("id"))) {
|
if (!this->webServer->hasArg(F("id"))) {
|
||||||
@@ -533,10 +551,12 @@ protected:
|
|||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on(F("/api/sensor"), HTTP_POST, [this]() {
|
this->webServer->on(F("/api/sensor"), HTTP_POST, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
}
|
||||||
}
|
|
||||||
|
if (vars.states.restarting) {
|
||||||
|
return this->webServer->send(503);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
@@ -612,13 +632,11 @@ protected:
|
|||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on(F("/api/vars"), HTTP_POST, [this]() {
|
this->webServer->on(F("/api/vars"), HTTP_POST, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired() && !this->isValidCredentials()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
return this->webServer->send(401);
|
||||||
return this->webServer->send(401);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String plain = this->webServer->arg(0);
|
const String& plain = this->webServer->arg(0);
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/vars %d bytes: %s"), plain.length(), plain.c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/vars %d bytes: %s"), plain.length(), plain.c_str());
|
||||||
|
|
||||||
if (plain.length() < 5) {
|
if (plain.length() < 5) {
|
||||||
@@ -632,7 +650,6 @@ protected:
|
|||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, plain);
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
plain.clear();
|
|
||||||
|
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
this->webServer->send(400);
|
this->webServer->send(400);
|
||||||
@@ -829,7 +846,7 @@ protected:
|
|||||||
this->webServer->onNotFound([this]() {
|
this->webServer->onNotFound([this]() {
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Page not found, uri: %s"), this->webServer->uri().c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Page not found, uri: %s"), this->webServer->uri().c_str());
|
||||||
|
|
||||||
const String uri = this->webServer->uri();
|
const String& uri = this->webServer->uri();
|
||||||
if (uri.equals(F("/"))) {
|
if (uri.equals(F("/"))) {
|
||||||
this->webServer->send(200, F("text/plain"), F("The file system is not flashed!"));
|
this->webServer->send(200, F("text/plain"), F("The file system is not flashed!"));
|
||||||
|
|
||||||
@@ -856,7 +873,7 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Started: AP up or STA connected"));
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Started: AP up or STA connected"));
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else if (this->stateWebServer() && !network->isApEnabled() && !network->isStaEnabled()) {
|
} else if (this->stateWebServer() && !network->isApEnabled() && !network->isStaEnabled()) {
|
||||||
@@ -864,7 +881,7 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Stopped: AP and STA down"));
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Stopped: AP and STA down"));
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -874,7 +891,7 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Started: AP up"));
|
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Started: AP up"));
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else if (this->stateDnsServer() && (!network->isApEnabled() || !this->stateWebServer())) {
|
} else if (this->stateDnsServer() && (!network->isApEnabled() || !this->stateWebServer())) {
|
||||||
@@ -882,18 +899,27 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Stopped: AP down"));
|
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Stopped: AP down"));
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->stateDnsServer()) {
|
if (this->stateDnsServer()) {
|
||||||
this->dnsServer->processNextRequest();
|
this->dnsServer->processNextRequest();
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->stateWebServer()) {
|
if (this->stateWebServer()) {
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
// Fix ERR_CONNECTION_RESET for Chrome based browsers
|
||||||
|
auto& client = this->webServer->client();
|
||||||
|
if (!client.getNoDelay()) {
|
||||||
|
client.setNoDelay(true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
this->webServer->handleClient();
|
this->webServer->handleClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,8 +932,12 @@ protected:
|
|||||||
return !network->isApEnabled() && settings.portal.auth && strlen(settings.portal.password);
|
return !network->isApEnabled() && settings.portal.auth && strlen(settings.portal.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isValidCredentials() {
|
||||||
|
return this->webServer->authenticate(settings.portal.login, settings.portal.password);
|
||||||
|
}
|
||||||
|
|
||||||
void onCaptivePortal() {
|
void onCaptivePortal() {
|
||||||
const String uri = this->webServer->uri();
|
const String& uri = this->webServer->uri();
|
||||||
|
|
||||||
if (uri.equals(F("/connecttest.txt"))) {
|
if (uri.equals(F("/connecttest.txt"))) {
|
||||||
this->webServer->sendHeader(F("Location"), F("http://logout.net"));
|
this->webServer->sendHeader(F("Location"), F("http://logout.net"));
|
||||||
@@ -927,10 +957,10 @@ protected:
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
String portalUrl = F("http://");
|
String portalUrl = F("http://");
|
||||||
portalUrl += network->getApIp().toString();
|
portalUrl += network->getApIp().toString().c_str();
|
||||||
portalUrl += '/';
|
portalUrl += '/';
|
||||||
|
|
||||||
this->webServer->sendHeader(F("Location"), portalUrl.c_str());
|
this->webServer->sendHeader(F("Location"), portalUrl);
|
||||||
this->webServer->send(302);
|
this->webServer->send(302);
|
||||||
|
|
||||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to portal page with 302 code"));
|
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to portal page with 302 code"));
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
if (vars.states.restarting || vars.states.upgrading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->indoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP);
|
this->indoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP);
|
||||||
//this->outdoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP);
|
//this->outdoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP);
|
||||||
|
|
||||||
@@ -55,14 +59,15 @@ protected:
|
|||||||
this->turbo();
|
this->turbo();
|
||||||
this->hysteresis();
|
this->hysteresis();
|
||||||
|
|
||||||
vars.master.heating.targetTemp = constrain(
|
vars.master.heating.targetTemp = settings.heating.target;
|
||||||
this->getHeatingSetpoint(),
|
vars.master.heating.setpointTemp = constrain(
|
||||||
vars.master.heating.minTemp,
|
this->getHeatingSetpointTemp(),
|
||||||
vars.master.heating.maxTemp
|
this->getHeatingMinSetpointTemp(),
|
||||||
|
this->getHeatingMaxSetpointTemp()
|
||||||
);
|
);
|
||||||
|
|
||||||
Sensors::setValueByType(
|
Sensors::setValueByType(
|
||||||
Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.targetTemp,
|
Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.setpointTemp,
|
||||||
Sensors::ValueType::PRIMARY, true, true
|
Sensors::ValueType::PRIMARY, true, true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -104,8 +109,19 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline float getHeatingMinSetpointTemp() {
|
||||||
|
return settings.opentherm.nativeHeatingControl
|
||||||
|
? vars.master.heating.minTemp
|
||||||
|
: settings.heating.minTemp;
|
||||||
|
}
|
||||||
|
|
||||||
float getHeatingSetpoint() {
|
inline float getHeatingMaxSetpointTemp() {
|
||||||
|
return settings.opentherm.nativeHeatingControl
|
||||||
|
? vars.master.heating.maxTemp
|
||||||
|
: settings.heating.maxTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getHeatingSetpointTemp() {
|
||||||
float newTemp = 0;
|
float newTemp = 0;
|
||||||
|
|
||||||
if (fabsf(prevHeatingTarget - settings.heating.target) > 0.0001f) {
|
if (fabsf(prevHeatingTarget - settings.heating.target) > 0.0001f) {
|
||||||
|
|||||||
152
src/Sensors.h
@@ -18,6 +18,13 @@ public:
|
|||||||
OT_PRESSURE = 9,
|
OT_PRESSURE = 9,
|
||||||
OT_MODULATION_LEVEL = 10,
|
OT_MODULATION_LEVEL = 10,
|
||||||
OT_CURRENT_POWER = 11,
|
OT_CURRENT_POWER = 11,
|
||||||
|
OT_EXHAUST_CO2 = 12,
|
||||||
|
OT_EXHAUST_FAN_SPEED = 13,
|
||||||
|
OT_SUPPLY_FAN_SPEED = 14,
|
||||||
|
OT_SOLAR_STORAGE_TEMP = 15,
|
||||||
|
OT_SOLAR_COLLECTOR_TEMP = 16,
|
||||||
|
OT_FAN_SPEED_SETPOINT = 17,
|
||||||
|
OT_FAN_SPEED_CURRENT = 18,
|
||||||
|
|
||||||
NTC_10K_TEMP = 50,
|
NTC_10K_TEMP = 50,
|
||||||
DALLAS_TEMP = 51,
|
DALLAS_TEMP = 51,
|
||||||
@@ -38,8 +45,11 @@ public:
|
|||||||
DHW_FLOW_RATE = 6,
|
DHW_FLOW_RATE = 6,
|
||||||
EXHAUST_TEMP = 7,
|
EXHAUST_TEMP = 7,
|
||||||
MODULATION_LEVEL = 8,
|
MODULATION_LEVEL = 8,
|
||||||
CURRENT_POWER = 9,
|
|
||||||
|
|
||||||
|
POWER_FACTOR = 248,
|
||||||
|
POWER = 249,
|
||||||
|
FAN_SPEED = 250,
|
||||||
|
CO2 = 251,
|
||||||
PRESSURE = 252,
|
PRESSURE = 252,
|
||||||
HUMIDITY = 253,
|
HUMIDITY = 253,
|
||||||
TEMPERATURE = 254,
|
TEMPERATURE = 254,
|
||||||
@@ -70,7 +80,7 @@ public:
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
unsigned long activityTime = 0;
|
unsigned long activityTime = 0;
|
||||||
uint8_t signalQuality = 0;
|
uint8_t signalQuality = 100;
|
||||||
//float raw[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
//float raw[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
float values[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
float values[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
} Result;
|
} Result;
|
||||||
@@ -112,14 +122,14 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t getAmountByType(Type type) {
|
static uint8_t getAmountByType(Type type, bool onlyEnabled = false) {
|
||||||
if (settings == nullptr) {
|
if (settings == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t amount = 0;
|
uint8_t amount = 0;
|
||||||
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
||||||
if (settings[id].type == type) {
|
if (settings[id].type == type && (!onlyEnabled || settings[id].enabled)) {
|
||||||
amount++;
|
amount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,9 +156,10 @@ public:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String refObjectId;
|
||||||
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
||||||
String _objectId = Sensors::makeObjectId(settings[id].name);
|
Sensors::makeObjectId(refObjectId, settings[id].name);
|
||||||
if (strcmp(_objectId.c_str(), objectId) == 0) {
|
if (refObjectId.equals(objectId)) {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,41 +181,44 @@ public:
|
|||||||
auto& rSensor = results[sensorId];
|
auto& rSensor = results[sensorId];
|
||||||
|
|
||||||
float compensatedValue = value;
|
float compensatedValue = value;
|
||||||
if (valueType == ValueType::PRIMARY) {
|
if (sSensor.type == Type::HEATING_SETPOINT_TEMP || sSensor.type == Type::MANUAL) {
|
||||||
if (fabsf(sSensor.factor) > 0.001f) {
|
|
||||||
compensatedValue *= sSensor.factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fabsf(sSensor.offset) > 0.001f) {
|
|
||||||
compensatedValue += sSensor.offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (valueType == ValueType::RSSI) {
|
|
||||||
if (sSensor.type == Type::BLUETOOTH) {
|
|
||||||
rSensor.signalQuality = Sensors::bluetoothRssiToQuality(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sSensor.filtering && fabs(rSensor.values[valueId]) >= 0.1f) {
|
|
||||||
rSensor.values[valueId] += (compensatedValue - rSensor.values[valueId]) * sSensor.filteringFactor;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
rSensor.values[valueId] = compensatedValue;
|
rSensor.values[valueId] = compensatedValue;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (valueType == ValueType::PRIMARY) {
|
||||||
|
if (fabsf(sSensor.factor) > 0.001f) {
|
||||||
|
compensatedValue *= sSensor.factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabsf(sSensor.offset) > 0.001f) {
|
||||||
|
compensatedValue += sSensor.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (valueType == ValueType::RSSI) {
|
||||||
|
if (sSensor.type == Type::BLUETOOTH) {
|
||||||
|
rSensor.signalQuality = Sensors::bluetoothRssiToQuality(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sSensor.filtering && fabsf(rSensor.values[valueId]) >= 0.1f) {
|
||||||
|
rSensor.values[valueId] += (compensatedValue - rSensor.values[valueId]) * sSensor.filteringFactor;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rSensor.values[valueId] = compensatedValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateActivityTime) {
|
if (updateActivityTime) {
|
||||||
rSensor.activityTime = millis();
|
rSensor.activityTime = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (markConnected) {
|
if (markConnected && !rSensor.connected) {
|
||||||
if (!rSensor.connected) {
|
rSensor.connected = true;
|
||||||
rSensor.connected = true;
|
|
||||||
|
|
||||||
Log.snoticeln(
|
Log.snoticeln(
|
||||||
FPSTR(L_SENSORS), F("#%hhu '%s' new status: CONNECTED"),
|
FPSTR(L_SENSORS), F("#%hhu '%s' new status: CONNECTED"),
|
||||||
sensorId, sSensor.name
|
sensorId, sSensor.name
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.snoticeln(
|
Log.snoticeln(
|
||||||
@@ -351,13 +365,10 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
static String& cleanName(String& value, char space = ' ') {
|
||||||
static String cleanName(T value, char space = ' ') {
|
|
||||||
String clean = value;
|
|
||||||
|
|
||||||
// only valid symbols
|
// only valid symbols
|
||||||
for (uint8_t pos = 0; pos < clean.length(); pos++) {
|
for (uint8_t pos = 0; pos < value.length(); pos++) {
|
||||||
char symbol = clean.charAt(pos);
|
char symbol = value.charAt(pos);
|
||||||
|
|
||||||
// 0..9
|
// 0..9
|
||||||
if (symbol >= 48 && symbol <= 57) {
|
if (symbol >= 48 && symbol <= 57) {
|
||||||
@@ -379,39 +390,68 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
clean.setCharAt(pos, space);
|
value.setCharAt(pos, space);
|
||||||
}
|
}
|
||||||
|
|
||||||
clean.trim();
|
value.trim();
|
||||||
|
|
||||||
return clean;
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static String cleanName(T value, char space = ' ') {
|
||||||
|
String res = value;
|
||||||
|
return cleanName(res, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static String& makeObjectId(String& res, T value, char separator = '_') {
|
||||||
|
res = value;
|
||||||
|
cleanName(res);
|
||||||
|
res.toLowerCase();
|
||||||
|
res.replace(' ', separator);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
static String makeObjectId(T value, char separator = '_') {
|
static String makeObjectId(T value, char separator = '_') {
|
||||||
auto objId = cleanName(value);
|
String res;
|
||||||
objId.toLowerCase();
|
makeObjectId(res, value, separator);
|
||||||
objId.replace(' ', separator);
|
|
||||||
|
|
||||||
return objId;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class TV, class TS>
|
template <class TV, class TS>
|
||||||
static auto makeObjectIdWithSuffix(TV value, TS suffix, char separator = '_') {
|
static String& makeObjectIdWithSuffix(String& res, TV value, TS suffix, char separator = '_') {
|
||||||
auto objId = makeObjectId(value, separator);
|
res.clear();
|
||||||
objId += separator;
|
makeObjectId(res, value, separator);
|
||||||
objId += suffix;
|
res += separator;
|
||||||
|
res += suffix;
|
||||||
|
|
||||||
return objId;
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TV, class TS>
|
||||||
|
static String makeObjectIdWithSuffix(TV value, TS suffix, char separator = '_') {
|
||||||
|
String res;
|
||||||
|
makeObjectIdWithSuffix(res, value, suffix, separator);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class TV, class TP>
|
template <class TV, class TP>
|
||||||
static auto makeObjectIdWithPrefix(TV value, TP prefix, char separator = '_') {
|
static String& makeObjectIdWithPrefix(String& res, TV value, TP prefix, char separator = '_') {
|
||||||
String objId = prefix;
|
res = prefix;
|
||||||
objId += separator;
|
res += separator;
|
||||||
objId += makeObjectId(value, separator);
|
res += makeObjectId(value, separator).c_str();
|
||||||
|
|
||||||
return objId;
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TV, class TP>
|
||||||
|
static String makeObjectIdWithPrefix(TV value, TP prefix, char separator = '_') {
|
||||||
|
String res;
|
||||||
|
return makeObjectIdWithPrefix(res, value, prefix, separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t bluetoothRssiToQuality(int rssi) {
|
static uint8_t bluetoothRssiToQuality(int rssi) {
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
if (vars.states.restarting || vars.states.upgrading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isPollingDallasSensors()) {
|
if (isPollingDallasSensors()) {
|
||||||
pollingDallasSensors(false);
|
pollingDallasSensors(false);
|
||||||
this->yield();
|
this->yield();
|
||||||
@@ -302,6 +306,7 @@ protected:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& rSensor = Sensors::results[sensorId];
|
||||||
float value = instance.getTempC(sSensor.address);
|
float value = instance.getTempC(sSensor.address);
|
||||||
if (value == DEVICE_DISCONNECTED_C) {
|
if (value == DEVICE_DISCONNECTED_C) {
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
@@ -309,6 +314,10 @@ protected:
|
|||||||
sSensor.gpio, sensorId, sSensor.name
|
sSensor.gpio, sensorId, sSensor.name
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (rSensor.signalQuality > 0) {
|
||||||
|
rSensor.signalQuality--;
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,6 +326,10 @@ protected:
|
|||||||
sSensor.gpio, sensorId, sSensor.name, value
|
sSensor.gpio, sensorId, sSensor.name, value
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (rSensor.signalQuality < 100) {
|
||||||
|
rSensor.signalQuality++;
|
||||||
|
}
|
||||||
|
|
||||||
// set sensor value
|
// set sensor value
|
||||||
Sensors::setValueById(sensorId, value, Sensors::ValueType::TEMPERATURE, true, true);
|
Sensors::setValueById(sensorId, value, Sensors::ValueType::TEMPERATURE, true, true);
|
||||||
}
|
}
|
||||||
@@ -334,6 +347,23 @@ protected:
|
|||||||
|
|
||||||
// check sensors on bus
|
// check sensors on bus
|
||||||
if (!instance.getDeviceCount()) {
|
if (!instance.getDeviceCount()) {
|
||||||
|
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
|
||||||
|
auto& sSensor = Sensors::settings[sensorId];
|
||||||
|
|
||||||
|
// only target & valid sensors
|
||||||
|
if (!sSensor.enabled || sSensor.type != Sensors::Type::DALLAS_TEMP || sSensor.purpose == Sensors::Purpose::NOT_CONFIGURED) {
|
||||||
|
continue;
|
||||||
|
|
||||||
|
} else if (sSensor.gpio != gpio || isEmptyAddress(sSensor.address)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& rSensor = Sensors::results[sensorId];
|
||||||
|
if (rSensor.signalQuality > 0) {
|
||||||
|
rSensor.signalQuality--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,13 +412,13 @@ protected:
|
|||||||
const auto value = analogRead(sSensor.gpio) / 1023 * DEFAULT_NTC_VREF;
|
const auto value = analogRead(sSensor.gpio) / 1023 * DEFAULT_NTC_VREF;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (value < DEFAULT_NTC_VLOW_TRESHOLD) {
|
if (value < DEFAULT_NTC_VLOW_TRESHOLD || value > DEFAULT_NTC_VHIGH_TRESHOLD) {
|
||||||
if (Sensors::getConnectionStatusById(sensorId)) {
|
if (Sensors::getConnectionStatusById(sensorId)) {
|
||||||
Sensors::setConnectionStatusById(sensorId, false, false);
|
Sensors::setConnectionStatusById(sensorId, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
FPSTR(L_SENSORS_NTC), F("GPIO %hhu, sensor #%hhu '%s', voltage too low: %.2f"),
|
FPSTR(L_SENSORS_NTC), F("GPIO %hhu, sensor #%hhu '%s', voltage is out of threshold: %.3f"),
|
||||||
sSensor.gpio, sensorId, sSensor.name, (value / 1000.0f)
|
sSensor.gpio, sensorId, sSensor.name, (value / 1000.0f)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -531,7 +561,7 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.01f);
|
float rawTemp = (pChar->getValue<int16_t>() * 0.01f);
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE),
|
FPSTR(L_SENSORS_BLE),
|
||||||
F("Sensor #%hhu '%s': received temp: %.2f"),
|
F("Sensor #%hhu '%s': received temp: %.2f"),
|
||||||
@@ -604,7 +634,7 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.1f);
|
float rawTemp = (pChar->getValue<int16_t>() * 0.1f);
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE),
|
FPSTR(L_SENSORS_BLE),
|
||||||
F("Sensor #%hhu '%s': received temp: %.2f"),
|
F("Sensor #%hhu '%s': received temp: %.2f"),
|
||||||
@@ -689,7 +719,7 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float rawHumidity = ((pData[0] | (pData[1] << 8)) * 0.01f);
|
float rawHumidity = (pChar->getValue<uint16_t>() * 0.01f);
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE),
|
FPSTR(L_SENSORS_BLE),
|
||||||
F("Sensor #%hhu '%s': received humidity: %.2f"),
|
F("Sensor #%hhu '%s': received humidity: %.2f"),
|
||||||
@@ -788,7 +818,7 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t rawBattery = pData[0];
|
auto rawBattery = pChar->getValue<uint8_t>();
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE),
|
FPSTR(L_SENSORS_BLE),
|
||||||
F("Sensor #%hhu '%s': received battery: %.2f"),
|
F("Sensor #%hhu '%s': received battery: %.2f"),
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ Sensors::Settings sensorsSettings[SENSORS_AMOUNT] = {
|
|||||||
false,
|
false,
|
||||||
"Outdoor temp",
|
"Outdoor temp",
|
||||||
Sensors::Purpose::OUTDOOR_TEMP,
|
Sensors::Purpose::OUTDOOR_TEMP,
|
||||||
Sensors::Type::DALLAS_TEMP,
|
Sensors::Type::OT_OUTDOOR_TEMP,
|
||||||
DEFAULT_SENSOR_OUTDOOR_GPIO
|
DEFAULT_SENSOR_OUTDOOR_GPIO
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -215,7 +215,7 @@ Sensors::Settings sensorsSettings[SENSORS_AMOUNT] = {
|
|||||||
{
|
{
|
||||||
true,
|
true,
|
||||||
"Power",
|
"Power",
|
||||||
Sensors::Purpose::CURRENT_POWER,
|
Sensors::Purpose::POWER,
|
||||||
Sensors::Type::OT_CURRENT_POWER,
|
Sensors::Type::OT_CURRENT_POWER,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -255,6 +255,7 @@ struct Variables {
|
|||||||
bool blocking = false;
|
bool blocking = false;
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
bool indoorTempControl = false;
|
bool indoorTempControl = false;
|
||||||
|
float setpointTemp = 0.0f;
|
||||||
float targetTemp = 0.0f;
|
float targetTemp = 0.0f;
|
||||||
float currentTemp = 0.0f;
|
float currentTemp = 0.0f;
|
||||||
float returnTemp = 0.0f;
|
float returnTemp = 0.0f;
|
||||||
@@ -287,7 +288,6 @@ struct Variables {
|
|||||||
bool connected = false;
|
bool connected = false;
|
||||||
bool flame = false;
|
bool flame = false;
|
||||||
float pressure = 0.0f;
|
float pressure = 0.0f;
|
||||||
float exhaustTemp = 0.0f;
|
|
||||||
float heatExchangerTemp = 0.0f;
|
float heatExchangerTemp = 0.0f;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -312,6 +312,23 @@ struct Variables {
|
|||||||
float max = 0.0f;
|
float max = 0.0f;
|
||||||
} power;
|
} power;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float temp = 0.0f;
|
||||||
|
uint16_t co2 = 0;
|
||||||
|
uint16_t fanSpeed = 0;
|
||||||
|
} exhaust;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float storage = 0.0f;
|
||||||
|
float collector = 0.0f;
|
||||||
|
} solar;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t setpoint = 0;
|
||||||
|
uint8_t current = 0;
|
||||||
|
uint16_t supply = 0;
|
||||||
|
} fanSpeed;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool active = false;
|
bool active = false;
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
@@ -349,4 +366,9 @@ struct Variables {
|
|||||||
bool resetFault = false;
|
bool resetFault = false;
|
||||||
bool resetDiagnostic = false;
|
bool resetDiagnostic = false;
|
||||||
} actions;
|
} actions;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool restarting = false;
|
||||||
|
bool upgrading = false;
|
||||||
|
} states;
|
||||||
} vars;
|
} vars;
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
#define DEFAULT_NTC_BETA_FACTOR 3950.0f
|
#define DEFAULT_NTC_BETA_FACTOR 3950.0f
|
||||||
#define DEFAULT_NTC_VREF 3300.0f
|
#define DEFAULT_NTC_VREF 3300.0f
|
||||||
#define DEFAULT_NTC_VLOW_TRESHOLD 25.0f
|
#define DEFAULT_NTC_VLOW_TRESHOLD 25.0f
|
||||||
|
#define DEFAULT_NTC_VHIGH_TRESHOLD 3298.0f
|
||||||
|
|
||||||
#ifndef BUILD_VERSION
|
#ifndef BUILD_VERSION
|
||||||
#define BUILD_VERSION "0.0.0"
|
#define BUILD_VERSION "0.0.0"
|
||||||
@@ -152,7 +153,7 @@
|
|||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
#elif !defined(GPIO_IS_VALID_GPIO)
|
#elif !defined(GPIO_IS_VALID_GPIO)
|
||||||
#define GPIO_IS_VALID_GPIO(gpioNum) (gpioNum >= 0 && gpioNum <= 16)
|
#define GPIO_IS_VALID_GPIO(gpioNum) (gpioNum >= 0 && gpioNum <= 17)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define GPIO_IS_VALID(gpioNum) (gpioNum != GPIO_IS_NOT_CONFIGURED && GPIO_IS_VALID_GPIO(gpioNum))
|
#define GPIO_IS_VALID(gpioNum) (gpioNum != GPIO_IS_NOT_CONFIGURED && GPIO_IS_VALID_GPIO(gpioNum))
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
#include <FileData.h>
|
#include <FileData.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
#include <ESPTelnetStream.h>
|
#include <ESPTelnetStream.h>
|
||||||
#include <TinyLogger.h>
|
|
||||||
#include <NetworkMgr.h>
|
|
||||||
|
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "strings.h"
|
#include "strings.h"
|
||||||
|
|
||||||
|
#include <TinyLogger.h>
|
||||||
|
#include <NetworkMgr.h>
|
||||||
#include "CrashRecorder.h"
|
#include "CrashRecorder.h"
|
||||||
#include "Sensors.h"
|
#include "Sensors.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ const char S_STA[] PROGMEM = "sta";
|
|||||||
const char S_STATE[] PROGMEM = "state";
|
const char S_STATE[] PROGMEM = "state";
|
||||||
const char S_STATIC_CONFIG[] PROGMEM = "staticConfig";
|
const char S_STATIC_CONFIG[] PROGMEM = "staticConfig";
|
||||||
const char S_STATUS_LED_GPIO[] PROGMEM = "statusLedGpio";
|
const char S_STATUS_LED_GPIO[] PROGMEM = "statusLedGpio";
|
||||||
|
const char S_SETPOINT_TEMP[] PROGMEM = "setpointTemp";
|
||||||
const char S_SUBNET[] PROGMEM = "subnet";
|
const char S_SUBNET[] PROGMEM = "subnet";
|
||||||
const char S_SUMMER_WINTER_MODE[] PROGMEM = "summerWinterMode";
|
const char S_SUMMER_WINTER_MODE[] PROGMEM = "summerWinterMode";
|
||||||
const char S_SYSTEM[] PROGMEM = "system";
|
const char S_SYSTEM[] PROGMEM = "system";
|
||||||
|
|||||||
49
src/utils.h
@@ -1351,20 +1351,24 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
|
|
||||||
// force check heating target
|
// force check heating target
|
||||||
{
|
{
|
||||||
float value = !src[FPSTR(S_HEATING)][FPSTR(S_TARGET)].isNull() ? src[FPSTR(S_HEATING)][FPSTR(S_TARGET)].as<float>() : dst.heating.target;
|
bool indoorTempControl = dst.equitherm.enabled || dst.pid.enabled || dst.opentherm.nativeHeatingControl;
|
||||||
|
float minTemp = indoorTempControl ? THERMOSTAT_INDOOR_MIN_TEMP : dst.heating.minTemp;
|
||||||
|
float maxTemp = indoorTempControl ? THERMOSTAT_INDOOR_MAX_TEMP : dst.heating.maxTemp;
|
||||||
|
|
||||||
|
float value = !src[FPSTR(S_HEATING)][FPSTR(S_TARGET)].isNull()
|
||||||
|
? src[FPSTR(S_HEATING)][FPSTR(S_TARGET)].as<float>()
|
||||||
|
: dst.heating.target;
|
||||||
bool valid = isValidTemp(
|
bool valid = isValidTemp(
|
||||||
value,
|
value,
|
||||||
dst.system.unitSystem,
|
dst.system.unitSystem,
|
||||||
vars.master.heating.minTemp,
|
minTemp,
|
||||||
vars.master.heating.maxTemp,
|
maxTemp,
|
||||||
dst.system.unitSystem
|
dst.system.unitSystem
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
value = convertTemp(
|
value = convertTemp(
|
||||||
vars.master.heating.indoorTempControl
|
indoorTempControl ? THERMOSTAT_INDOOR_DEFAULT_TEMP : DEFAULT_HEATING_TARGET_TEMP,
|
||||||
? THERMOSTAT_INDOOR_DEFAULT_TEMP
|
|
||||||
: DEFAULT_HEATING_TARGET_TEMP,
|
|
||||||
UnitSystem::METRIC,
|
UnitSystem::METRIC,
|
||||||
dst.system.unitSystem
|
dst.system.unitSystem
|
||||||
);
|
);
|
||||||
@@ -1378,7 +1382,9 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
|
|
||||||
// force check dhw target
|
// force check dhw target
|
||||||
{
|
{
|
||||||
float value = !src[FPSTR(S_DHW)][FPSTR(S_TARGET)].isNull() ? src[FPSTR(S_DHW)][FPSTR(S_TARGET)].as<float>() : dst.dhw.target;
|
float value = !src[FPSTR(S_DHW)][FPSTR(S_TARGET)].isNull()
|
||||||
|
? src[FPSTR(S_DHW)][FPSTR(S_TARGET)].as<float>()
|
||||||
|
: dst.dhw.target;
|
||||||
bool valid = isValidTemp(
|
bool valid = isValidTemp(
|
||||||
value,
|
value,
|
||||||
dst.system.unitSystem,
|
dst.system.unitSystem,
|
||||||
@@ -1461,7 +1467,8 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se
|
|||||||
|
|
||||||
// name
|
// name
|
||||||
if (!src[FPSTR(S_NAME)].isNull()) {
|
if (!src[FPSTR(S_NAME)].isNull()) {
|
||||||
String value = Sensors::cleanName(src[FPSTR(S_NAME)].as<String>());
|
auto value = src[FPSTR(S_NAME)].as<String>();
|
||||||
|
Sensors::cleanName(value);
|
||||||
|
|
||||||
if (value.length() < sizeof(dst.name) && !value.equals(dst.name)) {
|
if (value.length() < sizeof(dst.name) && !value.equals(dst.name)) {
|
||||||
strcpy(dst.name, value.c_str());
|
strcpy(dst.name, value.c_str());
|
||||||
@@ -1483,7 +1490,11 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se
|
|||||||
case static_cast<uint8_t>(Sensors::Purpose::DHW_FLOW_RATE):
|
case static_cast<uint8_t>(Sensors::Purpose::DHW_FLOW_RATE):
|
||||||
case static_cast<uint8_t>(Sensors::Purpose::EXHAUST_TEMP):
|
case static_cast<uint8_t>(Sensors::Purpose::EXHAUST_TEMP):
|
||||||
case static_cast<uint8_t>(Sensors::Purpose::MODULATION_LEVEL):
|
case static_cast<uint8_t>(Sensors::Purpose::MODULATION_LEVEL):
|
||||||
case static_cast<uint8_t>(Sensors::Purpose::CURRENT_POWER):
|
|
||||||
|
case static_cast<uint8_t>(Sensors::Purpose::POWER_FACTOR):
|
||||||
|
case static_cast<uint8_t>(Sensors::Purpose::POWER):
|
||||||
|
case static_cast<uint8_t>(Sensors::Purpose::FAN_SPEED):
|
||||||
|
case static_cast<uint8_t>(Sensors::Purpose::CO2):
|
||||||
case static_cast<uint8_t>(Sensors::Purpose::PRESSURE):
|
case static_cast<uint8_t>(Sensors::Purpose::PRESSURE):
|
||||||
case static_cast<uint8_t>(Sensors::Purpose::HUMIDITY):
|
case static_cast<uint8_t>(Sensors::Purpose::HUMIDITY):
|
||||||
case static_cast<uint8_t>(Sensors::Purpose::TEMPERATURE):
|
case static_cast<uint8_t>(Sensors::Purpose::TEMPERATURE):
|
||||||
@@ -1516,6 +1527,14 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se
|
|||||||
case static_cast<uint8_t>(Sensors::Type::OT_PRESSURE):
|
case static_cast<uint8_t>(Sensors::Type::OT_PRESSURE):
|
||||||
case static_cast<uint8_t>(Sensors::Type::OT_MODULATION_LEVEL):
|
case static_cast<uint8_t>(Sensors::Type::OT_MODULATION_LEVEL):
|
||||||
case static_cast<uint8_t>(Sensors::Type::OT_CURRENT_POWER):
|
case static_cast<uint8_t>(Sensors::Type::OT_CURRENT_POWER):
|
||||||
|
case static_cast<uint8_t>(Sensors::Type::OT_EXHAUST_CO2):
|
||||||
|
case static_cast<uint8_t>(Sensors::Type::OT_EXHAUST_FAN_SPEED):
|
||||||
|
case static_cast<uint8_t>(Sensors::Type::OT_SUPPLY_FAN_SPEED):
|
||||||
|
case static_cast<uint8_t>(Sensors::Type::OT_SOLAR_STORAGE_TEMP):
|
||||||
|
case static_cast<uint8_t>(Sensors::Type::OT_SOLAR_COLLECTOR_TEMP):
|
||||||
|
case static_cast<uint8_t>(Sensors::Type::OT_FAN_SPEED_SETPOINT):
|
||||||
|
case static_cast<uint8_t>(Sensors::Type::OT_FAN_SPEED_CURRENT):
|
||||||
|
|
||||||
case static_cast<uint8_t>(Sensors::Type::NTC_10K_TEMP):
|
case static_cast<uint8_t>(Sensors::Type::NTC_10K_TEMP):
|
||||||
case static_cast<uint8_t>(Sensors::Type::DALLAS_TEMP):
|
case static_cast<uint8_t>(Sensors::Type::DALLAS_TEMP):
|
||||||
case static_cast<uint8_t>(Sensors::Type::BLUETOOTH):
|
case static_cast<uint8_t>(Sensors::Type::BLUETOOTH):
|
||||||
@@ -1655,13 +1674,13 @@ void sensorResultToJson(const uint8_t sensorId, JsonVariant dst) {
|
|||||||
dst[FPSTR(S_SIGNAL_QUALITY)] = rSensor.signalQuality;
|
dst[FPSTR(S_SIGNAL_QUALITY)] = rSensor.signalQuality;
|
||||||
|
|
||||||
if (sSensor.type == Sensors::Type::BLUETOOTH) {
|
if (sSensor.type == Sensors::Type::BLUETOOTH) {
|
||||||
dst[FPSTR(S_TEMPERATURE)] = rSensor.values[static_cast<uint8_t>(Sensors::ValueType::TEMPERATURE)];
|
dst[FPSTR(S_TEMPERATURE)] = roundf(rSensor.values[static_cast<uint8_t>(Sensors::ValueType::TEMPERATURE)], 3);
|
||||||
dst[FPSTR(S_HUMIDITY)] = rSensor.values[static_cast<uint8_t>(Sensors::ValueType::HUMIDITY)];
|
dst[FPSTR(S_HUMIDITY)] = roundf(rSensor.values[static_cast<uint8_t>(Sensors::ValueType::HUMIDITY)], 3);
|
||||||
dst[FPSTR(S_BATTERY)] = rSensor.values[static_cast<uint8_t>(Sensors::ValueType::BATTERY)];
|
dst[FPSTR(S_BATTERY)] = roundf(rSensor.values[static_cast<uint8_t>(Sensors::ValueType::BATTERY)], 1);
|
||||||
dst[FPSTR(S_RSSI)] = rSensor.values[static_cast<uint8_t>(Sensors::ValueType::RSSI)];
|
dst[FPSTR(S_RSSI)] = roundf(rSensor.values[static_cast<uint8_t>(Sensors::ValueType::RSSI)], 0);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
dst[FPSTR(S_VALUE)] = rSensor.values[static_cast<uint8_t>(Sensors::ValueType::PRIMARY)];
|
dst[FPSTR(S_VALUE)] = roundf(rSensor.values[static_cast<uint8_t>(Sensors::ValueType::PRIMARY)], 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1734,6 +1753,7 @@ void varsToJson(const Variables& src, JsonVariant dst) {
|
|||||||
mHeating[FPSTR(S_ENABLED)] = src.master.heating.enabled;
|
mHeating[FPSTR(S_ENABLED)] = src.master.heating.enabled;
|
||||||
mHeating[FPSTR(S_BLOCKING)] = src.master.heating.blocking;
|
mHeating[FPSTR(S_BLOCKING)] = src.master.heating.blocking;
|
||||||
mHeating[FPSTR(S_INDOOR_TEMP_CONTROL)] = src.master.heating.indoorTempControl;
|
mHeating[FPSTR(S_INDOOR_TEMP_CONTROL)] = src.master.heating.indoorTempControl;
|
||||||
|
mHeating[FPSTR(S_SETPOINT_TEMP)] = roundf(src.master.heating.setpointTemp, 2);
|
||||||
mHeating[FPSTR(S_TARGET_TEMP)] = roundf(src.master.heating.targetTemp, 2);
|
mHeating[FPSTR(S_TARGET_TEMP)] = roundf(src.master.heating.targetTemp, 2);
|
||||||
mHeating[FPSTR(S_CURRENT_TEMP)] = roundf(src.master.heating.currentTemp, 2);
|
mHeating[FPSTR(S_CURRENT_TEMP)] = roundf(src.master.heating.currentTemp, 2);
|
||||||
mHeating[FPSTR(S_RETURN_TEMP)] = roundf(src.master.heating.returnTemp, 2);
|
mHeating[FPSTR(S_RETURN_TEMP)] = roundf(src.master.heating.returnTemp, 2);
|
||||||
@@ -1751,6 +1771,7 @@ void varsToJson(const Variables& src, JsonVariant dst) {
|
|||||||
mDhw[FPSTR(S_MAX_TEMP)] = settings.dhw.maxTemp;
|
mDhw[FPSTR(S_MAX_TEMP)] = settings.dhw.maxTemp;
|
||||||
|
|
||||||
master[FPSTR(S_NETWORK)][FPSTR(S_CONNECTED)] = src.network.connected;
|
master[FPSTR(S_NETWORK)][FPSTR(S_CONNECTED)] = src.network.connected;
|
||||||
|
master[FPSTR(S_NETWORK)][FPSTR(S_RSSI)] = src.network.rssi;
|
||||||
master[FPSTR(S_MQTT)][FPSTR(S_CONNECTED)] = src.mqtt.connected;
|
master[FPSTR(S_MQTT)][FPSTR(S_CONNECTED)] = src.mqtt.connected;
|
||||||
master[FPSTR(S_EMERGENCY)][FPSTR(S_STATE)] = src.emergency.state;
|
master[FPSTR(S_EMERGENCY)][FPSTR(S_STATE)] = src.emergency.state;
|
||||||
master[FPSTR(S_EXTERNAL_PUMP)][FPSTR(S_STATE)] = src.externalPump.state;
|
master[FPSTR(S_EXTERNAL_PUMP)][FPSTR(S_STATE)] = src.externalPump.state;
|
||||||
|
|||||||
@@ -49,6 +49,12 @@
|
|||||||
<glyph glyph-name="wifi-strength-2-1"
|
<glyph glyph-name="wifi-strength-2-1"
|
||||||
unicode="wifi_strength_2"
|
unicode="wifi_strength_2"
|
||||||
horiz-adv-x="1022.9733303792667" d="M511.4866651896334 768.1283337025917C332.0397604864727 768.1283337025917 157.7087214326013 708.0286505428097 16.1967447318741 597.6324456402496C187.9713494571903 381.9559014844185 336.3024823621839 198.2472739029395 511.4866651896334 -20.4132754656288C685.3921313541088 196.1154134663874 862.7071756207173 416.9078232715078 1008.0553023103668 597.6324456402497C866.1177527202434 708.0286505428097 691.3591427821901 768.1283337025917 511.4866651896334 768.1283337025917M511.4866651896334 682.8808891701171C642.3423366997788 682.8808891701171 771.0661477733722 646.2236788332648 882.7410693389065 578.4516956956384L745.9183864006797 408.8089514068742C693.0644313319532 438.2194346552781 612.505281564586 469.7607793428413 511.4866651896334 469.7607793428413C410.0414769278917 469.7607793428413 329.9088990473136 437.7928627684892 277.0549439785871 408.8089514068743L139.3801162641751 578.8782675824273C251.9071826058945 646.6502507200537 380.6309936794878 682.8808891701171 511.4866651896334 682.8808891701171z" />
|
horiz-adv-x="1022.9733303792667" d="M511.4866651896334 768.1283337025917C332.0397604864727 768.1283337025917 157.7087214326013 708.0286505428097 16.1967447318741 597.6324456402496C187.9713494571903 381.9559014844185 336.3024823621839 198.2472739029395 511.4866651896334 -20.4132754656288C685.3921313541088 196.1154134663874 862.7071756207173 416.9078232715078 1008.0553023103668 597.6324456402497C866.1177527202434 708.0286505428097 691.3591427821901 768.1283337025917 511.4866651896334 768.1283337025917M511.4866651896334 682.8808891701171C642.3423366997788 682.8808891701171 771.0661477733722 646.2236788332648 882.7410693389065 578.4516956956384L745.9183864006797 408.8089514068742C693.0644313319532 438.2194346552781 612.505281564586 469.7607793428413 511.4866651896334 469.7607793428413C410.0414769278917 469.7607793428413 329.9088990473136 437.7928627684892 277.0549439785871 408.8089514068743L139.3801162641751 578.8782675824273C251.9071826058945 646.6502507200537 380.6309936794878 682.8808891701171 511.4866651896334 682.8808891701171z" />
|
||||||
|
<glyph glyph-name="error"
|
||||||
|
unicode=""
|
||||||
|
horiz-adv-x="1024" d="M512 810.667C747.605 810.667 938.667 619.605 938.667 384C938.667 148.395 747.6049999999999 -42.6669999999999 511.9999999999999 -42.6669999999999C276.395 -42.6669999999999 85.333 148.3950000000001 85.333 384.0000000000001C85.333 619.605 276.395 810.667 512 810.667zM512 725.333C323.7970000000001 725.333 170.667 572.203 170.667 384C170.667 195.797 323.797 42.6669999999999 512 42.6669999999999C700.203 42.6669999999999 853.3330000000001 195.7969999999999 853.3330000000001 384C853.3330000000001 572.203 700.2030000000001 725.3330000000001 512 725.3330000000001zM609.835 542.165L670.1650000000001 481.835L572.33 384L670.1650000000001 286.165L609.835 225.8349999999999L512 323.67L414.165 225.8349999999999L353.8350000000001 286.165L451.67 384L353.8350000000001 481.835L414.165 542.165L512 444.33L609.835 542.165z" />
|
||||||
|
<glyph glyph-name="error-1"
|
||||||
|
unicode="error"
|
||||||
|
horiz-adv-x="1024" d="M512 810.667C747.605 810.667 938.667 619.605 938.667 384C938.667 148.395 747.6049999999999 -42.6669999999999 511.9999999999999 -42.6669999999999C276.395 -42.6669999999999 85.333 148.3950000000001 85.333 384.0000000000001C85.333 619.605 276.395 810.667 512 810.667zM512 725.333C323.7970000000001 725.333 170.667 572.203 170.667 384C170.667 195.797 323.797 42.6669999999999 512 42.6669999999999C700.203 42.6669999999999 853.3330000000001 195.7969999999999 853.3330000000001 384C853.3330000000001 572.203 700.2030000000001 725.3330000000001 512 725.3330000000001zM609.835 542.165L670.1650000000001 481.835L572.33 384L670.1650000000001 286.165L609.835 225.8349999999999L512 323.67L414.165 225.8349999999999L353.8350000000001 286.165L451.67 384L353.8350000000001 481.835L414.165 542.165L512 444.33L609.835 542.165z" />
|
||||||
<glyph glyph-name="wifi-strength-3"
|
<glyph glyph-name="wifi-strength-3"
|
||||||
unicode=""
|
unicode=""
|
||||||
horiz-adv-x="1022.9733303792667" d="M511.4866651896334 768.1283337025917C332.0397604864727 768.1283337025917 157.7087214326013 708.0286505428097 16.1967447318741 597.6324456402496C187.9713494571903 381.9559014844185 336.3024823621839 198.2472739029395 511.4866651896334 -20.4132754656288C685.3921313541088 196.1154134663874 862.7071756207173 416.9078232715078 1008.0553023103668 597.6324456402497C866.1177527202434 708.0286505428097 691.3591427821901 768.1283337025917 511.4866651896334 768.1283337025917M511.4866651896334 682.8808891701171C642.3423366997788 682.8808891701171 771.0661477733722 646.2236788332648 882.7410693389065 578.4516956956384L800.0500591349871 474.8756459947375C735.688653096887 512.385001107775 634.2434648351453 555.0092228727087 511.4866651896334 555.0092228727087C383.614998892225 555.0092228727087 284.7272439564316 512.385001107775 222.0701274707015 476.5809345445006L139.3801162641751 578.8782675824273C251.9071826058945 646.6502507200537 380.6309936794878 682.8808891701171 511.4866651896334 682.8808891701171z" />
|
horiz-adv-x="1022.9733303792667" d="M511.4866651896334 768.1283337025917C332.0397604864727 768.1283337025917 157.7087214326013 708.0286505428097 16.1967447318741 597.6324456402496C187.9713494571903 381.9559014844185 336.3024823621839 198.2472739029395 511.4866651896334 -20.4132754656288C685.3921313541088 196.1154134663874 862.7071756207173 416.9078232715078 1008.0553023103668 597.6324456402497C866.1177527202434 708.0286505428097 691.3591427821901 768.1283337025917 511.4866651896334 768.1283337025917M511.4866651896334 682.8808891701171C642.3423366997788 682.8808891701171 771.0661477733722 646.2236788332648 882.7410693389065 578.4516956956384L800.0500591349871 474.8756459947375C735.688653096887 512.385001107775 634.2434648351453 555.0092228727087 511.4866651896334 555.0092228727087C383.614998892225 555.0092228727087 284.7272439564316 512.385001107775 222.0701274707015 476.5809345445006L139.3801162641751 578.8782675824273C251.9071826058945 646.6502507200537 380.6309936794878 682.8808891701171 511.4866651896334 682.8808891701171z" />
|
||||||
@@ -67,12 +73,24 @@
|
|||||||
<glyph glyph-name="wifi-strength-4-1"
|
<glyph glyph-name="wifi-strength-4-1"
|
||||||
unicode="wifi_strength_4"
|
unicode="wifi_strength_4"
|
||||||
horiz-adv-x="1022.9733303792667" d="M511.4866651896334 768.1283337025917C332.0397604864727 768.1283337025917 157.7087214326013 708.0286505428097 16.1967447318741 597.6324456402496C187.9713494571903 381.9559014844185 336.3024823621839 198.2472739029395 511.4866651896334 -20.4132754656288C685.3921313541088 196.1154134663874 862.7071756207173 416.9078232715078 1008.0553023103668 597.6324456402497C866.1177527202434 708.0286505428097 691.3591427821901 768.1283337025917 511.4866651896334 768.1283337025917z" />
|
horiz-adv-x="1022.9733303792667" d="M511.4866651896334 768.1283337025917C332.0397604864727 768.1283337025917 157.7087214326013 708.0286505428097 16.1967447318741 597.6324456402496C187.9713494571903 381.9559014844185 336.3024823621839 198.2472739029395 511.4866651896334 -20.4132754656288C685.3921313541088 196.1154134663874 862.7071756207173 416.9078232715078 1008.0553023103668 597.6324456402497C866.1177527202434 708.0286505428097 691.3591427821901 768.1283337025917 511.4866651896334 768.1283337025917z" />
|
||||||
|
<glyph glyph-name="success"
|
||||||
|
unicode=""
|
||||||
|
horiz-adv-x="1024" d="M512 810.667C276.36 810.667 85.333 619.64 85.333 384C85.333 148.36 276.3590000000001 -42.6669999999999 512 -42.6669999999999C747.64 -42.6669999999999 938.667 148.3590000000002 938.667 384.0000000000001C938.667 619.64 747.64 810.667 512 810.667zM512 42.667C323.79 42.667 170.667 195.789 170.667 384.0000000000001C170.667 572.21 323.789 725.3330000000001 512 725.3330000000001C700.21 725.3330000000001 853.3330000000001 572.211 853.3330000000001 384.0000000000001C853.3330000000001 195.7900000000001 700.211 42.667 512 42.667zM672.672 536.437L733.005 476.104L469.333 211.328L311.167 369.495L371.5 429.828L469.333 331.995L672.673 536.438z" />
|
||||||
|
<glyph glyph-name="success-1"
|
||||||
|
unicode="success"
|
||||||
|
horiz-adv-x="1024" d="M512 810.667C276.36 810.667 85.333 619.64 85.333 384C85.333 148.36 276.3590000000001 -42.6669999999999 512 -42.6669999999999C747.64 -42.6669999999999 938.667 148.3590000000002 938.667 384.0000000000001C938.667 619.64 747.64 810.667 512 810.667zM512 42.667C323.79 42.667 170.667 195.789 170.667 384.0000000000001C170.667 572.21 323.789 725.3330000000001 512 725.3330000000001C700.21 725.3330000000001 853.3330000000001 572.211 853.3330000000001 384.0000000000001C853.3330000000001 195.7900000000001 700.211 42.667 512 42.667zM672.672 536.437L733.005 476.104L469.333 211.328L311.167 369.495L371.5 429.828L469.333 331.995L672.673 536.438z" />
|
||||||
<glyph glyph-name="up"
|
<glyph glyph-name="up"
|
||||||
unicode=""
|
unicode=""
|
||||||
horiz-adv-x="893.7641930109211" d="M69.6258266430383 317.8562626928574L25.3366188643721 362.1454704715236C6.5835308860179 380.8985584498777 6.5835308860179 411.2227007127483 25.3366188643721 429.7762877551625L412.9669373956711 817.6021172036826C431.7200253740253 836.3552051820368 462.0441676368958 836.3552051820368 480.59775467931 817.6021172036826L868.2280732106092 429.9717986723836C886.9811611889633 411.2187106940295 886.9811611889633 380.894568431159 868.2280732106092 362.3409813887447L823.938865431943 318.0517736100786C804.9862765176489 299.0991846957845 774.0636314469585 299.4981865676644 755.5100444045443 318.8497773538383L526.6824708814356 559.0528942442421V-14.3127956471388C526.6824708814356 -40.8464201271506 505.3358707358623 -62.1930202727239 478.8022462558506 -62.1930202727239H414.9619467550705C388.4283222750588 -62.1930202727239 367.0817221294855 -40.8464201271506 367.0817221294855 -14.3127956471388V559.0528942442422L138.0546476704369 318.6542664366173C119.5010606280227 299.1031747145034 88.5784155573324 298.7041728426234 69.6258266430383 317.8562626928575z" />
|
horiz-adv-x="893.7641930109211" d="M69.6258266430383 317.8562626928574L25.3366188643721 362.1454704715236C6.5835308860179 380.8985584498777 6.5835308860179 411.2227007127483 25.3366188643721 429.7762877551625L412.9669373956711 817.6021172036826C431.7200253740253 836.3552051820368 462.0441676368958 836.3552051820368 480.59775467931 817.6021172036826L868.2280732106092 429.9717986723836C886.9811611889633 411.2187106940295 886.9811611889633 380.894568431159 868.2280732106092 362.3409813887447L823.938865431943 318.0517736100786C804.9862765176489 299.0991846957845 774.0636314469585 299.4981865676644 755.5100444045443 318.8497773538383L526.6824708814356 559.0528942442421V-14.3127956471388C526.6824708814356 -40.8464201271506 505.3358707358623 -62.1930202727239 478.8022462558506 -62.1930202727239H414.9619467550705C388.4283222750588 -62.1930202727239 367.0817221294855 -40.8464201271506 367.0817221294855 -14.3127956471388V559.0528942442422L138.0546476704369 318.6542664366173C119.5010606280227 299.1031747145034 88.5784155573324 298.7041728426234 69.6258266430383 317.8562626928575z" />
|
||||||
<glyph glyph-name="up-1"
|
<glyph glyph-name="up-1"
|
||||||
unicode="up"
|
unicode="up"
|
||||||
horiz-adv-x="893.7641930109211" d="M69.6258266430383 317.8562626928574L25.3366188643721 362.1454704715236C6.5835308860179 380.8985584498777 6.5835308860179 411.2227007127483 25.3366188643721 429.7762877551625L412.9669373956711 817.6021172036826C431.7200253740253 836.3552051820368 462.0441676368958 836.3552051820368 480.59775467931 817.6021172036826L868.2280732106092 429.9717986723836C886.9811611889633 411.2187106940295 886.9811611889633 380.894568431159 868.2280732106092 362.3409813887447L823.938865431943 318.0517736100786C804.9862765176489 299.0991846957845 774.0636314469585 299.4981865676644 755.5100444045443 318.8497773538383L526.6824708814356 559.0528942442421V-14.3127956471388C526.6824708814356 -40.8464201271506 505.3358707358623 -62.1930202727239 478.8022462558506 -62.1930202727239H414.9619467550705C388.4283222750588 -62.1930202727239 367.0817221294855 -40.8464201271506 367.0817221294855 -14.3127956471388V559.0528942442422L138.0546476704369 318.6542664366173C119.5010606280227 299.1031747145034 88.5784155573324 298.7041728426234 69.6258266430383 317.8562626928575z" />
|
horiz-adv-x="893.7641930109211" d="M69.6258266430383 317.8562626928574L25.3366188643721 362.1454704715236C6.5835308860179 380.8985584498777 6.5835308860179 411.2227007127483 25.3366188643721 429.7762877551625L412.9669373956711 817.6021172036826C431.7200253740253 836.3552051820368 462.0441676368958 836.3552051820368 480.59775467931 817.6021172036826L868.2280732106092 429.9717986723836C886.9811611889633 411.2187106940295 886.9811611889633 380.894568431159 868.2280732106092 362.3409813887447L823.938865431943 318.0517736100786C804.9862765176489 299.0991846957845 774.0636314469585 299.4981865676644 755.5100444045443 318.8497773538383L526.6824708814356 559.0528942442421V-14.3127956471388C526.6824708814356 -40.8464201271506 505.3358707358623 -62.1930202727239 478.8022462558506 -62.1930202727239H414.9619467550705C388.4283222750588 -62.1930202727239 367.0817221294855 -40.8464201271506 367.0817221294855 -14.3127956471388V559.0528942442422L138.0546476704369 318.6542664366173C119.5010606280227 299.1031747145034 88.5784155573324 298.7041728426234 69.6258266430383 317.8562626928575z" />
|
||||||
|
<glyph glyph-name="alarm"
|
||||||
|
unicode=""
|
||||||
|
horiz-adv-x="1024" d="M512 810.667C747.22 810.667 938.667 619.22 938.667 384C938.667 148.78 747.2199999999999 -42.6669999999999 511.9999999999999 -42.6669999999999C276.78 -42.667 85.333 148.78 85.333 384C85.333 619.22 276.78 810.667 512 810.667zM512 725.333C323.082 725.333 170.667 572.918 170.667 384C170.667 195.082 323.082 42.6669999999999 512 42.6669999999999C700.918 42.6669999999999 853.3330000000001 195.0819999999999 853.3330000000001 384C853.3330000000001 572.918 700.9180000000001 725.3330000000001 512 725.3330000000001zM512 266.581C542.476 266.581 565.333 244.054 565.333 213.3340000000001S542.476 160.085 512 160.085C480.83 160.085 458.667 182.6130000000001 458.667 214.015C458.667 244.053 481.524 266.581 512 266.581zM554.667 640V341.3330000000001H469.333V640H554.667z" />
|
||||||
|
<glyph glyph-name="alarm-1"
|
||||||
|
unicode="alarm"
|
||||||
|
horiz-adv-x="1024" d="M512 810.667C747.22 810.667 938.667 619.22 938.667 384C938.667 148.78 747.2199999999999 -42.6669999999999 511.9999999999999 -42.6669999999999C276.78 -42.667 85.333 148.78 85.333 384C85.333 619.22 276.78 810.667 512 810.667zM512 725.333C323.082 725.333 170.667 572.918 170.667 384C170.667 195.082 323.082 42.6669999999999 512 42.6669999999999C700.918 42.6669999999999 853.3330000000001 195.0819999999999 853.3330000000001 384C853.3330000000001 572.918 700.9180000000001 725.3330000000001 512 725.3330000000001zM512 266.581C542.476 266.581 565.333 244.054 565.333 213.3340000000001S542.476 160.085 512 160.085C480.83 160.085 458.667 182.6130000000001 458.667 214.015C458.667 244.053 481.524 266.581 512 266.581zM554.667 640V341.3330000000001H469.333V640H554.667z" />
|
||||||
</font>
|
</font>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 27 KiB |
@@ -105,7 +105,8 @@
|
|||||||
"mHeatEnabled": "Heating enabled",
|
"mHeatEnabled": "Heating enabled",
|
||||||
"mHeatBlocking": "Heating blocked",
|
"mHeatBlocking": "Heating blocked",
|
||||||
"sHeatActive": "Heating active",
|
"sHeatActive": "Heating active",
|
||||||
"mHeatTargetTemp": "Heating setpoint temp",
|
"mHeatSetpointTemp": "Heating setpoint temp",
|
||||||
|
"mHeatTargetTemp": "Heating target temp",
|
||||||
"mHeatCurrTemp": "Heating current temp",
|
"mHeatCurrTemp": "Heating current temp",
|
||||||
"mHeatRetTemp": "Heating return temp",
|
"mHeatRetTemp": "Heating return temp",
|
||||||
"mHeatIndoorTemp": "Heating, indoor temp",
|
"mHeatIndoorTemp": "Heating, indoor temp",
|
||||||
@@ -113,9 +114,18 @@
|
|||||||
|
|
||||||
"mDhwEnabled": "DHW enabled",
|
"mDhwEnabled": "DHW enabled",
|
||||||
"sDhwActive": "DHW active",
|
"sDhwActive": "DHW active",
|
||||||
"mDhwTargetTemp": "DHW setpoint temp",
|
"mDhwTargetTemp": "DHW target temp",
|
||||||
"mDhwCurrTemp": "DHW current temp",
|
"mDhwCurrTemp": "DHW current temp",
|
||||||
"mDhwRetTemp": "DHW return temp"
|
"mDhwRetTemp": "DHW return temp"
|
||||||
|
},
|
||||||
|
|
||||||
|
"sensors": {
|
||||||
|
"values": {
|
||||||
|
"temp": "Temperature",
|
||||||
|
"humidity": "Humidity",
|
||||||
|
"battery": "Battery",
|
||||||
|
"rssi": "RSSI"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -178,7 +188,10 @@
|
|||||||
"dhwFlowRate": "DHW, flow rate",
|
"dhwFlowRate": "DHW, flow rate",
|
||||||
"exhaustTemp": "Exhaust temperature",
|
"exhaustTemp": "Exhaust temperature",
|
||||||
"modLevel": "Modulation level (in percents)",
|
"modLevel": "Modulation level (in percents)",
|
||||||
"currentPower": "Current power (in kWt)",
|
"powerFactor": "Power (in percent)",
|
||||||
|
"power": "Power (in kWt)",
|
||||||
|
"fanSpeed": "Fan speed",
|
||||||
|
"co2": "CO2",
|
||||||
"pressure": "Pressure",
|
"pressure": "Pressure",
|
||||||
"humidity": "Humidity",
|
"humidity": "Humidity",
|
||||||
"temperature": "Temperature",
|
"temperature": "Temperature",
|
||||||
@@ -198,6 +211,14 @@
|
|||||||
"otPressure": "OpenTherm, pressure",
|
"otPressure": "OpenTherm, pressure",
|
||||||
"otModLevel": "OpenTherm, modulation level",
|
"otModLevel": "OpenTherm, modulation level",
|
||||||
"otCurrentPower": "OpenTherm, current power",
|
"otCurrentPower": "OpenTherm, current power",
|
||||||
|
"otExhaustCo2": "OpenTherm, exhaust CO2",
|
||||||
|
"otExhaustFanSpeed": "OpenTherm, exhaust fan speed",
|
||||||
|
"otSupplyFanSpeed": "OpenTherm, supply fan speed",
|
||||||
|
"otSolarStorageTemp": "OpenTherm, solar storage temp",
|
||||||
|
"otSolarCollectorTemp": "OpenTherm, solar collector temp",
|
||||||
|
"otFanSpeedSetpoint": "OpenTherm, setpoint fan speed",
|
||||||
|
"otFanSpeedCurrent": "OpenTherm, current fan speed",
|
||||||
|
|
||||||
"ntcTemp": "NTC sensor",
|
"ntcTemp": "NTC sensor",
|
||||||
"dallasTemp": "DALLAS sensor",
|
"dallasTemp": "DALLAS sensor",
|
||||||
"bluetooth": "BLE sensor",
|
"bluetooth": "BLE sensor",
|
||||||
@@ -407,7 +428,7 @@
|
|||||||
|
|
||||||
"note": {
|
"note": {
|
||||||
"disclaimer1": "After a successful upgrade the filesystem, ALL settings will be reset to default values! Save backup before upgrading.",
|
"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 10 seconds."
|
"disclaimer2": "After a successful upgrade, the device will automatically reboot after 15 seconds."
|
||||||
},
|
},
|
||||||
|
|
||||||
"settingsFile": "Settings file",
|
"settingsFile": "Settings file",
|
||||||
|
|||||||
@@ -105,6 +105,7 @@
|
|||||||
"mHeatEnabled": "Отопление",
|
"mHeatEnabled": "Отопление",
|
||||||
"mHeatBlocking": "Блокировка отопления",
|
"mHeatBlocking": "Блокировка отопления",
|
||||||
"sHeatActive": "Активность отопления",
|
"sHeatActive": "Активность отопления",
|
||||||
|
"mHeatSetpointTemp": "Отопление, уставка",
|
||||||
"mHeatTargetTemp": "Отопление, целевая температура",
|
"mHeatTargetTemp": "Отопление, целевая температура",
|
||||||
"mHeatCurrTemp": "Отопление, текущая температура",
|
"mHeatCurrTemp": "Отопление, текущая температура",
|
||||||
"mHeatRetTemp": "Отопление, температура обратки",
|
"mHeatRetTemp": "Отопление, температура обратки",
|
||||||
@@ -116,6 +117,15 @@
|
|||||||
"mDhwTargetTemp": "ГВС, целевая температура",
|
"mDhwTargetTemp": "ГВС, целевая температура",
|
||||||
"mDhwCurrTemp": "ГВС, текущая температура",
|
"mDhwCurrTemp": "ГВС, текущая температура",
|
||||||
"mDhwRetTemp": "ГВС, температура обратки"
|
"mDhwRetTemp": "ГВС, температура обратки"
|
||||||
|
},
|
||||||
|
|
||||||
|
"sensors": {
|
||||||
|
"values": {
|
||||||
|
"temp": "Температура",
|
||||||
|
"humidity": "Влажность",
|
||||||
|
"battery": "Уровень заряда",
|
||||||
|
"rssi": "RSSI"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -178,7 +188,10 @@
|
|||||||
"dhwFlowRate": "ГВС, расход/скорость потока",
|
"dhwFlowRate": "ГВС, расход/скорость потока",
|
||||||
"exhaustTemp": "Температура выхлопных газов",
|
"exhaustTemp": "Температура выхлопных газов",
|
||||||
"modLevel": "Уровень модуляции (в процентах)",
|
"modLevel": "Уровень модуляции (в процентах)",
|
||||||
"currentPower": "Текущая мощность (в кВт)",
|
"powerFactor": "Мощность (в процентах)",
|
||||||
|
"power": "Мощность (в кВт)",
|
||||||
|
"fanSpeed": "Скорость вентилятора",
|
||||||
|
"co2": "CO2",
|
||||||
"pressure": "Давление",
|
"pressure": "Давление",
|
||||||
"humidity": "Влажность",
|
"humidity": "Влажность",
|
||||||
"temperature": "Температура",
|
"temperature": "Температура",
|
||||||
@@ -198,6 +211,14 @@
|
|||||||
"otPressure": "OpenTherm, давление",
|
"otPressure": "OpenTherm, давление",
|
||||||
"otModLevel": "OpenTherm, уровень модуляции",
|
"otModLevel": "OpenTherm, уровень модуляции",
|
||||||
"otCurrentPower": "OpenTherm, текущая мощность",
|
"otCurrentPower": "OpenTherm, текущая мощность",
|
||||||
|
"otExhaustCo2": "OpenTherm, CO2 вытяжного воздуха",
|
||||||
|
"otExhaustFanSpeed": "OpenTherm, скорость вытяжного вентилятора",
|
||||||
|
"otSupplyFanSpeed": "OpenTherm, скорость приточного вентилятора",
|
||||||
|
"otSolarStorageTemp": "OpenTherm, темп. бойлера солн. коллектора",
|
||||||
|
"otSolarCollectorTemp": "OpenTherm, темп. солн. коллектора",
|
||||||
|
"otFanSpeedSetpoint": "OpenTherm, установленная мощн. вентилятора",
|
||||||
|
"otFanSpeedCurrent": "OpenTherm, текущая мощн. вентилятора",
|
||||||
|
|
||||||
"ntcTemp": "NTC датчик",
|
"ntcTemp": "NTC датчик",
|
||||||
"dallasTemp": "DALLAS датчик",
|
"dallasTemp": "DALLAS датчик",
|
||||||
"bluetooth": "BLE датчик",
|
"bluetooth": "BLE датчик",
|
||||||
@@ -407,7 +428,7 @@
|
|||||||
|
|
||||||
"note": {
|
"note": {
|
||||||
"disclaimer1": "После успешного обновления файловой системы ВСЕ настройки будут сброшены на стандартные! Создайте резервную копию ПЕРЕД обновлением.",
|
"disclaimer1": "После успешного обновления файловой системы ВСЕ настройки будут сброшены на стандартные! Создайте резервную копию ПЕРЕД обновлением.",
|
||||||
"disclaimer2": "После успешного обновления устройство автоматически перезагрузится через 10 секунд."
|
"disclaimer2": "После успешного обновления устройство автоматически перезагрузится через 15 секунд."
|
||||||
},
|
},
|
||||||
|
|
||||||
"settingsFile": "Файл настроек",
|
"settingsFile": "Файл настроек",
|
||||||
|
|||||||
@@ -4,19 +4,20 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title data-i18n>dashboard.title</title>
|
<title data-i18n>dashboard.title</title>
|
||||||
<link rel="stylesheet" href="/static/app.css" />
|
<link rel="stylesheet" href="/static/app.css?{BUILD_TIME}" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header class="container">
|
<header class="container">
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">
|
<li>
|
||||||
|
<a href="/">
|
||||||
<div class="logo" data-i18n>logo</div>
|
<div class="logo" data-i18n>logo</div>
|
||||||
</a></li>
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<!--<li><a href="https://github.com/Laxilef/OTGateway/wiki" role="button" class="secondary" target="_blank">Help</a></li>-->
|
|
||||||
<li>
|
<li>
|
||||||
<select id="lang" aria-label="Lang">
|
<select id="lang" aria-label="Lang">
|
||||||
<option value="en" selected>EN</option>
|
<option value="en" selected>EN</option>
|
||||||
@@ -80,43 +81,43 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mNetworkConnected</th>
|
<th scope="row" data-i18n>dashboard.states.mNetworkConnected</th>
|
||||||
<td><input type="radio" class="mNetworkConnected" aria-invalid="false" checked disabled /></td>
|
<td><i class="mNetworkConnected"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mMqttConnected</th>
|
<th scope="row" data-i18n>dashboard.states.mMqttConnected</th>
|
||||||
<td><input type="radio" class="mMqttConnected" aria-invalid="false" checked disabled /></td>
|
<td><i class="mMqttConnected"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mEmergencyState</th>
|
<th scope="row" data-i18n>dashboard.states.mEmergencyState</th>
|
||||||
<td><input type="radio" class="mEmergencyState" aria-invalid="false" checked disabled /></td>
|
<td><i class="mEmergencyState"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mExtPumpState</th>
|
<th scope="row" data-i18n>dashboard.states.mExtPumpState</th>
|
||||||
<td><input type="radio" class="mExtPumpState" aria-invalid="false" checked disabled /></td>
|
<td><i class="mExtPumpState"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mCascadeControlInput</th>
|
<th scope="row" data-i18n>dashboard.states.mCascadeControlInput</th>
|
||||||
<td><input type="radio" id="mCascadeControlInput" aria-invalid="false" checked disabled /></td>
|
<td><i class="mCascadeControlInput"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mCascadeControlOutput</th>
|
<th scope="row" data-i18n>dashboard.states.mCascadeControlOutput</th>
|
||||||
<td><input type="radio" id="mCascadeControlOutput" aria-invalid="false" checked disabled /></td>
|
<td><i class="mCascadeControlOutput"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.sConnected</th>
|
<th scope="row" data-i18n>dashboard.states.sConnected</th>
|
||||||
<td><input type="radio" class="sConnected" aria-invalid="false" checked disabled /></td>
|
<td><i class="sConnected"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.sFlame</th>
|
<th scope="row" data-i18n>dashboard.states.sFlame</th>
|
||||||
<td><input type="radio" class="sFlame" aria-invalid="false" checked disabled /></td>
|
<td><i class="sFlame"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.sFaultActive</th>
|
<th scope="row" data-i18n>dashboard.states.sFaultActive</th>
|
||||||
<td><input type="radio" class="sFaultActive" aria-invalid="false" checked disabled /></td>
|
<td><i class="sFaultActive"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.sFaultCode</th>
|
<th scope="row" data-i18n>dashboard.states.sFaultCode</th>
|
||||||
@@ -124,7 +125,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.sDiagActive</th>
|
<th scope="row" data-i18n>dashboard.states.sDiagActive</th>
|
||||||
<td><input type="radio" class="sDiagActive" aria-invalid="false" checked disabled /></td>
|
<td><i class="sDiagActive"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.sDiagCode</th>
|
<th scope="row" data-i18n>dashboard.states.sDiagCode</th>
|
||||||
@@ -134,20 +135,24 @@
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mHeatEnabled</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatEnabled</th>
|
||||||
<td><input type="radio" class="mHeatEnabled" aria-invalid="false" checked disabled /></td>
|
<td><i class="mHeatEnabled"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mHeatBlocking</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatBlocking</th>
|
||||||
<td><input type="radio" class="mHeatBlocking" aria-invalid="false" checked disabled /></td>
|
<td><i class="mHeatBlocking"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.sHeatActive</th>
|
<th scope="row" data-i18n>dashboard.states.sHeatActive</th>
|
||||||
<td><input type="radio" class="sHeatActive" aria-invalid="false" checked disabled /></td>
|
<td><i class="sHeatActive"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mHeatTargetTemp</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatTargetTemp</th>
|
||||||
<td><b class="mHeatTargetTemp"></b> <span class="tempUnit"></span></td>
|
<td><b class="mHeatTargetTemp"></b> <span class="tempUnit"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.states.mHeatSetpointTemp</th>
|
||||||
|
<td><b class="mHeatSetpointTemp"></b> <span class="tempUnit"></span></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mHeatCurrTemp</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatCurrTemp</th>
|
||||||
<td><b class="mHeatCurrTemp"></b> <span class="tempUnit"></span></td>
|
<td><b class="mHeatCurrTemp"></b> <span class="tempUnit"></span></td>
|
||||||
@@ -168,11 +173,11 @@
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mDhwEnabled</th>
|
<th scope="row" data-i18n>dashboard.states.mDhwEnabled</th>
|
||||||
<td><input type="radio" class="mDhwEnabled" aria-invalid="false" checked disabled /></td>
|
<td><i class="mDhwEnabled"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.sDhwActive</th>
|
<th scope="row" data-i18n>dashboard.states.sDhwActive</th>
|
||||||
<td><input type="radio" class="sDhwActive" aria-invalid="false" checked disabled /></td>
|
<td><i class="sDhwActive"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.states.mDhwTargetTemp</th>
|
<th scope="row" data-i18n>dashboard.states.mDhwTargetTemp</th>
|
||||||
@@ -192,6 +197,25 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b data-i18n>dashboard.section.sensors</b></summary>
|
||||||
|
<table>
|
||||||
|
<tbody class="sensors">
|
||||||
|
<tr class="sensor template hidden">
|
||||||
|
<td style="width: 1.35rem">
|
||||||
|
<span class="sStatusContainer">
|
||||||
|
<i class="sStatus"></i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<th scope="row" class="sName"></th>
|
||||||
|
<td class="sValue"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b data-i18n>dashboard.section.diag</b></summary>
|
<summary><b data-i18n>dashboard.section.diag</b></summary>
|
||||||
<pre><b>Vendor:</b> <span class="sVendor"></span>
|
<pre><b>Vendor:</b> <span class="sVendor"></span>
|
||||||
@@ -220,7 +244,7 @@
|
|||||||
</small>
|
</small>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/static/app.js"></script>
|
<script src="/static/app.js?{BUILD_TIME}"></script>
|
||||||
<script>
|
<script>
|
||||||
let modifiedTime = null;
|
let modifiedTime = null;
|
||||||
let noRegulators;
|
let noRegulators;
|
||||||
@@ -331,6 +355,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(async function onLoadPage() {
|
setTimeout(async function onLoadPage() {
|
||||||
|
let unitSystem = null;
|
||||||
|
|
||||||
if (modifiedTime) {
|
if (modifiedTime) {
|
||||||
if ((Date.now() - modifiedTime) < 5000) {
|
if ((Date.now() - modifiedTime) < 5000) {
|
||||||
setTimeout(onLoadPage, 1000);
|
setTimeout(onLoadPage, 1000);
|
||||||
@@ -354,13 +380,18 @@
|
|||||||
console.log(newSettings);
|
console.log(newSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
let parameters = { cache: 'no-cache' };
|
let parameters = {
|
||||||
|
method: "GET",
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
};
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
parameters.method = "POST";
|
parameters.method = "POST";
|
||||||
parameters.body = JSON.stringify(newSettings);
|
parameters.body = JSON.stringify(newSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch('/api/settings', parameters);
|
const response = await fetch("/api/settings", parameters);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Response not valid');
|
throw new Error('Response not valid');
|
||||||
}
|
}
|
||||||
@@ -368,6 +399,7 @@
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
noRegulators = !result.opentherm.nativeHeatingControl && !result.equitherm.enabled && !result.pid.enabled;
|
noRegulators = !result.opentherm.nativeHeatingControl && !result.equitherm.enabled && !result.pid.enabled;
|
||||||
prevSettings = result;
|
prevSettings = result;
|
||||||
|
unitSystem = result.system.unitSystem;
|
||||||
newSettings.heating.enabled = result.heating.enabled;
|
newSettings.heating.enabled = result.heating.enabled;
|
||||||
newSettings.heating.turbo = result.heating.turbo;
|
newSettings.heating.turbo = result.heating.turbo;
|
||||||
newSettings.heating.target = result.heating.target;
|
newSettings.heating.target = result.heating.target;
|
||||||
@@ -387,9 +419,9 @@
|
|||||||
setCheckboxValue('#tDhwEnabled', result.dhw.enabled);
|
setCheckboxValue('#tDhwEnabled', result.dhw.enabled);
|
||||||
setValue('#tDhwTargetTemp', result.dhw.target);
|
setValue('#tDhwTargetTemp', result.dhw.target);
|
||||||
|
|
||||||
setValue('.tempUnit', temperatureUnit(result.system.unitSystem));
|
setValue('.tempUnit', temperatureUnit(unitSystem));
|
||||||
setValue('.pressureUnit', pressureUnit(result.system.unitSystem));
|
setValue('.pressureUnit', pressureUnit(unitSystem));
|
||||||
setValue('.volumeUnit', volumeUnit(result.system.unitSystem));
|
setValue('.volumeUnit', volumeUnit(unitSystem));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@@ -397,7 +429,11 @@
|
|||||||
|
|
||||||
// vars
|
// vars
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/vars', { cache: 'no-cache' });
|
const response = await fetch("/api/vars", {
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Response not valid');
|
throw new Error('Response not valid');
|
||||||
}
|
}
|
||||||
@@ -405,7 +441,7 @@
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
// Graph
|
// Graph
|
||||||
setValue('#tHeatCurrentTemp', result.master.indoorTempControl
|
setValue('#tHeatCurrentTemp', result.master.heating.indoorTempControl
|
||||||
? result.master.heating.indoorTemp
|
? result.master.heating.indoorTemp
|
||||||
: result.master.heating.currentTemp
|
: result.master.heating.currentTemp
|
||||||
);
|
);
|
||||||
@@ -420,7 +456,11 @@
|
|||||||
setValue('.sAppVersion', result.slave.appVersion);
|
setValue('.sAppVersion', result.slave.appVersion);
|
||||||
setValue('.sProtocolVersion', result.slave.protocolVersion);
|
setValue('.sProtocolVersion', result.slave.protocolVersion);
|
||||||
|
|
||||||
setState('.sConnected', result.slave.connected);
|
setStatus(
|
||||||
|
'.sConnected',
|
||||||
|
result.slave.connected ? "success" : "error",
|
||||||
|
result.slave.connected ? "green" : "red"
|
||||||
|
);
|
||||||
setState('.sFlame', result.slave.flame);
|
setState('.sFlame', result.slave.flame);
|
||||||
|
|
||||||
setValue('.sModMin', result.slave.modulation.min);
|
setValue('.sModMin', result.slave.modulation.min);
|
||||||
@@ -437,27 +477,40 @@
|
|||||||
setValue('.sDhwMinTemp', result.slave.dhw.minTemp);
|
setValue('.sDhwMinTemp', result.slave.dhw.minTemp);
|
||||||
setValue('.sDhwMaxTemp', result.slave.dhw.maxTemp);
|
setValue('.sDhwMaxTemp', result.slave.dhw.maxTemp);
|
||||||
|
|
||||||
setState('.sFaultActive', result.slave.fault.active);
|
setStatus(
|
||||||
|
'.sFaultActive',
|
||||||
|
result.slave.fault.active ? "success" : "error",
|
||||||
|
result.slave.fault.active ? "red" : "green"
|
||||||
|
);
|
||||||
setValue(
|
setValue(
|
||||||
'.sFaultCode',
|
'.sFaultCode',
|
||||||
result.slave.fault.active
|
result.slave.fault.active
|
||||||
? (result.slave.fault.code + " (0x" + dec2hex(result.slave.fault.code) + ")")
|
? `${result.slave.fault.code} (0x${dec2hex(result.slave.fault.code)})`
|
||||||
: "-"
|
: "-"
|
||||||
);
|
);
|
||||||
|
|
||||||
setState('.sDiagActive', result.slave.diag.active);
|
setStatus(
|
||||||
|
'.sDiagActive',
|
||||||
|
result.slave.diag.active ? "success" : "error",
|
||||||
|
result.slave.diag.active ? "red" : "green"
|
||||||
|
);
|
||||||
setValue(
|
setValue(
|
||||||
'.sDiagCode',
|
'.sDiagCode',
|
||||||
result.slave.diag.active
|
result.slave.diag.active
|
||||||
? (result.slave.diag.code + " (0x" + dec2hex(result.slave.diag.code) + ")")
|
? `${result.slave.diag.code} (0x${dec2hex(result.slave.diag.code)})`
|
||||||
: "-"
|
: "-"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// MASTER
|
// MASTER
|
||||||
setState('.mHeatEnabled', result.master.heating.enabled);
|
setState('.mHeatEnabled', result.master.heating.enabled);
|
||||||
setState('.mHeatBlocking', result.master.heating.blocking);
|
setStatus(
|
||||||
|
'.mHeatBlocking',
|
||||||
|
result.master.heating.blocking ? "success" : "error",
|
||||||
|
result.master.heating.blocking ? "red" : "green"
|
||||||
|
);
|
||||||
setState('.mHeatIndoorTempControl', result.master.heating.indoorTempControl);
|
setState('.mHeatIndoorTempControl', result.master.heating.indoorTempControl);
|
||||||
|
setValue('.mHeatSetpointTemp', result.master.heating.setpointTemp);
|
||||||
setValue('.mHeatTargetTemp', result.master.heating.targetTemp);
|
setValue('.mHeatTargetTemp', result.master.heating.targetTemp);
|
||||||
setValue('.mHeatCurrTemp', result.master.heating.currentTemp);
|
setValue('.mHeatCurrTemp', result.master.heating.currentTemp);
|
||||||
setValue('.mHeatRetTemp', result.master.heating.returnTemp);
|
setValue('.mHeatRetTemp', result.master.heating.returnTemp);
|
||||||
@@ -473,9 +526,17 @@
|
|||||||
setValue('.mDhwMinTemp', result.master.dhw.minTemp);
|
setValue('.mDhwMinTemp', result.master.dhw.minTemp);
|
||||||
setValue('.mDhwMaxTemp', result.master.dhw.maxTemp);
|
setValue('.mDhwMaxTemp', result.master.dhw.maxTemp);
|
||||||
|
|
||||||
setState('.mNetworkConnected', result.master.network.connected);
|
setStatus(
|
||||||
|
'.mNetworkConnected',
|
||||||
|
result.master.network.connected ? "success" : "error",
|
||||||
|
result.master.network.connected ? "green" : "red"
|
||||||
|
);
|
||||||
setState('.mMqttConnected', result.master.mqtt.connected);
|
setState('.mMqttConnected', result.master.mqtt.connected);
|
||||||
setState('.mEmergencyState', result.master.emergency.state);
|
setStatus(
|
||||||
|
'.mEmergencyState',
|
||||||
|
result.master.emergency.state ? "success" : "error",
|
||||||
|
result.master.emergency.state ? "red" : "green"
|
||||||
|
);
|
||||||
setState('.mExtPumpState', result.master.externalPump.state);
|
setState('.mExtPumpState', result.master.externalPump.state);
|
||||||
setState('.mCascadeControlInput', result.master.cascadeControl.input);
|
setState('.mCascadeControlInput', result.master.cascadeControl.input);
|
||||||
setState('.mCascadeControlOutput', result.master.cascadeControl.output);
|
setState('.mCascadeControlOutput', result.master.cascadeControl.output);
|
||||||
@@ -486,6 +547,88 @@
|
|||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sensors
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/sensors?detailed=1", {
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Response not valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.querySelector(".sensors");
|
||||||
|
const templateNode = container.querySelector(".template");
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
for (const sensorId in result) {
|
||||||
|
let sensorNode = container.querySelector(`.sensor[data-id='${sensorId}']`);
|
||||||
|
if (sensorNode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sensorNode = templateNode.cloneNode(true);
|
||||||
|
sensorNode.dataset.id = sensorId;
|
||||||
|
sensorNode.classList.remove("template");
|
||||||
|
container.appendChild(sensorNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const sensorId in result) {
|
||||||
|
const sensorNode = container.querySelector(`.sensor[data-id='${sensorId}']`);
|
||||||
|
if (!sensorNode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sData = result[sensorId];
|
||||||
|
if (!sData.enabled || sData.purpose == 255) {
|
||||||
|
sensorNode.classList.toggle("hidden", true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sensorNode.classList.toggle("hidden", false);
|
||||||
|
|
||||||
|
setStatus(
|
||||||
|
".sStatus",
|
||||||
|
sData.connected ? "success" : "error",
|
||||||
|
sData.connected ? "green" : "red",
|
||||||
|
sensorNode
|
||||||
|
);
|
||||||
|
setValue(".sName", sData.name, sensorNode);
|
||||||
|
setValue(".sValue", "", sensorNode);
|
||||||
|
|
||||||
|
const statusNode = sensorNode.querySelector(`.sStatusContainer`);
|
||||||
|
if (statusNode) {
|
||||||
|
statusNode.dataset.tooltip = `${sData.signalQuality}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sData.value !== undefined) {
|
||||||
|
const sUnit = purposeUnit(sData.purpose, unitSystem);
|
||||||
|
appendValue(".sValue", `<b>${sData.value.toFixed(2)}</b> ${sUnit !== null ? sUnit : ``}`, `<br />`, sensorNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sData.temperature !== undefined) {
|
||||||
|
const sUnit = temperatureUnit(unitSystem);
|
||||||
|
appendValue(".sValue", `${i18n('dashboard.sensors.values.temp')}: <b>${sData.temperature.toFixed(2)}</b> ${sUnit !== null ? sUnit : ``}`, `<br />`, sensorNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sData.humidity !== undefined) {
|
||||||
|
appendValue(".sValue", `${i18n('dashboard.sensors.values.humidity')}: <b>${sData.humidity.toFixed(2)}</b> %`, `<br />`, sensorNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sData.battery !== undefined) {
|
||||||
|
appendValue(".sValue", `${i18n('dashboard.sensors.values.battery')}: <b>${sData.battery.toFixed(2)}</b> %`, `<br />`, sensorNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sData.rssi !== undefined) {
|
||||||
|
appendValue(".sValue", `${i18n('dashboard.sensors.values.rssi')}: <b>${sData.rssi.toFixed(0)}</b> ${i18n('dbm')}`, `<br />`, sensorNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(onLoadPage, 10000);
|
setTimeout(onLoadPage, 10000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,19 +4,20 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title data-i18n>index.title</title>
|
<title data-i18n>index.title</title>
|
||||||
<link rel="stylesheet" href="/static/app.css" />
|
<link rel="stylesheet" href="/static/app.css?{BUILD_TIME}" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header class="container">
|
<header class="container">
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">
|
<li>
|
||||||
|
<a href="/">
|
||||||
<div class="logo" data-i18n>logo</div>
|
<div class="logo" data-i18n>logo</div>
|
||||||
</a></li>
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<!--<li><a href="https://github.com/Laxilef/OTGateway/wiki" role="button" class="secondary" target="_blank">Help</a></li>-->
|
|
||||||
<li>
|
<li>
|
||||||
<select id="lang" aria-label="Lang">
|
<select id="lang" aria-label="Lang">
|
||||||
<option value="en" selected>EN</option>
|
<option value="en" selected>EN</option>
|
||||||
@@ -48,7 +49,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>network.wifi.connected</th>
|
<th scope="row" data-i18n>network.wifi.connected</th>
|
||||||
<td><input type="radio" id="network-connected" aria-invalid="false" checked disabled /></td>
|
<td><i id="network-connected"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>network.wifi.ssid</th>
|
<th scope="row" data-i18n>network.wifi.ssid</th>
|
||||||
@@ -161,7 +162,7 @@
|
|||||||
</small>
|
</small>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/static/app.js"></script>
|
<script src="/static/app.js?{BUILD_TIME}"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const lang = new Lang(document.getElementById('lang'));
|
const lang = new Lang(document.getElementById('lang'));
|
||||||
@@ -169,7 +170,11 @@
|
|||||||
|
|
||||||
setTimeout(async function onLoadPage() {
|
setTimeout(async function onLoadPage() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/info', { cache: 'no-cache' });
|
const response = await fetch("/api/info", {
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Response not valid');
|
throw new Error('Response not valid');
|
||||||
}
|
}
|
||||||
@@ -184,7 +189,11 @@
|
|||||||
|
|
||||||
setValue('#network-hostname', result.network.hostname);
|
setValue('#network-hostname', result.network.hostname);
|
||||||
setValue('#network-mac', result.network.mac);
|
setValue('#network-mac', result.network.mac);
|
||||||
setState('#network-connected', result.network.connected);
|
setStatus(
|
||||||
|
'#network-connected',
|
||||||
|
result.network.connected ? "success" : "error",
|
||||||
|
result.network.connected ? "green" : "red"
|
||||||
|
);
|
||||||
setValue('#network-ssid', result.network.ssid);
|
setValue('#network-ssid', result.network.ssid);
|
||||||
setValue('#network-signal', result.network.signalQuality);
|
setValue('#network-signal', result.network.signalQuality);
|
||||||
setValue('#network-ip', result.network.ip);
|
setValue('#network-ip', result.network.ip);
|
||||||
|
|||||||
@@ -4,19 +4,20 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title data-i18n>network.title</title>
|
<title data-i18n>network.title</title>
|
||||||
<link rel="stylesheet" href="/static/app.css" />
|
<link rel="stylesheet" href="/static/app.css?{BUILD_TIME}" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header class="container">
|
<header class="container">
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">
|
<li>
|
||||||
|
<a href="/">
|
||||||
<div class="logo" data-i18n>logo</div>
|
<div class="logo" data-i18n>logo</div>
|
||||||
</a></li>
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<!--<li><a href="https://github.com/Laxilef/OTGateway/wiki" role="button" class="secondary" target="_blank">Help</a></li>-->
|
|
||||||
<li>
|
<li>
|
||||||
<select id="lang" aria-label="Lang">
|
<select id="lang" aria-label="Lang">
|
||||||
<option value="en" selected>EN</option>
|
<option value="en" selected>EN</option>
|
||||||
@@ -171,7 +172,7 @@
|
|||||||
</small>
|
</small>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/static/app.js"></script>
|
<script src="/static/app.js?{BUILD_TIME}"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const lang = new Lang(document.getElementById('lang'));
|
const lang = new Lang(document.getElementById('lang'));
|
||||||
@@ -198,7 +199,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/network/settings', { cache: 'no-cache' });
|
const response = await fetch("/api/network/settings", {
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Response not valid');
|
throw new Error('Response not valid');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,20 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title data-i18n>sensors.title</title>
|
<title data-i18n>sensors.title</title>
|
||||||
<link rel="stylesheet" href="/static/app.css" />
|
<link rel="stylesheet" href="/static/app.css?{BUILD_TIME}" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header class="container">
|
<header class="container">
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">
|
<li>
|
||||||
|
<a href="/">
|
||||||
<div class="logo" data-i18n>logo</div>
|
<div class="logo" data-i18n>logo</div>
|
||||||
</a></li>
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<!--<li><a href="https://github.com/Laxilef/OTGateway/wiki" role="button" class="secondary" target="_blank">Help</a></li>-->
|
|
||||||
<li>
|
<li>
|
||||||
<select id="lang" aria-label="Lang">
|
<select id="lang" aria-label="Lang">
|
||||||
<option value="en" selected>EN</option>
|
<option value="en" selected>EN</option>
|
||||||
@@ -67,7 +68,10 @@
|
|||||||
<option value="6" data-i18n>sensors.purposes.dhwFlowRate</option>
|
<option value="6" data-i18n>sensors.purposes.dhwFlowRate</option>
|
||||||
<option value="7" data-i18n>sensors.purposes.exhaustTemp</option>
|
<option value="7" data-i18n>sensors.purposes.exhaustTemp</option>
|
||||||
<option value="8" data-i18n>sensors.purposes.modLevel</option>
|
<option value="8" data-i18n>sensors.purposes.modLevel</option>
|
||||||
<option value="9" data-i18n>sensors.purposes.currentPower</option>
|
<option value="248" data-i18n>sensors.purposes.powerFactor</option>
|
||||||
|
<option value="249" data-i18n>sensors.purposes.power</option>
|
||||||
|
<option value="250" data-i18n>sensors.purposes.fanSpeed</option>
|
||||||
|
<option value="251" data-i18n>sensors.purposes.co2</option>
|
||||||
<option value="252" data-i18n>sensors.purposes.pressure</option>
|
<option value="252" data-i18n>sensors.purposes.pressure</option>
|
||||||
<option value="253" data-i18n>sensors.purposes.humidity</option>
|
<option value="253" data-i18n>sensors.purposes.humidity</option>
|
||||||
<option value="254" data-i18n>sensors.purposes.temperature</option>
|
<option value="254" data-i18n>sensors.purposes.temperature</option>
|
||||||
@@ -90,6 +94,14 @@
|
|||||||
<option value="9" data-i18n>sensors.types.otPressure</option>
|
<option value="9" data-i18n>sensors.types.otPressure</option>
|
||||||
<option value="10" data-i18n>sensors.types.otModLevel</option>
|
<option value="10" data-i18n>sensors.types.otModLevel</option>
|
||||||
<option value="11" data-i18n>sensors.types.otCurrentPower</option>
|
<option value="11" data-i18n>sensors.types.otCurrentPower</option>
|
||||||
|
<option value="12" data-i18n>sensors.types.otExhaustCo2</option>
|
||||||
|
<option value="13" data-i18n>sensors.types.otExhaustFanSpeed</option>
|
||||||
|
<option value="14" data-i18n>sensors.types.otSupplyFanSpeed</option>
|
||||||
|
<option value="15" data-i18n>sensors.types.otSolarStorageTemp</option>
|
||||||
|
<option value="16" data-i18n>sensors.types.otSolarCollectorTemp</option>
|
||||||
|
<option value="17" data-i18n>sensors.types.otFanSpeedSetpoint</option>
|
||||||
|
<option value="18" data-i18n>sensors.types.otFanSpeedCurrent</option>
|
||||||
|
|
||||||
<option value="50" data-i18n>sensors.types.ntcTemp</option>
|
<option value="50" data-i18n>sensors.types.ntcTemp</option>
|
||||||
<option value="51" data-i18n>sensors.types.dallasTemp</option>
|
<option value="51" data-i18n>sensors.types.dallasTemp</option>
|
||||||
<option value="52" data-i18n>sensors.types.bluetooth</option>
|
<option value="52" data-i18n>sensors.types.bluetooth</option>
|
||||||
@@ -114,10 +126,10 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr class="correction" />
|
||||||
|
|
||||||
<fieldset>
|
<details class="correction">
|
||||||
<legend><b data-i18n>sensors.correction.desc</b></legend>
|
<summary><b data-i18n>sensors.correction.desc</b></summary>
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<label>
|
<label>
|
||||||
@@ -130,27 +142,30 @@
|
|||||||
<input type="number" inputmode="numeric" name="factor" min="0.01" max="10" step="0.01" required>
|
<input type="number" inputmode="numeric" name="factor" min="0.01" max="10" step="0.01" required>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</details>
|
||||||
|
|
||||||
<hr />
|
<hr class="filtering" />
|
||||||
|
|
||||||
<fieldset>
|
<details class="filtering">
|
||||||
<legend><b data-i18n>sensors.filtering.desc</b></legend>
|
<summary><b data-i18n>sensors.filtering.desc</b></summary>
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="filtering" value="true">
|
|
||||||
<span data-i18n>sensors.filtering.enabled.title</span>
|
|
||||||
<br />
|
|
||||||
<small data-i18n>sensors.filtering.enabled.note</small>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<span data-i18n>sensors.filtering.factor.title</span>
|
|
||||||
<input type="number" inputmode="numeric" name="filteringFactor" min="0.01" max="1" step="0.01">
|
|
||||||
<small data-i18n>sensors.filtering.factor.note</small>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="filtering" value="true">
|
||||||
|
<span data-i18n>sensors.filtering.enabled.title</span>
|
||||||
|
<br />
|
||||||
|
<small data-i18n>sensors.filtering.enabled.note</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.filtering.factor.title</span>
|
||||||
|
<input type="number" inputmode="numeric" name="filteringFactor" min="0.01" max="1" step="0.01">
|
||||||
|
<small data-i18n>sensors.filtering.factor.note</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<br/>
|
||||||
<button type="submit" data-i18n>button.save</button>
|
<button type="submit" data-i18n>button.save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,7 +184,7 @@
|
|||||||
</small>
|
</small>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/static/app.js"></script>
|
<script src="/static/app.js?{BUILD_TIME}"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
const lang = new Lang(document.getElementById("lang"));
|
const lang = new Lang(document.getElementById("lang"));
|
||||||
@@ -179,7 +194,11 @@
|
|||||||
const templateNode = container.querySelector("#template");
|
const templateNode = container.querySelector("#template");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/sensors", { cache: "no-cache" });
|
const response = await fetch("/api/sensors", {
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Response not valid");
|
throw new Error("Response not valid");
|
||||||
}
|
}
|
||||||
@@ -221,6 +240,20 @@
|
|||||||
const parentGpio = gpio.parentElement;
|
const parentGpio = gpio.parentElement;
|
||||||
const parentAddress = address.parentElement;
|
const parentAddress = address.parentElement;
|
||||||
|
|
||||||
|
switch(parseInt(event.target.value)) {
|
||||||
|
// heating setpoint, manual
|
||||||
|
case 253:
|
||||||
|
case 254:
|
||||||
|
hide(".correction", sensorForm);
|
||||||
|
hide(".filtering", sensorForm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// other
|
||||||
|
default:
|
||||||
|
show(".correction", sensorForm);
|
||||||
|
show(".filtering", sensorForm);
|
||||||
|
}
|
||||||
|
|
||||||
switch(parseInt(event.target.value)) {
|
switch(parseInt(event.target.value)) {
|
||||||
// ntc
|
// ntc
|
||||||
case 50:
|
case 50:
|
||||||
@@ -251,13 +284,18 @@
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
sensorNode.addEventListener("click", async (event) => {
|
sensorNode.addEventListener("click", async (event) => {
|
||||||
if (parseInt(sensorNode.dataset.preloaded)) {
|
if (parseInt(sensorNode.dataset.preloaded)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(sensorForm.action, { cache: "no-cache" });
|
const response = await fetch(sensorForm.action, {
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
if (response.status != 200) {
|
if (response.status != 200) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -271,7 +309,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setupForm(".sensor[data-id='" + sensorId + "'] form", fillData, ['address']);
|
setupForm(`.sensor[data-id='${sensorId}'] form`, fillData, ['address']);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -4,19 +4,20 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title data-i18n>settings.title</title>
|
<title data-i18n>settings.title</title>
|
||||||
<link rel="stylesheet" href="/static/app.css" />
|
<link rel="stylesheet" href="/static/app.css?{BUILD_TIME}" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header class="container">
|
<header class="container">
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">
|
<li>
|
||||||
|
<a href="/">
|
||||||
<div class="logo" data-i18n>logo</div>
|
<div class="logo" data-i18n>logo</div>
|
||||||
</a></li>
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<!--<li><a href="https://github.com/Laxilef/OTGateway/wiki" role="button" class="secondary" target="_blank">Help</a></li>-->
|
|
||||||
<li>
|
<li>
|
||||||
<select id="lang" aria-label="Lang">
|
<select id="lang" aria-label="Lang">
|
||||||
<option value="en" selected>EN</option>
|
<option value="en" selected>EN</option>
|
||||||
@@ -95,12 +96,12 @@
|
|||||||
<legend data-i18n>settings.section.diag</legend>
|
<legend data-i18n>settings.section.diag</legend>
|
||||||
|
|
||||||
<label for="system-serial-enable">
|
<label for="system-serial-enable">
|
||||||
<input type="checkbox" id="system-serial-enable" name="system[serial][enable]" value="true">
|
<input type="checkbox" id="system-serial-enable" name="system[serial][enabled]" value="true">
|
||||||
<span data-i18n>settings.system.serial.enable</span>
|
<span data-i18n>settings.system.serial.enable</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label for="system-telnet-enable">
|
<label for="system-telnet-enable">
|
||||||
<input type="checkbox" id="system-telnet-enable" name="system[telnet][enable]" value="true">
|
<input type="checkbox" id="system-telnet-enable" name="system[telnet][enabled]" value="true">
|
||||||
<span data-i18n>settings.system.telnet.enable</span>
|
<span data-i18n>settings.system.telnet.enable</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -245,7 +246,7 @@
|
|||||||
<form action="/api/settings" id="equitherm-settings" class="hidden">
|
<form action="/api/settings" id="equitherm-settings" class="hidden">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="equitherm-enable">
|
<label for="equitherm-enable">
|
||||||
<input type="checkbox" id="equitherm-enable" name="equitherm[enable]" value="true">
|
<input type="checkbox" id="equitherm-enable" name="equitherm[enabled]" value="true">
|
||||||
<span data-i18n>settings.enable</span>
|
<span data-i18n>settings.enable</span>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -282,7 +283,7 @@
|
|||||||
<form action="/api/settings" id="pid-settings" class="hidden">
|
<form action="/api/settings" id="pid-settings" class="hidden">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="pid-enable">
|
<label for="pid-enable">
|
||||||
<input type="checkbox" id="pid-enable" name="pid[enable]" value="true">
|
<input type="checkbox" id="pid-enable" name="pid[enabled]" value="true">
|
||||||
<span data-i18n>settings.enable</span>
|
<span data-i18n>settings.enable</span>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -470,7 +471,7 @@
|
|||||||
<form action="/api/settings" id="mqtt-settings" class="hidden">
|
<form action="/api/settings" id="mqtt-settings" class="hidden">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="mqtt-enable">
|
<label for="mqtt-enable">
|
||||||
<input type="checkbox" id="mqtt-enable" name="mqtt[enable]" value="true">
|
<input type="checkbox" id="mqtt-enable" name="mqtt[enabled]" value="true">
|
||||||
<span data-i18n>settings.enable</span>
|
<span data-i18n>settings.enable</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -573,7 +574,7 @@
|
|||||||
<form action="/api/settings" id="cc-settings" class="hidden">
|
<form action="/api/settings" id="cc-settings" class="hidden">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="cc-input-enable">
|
<label for="cc-input-enable">
|
||||||
<input type="checkbox" id="cc-input-enable" name="cascadeControl[input][enable]" value="true">
|
<input type="checkbox" id="cc-input-enable" name="cascadeControl[input][enabled]" value="true">
|
||||||
<span data-i18n>settings.cascadeControl.input.enable</span>
|
<span data-i18n>settings.cascadeControl.input.enable</span>
|
||||||
<br />
|
<br />
|
||||||
<small data-i18n>settings.cascadeControl.input.desc</small>
|
<small data-i18n>settings.cascadeControl.input.desc</small>
|
||||||
@@ -601,7 +602,7 @@
|
|||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="cc-output-enable">
|
<label for="cc-output-enable">
|
||||||
<input type="checkbox" id="cc-output-enable" name="cascadeControl[output][enable]" value="true">
|
<input type="checkbox" id="cc-output-enable" name="cascadeControl[output][enabled]" value="true">
|
||||||
<span data-i18n>settings.cascadeControl.output.enable</span>
|
<span data-i18n>settings.cascadeControl.output.enable</span>
|
||||||
<br />
|
<br />
|
||||||
<small data-i18n>settings.cascadeControl.output.desc</small>
|
<small data-i18n>settings.cascadeControl.output.desc</small>
|
||||||
@@ -662,7 +663,7 @@
|
|||||||
</small>
|
</small>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/static/app.js"></script>
|
<script src="/static/app.js?{BUILD_TIME}"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const lang = new Lang(document.getElementById('lang'));
|
const lang = new Lang(document.getElementById('lang'));
|
||||||
@@ -807,7 +808,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/settings', { cache: 'no-cache' });
|
const response = await fetch("/api/settings", {
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Response not valid');
|
throw new Error('Response not valid');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,20 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title data-i18n>upgrade.title</title>
|
<title data-i18n>upgrade.title</title>
|
||||||
<link rel="stylesheet" href="/static/app.css">
|
<link rel="stylesheet" href="/static/app.css?{BUILD_TIME}">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header class="container">
|
<header class="container">
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">
|
<li>
|
||||||
|
<a href="/">
|
||||||
<div class="logo" data-i18n>logo</div>
|
<div class="logo" data-i18n>logo</div>
|
||||||
</a></li>
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<!--<li><a href="https://github.com/Laxilef/OTGateway/wiki" role="button" class="secondary" target="_blank">Help</a></li>-->
|
|
||||||
<li>
|
<li>
|
||||||
<select id="lang" aria-label="Lang">
|
<select id="lang" aria-label="Lang">
|
||||||
<option value="en" selected>EN</option>
|
<option value="en" selected>EN</option>
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
<input type="file" name="settings" id="restore-file" accept=".json">
|
<input type="file" name="settings" id="restore-file" accept=".json">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="grid">
|
<div role="group">
|
||||||
<button type="submit" data-i18n>button.restore</button>
|
<button type="submit" data-i18n>button.restore</button>
|
||||||
<button type="button" class="secondary" onclick="window.location='/api/backup/save';" data-i18n>button.backup</button>
|
<button type="button" class="secondary" onclick="window.location='/api/backup/save';" data-i18n>button.backup</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,7 +98,7 @@
|
|||||||
</small>
|
</small>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/static/app.js"></script>
|
<script src="/static/app.js?{BUILD_TIME}"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const lang = new Lang(document.getElementById('lang'));
|
const lang = new Lang(document.getElementById('lang'));
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class Lang {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.localeIsSupported(this.defaultLocale)) {
|
if (!this.localeIsSupported(this.defaultLocale)) {
|
||||||
const selected = this.switcher.selectedIndex ?? 0;
|
const selected = this.switcher.selectedIndex ? this.switcher.selectedIndex : 0;
|
||||||
this.defaultLocale = this.switcher.options[selected].value;
|
this.defaultLocale = this.switcher.options[selected].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ class Lang {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchTranslations(locale) {
|
async fetchTranslations(locale) {
|
||||||
const response = await fetch(`/static/locales/${locale}.json`);
|
const response = await fetch(`/static/locales/${locale}.json?{BUILD_TIME}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.values instanceof Object) {
|
if (data.values instanceof Object) {
|
||||||
|
|||||||
@@ -60,10 +60,11 @@ const setupForm = (formSelector, onResultCallback = null, noCastItems = []) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let response = await fetch(url, {
|
let response = await fetch(url, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
cache: 'no-cache',
|
cache: "no-cache",
|
||||||
|
credentials: "include",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: form2json(fd, noCastItems)
|
body: form2json(fd, noCastItems)
|
||||||
});
|
});
|
||||||
@@ -143,8 +144,8 @@ const setupNetworkScanForm = (formSelector, tableSelector) => {
|
|||||||
input.focus();
|
input.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
row.insertCell().textContent = "#" + (i + 1);
|
row.insertCell().textContent = `#${i + 1}`;
|
||||||
row.insertCell().innerHTML = result[i].hidden ? ("<i>" + result[i].bssid + "</i>") : result[i].ssid;
|
row.insertCell().innerHTML = result[i].hidden ? `<i>${result[i].bssid}</i>` : result[i].ssid;
|
||||||
|
|
||||||
// info cell
|
// info cell
|
||||||
let infoCell = row.insertCell();
|
let infoCell = row.insertCell();
|
||||||
@@ -164,7 +165,7 @@ const setupNetworkScanForm = (formSelector, tableSelector) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let signalQualityContainer = document.createElement("span");
|
let signalQualityContainer = document.createElement("span");
|
||||||
signalQualityContainer.setAttribute('data-tooltip', result[i].signalQuality + "%");
|
signalQualityContainer.setAttribute('data-tooltip', `${result[i].signalQuality}%`);
|
||||||
signalQualityContainer.appendChild(signalQualityIcon);
|
signalQualityContainer.appendChild(signalQualityIcon);
|
||||||
infoCell.appendChild(signalQualityContainer);
|
infoCell.appendChild(signalQualityContainer);
|
||||||
|
|
||||||
@@ -218,7 +219,10 @@ const setupNetworkScanForm = (formSelector, tableSelector) => {
|
|||||||
attempts--;
|
attempts--;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let response = await fetch(url, { cache: 'no-cache' });
|
let response = await fetch(url, {
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
await onSuccess(response);
|
await onSuccess(response);
|
||||||
@@ -306,31 +310,16 @@ const setupRestoreBackupForm = (formSelector) => {
|
|||||||
try {
|
try {
|
||||||
const data = JSON.parse(event.target.result);
|
const data = JSON.parse(event.target.result);
|
||||||
console.log("Backup: ", data);
|
console.log("Backup: ", data);
|
||||||
|
|
||||||
if (data.network != undefined) {
|
|
||||||
let response = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
cache: 'no-cache',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data.network)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
onFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.settings != undefined) {
|
if (data.settings != undefined) {
|
||||||
let response = await fetch(url, {
|
let response = await fetch(url, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
cache: 'no-cache',
|
cache: "no-cache",
|
||||||
|
credentials: "include",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data.settings)
|
body: JSON.stringify({"settings": data.settings})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -347,10 +336,11 @@ const setupRestoreBackupForm = (formSelector) => {
|
|||||||
payload["sensors"][sensorId] = data.sensors[sensorId];
|
payload["sensors"][sensorId] = data.sensors[sensorId];
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
cache: 'no-cache',
|
cache: "no-cache",
|
||||||
|
credentials: "include",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload)
|
||||||
});
|
});
|
||||||
@@ -362,6 +352,23 @@ const setupRestoreBackupForm = (formSelector) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.network != undefined) {
|
||||||
|
let response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({"network": data.network})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
onFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onSuccess();
|
onSuccess();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -427,7 +434,7 @@ const setupUpgradeForm = (formSelector) => {
|
|||||||
resItem.classList.add('failed');
|
resItem.classList.add('failed');
|
||||||
|
|
||||||
if (result.firmware.error != "") {
|
if (result.firmware.error != "") {
|
||||||
resItem.textContent += ": " + result.firmware.error;
|
resItem.textContent += `: ${result.firmware.error}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,7 +452,7 @@ const setupUpgradeForm = (formSelector) => {
|
|||||||
resItem.classList.add('failed');
|
resItem.classList.add('failed');
|
||||||
|
|
||||||
if (result.filesystem.error != "") {
|
if (result.filesystem.error != "") {
|
||||||
resItem.textContent += ": " + result.filesystem.error;
|
resItem.textContent += `: ${result.filesystem.error}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -496,8 +503,9 @@ const setupUpgradeForm = (formSelector) => {
|
|||||||
try {
|
try {
|
||||||
let fd = new FormData(form);
|
let fd = new FormData(form);
|
||||||
let response = await fetch(url, {
|
let response = await fetch(url, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
cache: 'no-cache',
|
cache: "no-cache",
|
||||||
|
credentials: "include",
|
||||||
body: fd
|
body: fd
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -526,8 +534,8 @@ const setBusy = (busySelector, contentSelector, value, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setState = (selector, value, parent = undefined) => {
|
const setAriaState = (selector, value, parent = undefined) => {
|
||||||
if (parent == undefined) {
|
if (parent === undefined) {
|
||||||
parent = document;
|
parent = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,8 +547,40 @@ const setState = (selector, value, parent = undefined) => {
|
|||||||
item.setAttribute('aria-invalid', !value);
|
item.setAttribute('aria-invalid', !value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setStatus = (selector, state, color = undefined, parent = undefined) => {
|
||||||
|
if (parent === undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = parent.querySelector(selector);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.classList.forEach(cName => {
|
||||||
|
if (cName.indexOf("icons-") === 0) {
|
||||||
|
item.classList.remove(cName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item.classList.add(`icons-${state}`);
|
||||||
|
|
||||||
|
if (color !== undefined) {
|
||||||
|
item.classList.add(`icons-color-${color}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setState = (selector, state, parent = undefined) => {
|
||||||
|
return setStatus(
|
||||||
|
selector,
|
||||||
|
state ? "success" : "error",
|
||||||
|
state ? "green" : "gray",
|
||||||
|
parent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const setValue = (selector, value, parent = undefined) => {
|
const setValue = (selector, value, parent = undefined) => {
|
||||||
if (parent == undefined) {
|
if (parent === undefined) {
|
||||||
parent = document;
|
parent = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,8 +594,32 @@ const setValue = (selector, value, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const appendValue = (selector, value, nl = null, parent = undefined) => {
|
||||||
|
if (parent === undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = parent.querySelectorAll(selector);
|
||||||
|
if (!items.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let item of items) {
|
||||||
|
if (item.innerHTML.trim().length > 0) {
|
||||||
|
if (nl !== null) {
|
||||||
|
item.innerHTML += nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.innerHTML += value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
item.innerHTML = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const setCheckboxValue = (selector, value, parent = undefined) => {
|
const setCheckboxValue = (selector, value, parent = undefined) => {
|
||||||
if (parent == undefined) {
|
if (parent === undefined) {
|
||||||
parent = document;
|
parent = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,7 +632,7 @@ const setCheckboxValue = (selector, value, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setRadioValue = (selector, value, parent = undefined) => {
|
const setRadioValue = (selector, value, parent = undefined) => {
|
||||||
if (parent == undefined) {
|
if (parent === undefined) {
|
||||||
parent = document;
|
parent = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,7 +647,7 @@ const setRadioValue = (selector, value, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setInputValue = (selector, value, attrs = {}, parent = undefined) => {
|
const setInputValue = (selector, value, attrs = {}, parent = undefined) => {
|
||||||
if (parent == undefined) {
|
if (parent === undefined) {
|
||||||
parent = document;
|
parent = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,7 +668,7 @@ const setInputValue = (selector, value, attrs = {}, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setSelectValue = (selector, value, parent = undefined) => {
|
const setSelectValue = (selector, value, parent = undefined) => {
|
||||||
if (parent == undefined) {
|
if (parent === undefined) {
|
||||||
parent = document;
|
parent = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,7 +683,7 @@ const setSelectValue = (selector, value, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const show = (selector, parent = undefined) => {
|
const show = (selector, parent = undefined) => {
|
||||||
if (parent == undefined) {
|
if (parent === undefined) {
|
||||||
parent = document;
|
parent = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,7 +700,7 @@ const show = (selector, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hide = (selector, parent = undefined) => {
|
const hide = (selector, parent = undefined) => {
|
||||||
if (parent == undefined) {
|
if (parent === undefined) {
|
||||||
parent = document;
|
parent = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,6 +743,29 @@ const volumeUnit = (unitSystem) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const purposeUnit = (purpose, unitSystem) => {
|
||||||
|
const tUnit = temperatureUnit(unitSystem);
|
||||||
|
|
||||||
|
return unit2str(purpose, {
|
||||||
|
0: tUnit,
|
||||||
|
1: tUnit,
|
||||||
|
2: tUnit,
|
||||||
|
3: tUnit,
|
||||||
|
4: tUnit,
|
||||||
|
5: tUnit,
|
||||||
|
6: `${volumeUnit(unitSystem)}/${i18n('time.min')}`,
|
||||||
|
7: tUnit,
|
||||||
|
8: "%",
|
||||||
|
248: "%",
|
||||||
|
249: i18n('kw'),
|
||||||
|
250: "RPM",
|
||||||
|
251: "ppm",
|
||||||
|
252: pressureUnit(unitSystem),
|
||||||
|
253: "%",
|
||||||
|
254: tUnit
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
const memberIdToVendor = (memberId) => {
|
const memberIdToVendor = (memberId) => {
|
||||||
// https://github.com/Jeroen88/EasyOpenTherm/blob/main/src/EasyOpenTherm.h
|
// https://github.com/Jeroen88/EasyOpenTherm/blob/main/src/EasyOpenTherm.h
|
||||||
// https://github.com/Evgen2/SmartTherm/blob/v0.7/src/Web.cpp
|
// https://github.com/Evgen2/SmartTherm/blob/v0.7/src/Web.cpp
|
||||||
@@ -757,7 +844,7 @@ function form2json(data, noCastItems = []) {
|
|||||||
function dec2hex(i) {
|
function dec2hex(i) {
|
||||||
let hex = parseInt(i).toString(16);
|
let hex = parseInt(i).toString(16);
|
||||||
if (hex.length % 2 != 0) {
|
if (hex.length % 2 != 0) {
|
||||||
hex = "0" + hex;
|
hex = `0${hex}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hex.toUpperCase();
|
return hex.toUpperCase();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ footer {
|
|||||||
/*nav li a:has(> div.logo) {
|
/*nav li a:has(> div.logo) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}*/
|
}*/
|
||||||
nav li :where(a,[role=link]) {
|
nav li :where(a, [role=link]) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +71,11 @@ pre {
|
|||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:nth-last-child(1 of table tr:not(.hidden)) th,
|
||||||
|
:nth-last-child(1 of table tr:not(.hidden)) td {
|
||||||
|
border-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
@@ -200,4 +205,20 @@ tr.network:hover {
|
|||||||
|
|
||||||
[data-tooltip]:has(> [class*=" icons-"], > [class=icons], > [class^=icons-]) {
|
[data-tooltip]:has(> [class*=" icons-"], > [class=icons], > [class^=icons-]) {
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icons-color-green {
|
||||||
|
color: var(--pico-form-element-valid-active-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icons-color-yellow {
|
||||||
|
color: #c89048;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icons-color-gray {
|
||||||
|
color: var(--pico-form-element-placeholder-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icons-color-red {
|
||||||
|
color: var(--pico-form-element-invalid-active-border-color);
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
font-family: "Icons";
|
font-family: "Icons";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url("/static/fonts/iconly.eot?1718563596894");
|
src: url("/static/fonts/iconly.eot?1732913937966");
|
||||||
src: url("/static/fonts/iconly.eot?#iefix") format("embedded-opentype"), url("/static/fonts/iconly.woff2?1718563596894") format("woff2"), url("/static/fonts/iconly.woff?1718563596894") format("woff"), url("/static/fonts/iconly.ttf?1718563596894") format("truetype"), url("/static/fonts/iconly.svg?1718563596894#Icons") format("svg");
|
src: url("/static/fonts/iconly.eot?#iefix") format("embedded-opentype"), url("/static/fonts/iconly.woff2?1732913937966") format("woff2"), url("/static/fonts/iconly.woff?1732913937966") format("woff"), url("/static/fonts/iconly.ttf?1732913937966") format("truetype"), url("/static/fonts/iconly.svg?1732913937966#Icons") format("svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
[class="icons"], [class^="icons-"], [class*=" icons-"] {
|
[class="icons"], [class^="icons-"], [class*=" icons-"] {
|
||||||
@@ -51,6 +51,10 @@
|
|||||||
content: "\e006";
|
content: "\e006";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icons-error:before {
|
||||||
|
content: "\e007";
|
||||||
|
}
|
||||||
|
|
||||||
.icons-wifi-strength-3:before {
|
.icons-wifi-strength-3:before {
|
||||||
content: "\e008";
|
content: "\e008";
|
||||||
}
|
}
|
||||||
@@ -63,6 +67,14 @@
|
|||||||
content: "\e00a";
|
content: "\e00a";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icons-success:before {
|
||||||
|
content: "\e00b";
|
||||||
|
}
|
||||||
|
|
||||||
.icons-up:before {
|
.icons-up:before {
|
||||||
content: "\e00c";
|
content: "\e00c";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icons-alarm:before {
|
||||||
|
content: "\e00d";
|
||||||
|
}
|
||||||
|
|||||||