mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-10 07:55:53 +00:00
Compare commits
243 Commits
v3.8.1
...
c40d828749
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c40d828749 | ||
|
|
84ad08887a | ||
|
|
37107d8500 | ||
|
|
dde6a8c5db | ||
|
|
e2750b8572 | ||
|
|
acd23925b5 | ||
|
|
b0db054e11 | ||
|
|
51cea8e757 | ||
|
|
bbb086ea41 | ||
|
|
539e6ed080 | ||
|
|
1d33a26318 | ||
|
|
86a20fc97a | ||
|
|
d6e00c4534 | ||
|
|
a813d38108 | ||
|
|
685a49c212 | ||
|
|
994706c9f2 | ||
|
|
2c8eb534af | ||
|
|
5210fab4cb | ||
|
|
49787d27f1 | ||
|
|
dfe7b46461 | ||
|
|
8a72ab42cb | ||
|
|
c4db8e3914 | ||
|
|
8d0225e595 | ||
|
|
f8257de0dd | ||
|
|
3b3ecc9f1d | ||
|
|
966049d0c9 | ||
|
|
907a65a701 | ||
|
|
f97b8e14e7 | ||
|
|
e65f634b21 | ||
|
|
fc71ed2b9d | ||
|
|
84105acf5d | ||
|
|
def5173692 | ||
|
|
6b31fef1af | ||
|
|
c9c059ca65 | ||
|
|
4d3b31e5a1 | ||
|
|
5a8195d430 | ||
|
|
24a7a607f3 | ||
|
|
061f9ffc52 | ||
|
|
9e17936bfc | ||
|
|
18bb2c4f39 | ||
|
|
7c3782a43f | ||
|
|
3ac807bdd5 | ||
|
|
1111458863 | ||
|
|
99c5e2230c | ||
|
|
3317aa845a | ||
|
|
97cd657336 | ||
|
|
3338f919bd | ||
|
|
7dd13bcab7 | ||
|
|
f226cb359f | ||
|
|
abbba0aa42 | ||
|
|
39b5a52b01 | ||
|
|
b6c3fc5bee | ||
|
|
909bea00df | ||
|
|
9522945e06 | ||
|
|
d6a9f2a731 | ||
|
|
0f30c81554 | ||
|
|
e514ba4bb5 | ||
|
|
38e63e3eaa | ||
|
|
0058324edd | ||
|
|
ac143d607a | ||
|
|
e9e3759db3 | ||
|
|
51d90095aa | ||
|
|
fb09e10f19 | ||
|
|
16c0370443 | ||
|
|
67bb38dcf4 | ||
|
|
049231a36e | ||
|
|
349d6b7375 | ||
|
|
b72b368d3c | ||
|
|
7f9fd44a02 | ||
|
|
a400c5974c | ||
|
|
afca995fe5 | ||
|
|
81504fedc5 | ||
|
|
3da3345683 | ||
|
|
c6c2889306 | ||
|
|
b60f0d260a | ||
|
|
cd750e4777 | ||
|
|
4e5d503b35 | ||
|
|
bd09e17e49 | ||
|
|
835eb743bb | ||
|
|
69a129d80e | ||
|
|
434bf483fd | ||
|
|
2b8e170b40 | ||
|
|
dc9b95f3e7 | ||
|
|
1616b0da0a | ||
|
|
91c457b22b | ||
|
|
70c60647c7 | ||
|
|
c0bea66d27 | ||
|
|
ed7cc078ed | ||
|
|
60b7d6d795 | ||
|
|
947f29cca0 | ||
|
|
d2a13ec0da | ||
|
|
cc39ba409e | ||
|
|
09473f17a0 | ||
|
|
ac9db6256e | ||
|
|
096f628d97 | ||
|
|
bbc2de08a5 | ||
|
|
22312812bb | ||
|
|
df808a2bcf | ||
|
|
d04e7c36f3 | ||
|
|
205d826adb | ||
|
|
3584975acb | ||
|
|
30b9ca4e6c | ||
|
|
7c6ff01ebe | ||
|
|
a54edcaf5b | ||
|
|
e446954844 | ||
|
|
4a2d0d6787 | ||
|
|
9725314135 | ||
|
|
4db8e43648 | ||
|
|
e610f0d57f | ||
|
|
8244af2940 | ||
|
|
cc60062678 | ||
|
|
40f371d23b | ||
|
|
817b791e59 | ||
|
|
25a7aac360 | ||
|
|
37115a174d | ||
|
|
1397f81fd0 | ||
|
|
56365cb403 | ||
|
|
dfd245ee7b | ||
|
|
9c81e4b34d | ||
|
|
67676df131 | ||
|
|
a73b129596 | ||
|
|
4600d886b5 | ||
|
|
e3305ab9db | ||
|
|
0fe45a2405 | ||
|
|
db87213242 | ||
|
|
b0157f288e | ||
|
|
5c3c010d5a | ||
|
|
c804cedd7a | ||
|
|
a9f50d9371 | ||
|
|
65a3226404 | ||
|
|
45690f5418 | ||
|
|
aa30ca99bf | ||
|
|
6836b6197f | ||
|
|
c0ca9d1069 | ||
|
|
5e79e1d57f | ||
|
|
8c732f9f1e | ||
|
|
5e94c2f636 | ||
|
|
69d4163b9d | ||
|
|
b1e974a82c | ||
|
|
34a2b20be8 | ||
|
|
f1fc8d9aae | ||
|
|
b04355e3e1 | ||
|
|
cd3ae5cdf2 | ||
|
|
a261ca23af | ||
|
|
cb96904a5c | ||
|
|
4a2d78f8e1 | ||
|
|
f5af4fb52f | ||
|
|
2037bc3a10 | ||
|
|
64d17d7c65 | ||
|
|
92e2633342 | ||
|
|
96a7ea8a02 | ||
|
|
5c4aaa4510 | ||
|
|
c05e1cb77b | ||
|
|
5879ce4090 | ||
|
|
ac3e5c793c | ||
|
|
64e5d29996 | ||
|
|
b320d8ded2 | ||
|
|
4326fb931b | ||
|
|
ced7051ce7 | ||
|
|
0be1b20996 | ||
|
|
6c55460622 | ||
|
|
421da246ed | ||
|
|
d627404dc2 | ||
|
|
148a721e17 | ||
|
|
f317123c26 | ||
|
|
e4df1887b0 | ||
|
|
34142c3e85 | ||
|
|
6e7f8bdf02 | ||
|
|
3dd9fcfb58 | ||
|
|
35e2954b8b | ||
|
|
59aa63db0f | ||
|
|
7a41a190f8 | ||
|
|
6741232450 | ||
|
|
a811670c5a | ||
|
|
72f08a86cf | ||
|
|
27c471f45f | ||
|
|
e303972d26 | ||
|
|
97bb03d703 | ||
|
|
e9f77c1bde | ||
|
|
81cba6c0a8 | ||
|
|
89029df25e | ||
|
|
3463b6818d | ||
|
|
349843e666 | ||
|
|
96ae3bbbba | ||
|
|
b153364b60 | ||
|
|
ccc40937fb | ||
|
|
909edf394d | ||
|
|
ac8ef646e9 | ||
|
|
3ef279ea10 | ||
|
|
769beeda37 | ||
|
|
f83404c216 | ||
|
|
c239658131 | ||
|
|
be82afd778 | ||
|
|
2156c96368 | ||
|
|
f7f078d82a | ||
|
|
95168cf514 | ||
|
|
6dc601c4a2 | ||
|
|
6b87bbb882 | ||
|
|
abdf2c5037 | ||
|
|
7beec1b80f | ||
|
|
3a0e46f064 | ||
|
|
85cc85a923 | ||
|
|
ca0079c0df | ||
|
|
28b662ad43 | ||
|
|
6bac6bbfeb | ||
|
|
958ec1002b | ||
|
|
438852ecaf | ||
|
|
92d82c0a00 | ||
|
|
7d37267f57 | ||
|
|
5641d53cc3 | ||
|
|
8fc6752290 | ||
|
|
cca6f87500 | ||
|
|
758d76051f | ||
|
|
95f7e66cff | ||
|
|
4e194287c9 | ||
|
|
3545830552 | ||
|
|
074f4c32ed | ||
|
|
b3fec5ed7d | ||
|
|
ffb90b8f9a | ||
|
|
584618043d | ||
|
|
d702c485b7 | ||
|
|
d3561da331 | ||
|
|
0e0aaf37df | ||
|
|
5ec068409f | ||
|
|
8796b6d340 | ||
|
|
bfbb18655d | ||
|
|
9088651e53 | ||
|
|
3e8f379502 | ||
|
|
265c2c4231 | ||
|
|
f671d79280 | ||
|
|
97c89d1d13 | ||
|
|
e0a26a38fa | ||
|
|
038f06e59f | ||
|
|
f4d2bae04f | ||
|
|
d443e275ea | ||
|
|
30d2057e01 | ||
|
|
d952b9aaae | ||
|
|
3402215e8d | ||
|
|
f01031dc26 | ||
|
|
01d4d116b9 | ||
|
|
bc7f82eef1 | ||
|
|
efdb355033 | ||
|
|
87c9fd010f |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -22,7 +22,7 @@ _Make sure your have performed every step and checked the applicable boxes befor
|
|||||||
- [ ] Searched the issue in [issues](https://github.com/emsesp/EMS-ESP32/issues)
|
- [ ] Searched the issue in [issues](https://github.com/emsesp/EMS-ESP32/issues)
|
||||||
- [ ] Searched the issue in [discussions](https://github.com/emsesp/EMS-ESP32/discussions)
|
- [ ] Searched the issue in [discussions](https://github.com/emsesp/EMS-ESP32/discussions)
|
||||||
- [ ] Searched the issue in the [docs](https://emsesp.org/Troubleshooting/)
|
- [ ] Searched the issue in the [docs](https://emsesp.org/Troubleshooting/)
|
||||||
- [ ] Searched the issue in the [chat](https://discord.gg/3J3GgnzpyT)
|
- [ ] Searched the issue in the [chat](https://discord.gg/GP9DPSgeJq)
|
||||||
- [ ] Provide the System information in the area below, taken from `http://<IP>/api/system`
|
- [ ] Provide the System information in the area below, taken from `http://<IP>/api/system`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -7,5 +7,5 @@ contact_links:
|
|||||||
url: https://github.com/emsesp/EMS-ESP32/discussions
|
url: https://github.com/emsesp/EMS-ESP32/discussions
|
||||||
about: EMS-ESP usage Questions, Feature Requests and Projects.
|
about: EMS-ESP usage Questions, Feature Requests and Projects.
|
||||||
- name: EMS-ESP Users Chat
|
- name: EMS-ESP Users Chat
|
||||||
url: https://discord.gg/3J3GgnzpyT
|
url: https://discord.gg/GP9DPSgeJq
|
||||||
about: Chat for feedback, questions and troubleshooting.
|
about: Chat for feedback, questions and troubleshooting.
|
||||||
|
|||||||
2
.github/workflows/dev_release.yml
vendored
2
.github/workflows/dev_release.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build webUI
|
- name: Build webUI
|
||||||
run: |
|
run: |
|
||||||
platformio run -e build_webUI
|
platformio run -e build-webUI
|
||||||
|
|
||||||
- name: Build modbus
|
- name: Build modbus
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/stable_release.yml
vendored
2
.github/workflows/stable_release.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build webUI
|
- name: Build webUI
|
||||||
run: |
|
run: |
|
||||||
platformio run -e build_webUI
|
platformio run -e build-webUI
|
||||||
|
|
||||||
- name: Build modbus
|
- name: Build modbus
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/test_release.yml
vendored
2
.github/workflows/test_release.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build webUI
|
- name: Build webUI
|
||||||
run: |
|
run: |
|
||||||
platformio run -e build_webUI
|
platformio run -e build-webUI
|
||||||
|
|
||||||
- name: Build modbus
|
- name: Build modbus
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
29
CHANGELOG.md
29
CHANGELOG.md
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [3.8.1] 11 January 2026
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- update time saved in nvs
|
||||||
|
- heatpump entities [#2883](https://github.com/emsesp/EMS-ESP32/issues/2883)
|
||||||
|
- HA input number format (mode) selectable box/slider (slider for max range 100) [#2900](https://github.com/emsesp/EMS-ESP32/discussions/2900)
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- fix EMS bus disconnected errors on some systems [#2881](https://github.com/emsesp/EMS-ESP32/issues/2881)
|
||||||
|
- selflowtemp fix [#2876](https://github.com/emsesp/EMS-ESP32/issues/2876)
|
||||||
|
- updated valid GPIOs for ESP32S2, ESP32S3 and ESP32 that caused custom systems to block gpios [#2887](https://github.com/emsesp/EMS-ESP32/issues/2887)
|
||||||
|
- Junkers wwcharge offset [#2860](https://github.com/emsesp/EMS-ESP32/issues/2860)
|
||||||
|
- fixed minflowtemp [#2890](https://github.com/emsesp/EMS-ESP32/issues/2890)
|
||||||
|
- don't add HA uom/classes for bool values [#2885](https://github.com/emsesp/EMS-ESP32/issues/2885)
|
||||||
|
- fixed missing progress bar on web firmware uploads
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- snapshot gpios stored in temporary ram
|
||||||
|
- GPIOs stored along with the name and reported in log if conflicting
|
||||||
|
- free GPIOs depend on board profile [#2901](https://github.com/emsesp/EMS-ESP32/issues/2901)
|
||||||
|
- prefer PSram for mqtt queue [#2889](https://github.com/emsesp/EMS-ESP32/issues/2889)
|
||||||
|
- day schedule defult to all days, no day selected is not allowed
|
||||||
|
- board profile `CUSTOM` can only be selected in developer mode
|
||||||
|
- mqtt sends round values without decimals (`28` instead of `28.0`)
|
||||||
|
|
||||||
|
|
||||||
## [3.8.0] 31 December 2025
|
## [3.8.0] 31 December 2025
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|||||||
@@ -2,30 +2,37 @@
|
|||||||
|
|
||||||
For more details go to [emsesp.org](https://emsesp.org/).
|
For more details go to [emsesp.org](https://emsesp.org/).
|
||||||
|
|
||||||
## [3.8.1]
|
## [3.8.2]
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
- update time saved in nvs
|
- comfortpoint for BC400 [#2935](https://github.com/emsesp/EMS-ESP32/issues/2935)
|
||||||
- heatpump entities [#2883](https://github.com/emsesp/EMS-ESP32/issues/2883)
|
- customize device brand [#2784](https://github.com/emsesp/EMS-ESP32/issues/2784)
|
||||||
- HA input number format (mode) selectable box/slider (slider for max range 100) [#2900](https://github.com/emsesp/EMS-ESP32/discussions/2900)
|
- set model for ems-esp devices temperature, analog, etc. [#2958](https://github.com/emsesp/EMS-ESP32/discussions/2958)
|
||||||
|
- prometheus metrics for temperature/analog/scheduler/custom [#2962](https://github.com/emsesp/EMS-ESP32/issues/2962)
|
||||||
|
- boiler pumpkick [#2965](https://github.com/emsesp/EMS-ESP32/discussions/2965)
|
||||||
|
- heatpump reset [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
|
||||||
|
- e-mail notification using ReadyMail Client
|
||||||
|
- 2.nd freshwater module (dhw4, dhw5) [#2991](https://github.com/emsesp/EMS-ESP32/issues/2991)
|
||||||
|
- full system backup and restore
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- fix EMS bus disconnected errors on some systems [#2881](https://github.com/emsesp/EMS-ESP32/issues/2881)
|
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
|
||||||
- selflowtemp fix [#2876](https://github.com/emsesp/EMS-ESP32/issues/2876)
|
- missing translations [#3015](https://github.com/emsesp/EMS-ESP32/issues/3015)
|
||||||
- updated valid GPIOs for ESP32S2, ESP32S3 and ESP32 that caused custom systems to block gpios [#2887](https://github.com/emsesp/EMS-ESP32/issues/2887)
|
|
||||||
- Junkers wwcharge offset [#2860](https://github.com/emsesp/EMS-ESP32/issues/2860)
|
|
||||||
- fixed minflowtemp [#2890](https://github.com/emsesp/EMS-ESP32/issues/2890)
|
|
||||||
- don't add HA uom/classes for bool values [#2885](https://github.com/emsesp/EMS-ESP32/issues/2885)
|
|
||||||
- fixed missing progress bar on web firmware uploads
|
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
- snapshot gpios stored in temporary ram
|
- weblogbuffer up to 1000 messages with PSRAM, mentioned in [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
|
||||||
- GPIOs stored along with the name and reported in log if conflicting
|
- validate custom entity writes, [#2931](https://github.com/emsesp/EMS-ESP32/issues/2931)
|
||||||
- free GPIOs depend on board profile [#2901](https://github.com/emsesp/EMS-ESP32/issues/2901)
|
- remove wrong burnMinPower [#2918](https://github.com/emsesp/EMS-ESP32/issues/2918)
|
||||||
- prefer PSram for mqtt queue [#2889](https://github.com/emsesp/EMS-ESP32/issues/2889)
|
- store scheduler active state to nvs [#2946](https://github.com/emsesp/EMS-ESP32/discussions/2946)
|
||||||
- day schedule defult to all days, no day selected is not allowed
|
- translated modes `heat` and `eco` for HA-climate mode-str-tpl
|
||||||
- board profile `CUSTOM` can only be selected in developer mode
|
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
|
||||||
- mqtt sends round values without decimals (`28` instead of `28.0`)
|
- update version if it is 00.00 in first read [#2981](https://github.com/emsesp/EMS-ESP32/issues/2981)
|
||||||
|
- device class for % values [#2980](https://github.com/emsesp/EMS-ESP32/issues/2980)
|
||||||
|
- use tasmota core 2026.03.30
|
||||||
|
- secure mqtt uses ESP_SSLClient
|
||||||
|
- fetch telegrams: set length to fetch [#3017](https://github.com/emsesp/EMS-ESP32/issues/3017)
|
||||||
|
- move http client from stack to heap
|
||||||
|
- heap optimizations [#3021](https://github.com/emsesp/EMS-ESP32/discussions/3021)
|
||||||
|
|||||||
15
Makefile
15
Makefile
@@ -47,24 +47,25 @@ MAKEFLAGS += -j$(JOBS) -l$(shell echo $$(($(JOBS) * 2)))
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
TARGET := emsesp
|
TARGET := emsesp
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := src/core src/devices src/web src/test lib_standalone lib/semver lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton
|
SOURCES := src/core src/devices src/web src/test lib_standalone lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton
|
||||||
INCLUDES := src/core src/devices src/web src/test lib_standalone lib/* lib/semver lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src
|
INCLUDES := src/core src/devices src/web src/test lib_standalone lib/* lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src
|
||||||
LIBRARIES :=
|
LIBRARIES :=
|
||||||
|
|
||||||
CPPCHECK = cppcheck
|
CPPCHECK = cppcheck
|
||||||
CHECKFLAGS = -q --force --std=gnu++17
|
CHECKFLAGS = -q --force --std=gnu++20
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Languages Standard
|
# Languages Standard
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
C_STANDARD := -std=c17
|
C_STANDARD := -std=c20
|
||||||
CXX_STANDARD := -std=gnu++17
|
CXX_STANDARD := -std=gnu++20
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Defined Symbols
|
# Defined Symbols
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
|
DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
|
||||||
DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMC_RX_BUFFER_SIZE=1500
|
DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMC_RX_BUFFER_SIZE=1500
|
||||||
|
DEFINES += -DNO_TLS_SUPPORT
|
||||||
DEFINES += $(ARGS)
|
DEFINES += $(ARGS)
|
||||||
|
|
||||||
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
|
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
|
||||||
@@ -79,6 +80,10 @@ SYMBOLS := $(CURDIR)/$(BUILD)/$(TARGET).out
|
|||||||
CSOURCES := $(shell find $(SOURCES) -name "*.c" 2>/dev/null)
|
CSOURCES := $(shell find $(SOURCES) -name "*.c" 2>/dev/null)
|
||||||
CXXSOURCES := $(shell find $(SOURCES) -name "*.cpp" 2>/dev/null)
|
CXXSOURCES := $(shell find $(SOURCES) -name "*.cpp" 2>/dev/null)
|
||||||
|
|
||||||
|
# Exclude files not needed for standalone build, if they exist
|
||||||
|
CSOURCES := $(filter-out src/core/ModuleLibrary.c,$(CSOURCES))
|
||||||
|
CXXSOURCES := $(filter-out src/core/ModuleLibrary.cpp,$(CXXSOURCES))
|
||||||
|
|
||||||
OBJS := $(patsubst %,$(BUILD)/%.o,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)))
|
OBJS := $(patsubst %,$(BUILD)/%.o,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)))
|
||||||
DEPS := $(patsubst %,$(BUILD)/%.d,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)))
|
DEPS := $(patsubst %,$(BUILD)/%.d,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)))
|
||||||
|
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -18,7 +18,7 @@
|
|||||||
<a href="https://emsesp.org">
|
<a href="https://emsesp.org">
|
||||||
<img src="https://img.shields.io/badge/Documentation-0077b5?style=for-the-badge&logo=googledocs&logoColor=white" alt="Guides" />
|
<img src="https://img.shields.io/badge/Documentation-0077b5?style=for-the-badge&logo=googledocs&logoColor=white" alt="Guides" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.gg/3J3GgnzpyT">
|
<a href="https://discord.gg/GP9DPSgeJq">
|
||||||
<img src="https://img.shields.io/badge/Discord-7289da?style=for-the-badge&logo=discord&logoColor=white" alt="Discord" />
|
<img src="https://img.shields.io/badge/Discord-7289da?style=for-the-badge&logo=discord&logoColor=white" alt="Discord" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md">
|
<a href="https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md">
|
||||||
@@ -32,7 +32,8 @@
|
|||||||
[](https://sonarcloud.io/summary/new_code?id=emsesp_EMS-ESP32)
|
[](https://sonarcloud.io/summary/new_code?id=emsesp_EMS-ESP32)
|
||||||
[](https://app.codacy.com/gh/emsesp/EMS-ESP32/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
[](https://app.codacy.com/gh/emsesp/EMS-ESP32/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
||||||
[](https://github.com/emsesp/EMS-ESP32/releases)
|
[](https://github.com/emsesp/EMS-ESP32/releases)
|
||||||
[](https://discord.gg/3J3GgnzpyT)
|
[](https://discord.gg/GP9DPSgeJq)
|
||||||
|
[](https://deepwiki.com/emsesp/EMS-ESP32)
|
||||||
|
|
||||||
[](https://github.com/emsesp/EMS-ESP32/stargazers)
|
[](https://github.com/emsesp/EMS-ESP32/stargazers)
|
||||||
[](https://github.com/emsesp/EMS-ESP32/network)
|
[](https://github.com/emsesp/EMS-ESP32/network)
|
||||||
@@ -40,7 +41,8 @@
|
|||||||
|
|
||||||
**EMS-ESP** is an open-source firmware for the Espressif ESP32 microcontroller to communicate with **EMS** (Energy Management System) compatible equipment from manufacturers such as Bosch, Buderus, Nefit, Junkers, Worcester, Sieger, elm.leblanc and iVT.
|
**EMS-ESP** is an open-source firmware for the Espressif ESP32 microcontroller to communicate with **EMS** (Energy Management System) compatible equipment from manufacturers such as Bosch, Buderus, Nefit, Junkers, Worcester, Sieger, elm.leblanc and iVT.
|
||||||
|
|
||||||
It requires a small circuit to interface with the EMS bus which can be purchased from <https://bbqkees-electronics.nl> or custom built.
|
It requires a small circuit to interface with the EMS bus which can be purchased from <https://bbqkees-electronics.nl>. These gateways are tested thoroughly and certified to work with EMS-ESP.
|
||||||
|
|
||||||
|
|
||||||
## 📦 **Key Features**
|
## 📦 **Key Features**
|
||||||
|
|
||||||
@@ -64,17 +66,17 @@ Head over to the [Installation Guide](https://emsesp.org/Installing) section of
|
|||||||
|
|
||||||
## 📋 **Documentation**
|
## 📋 **Documentation**
|
||||||
|
|
||||||
Visit [emsesp.org](https://emsesp.org) for more details on how to install and configure EMS-ESP. There is also a collection of Frequently Asked Questions and Troubleshooting tips with example customizations from the community.
|
Visit [emsesp.org](https://emsesp.org) for more details on how to setup and configure EMS-ESP. You'll also find more a collection of example configuarations, Frequently Asked Questions and Troubleshooting tips.
|
||||||
|
|
||||||
## 💬 **Getting Support**
|
## 💬 **Getting Support**
|
||||||
|
|
||||||
To chat with the community reach out on our [Discord Server](https://discord.gg/3J3GgnzpyT).
|
To chat with the community reach out on our [Discord Server](https://discord.gg/GP9DPSgeJq).
|
||||||
|
|
||||||
If you find an issue or have a request, see [how to request support](https://emsesp.org/Support/) on how to submit a bug report or feature request.
|
If you find an issue or have a request, see the [Getting Support](https://emsesp.org/Support/) section of the documentation. Note if you are using a non-BBQKees EMS gateway, you may need to contact the manufacturer for support.
|
||||||
|
|
||||||
## 🎥 **Live Demo**
|
## 🎥 **Live Demo**
|
||||||
|
|
||||||
For a live demo go to [demo.emsesp.org](https://demo.emsesp.org). Pick a language from the sign on page and log in with any username or password. Note not all features are operational as it's based on static data.
|
To see a live demo go to [demo.emsesp.org](https://demo.emsesp.org). Pick a language and use any username and password to log in. Note whast you're seeing is static example data so not all features are operational.
|
||||||
|
|
||||||
## 💖 **Contributors**
|
## 💖 **Contributors**
|
||||||
|
|
||||||
@@ -84,7 +86,7 @@ If you like **EMS-ESP**, please give it a ✨ on GitHub, or even better fork it
|
|||||||
|
|
||||||
## 📦 **Building**
|
## 📦 **Building**
|
||||||
|
|
||||||
See the [Building Guide](https://emsesp.org/Building) section of the documentation for instructions on how to build EMS-ESP.
|
See the [Building the firmware](https://emsesp.org/Building) guide in the documentation for instructions on how to build EMS-ESP from this source code.
|
||||||
|
|
||||||
## 📢 **Libraries used**
|
## 📢 **Libraries used**
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
},
|
},
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
"-DTASMOTA_SDK",
|
"-DNO_TLS_SUPPORT",
|
||||||
"-DARDUINO_LOLIN_C3_MINI",
|
"-DARDUINO_LOLIN_C3_MINI",
|
||||||
"-DARDUINO_USB_MODE=1",
|
"-DARDUINO_USB_MODE=1",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
"-DBOARD_HAS_PSRAM",
|
"-DBOARD_HAS_PSRAM",
|
||||||
"-DTASMOTA_SDK",
|
"-DNO_TLS_SUPPORT",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
"-DARDUINO_USB_MODE=0"
|
"-DARDUINO_USB_MODE=0"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"arduino",
|
"arduino",
|
||||||
"espidf"
|
"espidf"
|
||||||
],
|
],
|
||||||
"name": "Espressif ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS",
|
"name": "Tasmota ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS",
|
||||||
"upload": {
|
"upload": {
|
||||||
"flash_size": "32MB",
|
"flash_size": "32MB",
|
||||||
"maximum_ram_size": 327680,
|
"maximum_ram_size": 327680,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"build": {
|
"build": {
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": "-DTASMOTA_SDK",
|
"extra_flags": "-DNO_TLS_SUPPORT",
|
||||||
"f_cpu": "240000000L",
|
"f_cpu": "240000000L",
|
||||||
"f_flash": "40000000L",
|
"f_flash": "40000000L",
|
||||||
"flash_mode": "dio",
|
"flash_mode": "dio",
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"arduino",
|
"arduino",
|
||||||
"espidf"
|
"espidf"
|
||||||
],
|
],
|
||||||
"name": "Espressif ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
|
"name": "Tasmota ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
|
||||||
"upload": {
|
"upload": {
|
||||||
"flash_size": "16MB",
|
"flash_size": "16MB",
|
||||||
"maximum_ram_size": 327680,
|
"maximum_ram_size": 327680,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"arduino",
|
"arduino",
|
||||||
"espidf"
|
"espidf"
|
||||||
],
|
],
|
||||||
"name": "Espressif ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS",
|
"name": "Tasmota ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS",
|
||||||
"upload": {
|
"upload": {
|
||||||
"flash_size": "16MB",
|
"flash_size": "16MB",
|
||||||
"maximum_ram_size": 327680,
|
"maximum_ram_size": 327680,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"build": {
|
"build": {
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": "-DTASMOTA_SDK",
|
"extra_flags": "-DNO_TLS_SUPPORT",
|
||||||
"f_cpu": "240000000L",
|
"f_cpu": "240000000L",
|
||||||
"f_flash": "40000000L",
|
"f_flash": "40000000L",
|
||||||
"flash_mode": "dio",
|
"flash_mode": "dio",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
|
"-DNO_TLS_SUPPORT",
|
||||||
"-DARDUINO_XIAO_ESP32C6",
|
"-DARDUINO_XIAO_ESP32C6",
|
||||||
"-DARDUINO_USB_MODE=1",
|
"-DARDUINO_USB_MODE=1",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dictionaries": ["project-words"],
|
"dictionaries": ["project-words"],
|
||||||
|
"caseSensitive": false,
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"compile_commands.json",
|
"compile_commands.json",
|
||||||
@@ -36,6 +37,8 @@
|
|||||||
"pnpm-*.yaml",
|
"pnpm-*.yaml",
|
||||||
"vite.config.ts",
|
"vite.config.ts",
|
||||||
"lib/esp32-psram/**",
|
"lib/esp32-psram/**",
|
||||||
"test/test_api/test_api.h"
|
"test/test_api/test_api.h",
|
||||||
|
"lib_standalone/**",
|
||||||
|
"**/*.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,22 @@
|
|||||||
{
|
{
|
||||||
|
"type": "systembackup",
|
||||||
|
"version": "3.8.2",
|
||||||
|
"date": "2026-03-29T13:28:15",
|
||||||
|
"systembackup": [
|
||||||
|
{
|
||||||
"type": "settings",
|
"type": "settings",
|
||||||
"Network": {
|
"Network": {
|
||||||
"ssid": "my_wifi_ssid",
|
"ssid": "",
|
||||||
"bssid": "",
|
"bssid": "",
|
||||||
"password": "my_wifi_password",
|
"password": "",
|
||||||
"hostname": "ems-esp"
|
"hostname": "ems-esp",
|
||||||
|
"static_ip_config": false,
|
||||||
|
"bandwidth20": false,
|
||||||
|
"nosleep": true,
|
||||||
|
"enableMDNS": true,
|
||||||
|
"enableCORS": false,
|
||||||
|
"CORSOrigin": "*",
|
||||||
|
"tx_power": 0
|
||||||
},
|
},
|
||||||
"AP": {
|
"AP": {
|
||||||
"provision_mode": 2,
|
"provision_mode": 2,
|
||||||
@@ -21,12 +33,14 @@
|
|||||||
"enableTLS": false,
|
"enableTLS": false,
|
||||||
"rootCA": "",
|
"rootCA": "",
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"host": "127.0.0.1",
|
"host": "",
|
||||||
"port": 1883,
|
"port": 1883,
|
||||||
"base": "ems-esp",
|
"base": "ems-esp",
|
||||||
"username": "username",
|
"username": "",
|
||||||
"password": "password",
|
"password": "",
|
||||||
"client_id": "ems-esp",
|
"client_id": "esp32-b8ffc9ec",
|
||||||
|
"keep_alive": 60,
|
||||||
|
"clean_session": false,
|
||||||
"entity_format": 1,
|
"entity_format": 1,
|
||||||
"publish_time_boiler": 10,
|
"publish_time_boiler": 10,
|
||||||
"publish_time_thermostat": 10,
|
"publish_time_thermostat": 10,
|
||||||
@@ -42,6 +56,7 @@
|
|||||||
"nested_format": 1,
|
"nested_format": 1,
|
||||||
"discovery_prefix": "homeassistant",
|
"discovery_prefix": "homeassistant",
|
||||||
"discovery_type": 0,
|
"discovery_type": 0,
|
||||||
|
"ha_number_mode": 0,
|
||||||
"publish_single": false,
|
"publish_single": false,
|
||||||
"publish_single2cmd": false,
|
"publish_single2cmd": false,
|
||||||
"send_response": false
|
"send_response": false
|
||||||
@@ -68,18 +83,146 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Settings": {
|
"Settings": {
|
||||||
"board_profile": "S3",
|
"version": "3.8.2",
|
||||||
|
"board_profile": "E32V2_2",
|
||||||
|
"platform": "ESP32",
|
||||||
"locale": "en",
|
"locale": "en",
|
||||||
"tx_mode": 1,
|
"tx_mode": 1,
|
||||||
"ems_bus_id": 11,
|
"ems_bus_id": 11,
|
||||||
|
"syslog_enabled": false,
|
||||||
|
"syslog_level": 3,
|
||||||
|
"trace_raw": false,
|
||||||
|
"syslog_mark_interval": 0,
|
||||||
|
"syslog_host": "",
|
||||||
|
"syslog_port": 514,
|
||||||
"boiler_heatingoff": false,
|
"boiler_heatingoff": false,
|
||||||
"hide_led": true,
|
"remote_timeout": 24,
|
||||||
|
"remote_timeout_en": false,
|
||||||
|
"shower_timer": false,
|
||||||
|
"shower_alert": false,
|
||||||
|
"shower_alert_coldshot": 10,
|
||||||
|
"shower_alert_trigger": 7,
|
||||||
|
"shower_min_duration": 180,
|
||||||
|
"rx_gpio": 4,
|
||||||
|
"tx_gpio": 5,
|
||||||
|
"dallas_gpio": 14,
|
||||||
|
"dallas_parasite": false,
|
||||||
|
"led_gpio": 32,
|
||||||
|
"hide_led": false,
|
||||||
|
"led_type": 1,
|
||||||
|
"low_clock": false,
|
||||||
"telnet_enabled": true,
|
"telnet_enabled": true,
|
||||||
"notoken_api": false,
|
"notoken_api": false,
|
||||||
|
"readonly_mode": false,
|
||||||
"analog_enabled": true,
|
"analog_enabled": true,
|
||||||
|
"pbutton_gpio": 34,
|
||||||
|
"solar_maxflow": 30,
|
||||||
"fahrenheit": false,
|
"fahrenheit": false,
|
||||||
"bool_format": 1,
|
"bool_format": 1,
|
||||||
"bool_dashboard": 1,
|
"bool_dashboard": 1,
|
||||||
"enum_format": 1
|
"enum_format": 1,
|
||||||
|
"weblog_level": 6,
|
||||||
|
"weblog_buffer": 50,
|
||||||
|
"weblog_compact": true,
|
||||||
|
"phy_type": 1,
|
||||||
|
"eth_power": 15,
|
||||||
|
"eth_phy_addr": 0,
|
||||||
|
"eth_clock_mode": 1,
|
||||||
|
"modbus_enabled": false,
|
||||||
|
"modbus_port": 502,
|
||||||
|
"modbus_max_clients": 10,
|
||||||
|
"modbus_timeout": 300,
|
||||||
|
"developer_mode": true,
|
||||||
|
"email_enabled": false,
|
||||||
|
"email_ssl": false,
|
||||||
|
"email_starttls": true,
|
||||||
|
"email_server": "smtp.example.net",
|
||||||
|
"email_port": 587,
|
||||||
|
"email_login": "",
|
||||||
|
"email_pass": "",
|
||||||
|
"email_sender": "ems-esp@example.net",
|
||||||
|
"email_recp": "",
|
||||||
|
"email_subject": "ems-esp notification"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "schedule",
|
||||||
|
"Schedule": {
|
||||||
|
"schedule": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "customizations",
|
||||||
|
"Customizations": {
|
||||||
|
"ts": [
|
||||||
|
{
|
||||||
|
"id": "28_1767_7B13_2502",
|
||||||
|
"name": "gateway_temperature",
|
||||||
|
"offset": 0,
|
||||||
|
"is_system": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"as": [
|
||||||
|
{
|
||||||
|
"gpio": 39,
|
||||||
|
"name": "core_voltage",
|
||||||
|
"offset": 0,
|
||||||
|
"factor": 0.003771,
|
||||||
|
"uom": 23,
|
||||||
|
"type": 3,
|
||||||
|
"is_system": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gpio": 36,
|
||||||
|
"name": "supply_voltage",
|
||||||
|
"offset": 0,
|
||||||
|
"factor": 0.017,
|
||||||
|
"uom": 23,
|
||||||
|
"type": 3,
|
||||||
|
"is_system": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gpio": 2,
|
||||||
|
"name": "led",
|
||||||
|
"offset": 0,
|
||||||
|
"factor": 1,
|
||||||
|
"uom": 0,
|
||||||
|
"type": 6,
|
||||||
|
"is_system": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"masked_entities": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "entities",
|
||||||
|
"Entities": {
|
||||||
|
"entities": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "modules",
|
||||||
|
"Modules": {
|
||||||
|
"modules": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "customSupport",
|
||||||
|
"Support": {
|
||||||
|
"html": [
|
||||||
|
"This product is installed and managed by:",
|
||||||
|
"",
|
||||||
|
"<b>Bosch Installer Example</b>",
|
||||||
|
"",
|
||||||
|
"Nefit Road 12",
|
||||||
|
"1234 AB Amsterdam",
|
||||||
|
"Phone: +31 123 456 789",
|
||||||
|
"email: support@boschinstaller.nl",
|
||||||
|
"",
|
||||||
|
"For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
|
||||||
|
],
|
||||||
|
"img_url": "https://emsesp.org/media/images/designer.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ telegram_type_id,name,is_fetched
|
|||||||
0x19,UBAMonitorSlow,
|
0x19,UBAMonitorSlow,
|
||||||
0x1A,UBASetPoints,
|
0x1A,UBASetPoints,
|
||||||
0x1C,UBAMaintenanceStatus,
|
0x1C,UBAMaintenanceStatus,
|
||||||
0x1E,WM10TempMessage,
|
0x1E,HydrTemp,
|
||||||
0x23,JunkersSetMixer,fetched
|
0x23,JunkersSetMixer,fetched
|
||||||
0x27,UBASettingsWW,fetched
|
0x27,UBASettingsWW,fetched
|
||||||
0x28,WeatherComp,fetched
|
0x28,WeatherComp,fetched
|
||||||
@@ -72,11 +72,12 @@ telegram_type_id,name,is_fetched
|
|||||||
0xE6,UBAParametersPlus,fetched
|
0xE6,UBAParametersPlus,fetched
|
||||||
0xE9,UBAMonitorWWPlus,
|
0xE9,UBAMonitorWWPlus,
|
||||||
0xEA,UBAParameterWWPlus,fetched
|
0xEA,UBAParameterWWPlus,fetched
|
||||||
|
0xEB,PumpKick,fetched
|
||||||
0x0101,ISM1Set,fetched
|
0x0101,ISM1Set,fetched
|
||||||
0x0103,ISM1StatusMessage,fetched
|
0x0103,ISM1StatusMessage,fetched
|
||||||
0x0104,ISM2StatusMessage,
|
0x0104,ISM2StatusMessage,
|
||||||
0x010C,IPMStatusMessage,
|
0x010C,IPMStatusMessage,
|
||||||
0x011E,JunkersDisp,fetched
|
0x011E,IPMTempMessage,
|
||||||
0x012E,HPEnergy1,
|
0x012E,HPEnergy1,
|
||||||
0x013B,HPEnergy2,
|
0x013B,HPEnergy2,
|
||||||
0x0165,JunkersSet,
|
0x0165,JunkersSet,
|
||||||
@@ -111,8 +112,8 @@ telegram_type_id,name,is_fetched
|
|||||||
0x02A0,RC300Curves,
|
0x02A0,RC300Curves,
|
||||||
0x02A1,RC300Curves,
|
0x02A1,RC300Curves,
|
||||||
0x02A2,RC300Curves,
|
0x02A2,RC300Curves,
|
||||||
0x02A5,RC300Monitor,
|
0x02A5,RC300Monitor,fetched
|
||||||
0x02A6,RC300Monitor,
|
0x02A6,CRFMonitor,
|
||||||
0x02A7,RC300Monitor,
|
0x02A7,RC300Monitor,
|
||||||
0x02A8,CRFMonitor,
|
0x02A8,CRFMonitor,
|
||||||
0x02A9,RC300Monitor,
|
0x02A9,RC300Monitor,
|
||||||
@@ -197,7 +198,7 @@ telegram_type_id,name,is_fetched
|
|||||||
0x04A2,HpInput,fetched
|
0x04A2,HpInput,fetched
|
||||||
0x04A5,HPFan,fetched
|
0x04A5,HPFan,fetched
|
||||||
0x04A7,HPPowerLimit,fetched
|
0x04A7,HPPowerLimit,fetched
|
||||||
0x04AA,HPPower2,fetched
|
0x04AA,HPPower,
|
||||||
0x04AE,HPEnergy,fetched
|
0x04AE,HPEnergy,fetched
|
||||||
0x04AF,HPMeters,fetched
|
0x04AF,HPMeters,fetched
|
||||||
0x055C,VentilationSet,fetched
|
0x055C,VentilationSet,fetched
|
||||||
|
|||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"adapter": "react",
|
"adapter": "react",
|
||||||
"baseLocale": "pl",
|
"baseLocale": "pl",
|
||||||
"$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json"
|
"$schema": "https://unpkg.com/typesafe-i18n@5.27.1/schema/typesafe-i18n.json"
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import eslint from '@eslint/js';
|
import eslint from '@eslint/js';
|
||||||
import prettierConfig from 'eslint-config-prettier';
|
import prettierConfig from 'eslint-config-prettier';
|
||||||
|
import { defineConfig } from 'eslint/config';
|
||||||
import tseslint from 'typescript-eslint';
|
import tseslint from 'typescript-eslint';
|
||||||
|
|
||||||
export default tseslint.config(
|
export default defineConfig(
|
||||||
eslint.configs.recommended,
|
eslint.configs.recommended,
|
||||||
...tseslint.configs.recommendedTypeChecked,
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
prettierConfig,
|
prettierConfig,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "EMS-ESP",
|
"name": "EMS-ESP",
|
||||||
"version": "3.8.0",
|
"version": "3.8.2",
|
||||||
"description": "EMS-ESP WebUI",
|
"description": "EMS-ESP WebUI",
|
||||||
"homepage": "https://emsesp.org",
|
"homepage": "https://emsesp.org",
|
||||||
"author": "proddy, emsesp.org",
|
"author": "proddy, emsesp.org",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"pnpm:mock-rest\" \"vite preview\"",
|
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"pnpm:mock-rest\" \"vite preview\"",
|
||||||
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite dev\"",
|
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite dev\"",
|
||||||
"typesafe-i18n": "typesafe-i18n --no-watch",
|
"typesafe-i18n": "typesafe-i18n --no-watch",
|
||||||
"build_webUI": "typesafe-i18n --no-watch && vite build && node progmem-generator.js",
|
"build-webUI": "typesafe-i18n --no-watch && vite build && node progmem-generator.js",
|
||||||
"format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'",
|
"format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'",
|
||||||
"lint": "eslint . --fix",
|
"lint": "eslint . --fix",
|
||||||
"standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\""
|
"standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\""
|
||||||
@@ -26,46 +26,45 @@
|
|||||||
"@alova/adapter-xhr": "2.3.1",
|
"@alova/adapter-xhr": "2.3.1",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@mui/icons-material": "^7.3.7",
|
"@mui/icons-material": "^9.0.0",
|
||||||
"@mui/material": "^7.3.7",
|
"@mui/material": "^9.0.0",
|
||||||
"@preact/compat": "^18.3.1",
|
"@preact/compat": "^18.3.2",
|
||||||
"@table-library/react-table-library": "4.1.15",
|
"@table-library/react-table-library": "4.1.15",
|
||||||
"alova": "3.4.1",
|
"alova": "^3.5.1",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"formidable": "^3.5.4",
|
"formidable": "^3.5.4",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"preact": "^10.28.2",
|
"preact": "^10.29.1",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.5",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.5",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.6.0",
|
||||||
"react-router": "^7.12.0",
|
"react-router": "^7.14.1",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"typesafe-i18n": "^5.26.2",
|
"typesafe-i18n": "^5.27.1",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^6.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.28.5",
|
"@babel/core": "^7.29.0",
|
||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^10.0.1",
|
||||||
"@preact/compat": "^18.3.1",
|
"@preact/compat": "^18.3.2",
|
||||||
"@preact/preset-vite": "^2.10.2",
|
"@preact/preset-vite": "^2.10.5",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^25.0.6",
|
"@types/node": "^25.6.0",
|
||||||
"@types/react": "^19.2.8",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"axe-core": "^4.11.1",
|
"axe-core": "^4.11.3",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^10.2.1",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.8.3",
|
||||||
"rollup-plugin-visualizer": "^6.0.5",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"terser": "^5.44.1",
|
"terser": "^5.46.1",
|
||||||
"typescript-eslint": "^8.52.0",
|
"typescript-eslint": "^8.58.2",
|
||||||
"vite": "^7.3.1",
|
"vite": "^8.0.8",
|
||||||
"vite-plugin-imagemin": "^0.6.1",
|
"vite-plugin-imagemin": "^0.6.1"
|
||||||
"vite-tsconfig-paths": "^6.0.4"
|
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
|
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
||||||
}
|
}
|
||||||
|
|||||||
2503
interface/pnpm-lock.yaml
generated
2503
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -24,20 +24,26 @@ let bundleStats = {
|
|||||||
other: { count: 0, uncompressed: 0, compressed: 0 }
|
other: { count: 0, uncompressed: 0, compressed: 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateWWWClass =
|
// AsyncWebHandler that performs the lookup.
|
||||||
() => `typedef std::function<void(const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash)> RouteRegistrationHandler;
|
const generateWWWClass = () => `// Bundle Statistics:
|
||||||
// Bundle Statistics:
|
|
||||||
// - Total compressed size: ${(totalSize / 1000).toFixed(1)} KB
|
// - Total compressed size: ${(totalSize / 1000).toFixed(1)} KB
|
||||||
// - Total uncompressed size: ${(Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) / 1000).toFixed(1)} KB
|
// - Total uncompressed size: ${(Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) / 1000).toFixed(1)} KB
|
||||||
// - Compression ratio: ${(((Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) - totalSize) / Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0)) * 100).toFixed(1)}%
|
// - Compression ratio: ${(((Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) - totalSize) / Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0)) * 100).toFixed(1)}%
|
||||||
// - Generated on: ${new Date().toISOString()}
|
// - Generated on: ${new Date().toISOString()}
|
||||||
|
|
||||||
class WWWData {
|
struct WWWAsset {
|
||||||
${INDENT}public:
|
${INDENT}const char * uri;
|
||||||
${INDENT.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
|
${INDENT}const char * contentType;
|
||||||
${fileInfo.map((f) => `${INDENT.repeat(3)}handler("${f.uri}", "${f.mimeType}", ${f.variable}, ${f.size}, ${f.hash});`).join('\n')}
|
${INDENT}const uint8_t * content;
|
||||||
${INDENT.repeat(2)}}
|
${INDENT}size_t len;
|
||||||
|
${INDENT}const char * etag; // already includes enclosing double quotes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const WWWAsset WWW_ASSETS[] = {
|
||||||
|
${fileInfo.map((f) => `${INDENT}{"${f.uri}", "${f.mimeType}", ${f.variable}, ${f.size}, "\\"${f.rawHash}\\""},`).join('\n')}
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr size_t WWW_ASSETS_COUNT = sizeof(WWW_ASSETS) / sizeof(WWW_ASSETS[0]);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const getFilesSync = (dir, files = []) => {
|
const getFilesSync = (dir, files = []) => {
|
||||||
@@ -72,6 +78,7 @@ const writeFile = (relativeFilePath, buffer) => {
|
|||||||
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
|
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
|
||||||
// const hash = crypto.createHash('sha256').update(zipBuffer).digest('hex');
|
// const hash = crypto.createHash('sha256').update(zipBuffer).digest('hex');
|
||||||
const hash = etag(zipBuffer); // use smaller md5 instead of sha256
|
const hash = etag(zipBuffer); // use smaller md5 instead of sha256
|
||||||
|
const rawHash = hash.replace(/^"|"$/g, '');
|
||||||
|
|
||||||
zipBuffer.forEach((b) => {
|
zipBuffer.forEach((b) => {
|
||||||
if (!(size % bytesPerLine)) {
|
if (!(size % bytesPerLine)) {
|
||||||
@@ -94,7 +101,8 @@ const writeFile = (relativeFilePath, buffer) => {
|
|||||||
mimeType,
|
mimeType,
|
||||||
variable,
|
variable,
|
||||||
size,
|
size,
|
||||||
hash
|
hash,
|
||||||
|
rawHash
|
||||||
});
|
});
|
||||||
|
|
||||||
totalSize += size;
|
totalSize += size;
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
import { type FC, Suspense, lazy, memo, useContext, useEffect, useRef } from 'react';
|
import { type FC, memo, useContext, useEffect, useRef } from 'react';
|
||||||
import { Navigate, Route, Routes } from 'react-router';
|
import { Navigate, Route, Routes } from 'react-router';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import {
|
import AuthenticatedRouting from 'AuthenticatedRouting';
|
||||||
LoadingSpinner,
|
import SignIn from 'SignIn';
|
||||||
RequireAuthenticated,
|
import { RequireAuthenticated, RequireUnauthenticated } from 'components';
|
||||||
RequireUnauthenticated
|
|
||||||
} from 'components';
|
|
||||||
import { Authentication, AuthenticationContext } from 'contexts/authentication';
|
import { Authentication, AuthenticationContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
// Lazy load route components for better code splitting
|
|
||||||
const SignIn = lazy(() => import('SignIn'));
|
|
||||||
const AuthenticatedRouting = lazy(() => import('AuthenticatedRouting'));
|
|
||||||
|
|
||||||
interface SecurityRedirectProps {
|
interface SecurityRedirectProps {
|
||||||
readonly message: string;
|
readonly message: string;
|
||||||
readonly signOut?: boolean;
|
readonly signOut?: boolean;
|
||||||
@@ -45,7 +39,6 @@ const AppRouting: FC = memo(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Authentication>
|
<Authentication>
|
||||||
<Suspense fallback={<LoadingSpinner />}>
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
path="/unauthorized"
|
path="/unauthorized"
|
||||||
@@ -72,7 +65,6 @@ const AppRouting: FC = memo(() => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
|
||||||
</Authentication>
|
</Authentication>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,44 +1,39 @@
|
|||||||
import { Suspense, lazy, memo, useContext } from 'react';
|
import { memo, useContext } from 'react';
|
||||||
import { Navigate, Route, Routes } from 'react-router';
|
import { Navigate, Route, Routes } from 'react-router';
|
||||||
|
|
||||||
import { Layout, LoadingSpinner } from 'components';
|
import CustomEntities from 'app/main/CustomEntities';
|
||||||
|
import Customizations from 'app/main/Customizations';
|
||||||
|
import Dashboard from 'app/main/Dashboard';
|
||||||
|
import Devices from 'app/main/Devices';
|
||||||
|
import Help from 'app/main/Help';
|
||||||
|
import Modules from 'app/main/Modules';
|
||||||
|
import Scheduler from 'app/main/Scheduler';
|
||||||
|
import Sensors from 'app/main/Sensors';
|
||||||
|
import UserProfile from 'app/main/UserProfile';
|
||||||
|
import APSettings from 'app/settings/APSettings';
|
||||||
|
import ApplicationSettings from 'app/settings/ApplicationSettings';
|
||||||
|
import DownloadUpload from 'app/settings/DownloadUpload';
|
||||||
|
import MqttSettings from 'app/settings/MqttSettings';
|
||||||
|
import NTPSettings from 'app/settings/NTPSettings';
|
||||||
|
import Settings from 'app/settings/Settings';
|
||||||
|
import Network from 'app/settings/network/Network';
|
||||||
|
import Security from 'app/settings/security/Security';
|
||||||
|
import APStatus from 'app/status/APStatus';
|
||||||
|
import Activity from 'app/status/Activity';
|
||||||
|
import HardwareStatus from 'app/status/HardwareStatus';
|
||||||
|
import MqttStatus from 'app/status/MqttStatus';
|
||||||
|
import NTPStatus from 'app/status/NTPStatus';
|
||||||
|
import NetworkStatus from 'app/status/NetworkStatus';
|
||||||
|
import Status from 'app/status/Status';
|
||||||
|
import SystemLog from 'app/status/SystemLog';
|
||||||
|
import Version from 'app/status/Version';
|
||||||
|
import { Layout } from 'components';
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
|
||||||
// Lazy load all route components for better code splitting
|
|
||||||
const Dashboard = lazy(() => import('app/main/Dashboard'));
|
|
||||||
const Devices = lazy(() => import('app/main/Devices'));
|
|
||||||
const Sensors = lazy(() => import('app/main/Sensors'));
|
|
||||||
const Help = lazy(() => import('app/main/Help'));
|
|
||||||
const Customizations = lazy(() => import('app/main/Customizations'));
|
|
||||||
const Scheduler = lazy(() => import('app/main/Scheduler'));
|
|
||||||
const CustomEntities = lazy(() => import('app/main/CustomEntities'));
|
|
||||||
const Modules = lazy(() => import('app/main/Modules'));
|
|
||||||
const UserProfile = lazy(() => import('app/main/UserProfile'));
|
|
||||||
|
|
||||||
const Status = lazy(() => import('app/status/Status'));
|
|
||||||
const HardwareStatus = lazy(() => import('app/status/HardwareStatus'));
|
|
||||||
const Activity = lazy(() => import('app/status/Activity'));
|
|
||||||
const SystemLog = lazy(() => import('app/status/SystemLog'));
|
|
||||||
const MqttStatus = lazy(() => import('app/status/MqttStatus'));
|
|
||||||
const NTPStatus = lazy(() => import('app/status/NTPStatus'));
|
|
||||||
const APStatus = lazy(() => import('app/status/APStatus'));
|
|
||||||
const NetworkStatus = lazy(() => import('app/status/NetworkStatus'));
|
|
||||||
const Version = lazy(() => import('app/status/Version'));
|
|
||||||
|
|
||||||
const Settings = lazy(() => import('app/settings/Settings'));
|
|
||||||
const ApplicationSettings = lazy(() => import('app/settings/ApplicationSettings'));
|
|
||||||
const MqttSettings = lazy(() => import('app/settings/MqttSettings'));
|
|
||||||
const NTPSettings = lazy(() => import('app/settings/NTPSettings'));
|
|
||||||
const APSettings = lazy(() => import('app/settings/APSettings'));
|
|
||||||
const DownloadUpload = lazy(() => import('app/settings/DownloadUpload'));
|
|
||||||
const Network = lazy(() => import('app/settings/network/Network'));
|
|
||||||
const Security = lazy(() => import('app/settings/security/Security'));
|
|
||||||
|
|
||||||
const AuthenticatedRouting = memo(() => {
|
const AuthenticatedRouting = memo(() => {
|
||||||
const { me } = useContext(AuthenticatedContext);
|
const { me } = useContext(AuthenticatedContext);
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Suspense fallback={<LoadingSpinner />}>
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/dashboard/*" element={<Dashboard />} />
|
<Route path="/dashboard/*" element={<Dashboard />} />
|
||||||
<Route path="/devices/*" element={<Devices />} />
|
<Route path="/devices/*" element={<Devices />} />
|
||||||
@@ -59,10 +54,7 @@ const AuthenticatedRouting = memo(() => {
|
|||||||
{me.admin && (
|
{me.admin && (
|
||||||
<>
|
<>
|
||||||
<Route path="/settings" element={<Settings />} />
|
<Route path="/settings" element={<Settings />} />
|
||||||
<Route
|
<Route path="/settings/application" element={<ApplicationSettings />} />
|
||||||
path="/settings/application"
|
|
||||||
element={<ApplicationSettings />}
|
|
||||||
/>
|
|
||||||
<Route path="/settings/mqtt" element={<MqttSettings />} />
|
<Route path="/settings/mqtt" element={<MqttSettings />} />
|
||||||
<Route path="/settings/ntp" element={<NTPSettings />} />
|
<Route path="/settings/ntp" element={<NTPSettings />} />
|
||||||
<Route path="/settings/ap" element={<APSettings />} />
|
<Route path="/settings/ap" element={<APSettings />} />
|
||||||
@@ -80,7 +72,6 @@ const AuthenticatedRouting = memo(() => {
|
|||||||
|
|
||||||
<Route path="/*" element={<Navigate to="/" />} />
|
<Route path="/*" element={<Navigate to="/" />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { toast } from 'react-toastify';
|
|||||||
|
|
||||||
import ForwardIcon from '@mui/icons-material/Forward';
|
import ForwardIcon from '@mui/icons-material/Forward';
|
||||||
import { Box, Button, Paper, Typography } from '@mui/material';
|
import { Box, Button, Paper, Typography } from '@mui/material';
|
||||||
|
import type { Theme } from '@mui/material/styles';
|
||||||
|
|
||||||
import * as AuthenticationApi from 'components/routing/authentication';
|
import * as AuthenticationApi from 'components/routing/authentication';
|
||||||
import { useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
@@ -36,7 +37,7 @@ const SignIn = memo(() => {
|
|||||||
{
|
{
|
||||||
immediate: false
|
immediate: false
|
||||||
}
|
}
|
||||||
).onSuccess((response) => {
|
).onSuccess((response: { data: { access_token: string } }) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
authenticationContext.signIn(response.data.access_token);
|
authenticationContext.signIn(response.data.access_token);
|
||||||
}
|
}
|
||||||
@@ -78,7 +79,6 @@ const SignIn = memo(() => {
|
|||||||
}
|
}
|
||||||
}, [signInRequest, signIn, LL]);
|
}, [signInRequest, signIn, LL]);
|
||||||
|
|
||||||
// Memoize callback to prevent recreation on every render
|
|
||||||
const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]);
|
const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]);
|
||||||
|
|
||||||
// get rid of scrollbar
|
// get rid of scrollbar
|
||||||
@@ -92,13 +92,15 @@ const SignIn = memo(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
sx={(theme: Theme) => ({
|
||||||
height="100vh"
|
display: 'flex',
|
||||||
margin="auto"
|
height: '100vh',
|
||||||
padding={2}
|
margin: 'auto',
|
||||||
justifyContent="center"
|
padding: 2,
|
||||||
flexDirection="column"
|
justifyContent: 'center',
|
||||||
maxWidth={(theme) => theme.breakpoints.values.sm}
|
flexDirection: 'column',
|
||||||
|
maxWidth: theme.breakpoints.values.sm
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<Paper
|
<Paper
|
||||||
sx={(theme) => ({
|
sx={(theme) => ({
|
||||||
@@ -111,16 +113,18 @@ const SignIn = memo(() => {
|
|||||||
width: '100%'
|
width: '100%'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Typography mb={1} variant="h4">
|
<Typography sx={{ mb: 1 }} variant="h4">
|
||||||
{PROJECT_NAME}
|
{PROJECT_NAME}
|
||||||
</Typography>
|
</Typography>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
<Box
|
<Box
|
||||||
mt={1}
|
sx={{
|
||||||
display="flex"
|
mt: 1,
|
||||||
flexDirection="column"
|
display: 'flex',
|
||||||
gap={1}
|
flexDirection: 'column',
|
||||||
alignItems="center"
|
gap: 1,
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors || {}}
|
fieldErrors={fieldErrors || {}}
|
||||||
|
|||||||
@@ -310,13 +310,15 @@ const CustomEntities = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Cell>
|
</Cell>
|
||||||
|
<Cell>{ei.ram > 0 ? '' : showHex(ei.device_id as number, 2)}</Cell>
|
||||||
|
<Cell>{ei.ram > 0 ? '' : showHex(ei.type_id as number, 3)}</Cell>
|
||||||
|
<Cell>{ei.ram > 0 ? '' : ei.offset}</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
{ei.ram === 1 ? '' : showHex(ei.device_id as number, 2)}
|
{ei.ram === 1
|
||||||
</Cell>
|
? 'RAM'
|
||||||
<Cell>{ei.ram === 1 ? '' : showHex(ei.type_id as number, 3)}</Cell>
|
: ei.ram === 2
|
||||||
<Cell>{ei.ram === 1 ? '' : ei.offset}</Cell>
|
? 'NVS'
|
||||||
<Cell>
|
: DeviceValueTypeNames[ei.value_type]}
|
||||||
{ei.ram === 1 ? 'RAM' : DeviceValueTypeNames[ei.value_type]}
|
|
||||||
</Cell>
|
</Cell>
|
||||||
<Cell>{formatValue(ei.value, ei.uom)}</Cell>
|
<Cell>{formatValue(ei.value, ei.uom)}</Cell>
|
||||||
</Row>
|
</Row>
|
||||||
@@ -341,9 +343,9 @@ const CustomEntities = () => {
|
|||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<Box mb={2} color="warning.main">
|
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.ENTITIES_HELP_1()}.</Typography>
|
{LL.ENTITIES_HELP_1()}.
|
||||||
</Box>
|
</Typography>
|
||||||
|
|
||||||
{renderEntity()}
|
{renderEntity()}
|
||||||
|
|
||||||
@@ -359,8 +361,8 @@ const CustomEntities = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box mt={2} display="flex" flexWrap="wrap">
|
<Box sx={{ mt: 2, display: 'flex', flexWrap: 'wrap' }}>
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
{numChanges > 0 && (
|
{numChanges > 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
@@ -382,7 +384,7 @@ const CustomEntities = () => {
|
|||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<AddIcon />}
|
startIcon={<AddIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import DoneIcon from '@mui/icons-material/Done';
|
|||||||
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
||||||
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
||||||
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
||||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@@ -178,7 +178,7 @@ const CustomEntitiesDialog = ({
|
|||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid mt={3}>
|
<Grid sx={{ mt: 3 }}>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -205,9 +205,10 @@ const CustomEntitiesDialog = ({
|
|||||||
>
|
>
|
||||||
<MenuItem value={0}>EMS-{LL.VALUE(1)}</MenuItem>
|
<MenuItem value={0}>EMS-{LL.VALUE(1)}</MenuItem>
|
||||||
<MenuItem value={1}>RAM-{LL.VALUE(1)}</MenuItem>
|
<MenuItem value={1}>RAM-{LL.VALUE(1)}</MenuItem>
|
||||||
|
<MenuItem value={2}>NVS-{LL.VALUE(1)}</MenuItem>
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
{editItem.ram === 1 && (
|
{editItem.ram > 0 && (
|
||||||
<>
|
<>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -237,7 +238,7 @@ const CustomEntitiesDialog = ({
|
|||||||
)}
|
)}
|
||||||
{editItem.ram === 0 && (
|
{editItem.ram === 0 && (
|
||||||
<>
|
<>
|
||||||
<Grid mt={3}>
|
<Grid sx={{ mt: 3 }}>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -403,7 +404,7 @@ const CustomEntitiesDialog = ({
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
{!creating && (
|
{!creating && (
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<RemoveIcon />}
|
startIcon={<RemoveIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -111,13 +111,14 @@ const Customizations = () => {
|
|||||||
const [selectedDeviceTypeNameURL, setSelectedDeviceTypeNameURL] =
|
const [selectedDeviceTypeNameURL, setSelectedDeviceTypeNameURL] =
|
||||||
useState<string>(''); // needed for API URL
|
useState<string>(''); // needed for API URL
|
||||||
const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
|
const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
|
||||||
|
const [selectedDeviceBrand, setSelectedDeviceBrand] = useState<string>('');
|
||||||
|
|
||||||
const { send: sendResetCustomizations } = useRequest(resetCustomizations(), {
|
const { send: sendResetCustomizations } = useRequest(resetCustomizations(), {
|
||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { send: sendDeviceName } = useRequest(
|
const { send: sendDeviceName } = useRequest(
|
||||||
(data: { id: number; name: string }) => writeDeviceName(data),
|
(data: { id: number; name: string; brand: string }) => writeDeviceName(data),
|
||||||
{
|
{
|
||||||
immediate: false
|
immediate: false
|
||||||
}
|
}
|
||||||
@@ -267,6 +268,7 @@ const Customizations = () => {
|
|||||||
if (device) {
|
if (device) {
|
||||||
setSelectedDeviceTypeNameURL(device.url || '');
|
setSelectedDeviceTypeNameURL(device.url || '');
|
||||||
setSelectedDeviceName(device.n);
|
setSelectedDeviceName(device.n);
|
||||||
|
setSelectedDeviceBrand(device.b);
|
||||||
}
|
}
|
||||||
setNumChanges(0);
|
setNumChanges(0);
|
||||||
setRestartNeeded(false);
|
setRestartNeeded(false);
|
||||||
@@ -442,7 +444,11 @@ const Customizations = () => {
|
|||||||
}, [devices, deviceEntities, selectedDevice, sendCustomizationEntities, LL]);
|
}, [devices, deviceEntities, selectedDevice, sendCustomizationEntities, LL]);
|
||||||
|
|
||||||
const renameDevice = useCallback(async () => {
|
const renameDevice = useCallback(async () => {
|
||||||
await sendDeviceName({ id: selectedDevice, name: selectedDeviceName })
|
await sendDeviceName({
|
||||||
|
id: selectedDevice,
|
||||||
|
name: selectedDeviceName,
|
||||||
|
brand: selectedDeviceBrand
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(LL.UPDATED_OF(LL.NAME(1)));
|
toast.success(LL.UPDATED_OF(LL.NAME(1)));
|
||||||
})
|
})
|
||||||
@@ -453,24 +459,42 @@ const Customizations = () => {
|
|||||||
setRename(false);
|
setRename(false);
|
||||||
await fetchCoreData();
|
await fetchCoreData();
|
||||||
});
|
});
|
||||||
}, [selectedDevice, selectedDeviceName, sendDeviceName, LL, fetchCoreData]);
|
}, [
|
||||||
|
selectedDevice,
|
||||||
|
selectedDeviceName,
|
||||||
|
selectedDeviceBrand,
|
||||||
|
sendDeviceName,
|
||||||
|
LL,
|
||||||
|
fetchCoreData
|
||||||
|
]);
|
||||||
|
|
||||||
const renderDeviceList = () => (
|
const renderDeviceList = () => (
|
||||||
<>
|
<>
|
||||||
<Box mb={1} color="warning.main">
|
<Typography sx={{ mb: 1 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography>
|
{LL.CUSTOMIZATIONS_HELP_1()}.
|
||||||
</Box>
|
</Typography>
|
||||||
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2 }}>
|
||||||
{rename ? (
|
{rename ? (
|
||||||
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
name="device"
|
name="device"
|
||||||
label={LL.EMS_DEVICE()}
|
label={LL.EMS_DEVICE()}
|
||||||
fullWidth
|
style={{ minWidth: '48%' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={selectedDeviceName}
|
value={selectedDeviceName}
|
||||||
onChange={(e) => setSelectedDeviceName(e.target.value)}
|
onChange={(e) => setSelectedDeviceName(e.target.value)}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
|
<TextField
|
||||||
|
name="brand"
|
||||||
|
label={LL.BRAND()}
|
||||||
|
style={{ minWidth: '48%' }}
|
||||||
|
variant="outlined"
|
||||||
|
value={selectedDeviceBrand}
|
||||||
|
onChange={(e) => setSelectedDeviceBrand(e.target.value)}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<TextField
|
<TextField
|
||||||
name="device"
|
name="device"
|
||||||
@@ -546,27 +570,22 @@ const Customizations = () => {
|
|||||||
const renderDeviceData = () => {
|
const renderDeviceData = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box color="warning.main">
|
<Typography sx={{ mt: 1, mb: 1 }} color="warning" variant="body2">
|
||||||
<Typography variant="body2" mt={1} mb={1}>
|
|
||||||
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
|
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
|
||||||
|
|
||||||
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
|
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
|
||||||
|
|
||||||
<OptionIcon type="api_mqtt_exclude" isSet={true} />=
|
<OptionIcon type="api_mqtt_exclude" isSet={true} />=
|
||||||
{LL.CUSTOMIZATIONS_HELP_4()}
|
{LL.CUSTOMIZATIONS_HELP_4()}
|
||||||
<OptionIcon type="web_exclude" isSet={true} />=
|
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
|
||||||
{LL.CUSTOMIZATIONS_HELP_5()}
|
|
||||||
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
|
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
mb={1}
|
|
||||||
mt={0}
|
|
||||||
spacing={2}
|
spacing={2}
|
||||||
direction="row"
|
direction="row"
|
||||||
justifyContent="flex-start"
|
sx={{ mb: 1, mt: 0, justifyContent: 'flex-start', alignItems: 'center' }}
|
||||||
alignItems="center"
|
|
||||||
>
|
>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -755,8 +774,8 @@ const Customizations = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</MessageBox>
|
</MessageBox>
|
||||||
) : (
|
) : (
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
{numChanges !== 0 && (
|
{numChanges !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ interface LabelValueProps {
|
|||||||
|
|
||||||
const LabelValue = memo(({ label, value }: LabelValueProps) => (
|
const LabelValue = memo(({ label, value }: LabelValueProps) => (
|
||||||
<Grid container direction="row">
|
<Grid container direction="row">
|
||||||
<Typography variant="body2" color="warning.main">
|
<Typography variant="body2" color="warning">
|
||||||
{label}:
|
{label}:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">{value}</Typography>
|
<Typography variant="body2">{value}</Typography>
|
||||||
@@ -131,7 +131,7 @@ const CustomizationsDialog = ({
|
|||||||
/>
|
/>
|
||||||
<LabelValue label={LL.WRITEABLE()} value={writeableIcon} />
|
<LabelValue label={LL.WRITEABLE()} value={writeableIcon} />
|
||||||
|
|
||||||
<Box mt={1} mb={2}>
|
<Box sx={{ mt: 1, mb: 2 }}>
|
||||||
<EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} />
|
<EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ const CustomizationsDialog = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<Typography variant="body2" color="error" mt={2}>
|
<Typography sx={{ mt: 2 }} variant="body2" color="error">
|
||||||
Error: Check min and max values
|
Error: Check min and max values
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { toast } from 'react-toastify';
|
|||||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
|
import HelpOutlineIcon from '@mui/icons-material/HelpOutlined';
|
||||||
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
|
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
|
||||||
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
|
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
|
||||||
import {
|
import {
|
||||||
@@ -262,12 +262,8 @@ const Dashboard = memo(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!data.connected && (
|
|
||||||
<MessageBox level="error" message={LL.EMS_BUS_WARNING()} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{data.connected && data.nodes.length > 0 && !hasFavEntities && (
|
{data.connected && data.nodes.length > 0 && !hasFavEntities && (
|
||||||
<MessageBox mb={2} level="warning">
|
<MessageBox sx={{ mb: 2 }} level="warning">
|
||||||
<Typography>
|
<Typography>
|
||||||
{LL.NO_DATA_1()}
|
{LL.NO_DATA_1()}
|
||||||
<Link to="/customizations" style={{ color: 'white' }}>
|
<Link to="/customizations" style={{ color: 'white' }}>
|
||||||
@@ -284,10 +280,12 @@ const Dashboard = memo(() => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
sx={{
|
||||||
justifyContent="flex-end"
|
display: 'flex',
|
||||||
flexWrap="nowrap"
|
justifyContent: 'flex-end',
|
||||||
whiteSpace="nowrap"
|
flexWrap: 'nowrap',
|
||||||
|
whiteSpace: 'nowrap'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
size="small"
|
size="small"
|
||||||
@@ -310,7 +308,7 @@ const Dashboard = memo(() => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{data.nodes.length > 0 ? (
|
{data.nodes.length > 0 ? (
|
||||||
<Box mt={1} justifyContent="center" flexDirection="column">
|
<Box sx={{ mt: 1, justifyContent: 'center', flexDirection: 'column' }}>
|
||||||
<IconContext.Provider
|
<IconContext.Provider
|
||||||
value={{
|
value={{
|
||||||
color: 'lightblue',
|
color: 'lightblue',
|
||||||
@@ -377,13 +375,8 @@ const Dashboard = memo(() => {
|
|||||||
</IconContext.Provider>
|
</IconContext.Provider>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Box sx={{ display: 'flex' }}>
|
||||||
display="flex"
|
<Typography sx={{ mt: 1 }} color="warning" variant="body1">
|
||||||
// justifyContent="flex-end"
|
|
||||||
// flexWrap="nowrap"
|
|
||||||
// whiteSpace="nowrap"
|
|
||||||
>
|
|
||||||
<Typography mt={1} color="warning.main" variant="body1">
|
|
||||||
no data
|
no data
|
||||||
</Typography>
|
</Typography>
|
||||||
<Tooltip title={LL.DASHBOARD_1()}>
|
<Tooltip title={LL.DASHBOARD_1()}>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
import type { IconType } from 'react-icons';
|
||||||
import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai';
|
import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai';
|
||||||
import { CgSmartHomeBoiler } from 'react-icons/cg';
|
import { CgSmartHomeBoiler } from 'react-icons/cg';
|
||||||
import { FaSolarPanel } from 'react-icons/fa';
|
import { FaSolarPanel } from 'react-icons/fa';
|
||||||
@@ -15,14 +16,9 @@ import { PiFan, PiGauge } from 'react-icons/pi';
|
|||||||
import { TiFlowSwitch, TiThermometer } from 'react-icons/ti';
|
import { TiFlowSwitch, TiThermometer } from 'react-icons/ti';
|
||||||
import { VscVmConnect } from 'react-icons/vsc';
|
import { VscVmConnect } from 'react-icons/vsc';
|
||||||
|
|
||||||
import type { SvgIconProps } from '@mui/material';
|
|
||||||
|
|
||||||
import { DeviceType } from './types';
|
import { DeviceType } from './types';
|
||||||
|
|
||||||
const deviceIconLookup: Record<
|
const deviceIconLookup: Record<DeviceType, IconType | null> = {
|
||||||
DeviceType,
|
|
||||||
React.ComponentType<SvgIconProps> | null
|
|
||||||
> = {
|
|
||||||
[DeviceType.TEMPERATURESENSOR]: TiThermometer,
|
[DeviceType.TEMPERATURESENSOR]: TiThermometer,
|
||||||
[DeviceType.ANALOGSENSOR]: PiGauge,
|
[DeviceType.ANALOGSENSOR]: PiGauge,
|
||||||
[DeviceType.BOILER]: CgSmartHomeBoiler,
|
[DeviceType.BOILER]: CgSmartHomeBoiler,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
useState
|
useState
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { IconContext } from 'react-icons';
|
import { IconContext } from 'react-icons';
|
||||||
import { useNavigate } from 'react-router';
|
import { Link, useNavigate } from 'react-router';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
||||||
@@ -534,9 +534,19 @@ const Devices = memo(() => {
|
|||||||
const renderCoreData = () => (
|
const renderCoreData = () => (
|
||||||
<>
|
<>
|
||||||
{!coreData.connected ? (
|
{!coreData.connected ? (
|
||||||
<MessageBox level="error" message={LL.EMS_BUS_WARNING()} />
|
<MessageBox level="error" message={LL.EMS_BUS_WARNING() + '.'}>
|
||||||
|
(
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
to="https://docs.emsesp.org/Troubleshooting#ems-bus-is-not-connecting"
|
||||||
|
style={{ color: 'white' }}
|
||||||
|
>
|
||||||
|
{LL.ONLINE_HELP()}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
</MessageBox>
|
||||||
) : (
|
) : (
|
||||||
<Box justifyContent="center" flexDirection="column">
|
<Box sx={{ justifyContent: 'center', flexDirection: 'column' }}>
|
||||||
<IconContext.Provider
|
<IconContext.Provider
|
||||||
value={{
|
value={{
|
||||||
color: 'lightblue',
|
color: 'lightblue',
|
||||||
@@ -660,12 +670,12 @@ const Devices = memo(() => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ p: 1 }}>
|
<Box sx={{ p: 1 }}>
|
||||||
<Grid container justifyContent="space-between">
|
<Grid container sx={{ justifyContent: 'space-between' }}>
|
||||||
<Typography noWrap variant="subtitle1" color="warning.main">
|
<Typography noWrap variant="subtitle1" color="warning">
|
||||||
{deviceInfo.n} (
|
{deviceInfo.n} (
|
||||||
{deviceInfo.tn})
|
{deviceInfo.tn})
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid justifyContent="flex-end">
|
<Grid sx={{ justifyContent: 'flex-end' }}>
|
||||||
<ButtonTooltip title={LL.CLOSE()}>
|
<ButtonTooltip title={LL.CLOSE()}>
|
||||||
<IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}>
|
<IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}>
|
||||||
<HighlightOffIcon color="primary" sx={{ fontSize: 18 }} />
|
<HighlightOffIcon color="primary" sx={{ fontSize: 18 }} />
|
||||||
|
|||||||
@@ -128,9 +128,9 @@ const DevicesDialog = ({
|
|||||||
<Dialog sx={dialogStyle} open={open} onClose={onClose}>
|
<Dialog sx={dialogStyle} open={open} onClose={onClose}>
|
||||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
<DialogTitle>{dialogTitle}</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Box color="warning.main" mb={2}>
|
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
|
||||||
<Typography variant="body2">{editItem.id.slice(2)}</Typography>
|
{editItem.id.slice(2)}
|
||||||
</Box>
|
</Typography>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
{editItem.l ? (
|
{editItem.l ? (
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
|
Grid,
|
||||||
Link,
|
Link,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
@@ -42,7 +43,7 @@ interface CustomSupport {
|
|||||||
html: string | null;
|
html: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_IMAGE_URL = 'https://emsesp.org/_media/images/installer.jpeg';
|
const DEFAULT_IMAGE_URL = 'https://emsesp.org/media/images/installer.jpeg';
|
||||||
|
|
||||||
const SUPPORT_BOX_STYLES: SxProps<Theme> = {
|
const SUPPORT_BOX_STYLES: SxProps<Theme> = {
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
@@ -71,7 +72,6 @@ const HelpComponent = () => {
|
|||||||
});
|
});
|
||||||
const [imgError, setImgError] = useState<boolean>(false);
|
const [imgError, setImgError] = useState<boolean>(false);
|
||||||
|
|
||||||
// Memoize the request method to prevent re-creation on every render
|
|
||||||
const getCustomSupportMethod = useMemo(
|
const getCustomSupportMethod = useMemo(
|
||||||
() => callAction({ action: 'getCustomSupport' }),
|
() => callAction({ action: 'getCustomSupport' }),
|
||||||
[]
|
[]
|
||||||
@@ -120,7 +120,7 @@ const HelpComponent = () => {
|
|||||||
label: () => LL.HELP_INFORMATION_1()
|
label: () => LL.HELP_INFORMATION_1()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: 'https://discord.gg/3J3GgnzpyT',
|
href: 'https://discord.gg/GP9DPSgeJq',
|
||||||
icon: <CommentIcon />,
|
icon: <CommentIcon />,
|
||||||
label: () => LL.HELP_INFORMATION_2()
|
label: () => LL.HELP_INFORMATION_2()
|
||||||
},
|
},
|
||||||
@@ -146,11 +146,9 @@ const HelpComponent = () => {
|
|||||||
<SectionContent>
|
<SectionContent>
|
||||||
{customSupport.html && (
|
{customSupport.html && (
|
||||||
<Stack
|
<Stack
|
||||||
padding={1}
|
|
||||||
mb={2}
|
|
||||||
direction="row"
|
direction="row"
|
||||||
divider={<Divider orientation="vertical" flexItem />}
|
divider={<Divider orientation="vertical" flexItem />}
|
||||||
sx={SUPPORT_BOX_STYLES}
|
sx={{ padding: 1, mb: 2, ...SUPPORT_BOX_STYLES }}
|
||||||
>
|
>
|
||||||
<Typography variant="subtitle1">
|
<Typography variant="subtitle1">
|
||||||
<div dangerouslySetInnerHTML={{ __html: customSupport.html }} />
|
<div dangerouslySetInnerHTML={{ __html: customSupport.html }} />
|
||||||
@@ -185,9 +183,9 @@ const HelpComponent = () => {
|
|||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box p={2} color="warning.main">
|
<Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
|
||||||
<Typography mb={1} variant="body1">
|
<Typography sx={{ mb: 1 }} color="warning" variant="body1">
|
||||||
{LL.HELP_INFORMATION_4()}.
|
{LL.HELP_INFORMATION_4()}:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
@@ -197,11 +195,11 @@ const HelpComponent = () => {
|
|||||||
>
|
>
|
||||||
{LL.SUPPORT_INFORMATION(0)}
|
{LL.SUPPORT_INFORMATION(0)}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Grid>
|
||||||
|
|
||||||
<Divider sx={{ mt: 4 }} />
|
<Divider sx={{ mt: 4 }} />
|
||||||
|
|
||||||
<Typography color="white" variant="subtitle1" align="center" mt={1}>
|
<Typography color="white" variant="subtitle1" align="center" sx={{ mt: 1 }}>
|
||||||
©
|
©
|
||||||
<Link
|
<Link
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -186,9 +186,9 @@ const Modules = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box mb={2} color="warning.main">
|
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.MODULES_DESCRIPTION()}.</Typography>
|
{LL.MODULES_DESCRIPTION()}.
|
||||||
</Box>
|
</Typography>
|
||||||
<Table
|
<Table
|
||||||
data={{ nodes: modules }}
|
data={{ nodes: modules }}
|
||||||
theme={modules_theme}
|
theme={modules_theme}
|
||||||
@@ -236,8 +236,8 @@ const Modules = () => {
|
|||||||
)}
|
)}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<Box mt={1} display="flex" flexWrap="wrap">
|
<Box sx={{ mt: 1, display: 'flex', flexWrap: 'wrap' }}>
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
{numChanges !== 0 && (
|
{numChanges !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ const ModulesDialog = ({
|
|||||||
label="Enabled"
|
label="Enabled"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Box mt={2} mb={1}>
|
<Box sx={{ mt: 2, mb: 1 }}>
|
||||||
<TextField
|
<TextField
|
||||||
name="license"
|
name="license"
|
||||||
label="License Key"
|
label="License Key"
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { memo } from 'react';
|
|||||||
|
|
||||||
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
||||||
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
||||||
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
|
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutlined';
|
||||||
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
||||||
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
||||||
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
|
||||||
import StarIcon from '@mui/icons-material/Star';
|
import StarIcon from '@mui/icons-material/Star';
|
||||||
import StarOutlineIcon from '@mui/icons-material/StarOutline';
|
import StarOutlineIcon from '@mui/icons-material/StarOutlined';
|
||||||
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
|
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
|
||||||
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
|
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
|
||||||
import type { SvgIconProps } from '@mui/material';
|
import type { SvgIconProps } from '@mui/material';
|
||||||
|
|||||||
@@ -358,9 +358,9 @@ const Scheduler = () => {
|
|||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<Box mb={2} color="warning.main">
|
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.SCHEDULER_HELP_1()}.</Typography>
|
{LL.SCHEDULER_HELP_1()}.
|
||||||
</Box>
|
</Typography>
|
||||||
{renderSchedule()}
|
{renderSchedule()}
|
||||||
|
|
||||||
{selectedScheduleItem && (
|
{selectedScheduleItem && (
|
||||||
@@ -375,8 +375,8 @@ const Scheduler = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
{numChanges !== 0 && (
|
{numChanges !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
@@ -398,7 +398,7 @@ const Scheduler = () => {
|
|||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<AddIcon />}
|
startIcon={<AddIcon />}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import AddIcon from '@mui/icons-material/Add';
|
|||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import DoneIcon from '@mui/icons-material/Done';
|
import DoneIcon from '@mui/icons-material/Done';
|
||||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@@ -338,11 +338,13 @@ const SchedulerDialog = ({
|
|||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
/>
|
/>
|
||||||
{isTimerSchedule && (
|
{isTimerSchedule && (
|
||||||
<Box color="warning.main" ml={2} mt={4}>
|
<Typography
|
||||||
<Typography variant="body2">
|
sx={{ ml: 2, mt: 4 }}
|
||||||
|
color="warning"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
{LL.SCHEDULER_HELP_2()}
|
{LL.SCHEDULER_HELP_2()}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -391,7 +393,7 @@ const SchedulerDialog = ({
|
|||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
{!creating && (
|
{!creating && (
|
||||||
<Box flexGrow={1}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<RemoveIcon />}
|
startIcon={<RemoveIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ const Sensors = () => {
|
|||||||
|
|
||||||
const addAnalogSensor = useCallback(() => {
|
const addAnalogSensor = useCallback(() => {
|
||||||
if (firstAvailableGPIO.current === undefined) {
|
if (firstAvailableGPIO.current === undefined) {
|
||||||
toast.error('No available GPIO found');
|
toast.error(LL.NO_GPIO());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setCreating(true);
|
setCreating(true);
|
||||||
@@ -591,7 +591,14 @@ const Sensors = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sensorData?.analog_enabled === true && me.admin && (
|
{sensorData?.analog_enabled === true && me.admin && (
|
||||||
<Box mt={2} display="flex" flexWrap="wrap" justifyContent="flex-end">
|
<Box
|
||||||
|
sx={{
|
||||||
|
mt: 2,
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
justifyContent: 'flex-end'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import DoneIcon from '@mui/icons-material/Done';
|
import DoneIcon from '@mui/icons-material/Done';
|
||||||
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
|
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -479,7 +479,7 @@ const SensorsAnalogDialog = ({
|
|||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
{fieldErrors && Object.keys(fieldErrors).length > 0 && (
|
{fieldErrors && Object.keys(fieldErrors).length > 0 && (
|
||||||
<Box mt={1}>
|
<Box sx={{ mt: 1 }}>
|
||||||
{Object.values(fieldErrors).map((errArr, idx) =>
|
{Object.values(fieldErrors).map((errArr, idx) =>
|
||||||
Array.isArray(errArr)
|
Array.isArray(errArr)
|
||||||
? errArr.map((err, j) => (
|
? errArr.map((err, j) => (
|
||||||
@@ -487,7 +487,7 @@ const SensorsAnalogDialog = ({
|
|||||||
key={`${idx}-${j}`}
|
key={`${idx}-${j}`}
|
||||||
color="error"
|
color="error"
|
||||||
variant="caption"
|
variant="caption"
|
||||||
display="block"
|
sx={{ display: 'block' }}
|
||||||
>
|
>
|
||||||
{err.message}
|
{err.message}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -498,7 +498,7 @@ const SensorsAnalogDialog = ({
|
|||||||
)}
|
)}
|
||||||
{editItem.s && (
|
{editItem.s && (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Typography mt={1} color="warning.main" variant="body2">
|
<Typography sx={{ mt: 1 }} color="warning" variant="body2">
|
||||||
<WarningIcon
|
<WarningIcon
|
||||||
fontSize="small"
|
fontSize="small"
|
||||||
sx={{ mr: 1, verticalAlign: 'middle' }}
|
sx={{ mr: 1, verticalAlign: 'middle' }}
|
||||||
@@ -511,7 +511,7 @@ const SensorsAnalogDialog = ({
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
{!creating && (
|
{!creating && (
|
||||||
<Box flexGrow={1} sx={{ '& button': { mt: 0 } }}>
|
<Box sx={{ flexGrow: 1, '& button': { mt: 0 } }}>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<RemoveIcon />}
|
startIcon={<RemoveIcon />}
|
||||||
disabled={editItem.s}
|
disabled={editItem.s}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import CancelIcon from '@mui/icons-material/Cancel';
|
|||||||
import DoneIcon from '@mui/icons-material/Done';
|
import DoneIcon from '@mui/icons-material/Done';
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
@@ -111,11 +110,9 @@ const SensorsTemperatureDialog = ({
|
|||||||
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
||||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
<DialogTitle>{dialogTitle}</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Box color="warning.main" mb={2}>
|
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
|
||||||
<Typography variant="body2">
|
|
||||||
{LL.ID_OF(LL.SENSOR(0))}: {editItem.id}
|
{LL.ID_OF(LL.SENSOR(0))}: {editItem.id}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
@@ -142,7 +139,7 @@ const SensorsTemperatureDialog = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
{editItem.s && (
|
{editItem.s && (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Typography mt={1} color="warning.main" variant="body2">
|
<Typography sx={{ mt: 1 }} color="warning" variant="body2">
|
||||||
<WarningIcon
|
<WarningIcon
|
||||||
fontSize="small"
|
fontSize="small"
|
||||||
sx={{ mr: 1, verticalAlign: 'middle' }}
|
sx={{ mr: 1, verticalAlign: 'middle' }}
|
||||||
|
|||||||
@@ -41,8 +41,12 @@ const UserProfileComponent = () => {
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
<Box mt={2} mb={2} display="flex" alignItems="center">
|
<Box sx={{ mt: 2, mb: 2, display: 'flex', alignItems: 'center' }}>
|
||||||
<Typography mr={2} variant="body1" align="center">
|
<Typography
|
||||||
|
sx={{ mr: 2, textAlign: 'center' }}
|
||||||
|
color="warning"
|
||||||
|
variant="body1"
|
||||||
|
>
|
||||||
{LL.LANGUAGE()}:
|
{LL.LANGUAGE()}:
|
||||||
</Typography>
|
</Typography>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
|
|||||||
@@ -43,6 +43,16 @@ export interface Settings {
|
|||||||
modbus_port: number;
|
modbus_port: number;
|
||||||
modbus_max_clients: number;
|
modbus_max_clients: number;
|
||||||
modbus_timeout: number;
|
modbus_timeout: number;
|
||||||
|
email_enabled: boolean;
|
||||||
|
email_ssl?: boolean;
|
||||||
|
email_starttls?: boolean;
|
||||||
|
email_server: string;
|
||||||
|
email_port: number;
|
||||||
|
email_login: string;
|
||||||
|
email_pass: string;
|
||||||
|
email_sender: string;
|
||||||
|
email_recp: string;
|
||||||
|
email_subject: string;
|
||||||
developer_mode: boolean;
|
developer_mode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
FormLoader,
|
FormLoader,
|
||||||
MessageBox,
|
MessageBox,
|
||||||
SectionContent,
|
SectionContent,
|
||||||
|
ValidatedPasswordField,
|
||||||
ValidatedTextField,
|
ValidatedTextField,
|
||||||
useLayoutTitle
|
useLayoutTitle
|
||||||
} from 'components';
|
} from 'components';
|
||||||
@@ -351,6 +352,156 @@ const ApplicationSettings = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
<Typography color="secondary">eMail</Typography>
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.email_enabled}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
name="email_enabled"
|
||||||
|
disabled={!hardwareData.psram}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography color={!hardwareData.psram ? 'grey' : 'default'}>
|
||||||
|
Enable eMail notification
|
||||||
|
{!hardwareData.psram && (
|
||||||
|
<Typography variant="caption">
|
||||||
|
({LL.IS_REQUIRED('PSRAM')})
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{data.email_enabled && (
|
||||||
|
<>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={2}
|
||||||
|
direction="row"
|
||||||
|
justifyContent="flex-start"
|
||||||
|
alignItems="flex-start"
|
||||||
|
>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_server"
|
||||||
|
label="SMTP Server"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_server}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
sx={{ width: '12ch' }}
|
||||||
|
name="email_port"
|
||||||
|
variant="outlined"
|
||||||
|
label="Port"
|
||||||
|
value={numberValue(data.email_port)}
|
||||||
|
type="number"
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={4} mt={!data.email_ssl && !data.email_starttls ? 0 : 3}>
|
||||||
|
{!data.email_starttls && (
|
||||||
|
<BlockFormControlLabel
|
||||||
|
sx={{ width: '12ch' }}
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.email_ssl}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
name="email_ssl"
|
||||||
|
disabled={
|
||||||
|
data.email_starttls || data.email_ssl === undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="SSL/TLS"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!data.email_ssl && (
|
||||||
|
<BlockFormControlLabel
|
||||||
|
sx={{ width: '12ch' }}
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.email_starttls}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
name="email_starttls"
|
||||||
|
disabled={
|
||||||
|
data.email_ssl || data.email_starttls === undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="STARTTLS"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_login"
|
||||||
|
label="Login"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_login}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedPasswordField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_pass"
|
||||||
|
label="Password"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_pass}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_sender"
|
||||||
|
label="From"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_sender}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_recp"
|
||||||
|
label="To"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_recp}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_subject"
|
||||||
|
label="Subject"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_subject}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
||||||
{LL.SENSORS()}
|
{LL.SENSORS()}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -771,7 +922,7 @@ const ApplicationSettings = () => {
|
|||||||
label={LL.REMOTE_TIMEOUT_EN()}
|
label={LL.REMOTE_TIMEOUT_EN()}
|
||||||
/>
|
/>
|
||||||
{data.remote_timeout_en && (
|
{data.remote_timeout_en && (
|
||||||
<Box mt={2}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors || {}}
|
fieldErrors={fieldErrors || {}}
|
||||||
name="remote_timeout"
|
name="remote_timeout"
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||||
import { Box, Button, Grid, Typography } from '@mui/material';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Grid,
|
||||||
|
Typography
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
import { API, callAction } from 'api/app';
|
import { API, callAction } from 'api/app';
|
||||||
|
|
||||||
|
import { dialogStyle } from '@/CustomTheme';
|
||||||
import { useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
import type { APIcall } from 'app/main/types';
|
import type { APIcall } from 'app/main/types';
|
||||||
import SystemMonitor from 'app/status/SystemMonitor';
|
import SystemMonitor from 'app/status/SystemMonitor';
|
||||||
@@ -19,16 +30,11 @@ import {
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { saveFile } from 'utils';
|
import { saveFile } from 'utils';
|
||||||
|
|
||||||
interface DownloadButton {
|
|
||||||
key: string;
|
|
||||||
type: string;
|
|
||||||
label: string | number;
|
|
||||||
isGridButton: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DownloadUpload = () => {
|
const DownloadUpload = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
const [confirmBackup, setConfirmBackup] = useState<boolean>(false);
|
||||||
|
|
||||||
const [restarting, setRestarting] = useState<boolean>(false);
|
const [restarting, setRestarting] = useState<boolean>(false);
|
||||||
|
|
||||||
const { send: sendExportData } = useRequest(
|
const { send: sendExportData } = useRequest(
|
||||||
@@ -63,45 +69,50 @@ const DownloadUpload = () => {
|
|||||||
|
|
||||||
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
|
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
|
||||||
|
|
||||||
const downloadButtons: DownloadButton[] = useMemo(
|
const handleCloseBackupDialog = useCallback(() => {
|
||||||
() => [
|
setConfirmBackup(false);
|
||||||
{
|
}, []);
|
||||||
key: 'settings',
|
|
||||||
type: 'settings',
|
const renderBackupDialog = useMemo(
|
||||||
label: LL.SETTINGS_OF(LL.APPLICATION()),
|
() => (
|
||||||
isGridButton: true
|
<Dialog
|
||||||
},
|
sx={dialogStyle}
|
||||||
{
|
open={confirmBackup}
|
||||||
key: 'customizations',
|
onClose={handleCloseBackupDialog}
|
||||||
type: 'customizations',
|
>
|
||||||
label: LL.CUSTOMIZATIONS(),
|
<DialogTitle>{LL.DOWNLOAD_SYSTEM_BACKUP()}</DialogTitle>
|
||||||
isGridButton: true
|
<DialogContent dividers>
|
||||||
},
|
<WarningIcon color="warning" sx={{ fontSize: 18 }} />
|
||||||
{
|
|
||||||
key: 'entities',
|
{LL.WARNING_SYSTEM_BACKUP()}
|
||||||
type: 'entities',
|
</DialogContent>
|
||||||
label: LL.CUSTOM_ENTITIES(0),
|
<DialogActions>
|
||||||
isGridButton: true
|
<Button
|
||||||
},
|
startIcon={<CancelIcon />}
|
||||||
{
|
variant="outlined"
|
||||||
key: 'schedule',
|
onClick={handleCloseBackupDialog}
|
||||||
type: 'schedule',
|
color="secondary"
|
||||||
label: LL.SCHEDULE(0),
|
>
|
||||||
isGridButton: true
|
{LL.CANCEL()}
|
||||||
},
|
</Button>
|
||||||
{
|
<Button
|
||||||
key: 'allvalues',
|
startIcon={<DownloadIcon />}
|
||||||
type: 'allvalues',
|
variant="outlined"
|
||||||
label: LL.ALLVALUES(),
|
onClick={() => handleDownload('systembackup')()}
|
||||||
isGridButton: false
|
color="primary"
|
||||||
}
|
>
|
||||||
],
|
{LL.DOWNLOAD(0)}
|
||||||
[LL]
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
),
|
||||||
|
[confirmBackup, handleCloseBackupDialog, LL]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDownload = useCallback(
|
const handleDownload = useCallback(
|
||||||
(type: string) => () => {
|
(type: string) => () => {
|
||||||
void sendExportData(type);
|
void sendExportData(type);
|
||||||
|
setConfirmBackup(false);
|
||||||
},
|
},
|
||||||
[sendExportData]
|
[sendExportData]
|
||||||
);
|
);
|
||||||
@@ -118,58 +129,57 @@ const DownloadUpload = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const gridButtons = downloadButtons.filter((btn) => btn.isGridButton);
|
|
||||||
const standaloneButton = downloadButtons.find((btn) => !btn.isGridButton);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContent>
|
<SectionContent>
|
||||||
|
{renderBackupDialog}
|
||||||
|
|
||||||
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
|
||||||
{LL.DOWNLOAD(0)}
|
{LL.DOWNLOAD(0)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography mb={1} variant="body1" color="warning">
|
<Grid
|
||||||
{LL.DOWNLOAD_SETTINGS_TEXT()}.
|
container
|
||||||
|
spacing={2}
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body1" color="warning">
|
||||||
|
{LL.DOWNLOAD_SETTINGS_TEXT()}:
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Grid container spacing={2}>
|
|
||||||
{gridButtons.map((button) => (
|
|
||||||
<Grid key={button.key}>
|
|
||||||
<Button
|
<Button
|
||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleDownload(button.type)}
|
onClick={() => setConfirmBackup(true)}
|
||||||
>
|
>
|
||||||
{button.label}
|
{LL.DOWNLOAD_SYSTEM_BACKUP()}
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Typography mt={2} mb={1} variant="body1" color="warning">
|
<Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
|
||||||
{LL.DOWNLOAD_SETTINGS_TEXT2()}.
|
<Typography variant="body1" color="warning">
|
||||||
|
{LL.DOWNLOAD_SETTINGS_TEXT2()}:
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{standaloneButton && (
|
|
||||||
<Button
|
<Button
|
||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleDownload(standaloneButton.type)}
|
onClick={handleDownload('allvalues')}
|
||||||
>
|
>
|
||||||
{standaloneButton.label}
|
{LL.ALLVALUES()}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</Grid>
|
||||||
|
|
||||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||||
{LL.UPLOAD()}
|
{LL.UPLOAD()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Box color="warning.main" sx={{ pb: 2 }}>
|
<Typography sx={{ pb: 2 }} color="warning" variant="body1">
|
||||||
<Typography variant="body1">{LL.UPLOAD_TEXT()}.</Typography>
|
{LL.UPLOAD_TEXT()}:
|
||||||
</Box>
|
</Typography>
|
||||||
|
|
||||||
<SingleUpload text={LL.UPLOAD_DRAG()} doRestart={doRestart} />
|
<SingleUpload doRestart={doRestart} />
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ const MqttSettings = () => {
|
|||||||
<SectionContent>
|
<SectionContent>
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||||
<>
|
<>
|
||||||
<Box display="flex" gap={2} mb={1}>
|
<Box sx={{ display: 'flex', gap: 2, mb: 1 }}>
|
||||||
<BlockFormControlLabel
|
<BlockFormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
@@ -193,9 +193,9 @@ const NTPSettings = () => {
|
|||||||
{timeZoneItems}
|
{timeZoneItems}
|
||||||
</ValidatedTextField>
|
</ValidatedTextField>
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
{!data.enabled && !dirtyFlags.length && (
|
{!data.enabled && !dirtyFlags.length && (
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
onClick={openSetTime}
|
onClick={openSetTime}
|
||||||
@@ -259,9 +259,9 @@ const NTPSettings = () => {
|
|||||||
<Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}>
|
<Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}>
|
||||||
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
|
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={0} mb={2}>
|
<Typography color="warning" variant="body2">
|
||||||
<Typography variant="body2">{LL.SET_TIME_TEXT()}</Typography>
|
{LL.SET_TIME_TEXT()}
|
||||||
</Box>
|
</Typography>
|
||||||
<TextField
|
<TextField
|
||||||
label={LL.LOCAL_TIME(0)}
|
label={LL.LOCAL_TIME(0)}
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
|
|||||||
@@ -156,11 +156,13 @@ const Settings = () => {
|
|||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
mt={2}
|
sx={{
|
||||||
display="flex"
|
mt: 2,
|
||||||
justifyContent="flex-end"
|
display: 'flex',
|
||||||
flexWrap="nowrap"
|
justifyContent: 'flex-end',
|
||||||
whiteSpace="nowrap"
|
flexWrap: 'nowrap',
|
||||||
|
whiteSpace: 'nowrap'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<SettingsBackupRestoreIcon />}
|
startIcon={<SettingsBackupRestoreIcon />}
|
||||||
|
|||||||
@@ -54,19 +54,27 @@ const GenerateToken = ({ username, onClose }: GenerateTokenProps) => {
|
|||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
{token ? (
|
{token ? (
|
||||||
<>
|
<>
|
||||||
<MessageBox message={LL.ACCESS_TOKEN_TEXT()} level="info" my={2} />
|
<MessageBox
|
||||||
<Box mt={2} mb={2}>
|
message={LL.ACCESS_TOKEN_TEXT()}
|
||||||
|
level="info"
|
||||||
|
sx={{ mt: 2, mb: 2 }}
|
||||||
|
/>
|
||||||
|
<Box sx={{ mt: 2, mb: 2 }}>
|
||||||
<TextField
|
<TextField
|
||||||
label="Token"
|
label="Token"
|
||||||
multiline
|
multiline
|
||||||
value={token.token}
|
value={token.token}
|
||||||
fullWidth
|
fullWidth
|
||||||
contentEditable={false}
|
slotProps={{
|
||||||
|
input: {
|
||||||
|
readOnly: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Box m={4} textAlign="center">
|
<Box sx={{ m: 4, textAlign: 'center' }}>
|
||||||
<LinearProgress />
|
<LinearProgress />
|
||||||
<Typography variant="h6">{LL.GENERATING_TOKEN()}…</Typography>
|
<Typography variant="h6">{LL.GENERATING_TOKEN()}…</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -240,12 +240,16 @@ const ManageUsers = () => {
|
|||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
{noAdminConfigured() && (
|
{noAdminConfigured() && (
|
||||||
<MessageBox level="warning" message={LL.USER_WARNING()} my={2} />
|
<MessageBox
|
||||||
|
level="warning"
|
||||||
|
message={LL.USER_WARNING()}
|
||||||
|
sx={{ mt: 2, mb: 2 }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
{changed !== 0 && (
|
{changed !== 0 && (
|
||||||
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
|
<Box sx={{ flexGrow: 1, '& button': { mt: 2 } }}>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<CancelIcon />}
|
startIcon={<CancelIcon />}
|
||||||
@@ -270,7 +274,7 @@ const ManageUsers = () => {
|
|||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<PersonAddIcon />}
|
startIcon={<PersonAddIcon />}
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ import type { LogEntry, LogSettings } from 'types';
|
|||||||
import { LogLevel } from 'types';
|
import { LogLevel } from 'types';
|
||||||
import { updateValueDirty, useRest } from 'utils';
|
import { updateValueDirty, useRest } from 'utils';
|
||||||
|
|
||||||
const MAX_LOG_ENTRIES = 1000; // Limit log entries to prevent memory issues
|
|
||||||
|
|
||||||
const TextColors: Record<LogLevel, string> = {
|
const TextColors: Record<LogLevel, string> = {
|
||||||
[LogLevel.ERROR]: '#ff0000', // red
|
[LogLevel.ERROR]: '#ff0000', // red
|
||||||
[LogLevel.WARNING]: '#ff0000', // red
|
[LogLevel.WARNING]: '#ff0000', // red
|
||||||
@@ -200,10 +198,6 @@ const SystemLog = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const newLog = [...log, logentry];
|
const newLog = [...log, logentry];
|
||||||
// Limit log entries to prevent memory issues - only slice when necessary
|
|
||||||
if (newLog.length > MAX_LOG_ENTRIES) {
|
|
||||||
return newLog.slice(-MAX_LOG_ENTRIES);
|
|
||||||
}
|
|
||||||
return newLog;
|
return newLog;
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
@@ -272,7 +266,7 @@ const SystemLog = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid container spacing={2} alignItems="center">
|
<Grid container spacing={2} sx={{ alignItems: 'center' }}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextField
|
<TextField
|
||||||
name="level"
|
name="level"
|
||||||
@@ -308,6 +302,8 @@ const SystemLog = () => {
|
|||||||
<MenuItem value={50}>50</MenuItem>
|
<MenuItem value={50}>50</MenuItem>
|
||||||
<MenuItem value={75}>75</MenuItem>
|
<MenuItem value={75}>75</MenuItem>
|
||||||
<MenuItem value={100}>100</MenuItem>
|
<MenuItem value={100}>100</MenuItem>
|
||||||
|
<MenuItem value={500}>500</MenuItem>
|
||||||
|
<MenuItem value={1000}>1000</MenuItem>
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -60,18 +60,16 @@ const SystemMonitor = () => {
|
|||||||
const { statusMessage, isUploading, progressValue } = useMemo(() => {
|
const { statusMessage, isUploading, progressValue } = useMemo(() => {
|
||||||
const status = data?.status;
|
const status = data?.status;
|
||||||
|
|
||||||
let message = '';
|
const message =
|
||||||
if (status && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING) {
|
status && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING
|
||||||
message = LL.WAIT_FIRMWARE();
|
? LL.WAIT_FIRMWARE()
|
||||||
} else if (status === SystemStatusCodes.SYSTEM_STATUS_PENDING_RESTART) {
|
: status === SystemStatusCodes.SYSTEM_STATUS_PENDING_RESTART
|
||||||
message = LL.APPLICATION_RESTARTING();
|
? LL.APPLICATION_RESTARTING()
|
||||||
} else if (status === SystemStatusCodes.SYSTEM_STATUS_NORMAL) {
|
: status === SystemStatusCodes.SYSTEM_STATUS_NORMAL
|
||||||
message = LL.RESTARTING_PRE();
|
? LL.RESTARTING_PRE()
|
||||||
} else if (status === SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD) {
|
: status === SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD
|
||||||
message = 'Upload Failed';
|
? 'Upload Failed'
|
||||||
} else {
|
: LL.RESTARTING_POST();
|
||||||
message = LL.RESTARTING_POST();
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploading =
|
const uploading =
|
||||||
status !== undefined && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING;
|
status !== undefined && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING;
|
||||||
@@ -120,17 +118,15 @@ const SystemMonitor = () => {
|
|||||||
p: 3
|
p: 3
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box display="flex" alignItems="center" flexDirection="column">
|
<Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
|
||||||
<img
|
<img
|
||||||
src="/app/icon.png"
|
src="/app/icon.png"
|
||||||
alt="EMS-ESP"
|
alt="EMS-ESP"
|
||||||
style={{ width: '40px', height: '40px', marginBottom: '16px' }}
|
style={{ width: '40px', height: '40px', marginBottom: '16px' }}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
color="secondary"
|
sx={{ color: 'secondary', fontWeight: 400, textAlign: 'center' }}
|
||||||
variant="h6"
|
variant="h6"
|
||||||
fontWeight={400}
|
|
||||||
textAlign="center"
|
|
||||||
>
|
>
|
||||||
{statusMessage}
|
{statusMessage}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -150,11 +146,14 @@ const SystemMonitor = () => {
|
|||||||
</MessageBox>
|
</MessageBox>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Typography mt={2} variant="h6" fontWeight={400} textAlign="center">
|
<Typography
|
||||||
|
sx={{ mt: 2, fontWeight: 400, textAlign: 'center' }}
|
||||||
|
variant="h6"
|
||||||
|
>
|
||||||
{LL.PLEASE_WAIT()}…
|
{LL.PLEASE_WAIT()}…
|
||||||
</Typography>
|
</Typography>
|
||||||
{isUploading && (
|
{isUploading && (
|
||||||
<Box width="100%" pl={2} pr={2} py={2}>
|
<Box sx={{ width: '100%', pl: 2, pr: 2, py: 2 }}>
|
||||||
<LinearProgressWithLabel value={progressValue} />
|
<LinearProgressWithLabel value={progressValue} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
useState
|
useState
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { Link } from 'react-router';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
@@ -24,7 +25,6 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
Grid,
|
Grid,
|
||||||
IconButton,
|
IconButton,
|
||||||
Link,
|
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
@@ -274,6 +274,7 @@ const InstallDialog = memo(
|
|||||||
fetchDevVersion,
|
fetchDevVersion,
|
||||||
latestVersion,
|
latestVersion,
|
||||||
latestDevVersion,
|
latestDevVersion,
|
||||||
|
upgradeImportantMessageType,
|
||||||
downloadOnly,
|
downloadOnly,
|
||||||
platform,
|
platform,
|
||||||
LL,
|
LL,
|
||||||
@@ -284,6 +285,7 @@ const InstallDialog = memo(
|
|||||||
fetchDevVersion: boolean;
|
fetchDevVersion: boolean;
|
||||||
latestVersion?: VersionInfo;
|
latestVersion?: VersionInfo;
|
||||||
latestDevVersion?: VersionInfo;
|
latestDevVersion?: VersionInfo;
|
||||||
|
upgradeImportantMessageType: number;
|
||||||
downloadOnly: boolean;
|
downloadOnly: boolean;
|
||||||
platform: string;
|
platform: string;
|
||||||
LL: TranslationFunctions;
|
LL: TranslationFunctions;
|
||||||
@@ -307,12 +309,24 @@ const InstallDialog = memo(
|
|||||||
{`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`}
|
{`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Typography mb={2}>
|
<Typography sx={{ mb: 2 }}>
|
||||||
{LL.INSTALL_VERSION(
|
{LL.INSTALL_VERSION(
|
||||||
downloadOnly ? LL.DOWNLOAD(1) : LL.INSTALL(),
|
downloadOnly ? LL.DOWNLOAD(1) : LL.INSTALL(),
|
||||||
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
|
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{upgradeImportantMessageType === 1 && LL.UPGRADE_IMPORTANT_MESSAGES_1()}
|
||||||
|
{upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()}
|
||||||
|
<Typography sx={{ mt: 2 }}>
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
|
||||||
|
style={{ color: 'lightblue' }}
|
||||||
|
>
|
||||||
|
{LL.ONLINE_HELP()}
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
@@ -329,7 +343,12 @@ const InstallDialog = memo(
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
<Link underline="none" target="_blank" href={binURL} color="primary">
|
<Link
|
||||||
|
to={binURL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
style={{ color: 'lightblue', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
{LL.DOWNLOAD(0)}
|
{LL.DOWNLOAD(0)}
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -371,7 +390,9 @@ const InstallPartitionDialog = memo(
|
|||||||
{LL.INSTALL()} {LL.STORED_VERSIONS()}
|
{LL.INSTALL()} {LL.STORED_VERSIONS()}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<Typography mb={2}>{LL.INSTALL_VERSION(LL.INSTALL(), version)}</Typography>
|
<Typography sx={{ mb: 2 }}>
|
||||||
|
{LL.INSTALL_VERSION(LL.INSTALL(), version)}
|
||||||
|
</Typography>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
@@ -467,6 +488,26 @@ const Version = () => {
|
|||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [upgradeImportantMessageType, setUpgradeImportantMessageType] =
|
||||||
|
useState<number>(0);
|
||||||
|
|
||||||
|
const { send: checkUpgradeImportantMessages } = useRequest(
|
||||||
|
(version: string) =>
|
||||||
|
callAction({ action: 'upgradeImportantMessages', param: version }),
|
||||||
|
{
|
||||||
|
immediate: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onSuccess((event) => {
|
||||||
|
const upgradeImportantMessageType_n = (
|
||||||
|
event.data as { upgradeImportantMessageType: number }
|
||||||
|
).upgradeImportantMessageType;
|
||||||
|
setUpgradeImportantMessageType(upgradeImportantMessageType_n);
|
||||||
|
})
|
||||||
|
.onError((error) => {
|
||||||
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
|
});
|
||||||
|
|
||||||
// Memoized values
|
// Memoized values
|
||||||
const platform = useMemo(() => (data ? getPlatform(data) : ''), [data]);
|
const platform = useMemo(() => (data ? getPlatform(data) : ''), [data]);
|
||||||
|
|
||||||
@@ -532,10 +573,16 @@ const Version = () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const showFirmwareDialog = useCallback((useDevVersion: boolean) => {
|
const showFirmwareDialog = useCallback(
|
||||||
|
(useDevVersion: boolean) => {
|
||||||
setFetchDevVersion(useDevVersion);
|
setFetchDevVersion(useDevVersion);
|
||||||
|
void checkUpgradeImportantMessages(
|
||||||
|
useDevVersion ? latestDevVersion?.name : latestVersion?.name
|
||||||
|
);
|
||||||
setOpenInstallDialog(true);
|
setOpenInstallDialog(true);
|
||||||
}, []);
|
},
|
||||||
|
[latestDevVersion, latestVersion, fetchDevVersion]
|
||||||
|
);
|
||||||
|
|
||||||
const closeInstallDialog = useCallback(() => {
|
const closeInstallDialog = useCallback(() => {
|
||||||
setOpenInstallDialog(false);
|
setOpenInstallDialog(false);
|
||||||
@@ -637,8 +684,8 @@ const Version = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box p={2} border="1px solid grey" borderRadius={2}>
|
<Box sx={{ p: 2, border: '1px solid #565656', borderRadius: 2 }}>
|
||||||
<Typography mb={1} variant="h6" color="primary">
|
<Typography sx={{ mb: 1 }} variant="h6" color="primary">
|
||||||
{LL.THIS_VERSION()}
|
{LL.THIS_VERSION()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
@@ -703,7 +750,7 @@ const Version = () => {
|
|||||||
|
|
||||||
{internetLive ? (
|
{internetLive ? (
|
||||||
<>
|
<>
|
||||||
<Typography mt={4} mb={1} variant="h6" color="primary">
|
<Typography sx={{ mt: 4, mb: 1 }} variant="h6" color="primary">
|
||||||
{LL.AVAILABLE_VERSION()}
|
{LL.AVAILABLE_VERSION()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
@@ -725,7 +772,7 @@ const Version = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={{ xs: 8, md: 10 }}>
|
<Grid size={{ xs: 8, md: 10 }}>
|
||||||
{otherPartitions.map((partition) => (
|
{otherPartitions.map((partition) => (
|
||||||
<Typography key={partition.partition} mb={1}>
|
<Typography key={partition.partition} sx={{ mb: 1 }}>
|
||||||
{partition.version}
|
{partition.version}
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -791,7 +838,7 @@ const Version = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Typography mt={2} color="warning">
|
<Typography sx={{ mt: 2 }} color="warning">
|
||||||
<WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} />
|
<WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} />
|
||||||
{LL.INTERNET_CONNECTION_REQUIRED()}
|
{LL.INTERNET_CONNECTION_REQUIRED()}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -815,6 +862,7 @@ const Version = () => {
|
|||||||
fetchDevVersion={fetchDevVersion}
|
fetchDevVersion={fetchDevVersion}
|
||||||
latestVersion={latestVersion}
|
latestVersion={latestVersion}
|
||||||
latestDevVersion={latestDevVersion}
|
latestDevVersion={latestDevVersion}
|
||||||
|
upgradeImportantMessageType={upgradeImportantMessageType}
|
||||||
downloadOnly={downloadOnly}
|
downloadOnly={downloadOnly}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
LL={LL}
|
LL={LL}
|
||||||
@@ -832,7 +880,7 @@ const Version = () => {
|
|||||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||||
{LL.UPLOAD()}
|
{LL.UPLOAD()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<SingleUpload text={LL.UPLOAD_DROP_TEXT()} doRestart={doRestart} />
|
<SingleUpload doRestart={doRestart} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -53,12 +53,16 @@ const MessageBox: FC<PropsWithChildren<MessageBoxProps>> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
p={2}
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
borderRadius={1}
|
|
||||||
sx={{ backgroundColor, color: 'white', ...sx }}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 1,
|
||||||
|
backgroundColor,
|
||||||
|
color: 'white',
|
||||||
|
p: 2,
|
||||||
|
...sx
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Icon />
|
<Icon />
|
||||||
{(message || children) && (
|
{(message || children) && (
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
|
|||||||
() => (
|
() => (
|
||||||
<>
|
<>
|
||||||
<Toolbar disableGutters>
|
<Toolbar disableGutters>
|
||||||
<Box display="flex" alignItems="center" px={2}>
|
<Box sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
|
||||||
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
|
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
|
||||||
<Typography variant="h6">{PROJECT_NAME}</Typography>
|
<Typography variant="h6">{PROJECT_NAME}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -51,9 +51,7 @@ const LayoutMenuComponent = () => {
|
|||||||
sx={{ my: 0 }}
|
sx={{ my: 0 }}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
primary: {
|
primary: {
|
||||||
fontWeight: '600',
|
sx: { fontWeight: 600, mb: '2px', color: 'lightblue' }
|
||||||
mb: '2px',
|
|
||||||
color: 'lightblue'
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -32,8 +32,16 @@ const FormLoaderComponent = ({ errorMessage, onRetry }: FormLoaderProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Box m={2} py={2} display="flex" alignItems="center" flexDirection="column">
|
<Box
|
||||||
<Box py={2}>
|
sx={{
|
||||||
|
m: 2,
|
||||||
|
py: 2,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'column'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ p: 2 }}>
|
||||||
<CircularProgress size={100} />
|
<CircularProgress size={100} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -15,12 +15,14 @@ const circularProgressStyles: SxProps<Theme> = (theme: Theme) => ({
|
|||||||
const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => {
|
const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
sx={{
|
||||||
alignItems="center"
|
display: 'flex',
|
||||||
justifyContent="center"
|
alignItems: 'center',
|
||||||
flexDirection="column"
|
justifyContent: 'center',
|
||||||
padding={2}
|
flexDirection: 'column',
|
||||||
height={height}
|
padding: 2,
|
||||||
|
height
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<CircularProgress sx={circularProgressStyles} size={100} />
|
<CircularProgress sx={circularProgressStyles} size={100} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0
|
// drag/drop code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0
|
||||||
import {
|
import {
|
||||||
type ChangeEvent,
|
type ChangeEvent,
|
||||||
type DragEvent,
|
type DragEvent,
|
||||||
@@ -6,12 +6,28 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
useState
|
useState
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { Link } from 'react-router';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
|
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
|
||||||
import UploadIcon from '@mui/icons-material/Upload';
|
import UploadIcon from '@mui/icons-material/Upload';
|
||||||
import { Box, Button, Typography, styled } from '@mui/material';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Typography,
|
||||||
|
styled
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
import { callAction } from 'api/app';
|
||||||
|
|
||||||
|
import { dialogStyle } from '@/CustomTheme';
|
||||||
|
import { useRequest } from 'alova/client';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
|
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
|
||||||
@@ -58,6 +74,29 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
|||||||
const [dragged, setDragged] = useState(false);
|
const [dragged, setDragged] = useState(false);
|
||||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false);
|
||||||
|
const [upgradeImportantMessageType, setUpgradeImportantMessageType] =
|
||||||
|
useState<number>(0);
|
||||||
|
|
||||||
|
const { send: checkUpgradeImportantMessages } = useRequest(
|
||||||
|
(version: string) =>
|
||||||
|
callAction({ action: 'upgradeImportantMessages', param: version }),
|
||||||
|
{
|
||||||
|
immediate: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onSuccess((event) => {
|
||||||
|
const upgradeImportantMessageType_n = (
|
||||||
|
event.data as { upgradeImportantMessageType: number }
|
||||||
|
).upgradeImportantMessageType;
|
||||||
|
setUpgradeImportantMessageType(upgradeImportantMessageType_n);
|
||||||
|
if (upgradeImportantMessageType_n === 0) {
|
||||||
|
onFileSelected(file);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.onError((error) => {
|
||||||
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
|
});
|
||||||
|
|
||||||
const checkFileExtension = (file: File) => {
|
const checkFileExtension = (file: File) => {
|
||||||
const validExtensions = ['.json', '.bin', '.md5'];
|
const validExtensions = ['.json', '.bin', '.md5'];
|
||||||
@@ -97,9 +136,8 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
|||||||
|
|
||||||
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
|
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (file) {
|
void checkUpgradeImportantMessages(file?.name || '');
|
||||||
onFileSelected(file);
|
setShowUpgradeDialog(true);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBrowseClick = () => {
|
const handleBrowseClick = () => {
|
||||||
@@ -158,6 +196,56 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
|
|||||||
{LL.UPLOAD()}
|
{LL.UPLOAD()}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
{showUpgradeDialog && upgradeImportantMessageType > 0 && (
|
||||||
|
<Dialog
|
||||||
|
sx={dialogStyle}
|
||||||
|
open={showUpgradeDialog}
|
||||||
|
onClose={() => setShowUpgradeDialog(false)}
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
<WarningIcon
|
||||||
|
color="warning"
|
||||||
|
sx={{ fontSize: 18, verticalAlign: 'middle' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{LL.UPGRADE_IMPORTANT_MESSAGES()}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
{upgradeImportantMessageType === 2 &&
|
||||||
|
LL.UPGRADE_IMPORTANT_MESSAGES_2()}
|
||||||
|
{upgradeImportantMessageType === 1 &&
|
||||||
|
LL.UPGRADE_IMPORTANT_MESSAGES_1()}
|
||||||
|
<Typography sx={{ mt: 2 }}>
|
||||||
|
<Link
|
||||||
|
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
style={{ color: 'lightblue' }}
|
||||||
|
>
|
||||||
|
{LL.ONLINE_HELP()}
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
startIcon={<CancelIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setShowUpgradeDialog(false)}
|
||||||
|
color="secondary"
|
||||||
|
>
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<UploadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => onFileSelected(file)}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{LL.UPLOAD()}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DocumentUploader>
|
</DocumentUploader>
|
||||||
|
|||||||
@@ -13,11 +13,10 @@ import DragNdrop from './DragNdrop';
|
|||||||
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
|
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
|
||||||
|
|
||||||
interface SingleUploadProps {
|
interface SingleUploadProps {
|
||||||
text: string;
|
|
||||||
doRestart: () => void;
|
doRestart: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
const SingleUpload = ({ doRestart }: SingleUploadProps) => {
|
||||||
const [md5, setMd5] = useState<string>();
|
const [md5, setMd5] = useState<string>();
|
||||||
const [file, setFile] = useState<File>();
|
const [file, setFile] = useState<File>();
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
@@ -58,7 +57,7 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
|||||||
<>
|
<>
|
||||||
{isUploading ? (
|
{isUploading ? (
|
||||||
<>
|
<>
|
||||||
<Box width="100%" pl={2} pr={2}>
|
<Box sx={{ width: '100%', pl: 2, pr: 2 }}>
|
||||||
<LinearProgressWithLabel
|
<LinearProgressWithLabel
|
||||||
value={
|
value={
|
||||||
progress.total === 0 || progress.loaded === 0
|
progress.total === 0 || progress.loaded === 0
|
||||||
@@ -81,11 +80,11 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<DragNdrop text={text} onFileSelected={setFile} />
|
<DragNdrop text={LL.UPLOAD_DROP_TEXT()} onFileSelected={setFile} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{md5 && (
|
{md5 && (
|
||||||
<Box mt={2}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ const cz: Translation = {
|
|||||||
BUFFER_SIZE: 'Maximální velikost vyrovnávací paměti',
|
BUFFER_SIZE: 'Maximální velikost vyrovnávací paměti',
|
||||||
COMPACT: 'Kompaktní',
|
COMPACT: 'Kompaktní',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Vytvořte zálohu svého nastavení a konfigurace',
|
DOWNLOAD_SETTINGS_TEXT: 'Vytvořte zálohu svého nastavení a konfigurace',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportovat všechna data',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportovat všechny hodnoty',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
|
||||||
UPLOAD_TEXT: 'Nahrajte nový soubor firmwaru (.bin) nebo záložní soubor (.json)',
|
UPLOAD_TEXT: 'Nahrajte nový soubor firmwaru (.bin) nebo záložní soubor (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Přetáhněte soubor sem nebo klikněte pro výběr',
|
UPLOAD_DROP_TEXT: 'Přetáhněte soubor sem nebo klikněte pro výběr',
|
||||||
ERROR: 'Neočekávaná chyba, zkuste to prosím znovu',
|
ERROR: 'Neočekávaná chyba, zkuste to prosím znovu',
|
||||||
@@ -349,15 +350,22 @@ const cz: Translation = {
|
|||||||
NO_DATA_1: 'Nebyly nalezeny žádné oblíbené entity. Použijte modul',
|
NO_DATA_1: 'Nebyly nalezeny žádné oblíbené entity. Použijte modul',
|
||||||
NO_DATA_2: 'pro jejich výběr.',
|
NO_DATA_2: 'pro jejich výběr.',
|
||||||
NO_DATA_3: 'Pro zobrazení všech dostupných entit navštivte stránku',
|
NO_DATA_3: 'Pro zobrazení všech dostupných entit navštivte stránku',
|
||||||
|
NO_GPIO: 'Nebylo nalezeno žádné volné GPIO',
|
||||||
THIS_VERSION: 'Tato verze',
|
THIS_VERSION: 'Tato verze',
|
||||||
PLATFORM: 'Platforma',
|
PLATFORM: 'Platforma',
|
||||||
RELEASE_TYPE: 'Typ sestavení',
|
RELEASE_TYPE: 'Typ sestavení',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Pro automatickou kontrolu a instalaci aktualizací je třeba internetové připojení',
|
INTERNET_CONNECTION_REQUIRED: 'Pro automatickou kontrolu a instalaci aktualizací je třeba internetové připojení',
|
||||||
SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi',
|
SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi',
|
||||||
FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru',
|
FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru',
|
||||||
NO_DATA: 'Žádná data',
|
NO_DATA: 'žádné údaje',
|
||||||
USER_PROFILE: 'Uživatelský profil',
|
USER_PROFILE: 'Uživatelský profil',
|
||||||
STORED_VERSIONS: 'Uložené verze'
|
STORED_VERSIONS: 'Uložené verze',
|
||||||
|
ONLINE_HELP: 'online nápověda',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizovat důležité zprávy',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Tato aktualizace vyžaduje obnovení továrního nastavení. Ujistěte se, že nejprve stáhnete systémovou zálohu před pokračováním a poté nahrajte tento soubor po instalaci nové verze.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete se na novou hlavní verzi. Ujistěte se, že jste přečetli ChangeLog pro jakékoliv závažné změny.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Toto vytvoří zálohu vašich celých systémových konfigurací a nastavení. Všechna hesla budou v zálohovém souboru čitelná. Buďte opatrní při sdílení! Opravdu chcete pokračovat?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default cz;
|
export default cz;
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ const de: Translation = {
|
|||||||
BUFFER_SIZE: 'Max. Puffergröße',
|
BUFFER_SIZE: 'Max. Puffergröße',
|
||||||
COMPACT: 'Kompakte Darstellung',
|
COMPACT: 'Kompakte Darstellung',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
|
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Daten',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'System Sicherung',
|
||||||
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch',
|
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch',
|
||||||
UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier',
|
UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier',
|
||||||
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
|
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
|
||||||
@@ -349,6 +350,7 @@ const de: Translation = {
|
|||||||
NO_DATA_1: 'Keine favorisierten EMS-Entitäten gefunden! Verwenden Sie das Modul',
|
NO_DATA_1: 'Keine favorisierten EMS-Entitäten gefunden! Verwenden Sie das Modul',
|
||||||
NO_DATA_2: ', um sie zu markieren.',
|
NO_DATA_2: ', um sie zu markieren.',
|
||||||
NO_DATA_3: 'Um alle verfügbaren Entitäten anzuzeigen, gehen Sie zu',
|
NO_DATA_3: 'Um alle verfügbaren Entitäten anzuzeigen, gehen Sie zu',
|
||||||
|
NO_GPIO: 'Keine freien GPIO gefunden',
|
||||||
THIS_VERSION: 'Diese Version',
|
THIS_VERSION: 'Diese Version',
|
||||||
PLATFORM: 'Plattform',
|
PLATFORM: 'Plattform',
|
||||||
RELEASE_TYPE: 'Release Typ',
|
RELEASE_TYPE: 'Release Typ',
|
||||||
@@ -357,7 +359,13 @@ const de: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Firmware-Versionsinformation',
|
FIRMWARE_VERSION_INFO: 'Firmware-Versionsinformation',
|
||||||
NO_DATA: 'Keine Daten',
|
NO_DATA: 'Keine Daten',
|
||||||
USER_PROFILE: 'Benutzerprofil',
|
USER_PROFILE: 'Benutzerprofil',
|
||||||
STORED_VERSIONS: 'Gespeicherte Versionen'
|
STORED_VERSIONS: 'Gespeicherte Versionen',
|
||||||
|
ONLINE_HELP: 'Online-Hilfe',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Wichtige Nachrichten aktualisieren',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Diese Aktualisierung erfordert eine Werkseinstellung. Stellen Sie sicher, dass Sie zuerst eine Systemsicherung herunterladen, bevor Sie fortfahren, und laden Sie diese Datei dann nach der Installation der neuen Version hoch.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und -einstellungen erstellen. Alle Passwörter werden im Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ const en: Translation = {
|
|||||||
BUFFER_SIZE: 'Max Buffer Size',
|
BUFFER_SIZE: 'Max Buffer Size',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Export all data',
|
DOWNLOAD_SETTINGS_TEXT2: 'Export all values',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'System Backup',
|
||||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
|
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
||||||
ERROR: 'Unexpected Error, please try again',
|
ERROR: 'Unexpected Error, please try again',
|
||||||
@@ -349,6 +350,7 @@ const en: Translation = {
|
|||||||
NO_DATA_1: 'No favorite EMS entities found yet. Use the',
|
NO_DATA_1: 'No favorite EMS entities found yet. Use the',
|
||||||
NO_DATA_2: 'module to mark them.',
|
NO_DATA_2: 'module to mark them.',
|
||||||
NO_DATA_3: 'To see all available entities go to',
|
NO_DATA_3: 'To see all available entities go to',
|
||||||
|
NO_GPIO: 'No available GPIO found',
|
||||||
THIS_VERSION: 'This Version',
|
THIS_VERSION: 'This Version',
|
||||||
PLATFORM: 'Platform',
|
PLATFORM: 'Platform',
|
||||||
RELEASE_TYPE: 'Release Type',
|
RELEASE_TYPE: 'Release Type',
|
||||||
@@ -357,7 +359,13 @@ const en: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Firmware Version Information',
|
FIRMWARE_VERSION_INFO: 'Firmware Version Information',
|
||||||
NO_DATA: 'No data',
|
NO_DATA: 'No data',
|
||||||
USER_PROFILE: 'User Profile',
|
USER_PROFILE: 'User Profile',
|
||||||
STORED_VERSIONS: 'Stored Versions'
|
STORED_VERSIONS: 'Stored Versions',
|
||||||
|
ONLINE_HELP: 'online help',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Upgrade Important Messages',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'This upgrade requires a factory reset. Make sure you first download a System Backup before continuing, and then upload this file after the new version is installed.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'You are upgrading to a new major version. Make sure you have read the ChangeLog for any breaking changes.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'This will create a backup of your full system configuration and settings. All passwords will be readable in the backup file. Be careful with sharing! Do you want to continue?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ const fr: Translation = {
|
|||||||
BUFFER_SIZE: 'Max taille du buffer',
|
BUFFER_SIZE: 'Max taille du buffer',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Créer une sauvegarde de vos paramètres et configurations',
|
DOWNLOAD_SETTINGS_TEXT: 'Créer une sauvegarde de vos paramètres et configurations',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exporter toutes les données',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exporter toutes les valeurs',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'Sauvegarde système',
|
||||||
UPLOAD_TEXT: 'Télécharger un nouveau fichier firmware (.bin) ou une sauvegarde (.json)',
|
UPLOAD_TEXT: 'Télécharger un nouveau fichier firmware (.bin) ou une sauvegarde (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
||||||
ERROR: 'Erreur inattendue, veuillez réessayer',
|
ERROR: 'Erreur inattendue, veuillez réessayer',
|
||||||
@@ -349,6 +350,7 @@ const fr: Translation = {
|
|||||||
NO_DATA_1: 'Aucune entité EMS favorite trouvée. Utilisez le',
|
NO_DATA_1: 'Aucune entité EMS favorite trouvée. Utilisez le',
|
||||||
NO_DATA_2: 'module pour les marquer.',
|
NO_DATA_2: 'module pour les marquer.',
|
||||||
NO_DATA_3: 'Pour voir toutes les entités disponibles, aller à',
|
NO_DATA_3: 'Pour voir toutes les entités disponibles, aller à',
|
||||||
|
NO_GPIO: "Aucun GPIO disponible n'a été détecté",
|
||||||
THIS_VERSION: 'Cette version',
|
THIS_VERSION: 'Cette version',
|
||||||
PLATFORM: 'Plateforme',
|
PLATFORM: 'Plateforme',
|
||||||
RELEASE_TYPE: 'Type de version',
|
RELEASE_TYPE: 'Type de version',
|
||||||
@@ -357,7 +359,13 @@ const fr: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Informations sur la version du firmware',
|
FIRMWARE_VERSION_INFO: 'Informations sur la version du firmware',
|
||||||
NO_DATA: 'Aucune donnée',
|
NO_DATA: 'Aucune donnée',
|
||||||
USER_PROFILE: 'Profil utilisateur',
|
USER_PROFILE: 'Profil utilisateur',
|
||||||
STORED_VERSIONS: 'Versions stockées'
|
STORED_VERSIONS: 'Versions stockées',
|
||||||
|
ONLINE_HELP: 'aide en ligne',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Mettre à jour les messages importants',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Cette mise à jour nécessite une réinitialisation de fabrique. Assurez-vous de télécharger une sauvegarde système avant de continuer, et de la charger après l\'installation de la nouvelle version.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Vous mettez à jour vers une nouvelle version majeure. Assurez-vous de lire le ChangeLog pour tout changement important.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Cela créera une sauvegarde de votre configuration et paramètres complets. Tous les mots de passe seront lisibles dans le fichier de sauvegarde. Soyez prudent avec le partage ! Voulez-vous continuer ?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fr;
|
export default fr;
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ const it: Translation = {
|
|||||||
BUFFER_SIZE: 'Max Buffer Size',
|
BUFFER_SIZE: 'Max Buffer Size',
|
||||||
COMPACT: 'Compatto',
|
COMPACT: 'Compatto',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Esporta tutti i dati',
|
DOWNLOAD_SETTINGS_TEXT2: 'Esporta tutti i valori',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'Backup sistema',
|
||||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
|
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
||||||
ERROR: 'Errore Inaspettato, prego tenta ancora',
|
ERROR: 'Errore Inaspettato, prego tenta ancora',
|
||||||
@@ -349,6 +350,7 @@ const it: Translation = {
|
|||||||
NO_DATA_1: 'Nessuna entità EMS preferita trovata. Usa il',
|
NO_DATA_1: 'Nessuna entità EMS preferita trovata. Usa il',
|
||||||
NO_DATA_2: 'modulo per marcarle.',
|
NO_DATA_2: 'modulo per marcarle.',
|
||||||
NO_DATA_3: 'Per vedere tutte le entità disponibili vai a',
|
NO_DATA_3: 'Per vedere tutte le entità disponibili vai a',
|
||||||
|
NO_GPIO: 'Non è stato trovato alcun GPIO disponibile',
|
||||||
THIS_VERSION: 'Questa versione',
|
THIS_VERSION: 'Questa versione',
|
||||||
PLATFORM: 'Piattaforma',
|
PLATFORM: 'Piattaforma',
|
||||||
RELEASE_TYPE: 'Tipo di rilascio',
|
RELEASE_TYPE: 'Tipo di rilascio',
|
||||||
@@ -357,7 +359,13 @@ const it: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Informazioni sulla versione del firmware',
|
FIRMWARE_VERSION_INFO: 'Informazioni sulla versione del firmware',
|
||||||
NO_DATA: 'Nessun dato',
|
NO_DATA: 'Nessun dato',
|
||||||
USER_PROFILE: 'Profilo utente',
|
USER_PROFILE: 'Profilo utente',
|
||||||
STORED_VERSIONS: 'Versioni memorizzate'
|
STORED_VERSIONS: 'Versioni memorizzate',
|
||||||
|
ONLINE_HELP: 'aiuto online',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Aggiorna Messaggi Importanti',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Questa aggiornamento richiede un ripristino di fabbrica. Assicurati di prima scaricare un backup del sistema prima di continuare, e poi caricare questo file dopo l\'installazione della nuova versione.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Stai aggiornando a una nuova versione principale. Assicurati di aver letto il ChangeLog per qualsiasi cambiamento importante.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Questo creerà un backup delle tue configurazioni e impostazioni complete. Tutte le password saranno leggibili nel file di backup. Sei sicuro di voler continuare?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default it;
|
export default it;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const nl: Translation = {
|
|||||||
REFRESH: 'Ververs',
|
REFRESH: 'Ververs',
|
||||||
EXPORT: 'Export',
|
EXPORT: 'Export',
|
||||||
FAVORITES: "Favorieten",
|
FAVORITES: "Favorieten",
|
||||||
DEVICE_DETAILS: 'Device Gegevens',
|
DEVICE_DETAILS: 'Apparaat Gegevens',
|
||||||
ID_OF: '{0} ID',
|
ID_OF: '{0} ID',
|
||||||
DEVICE: 'Apparaat',
|
DEVICE: 'Apparaat',
|
||||||
PRODUCT: 'Product',
|
PRODUCT: 'Product',
|
||||||
@@ -65,7 +65,7 @@ const nl: Translation = {
|
|||||||
TEMP_SENSOR: 'Temperatuur sensor',
|
TEMP_SENSOR: 'Temperatuur sensor',
|
||||||
TEMP_SENSORS: 'Temperatuur Sensoren',
|
TEMP_SENSORS: 'Temperatuur Sensoren',
|
||||||
WRITE_CMD_SENT: 'Schrijf commando gestuurd',
|
WRITE_CMD_SENT: 'Schrijf commando gestuurd',
|
||||||
EMS_BUS_WARNING: 'EMS bus niet gevonden. Als deze waarschuwing blijft staan na een paar seconden dan loop de instellingen na en in het bijzonder het apparaat type profiel na.',
|
EMS_BUS_WARNING: 'EMS bus niet gevonden. Als deze waarschuwing blijft staan na een paar seconden loop dan de instellingen na en in het bijzonder het apparaat type profiel.',
|
||||||
EMS_BUS_SCANNING: 'Scannen naar EMS apparaten...',
|
EMS_BUS_SCANNING: 'Scannen naar EMS apparaten...',
|
||||||
CONNECTED: 'Verbonden',
|
CONNECTED: 'Verbonden',
|
||||||
TX_ISSUES: 'Tx bus probleem. Probeer een andere Tx verzendmodus',
|
TX_ISSUES: 'Tx bus probleem. Probeer een andere Tx verzendmodus',
|
||||||
@@ -75,7 +75,7 @@ const nl: Translation = {
|
|||||||
EMS_DEVICE: 'EMS Apparaat',
|
EMS_DEVICE: 'EMS Apparaat',
|
||||||
SUCCESS: 'SUCCESS',
|
SUCCESS: 'SUCCESS',
|
||||||
FAIL: 'MISLUKT',
|
FAIL: 'MISLUKT',
|
||||||
QUALITY: 'QUALITEIT',
|
QUALITY: 'KWALITEIT',
|
||||||
SCAN: 'Scan',
|
SCAN: 'Scan',
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegrammen ontvangen (Rx)',
|
'EMS Telegrammen ontvangen (Rx)',
|
||||||
@@ -120,7 +120,7 @@ const nl: Translation = {
|
|||||||
ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)',
|
ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)',
|
||||||
ENABLE_SHOWER_ALERT: 'Activeer Douchemelding',
|
ENABLE_SHOWER_ALERT: 'Activeer Douchemelding',
|
||||||
TRIGGER_TIME: 'Trigger tijd',
|
TRIGGER_TIME: 'Trigger tijd',
|
||||||
COLD_SHOT_DURATION: 'Tijd Shot koud water',
|
COLD_SHOT_DURATION: 'Lengte koud water puls',
|
||||||
FORMATTING_OPTIONS: 'Formatteringsopties',
|
FORMATTING_OPTIONS: 'Formatteringsopties',
|
||||||
BOOLEAN_FORMAT_DASHBOARD: 'Boolean formaat web',
|
BOOLEAN_FORMAT_DASHBOARD: 'Boolean formaat web',
|
||||||
BOOLEAN_FORMAT_API: 'Boolean formaat API/MQTT',
|
BOOLEAN_FORMAT_API: 'Boolean formaat API/MQTT',
|
||||||
@@ -143,7 +143,7 @@ const nl: Translation = {
|
|||||||
CUSTOMIZATIONS_FULL: 'Te veel entiteiten geselecteerd. Sla op in delen aub',
|
CUSTOMIZATIONS_FULL: 'Te veel entiteiten geselecteerd. Sla op in delen aub',
|
||||||
CUSTOMIZATIONS_SAVED: 'Custom aanpassingen opgeslagen',
|
CUSTOMIZATIONS_SAVED: 'Custom aanpassingen opgeslagen',
|
||||||
CUSTOMIZATIONS_HELP_1: 'Selecteer een apparaat en pas de entiteiten aan door middel van de opties',
|
CUSTOMIZATIONS_HELP_1: 'Selecteer een apparaat en pas de entiteiten aan door middel van de opties',
|
||||||
CUSTOMIZATIONS_HELP_2: 'Markeer as favoriet',
|
CUSTOMIZATIONS_HELP_2: 'Markeer als favoriet',
|
||||||
CUSTOMIZATIONS_HELP_3: 'Zet schrijfacties uit',
|
CUSTOMIZATIONS_HELP_3: 'Zet schrijfacties uit',
|
||||||
CUSTOMIZATIONS_HELP_4: 'Uitsluiten van MQTT en API',
|
CUSTOMIZATIONS_HELP_4: 'Uitsluiten van MQTT en API',
|
||||||
CUSTOMIZATIONS_HELP_5: 'verbergen voor apparaten',
|
CUSTOMIZATIONS_HELP_5: 'verbergen voor apparaten',
|
||||||
@@ -186,12 +186,13 @@ const nl: Translation = {
|
|||||||
BUFFER_SIZE: 'Max buffer grootte',
|
BUFFER_SIZE: 'Max buffer grootte',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Maak een back-up van uw configuratie en instellingen',
|
DOWNLOAD_SETTINGS_TEXT: 'Maak een back-up van uw configuratie en instellingen',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exporteer alle data',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exporteer alle waarden',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'Systeem Backup',
|
||||||
UPLOAD_TEXT: 'Upload een nieuw firmwarebestand (.bin) of een back-upbestand (.json)',
|
UPLOAD_TEXT: 'Upload een nieuw firmwarebestand (.bin) of een back-upbestand (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier',
|
UPLOAD_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier',
|
||||||
ERROR: 'Onverwachte fout, probeer opnieuw',
|
ERROR: 'Onverwachte fout, probeer opnieuw',
|
||||||
TIME_SET: 'Tijd ingesteld',
|
TIME_SET: 'Tijd ingesteld',
|
||||||
MANAGE_USERS: 'Beheer Gebruikers',
|
MANAGE_USERS: 'Gebruikersbeheer',
|
||||||
IS_ADMIN: 'is Admin',
|
IS_ADMIN: 'is Admin',
|
||||||
USER_WARNING: 'U dient tenminste 1 admin gebruiker te configureren',
|
USER_WARNING: 'U dient tenminste 1 admin gebruiker te configureren',
|
||||||
ADD: 'Toevoegen',
|
ADD: 'Toevoegen',
|
||||||
@@ -200,7 +201,7 @@ const nl: Translation = {
|
|||||||
GENERATING_TOKEN: 'Token aan het genereren',
|
GENERATING_TOKEN: 'Token aan het genereren',
|
||||||
USER: 'Gebruiker',
|
USER: 'Gebruiker',
|
||||||
MODIFY: 'Aanpassen',
|
MODIFY: 'Aanpassen',
|
||||||
SU_TEXT: 'Het su (super user) wachtwoord wordt gebruikt om authorisatie tokens te signeren en ook om admin privileges te activeren in de console.',
|
SU_TEXT: 'Het su (super user) wachtwoord wordt gebruikt om authorisatie tokens te ondertekenen en ook om admin privileges te activeren in de console.',
|
||||||
NOT_ENABLED: 'Niet geactiveerd',
|
NOT_ENABLED: 'Niet geactiveerd',
|
||||||
ERRORS_OF: '{0} Foutmeldingen',
|
ERRORS_OF: '{0} Foutmeldingen',
|
||||||
DISCONNECT_REASON: 'Verbinding verbroken vanwege',
|
DISCONNECT_REASON: 'Verbinding verbroken vanwege',
|
||||||
@@ -349,6 +350,7 @@ const nl: Translation = {
|
|||||||
NO_DATA_1: 'Er zijn nog geen favoriete EMS-entiteiten gevonden. Gebruik de',
|
NO_DATA_1: 'Er zijn nog geen favoriete EMS-entiteiten gevonden. Gebruik de',
|
||||||
NO_DATA_2: 'module om ze te markeren.',
|
NO_DATA_2: 'module om ze te markeren.',
|
||||||
NO_DATA_3: 'Om alle beschikbare entiteiten te zien, ga naar',
|
NO_DATA_3: 'Om alle beschikbare entiteiten te zien, ga naar',
|
||||||
|
NO_GPIO: 'Er is geen beschikbare GPIO gevonden',
|
||||||
THIS_VERSION: 'Deze Versie',
|
THIS_VERSION: 'Deze Versie',
|
||||||
PLATFORM: 'Platform',
|
PLATFORM: 'Platform',
|
||||||
RELEASE_TYPE: 'Release Typ',
|
RELEASE_TYPE: 'Release Typ',
|
||||||
@@ -357,7 +359,13 @@ const nl: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Informatie over firmwareversie',
|
FIRMWARE_VERSION_INFO: 'Informatie over firmwareversie',
|
||||||
NO_DATA: 'Geen data',
|
NO_DATA: 'Geen data',
|
||||||
USER_PROFILE: 'Gebruikersprofiel',
|
USER_PROFILE: 'Gebruikersprofiel',
|
||||||
STORED_VERSIONS: 'Opgeslagen versies'
|
STORED_VERSIONS: 'Opgeslagen versies',
|
||||||
|
ONLINE_HELP: 'online help',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Upgrade Belangrijke Berichten',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Deze upgrade vereist een fabrieksinstelling. Zorg ervoor dat u eerst een Systeem Backup download voordat u doorgaat, en upload deze file na de installatie van de nieuwe versie.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'U updatet naar een nieuwe grote versie. Zorg ervoor dat u de ChangeLog hebt gelezen voor alle brekende wijzigingen.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Dit zal een back-up van uw volledige systeemconfiguratie en instellingen maken. Alle wachtwoorden zijn leesbaar in het back-upbestand. Wees voorzichtig bij delen! Wilt u doorgaan?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nl;
|
export default nl;
|
||||||
@@ -186,7 +186,8 @@ const no: Translation = {
|
|||||||
BUFFER_SIZE: 'Max Buffer Størrelse',
|
BUFFER_SIZE: 'Max Buffer Størrelse',
|
||||||
COMPACT: 'Komprimere',
|
COMPACT: 'Komprimere',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Lag en sikkerhetskopi av dine konfigurasjon og innstillinger',
|
DOWNLOAD_SETTINGS_TEXT: 'Lag en sikkerhetskopi av dine konfigurasjon og innstillinger',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Eksporter alle data',
|
DOWNLOAD_SETTINGS_TEXT2: 'Eksporter alle verdier',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'System Sikkerhetskopi',
|
||||||
UPLOAD_TEXT: 'Last opp en ny firmware fil (.bin) eller en sikkerhetskopi fil (.json)',
|
UPLOAD_TEXT: 'Last opp en ny firmware fil (.bin) eller en sikkerhetskopi fil (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Dropp en firmware fil (.bin) eller klikk her',
|
UPLOAD_DROP_TEXT: 'Dropp en firmware fil (.bin) eller klikk her',
|
||||||
ERROR: 'Ukjent feil, prøv igjen',
|
ERROR: 'Ukjent feil, prøv igjen',
|
||||||
@@ -349,6 +350,7 @@ const no: Translation = {
|
|||||||
NO_DATA_1: 'Ingen favoritte EMS enheter funnet enda. Bruk',
|
NO_DATA_1: 'Ingen favoritte EMS enheter funnet enda. Bruk',
|
||||||
NO_DATA_2: 'modul for å markere dem.',
|
NO_DATA_2: 'modul for å markere dem.',
|
||||||
NO_DATA_3: 'For å se alle tilgjengelige enheter, gå til',
|
NO_DATA_3: 'For å se alle tilgjengelige enheter, gå til',
|
||||||
|
NO_GPIO: 'Det ble ikke funnet noen tilgjengelige GPIO-porter',
|
||||||
THIS_VERSION: 'Denne versjonen',
|
THIS_VERSION: 'Denne versjonen',
|
||||||
PLATFORM: 'Plattform',
|
PLATFORM: 'Plattform',
|
||||||
RELEASE_TYPE: 'Utgivelses type',
|
RELEASE_TYPE: 'Utgivelses type',
|
||||||
@@ -357,7 +359,13 @@ const no: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Informasjon om firmwareversjon',
|
FIRMWARE_VERSION_INFO: 'Informasjon om firmwareversjon',
|
||||||
NO_DATA: 'Ingen data',
|
NO_DATA: 'Ingen data',
|
||||||
USER_PROFILE: 'Brukerprofil',
|
USER_PROFILE: 'Brukerprofil',
|
||||||
STORED_VERSIONS: 'Lagret versjoner'
|
STORED_VERSIONS: 'Lagret versjoner',
|
||||||
|
ONLINE_HELP: 'online hjelp',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Oppdater viktige meldinger',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Denne oppdateringen krever en fabriksinstilling. Sørg for at du først lastet ned en System Backup før du fortsetter, og last denne filen etter at den nye versjonen er installert.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Du oppdaterer til en ny hovedversjon. Sørg for at du har lest ChangeLog for eventuelle bruddende endringer.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Dette vil lage en sikkerhetskopi av din fullstendige systemkonfigurasjon og innstillinger. Alle passord vil være lesbare i sikkerhetskopien. Vær forsiktig med deling! Vil du fortsette?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default no;
|
export default no;
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ const pl: BaseTranslation = {
|
|||||||
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
|
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
|
||||||
COMPACT: 'Kompaktowy',
|
COMPACT: 'Kompaktowy',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Utwórz kopię swoich ustawień i konfiguracji',
|
DOWNLOAD_SETTINGS_TEXT: 'Utwórz kopię swoich ustawień i konfiguracji',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Eksportuj wszystkie dane',
|
DOWNLOAD_SETTINGS_TEXT2: 'Eksportuj wszystkie wartości',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'Kopia zapasowa systemu',
|
||||||
UPLOAD_TEXT: 'Wgraj nowy plik firmware (.bin) lub kopię ustawień (.json)',
|
UPLOAD_TEXT: 'Wgraj nowy plik firmware (.bin) lub kopię ustawień (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj',
|
UPLOAD_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj',
|
||||||
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
||||||
@@ -349,6 +350,7 @@ const pl: BaseTranslation = {
|
|||||||
NO_DATA_1: 'Brak ulubionych encji EMS. Użyj',
|
NO_DATA_1: 'Brak ulubionych encji EMS. Użyj',
|
||||||
NO_DATA_2: 'moduł do ich oznaczenia.',
|
NO_DATA_2: 'moduł do ich oznaczenia.',
|
||||||
NO_DATA_3: 'Aby zobaczyć wszystkie dostępne encje przejdź do',
|
NO_DATA_3: 'Aby zobaczyć wszystkie dostępne encje przejdź do',
|
||||||
|
NO_GPIO: 'Nie znaleziono dostępnych pinów GPIO',
|
||||||
THIS_VERSION: 'Ta wersja',
|
THIS_VERSION: 'Ta wersja',
|
||||||
PLATFORM: 'Platforma',
|
PLATFORM: 'Platforma',
|
||||||
RELEASE_TYPE: 'Typ wydania',
|
RELEASE_TYPE: 'Typ wydania',
|
||||||
@@ -357,7 +359,13 @@ const pl: BaseTranslation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Informacje o wersji firmware',
|
FIRMWARE_VERSION_INFO: 'Informacje o wersji firmware',
|
||||||
NO_DATA: 'Brak danych',
|
NO_DATA: 'Brak danych',
|
||||||
USER_PROFILE: 'Profil użytkownika',
|
USER_PROFILE: 'Profil użytkownika',
|
||||||
STORED_VERSIONS: 'Zapisane wersje'
|
STORED_VERSIONS: 'Zapisane wersje',
|
||||||
|
ONLINE_HELP: 'pomoc online',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizuj ważne wiadomości',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Ta aktualizacja wymaga resetu fabrycznego. Upewnij się, że najpierw pobierzesz kopię zapasową systemu przed kontynuowaniem, a następnie przesuń tę plik po zainstalowaniu nowej wersji.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujesz się do nowej głównej wersji. Upewnij się, że przeczytałeś ChangeLog dla wszelkich istotnych zmian.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'To spowoduje utworzenie kopii zapasowej całej konfiguracji i ustawień systemu. Wszystkie hasła będą widoczne w pliku kopii zapasowej. Bądź ostrożny przy udostępnianiu! Chcesz kontynuować?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pl;
|
export default pl;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const sk: Translation = {
|
|||||||
LANGUAGE: 'Jazyk',
|
LANGUAGE: 'Jazyk',
|
||||||
RETRY: 'Opakovať',
|
RETRY: 'Opakovať',
|
||||||
LOADING: 'Načítanie',
|
LOADING: 'Načítanie',
|
||||||
IS_REQUIRED: '{0} je požadovaných',
|
IS_REQUIRED: '{0} je požadovaná',
|
||||||
SIGN_IN: 'Prihlásiť sa',
|
SIGN_IN: 'Prihlásiť sa',
|
||||||
SIGN_OUT: 'Odhlásiť sa',
|
SIGN_OUT: 'Odhlásiť sa',
|
||||||
USERNAME: 'Užívateľské meno',
|
USERNAME: 'Užívateľské meno',
|
||||||
@@ -186,7 +186,8 @@ const sk: Translation = {
|
|||||||
BUFFER_SIZE: 'Buffer-max. veľkosť',
|
BUFFER_SIZE: 'Buffer-max. veľkosť',
|
||||||
COMPACT: 'Kompaktné',
|
COMPACT: 'Kompaktné',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Vytvorte zálohu svojej konfigurácie a nastavení',
|
DOWNLOAD_SETTINGS_TEXT: 'Vytvorte zálohu svojej konfigurácie a nastavení',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportovať všetky dáta',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportovať všetky hodnoty',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
|
||||||
UPLOAD_TEXT: 'Nahrajte nový súbor firmvéru (.bin) alebo súbor zálohy (.json)',
|
UPLOAD_TEXT: 'Nahrajte nový súbor firmvéru (.bin) alebo súbor zálohy (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Presuňte súbor .bin firmvéru alebo kliknite sem',
|
UPLOAD_DROP_TEXT: 'Presuňte súbor .bin firmvéru alebo kliknite sem',
|
||||||
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
||||||
@@ -275,11 +276,11 @@ const sk: Translation = {
|
|||||||
NETWORK_SUBNET: 'Maska podsiete',
|
NETWORK_SUBNET: 'Maska podsiete',
|
||||||
NETWORK_DNS: 'DNS servery',
|
NETWORK_DNS: 'DNS servery',
|
||||||
ADDRESS_OF: '{0} adresa',
|
ADDRESS_OF: '{0} adresa',
|
||||||
ADMINISTRATOR: 'Administrator',
|
ADMINISTRATOR: 'Administrátor',
|
||||||
GUEST: 'Hosť',
|
GUEST: 'Hosť',
|
||||||
NEW: 'Nová',
|
NEW: 'Novú',
|
||||||
NEW_NAME_OF: 'Nový názov {0}',
|
NEW_NAME_OF: 'Nový názov {0}',
|
||||||
ENTITY: 'entita',
|
ENTITY: 'entitu',
|
||||||
MIN: 'min',
|
MIN: 'min',
|
||||||
MAX: 'max',
|
MAX: 'max',
|
||||||
BLOCK_NAVIGATE_1: 'Máte neuložené zmeny',
|
BLOCK_NAVIGATE_1: 'Máte neuložené zmeny',
|
||||||
@@ -349,6 +350,7 @@ const sk: Translation = {
|
|||||||
NO_DATA_1: 'Nenašli sa žiadne obľúbené entity EMS. Použite',
|
NO_DATA_1: 'Nenašli sa žiadne obľúbené entity EMS. Použite',
|
||||||
NO_DATA_2: 'modul na ich označenie.',
|
NO_DATA_2: 'modul na ich označenie.',
|
||||||
NO_DATA_3: 'Ak chcete zobraziť všetky dostupné entity, prejdite na',
|
NO_DATA_3: 'Ak chcete zobraziť všetky dostupné entity, prejdite na',
|
||||||
|
NO_GPIO: 'Nebol nájdený žiadny dostupný GPIO',
|
||||||
THIS_VERSION: 'Táto verzia',
|
THIS_VERSION: 'Táto verzia',
|
||||||
PLATFORM: 'Platforma',
|
PLATFORM: 'Platforma',
|
||||||
RELEASE_TYPE: 'Typ vydania',
|
RELEASE_TYPE: 'Typ vydania',
|
||||||
@@ -357,7 +359,13 @@ const sk: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Informácie o verzii firmware',
|
FIRMWARE_VERSION_INFO: 'Informácie o verzii firmware',
|
||||||
NO_DATA: 'Žiadne dáta',
|
NO_DATA: 'Žiadne dáta',
|
||||||
USER_PROFILE: 'Profil používateľa',
|
USER_PROFILE: 'Profil používateľa',
|
||||||
STORED_VERSIONS: 'Uložené verzie'
|
STORED_VERSIONS: 'Uložené verzie',
|
||||||
|
ONLINE_HELP: 'online pomoc',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizovať dôležité správy',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Táto aktualizácia vyžaduje reštart základných nastavení. Uistite sa, že najprv stiahnete systémovú zálohu pred pokračovaním, a potom nahrajte tento súbor po instalácii novej verzie.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete sa na novú hlavnú verziu. Uistite sa, že ste prečítali ChangeLog pre akékoľvek dôležité zmeny.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Toto vytvorí zálohu všetkých vašich celých systémových konfigurácií a nastavení. Všetky hesla budú čitateľné v zálohovom súbore. Buďte opatrní pri zdieľaní! Chcete pokračovať?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sk;
|
export default sk;
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ const sv: Translation = {
|
|||||||
BUFFER_SIZE: 'Max bufferstorlek',
|
BUFFER_SIZE: 'Max bufferstorlek',
|
||||||
COMPACT: 'Komprimerad',
|
COMPACT: 'Komprimerad',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Skapa en säkerhetskopia av din konfiguration och inställningar',
|
DOWNLOAD_SETTINGS_TEXT: 'Skapa en säkerhetskopia av din konfiguration och inställningar',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportera alla data',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportera alla värden',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'System säkerhetskopia',
|
||||||
UPLOAD_TEXT: 'Ladda upp en ny firmwarefil (.bin) eller en säkerhetskopiafil (.json)',
|
UPLOAD_TEXT: 'Ladda upp en ny firmwarefil (.bin) eller en säkerhetskopiafil (.json)',
|
||||||
UPLOAD_DROP_TEXT: 'Droppa en firmware .bin fil eller klicka här',
|
UPLOAD_DROP_TEXT: 'Droppa en firmware .bin fil eller klicka här',
|
||||||
ERROR: 'Okänt fel, var god försök igen',
|
ERROR: 'Okänt fel, var god försök igen',
|
||||||
@@ -349,6 +350,7 @@ const sv: Translation = {
|
|||||||
NO_DATA_1: 'Inga favorit EMS enheter hittade än. Använd',
|
NO_DATA_1: 'Inga favorit EMS enheter hittade än. Använd',
|
||||||
NO_DATA_2: 'modul för att markera dem.',
|
NO_DATA_2: 'modul för att markera dem.',
|
||||||
NO_DATA_3: 'För att se alla tillgängliga enheter, gå till',
|
NO_DATA_3: 'För att se alla tillgängliga enheter, gå till',
|
||||||
|
NO_GPIO: 'Inga tillgängliga GPIO-portar hittades',
|
||||||
THIS_VERSION: 'Denna version',
|
THIS_VERSION: 'Denna version',
|
||||||
PLATFORM: 'Plattform',
|
PLATFORM: 'Plattform',
|
||||||
RELEASE_TYPE: 'Utgivelsestyp',
|
RELEASE_TYPE: 'Utgivelsestyp',
|
||||||
@@ -357,7 +359,13 @@ const sv: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Information om firmwareversion',
|
FIRMWARE_VERSION_INFO: 'Information om firmwareversion',
|
||||||
NO_DATA: 'Ingen data',
|
NO_DATA: 'Ingen data',
|
||||||
USER_PROFILE: 'Användarprofil',
|
USER_PROFILE: 'Användarprofil',
|
||||||
STORED_VERSIONS: 'Lagrad versioner'
|
STORED_VERSIONS: 'Lagrad versioner',
|
||||||
|
ONLINE_HELP: 'online hjälp',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Uppdatera viktiga meddelanden',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Denna uppdatering kräver en fabriksåterställning. Se till att du först laddar ned en System Backup innan du fortsätter, och ladda upp denna fil efter att den nya versionen är installerad.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Du uppdaterar till en ny huvudversion. Se till att du har läst ChangeLog för eventuella brkande ändringar.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Detta kommer att skapa en säkerhetskopia av din fullständiga systemkonfiguration och inställningar. Alla lösenord kommer att vara läsbara i säkerhetskopien. Var försiktig med att dela! Vill du fortsätta?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sv;
|
export default sv;
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ const tr: Translation = {
|
|||||||
BUFFER_SIZE: 'En fazla bellek boyutu',
|
BUFFER_SIZE: 'En fazla bellek boyutu',
|
||||||
COMPACT: 'Sıkışık',
|
COMPACT: 'Sıkışık',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Yapılandırma ve ayarlarınızın yedekleme yapın',
|
DOWNLOAD_SETTINGS_TEXT: 'Yapılandırma ve ayarlarınızın yedekleme yapın',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Tüm verileri dışarı al',
|
DOWNLOAD_SETTINGS_TEXT2: 'Tüm değerleri dışarı al',
|
||||||
|
DOWNLOAD_SYSTEM_BACKUP: 'Sistem yedekleme',
|
||||||
UPLOAD_TEXT: 'Yeni bir firmware dosyası (.bin) veya yedek dosyası (.json) yükle',
|
UPLOAD_TEXT: 'Yeni bir firmware dosyası (.bin) veya yedek dosyası (.json) yükle',
|
||||||
UPLOAD_DROP_TEXT: 'Bir firmware .bin dosyası veya buraya tıklayın',
|
UPLOAD_DROP_TEXT: 'Bir firmware .bin dosyası veya buraya tıklayın',
|
||||||
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
|
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
|
||||||
@@ -349,6 +350,7 @@ const tr: Translation = {
|
|||||||
NO_DATA_1: 'Henüz bir favori EMS varlığı bulunamadı. Kullanın',
|
NO_DATA_1: 'Henüz bir favori EMS varlığı bulunamadı. Kullanın',
|
||||||
NO_DATA_2: 'modülünü kullanın.',
|
NO_DATA_2: 'modülünü kullanın.',
|
||||||
NO_DATA_3: 'Tüm kullanılabilir varlıkları görmek için git',
|
NO_DATA_3: 'Tüm kullanılabilir varlıkları görmek için git',
|
||||||
|
NO_GPIO: 'Kullanılabilir GPIO bulunamadı',
|
||||||
THIS_VERSION: 'Bu Sürüm',
|
THIS_VERSION: 'Bu Sürüm',
|
||||||
PLATFORM: 'Platforma',
|
PLATFORM: 'Platforma',
|
||||||
RELEASE_TYPE: 'Sürüm Tipi',
|
RELEASE_TYPE: 'Sürüm Tipi',
|
||||||
@@ -357,7 +359,13 @@ const tr: Translation = {
|
|||||||
FIRMWARE_VERSION_INFO: 'Firmware Sürüm Bilgisi',
|
FIRMWARE_VERSION_INFO: 'Firmware Sürüm Bilgisi',
|
||||||
NO_DATA: 'Hiçbir veri yok',
|
NO_DATA: 'Hiçbir veri yok',
|
||||||
USER_PROFILE: 'Kullanıcı Profili',
|
USER_PROFILE: 'Kullanıcı Profili',
|
||||||
STORED_VERSIONS: 'Kaydedilmiş Sürümler'
|
STORED_VERSIONS: 'Kaydedilmiş Sürümler',
|
||||||
|
ONLINE_HELP: 'online yardım',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES: 'Önemli Mesajları Güncelle',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Bu güncelleme továrnı ayarlarını gerektirir. Yapılandırmanızı ve ayarlarınızı önce yedekleyin ve ardından yeni sürüm yüklendikten sonra yükleyin.',
|
||||||
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Yeni bir büyük sürüme yükselteceksiniz. Değişiklikleri ChangeLogı okuduğunuzdan emin olun.',
|
||||||
|
WARNING_SYSTEM_BACKUP: 'Bu, sistem yapılandırmanızı ve ayarlarınızın bir yedeklemesi oluşturacaktır. Tüm şifreler yedekleme dosyasında okunabilir olacaktır. Paylaşırken dikkatli olun! Devam etmek istediğinize emin misiniz?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default tr;
|
export default tr;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ export const saveFile = (
|
|||||||
}, 100);
|
}, 100);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save file:', error);
|
console.error('Failed to save file:', error);
|
||||||
throw new Error(`Unable to save file: ${filename}${extension}`);
|
throw new Error(`Unable to save file: ${filename}${extension}`, {
|
||||||
|
cause: error
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
const DEFAULT_DELAY = 3000;
|
const DEFAULT_DELAY = 5000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook for setting up an interval with proper cleanup
|
* Custom hook for setting up an interval with proper cleanup
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import preact from '@preact/preset-vite';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { visualizer } from 'rollup-plugin-visualizer';
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
import { Plugin, defineConfig } from 'vite';
|
import { Plugin, PluginOption, defineConfig } from 'vite';
|
||||||
import viteImagemin from 'vite-plugin-imagemin';
|
import viteImagemin from 'vite-plugin-imagemin';
|
||||||
import viteTsconfigPaths from 'vite-tsconfig-paths';
|
|
||||||
import zlib from 'zlib';
|
import zlib from 'zlib';
|
||||||
|
|
||||||
// @ts-expect-error - mock server doesn't have type declarations
|
// @ts-expect-error - mock server doesn't have type declarations
|
||||||
@@ -16,7 +15,7 @@ const REPEAT_CHAR = '=';
|
|||||||
const REPEAT_COUNT = 50;
|
const REPEAT_COUNT = 50;
|
||||||
const DEFAULT_OUT_DIR = 'dist';
|
const DEFAULT_OUT_DIR = 'dist';
|
||||||
const ES_TARGET = 'es2020';
|
const ES_TARGET = 'es2020';
|
||||||
const CHUNK_SIZE_WARNING_LIMIT = 512;
|
const CHUNK_SIZE_WARNING_LIMIT = 1024;
|
||||||
const ASSETS_INLINE_LIMIT = 4096;
|
const ASSETS_INLINE_LIMIT = 4096;
|
||||||
|
|
||||||
// Common resolve aliases
|
// Common resolve aliases
|
||||||
@@ -99,16 +98,31 @@ const createPreactPlugin = (devToolsEnabled: boolean) =>
|
|||||||
prefreshEnabled: false
|
prefreshEnabled: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Patch preact/compat to export stub React 19 APIs (use, useOptimistic) so that
|
||||||
|
// react-router v7 doesn't trigger IMPORT_IS_UNDEFINED warnings from Rolldown.
|
||||||
|
const preactCompatPatchPlugin = (): Plugin => ({
|
||||||
|
name: 'preact-compat-react19-patch',
|
||||||
|
transform(code, id) {
|
||||||
|
if (id.includes('preact') && id.includes('compat.module.js')) {
|
||||||
|
return {
|
||||||
|
code:
|
||||||
|
code +
|
||||||
|
'\nexport var use = undefined;\nexport var useOptimistic = undefined;\n',
|
||||||
|
map: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Common base plugins
|
// Common base plugins
|
||||||
const createBasePlugins = (
|
const createBasePlugins = (
|
||||||
devToolsEnabled: boolean,
|
devToolsEnabled: boolean,
|
||||||
includeBundleReporter = true
|
includeBundleReporter = true
|
||||||
) => {
|
): PluginOption[] => {
|
||||||
const plugins = [
|
const plugins: PluginOption[] = [
|
||||||
createPreactPlugin(devToolsEnabled),
|
createPreactPlugin(devToolsEnabled),
|
||||||
viteTsconfigPaths({
|
preactCompatPatchPlugin()
|
||||||
projects: ['./tsconfig.json']
|
|
||||||
})
|
|
||||||
];
|
];
|
||||||
if (includeBundleReporter) {
|
if (includeBundleReporter) {
|
||||||
plugins.push(bundleSizeReporter());
|
plugins.push(bundleSizeReporter());
|
||||||
@@ -116,40 +130,9 @@ const createBasePlugins = (
|
|||||||
return plugins;
|
return plugins;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Manual chunk splitting strategy
|
const manualChunks = (id: string): string | undefined => {
|
||||||
const createManualChunks = (detailed = false) => {
|
if (id.includes('node_modules')) return 'vendor';
|
||||||
return (id: string): string | undefined => {
|
|
||||||
if (id.includes('node_modules')) {
|
|
||||||
if (id.includes('preact')) return '@preact';
|
|
||||||
if (detailed) {
|
|
||||||
if (id.includes('react-router')) return '@react-router';
|
|
||||||
if (id.includes('@mui/material')) return '@mui-material';
|
|
||||||
if (id.includes('@mui/icons-material')) return '@mui-icons';
|
|
||||||
if (id.includes('alova')) return '@alova';
|
|
||||||
if (id.includes('typesafe-i18n')) return '@i18n';
|
|
||||||
if (id.includes('react-toastify')) return '@toastify';
|
|
||||||
if (id.includes('@table-library')) return '@table-library';
|
|
||||||
if (id.includes('uuid')) return '@uuid';
|
|
||||||
if (id.includes('axios') || id.includes('fetch')) return '@http';
|
|
||||||
if (id.includes('lodash') || id.includes('ramda')) return '@utils';
|
|
||||||
}
|
|
||||||
return 'vendor';
|
|
||||||
}
|
|
||||||
if (detailed) {
|
|
||||||
// Group circularly dependent modules together to avoid circular chunk warnings
|
|
||||||
// components, app, and utils are tightly coupled, so combine them
|
|
||||||
if (
|
|
||||||
id.includes('components/') ||
|
|
||||||
id.includes('app/') ||
|
|
||||||
id.includes('utils/')
|
|
||||||
) {
|
|
||||||
return 'app';
|
|
||||||
}
|
|
||||||
// Keep api separate as it's typically more independent
|
|
||||||
if (id.includes('api/')) return 'api';
|
|
||||||
}
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Common build base configuration
|
// Common build base configuration
|
||||||
@@ -234,7 +217,8 @@ export default defineConfig(
|
|||||||
plugins: [...createBasePlugins(true, true), mockServer()],
|
plugins: [...createBasePlugins(true, true), mockServer()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: RESOLVE_ALIASES,
|
alias: RESOLVE_ALIASES,
|
||||||
extensions: RESOLVE_EXTENSIONS
|
extensions: RESOLVE_EXTENSIONS,
|
||||||
|
tsconfigPaths: true
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
open: true,
|
open: true,
|
||||||
@@ -263,7 +247,8 @@ export default defineConfig(
|
|||||||
plugins: createBasePlugins(false, true),
|
plugins: createBasePlugins(false, true),
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: RESOLVE_ALIASES,
|
alias: RESOLVE_ALIASES,
|
||||||
extensions: RESOLVE_EXTENSIONS
|
extensions: RESOLVE_EXTENSIONS,
|
||||||
|
tsconfigPaths: true
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
...createBaseBuildConfig(),
|
...createBaseBuildConfig(),
|
||||||
@@ -274,7 +259,7 @@ export default defineConfig(
|
|||||||
moduleSideEffects: false
|
moduleSideEffects: false
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
manualChunks: createManualChunks(false)
|
manualChunks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,7 +282,8 @@ export default defineConfig(
|
|||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: RESOLVE_ALIASES,
|
alias: RESOLVE_ALIASES,
|
||||||
extensions: RESOLVE_EXTENSIONS
|
extensions: RESOLVE_EXTENSIONS,
|
||||||
|
tsconfigPaths: true
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
...createBaseBuildConfig(),
|
...createBaseBuildConfig(),
|
||||||
@@ -306,15 +292,14 @@ export default defineConfig(
|
|||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
treeshake: {
|
treeshake: {
|
||||||
moduleSideEffects: false,
|
moduleSideEffects: false,
|
||||||
propertyReadSideEffects: false,
|
propertyReadSideEffects: false as const,
|
||||||
tryCatchDeoptimization: false,
|
|
||||||
unknownGlobalSideEffects: false
|
unknownGlobalSideEffects: false
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
chunkFileNames: 'assets/[name]-[hash].js',
|
chunkFileNames: 'assets/[name]-[hash].js',
|
||||||
entryFileNames: 'assets/[name]-[hash].js',
|
entryFileNames: 'assets/[name]-[hash].js',
|
||||||
assetFileNames: 'assets/[name]-[hash].[ext]',
|
assetFileNames: 'assets/[name]-[hash].[ext]',
|
||||||
manualChunks: createManualChunks(true),
|
manualChunks,
|
||||||
sourcemap: false
|
sourcemap: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,9 +148,7 @@
|
|||||||
#elif defined(ARDUINO_ARCH_ESP32)
|
#elif defined(ARDUINO_ARCH_ESP32)
|
||||||
#include <driver/rtc_io.h>
|
#include <driver/rtc_io.h>
|
||||||
#include <soc/gpio_struct.h>
|
#include <soc/gpio_struct.h>
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
#include "soc/gpio_periph.h"
|
#include "soc/gpio_periph.h"
|
||||||
#endif // ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
#define PIN_TO_BASEREG(pin) (0)
|
#define PIN_TO_BASEREG(pin) (0)
|
||||||
#define PIN_TO_BITMASK(pin) (pin)
|
#define PIN_TO_BITMASK(pin) (pin)
|
||||||
#define IO_REG_TYPE uint32_t
|
#define IO_REG_TYPE uint32_t
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
// Include all library components
|
// Include all library components
|
||||||
#include "esp32-psram/AllocatorPSRAM.h" // PSRAM-backed vector
|
#include "esp32-psram/AllocatorPSRAM.h" // PSRAM-backed vector
|
||||||
#include "esp32-psram/VectorPSRAM.h" // PSRAM-backed vector
|
#include "esp32-psram/VectorPSRAM.h" // PSRAM-backed vector
|
||||||
#include "esp32-psram/VectorHIMEM.h" // HIMEM-backed vector
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#include "esp32-psram/VectorHIMEM.h" // HIMEM-backed vector (ESP32 only)
|
||||||
|
#endif
|
||||||
// #include "esp32-psram/InMemoryFile.h" // File interface using vectors
|
// #include "esp32-psram/InMemoryFile.h" // File interface using vectors
|
||||||
// #include "esp32-psram/PSRAM.h" // PSRAM file system
|
// #include "esp32-psram/PSRAM.h" // PSRAM file system
|
||||||
// #include "esp32-psram/HIMEM.h" // HIMEM file system
|
// #include "esp32-psram/HIMEM.h" // HIMEM file system
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// HIMEM is only available on original ESP32
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -360,3 +363,5 @@ class HimemBlock {
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esp32_psram
|
} // namespace esp32_psram
|
||||||
|
|
||||||
|
#endif // CONFIG_IDF_TARGET_ESP32
|
||||||
|
|||||||
@@ -238,7 +238,9 @@ using RingBufferStreamPSRAM = RingBufferStream<VectorPSRAM<uint8_t>>;
|
|||||||
/**
|
/**
|
||||||
* @brief Type alias for a RingBufferStream that uses HIMEM-backed vector storage
|
* @brief Type alias for a RingBufferStream that uses HIMEM-backed vector storage
|
||||||
*/
|
*/
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
using RingBufferStreamHIMEM = RingBufferStream<VectorHIMEM<uint8_t>>;
|
using RingBufferStreamHIMEM = RingBufferStream<VectorHIMEM<uint8_t>>;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Type alias for a RingBufferStream that uses std::vector storage
|
* @brief Type alias for a RingBufferStream that uses std::vector storage
|
||||||
|
|||||||
@@ -209,8 +209,10 @@ using TypedRingBufferRAM = TypedRingBuffer<T, std::vector<T>>;
|
|||||||
/**
|
/**
|
||||||
* @brief Type alias for a typed ring buffer that uses HIMEM-backed vector storage
|
* @brief Type alias for a typed ring buffer that uses HIMEM-backed vector storage
|
||||||
*/
|
*/
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using TypedRingBufferHIMEM = TypedRingBuffer<T, VectorHIMEM<T>>;
|
using TypedRingBufferHIMEM = TypedRingBuffer<T, VectorHIMEM<T>>;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Type alias for a typed ring buffer that uses PSRAM-backed vector storage
|
* @brief Type alias for a typed ring buffer that uses PSRAM-backed vector storage
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// HIMEM is only available on original ESP32
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
|
||||||
#include "HimemBlock.h"
|
#include "HimemBlock.h"
|
||||||
|
|
||||||
namespace esp32_psram {
|
namespace esp32_psram {
|
||||||
@@ -527,3 +530,5 @@ void swap(VectorHIMEM<T>& lhs, VectorHIMEM<T>& rhs) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esp32_psram
|
} // namespace esp32_psram
|
||||||
|
|
||||||
|
#endif // CONFIG_IDF_TARGET_ESP32
|
||||||
@@ -49,6 +49,10 @@ the LICENSE file.
|
|||||||
#define EMC_CLIENTID_LENGTH 23 + 1
|
#define EMC_CLIENTID_LENGTH 23 + 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef EMSESP_MQTT_STACKSIZE
|
||||||
|
#define EMC_TASK_STACK_SIZE EMSESP_MQTT_STACKSIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef EMC_TASK_STACK_SIZE
|
#ifndef EMC_TASK_STACK_SIZE
|
||||||
#define EMC_TASK_STACK_SIZE 5120
|
#define EMC_TASK_STACK_SIZE 5120
|
||||||
#endif
|
#endif
|
||||||
@@ -66,14 +70,10 @@ the LICENSE file.
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if EMC_USE_MEMPOOL
|
#if EMC_USE_MEMPOOL
|
||||||
#ifndef EMC_NUM_POOL_ELEMENTS
|
#ifndef EMC_NUM_POOL_ELEMENTS
|
||||||
#define EMC_NUM_POOL_ELEMENTS 32
|
#define EMC_NUM_POOL_ELEMENTS 32
|
||||||
#endif
|
#endif
|
||||||
#ifndef EMC_SIZE_POOL_ELEMENTS
|
#ifndef EMC_SIZE_POOL_ELEMENTS
|
||||||
#define EMC_SIZE_POOL_ELEMENTS 128
|
#define EMC_SIZE_POOL_ELEMENTS 128
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef TASMOTA_SDK
|
|
||||||
#define EMC_CLIENT_SECURE
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -62,14 +62,19 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
|
|||||||
_xSemaphore = xSemaphoreCreateMutex();
|
_xSemaphore = xSemaphoreCreateMutex();
|
||||||
EMC_SEMAPHORE_GIVE(); // release before first use
|
EMC_SEMAPHORE_GIVE(); // release before first use
|
||||||
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
|
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
|
||||||
|
if (core > 1) {
|
||||||
|
xTaskCreate((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle);
|
||||||
|
} else {
|
||||||
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core);
|
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
(void) useInternalTask;
|
(void) useInternalTask;
|
||||||
(void) priority;
|
(void) priority;
|
||||||
(void) core;
|
(void) core;
|
||||||
#endif
|
#endif
|
||||||
_clientId = _generatedClientId;
|
_clientId = _generatedClientId;
|
||||||
|
_core = core;
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttClient::~MqttClient() {
|
MqttClient::~MqttClient() {
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ class MqttClient {
|
|||||||
const char* getClientId() const;
|
const char* getClientId() const;
|
||||||
size_t queueSize(); // No const because of mutex
|
size_t queueSize(); // No const because of mutex
|
||||||
void loop();
|
void loop();
|
||||||
|
uint32_t stack() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
return uxTaskGetStackHighWaterMark(_taskHandle);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
uint8_t core() {
|
||||||
|
return _core;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
|
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
|
||||||
@@ -98,6 +108,7 @@ class MqttClient {
|
|||||||
uint8_t _willQos;
|
uint8_t _willQos;
|
||||||
bool _willRetain;
|
bool _willRetain;
|
||||||
uint32_t _timeout;
|
uint32_t _timeout;
|
||||||
|
uint8_t _core;
|
||||||
|
|
||||||
// state is protected to allow state changes by the transport system, defined in child classes
|
// state is protected to allow state changes by the transport system, defined in child classes
|
||||||
// eg. to allow AsyncTCP
|
// eg. to allow AsyncTCP
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ the LICENSE file.
|
|||||||
|
|
||||||
#include "ClientPosix.h"
|
#include "ClientPosix.h"
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ the LICENSE file.
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -6,51 +6,50 @@ For a copy, see <https://opensource.org/licenses/MIT> or
|
|||||||
the LICENSE file.
|
the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
#ifndef NO_TLS_SUPPORT
|
||||||
|
|
||||||
#include "ClientSecureSync.h"
|
#include "ClientSecureSync.h"
|
||||||
#include <lwip/sockets.h> // socket options
|
#include <lwip/sockets.h>
|
||||||
|
#include "../Config.h"
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
|
|
||||||
ClientSecureSync::ClientSecureSync()
|
ClientSecureSync::ClientSecureSync()
|
||||||
: client() {
|
: client() {
|
||||||
// empty
|
client.setClient(&basic_client, true);
|
||||||
|
client.setBufferSizes(EMC_RX_BUFFER_SIZE, EMC_TX_BUFFER_SIZE);
|
||||||
|
client.setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientSecureSync::~ClientSecureSync() {
|
||||||
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
|
bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
|
||||||
bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool
|
bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool
|
||||||
if (ret) {
|
if (ret) {
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
|
||||||
client.setNoDelay(true);
|
|
||||||
#elif defined(ARDUINO_ARCH_ESP32)
|
|
||||||
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
||||||
int val = true;
|
int val = true;
|
||||||
client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientSecureSync::connect(const char* host, uint16_t port) {
|
bool ClientSecureSync::connect(const char * host, uint16_t port) {
|
||||||
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
|
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
|
||||||
if (ret) {
|
if (ret) {
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
|
||||||
client.setNoDelay(true);
|
|
||||||
#elif defined(ARDUINO_ARCH_ESP32)
|
|
||||||
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
||||||
int val = true;
|
int val = true;
|
||||||
client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ClientSecureSync::write(const uint8_t* buf, size_t size) {
|
size_t ClientSecureSync::write(const uint8_t * buf, size_t size) {
|
||||||
return client.write(buf, size);
|
return client.write(buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClientSecureSync::read(uint8_t* buf, size_t size) {
|
int ClientSecureSync::read(uint8_t * buf, size_t size) {
|
||||||
return client.read(buf, size);
|
return client.read(buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,11 @@ the LICENSE file.
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
#ifndef NO_TLS_SUPPORT
|
||||||
|
|
||||||
// Added for EMS-ESP
|
// #include "esp_tls.h"
|
||||||
#include "../Config.h"
|
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
|
||||||
#include <WiFiClientSecure.h> // includes IPAddress
|
|
||||||
#else
|
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
#endif
|
#include <ESP_SSLClient.h>
|
||||||
#include "Transport.h"
|
#include "Transport.h"
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
@@ -24,6 +20,7 @@ namespace espMqttClientInternals {
|
|||||||
class ClientSecureSync : public Transport {
|
class ClientSecureSync : public Transport {
|
||||||
public:
|
public:
|
||||||
ClientSecureSync();
|
ClientSecureSync();
|
||||||
|
~ClientSecureSync();
|
||||||
bool connect(IPAddress ip, uint16_t port) override;
|
bool connect(IPAddress ip, uint16_t port) override;
|
||||||
bool connect(const char * host, uint16_t port) override;
|
bool connect(const char * host, uint16_t port) override;
|
||||||
size_t write(const uint8_t * buf, size_t size) override;
|
size_t write(const uint8_t * buf, size_t size) override;
|
||||||
@@ -31,12 +28,9 @@ class ClientSecureSync : public Transport {
|
|||||||
void stop() override;
|
void stop() override;
|
||||||
bool connected() override;
|
bool connected() override;
|
||||||
bool disconnected() override;
|
bool disconnected() override;
|
||||||
// added for EMS-ESP
|
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
WiFiClient basic_client;
|
||||||
WiFiClientSecure client;
|
ESP_SSLClient client;
|
||||||
#else
|
|
||||||
WiFiClient client;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace espMqttClientInternals
|
} // namespace espMqttClientInternals
|
||||||
|
|||||||
@@ -8,50 +8,6 @@ the LICENSE file.
|
|||||||
|
|
||||||
#include "espMqttClient.h"
|
#include "espMqttClient.h"
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
|
||||||
espMqttClient::espMqttClient()
|
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
|
||||||
, _client() {
|
|
||||||
_transport = &_client;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure::espMqttClientSecure()
|
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
|
||||||
, _client() {
|
|
||||||
_transport = &_client;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
|
||||||
_client.client.setInsecure();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setFingerprint(const uint8_t fingerprint[20]) {
|
|
||||||
_client.client.setFingerprint(fingerprint);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setTrustAnchors(const X509List * ta) {
|
|
||||||
_client.client.setTrustAnchors(ta);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setClientRSACert(const X509List * cert, const PrivateKey * sk) {
|
|
||||||
_client.client.setClientRSACert(cert, sk);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setClientECCert(const X509List * cert, const PrivateKey * sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
|
|
||||||
_client.client.setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setCertStore(CertStoreBase * certStore) {
|
|
||||||
_client.client.setCertStore(certStore);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
|
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
|
||||||
: MqttClientSetup(useInternalTask)
|
: MqttClientSetup(useInternalTask)
|
||||||
@@ -78,51 +34,45 @@ espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core)
|
|||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setInsecure();
|
_client.client.setInsecure();
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) {
|
espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setCACert(rootCA);
|
_client.client.setCACert(rootCA);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) {
|
espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setCertificate(clientCa);
|
_client.client.setCertificate(clientCa);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) {
|
espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setPrivateKey(privateKey);
|
_client.client.setPrivateKey(privateKey);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) {
|
espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setPreSharedKey(pskIdent, psKey);
|
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
espMqttClient::espMqttClient()
|
espMqttClient::espMqttClient()
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
#elif defined(_WIN32) || defined(__APPLE__)
|
|
||||||
// Windows
|
|
||||||
espMqttClient::espMqttClient()
|
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) {
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ the LICENSE file.
|
|||||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||||
#include "Transport/ClientSync.h"
|
#include "Transport/ClientSync.h"
|
||||||
#include "Transport/ClientSecureSync.h"
|
#include "Transport/ClientSecureSync.h"
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__) || defined(__APPLE__)
|
||||||
#include "Transport/ClientPosix.h"
|
#include "Transport/ClientPosix.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -65,10 +65,16 @@ class espMqttClientSecure : public MqttClientSetup<espMqttClientSecure> {
|
|||||||
espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey);
|
espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
#ifndef NO_TLS_SUPPORT
|
||||||
espMqttClientInternals::ClientSecureSync _client;
|
espMqttClientInternals::ClientSecureSync _client;
|
||||||
|
#else
|
||||||
|
espMqttClientInternals::ClientSync _client;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
class espMqttClient : public MqttClientSetup<espMqttClient> {
|
class espMqttClient : public MqttClientSetup<espMqttClient> {
|
||||||
public:
|
public:
|
||||||
espMqttClient();
|
espMqttClient();
|
||||||
@@ -76,10 +82,4 @@ class espMqttClient : public MqttClientSetup<espMqttClient> {
|
|||||||
protected:
|
protected:
|
||||||
espMqttClientInternals::ClientPosix _client;
|
espMqttClientInternals::ClientPosix _client;
|
||||||
};
|
};
|
||||||
#elif defined(_WIN32) || defined(__APPLE__)
|
|
||||||
class espMqttClient : public MqttClientSetup<espMqttClient> {
|
|
||||||
public:
|
|
||||||
espMqttClient();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Živanović
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# About
|
|
||||||
|
|
||||||
This project is MIT-licensed, C++14 implementation of [semantic versioning](http://semver.org) parser and comparator with support for modifying parsed version strings. Semantic versioning 2.0.0 specification is supported out-of-the-box and the code should be flexible-enough to support future revisions or other similar versioning schemes.
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Based on https://github.com/zmarko/semver
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include "semver200.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Compare normal version identifiers.
|
|
||||||
int compare_normal(const Version_data & l, const Version_data & r) {
|
|
||||||
if (l.major > r.major)
|
|
||||||
return 1;
|
|
||||||
if (l.major < r.major)
|
|
||||||
return -1;
|
|
||||||
if (l.minor > r.minor)
|
|
||||||
return 1;
|
|
||||||
if (l.minor < r.minor)
|
|
||||||
return -1;
|
|
||||||
if (l.patch > r.patch)
|
|
||||||
return 1;
|
|
||||||
if (l.patch < r.patch)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare alphanumeric prerelease identifiers.
|
|
||||||
inline int cmp_alnum_prerel_ids(const string & l, const string & r) {
|
|
||||||
auto cmp = l.compare(r);
|
|
||||||
if (cmp == 0) {
|
|
||||||
return cmp;
|
|
||||||
} else {
|
|
||||||
return cmp > 0 ? 1 : -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare numeric prerelease identifiers.
|
|
||||||
inline int cmp_num_prerel_ids(const string & l, const string & r) {
|
|
||||||
long long li = stoll(l);
|
|
||||||
long long ri = stoll(r);
|
|
||||||
if (li == ri)
|
|
||||||
return 0;
|
|
||||||
return li > ri ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
using Prerel_type_pair = pair<Id_type, Id_type>;
|
|
||||||
using Prerel_id_comparator = function<int(const string &, const string &)>;
|
|
||||||
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {{{Id_type::alnum, Id_type::alnum}, cmp_alnum_prerel_ids},
|
|
||||||
{{Id_type::alnum, Id_type::num}, [](const string &, const string &) { return 1; }},
|
|
||||||
{{Id_type::num, Id_type::alnum}, [](const string &, const string &) { return -1; }},
|
|
||||||
{{Id_type::num, Id_type::num}, cmp_num_prerel_ids}};
|
|
||||||
|
|
||||||
// Compare prerelease identifiers based on their types.
|
|
||||||
inline int compare_prerel_identifiers(const Prerelease_identifier & l, const Prerelease_identifier & r) {
|
|
||||||
auto cmp = comparators.at({l.second, r.second});
|
|
||||||
return cmp(l.first, r.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int cmp_rel_prerel(const Prerelease_identifiers & l, const Prerelease_identifiers & r) {
|
|
||||||
if (l.empty() && !r.empty())
|
|
||||||
return 1;
|
|
||||||
if (r.empty() && !l.empty())
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int Semver200_comparator::compare(const Version_data & l, const Version_data & r) const {
|
|
||||||
// Compare normal version components.
|
|
||||||
int cmp = compare_normal(l, r);
|
|
||||||
if (cmp != 0)
|
|
||||||
return cmp;
|
|
||||||
|
|
||||||
// Compare if one version is release and the other prerelease - release is always higher.
|
|
||||||
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
|
|
||||||
if (cmp != 0)
|
|
||||||
return cmp;
|
|
||||||
|
|
||||||
// Compare prerelease by looking at each identifier: numeric ones are compared as numbers,
|
|
||||||
// alphanum as ASCII strings.
|
|
||||||
auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size());
|
|
||||||
for (size_t i = 0; i < shorter; i++) {
|
|
||||||
cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]);
|
|
||||||
if (cmp != 0)
|
|
||||||
return cmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prerelease identifiers are the same, to the length of the shorter version string;
|
|
||||||
// if they are the same length, then versions are equal, otherwise, longer one wins.
|
|
||||||
if (l.prerelease_ids.size() == r.prerelease_ids.size())
|
|
||||||
return 0;
|
|
||||||
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include "semver200.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
enum class Parser_state { major, minor, patch, prerelease, build };
|
|
||||||
|
|
||||||
using Validator = function<void(const string &, const char)>;
|
|
||||||
using State_transition_hook = function<void(string &)>;
|
|
||||||
/// State transition is described by a character that triggers it, a state to transition to and
|
|
||||||
/// optional hook to be invoked on transition.
|
|
||||||
using Transition = tuple<const char, Parser_state, State_transition_hook>;
|
|
||||||
using Transitions = vector<Transition>;
|
|
||||||
using State = tuple<Transitions, string &, Validator>;
|
|
||||||
using State_machine = std::map<Parser_state, State>;
|
|
||||||
|
|
||||||
// Ranges of characters allowed in prerelease and build identifiers.
|
|
||||||
const vector<pair<char, char>> allowed_prerel_id_chars = {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {'-', '-'}};
|
|
||||||
|
|
||||||
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) {
|
|
||||||
return make_tuple(c, p, pth);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Parse_error(const std::string & s) {
|
|
||||||
// EMSESP::logger().err("parse error: %s", s.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advance parser state machine by a single step.
|
|
||||||
/**
|
|
||||||
Perform single step of parser state machine: if character matches one from transition tables -
|
|
||||||
trigger transition to next state; otherwise, validate if current token is in legal state
|
|
||||||
(throw Parse_error if not) and then add character to current token; State transition includes
|
|
||||||
preparing various vars for next state and invoking state transition hook (if specified) which is
|
|
||||||
where whole tokens are validated.
|
|
||||||
*/
|
|
||||||
inline void process_char(const char c, Parser_state & cstate, Parser_state & pstate, const Transitions & transitions, string & target, Validator validate) {
|
|
||||||
for (const auto & transition : transitions) {
|
|
||||||
if (c == get<0>(transition)) {
|
|
||||||
if (get<2>(transition))
|
|
||||||
get<2>(transition)(target);
|
|
||||||
pstate = cstate;
|
|
||||||
cstate = get<1>(transition);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
validate(target, c);
|
|
||||||
target.push_back(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate normal (major, minor, patch) version components.
|
|
||||||
inline void normal_version_validator(const string & tgt, const char c) {
|
|
||||||
if (c < '0' || c > '9')
|
|
||||||
Parse_error("invalid character encountered: " + string(1, c));
|
|
||||||
if (tgt.compare(0, 1, "0") == 0)
|
|
||||||
Parse_error("leading 0 not allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
|
|
||||||
inline void prerelease_version_validator(const string &, const char c) {
|
|
||||||
bool res = false;
|
|
||||||
for (const auto & r : allowed_prerel_id_chars) {
|
|
||||||
res |= (c >= r.first && c <= r.second);
|
|
||||||
}
|
|
||||||
if (!res)
|
|
||||||
Parse_error("invalid character encountered: " + string(1, c));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool is_identifier_numeric(const string & id) {
|
|
||||||
return id.find_first_not_of("0123456789") == string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool check_for_leading_0(const string & str) {
|
|
||||||
return str.length() > 1 && str[0] == '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
|
|
||||||
void prerelease_hook_impl(string & id, Prerelease_identifiers & prerelease) {
|
|
||||||
if (id.empty())
|
|
||||||
Parse_error("version identifier cannot be empty");
|
|
||||||
Id_type t = Id_type::alnum;
|
|
||||||
if (is_identifier_numeric(id)) {
|
|
||||||
t = Id_type::num;
|
|
||||||
if (check_for_leading_0(id)) {
|
|
||||||
Parse_error("numeric identifiers cannot have leading 0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prerelease.push_back(Prerelease_identifier(id, t));
|
|
||||||
id.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate every individual build identifier and add it to collection.
|
|
||||||
void build_hook_impl(string & id, Parser_state & pstate, Build_identifiers & build, std::string & prerelease_id, Prerelease_identifiers & prerelease) {
|
|
||||||
// process last token left from parsing prerelease data
|
|
||||||
if (pstate == Parser_state::prerelease)
|
|
||||||
prerelease_hook_impl(prerelease_id, prerelease);
|
|
||||||
if (id.empty())
|
|
||||||
Parse_error("version identifier cannot be empty");
|
|
||||||
build.push_back(id);
|
|
||||||
id.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
/// Parse semver 2.0.0-compatible string to Version_data structure.
|
|
||||||
/**
|
|
||||||
Version text parser is implemented as a state machine. In each step one successive character from version
|
|
||||||
string is consumed and is either added to current token or triggers state transition. Hooks can be
|
|
||||||
injected into state transitions for validation/customization purposes.
|
|
||||||
*/
|
|
||||||
Version_data Semver200_parser::parse(const string & s) const {
|
|
||||||
string major;
|
|
||||||
string minor;
|
|
||||||
string patch;
|
|
||||||
string prerelease_id;
|
|
||||||
string build_id;
|
|
||||||
Prerelease_identifiers prerelease;
|
|
||||||
Build_identifiers build;
|
|
||||||
Parser_state cstate{Parser_state::major};
|
|
||||||
Parser_state pstate;
|
|
||||||
|
|
||||||
auto prerelease_hook = [&](string & id) { prerelease_hook_impl(id, prerelease); };
|
|
||||||
|
|
||||||
auto build_hook = [&](string & id) { build_hook_impl(id, pstate, build, prerelease_id, prerelease); };
|
|
||||||
|
|
||||||
// State transition tables
|
|
||||||
auto major_trans = {mkx('.', Parser_state::minor, {})};
|
|
||||||
auto minor_trans = {mkx('.', Parser_state::patch, {})};
|
|
||||||
auto patch_trans = {mkx('-', Parser_state::prerelease, {}), mkx('+', Parser_state::build, {})};
|
|
||||||
auto prerelease_trans = {// When identifier separator (.) is found, stay in the same state but invoke hook
|
|
||||||
// in order to process each individual identifier separately.
|
|
||||||
mkx('.', Parser_state::prerelease, prerelease_hook),
|
|
||||||
mkx('+', Parser_state::build, {})};
|
|
||||||
auto build_trans = {// Same stay-in-the-same-state-but-invoke-hook trick from above.
|
|
||||||
mkx('.', Parser_state::build, build_hook)};
|
|
||||||
|
|
||||||
State_machine state_machine = {{Parser_state::major, State{major_trans, major, normal_version_validator}},
|
|
||||||
{Parser_state::minor, State{minor_trans, minor, normal_version_validator}},
|
|
||||||
{Parser_state::patch, State{patch_trans, patch, normal_version_validator}},
|
|
||||||
{Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}},
|
|
||||||
{Parser_state::build, State{build_trans, build_id, prerelease_version_validator}}};
|
|
||||||
|
|
||||||
// Main loop.
|
|
||||||
for (const auto & c : s) {
|
|
||||||
auto state = state_machine.at(cstate);
|
|
||||||
process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger appropriate hooks in order to process last token, because no state transition was
|
|
||||||
// triggered for it.
|
|
||||||
if (cstate == Parser_state::prerelease) {
|
|
||||||
prerelease_hook(prerelease_id);
|
|
||||||
} else if (cstate == Parser_state::build) {
|
|
||||||
build_hook(build_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Version_data{stoi(major), stoi(minor), stoi(patch), prerelease, build};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
/// Parse string into Version_data structure according to semantic versioning 2.0.0 rules.
|
|
||||||
struct Semver200_parser {
|
|
||||||
Version_data parse(const std::string &) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Compare Version_data to another using semantic versioning 2.0.0 rules.
|
|
||||||
struct Semver200_comparator {
|
|
||||||
int compare(const Version_data &, const Version_data &) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Concrete version class that binds all semver 2.0.0 functionality together.
|
|
||||||
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator> {
|
|
||||||
public:
|
|
||||||
Semver200_version()
|
|
||||||
: Basic_version{Semver200_parser(), Semver200_comparator()} {
|
|
||||||
}
|
|
||||||
|
|
||||||
Semver200_version(const std::string & v)
|
|
||||||
: Basic_version{v, Semver200_parser(), Semver200_comparator()} {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
/// Type of prerelease identifier: alphanumeric or numeric.
|
|
||||||
/**
|
|
||||||
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
|
|
||||||
numeric identifiers are compared as numbers.
|
|
||||||
*/
|
|
||||||
enum class Id_type {
|
|
||||||
alnum, ///< Identifier is alphanumerical
|
|
||||||
num ///< Identifier is numeric
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Container for prerelease identifier value and it's type.
|
|
||||||
/**
|
|
||||||
Prerelease version string consist of an optional series of dot-separated identifiers.
|
|
||||||
These identifiers can be either numerical or alphanumerical.
|
|
||||||
This structure describes one such identifier.
|
|
||||||
*/
|
|
||||||
using Prerelease_identifier = std::pair<std::string, Id_type>;
|
|
||||||
|
|
||||||
/// Container for all prerelease identifiers for a given version string.
|
|
||||||
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
|
|
||||||
|
|
||||||
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
|
|
||||||
using Build_identifier = std::string;
|
|
||||||
|
|
||||||
/// Container for all build identifiers of a given version string.
|
|
||||||
using Build_identifiers = std::vector<Build_identifier>;
|
|
||||||
|
|
||||||
/// Description of version broken into parts, as per semantic versioning specification.
|
|
||||||
struct Version_data {
|
|
||||||
Version_data(const int M, const int m, const int p, const Prerelease_identifiers & pr, const Build_identifiers & b)
|
|
||||||
: major{M}
|
|
||||||
, minor{m}
|
|
||||||
, patch{p}
|
|
||||||
, prerelease_ids{pr}
|
|
||||||
, build_ids{b} {
|
|
||||||
}
|
|
||||||
|
|
||||||
int major; ///< Major version, change only on incompatible API modifications.
|
|
||||||
int minor; ///< Minor version, change on backwards-compatible API modifications.
|
|
||||||
int patch; ///< Patch version, change only on bugfixes.
|
|
||||||
|
|
||||||
/// Optional series of prerelease identifiers.
|
|
||||||
Prerelease_identifiers prerelease_ids;
|
|
||||||
|
|
||||||
/// Optional series of build identifiers.
|
|
||||||
Build_identifiers build_ids;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Forward declaration required for operators' template declarations.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
class Basic_version;
|
|
||||||
|
|
||||||
/// Test if left-hand version operand is of lower precedence than the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator<(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version operand if of equal precedence as the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator==(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version and right-hand version are of different precedence.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator!=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version operand is of higher precedence than the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator>(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator>=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator<=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Base class for various version parsing, precedence ordering and data manipulation schemes.
|
|
||||||
/**
|
|
||||||
Basic_version class describes general version object without prescribing parsing,
|
|
||||||
validation, comparison and modification rules. These rules are implemented by supplied Parser, Comparator
|
|
||||||
and Modifier objects.
|
|
||||||
*/
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
class Basic_version {
|
|
||||||
public:
|
|
||||||
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string, Comparator for comparison and Modifier for modification.
|
|
||||||
Basic_version(Parser, Comparator);
|
|
||||||
|
|
||||||
/// Construct Basic_version object using Parser to parse supplied version string, Comparator for comparison and Modifier for modification.
|
|
||||||
Basic_version(const std::string &, Parser, Comparator);
|
|
||||||
|
|
||||||
/// Construct Basic_version object using supplied Version_data, Parser, Comparator and Modifier objects.
|
|
||||||
Basic_version(const Version_data &, Parser, Comparator);
|
|
||||||
|
|
||||||
/// Construct Basic_version by copying data from another one.
|
|
||||||
Basic_version(const Basic_version &);
|
|
||||||
|
|
||||||
/// Copy version data from another Basic_version to this one.
|
|
||||||
Basic_version & operator=(const Basic_version &);
|
|
||||||
|
|
||||||
int major() const; ///< Get major version.
|
|
||||||
int minor() const; ///< Get minor version.
|
|
||||||
int patch() const; ///< Get patch version.
|
|
||||||
const std::string prerelease() const; ///< Get prerelease version string.
|
|
||||||
const std::string build() const; ///< Get build version string.
|
|
||||||
|
|
||||||
friend bool operator< <>(const Basic_version &, const Basic_version &);
|
|
||||||
friend bool operator==<>(const Basic_version &, const Basic_version &);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Parser parser_;
|
|
||||||
Comparator comparator_;
|
|
||||||
Version_data ver_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
|
|
||||||
#include "version.inl"
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user