795 Commits

Author SHA1 Message Date
proddy
ee3a5d231a Merge remote-tracking branch 'origin/dev' 2026-05-12 20:15:32 +02:00
Proddy
ddb2390bf7 Merge pull request #3071 from proddy/dev
support filesystem ota
2026-05-12 19:59:58 +02:00
Proddy
91912cf5be Merge branch 'emsesp:dev' into dev 2026-05-12 19:49:18 +02:00
Proddy
4f9851a9fe Merge pull request #3072 from MichaelDvP/dev
add polarity setting for digital_in sensor, #3070
2026-05-12 19:48:27 +02:00
MichaelDvP
a897afd4e6 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-05-11 14:36:44 +02:00
MichaelDvP
344dcd0019 add polarity setting for digital_in sensor, #3070 2026-05-11 14:35:48 +02:00
proddy
7854349dbe support filesystem ota 2026-05-10 13:22:00 +02:00
proddy
d1d046f3fd package update 2026-05-10 13:21:47 +02:00
proddy
b988c67c8e update example 2026-05-10 13:21:37 +02:00
proddy
37a94f8e0f auto-formatting 2026-05-10 13:21:28 +02:00
Proddy
017a7de639 Merge pull request #3068 from MichaelDvP/dev
tx-mode: auto, default bus-id 0x49
2026-05-09 08:11:59 +02:00
MichaelDvP
7b61429a02 fix testdata to device-id 0x49, txMode 5 2026-05-09 08:01:29 +02:00
MichaelDvP
fed15f0f96 tx-mode: auto, default bus-id 0x49 2026-05-07 21:08:56 +02:00
Proddy
ad5d13168b Merge pull request #3066 from MichaelDvP/dev
fix modbus start #3064
2026-05-07 18:05:48 +02:00
MichaelDvP
ae5beccb9d dev.20, fixes #3064, handling of optional gpios 2026-05-07 12:02:33 +02:00
MichaelDvP
764c660b14 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-05-06 09:23:27 +02:00
MichaelDvP
ca0f32b087 update pkg 2026-05-06 09:06:15 +02:00
Proddy
8ad91de54a Merge pull request #3061 from MichaelDvP/dev 2026-05-06 09:05:10 +02:00
MichaelDvP
58da157272 chore: update generated files for v3.8.2-dev.20 2026-05-05 16:04:29 +00:00
MichaelDvP
b5014bf9ac dev.20, check and set 0x470 to summer2_typeids[0] ony if received. #2686 2026-05-05 17:51:52 +02:00
MichaelDvP
14351436a7 fixes #3055, revert commit daffdcf 2026-05-05 10:33:56 +02:00
Proddy
469d412951 Merge pull request #3045 from MichaelDvP/dev
fix legegram length, #2969
2026-04-24 17:14:58 +02:00
MichaelDvP
6edbac86e2 fix legegram length, #2969 2026-04-24 14:46:53 +02:00
proddy
db2be70d66 chore: update generated files for v3.8.2-dev.18 2026-04-22 14:22:25 +00:00
Proddy
c36f231990 Merge pull request #3042 from proddy/dev
minor updates
2026-04-22 16:10:20 +02:00
proddy
26102121e1 async-validator fixes 2026-04-22 16:07:56 +02:00
proddy
8e64c6303e package update 2026-04-22 15:43:58 +02:00
proddy
74c76eb90b remove YIELD 2026-04-22 15:43:29 +02:00
proddy
daffdcf58e https://github.com/emsesp/EMS-ESP32/issues/2686 2026-04-22 15:43:20 +02:00
Proddy
4bc4fa903f Merge pull request #3040 from MichaelDvP/dev
version checks prelease
2026-04-22 15:11:02 +02:00
MichaelDvP
29380f0303 version checks prelease 2026-04-22 14:59:46 +02:00
Proddy
6b2370b79d Merge pull request #3035 from mattreim/dev
Update German translation
2026-04-22 08:34:14 +02:00
Proddy
dbc636c9bf Merge pull request #3036 from MichaelDvP/dev
small fixes
2026-04-22 08:33:38 +02:00
MichaelDvP
0c0660c04b Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-04-22 07:41:29 +02:00
mattreim
c9fd076394 Update German translation 2026-04-22 01:05:05 +02:00
MichaelDvP
35550553be check fetch length for custom entities, dev17 2026-04-21 19:51:48 +02:00
MichaelDvP
06ff219385 version check order 2026-04-21 18:58:42 +02:00
MichaelDvP
e705a5629f fetch length of holiday to 18 2026-04-21 18:58:13 +02:00
Proddy
cb3c9653ce Merge pull request #3032 from proddy/dev
rename build_webUI for Python
2026-04-20 15:38:52 +02:00
proddy
0b5a83f6ae package update (vite fix) 2026-04-20 15:37:50 +02:00
MichaelDvP
a079169005 backup nvs1 if exist 2026-04-20 13:18:50 +02:00
proddy
845c51d5f9 rename build_webUI for Python 2026-04-19 21:23:59 +02:00
Proddy
d6d3a034ad Merge pull request #3030 from proddy/dev
build_webUI -> build-webUI
2026-04-19 19:14:31 +02:00
proddy
ece08d96ee build_webUI -> build-webUI 2026-04-19 19:09:45 +02:00
MichaelDvP
854f4d559a Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-04-19 15:40:15 +02:00
Proddy
f186f2a8f2 Merge pull request #3028 from proddy/dev
heap memory optimizations
2026-04-19 15:15:45 +02:00
proddy
6b68cb7c61 store UTC epoch time and convert to localtime when render (fixes bug as TZ not set) 2026-04-19 15:12:22 +02:00
proddy
a1e0288e09 close dialog after downloading 2026-04-19 15:11:41 +02:00
proddy
e6c173bdf9 don't show system backup as it's the same page! 2026-04-19 15:11:29 +02:00
MichaelDvP
d9b6de0652 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2026-04-19 13:41:01 +02:00
MichaelDvP
c54da18822 remove pr#3021 2026-04-19 13:40:51 +02:00
proddy
555801dc5c remove lazy loading, optimize chunking 2026-04-19 10:05:47 +02:00
proddy
6f81945da6 typo 2026-04-18 18:43:03 +02:00
proddy
865c309475 remove c++17 2026-04-18 18:39:45 +02:00
proddy
77b8b21aea use C++ 20 (espressif32@6.13.0 still uses GCC 8 so only 2a supported) 2026-04-18 18:34:47 +02:00
proddy
2f5edffec6 update changelog 2026-04-18 18:29:30 +02:00
proddy
71de64502e include cstdint for uint8_t on new GCC 2026-04-18 17:52:05 +02:00
proddy
6994d3559a package update 2026-04-18 17:43:31 +02:00
proddy
a7d484d218 3.8.2-dev.16 2026-04-18 17:43:23 +02:00
proddy
a810c41acd exclude js 2026-04-18 17:43:14 +02:00
proddy
2fbfdf94ab minor optimizations, use EMSESP_Version, only call esp_image_verify() and store the entry for partitions that actually have a value 2026-04-18 17:40:21 +02:00
proddy
2d7c8f0863 remove semver 2026-04-18 17:36:08 +02:00
proddy
c3b734ab47 add back LTO, remove semver 2026-04-18 17:35:57 +02:00
proddy
644abf105d replace semver with home grown simplier alternative 2026-04-18 17:35:11 +02:00
proddy
5a8a451774 improve chunking 2026-04-18 17:34:48 +02:00
proddy
dae139aa01 single static-content handler serving all assets 2026-04-18 17:33:13 +02:00
proddy
b13fcd8939 single static-content handler serving all assets 2026-04-18 17:32:54 +02:00
Proddy
26b42b4eea Merge pull request #3027 from proddy/dev
update github actions
2026-04-18 10:22:39 +02:00
proddy
c9005e8aa9 upgrade github actions 2026-04-18 10:02:25 +02:00
proddy
6658b11adf use c++20 2026-04-17 18:01:51 +02:00
proddy
e542f5809f remove bogus file 2026-04-17 18:01:44 +02:00
proddy
ce1dd6233d update 2026-04-17 18:01:37 +02:00
proddy
fe488443da package update 2026-04-17 18:01:26 +02:00
Proddy
b264a39780 Merge pull request #3026 from MichaelDvP/dev 2026-04-17 16:41:59 +02:00
MichaelDvP
d2302eaa85 dev15, rollback mbedlt change for memory saving 2026-04-17 15:59:13 +02:00
Proddy
994706c9f2 Merge pull request #3023 from MichaelDvP/dev
fetch telegrams with length, dev14
2026-04-16 12:22:06 +02:00
MichaelDvP
8a72ab42cb dev 14, changelog 2026-04-16 08:18:17 +02:00
MichaelDvP
c4db8e3914 set length for more fetch telegrams 2026-04-16 08:10:43 +02:00
MichaelDvP
8d0225e595 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-04-16 08:09:58 +02:00
Proddy
966049d0c9 Merge pull request #3006 from proddy/dev
sync with core3 features
2026-04-15 21:29:24 +02:00
proddy
907a65a701 show link to backup page 2026-04-15 21:19:09 +02:00
proddy
f97b8e14e7 package update 2026-04-15 21:18:51 +02:00
proddy
e65f634b21 3.8.2 2026-04-15 21:18:38 +02:00
proddy
fc71ed2b9d 3.8.2 2026-04-15 21:18:27 +02:00
proddy
5a8195d430 auto-formatting 2026-04-15 08:12:22 +02:00
proddy
24a7a607f3 add test data 2026-04-15 08:08:49 +02:00
proddy
061f9ffc52 update prettier 2026-04-15 08:08:45 +02:00
proddy
9e17936bfc fix lint 2026-04-14 21:13:25 +02:00
Proddy
18bb2c4f39 Merge branch 'emsesp:dev' into dev 2026-04-14 21:09:31 +02:00
proddy
7c3782a43f upload warnings 2026-04-14 21:08:45 +02:00
proddy
3ac807bdd5 text change 2026-04-14 21:08:04 +02:00
proddy
1111458863 upgrade message warnings 2026-04-14 09:31:50 +02:00
proddy
99c5e2230c fix link 2026-04-14 09:31:35 +02:00
proddy
3317aa845a package update 2026-04-14 09:31:19 +02:00
proddy
97cd657336 fix links 2026-04-14 09:31:05 +02:00
MichaelDvP
3338f919bd Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2026-04-14 08:09:50 +02:00
proddy
7dd13bcab7 mui upgrade 2026-04-13 23:30:36 +02:00
MichaelDvP
f226cb359f Merge branch 'dev' of https://github.com/MichaelDvP/EMS-ESP32 into dev 2026-04-13 21:04:23 +02:00
MichaelDvP
abbba0aa42 telegram length for fetched telegrams 2026-04-13 20:57:20 +02:00
MichaelDvP
39b5a52b01 chore: update generated files for v3.8.2-dev.13 2026-04-13 12:12:42 +00:00
MichaelDvP
b6c3fc5bee read fragmented telegram 0x484, #3017 2026-04-13 14:00:12 +02:00
MichaelDvP
909bea00df update espressif32 6.13.0 2026-04-13 13:59:12 +02:00
MichaelDvP
9522945e06 uart buffer size 2026-04-13 13:58:30 +02:00
MichaelDvP
d6a9f2a731 prepare for translations #3015, update pkg 2026-04-13 13:58:10 +02:00
proddy
0f30c81554 fix compile on linux/osx 2026-04-12 20:32:36 +02:00
MichaelDvP
e514ba4bb5 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-04-12 09:46:43 +02:00
Proddy
38e63e3eaa Merge pull request #3016 from misa1515/patch-30
Update index.ts
2026-04-11 22:44:28 +02:00
misa1515
0058324edd Update index.ts 2026-04-10 21:10:36 +02:00
MichaelDvP
ac143d607a http-client to heap 2026-04-10 14:20:17 +02:00
MichaelDvP
e9e3759db3 add solar ts3 2026-04-10 14:19:39 +02:00
proddy
fb09e10f19 sync with core3 features 2026-03-30 23:26:04 +02:00
Proddy
09473f17a0 Merge pull request #2994 from MichaelDvP/dev
dev.12, add dhw4, 5,.. circuits, #2991
2026-03-21 15:46:21 +01:00
MichaelDvP
bbc2de08a5 support dhw5... 2026-03-21 09:52:04 +01:00
MichaelDvP
df808a2bcf dev.12, add dhw4 circuit, #2991 2026-03-20 14:53:07 +01:00
MichaelDvP
d04e7c36f3 reset reason 2026-03-20 14:50:58 +01:00
MichaelDvP
205d826adb asyncWebserver 3.10.3, remove C6 (no core 2 support) 2026-03-20 14:48:34 +01:00
Proddy
4db8e43648 Merge pull request #2990 from MichaelDvP/dev
dev.11, fix #2988, asyncWebserver 3.10.2
2026-03-18 20:18:11 +01:00
MichaelDvP
cc60062678 dev.11, fix #2988, asyncWebserver 3.10.2 2026-03-18 10:53:27 +01:00
Proddy
e3305ab9db Merge pull request #2985 from MichaelDvP/dev
devcie class #2980 and version update #2981, dev10
2026-03-17 20:17:29 +01:00
MichaelDvP
b0157f288e update changelog, dev10 2026-03-17 10:16:01 +01:00
MichaelDvP
a9f50d9371 update version number fixes #2981 2026-03-16 12:55:27 +01:00
MichaelDvP
65a3226404 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-03-16 10:05:59 +01:00
Proddy
45690f5418 Merge pull request #2982 from misa1515/patch-29
Update locale_translations.h
2026-03-15 22:16:51 +01:00
misa1515
6836b6197f Update locale_translations.h 2026-03-15 21:27:58 +01:00
MichaelDvP
69d4163b9d fix device_class #2980 2026-03-15 14:12:47 +01:00
proddy
b1e974a82c chore: update generated files for v3.8.2-dev.9 2026-03-13 19:09:14 +00:00
Proddy
34a2b20be8 Merge pull request #2978 from MichaelDvP/dev
add basflowtemp #2969, add pumpkick #2965, add reset HP #2933, fix custom brand
2026-03-13 19:58:03 +01:00
MichaelDvP
f1fc8d9aae update testdata 2026-03-13 16:52:48 +01:00
MichaelDvP
b04355e3e1 update asyncwebserver 2026-03-13 10:32:33 +01:00
MichaelDvP
cd3ae5cdf2 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-03-13 10:22:24 +01:00
MichaelDvP
a261ca23af add baseflowtemp #2969 2026-03-13 10:15:51 +01:00
MichaelDvP
cb96904a5c fix custom brand use after free of c_str() in json. 2026-03-13 10:15:00 +01:00
MichaelDvP
4a2d78f8e1 minflowtemp taken from offset 13 or 8 2026-03-11 18:43:25 +01:00
Proddy
f5af4fb52f Merge pull request #2975 from mrkev-gh/esp32s3-no-psram
fix allowed pins for S32S3 without PSRAM
2026-03-11 17:36:38 +01:00
MichaelDvP
2037bc3a10 add reset of HP errors #2933, dev9 2026-03-07 12:02:09 +01:00
MichaelDvP
64d17d7c65 Test for minflowtemp 2026-03-07 11:43:12 +01:00
MichaelDvP
92e2633342 typo 2026-03-07 11:42:27 +01:00
mrkev-gh
96a7ea8a02 fix allowed pins for S32S3 without PSRAM
Some S32S3 do not have PSRAM (e.g. ESP32-S3FN8) and use those GPIO pins
2026-02-28 11:44:53 +01:00
MichaelDvP
5c4aaa4510 add pumpkick #2965, dev.8 2026-02-20 09:56:08 +01:00
Proddy
c05e1cb77b Merge pull request #2966 from MichaelDvP/dev
fixes for #2960 and #2962
2026-02-19 21:58:20 +01:00
MichaelDvP
5879ce4090 fix SRC mode setting from HA #2960 2026-02-18 08:14:47 +01:00
MichaelDvP
ac3e5c793c fix typo for SRC ha-climate creation 2026-02-17 10:09:22 +01:00
MichaelDvP
4326fb931b add prometheus metrics for analog/scheduler/custom #2962 2026-02-16 15:56:23 +01:00
MichaelDvP
ced7051ce7 add prometheus metrics for temperaturesensors 2026-02-16 12:05:45 +01:00
MichaelDvP
421da246ed fix SRC seltemp offset for auto mode #2960 2026-02-16 07:51:10 +01:00
MichaelDvP
148a721e17 read connect seltemp after mode/icon to create HA-climate 2026-02-15 16:49:21 +01:00
MichaelDvP
a811670c5a 3.8.2-dev.6, changelog 2026-02-15 12:03:33 +01:00
MichaelDvP
72f08a86cf fix SRC climate, #2960 2026-02-15 12:03:07 +01:00
MichaelDvP
27c471f45f set model for ems-esp devices, #2958 2026-02-15 12:02:36 +01:00
MichaelDvP
e303972d26 update AsyncWebserver and pkg 2026-02-15 12:01:50 +01:00
MichaelDvP
97bb03d703 add missing check for number mode change 2026-02-15 12:01:10 +01:00
Proddy
e9f77c1bde Merge pull request #2954 from MichaelDvP/dev
fix brand in HA
2026-02-12 17:54:41 +01:00
MichaelDvP
81cba6c0a8 fix brand in HA 2026-02-12 17:41:10 +01:00
Proddy
89029df25e Merge pull request #2953 from MichaelDvP/dev
customize device brand #2784
2026-02-12 13:19:46 +01:00
MichaelDvP
3463b6818d update testdata 2026-02-12 12:14:13 +01:00
MichaelDvP
349843e666 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-02-12 12:02:45 +01:00
MichaelDvP
96ae3bbbba customze device brand #2784 2026-02-12 12:01:39 +01:00
Proddy
b153364b60 Merge pull request #2947 from MichaelDvP/dev
save scheduler, custom to nvs, fix #2946
2026-02-10 19:39:59 +01:00
MichaelDvP
ccc40937fb chore: update generated files for v3.8.2-dev.3 2026-02-09 12:02:38 +00:00
MichaelDvP
909edf394d add back 4wayValve as bool, #2844 2026-02-09 09:36:13 +01:00
MichaelDvP
ac8ef646e9 fix standalone test 2026-02-08 14:46:54 +01:00
MichaelDvP
3ef279ea10 3.8.2-dev.3, changelog, update pkg 2026-02-08 14:17:20 +01:00
MichaelDvP
769beeda37 save scheduler active flag to nvs 2026-02-08 13:53:57 +01:00
MichaelDvP
f83404c216 add custom entity type NVS 2026-02-08 13:53:32 +01:00
MichaelDvP
c239658131 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-02-02 18:52:29 +01:00
MichaelDvP
be82afd778 add translated heat/eco modes to mode_str_tpl 2026-02-02 18:52:21 +01:00
Proddy
2156c96368 Merge pull request #2942 from misa1515/patch-28
Update locale_translations.h
2026-02-02 17:48:39 +01:00
misa1515
f7f078d82a Update locale_translations.h 2026-02-02 14:15:09 +01:00
Proddy
95168cf514 Add DeepWiki badge to README 2026-02-01 12:57:10 +01:00
Proddy
6dc601c4a2 Merge pull request #2940 from proddy/dev
set range for comfort point temp + offset - #2935
2026-02-01 12:03:22 +01:00
proddy
6b87bbb882 set range for comfort point temp + offset - #2935 2026-02-01 12:02:40 +01:00
proddy
abdf2c5037 package update 2026-02-01 12:02:04 +01:00
Proddy
7beec1b80f Merge pull request #2939 from MichaelDvP/dev
fixes and additions: #2918, #2931, #2933, #2935, #2936
2026-01-30 11:30:34 +01:00
MichaelDvP
3a0e46f064 update expected test data 2026-01-30 11:21:44 +01:00
MichaelDvP
85cc85a923 remove MAX_LOG_ENTRIES check in logger web page 2026-01-30 09:56:38 +01:00
MichaelDvP
ca0079c0df Merge branch 'dev' of https://github.com/MichaelDvP/EMS-ESP32 into dev 2026-01-30 08:58:21 +01:00
MichaelDvP
28b662ad43 update to ESPAsyncWebServer 3.9.6 2026-01-30 08:57:14 +01:00
MichaelDvP
6bac6bbfeb chore: update generated files for v3.8.2-dev.2 2026-01-30 07:08:12 +00:00
MichaelDvP
958ec1002b dev.2 2026-01-30 07:56:39 +01:00
MichaelDvP
438852ecaf Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-01-30 07:51:12 +01:00
MichaelDvP
92d82c0a00 remove burnMinPower, #2918 2026-01-30 07:50:54 +01:00
Proddy
7d37267f57 Merge pull request #2934 from proddy/dev
a collection of small changes
2026-01-29 21:03:23 +01:00
MichaelDvP
5641d53cc3 revert syslog buffer (still in heap) 2026-01-29 18:26:03 +01:00
MichaelDvP
8fc6752290 HA climate mode and icon check 2026-01-29 09:53:20 +01:00
proddy
cca6f87500 package update 2026-01-28 21:47:20 +01:00
proddy
758d76051f update discord URL 2026-01-28 21:47:13 +01:00
proddy
95f7e66cff update discord URL and tidy up 2026-01-28 21:46:58 +01:00
MichaelDvP
4e194287c9 remove SRC climate for test 2026-01-28 18:07:47 +01:00
MichaelDvP
3545830552 chore: update generated files for v3.8.2-dev.1 2026-01-28 14:20:04 +00:00
MichaelDvP
074f4c32ed dev.1, changelog 2026-01-28 15:08:28 +01:00
MichaelDvP
b3fec5ed7d fix SRC climate creation, #2936 2026-01-28 15:02:28 +01:00
MichaelDvP
ffb90b8f9a comfortpoint temperature and offset, #2935 2026-01-28 15:01:57 +01:00
MichaelDvP
584618043d weblog buffer max 1000 messages with psram, syslog buffer 250 with psram 2026-01-28 15:01:15 +01:00
MichaelDvP
d702c485b7 validate custom entity writes, #2931 2026-01-25 19:37:34 +01:00
MichaelDvP
d3561da331 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-01-25 19:22:07 +01:00
MichaelDvP
0e0aaf37df add climate modes night/eco as off 2026-01-25 19:21:59 +01:00
proddy
5ec068409f package update 2026-01-24 12:10:52 +01:00
proddy
8796b6d340 update dictionary 2026-01-24 12:10:45 +01:00
proddy
bfbb18655d memory optimzations 2026-01-23 19:37:21 +01:00
proddy
9088651e53 asyncwebserver update, improved caching 2026-01-23 19:35:53 +01:00
proddy
3e8f379502 package update 2026-01-23 19:35:37 +01:00
proddy
265c2c4231 3.8.2-dev.1 2026-01-23 12:52:55 +01:00
proddy
f671d79280 package update 2026-01-22 16:40:37 +01:00
proddy
97c89d1d13 add psram 2026-01-22 16:40:15 +01:00
proddy
e0a26a38fa replace unordered_map with map, less heap 2026-01-22 16:40:05 +01:00
proddy
038f06e59f show psram on startup 2026-01-22 16:39:14 +01:00
proddy
f4d2bae04f add psram 2026-01-22 16:38:52 +01:00
proddy
d443e275ea Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2026-01-21 14:46:10 +01:00
Proddy
30d2057e01 Merge pull request #2930 from jvhaarst/patch-1
Update Dutch translations for various terms
2026-01-20 08:49:45 +00:00
Jan van Haarst
d952b9aaae Update Dutch translations for various terms
As I don't have the board yet, I saw the error message "Als deze waarschuwing blijft staan na een paar seconden dan loop de instellingen na en in het bijzonder het apparaat type profiel na."
That sentence didn't really flow right for me, so I had a look at the rest of the text, with this result.
2026-01-20 08:27:24 +01:00
proddy
3402215e8d optimizations 2026-01-18 15:49:35 +00:00
proddy
f01031dc26 optimize 2026-01-18 15:00:43 +00:00
proddy
01d4d116b9 cspell updates 2026-01-18 12:36:34 +00:00
proddy
bc7f82eef1 package update 2026-01-18 12:36:20 +00:00
Proddy
efdb355033 Merge pull request #2923 from proddy/dev
v3.8.2
2026-01-12 12:07:02 +01:00
proddy
87c9fd010f v3.8.2 2026-01-11 19:49:01 +01:00
proddy
e8f57b6343 v3.8.1 2026-01-11 19:28:38 +01:00
proddy
a1ab81de66 Merge branch 'dev' 2026-01-11 19:14:11 +01:00
Proddy
930f0e615a Merge pull request #2921 from MichaelDvP/dev
set gpios when switching to custom board
2026-01-11 19:00:24 +01:00
MichaelDvP
4dbbdb3290 dangling Divider 2026-01-11 16:27:49 +01:00
MichaelDvP
8379b3f5aa Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-01-11 16:26:00 +01:00
MichaelDvP
08124fa4db set gpio when switching to custom board 2026-01-11 16:25:42 +01:00
Proddy
39414db732 Merge pull request #2920 from proddy/dev
add SECURITY
2026-01-11 16:13:29 +01:00
proddy
da57a08005 add 2026-01-11 16:12:28 +01:00
Proddy
b6b8700c3f Merge pull request #2919 from proddy/dev
rollback ApplicationSettings for CUSTOM
2026-01-11 14:23:07 +01:00
proddy
8ee0789dad emsesp.org ownership 2026-01-11 14:17:49 +01:00
proddy
1eabe86015 package update 2026-01-11 13:17:15 +01:00
proddy
5fe4b315f3 make reference to emsesp docs 2026-01-11 13:13:40 +01:00
proddy
03ec96bf96 rollback 2026-01-11 13:13:27 +01:00
Proddy
aa05e37fbb Merge pull request #2916 from proddy/dev
webUI changes and comments
2026-01-10 22:21:48 +01:00
Proddy
959a00c19a Merge branch 'emsesp:dev' into dev 2026-01-10 22:10:23 +01:00
Proddy
b42060be3a Merge pull request #2917 from MichaelDvP/dev 2026-01-10 20:23:37 +01:00
MichaelDvP
33bb433d7e always set valid gpio in load board profile 2026-01-10 19:47:03 +01:00
proddy
28a5d4ef1a fix comment 2026-01-10 19:00:15 +01:00
proddy
b78d47cbd0 minor ui change to how board profile is shown 2026-01-10 19:00:15 +01:00
proddy
8a7a1383a7 added comment reference to HA 2026-01-10 19:00:15 +01:00
Proddy
3f5163c1e4 Merge branch 'emsesp:dev' into dev 2026-01-10 18:39:48 +01:00
Proddy
fad82c8c68 Merge pull request #2915 from MichaelDvP/dev
fix board change ignore old gpio settings
2026-01-10 18:39:17 +01:00
MichaelDvP
6fc3bf30b6 HA uom L, formatting 2026-01-10 18:19:14 +01:00
proddy
43b3e74c08 input number format 2026-01-10 18:09:38 +01:00
proddy
64c9882d8c add children to avoid linting errors 2026-01-10 18:09:17 +01:00
proddy
a690510903 update 2026-01-10 18:09:02 +01:00
proddy
c732ec301a update 2026-01-10 18:08:54 +01:00
proddy
cc1f16596a add ha_number_node 2026-01-10 18:08:44 +01:00
proddy
66c74f85a4 formatting 2026-01-10 18:08:17 +01:00
proddy
db667b9437 package update 2026-01-10 18:08:06 +01:00
MichaelDvP
8799015f59 fix board change ignore old gpio settings 2026-01-10 16:06:43 +01:00
Proddy
d71b3c64e1 Merge pull request #2914 from proddy/dev
remove build section, refer to docs
2026-01-10 09:43:15 +01:00
proddy
6d3083fff4 remove build section, refer to docs 2026-01-10 09:39:23 +01:00
Proddy
56b2c111b8 Merge pull request #2912 from MichaelDvP/dev
update testdata (no `.0`-decimales), fix c3 compile issue
2026-01-09 21:25:29 +01:00
MichaelDvP
12ce736580 remove eth fixed pins 2026-01-09 21:03:23 +01:00
MichaelDvP
debe90eb8d formatting, update gpios for C3, S2 2026-01-09 18:57:25 +01:00
Proddy
8d318143a4 Merge pull request #2913 from proddy/dev
fix ha climate mode to use bool format
2026-01-09 18:48:45 +01:00
proddy
59f90f5d1c comment change 2026-01-09 18:47:34 +01:00
proddy
0efbd0528e fix ha mode, boolean to match setting bool format 2026-01-09 18:47:14 +01:00
proddy
3218620a0e update dictionary 2026-01-09 18:46:00 +01:00
proddy
a93921c875 adding missing board to settings 2026-01-09 18:45:50 +01:00
MichaelDvP
fb77b455be testdata without decimals 2026-01-09 17:21:44 +01:00
MichaelDvP
b64c392c58 rename uom::HZ to HERTZ, avoids compile error on ESP32C3 2026-01-09 17:21:26 +01:00
Proddy
7bc6cf3910 Merge pull request #2911 from MichaelDvP/dev
day schedule defaults to all days, allow gpio 1,3 for custom #2908
2026-01-09 17:00:53 +01:00
MichaelDvP
e19e76546e update changelog, dev7 2026-01-09 16:45:18 +01:00
MichaelDvP
b9aaaae90a allow uart0 pins for custom boards 2026-01-09 16:30:07 +01:00
MichaelDvP
86b395d612 don't allow day schedule without selecting a day, default to daily 2026-01-09 16:29:12 +01:00
Proddy
6204b9acc7 Merge pull request #2909 from MichaelDvP/dev
HA number mode selection #2900, board specific gpio settings
2026-01-09 12:48:52 +01:00
MichaelDvP
0777f420b0 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-01-09 11:52:42 +01:00
MichaelDvP
4a6a662aa0 known board gpio settings, custom with less restriction 2026-01-09 11:52:18 +01:00
Proddy
66837d399f Merge pull request #2907 from MichaelDvP/dev
add gpios for system sensors on first start
2026-01-09 09:43:46 +01:00
MichaelDvP
34ff5f12ea mqtt decimals: remove trailing zeros 2026-01-08 21:18:16 +01:00
MichaelDvP
82d160cabb merge mqtt ha number mode 2026-01-08 21:17:14 +01:00
MichaelDvP
2c85d3829b Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-01-08 21:05:59 +01:00
MichaelDvP
cc258dae16 add gpios for system sensors on first start 2026-01-08 19:15:48 +01:00
Proddy
577964befe Merge pull request #2904 from MichaelDvP/dev
GPIOs depends on board profile, #2901
2026-01-08 17:42:34 +01:00
MichaelDvP
f2500013ab test fix 2026-01-08 16:55:57 +01:00
MichaelDvP
98fb6941d2 add E32V2_2 single led as system sensor 2026-01-08 16:32:22 +01:00
MichaelDvP
7308b8e73d fix another test 2026-01-08 16:31:36 +01:00
Proddy
e97cfaf9ee Merge pull request #2905 from proddy/dev
min/max fixes
2026-01-08 14:50:13 +01:00
MichaelDvP
fd0734d8d8 custom board option only in developer mode 2026-01-08 14:43:35 +01:00
MichaelDvP
739f32f045 update pkg 2026-01-08 13:10:56 +01:00
MichaelDvP
b66b49e812 remove blank line 2026-01-08 12:25:23 +01:00
MichaelDvP
d4b81a2909 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev2 2026-01-08 12:24:46 +01:00
MichaelDvP
fb57537e88 fix standalone 2026-01-08 12:19:18 +01:00
MichaelDvP
335b1274cf test version 2026-01-08 12:08:14 +01:00
MichaelDvP
f6d1c87eaf skip trailing zeros 2026-01-08 12:07:00 +01:00
MichaelDvP
5e07e9a11b add ha number mode selection #2900 2026-01-08 12:06:48 +01:00
MichaelDvP
dd0ea5df0e fix test gpios for S32 gateway 2026-01-08 11:16:32 +01:00
proddy
2b7f592957 use max version for dummy data, don't show min/max range for non writeable entries. fixes entity_dump.xls 2026-01-07 22:02:19 +01:00
proddy
db7b5df85d auto-gen 2026-01-07 22:01:13 +01:00
proddy
1c2534ed8f package update 2026-01-07 22:01:06 +01:00
MichaelDvP
90535d7b94 changelog, dev5 2026-01-07 11:43:02 +01:00
MichaelDvP
0d3a8fc719 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-01-07 10:30:40 +01:00
MichaelDvP
d624b9eac9 GPIOs depends on board profile, #2901 2026-01-07 10:29:59 +01:00
Proddy
068cbf757c Merge pull request #2903 from MichaelDvP/dev
HP entities #2883, Mqtt queue to psram #2889
2026-01-07 09:54:11 +01:00
MichaelDvP
738d6f0b0f chore: update generated files for v3.8.1-dev.4 2026-01-07 08:17:16 +00:00
MichaelDvP
d9551bc4c3 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-01-07 08:38:01 +01:00
Proddy
722659325a Merge pull request #2884 from proddy/dev
some small fixes and changes
2026-01-06 22:19:40 +01:00
proddy
927e7c80f4 update 2026-01-06 22:18:35 +01:00
proddy
39919b4ad8 update 2026-01-06 22:17:54 +01:00
proddy
b5defa552e improve chunking and fix circular refs 2026-01-06 22:15:22 +01:00
proddy
e8fbbe5a1c skip blured paper component when in systemMonitor 2026-01-06 22:04:54 +01:00
proddy
949172128f update packages 2026-01-06 22:04:26 +01:00
proddy
35a8db4581 fix missing progress bar on WebUI upload 2026-01-06 22:04:15 +01:00
proddy
3a74abb4db fix blue dots appearing when transitioning 2026-01-06 22:03:53 +01:00
proddy
bcc7687b1b pins 22-25 don't exist on S2+S3 2026-01-06 21:01:26 +01:00
MichaelDvP
13aa544214 mqtt queue: prefere PSram 2026-01-06 18:50:50 +01:00
proddy
696141721a updated with BBQKees pins 2026-01-06 11:57:21 +01:00
proddy
4781eea665 gpios 2026-01-06 10:56:44 +01:00
proddy
2fd6bed485 update test 2026-01-05 21:37:40 +01:00
proddy
db40d1d381 set default values, just to be sure 2026-01-05 21:36:21 +01:00
proddy
33bde8b407 fix default version just to be sure 2026-01-05 21:36:10 +01:00
proddy
bf5990a992 fix setting default version 2026-01-05 21:35:39 +01:00
proddy
cff4bd0a71 add debug statement 2026-01-05 21:35:18 +01:00
proddy
28ee0834d8 don't log debug messages if nothing connected 2026-01-05 21:35:03 +01:00
proddy
9be1cb1d3e always set fresh flag 2026-01-05 21:34:50 +01:00
proddy
81d46fede2 check for USB uploads and set 'fresh' flag 2026-01-05 21:34:32 +01:00
proddy
664a8e9f5f remove defaults 2026-01-05 21:26:58 +01:00
proddy
def7501c62 dictionary update 2026-01-05 20:12:21 +01:00
proddy
1cc4dc52d4 package update 2026-01-05 20:12:12 +01:00
MichaelDvP
978c738f27 add hp entities, +2883 2026-01-05 17:32:49 +01:00
proddy
feeb8500ac #2896 2026-01-05 13:34:10 +01:00
proddy
18a55d4622 more gpio pin updates 2026-01-05 13:31:27 +01:00
Proddy
29ea67f438 Merge branch 'emsesp:dev' into dev 2026-01-05 10:10:27 +01:00
Proddy
a3df77171b Merge pull request #2898 from g6094199/patch-1
Update mqtt.cpp: use the same nomenclature as Tasmota and OpenBK uses…
2026-01-05 10:10:14 +01:00
g6094199
aa6f5c50b2 Update mqtt.cpp: use the same nomenclature as Tasmota and OpenBK uses for Homeassistant
to be compliant to the HA typical nomenclatura use the same wording.

since we are anyway connecting via wifi or ethernet the wording of "WIFI rssi" is redundant. just use "RSSI" as other platforms do.
2026-01-05 09:03:12 +01:00
proddy
53a43ca147 update gpio to not conflict with board profile 2026-01-04 21:56:37 +01:00
proddy
da76fe3871 asyncTCP change 2026-01-04 21:51:57 +01:00
proddy
84af132e2c fix mix-up of GPIOs 2026-01-04 21:33:30 +01:00
proddy
712a8537c9 remove unused struct 2026-01-04 21:32:32 +01:00
proddy
bb22386f7f updated 2026-01-04 21:32:15 +01:00
proddy
89dfe11ee3 fix tests 2026-01-04 12:45:54 +01:00
proddy
be1e08af9c update 2026-01-04 12:21:17 +01:00
proddy
68ebcdded4 gpio exclusion, add name 2026-01-04 11:44:28 +01:00
Proddy
4afe041880 Merge branch 'emsesp:dev' into dev 2026-01-04 11:41:07 +01:00
Proddy
8b690d23da Merge pull request #2892 from MichaelDvP/dev
Junkers wwcharge offset #2860, fix minflowtemp #2890
2026-01-04 11:39:32 +01:00
MichaelDvP
62c7fb671b Junkers wwcharge offset #2860, fix minflowtemp #2890 2026-01-04 11:05:38 +01:00
proddy
5a82064a88 add name to gpios 2026-01-04 00:08:17 +01:00
proddy
4ae4000944 asyncTCP fix 2026-01-03 22:48:17 +01:00
proddy
616c73f658 first test to exclude gpios 2026-01-03 22:16:33 +01:00
Proddy
b698485814 Merge branch 'emsesp:dev' into dev 2026-01-03 19:00:11 +01:00
Proddy
b4036bf8cd Merge pull request #2888 from MichaelDvP/dev
don't add HA uom/classes for bool values, fix #2885
2026-01-03 18:59:44 +01:00
MichaelDvP
4b457d6cdb don't add HA uom/classes for bool values, fix #2885 2026-01-03 18:24:54 +01:00
proddy
425b44e334 init settings 2026-01-03 14:13:54 +01:00
proddy
41bf293db3 AsyncTCP change 2026-01-03 13:58:25 +01:00
proddy
af349edd54 text changes 2026-01-03 13:58:10 +01:00
proddy
5b303bd58a package update 2026-01-03 13:58:00 +01:00
proddy
b992f90fe2 AsyncTCP changes 2026-01-03 13:56:27 +01:00
proddy
92c34dddba add comment 2026-01-03 13:56:11 +01:00
proddy
1475fc094d ESPAsyncWebServer @ 3.9.4 2026-01-03 13:55:57 +01:00
Proddy
48f7b48216 Merge pull request #2882 from MichaelDvP/dev 2026-01-02 16:54:15 +01:00
MichaelDvP
cd054b293a dev3 2026-01-02 16:13:52 +01:00
MichaelDvP
ea6b7c0be0 revert commit 1a03b98, used fixed buffer length 2026-01-02 16:13:14 +01:00
MichaelDvP
a49a5537d3 fix minflowtemp #2879 2026-01-02 16:04:02 +01:00
MichaelDvP
c407ad04bf Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-01-01 18:58:45 +01:00
MichaelDvP
480e0951b8 update asyncTCP 2026-01-01 18:58:20 +01:00
Proddy
a06c5bb297 Merge pull request #2878 from MichaelDvP/dev
fix selflowtemp #2876 and other
2026-01-01 17:04:00 +01:00
Proddy
77a54792dd Merge pull request #2877 from proddy/dev
fix modbus md doc
2026-01-01 13:17:36 +01:00
proddy
d0d49397ca fix modbus table for emsesp.org doc 2026-01-01 13:00:18 +01:00
proddy
b51abeabac auto-gen 2026-01-01 13:00:06 +01:00
MichaelDvP
f0e4f17ab8 safe update time in nvs 2026-01-01 11:50:14 +01:00
MichaelDvP
205da33fe5 snapshot gpios in temporarly ram 2026-01-01 11:49:38 +01:00
MichaelDvP
9aa78111be fix selflowtemp #2876 2026-01-01 10:48:21 +01:00
proddy
c3f93d4aae fix for demo building 2026-01-01 10:47:54 +01:00
Proddy
5a5c0d7179 Merge pull request #2875 from proddy/dev
fixes #1953 #2874
2026-01-01 10:10:10 +01:00
proddy
ba57942b7d txpause in system info (#1953), added Wemos S3 pins (#2874) 2026-01-01 10:08:57 +01:00
proddy
c782deb581 package udpate 2026-01-01 10:07:11 +01:00
Proddy
566edfcd7b Merge pull request #2872 from proddy/dev
3.8.1-dev-0
2025-12-31 22:01:08 +01:00
proddy
c3cf38c330 3.8.1-dev-0 2025-12-31 21:54:36 +01:00
proddy
28135c225b Merge branch 'dev' 2025-12-31 21:26:15 +01:00
Proddy
4953a41330 Merge pull request #2871 from proddy/dev
overcome strange chars in header files
2025-12-31 17:05:04 +01:00
proddy
7ff4ed640d overcome strange chars in header files 2025-12-31 16:53:32 +01:00
Proddy
898a13fcb5 Merge pull request #2870 from proddy/dev
minor fixes
2025-12-31 15:31:16 +01:00
proddy
3fdc370466 clean up check_upgrade, remove OTA onProgress setting status each time 2025-12-31 15:25:05 +01:00
proddy
39055ad0d2 show firmware size in KB 2025-12-31 15:23:38 +01:00
Proddy
21e73c973a Merge pull request #2869 from MichaelDvP/dev
fix minflowtemp #2781
2025-12-31 12:13:44 +01:00
MichaelDvP
4d03976032 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-31 11:45:59 +01:00
MichaelDvP
eb7587270f fix minflowtemp #2781 2025-12-31 11:45:29 +01:00
Proddy
3ea88a2be0 Merge pull request #2867 from proddy/dev
last minute 3.8 cleanup
2025-12-31 09:25:51 +01:00
proddy
55778ba0b5 update test data to 3.8 2025-12-31 09:22:53 +01:00
proddy
5d3694abd3 update package manager 2025-12-31 09:22:43 +01:00
proddy
21d9ba0182 update test data to 3.8 2025-12-31 09:22:28 +01:00
Proddy
27848feddd Merge pull request #2865 from proddy/dev
force 3.8.0-dev.1
2025-12-30 10:28:37 +01:00
proddy
ce261eeb65 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2025-12-30 10:28:10 +01:00
proddy
8de3ae5468 package update 2025-12-30 10:28:09 +01:00
proddy
367d27d48f 3.8.0-dev.1 2025-12-30 10:28:03 +01:00
Proddy
1a9b6ab2a5 Merge pull request #2864 from proddy/dev
fixes #2862
2025-12-30 10:26:44 +01:00
Proddy
323b8ee67d Merge branch 'emsesp:dev' into dev 2025-12-30 10:23:51 +01:00
Proddy
5b95e1d41f Merge pull request #2863 from MichaelDvP/dev
fixes #2844, #2856
2025-12-30 10:23:32 +01:00
MichaelDvP
a272d8e253 chore: update generated files for v3.8.0-dev.0 2025-12-30 09:07:17 +00:00
MichaelDvP
79285ca12e remove wrong 4wayValve, #2844 2025-12-30 09:55:42 +01:00
proddy
f90f676faf fixes #2862 2025-12-29 21:30:33 +01:00
MichaelDvP
3224d8823d fix HA config topic to basename 2025-12-29 18:35:34 +01:00
MichaelDvP
1a03b98670 avoid variabe length array 2025-12-29 18:35:06 +01:00
Proddy
80f32bfeb4 Merge pull request #2859 from proddy/dev
fix modbus md, so its compatible with docusaurus markup
2025-12-29 16:35:28 +01:00
proddy
1b4693b981 fix show md compatible with docusaurus markup 2025-12-29 16:34:01 +01:00
proddy
535b760dd7 auto-generated 2025-12-29 16:33:44 +01:00
proddy
14775f6503 added comments 2025-12-29 16:33:36 +01:00
proddy
a856d249c9 update screenshots 2025-12-29 16:31:37 +01:00
proddy
484b547df5 fix typo 2025-12-29 12:31:42 +01:00
Proddy
d73ca2c890 Merge pull request #2858 from proddy/dev
rename docs.emsesp.org to emsesp.org
2025-12-29 10:45:26 +01:00
Proddy
6211bd8c69 Merge branch 'emsesp:dev' into dev 2025-12-29 10:44:46 +01:00
proddy
e638a471d1 rename docs.emsesp.org to emsesp.org 2025-12-29 10:44:32 +01:00
Proddy
42ee21e883 Merge pull request #2840 from proddy/dev
a selection of minor fixes and some new features
2025-12-29 10:39:13 +01:00
proddy
263af58dc0 package update 2025-12-29 10:37:55 +01:00
proddy
6727c0655a update dictionary 2025-12-28 11:20:46 +01:00
proddy
cba249938a add missing translations (thanks to AI) 2025-12-27 17:35:58 +01:00
proddy
9fbed47617 txenabled to txpause 2025-12-27 17:34:19 +01:00
proddy
b5dd722888 rename txenabled to txpause 2025-12-27 17:34:04 +01:00
proddy
11bef52568 fix HA mapping for uA and l/min 2025-12-27 17:33:46 +01:00
proddy
d22a369333 add tests for txmode 2025-12-27 11:20:38 +01:00
proddy
dfe95296d9 fix txmode on/off logic 2025-12-27 11:20:30 +01:00
proddy
8d39893e5e use Yellow RGB for flash and button 2025-12-27 10:58:08 +01:00
proddy
b8b8a501e1 package update 2025-12-27 10:24:43 +01:00
proddy
2a36f378b4 auto-formatting 2025-12-27 10:24:35 +01:00
proddy
6746df37a1 updated logic on fresh firmware install 2025-12-26 17:10:26 +01:00
proddy
364f66b7d4 updated comment 2025-12-26 16:55:05 +01:00
proddy
84bbd93216 fix standalone 2025-12-26 16:24:46 +01:00
proddy
85ef8d7d50 text changes 2025-12-26 15:28:03 +01:00
proddy
2b6606d8ad disable uart when uploading, show when uploading, store flag showing its a new firmware 2025-12-26 15:25:41 +01:00
proddy
4772a61e7c use EMS_TXMODE_INIT 2025-12-26 15:24:56 +01:00
proddy
d30375f3c4 set_partition_install_date - when NVS connects 2025-12-26 13:34:02 +01:00
proddy
8c831ac0e9 only update partition info on boot 2025-12-26 13:33:38 +01:00
proddy
1ede7028a5 add back missing webLogService.loop() 2025-12-26 12:06:31 +01:00
proddy
5b8dd0a693 msgpack update 2025-12-26 11:56:48 +01:00
proddy
cc041510be fixes install time in NVS 2025-12-26 10:18:58 +01:00
proddy
05baec85b7 implement txenabled system command - #2850 2025-12-26 10:10:27 +01:00
proddy
fb698fd029 if LED flashing skip other chores 2025-12-26 09:34:25 +01:00
proddy
bbfec136e8 use EMS_TXMODE_OFF 2025-12-26 09:33:59 +01:00
proddy
36271a2c24 add comments 2025-12-26 09:33:50 +01:00
proddy
bbe1f133dc add EMS_TXMODE_INIT and EMS_TXMODE_NONE 2025-12-26 09:33:17 +01:00
proddy
d7b0614556 add comment 2025-12-26 09:32:57 +01:00
proddy
7e9f27a613 tidy up comments 2025-12-26 09:32:46 +01:00
proddy
3bdc97d4d5 rollback changes to get_metrics_prometheus() 2025-12-24 17:56:07 +01:00
proddy
8a7511a941 default no sleep is true in standalone 2025-12-24 17:55:27 +01:00
proddy
a9511e6a29 implements #2848 2025-12-24 16:59:18 +01:00
Proddy
ac37ead419 Merge branch 'emsesp:dev' into dev 2025-12-24 13:25:26 +01:00
proddy
39fcda59da update 3.7.3->3.8.0 2025-12-24 13:24:48 +01:00
Proddy
9c44e104bb Merge pull request #2847 from VlastiBroucek/dev
Added some missing Czech translations
2025-12-24 13:18:34 +01:00
proddy
18f8db7942 text changes 2025-12-24 13:06:28 +01:00
proddy
71281bc82d rename to stored version, use same infoDialog 2025-12-24 12:54:08 +01:00
proddy
0557def0b6 add install date to firmware versions 2025-12-24 11:18:22 +01:00
proddy
1a3f7fbbee don't run pio as default 2025-12-24 11:17:55 +01:00
Vlasti Broucek
7bed8bf84e Added some missing Czech translations 2025-12-24 12:22:11 +11:00
proddy
790371a9e2 minor optimizations 2025-12-23 23:28:10 +01:00
proddy
537cf19e97 only show previous if in developer mode and there are something to show 2025-12-23 23:28:02 +01:00
proddy
35ad43b7b3 package update 2025-12-23 23:27:22 +01:00
proddy
3d70e8c1e6 clear_snapshot_gpios 2025-12-23 19:32:25 +01:00
proddy
7bb30d37ce rollback to other partitions - #2837 2025-12-23 18:08:50 +01:00
proddy
0267f00b48 package update 2025-12-23 18:08:05 +01:00
Proddy
5a7cec91c5 Merge branch 'emsesp:dev' into dev 2025-12-23 08:25:22 +01:00
Proddy
642bf63abc Merge pull request #2842 from misa1515/patch-27
Update locale_translations.h
2025-12-23 08:25:11 +01:00
misa1515
6f3197f482 Update locale_translations.h 2025-12-22 23:03:16 +01:00
proddy
5c88968879 reset data and remove Apply button when error 2025-12-22 17:43:11 +01:00
proddy
c4a43183b3 GPIOs not checked when board profile is adjusted
#2841
2025-12-22 17:18:37 +01:00
Proddy
94b583d7f3 Merge branch 'emsesp:dev' into dev 2025-12-22 15:46:23 +01:00
Proddy
37012e55e3 Merge pull request #2839 from MichaelDvP/dev
add back uom and factor inputs for some analogsensor types
2025-12-22 15:45:40 +01:00
proddy
ea4d613d12 updates to add_ha_classes 2025-12-22 15:41:14 +01:00
proddy
0e6108b5a9 3.7.3-dev.40 2025-12-22 15:40:27 +01:00
MichaelDvP
fdbaf7509f add back uom and factor inputs for some analogsensor types 2025-12-22 15:13:54 +01:00
Proddy
74182031ae Merge pull request #2838 from proddy/dev
formatting and EMS bus logging
2025-12-22 11:28:59 +01:00
proddy
6c80a34578 don't output Rx errors in log if no bus connected 2025-12-22 09:07:04 +01:00
proddy
09f1c13d28 formatting 2025-12-22 09:06:27 +01:00
proddy
5668fe13ae package update 2025-12-22 08:57:29 +01:00
proddy
78a02b6d85 run unit tests 2025-12-22 08:57:22 +01:00
proddy
ccab932e8d dont long rx garbage if ems bus not connected 2025-12-21 22:30:03 +01:00
proddy
eaea1f383b formatting 2025-12-21 22:29:28 +01:00
Proddy
de8309de4a Merge pull request #2836 from MichaelDvP/dev
force publish single on connect
2025-12-21 14:45:49 +01:00
MichaelDvP
bc3269037f chore: update generated files for v3.7.3-dev.39 2025-12-21 11:05:49 +00:00
MichaelDvP
31131427b8 fix RC120RF check 2025-12-21 11:53:39 +01:00
MichaelDvP
9957bff62b check Mqtt::enabled 2025-12-21 11:17:31 +01:00
MichaelDvP
f1841347a7 set lastresponse also if not connected 2025-12-19 18:50:51 +01:00
MichaelDvP
1b8b72c443 publish mqtt emsesp on-change messages on connect 2025-12-19 17:14:50 +01:00
MichaelDvP
b4affbff6d Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-19 16:53:36 +01:00
Proddy
c8b4a38bb6 Merge pull request #2829 from proddy/dev
HA fixes part 3
2025-12-19 16:36:17 +01:00
proddy
eec373e2ae publish ha only if ha is enabled 2025-12-19 16:35:55 +01:00
proddy
48b261317d 3.7.3-dev.39 2025-12-19 16:35:40 +01:00
proddy
e6beb01075 package update 2025-12-19 16:35:29 +01:00
proddy
ecc6e9286a tidy up add_ha_dev_section 2025-12-19 08:55:33 +01:00
proddy
179351cb6b add back force, fix for non-HA 2025-12-19 08:54:59 +01:00
proddy
778fe43012 package update 2025-12-19 08:54:30 +01:00
Proddy
d84d52df4b Merge pull request #2827 from proddy/dev
fix HA custom entities
2025-12-18 21:53:22 +01:00
proddy
11d4109915 package update 2025-12-18 21:40:53 +01:00
proddy
0eddbac150 remove force in HA MQTT 2025-12-18 21:40:41 +01:00
proddy
99afeb221a changes to HA 2025-12-18 21:40:08 +01:00
proddy
39d18b78a1 formatting 2025-12-18 21:39:43 +01:00
MichaelDvP
4ebe8cc0cc dallas dev_cla 2025-12-18 17:17:00 +01:00
MichaelDvP
6dabfb7fe2 analogsensor: add_ha_classes 2025-12-18 13:31:10 +01:00
MichaelDvP
611b1d9aca add RC120RF as remote 2025-12-18 13:30:41 +01:00
Proddy
2821f8e750 Merge pull request #2824 from MichaelDvP/dev
some more fixes
2025-12-18 11:23:37 +01:00
MichaelDvP
1cccd8dc2c Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-18 09:18:03 +01:00
Proddy
4d13982594 Merge pull request #2823 from proddy/dev
fix HA sensors not been added to HA device
2025-12-18 08:43:57 +01:00
MichaelDvP
10c63640c0 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2025-12-18 07:20:50 +01:00
proddy
14ad1239db don't need to add name to ids each time for HA 2025-12-18 06:45:48 +01:00
proddy
2a16cb6e64 3.7.3-dev.38 2025-12-18 06:45:25 +01:00
proddy
89fa5947bd fix standalone 2025-12-17 22:10:17 +01:00
proddy
a81e56e3bf package update 2025-12-17 22:10:05 +01:00
proddy
90ad2dde54 minor optimization 2025-12-17 22:09:55 +01:00
proddy
9c243cbe8d sort types 2025-12-17 22:09:32 +01:00
proddy
c1b444541f fix HA 2025-12-17 22:09:17 +01:00
MichaelDvP
8527b16e9d dev 38 2025-12-17 18:14:09 +01:00
MichaelDvP
a728420010 small fix 2025-12-17 18:13:35 +01:00
MichaelDvP
378d9e8634 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2025-12-17 18:11:53 +01:00
MichaelDvP
1973081529 ha_dev_section for custom and scheduler 2025-12-17 18:11:43 +01:00
MichaelDvP
45ae6d802c use (const char *) for PSRAM-stored sensor.name in ArduinoJson 2025-12-17 16:42:38 +01:00
MichaelDvP
c4297e2996 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-17 12:34:54 +01:00
proddy
310afe3ab8 fix HA sensor referencing 2025-12-17 11:37:46 +01:00
MichaelDvP
3215f36530 fix typo in analogsensor:remove_ha_topic 2025-12-17 11:36:50 +01:00
proddy
55621e12d9 package update - fixes vite-tsconfig-paths 2025-12-17 11:26:57 +01:00
Proddy
458dc516f4 Merge pull request #2822 from proddy/dev
fix HA shower sensor
2025-12-16 23:15:53 +01:00
Proddy
f2ae84bd22 Merge branch 'emsesp:dev' into dev 2025-12-16 23:15:20 +01:00
proddy
f093df1cb9 fix build for windows 2025-12-16 23:14:48 +01:00
proddy
05f15f7876 package update 2025-12-16 23:14:36 +01:00
proddy
12f4a74094 fix mqtt base for shower 2025-12-16 23:14:02 +01:00
Proddy
15d895fd0a Merge pull request #2821 from MichaelDvP/dev
mqtt publish on change: wait for queue empty
2025-12-16 22:35:30 +01:00
MichaelDvP
1890948924 mqtt-queue check also for publish-on-change analog/temperature 2025-12-16 11:36:58 +01:00
MichaelDvP
fec246127f Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-16 08:27:45 +01:00
MichaelDvP
ecab30d4ac do not flood mqtt queue if publish on change is set 2025-12-16 08:27:21 +01:00
Proddy
f8cc688241 Merge pull request #2820 from proddy/dev
fix HA modes
2025-12-15 21:13:11 +01:00
proddy
f51b3528d5 ESPAsyncWebServer @ 3.9.3 2025-12-15 21:12:20 +01:00
proddy
42c94a1017 formatting 2025-12-15 21:12:09 +01:00
proddy
acccb56f07 add comment 2025-12-15 20:47:07 +01:00
proddy
3a2d3ac985 fix mixed up logic with HA modes 2025-12-15 19:34:48 +01:00
proddy
0ab18e6e08 package update 2025-12-15 19:34:36 +01:00
proddy
44db5991e7 package update 2025-12-15 19:34:22 +01:00
Proddy
15a6c50326 Merge pull request #2819 from MichaelDvP/dev
dev37, fix mqtt booleans, add SRC climate icon
2025-12-15 19:29:21 +01:00
MichaelDvP
911aa40ca1 fix mqtt bool output for analog, scheduler 2025-12-15 16:48:33 +01:00
MichaelDvP
2b679daabc dev 37, add SRC thermostat icons to climate 2025-12-15 11:56:54 +01:00
MichaelDvP
71b956e613 fix analog mqtt 2025-12-15 10:58:32 +01:00
Proddy
79089a93bc Merge pull request #2817 from proddy/dev
SRC for HA climate, "show gpio" command
2025-12-14 21:11:54 +01:00
proddy
da3ac1794e add telnet command show gpio 2025-12-14 21:10:45 +01:00
proddy
bc870b2aa2 add HA climate control with cool for SRC thermostat controls 2025-12-14 21:10:29 +01:00
Proddy
6bc40ce2e1 Merge pull request #2815 from proddy/dev
package update
2025-12-14 12:44:48 +01:00
Proddy
84544979fa Merge branch 'emsesp:dev' into dev 2025-12-14 12:44:23 +01:00
proddy
2446e4d1fd package update 2025-12-14 12:44:04 +01:00
Proddy
4a15b39945 Merge pull request #2814 from MichaelDvP/dev
remove unused parameter
2025-12-14 12:12:19 +01:00
MichaelDvP
df080bbad9 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-14 11:32:46 +01:00
Proddy
8632af8820 Merge pull request #2813 from proddy/dev
fix HA modes (#2812)
2025-12-14 11:07:58 +01:00
Proddy
27047c0f39 Merge branch 'emsesp:dev' into dev 2025-12-14 11:07:33 +01:00
proddy
95de3e339d fix HA modes 2025-12-14 11:07:16 +01:00
MichaelDvP
99f44aece5 remove unused parameter 2025-12-14 09:23:28 +01:00
MichaelDvP
4e589aecbf Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-14 09:20:58 +01:00
Proddy
ab013554bd Merge pull request #2805 from gr3enk/dev
Fix system/metrics endpoint duplicate labels & add enum support for metrics
2025-12-13 19:28:56 +01:00
Jakob
5c966e291b chore: avoid dangling references while processing json objects recursively 2025-12-13 15:50:04 +01:00
MichaelDvP
068744b681 config check 2025-12-13 15:01:14 +01:00
Jakob
8c794baa7b Merge branch 'dev' into dev 2025-12-13 14:34:05 +01:00
Proddy
f6a23723f8 Merge pull request #2811 from proddy/dev
minor HA improvements
2025-12-13 11:38:06 +01:00
proddy
4b3cc2e3ec update 2025-12-13 11:30:43 +01:00
proddy
4ee743d309 fix standalone 2025-12-13 11:29:01 +01:00
proddy
d3b4bab40a dynamically add HA thermostat modes based on model 2025-12-13 11:22:22 +01:00
proddy
c71b54fb5b auto-formatting 2025-12-13 11:21:57 +01:00
proddy
ecbdf01bdf add HA dev section linking all devices to ems-esp 2025-12-13 11:21:45 +01:00
proddy
c2e8a6c73d added get_ip_or_hostname() 2025-12-13 11:20:48 +01:00
proddy
29037d19fb auto-formatting 2025-12-13 11:20:24 +01:00
proddy
67d242d210 package update 2025-12-13 11:19:52 +01:00
proddy
91a67def99 minor fix 2025-12-13 11:19:45 +01:00
MichaelDvP
c8ab89ef37 fix pl translation 2025-12-13 10:52:58 +01:00
proddy
17a9b7eb0a send HA modes based on actual thermostat modes 2025-12-12 21:58:35 +01:00
proddy
cdb592d744 package update 2025-12-12 21:58:15 +01:00
proddy
af19941a07 https://github.com/emsesp/EMS-ESP32/discussions/2761 2025-12-12 21:58:06 +01:00
Jakob
00dce83096 Merge branch 'dev' of https://github.com/gr3enk/EMS-ESP32 into dev 2025-12-12 17:57:13 +01:00
Jakob
597e60d6f1 chore: filter out entities with no values 2025-12-12 17:53:50 +01:00
Jakob
4e4311cac0 Merge branch 'dev' into dev 2025-12-12 14:42:59 +01:00
Jakob
c11402195f chore: reserve string capacity for prometheus metrics 2025-12-12 14:08:47 +01:00
Proddy
31ec15811f Merge pull request #2806 from MichaelDvP/dev
small fixes,update changelog
2025-12-12 11:53:04 +01:00
MichaelDvP
739ac7b42e update changelog 2025-12-12 11:02:47 +01:00
MichaelDvP
ad3049ab0a smal fixes 2025-12-12 10:47:06 +01:00
MichaelDvP
d96c3f3ed7 removelast topic/payload 2025-12-12 10:46:26 +01:00
Jakob
dcfd0d5b11 test: add unit tests for metrics enum outputs 2025-12-12 10:06:02 +01:00
Jakob
b05712cf83 fix: check for duplicate labels 2025-12-12 10:04:51 +01:00
Jakob
df15485d7c feat: add enum support for metrics endpoint 2025-12-12 10:03:52 +01:00
Proddy
bb15fcdc46 Merge pull request #2804 from proddy/dev
various minor fixes to dev-35
2025-12-12 00:03:08 +01:00
proddy
e34291fbd5 replace VLAs with standard C++ 2025-12-12 00:00:49 +01:00
proddy
55b8c2d04c fix standalone/make, fix HA avty, fix deprecated arduinojson, update packages 2025-12-11 23:52:44 +01:00
Proddy
8bd1cd03b4 Merge pull request #2793 from MichaelDvP/dev
PSram memory
2025-12-11 22:09:41 +01:00
MichaelDvP
bafb7c35fb Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-11 16:26:16 +01:00
MichaelDvP
9425558ff7 Merge branch 'dev' of https://github.com/MichaelDvP/EMS-ESP32 into dev 2025-12-11 16:22:18 +01:00
MichaelDvP
f20aad3813 add Phy RTL8201 2025-12-11 16:10:41 +01:00
MichaelDvP
7a683d3637 more to psram, names for sensors, schedule, custom as char[20] 2025-12-11 16:00:24 +01:00
MichaelDvP
ac982cbb15 fix #2800 2025-12-09 21:11:36 +01:00
Proddy
9889b1b5c4 Merge pull request #2796 from gr3enk/dev
Adding api/system/metrics endpoint for Prometheus integration
2025-12-08 18:49:42 +01:00
MichaelDvP
7cb647e5f1 chore: update generated files for v3.7.3-dev.35 2025-12-08 15:09:51 +00:00
MichaelDvP
515b75160c stub to remove #ifndef EMSESP_STANDALONE 2025-12-08 12:42:11 +01:00
MichaelDvP
a365dc7519 remove syslog debug level 2025-12-07 18:25:12 +01:00
MichaelDvP
764520714b fix syslog quality calculation 2025-12-07 18:24:41 +01:00
MichaelDvP
43e087ae91 allow uart0 pin for eth (check eth first) #2794 2025-12-07 13:02:25 +01:00
Jakob
bffccc585a test: add tests for /api/system/metrics endpoint 2025-12-07 11:32:37 +01:00
Jakob
a01f10b042 feat: add /api/system/metrics endpoint 2025-12-07 11:31:55 +01:00
MichaelDvP
26a5f98aae testdata 2025-12-06 14:30:45 +01:00
MichaelDvP
67280546af weblog queue complete in psram, save weblog settings in one call 2025-12-06 14:06:15 +01:00
Proddy
64058b0f61 Merge pull request #2792 from proddy/dev
small updates
2025-12-04 20:32:21 +01:00
proddy
d7b5c81b0e package update 2025-12-04 20:29:20 +01:00
MichaelDvP
1d03056784 move some vectors to psram, fix syslog start/stop 2025-12-04 19:57:01 +01:00
MichaelDvP
dd0ab8f962 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2025-12-04 09:22:47 +01:00
proddy
02e8dba971 fix max size to 32MB 2025-12-03 22:16:39 +01:00
proddy
59878fb190 remove vscode settings and package.json 2025-12-03 22:11:44 +01:00
proddy
9ff0f83af9 remove obsolete double auto-commit 2025-12-03 22:06:06 +01:00
proddy
e6f825371e fix make for Windows 2025-12-03 22:05:45 +01:00
proddy
45f3f23033 package update 2025-12-03 21:53:33 +01:00
proddy
ffd27db208 show elapsed time 2025-12-03 21:53:26 +01:00
proddy
a452d6131b DiffTemp ranged to 4-15 K - fix #2783 2025-12-03 21:53:16 +01:00
MichaelDvP
0bc478f9d3 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-12-02 21:08:49 +01:00
Proddy
03ef981765 Merge pull request #2788 from proddy/dev
fix mem issue
2025-12-02 19:42:05 +01:00
proddy
9ca9f25fd3 rollback modbus hash map - fix #2752 2025-12-02 19:41:17 +01:00
proddy
41122dddb2 dev-34 2025-12-02 19:40:45 +01:00
proddy
1e0c94d007 package update 2025-12-02 19:40:38 +01:00
proddy
3e42a7fb4c package update 2025-12-01 19:56:59 +01:00
proddy
a8fcc1fb44 show mem 2025-12-01 19:56:53 +01:00
proddy
e43416019d package update 2025-12-01 17:40:53 +01:00
Proddy
9f467ecec1 Merge pull request #2782 from proddy/dev
MQTT base fixes
2025-11-30 23:28:16 +01:00
proddy
273d87dbf1 use ~ as MQTT base 2025-11-30 23:27:28 +01:00
proddy
befd21f8cb fixe #2780 2025-11-30 23:27:18 +01:00
MichaelDvP
882e3bc1cd Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2025-11-30 21:25:07 +01:00
Proddy
15e05c4abc Merge pull request #2778 from proddy/dev
update to show heap
2025-11-30 20:51:42 +01:00
proddy
748a2f5fcf update to show heap 2025-11-30 20:51:17 +01:00
Proddy
37ba42faf8 Merge pull request #2775 from proddy/dev
loop tests
2025-11-30 17:24:56 +01:00
proddy
19e343e517 loop tests 2025-11-30 17:23:58 +01:00
Proddy
8f39129bf8 Merge pull request #2773 from proddy/dev
a collection of changes
2025-11-30 15:43:18 +01:00
proddy
9c3521caf2 add target native-test-create 2025-11-30 15:39:36 +01:00
proddy
40fc0fd2f9 anti-rollback! 2025-11-30 15:39:13 +01:00
proddy
ff8566498f typo 2025-11-30 15:15:03 +01:00
proddy
dd06882860 auto-formatting 2025-11-30 14:05:50 +01:00
proddy
fb2294c945 added Prometheus metrics 2025-11-30 14:05:02 +01:00
proddy
26ea8320ce rollback for #2752 2025-11-30 14:04:45 +01:00
proddy
0f4963d91e formatting 2025-11-30 14:04:14 +01:00
proddy
91020abc90 formatting 2025-11-30 14:04:05 +01:00
Proddy
64906f3ea0 Merge branch 'emsesp:dev' into dev 2025-11-30 13:54:26 +01:00
Proddy
28f85b4c5a Merge pull request #2774 from gr3enk/dev
Adding api/metrics endpoint for Prometheus integration
2025-11-30 13:51:47 +01:00
Jakob
b44a0d6813 test: add tests for api/metrics endpoint 2025-11-30 10:37:34 +01:00
Jakob
8af7cde2d6 feat: add api/metrics endpoint 2025-11-30 10:35:19 +01:00
proddy
3fcd656bb6 package update with new alova 2025-11-30 10:22:20 +01:00
proddy
76c827257e mqtt heartbeat, no need to check if connected 2025-11-30 10:22:08 +01:00
proddy
8ca9f7ee30 change ip 2025-11-29 15:36:24 +01:00
proddy
da7a06646a rollback AsyncWS fix #2752 2025-11-29 15:35:57 +01:00
proddy
0a36f1df7a updated 2025-11-29 15:14:33 +01:00
proddy
80e5d30781 dev-33 2025-11-29 15:14:26 +01:00
proddy
9c4beba3b1 add LWT to HA discovery config topic 2025-11-29 15:12:07 +01:00
proddy
0cf932f57e comment change 2025-11-29 14:49:08 +01:00
proddy
5cb9f3b014 init board profile correctly 2025-11-29 14:48:57 +01:00
proddy
3eb581142a default keep alive 60 seconds 2025-11-29 14:48:42 +01:00
proddy
d6c460e7fd ESP32Async/ESPAsyncWebServer @ 3.9.2 2025-11-29 14:48:24 +01:00
proddy
a2baa50530 show users reports error if not admin 2025-11-29 14:48:13 +01:00
proddy
6569b8c038 enable TLS for test data 2025-11-29 14:47:42 +01:00
proddy
48b4bf02a3 wider box for TLS 2025-11-29 14:47:35 +01:00
proddy
693054a92a package update 2025-11-29 14:47:27 +01:00
MichaelDvP
1472e53410 update asyncwebserver 3.9.1 2025-11-28 09:09:12 +01:00
MichaelDvP
5ed4970d62 change uart pins without restart 2025-11-28 09:08:53 +01:00
MichaelDvP
056cf3cbd6 settingservice gpio initialization for custom boards 2025-11-26 18:17:40 +01:00
MichaelDvP
2bcd548747 add counter 0..2 for short pulses, high frequency, #2758 2025-11-26 18:14:58 +01:00
MichaelDvP
9edcf47073 tx read, wait 2 sec brfore 3rd. retry 2025-11-26 18:12:24 +01:00
MichaelDvP
084d90e714 solar heat assistance, rounding custom entities #2763 2025-11-26 18:11:48 +01:00
Proddy
a738cc36dd Merge pull request #2762 from proddy/dev
gpio update
2025-11-26 17:03:09 +01:00
proddy
9b6f9aeda3 dev-32 2025-11-26 16:52:54 +01:00
proddy
8ea8c1821d Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2025-11-26 14:00:08 +01:00
proddy
53a1a8826e update 2025-11-26 14:00:06 +01:00
proddy
237551d9f6 remove eth power gpio if set 2025-11-26 14:00:02 +01:00
proddy
f7c7bc65f2 keep loop even if there is gpio errors 2025-11-26 13:59:46 +01:00
proddy
badbd9c6fe show system status in status page 2025-11-26 13:59:34 +01:00
proddy
cc7ac5b911 package update 2025-11-26 13:59:08 +01:00
proddy
5957300c4e change (c) date to 2025 2025-11-24 23:27:01 +01:00
proddy
2e343ce0c3 optimize modbus 2025-11-24 23:25:53 +01:00
proddy
3113a4be2b remove old todos 2025-11-24 23:25:43 +01:00
proddy
7d9cb2932f update tests 2025-11-24 23:17:37 +01:00
proddy
026c2caea0 fix when manually setting board type 2025-11-24 23:17:27 +01:00
proddy
eb058b688d lint warnings 2025-11-24 22:37:51 +01:00
proddy
2a7a0ce3f6 formatting 2025-11-24 22:37:41 +01:00
proddy
a330110eef update 2025-11-24 22:37:30 +01:00
proddy
feed534be5 package update 2025-11-24 22:37:23 +01:00
proddy
2375633bca don't show confusing release type checkboxes 2025-11-24 22:35:07 +01:00
proddy
7018139289 update version data 2025-11-24 22:34:34 +01:00
proddy
5f67934f26 updated 2025-11-24 22:12:46 +01:00
proddy
574cc8ad33 error message text change 2025-11-24 22:12:41 +01:00
proddy
658c613c4e init data with 0, no difference really 2025-11-24 22:12:24 +01:00
proddy
c2ce54c2a7 adjust gpio for ESP32 boards 2025-11-24 16:58:06 +01:00
proddy
58da0d9778 update 2025-11-24 16:57:55 +01:00
proddy
8b90036c11 fixed uart0 for esp chips 2025-11-24 16:44:13 +01:00
proddy
65678ea739 package update 2025-11-24 16:44:03 +01:00
Proddy
34ddc9b7ff Merge pull request #2750 from proddy/dev
adjustments to invalid sensors
2025-11-23 20:35:46 +01:00
proddy
97f9914d33 grey out entities with no value 2025-11-23 20:32:25 +01:00
proddy
8b64851f6f renaming 2025-11-23 20:07:36 +01:00
proddy
c00f50238e add/remove analog gpio from list - fixes #2758 2025-11-23 20:07:22 +01:00
proddy
25ea57e02f don't allow gpio 1 and 3, as will conflict with Serial/UART0 2025-11-23 20:06:50 +01:00
proddy
a951afe205 rename valid_gpio_list to available_gpios 2025-11-23 20:06:26 +01:00
proddy
44c7954ce7 package update 2025-11-23 20:05:17 +01:00
proddy
ef315b6dde consistent borders and removed some comments 2025-11-23 16:55:46 +01:00
proddy
935a9dcbb7 fix remove_gpio 2025-11-23 16:55:25 +01:00
proddy
2bda432d70 show in log if we're autodetecting board 2025-11-23 16:54:56 +01:00
proddy
d567ea3cf0 adjusted remove pin 2025-11-23 16:34:06 +01:00
proddy
7b5e386595 update tests 2025-11-23 14:54:31 +01:00
proddy
1c65a7caba fix count of temp sensors - #2756 2025-11-23 14:46:57 +01:00
proddy
12ebacf1a7 typo 2025-11-23 14:13:29 +01:00
proddy
f2c176111f allow some strapping pins (Michael's fix) 2025-11-23 13:19:09 +01:00
proddy
cc242e5eba get rid of scrollbar 2025-11-23 13:12:59 +01:00
proddy
4888808ad0 skip if CUSTOM 2025-11-23 13:05:06 +01:00
proddy
cd1b5e1d57 skip if CUSTOM 2025-11-23 13:04:54 +01:00
proddy
9661c9a0eb typo 2025-11-23 13:04:31 +01:00
proddy
f6ccf6da44 formatting 2025-11-23 13:04:10 +01:00
proddy
b691488240 fix standalone 2025-11-22 23:05:58 +01:00
proddy
eb71996e6a align signon username/password boxes 2025-11-22 22:58:17 +01:00
proddy
b9566ae1d6 changed debug text 2025-11-22 22:57:59 +01:00
proddy
8016fc4287 gpio checking 2025-11-22 22:38:46 +01:00
proddy
c95b43ea69 remove emsesp namespace 2025-11-22 22:38:20 +01:00
proddy
d767a503cd remove log out of loop if we find a gpio error 2025-11-22 22:37:12 +01:00
proddy
221131f9d3 restart after setting board profile in console 2025-11-22 22:36:43 +01:00
proddy
f307cccabb don't check gpio 2025-11-22 22:36:15 +01:00
proddy
2f21d0d94c remove emsesp namespace 2025-11-22 22:34:28 +01:00
proddy
132d1292ce remove emsesp namespace 2025-11-22 22:34:14 +01:00
proddy
df9a10cb53 remove esmesp:: 2025-11-22 22:33:31 +01:00
proddy
95d564901b formatting 2025-11-22 22:33:17 +01:00
proddy
6424d9b1c0 package update 2025-11-22 22:33:06 +01:00
proddy
38a3d20acf prevent double calling api 2025-11-22 22:32:49 +01:00
proddy
89b117bbc2 formatting 2025-11-22 22:32:39 +01:00
proddy
eeba7a3a6b fix 2025-11-20 23:03:06 +01:00
proddy
23a660aabb updated gpio test logic 2025-11-20 22:58:26 +01:00
proddy
c9bddba446 fix standalone 2025-11-20 15:21:38 +01:00
proddy
3a508a3ec4 fixes #2752 2025-11-20 14:58:54 +01:00
proddy
6bf33f6447 update test API 2025-11-20 14:57:57 +01:00
proddy
a02054ceb6 new test for #2752 2025-11-20 14:35:58 +01:00
proddy
8422521975 formatting of error message box to align buttons 2025-11-20 14:25:25 +01:00
proddy
fbfacc5ed5 3.7.3-dev.31 2025-11-20 14:24:57 +01:00
proddy
b9d96620a4 show restart needed after changing board profile. also no need to restart sensors. 2025-11-20 14:24:40 +01:00
proddy
8c61735579 package update 2025-11-20 14:12:04 +01:00
proddy
56e8ccdfc4 remove gpio checks, as they are performed in supporting functions 2025-11-18 21:38:19 +01:00
proddy
5454e7dd16 warning message formatting so its same as Dashboard 2025-11-18 21:37:55 +01:00
proddy
901a27140c only dallas and led use GPIO 0 for disabled 2025-11-18 20:41:30 +01:00
proddy
37db2b9504 add comment 2025-11-18 20:40:07 +01:00
proddy
5e7768f912 package update 2025-11-18 20:39:51 +01:00
proddy
80dd16740d some fixes on https://github.com/emsesp/EMS-ESP32/pull/2750 2025-11-17 23:00:45 +01:00
proddy
24421a0224 package update 2025-11-17 17:50:43 +01:00
proddy
26d26cf088 formatting 2025-11-17 17:50:34 +01:00
proddy
615c5e8439 addition 2025-11-17 17:49:37 +01:00
proddy
090387ef37 replace disabled sensor gpio (99) with code logic 2025-11-17 17:49:30 +01:00
proddy
eaa277fef0 v3.7.2 2025-03-22 10:32:03 +01:00
proddy
e418b7d8e7 fix routing in tabs - #2264 2024-11-30 21:31:42 +01:00
proddy
a4e3be6a69 3.7.1 changelog 2024-11-29 10:30:14 +01:00
proddy
a1bc5bb055 3.7.1 2024-11-29 10:30:05 +01:00
proddy
f444ca31e0 Merge remote-tracking branch 'origin/dev' 2024-10-27 11:41:47 +01:00
proddy
1b70b55989 update demo url 2024-08-10 14:58:48 +02:00
proddy
1487f30c43 Merge remote-tracking branch 'origin/dev' for 3.6.5 2024-03-23 17:56:05 +01:00
Proddy
e00eb8e64f 3.6.4 2023-11-26 20:11:36 +01:00
Proddy
f41bb3671c 3.6.4 2023-11-24 07:36:36 +01:00
Proddy
22c75e6df3 3.6.4 2023-11-24 07:36:29 +01:00
proddy
5ab10b7aa6 fixes #1450 2023-11-22 09:48:09 +01:00
Proddy
ee5fd4d0eb 3.6.3 2023-11-21 14:40:47 +01:00
Proddy
46f35bc67c another patch on 3.6.3 2023-11-21 14:40:32 +01:00
Proddy
ec85a7ec24 3.6.3 (refershed) 2023-11-19 21:24:01 +01:00
Proddy
02f2389587 add workflow_dispatch: 2023-11-18 18:51:12 +01:00
Proddy
7f140021aa quick fix - https://github.com/emsesp/EMS-ESP32/pull/1438 2023-11-18 18:47:28 +01:00
Proddy
6796962c1e 3.6.3 2023-11-18 13:35:20 +01:00
Proddy
df6de21cf4 Merge remote-tracking branch 'origin/dev' 2023-11-18 13:35:04 +01:00
Proddy
df9f75a5c9 updated yarn for 3.6.2 2023-10-01 17:42:54 +02:00
Proddy
7bd8710eb6 3.6.2 2023-10-01 17:40:09 +02:00
Proddy
32f2c6d341 Merge remote-tracking branch 'origin/dev' 2023-10-01 17:40:02 +02:00
Proddy
86919c1684 Merge branch 'origin/dev' 2023-09-09 14:12:07 +02:00
Proddy
86e29515e7 build s3 2023-08-15 18:44:24 +02:00
Proddy
46eb4185d7 update with 3.6.0 2023-08-13 14:37:13 +02:00
Proddy
8da6761a48 Merge branch 'dev' 2023-08-13 14:32:41 +02:00
Proddy
9233f0dfcc v3.5.1 - merge with patch 2023-03-11 16:06:05 +01:00
Proddy
292f743b14 Update bug_report.md 2023-02-19 11:18:08 +01:00
Proddy
dd6dfffd57 Delete questions---troubleshooting.md 2023-02-19 10:49:52 +01:00
Proddy
ec705a5307 Delete feature_request.md 2023-02-19 10:49:45 +01:00
Proddy
f45f071710 Create config.yml 2023-02-19 10:49:32 +01:00
Proddy
f3858546de Merge branch 'origin/dev' 2023-02-06 21:58:27 +01:00
Proddy
d0ac0b7804 update with version on dev 2022-10-30 16:58:37 +01:00
Proddy
d8284ec09f Merge pull request #705 from MichaelDvP/main
v3.4.4 Fix for new installations with filesystem not initializing
2022-10-29 10:46:41 +02:00
MichaelDvP
6e982acde8 v3.4.4 Fix for new installations with filesystem not initializing 2022-10-28 10:50:51 +02:00
Proddy
8c94ce99b2 quick fix for filesystem initialization 2022-10-08 09:23:00 +02:00
proddy
fc057d18c9 Merge remote-tracking branch 'origin/dev' 2022-09-18 14:33:23 +02:00
Proddy
18e9b99413 Merge remote-tracking branch 'origin/dev' into main 2022-05-29 16:16:38 +02:00
Proddy
a47e0e8266 update for 3.4.0 2022-05-23 21:20:45 +02:00
Proddy
f412ddc716 Merge remote-tracking branch 'origin/dev' into main 2022-05-23 21:20:36 +02:00
proddy
29110e96e5 Merge remote-tracking branch 'origin/dev' 2022-01-20 10:51:40 +01:00
proddy
b65866217a 3.4.0 2021-11-28 23:03:28 +01:00
proddy
611e3b1243 Merge remote-tracking branch 'origin/dev' 2021-11-28 23:03:15 +01:00
proddy
2ca0a0c634 v3.2.1 merged from dev 2021-08-08 14:46:14 +02:00
proddy
7eb1f061b7 Merge remote-tracking branch 'origin/dev' for 3.2.0 release 2021-08-06 12:06:08 +02:00
proddy
50459a23fe force v16 of nodejs 2021-06-26 11:13:07 +02:00
proddy
5bf53c3389 3.1.1 2021-06-26 11:03:03 +02:00
proddy
4b7aa95be3 Merge remote-tracking branch 'origin/dev' 2021-06-26 11:02:55 +02:00
Proddy
70943f5758 Update pre_release.yml 2021-05-16 15:52:09 +02:00
Proddy
3bc280b817 Delete check_code.yml 2021-05-16 15:51:56 +02:00
Proddy
62b15a5319 Update pre_release.yml 2021-05-16 15:35:06 +02:00
Proddy
8dd18802d6 Update tagged_release.yml 2021-05-16 15:34:45 +02:00
proddy
57a516a83a updated README and images 2021-05-09 15:13:16 +02:00
proddy
a57fdaa4b3 Merge remote-tracking branch 'origin/dev' into main 2021-05-04 12:21:51 +02:00
proddy
4841e42286 Merge remote-tracking branch 'origin/dev' into main 2021-03-30 16:35:18 +02:00
proddy
8c2d2b06ed cleaned up old changelog 2021-03-18 20:59:09 +01:00
proddy
38c8b1b7f0 3.0.0 2021-03-18 20:58:21 +01:00
proddy
6fb5933a02 Merge remote-tracking branch 'origin/dev' into main 2021-03-18 20:58:12 +01:00
proddy
c0944433be remove workspace.code-workspace 2021-03-16 17:41:42 +01:00
Proddy
478e6362c9 Merge pull request #27 from FauthD:main
Add global names to Dallas sensors to avoid ugly <unknown> and other …
2021-03-16 17:39:00 +01:00
fauthd
4d6354db78 Add stuff to gitignore, add vscode workspace 2021-03-16 16:47:09 +01:00
fauthd
beab0f0c77 Add global names to Dallas sensors to avoid ugly <unknown> and other issues in HA 2021-03-16 16:00:23 +01:00
Proddy
c17749bd22 Update README.md 2021-03-14 23:43:33 +01:00
proddy
2bad769c5c build: include assets 2021-03-14 21:20:51 +01:00
proddy
8ad89ca64b move repo 2021-03-14 21:05:15 +01:00
proddy
9244d8daec Semantic Commit Messages 2021-03-14 18:10:57 +01:00
proddy
02d01334b2 update new build 2021-03-14 17:37:18 +01:00
325 changed files with 26613 additions and 16852 deletions

View File

@@ -21,8 +21,8 @@ _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 [discussions](https://github.com/emsesp/EMS-ESP32/discussions)
- [ ] Searched the issue in the [docs](https://docs.emsesp.org/Troubleshooting/)
- [ ] Searched the issue in the [chat](https://discord.gg/3J3GgnzpyT)
- [ ] Searched the issue in the [docs](https://emsesp.org/Troubleshooting/)
- [ ] Searched the issue in the [chat](https://discord.gg/GP9DPSgeJq)
- [ ] Provide the System information in the area below, taken from `http://<IP>/api/system`
```json

View File

@@ -1,11 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: EMS-ESP Docs
url: https://docs.emsesp.org
url: https://emsesp.org
about: All the information related to EMS-ESP.
- name: EMS-ESP Discussions and Support
url: https://github.com/emsesp/EMS-ESP32/discussions
about: EMS-ESP usage Questions, Feature Requests and Projects.
- name: EMS-ESP Users Chat
url: https://discord.gg/3J3GgnzpyT
url: https://discord.gg/GP9DPSgeJq
about: Chat for feedback, questions and troubleshooting.

View File

@@ -28,7 +28,7 @@ jobs:
node-version: 24
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Enable Corepack
run: corepack enable pnpm
@@ -47,7 +47,7 @@ jobs:
- name: Build webUI
run: |
platformio run -e build_webUI
platformio run -e build-webUI
- name: Build modbus
run: |
@@ -62,35 +62,13 @@ jobs:
platformio run
- name: Commit the generated files
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: "chore: update generated files"
- name: Configure Git
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
- name: Check for changes and commit
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "Changes detected, committing..."
git add .
git commit -m "Auto-commit build artifacts and configuration updates
- Updated build configurations
- Generated build artifacts
- Version: ${{steps.build_info.outputs.VERSION}}"
echo "Pushing changes to repository..."
git push origin dev
else
echo "No changes to commit"
fi
commit_message: "chore: update generated files for v${{steps.build_info.outputs.VERSION}}"
- name: Create GitHub Release
id: 'automatic_releases'
uses: emsesp/action-automatic-releases@v1.0.0
uses: emsesp/action-automatic-releases@v1.0.1
with:
repo_token: '${{ secrets.GITHUB_TOKEN }}'
title: Development Build v${{steps.build_info.outputs.VERSION}}

View File

@@ -11,7 +11,7 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: GitHub Releases To Discord
uses: SethCohen/github-releases-to-discord@v1.13.1

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install python 3.13
uses: actions/setup-python@v6

View File

@@ -19,7 +19,7 @@ jobs:
BUILD_WRAPPER_OUT_DIR: bw-output
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Build Wrapper

View File

@@ -26,7 +26,7 @@ jobs:
node-version: 24
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Enable Corepack
run: corepack enable pnpm
@@ -39,7 +39,7 @@ jobs:
- name: Build webUI
run: |
platformio run -e build_webUI
platformio run -e build-webUI
- name: Build modbus
run: |
@@ -54,7 +54,7 @@ jobs:
platformio run
- name: Create GitHub Release
uses: emsesp/action-automatic-releases@v1.0.0
uses: emsesp/action-automatic-releases@v1.0.1
with:
repo_token: '${{ secrets.GITHUB_TOKEN }}'
prerelease: false

View File

@@ -28,7 +28,7 @@ jobs:
node-version: 24
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Enable Corepack
run: corepack enable pnpm
@@ -47,7 +47,7 @@ jobs:
- name: Build webUI
run: |
platformio run -e build_webUI
platformio run -e build-webUI
- name: Build modbus
run: |
@@ -63,7 +63,7 @@ jobs:
- name: Create GitHub Release
id: 'automatic_releases'
uses: emsesp/action-automatic-releases@v1.0.0
uses: emsesp/action-automatic-releases@v1.0.1
with:
repo_token: '${{ secrets.GITHUB_TOKEN }}'
title: Test Build v${{steps.build_info.outputs.VERSION}}

6
.gitignore vendored
View File

@@ -2,7 +2,6 @@
.vscode/c_cpp_properties.json
.vscode/extensions.json
.vscode/launch.json
.vscode/settings.json
# c++ compiling
.clang_complete
@@ -73,5 +72,6 @@ logs/*
sdkconfig.*
sdkconfig_tasmota_esp32
pnpm-lock.yaml
package.json
.cache/
.cache/
interface/.tsbuildinfo
test/test_api/package-lock.json

View File

@@ -5,6 +5,157 @@ 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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.8.2] 12 May 2026
## Added
- comfortpoint for BC400 [#2935](https://github.com/emsesp/EMS-ESP32/issues/2935)
- customize device brand [#2784](https://github.com/emsesp/EMS-ESP32/issues/2784)
- 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)
- 2.nd freshwater module (dhw4, dhw5) [#2991](https://github.com/emsesp/EMS-ESP32/issues/2991)
- full system backup and restore
- auto-logic to set ht3/ems+ tx-mode
- polariity for digital_in sensors [#3070](https://github.com/emsesp/EMS-ESP32/discussions/3070)
## Fixed
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
- missing translations [#3015](https://github.com/emsesp/EMS-ESP32/issues/3015)
- custom entities check fetch length
- modbus initialization [#3064](https://github.com/emsesp/EMS-ESP32/issues/3064)
## Changed
- weblogbuffer up to 1000 messages with PSRAM, mentioned in [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
- validate custom entity writes, [#2931](https://github.com/emsesp/EMS-ESP32/issues/2931)
- remove wrong burnMinPower [#2918](https://github.com/emsesp/EMS-ESP32/issues/2918)
- store scheduler active state to nvs [#2946](https://github.com/emsesp/EMS-ESP32/discussions/2946)
- translated modes `heat` and `eco` for HA-climate mode-str-tpl
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
- 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)
- 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)
- check and read 0x470 as summer2_typeids[0] only if received [#2686](https://github.com/emsesp/EMS-ESP32/issues/2686), [#3055](https://github.com/emsesp/EMS-ESP32/issues/3055)
- default bus-id: gateway1(0x49), tx-mode: auto
## [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
## Added
- analogsensor types: NTC and RGB-Led
- Flag for HMC310 [#2465](https://github.com/emsesp/EMS-ESP32/issues/2465)
- boiler auxheatersource [#2489](https://github.com/emsesp/EMS-ESP32/discussions/2489)
- thermostat last error for RC100/300 [#2501](https://github.com/emsesp/EMS-ESP32/issues/2501)
- boiler 0xC6 telegram [#1963](https://github.com/emsesp/EMS-ESP32/issues/1963)
- CS6800i changes [#2448](https://github.com/emsesp/EMS-ESP32/issues/2448), [#2449](https://github.com/emsesp/EMS-ESP32/issues/2449)
- charging pump [#2544](https://github.com/emsesp/EMS-ESP32/issues/2544)
- hybrid CSH5800iG [#2569](https://github.com/emsesp/EMS-ESP32/issues/2569)
- added EMS Device details to Home Assistant MQTT Discovery
- disinfection command [#2601](https://github.com/emsesp/EMS-ESP32/issues/2601)
- added new board profile for upcoming BBQKees E32V2.2
- set differential pressure entity in Mixer device
- set set climate action cooling/heating in HA [#2583](https://github.com/emsesp/EMS-ESP32/issues/2583)
- Internal sensors of E32V2_2
- FW200 display options [#2610](https://github.com/emsesp/EMS-ESP32/discussions/2610)
- CR11 mode settings OFF/MANUAL depends on selTemp [#2437](https://github.com/emsesp/EMS-ESP32/issues/2437)
- implemented eFuse settings for BBQKees boards to store model type and ESP chipset
- analogsensors for pulse output [#2624](https://github.com/emsesp/EMS-ESP32/discussions/2624)
- analogsensors frequency input [#2631](https://github.com/emsesp/EMS-ESP32/discussions/2631)
- SRC plus thermostats [#2636](https://github.com/emsesp/EMS-ESP32/issues/2636)
- Greenstar 2000 [#2645](https://github.com/emsesp/EMS-ESP32/issues/2645)
- RC3xx `dhw modetype` [#2659](https://github.com/emsesp/EMS-ESP32/discussions/2659)
- new boiler entities VR0,VR1, compressor speed [#2669](https://github.com/emsesp/EMS-ESP32/issues/2669)
- solar temperature TS16 [#2690](https://github.com/emsesp/EMS-ESP32/issues/2690)
- pumpmode enum for HT3 boilers, add commands for manual defrost, chimneysweeper [#2727](https://github.com/emsesp/EMS-ESP32/issues/2727)
- pid settings [#2735](https://github.com/emsesp/EMS-ESP32/issues/2735)
- refresh MQTT button added to MQTT Settings page
- heating assistance, rounding custum settings [#2763](https://github.com/emsesp/EMS-ESP32/discussions/2763)
- added counter 0..2 for short pulses, high frequency [#2758](https://github.com/emsesp/EMS-ESP32/issues/2758)
- added LWT (Last Will and Testament) to MQTT entities in Home Assistant
- added api/metrics endpoint for prometheus integration by @gr3enk [#2774](https://github.com/emsesp/EMS-ESP32/pull/2774)
- added RTL8201 to eth phy list [#2800](https://github.com/emsesp/EMS-ESP32/issues/2800)
- added partitions to Web UI Version page, so previous firmware versions can be installed [#2837](https://github.com/emsesp/EMS-ESP32/issues/2837)
- button pressures show LED. On a long press (10 seconds) the LED flashes for 5 seconds to indicate a factory reset is about to happen. [#2848](https://github.com/emsesp/EMS-ESP32/issues/2848)
- added `txpause` command to pause the TX, by setting Txmode to 0 (disabled) [#2850](https://github.com/emsesp/EMS-ESP32/issues/2850)
## Fixed
- dhw/switchtime [#2490](https://github.com/emsesp/EMS-ESP32/issues/2490)
- switch to secure mqtt [#2492](https://github.com/emsesp/EMS-ESP32/issues/2492)
- update link buttons [#2497](https://github.com/emsesp/EMS-ESP32/issues/2497)
- refresh scheduler states [#2502](https://github.com/emsesp/EMS-ESP32/discussions/2502)
- also rebuild HA config on mqtt connect for scheduler, custom and shower
- FB100 controls the hc, not the master [#2510](https://github.com/emsesp/EMS-ESP32/issues/2510)
- IPM DHW module, [#2524](https://github.com/emsesp/EMS-ESP32/issues/2524)
- charge optimization [#2543](https://github.com/emsesp/EMS-ESP32/issues/2543)
- shower active state retained, shows correctly in HA
- MQTT Command Topic with slashes [#2571](https://github.com/emsesp/EMS-ESP32/issues/2571)
- Add pulsed water meter input to V1.3 gateway with Lilygo S3 [#2550](https://github.com/emsesp/EMS-ESP32/issues/2550)
- fix missing long 10-second press of Button to perform a factory reset
- fix wwMaxPower on Junkers ZBS14 [#2609](https://github.com/emsesp/EMS-ESP32/issues/2609)
- ventilation bypass state from telegram 0x55C [#1197](https://github.com/emsesp/EMS-ESP32/issues/1197)
- set selflowtemp for ems+ boilers [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
- syslog timestamp [#2704](https://github.com/emsesp/EMS-ESP32/issues/2704)
- fixed FS format command [#2720](https://github.com/emsesp/EMS-ESP32/discussions/2720)
- dhw priority setting to boiler and mixer, telegrams 0x2CC, 0x2CD, etc.
- check for valid GPIOs when board profile is changed [#2841](https://github.com/emsesp/EMS-ESP32/issues/2841)
## Changed
- show console log with ISO date/time [#2533](https://github.com/emsesp/EMS-ESP32/discussions/2533)
- removed ESP32 CPU temperature
- updated core libraries like AsyncTCP, AsyncWebServer and Modbus
- remove command `scan deep`
- ignore repeated `forceheatingoff` commands [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
- optimized web for better performance by adding lazy loading and caching
- internal system analog sensors (core_voltage, supply_voltage and gateway_temperature) cannot be accidentally removed
- double click button reconnects EMS-ESP to AP
- place system message command in side scheduler loop to reduce stack memory usage by 2KB
- syslog mark interval set to 1 hour
- handle process_telegram in oneloop
- improved GPIO validation for Analog Sensors and System GPIOs
- entities with no values are greyed out in the Web UI in the Customization page
- added System Status to Web Status page
- show number on entities and supported languages in log on boot
- on tx read fail delay the 3rd. retry 2 sec
- move vectors and lists to PSRAM
- removed unused last topic/payload echo-check
- added Home Assistant device details to MQTT Discovery for all devices
- device_class and state_class changes for HA MQTT Discovery [#2825](https://github.com/emsesp/EMS-ESP32/issues/2825)
## [3.7.2] 22 March 2025
## Added
@@ -85,7 +236,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The automatically generated temperature sensor ID has replaced dashes (`-`) with underscores (`_`) to be compatible with Home Assistant.
- `api/system/info` has it's JSON key names changed to camelCase syntax.
For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
For more details go to [emsesp.org](https://emsesp.org/).
## Added

View File

@@ -1,71 +1,13 @@
# Changelog
For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
For more details go to [emsesp.org](https://emsesp.org/).
## [3.7.3]
## [3.8.3]
## Added
- analogsensor types: NTC and RGB-Led
- Flag for HMC310 [#2465](https://github.com/emsesp/EMS-ESP32/issues/2465)
- boiler auxheatersource [#2489](https://github.com/emsesp/EMS-ESP32/discussions/2489)
- thermostat last error for RC100/300 [#2501](https://github.com/emsesp/EMS-ESP32/issues/2501)
- boiler 0xC6 telegram [#1963](https://github.com/emsesp/EMS-ESP32/issues/1963)
- CS6800i changes [#2448](https://github.com/emsesp/EMS-ESP32/issues/2448), [#2449](https://github.com/emsesp/EMS-ESP32/issues/2449)
- charging pump [#2544](https://github.com/emsesp/EMS-ESP32/issues/2544)
- hybrid CSH5800iG [#2569](https://github.com/emsesp/EMS-ESP32/issues/2569)
- add EMS Device details to Home Assistant MQTT Discovery
- disinfection command [#2601](https://github.com/emsesp/EMS-ESP32/issues/2601)
- added new board profile for upcoming BBQKees E32V2.2
- set differential pressure entity in Mixer device
- set set climate action cooling/heating in HA [#2583](https://github.com/emsesp/EMS-ESP32/issues/2583)
- Internal sensors of E32V2_2
- FW200 display options [#2610](https://github.com/emsesp/EMS-ESP32/discussions/2610)
- CR11 mode settings OFF/MANUAL depends on selTemp [#2437](https://github.com/emsesp/EMS-ESP32/issues/2437)
- implemented eFuse settings for BBQKees boards to store model type and ESP chipset
- Analogsensors for pulse output [#2624](https://github.com/emsesp/EMS-ESP32/discussions/2624)
- Analogsensors frequency input [#2631](https://github.com/emsesp/EMS-ESP32/discussions/2631)
- SRC plus thermostats [#2636](https://github.com/emsesp/EMS-ESP32/issues/2636)
- Greenstar 2000 [#2645](https://github.com/emsesp/EMS-ESP32/issues/2645)
- RC3xx `dhw modetype` [#2659](https://github.com/emsesp/EMS-ESP32/discussions/2659)
- new boiler entities VR0,VR1, compressor speed [#2669](https://github.com/emsesp/EMS-ESP32/issues/2669)
- solar temperature TS16 [#2690](https://github.com/emsesp/EMS-ESP32/issues/2690)
- pumpmode enum for HT3 boilers, add commands for manual defrost, chimneysweeper [#2727](https://github.com/emsesp/EMS-ESP32/issues/2727)
- pid settings [#2735](https://github.com/emsesp/EMS-ESP32/issues/2735)
- refresh MQTT button added to MQTT Settings page
## Fixed
- dhw/switchtime [#2490](https://github.com/emsesp/EMS-ESP32/issues/2490)
- switch to secure mqtt [#2492](https://github.com/emsesp/EMS-ESP32/issues/2492)
- update link buttons [#2497](https://github.com/emsesp/EMS-ESP32/issues/2497)
- refresh scheduler states [#2502](https://github.com/emsesp/EMS-ESP32/discussions/2502)
- also rebuild HA config on mqtt connect for scheduler, custom and shower
- FB100 controls the hc, not the master [#2510](https://github.com/emsesp/EMS-ESP32/issues/2510)
- IPM DHW module, [#2524](https://github.com/emsesp/EMS-ESP32/issues/2524)
- charge optimization [#2543](https://github.com/emsesp/EMS-ESP32/issues/2543)
- shower active state retained, shows correctly in HA
- MQTT Command Topic with slashes [#2571](https://github.com/emsesp/EMS-ESP32/issues/2571)
- Add pulsed water meter input to V1.3 gateway with Lilygo S3 [#2550](https://github.com/emsesp/EMS-ESP32/issues/2550)
- fix missing long 10-second press of Button to perform a factory reset
- fix wwMaxPower on Junkers ZBS14 [#2609](https://github.com/emsesp/EMS-ESP32/issues/2609)
- ventilation bypass state from telegram 0x55C [#1197](https://github.com/emsesp/EMS-ESP32/issues/1197)
- set selflowtemp for ems+ boilers [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
- syslog timestamp [#2704](https://github.com/emsesp/EMS-ESP32/issues/2704)
- fixed FS format command [#2720](https://github.com/emsesp/EMS-ESP32/discussions/2720)
- dhw priority setting to boiler and mixer, telegrams 0x2CC, 0x2CD, etc.
## Changed
- show console log with ISO date/time [#2533](https://github.com/emsesp/EMS-ESP32/discussions/2533)
- remove ESP32 CPU temperature
- updated core libraries like AsyncTCP, AsyncWebServer and Modbus
- remove command `scan deep`
- ignore repeated `forceheatingoff` commands [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
- optimized web for better performance by adding lazy loading and caching
- internal system analog sensors (core_voltage, supply_voltage and gateway_temperature) cannot be accidentally removed
- double click button reconnects EMS-ESP to AP
- place system message command in side scheduler loop to reduce stack memory usage by 2KB
- syslog mark interval set to 1 hour
- handle process_telegram in oneloop
- improved GPIO validation in Analog Sensors and System settings

View File

@@ -6,7 +6,7 @@ Everybody is welcome and invited to contribute to the EMS-ESP Project by:
- providing Pull Requests (Features, Fixes, suggestions)
- testing new released features and report issues on your EMS equipment
- contributing to missing [documentation](https://docs.emsesp.org)
- contributing to missing [documentation](https://emsesp.org)
This document describes rules that are in effect for this repository, meant for handling issues by contributors in the issue tracker and PRs.

View File

@@ -21,13 +21,14 @@ endif
# Optimize parallel build configuration
UNAME_S := $(shell uname -s)
JOBS ?= 1
ifeq ($(UNAME_S),Linux)
EXTRA_CPPFLAGS = -D LINUX
JOBS ?= $(shell nproc)
JOBS := $(shell nproc)
endif
ifeq ($(UNAME_S),Darwin)
EXTRA_CPPFLAGS = -D OSX -Wno-tautological-constant-out-of-range-compare
JOBS ?= $(shell sysctl -n hw.ncpu)
JOBS := $(shell sysctl -n hw.ncpu)
endif
# Set optimal parallel build settings
@@ -46,27 +47,28 @@ MAKEFLAGS += -j$(JOBS) -l$(shell echo $$(($(JOBS) * 2)))
#----------------------------------------------------------------------
TARGET := emsesp
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
INCLUDES := src/core src/devices src/web src/test lib/* lib_standalone 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
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/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 :=
CPPCHECK = cppcheck
CHECKFLAGS = -q --force --std=gnu++17
CHECKFLAGS = -q --force --std=gnu++20
#----------------------------------------------------------------------
# Languages Standard
#----------------------------------------------------------------------
C_STANDARD := -std=c17
CXX_STANDARD := -std=gnu++17
C_STANDARD := -std=c20
CXX_STANDARD := -std=gnu++20
#----------------------------------------------------------------------
# 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 += -DNO_TLS_SUPPORT
DEFINES += $(ARGS)
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.3-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
#----------------------------------------------------------------------
# Sources & Files
@@ -78,6 +80,10 @@ SYMBOLS := $(CURDIR)/$(BUILD)/$(TARGET).out
CSOURCES := $(shell find $(SOURCES) -name "*.c" 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)))
DEPS := $(patsubst %,$(BUILD)/%.d,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)))
@@ -137,6 +143,7 @@ DEPFLAGS += -MF $(BUILD)/$*.d -MT $@
LINK.o = $(LD) $(LDFLAGS) $(LDLIBS) $^ -o $@
COMPILE.c = $(CC) $(C_STANDARD) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
COMPILE.cpp = $(CXX) $(CXX_STANDARD) $(CXXFLAGS) $(DEPFLAGS) -c $< -o $@
COMPILE.s = $(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
#----------------------------------------------------------------------
# Special Built-in Target
@@ -180,6 +187,7 @@ $(BUILD)/%.o: %.cpp
$(BUILD)/%.o: %.s
@mkdir -p $(@D)
@$(ECHO) Compiling $@
@$(COMPILE.s)
cppcheck: $(SOURCES)

View File

@@ -15,10 +15,10 @@
<a href="https://github.com/emsesp/EMS-ESP32/blob/dev/CONTRIBUTING.md">
<img src="https://img.shields.io/badge/Contribute-ff4785?style=for-the-badge&logo=git&logoColor=white" alt="Contribute" />
</a>
<a href="https://docs.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" />
</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" />
</a>
<a href="https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md">
@@ -32,7 +32,8 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=emsesp_EMS-ESP32&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=emsesp_EMS-ESP32)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9441142f49424ef891e8f5251866ee6b)](https://app.codacy.com/gh/emsesp/EMS-ESP32/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![downloads](https://img.shields.io/github/downloads/emsesp/EMS-ESP32/total.svg)](https://github.com/emsesp/EMS-ESP32/releases)
[![chat](https://img.shields.io/discord/816637840644505620.svg?style=flat-square&color=blueviolet)](https://discord.gg/3J3GgnzpyT)
[![chat](https://img.shields.io/discord/816637840644505620.svg?style=flat-square&color=blueviolet)](https://discord.gg/GP9DPSgeJq)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/emsesp/EMS-ESP32)
[![GitHub stars](https://img.shields.io/github/stars/emsesp/EMS-ESP32.svg?style=social&label=Star)](https://github.com/emsesp/EMS-ESP32/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/emsesp/EMS-ESP32.svg?style=social&label=Fork)](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.
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.
## 📦&nbsp; **Key Features**
@@ -60,33 +62,31 @@ It requires a small circuit to interface with the EMS bus which can be purchased
## 🚀&nbsp; **Installing**
Head over to [download.emsesp.org](https://download.emsesp.org) for instructions on how to install EMS-ESP. There is also further details on which boards are supported in [this section](https://docs.emsesp.org/Installing/) of the documentation.
Head over to the [Installation Guide](https://emsesp.org/Installing) section of the documentation for instructions on how to install EMS-ESP.
## 📋&nbsp; **Documentation**
Visit [emsesp.org](https://docs.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.
## 💬&nbsp; **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://docs.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.
## 🎥&nbsp; **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.
## 💖&nbsp; **Contributors**
EMS-ESP is a project created by [proddy](https://github.com/proddy) and owned and maintained by both [proddy](https://github.com/proddy) and [MichaelDvP](https://github.com/MichaelDvP) with support from [BBQKees Electronics](https://bbqkees-electronics.nl).
EMS-ESP is a project originally created by [proddy](https://github.com/proddy) and maintained by the ems-esp community.
If you like **EMS-ESP**, please give it a ✨ on GitHub, or even better fork it and contribute. You can also offer a small donation. This is an open-source project maintained by volunteers, and your support is greatly appreciated.
## 📦&nbsp; **Building**
To build the web interface only, run `platformio run -e build_webUI`. This will install the necessary dependencies and build the web interface and also create the embedded code used need to build the firmware. You can run the web interface locally by going to the `interface` directory and running `pnpm standalone`.
To build the firmware, run `platformio run`. This will build the firmware for all ESP32 modules and place the binaries in the `build/firmware` folder. If you want to configure the build for a single platform create a local `pio_local.ni` file in the root directory (see example in `pio_local.ini_example`).
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.
## 📢&nbsp; **Libraries used**

5
SECURITY.md Normal file
View File

@@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
Please report any security vulnerabilities using the [Contact Form](https://emsesp.org/About/#-contact).

View File

@@ -37,8 +37,8 @@
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"use_1200bps_touch": false,
"wait_for_upload_port": false,
"require_upload_port": true,
"speed": 921600
},

View File

@@ -25,7 +25,7 @@
"upload": {
"flash_size": "32MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"maximum_size": 33554432,
"require_upload_port": true,
"speed": 460800
},

View File

@@ -9,6 +9,7 @@
}
],
"dictionaries": ["project-words"],
"caseSensitive": false,
"ignorePaths": [
"node_modules",
"compile_commands.json",
@@ -34,6 +35,10 @@
"sdkconfig.*",
"managed_components/**",
"pnpm-*.yaml",
"vite.config.ts"
"vite.config.ts",
"lib/esp32-psram/**",
"test/test_api/test_api.h",
"lib_standalone/**",
"**/*.js"
]
}

View File

@@ -1,85 +1,209 @@
{
"type": "settings",
"Network": {
"ssid": "my_wifi_ssid",
"bssid": "",
"password": "my_wifi_password",
"hostname": "ems-esp"
"type": "systembackup",
"version": "3.8.2",
"systembackup": [
{
"type": "settings",
"Network": {
"ssid": "",
"bssid": "",
"password": "",
"hostname": "ems-esp",
"static_ip_config": false,
"bandwidth20": false,
"nosleep": true,
"enableMDNS": true,
"enableCORS": false,
"CORSOrigin": "*",
"tx_power": 0
},
"AP": {
"provision_mode": 1,
"ssid": "ems-esp",
"password": "ems-esp-neo",
"channel": 1,
"ssid_hidden": false,
"max_clients": 4,
"local_ip": "192.168.4.1",
"gateway_ip": "192.168.4.1",
"subnet_mask": "255.255.255.0"
},
"MQTT": {
"enableTLS": false,
"rootCA": "",
"enabled": false,
"host": "",
"port": 1883,
"base": "ems-esp",
"username": "",
"password": "",
"client_id": "esp32-b8ffc9ec",
"keep_alive": 60,
"clean_session": false,
"entity_format": 1,
"publish_time_boiler": 10,
"publish_time_thermostat": 10,
"publish_time_solar": 10,
"publish_time_mixer": 10,
"publish_time_water": 10,
"publish_time_other": 60,
"publish_time_sensor": 10,
"publish_time_heartbeat": 60,
"mqtt_qos": 0,
"mqtt_retain": false,
"ha_enabled": false,
"nested_format": 1,
"discovery_prefix": "homeassistant",
"discovery_type": 0,
"ha_number_mode": 0,
"publish_single": false,
"publish_single2cmd": false,
"send_response": false
},
"NTP": {
"enabled": false,
"server": "time.google.com",
"tz_label": "Europe/Amsterdam",
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
},
"Security": {
"jwt_secret": "ems-esp-neo",
"users": [
{
"username": "admin",
"password": "admin",
"admin": true
},
{
"username": "guest",
"password": "guest",
"admin": false
}
]
},
"Settings": {
"version": "3.8.2-dev.22",
"board_profile": "E32V2_2",
"platform": "ESP32",
"locale": "en",
"tx_mode": 5,
"ems_bus_id": 73,
"syslog_enabled": false,
"syslog_level": 3,
"trace_raw": false,
"syslog_mark_interval": 0,
"syslog_host": "",
"syslog_port": 514,
"boiler_heatingoff": false,
"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,
"notoken_api": false,
"readonly_mode": false,
"analog_enabled": true,
"pbutton_gpio": 34,
"solar_maxflow": 30,
"fahrenheit": false,
"bool_format": 1,
"bool_dashboard": 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": false
}
},
"AP": {
"provision_mode": 2,
"ssid": "ems-esp",
"password": "ems-esp-neo",
"channel": 1,
"ssid_hidden": false,
"max_clients": 4,
"local_ip": "192.168.4.1",
"gateway_ip": "192.168.4.1",
"subnet_mask": "255.255.255.0"
{
"type": "schedule",
"Schedule": {
"schedule": []
}
},
"MQTT": {
"enableTLS": false,
"rootCA": "",
"enabled": false,
"host": "127.0.0.1",
"port": 1883,
"base": "ems-esp",
"username": "username",
"password": "password",
"client_id": "ems-esp",
"entity_format": 1,
"publish_time_boiler": 10,
"publish_time_thermostat": 10,
"publish_time_solar": 10,
"publish_time_mixer": 10,
"publish_time_water": 10,
"publish_time_other": 60,
"publish_time_sensor": 10,
"publish_time_heartbeat": 60,
"mqtt_qos": 0,
"mqtt_retain": false,
"ha_enabled": false,
"nested_format": 1,
"discovery_prefix": "homeassistant",
"discovery_type": 0,
"publish_single": false,
"publish_single2cmd": false,
"send_response": false
{
"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": []
}
},
"NTP": {
"enabled": true,
"server": "time.google.com",
"tz_label": "Europe/Amsterdam",
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
{
"type": "entities",
"Entities": {
"entities": []
}
},
"Security": {
"jwt_secret": "ems-esp-neo",
"users": [
{
"type": "modules",
"Modules": {
"modules": []
}
},
{
"type": "nvs",
"nvs": [
{
"username": "admin",
"password": "admin",
"admin": true
},
{
"username": "guest",
"password": "guest",
"admin": false
"type": 1,
"key": "fresh_firmware",
"value": 0
}
]
},
"Settings": {
"board_profile": "S3",
"locale": "en",
"tx_mode": 1,
"ems_bus_id": 11,
"boiler_heatingoff": false,
"hide_led": true,
"telnet_enabled": true,
"notoken_api": false,
"analog_enabled": true,
"fahrenheit": false,
"bool_format": 1,
"bool_dashboard": 1,
"enum_format": 1
}
}
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@ telegram_type_id,name,is_fetched
0x19,UBAMonitorSlow,
0x1A,UBASetPoints,
0x1C,UBAMaintenanceStatus,
0x1E,WM10TempMessage,
0x1E,HydrTemp,
0x23,JunkersSetMixer,fetched
0x27,UBASettingsWW,fetched
0x28,WeatherComp,fetched
@@ -72,11 +72,12 @@ telegram_type_id,name,is_fetched
0xE6,UBAParametersPlus,fetched
0xE9,UBAMonitorWWPlus,
0xEA,UBAParameterWWPlus,fetched
0xEB,PumpKick,fetched
0x0101,ISM1Set,fetched
0x0103,ISM1StatusMessage,fetched
0x0104,ISM2StatusMessage,
0x010C,IPMStatusMessage,
0x011E,JunkersDisp,fetched
0x011E,IPMTempMessage,
0x012E,HPEnergy1,
0x013B,HPEnergy2,
0x0165,JunkersSet,
@@ -111,7 +112,7 @@ telegram_type_id,name,is_fetched
0x02A0,RC300Curves,
0x02A1,RC300Curves,
0x02A2,RC300Curves,
0x02A5,RC300Monitor,fetched
0x02A5,RC300Monitor,
0x02A6,RC300Monitor,
0x02A7,RC300Monitor,
0x02A8,RC300Monitor,
@@ -170,6 +171,7 @@ telegram_type_id,name,is_fetched
0x0468,HPSet,
0x0469,HPSet,
0x046A,HPSet,
0x0470,RC300Summer2,fetched
0x0471,RC300Summer2,
0x0472,RC300Summer2,
0x0473,RC300Summer2,
@@ -197,7 +199,7 @@ telegram_type_id,name,is_fetched
0x04A2,HpInput,fetched
0x04A5,HPFan,fetched
0x04A7,HPPowerLimit,fetched
0x04AA,HPPower2,fetched
0x04AA,HPPower,
0x04AE,HPEnergy,fetched
0x04AF,HPMeters,fetched
0x055C,VentilationSet,fetched
1 telegram_type_id name is_fetched
13 0x19 UBAMonitorSlow
14 0x1A UBASetPoints
15 0x1C UBAMaintenanceStatus
16 0x1E WM10TempMessage HydrTemp
17 0x23 JunkersSetMixer fetched
18 0x27 UBASettingsWW fetched
19 0x28 WeatherComp fetched
72 0xE6 UBAParametersPlus fetched
73 0xE9 UBAMonitorWWPlus
74 0xEA UBAParameterWWPlus fetched
75 0xEB PumpKick fetched
76 0x0101 ISM1Set fetched
77 0x0103 ISM1StatusMessage fetched
78 0x0104 ISM2StatusMessage
79 0x010C IPMStatusMessage
80 0x011E JunkersDisp IPMTempMessage fetched
81 0x012E HPEnergy1
82 0x013B HPEnergy2
83 0x0165 JunkersSet
112 0x02A0 RC300Curves
113 0x02A1 RC300Curves
114 0x02A2 RC300Curves
115 0x02A5 RC300Monitor fetched
116 0x02A6 RC300Monitor
117 0x02A7 RC300Monitor
118 0x02A8 RC300Monitor
171 0x0468 HPSet
172 0x0469 HPSet
173 0x046A HPSet
174 0x0470 RC300Summer2 fetched
175 0x0471 RC300Summer2
176 0x0472 RC300Summer2
177 0x0473 RC300Summer2
199 0x04A2 HpInput fetched
200 0x04A5 HPFan fetched
201 0x04A7 HPPowerLimit fetched
202 0x04AA HPPower2 HPPower fetched
203 0x04AE HPEnergy fetched
204 0x04AF HPMeters fetched
205 0x055C VentilationSet fetched

View File

@@ -1,5 +1,5 @@
{
"adapter": "react",
"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"
}

View File

@@ -1,9 +1,10 @@
// @ts-check
import eslint from '@eslint/js';
import prettierConfig from 'eslint-config-prettier';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default tseslint.config(
export default defineConfig(
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
prettierConfig,

View File

@@ -1,6 +1,6 @@
{
"name": "EMS-ESP",
"version": "3.7.3",
"version": "3.8.2",
"description": "EMS-ESP WebUI",
"homepage": "https://emsesp.org",
"author": "proddy, emsesp.org",
@@ -12,60 +12,60 @@
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"build-hosted": "typesafe-i18n && vite build --mode hosted",
"build-hosted": "typesafe-i18n --no-watch && vite build --mode hosted",
"mock-rest": "bun --watch ../mock-api/restServer.ts",
"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\"",
"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}'",
"lint": "eslint . --fix",
"standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\""
},
"dependencies": {
"@alova/adapter-xhr": "2.2.1",
"@alova/adapter-xhr": "2.3.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.5",
"@mui/material": "^7.3.5",
"@preact/compat": "^18.3.1",
"@mui/icons-material": "^9.0.1",
"@mui/material": "^9.0.1",
"@preact/compat": "^18.3.2",
"@table-library/react-table-library": "4.1.15",
"alova": "3.3.4",
"alova": "3.5.1",
"async-validator": "^4.2.5",
"etag": "^1.8.1",
"formidable": "^3.5.4",
"jwt-decode": "^4.0.0",
"magic-string": "^0.30.21",
"mime-types": "^3.0.1",
"preact": "^10.27.2",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-icons": "^5.5.0",
"react-router": "^7.9.6",
"react-toastify": "^11.0.5",
"typesafe-i18n": "^5.26.2",
"typescript": "^5.9.3"
"mime-types": "^3.0.2",
"preact": "^10.29.1",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-icons": "^5.6.0",
"react-router": "^7.15.0",
"react-toastify": "^11.1.0",
"typesafe-i18n": "^5.27.1",
"typescript": "^6.0.3"
},
"devDependencies": {
"@babel/core": "^7.28.5",
"@eslint/js": "^9.39.1",
"@preact/compat": "^18.3.1",
"@preact/preset-vite": "^2.10.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@babel/core": "^7.29.0",
"@eslint/js": "^10.0.1",
"@preact/compat": "^18.3.2",
"@preact/preset-vite": "^2.10.5",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.7.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"axe-core": "^4.11.0",
"axe-core": "^4.11.4",
"concurrently": "^9.2.1",
"eslint": "^9.39.1",
"eslint": "^10.3.0",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.6.2",
"rollup-plugin-visualizer": "^6.0.5",
"terser": "^5.44.1",
"typescript-eslint": "^8.46.4",
"vite": "^7.2.2",
"prettier": "^3.8.3",
"rollup-plugin-visualizer": "^7.0.1",
"terser": "^5.47.1",
"typescript-eslint": "^8.59.3",
"vite": "^8.0.12",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4"
"vite-tsconfig-paths": "^6.1.1"
},
"packageManager": "pnpm@10.22.0+sha512.bf049efe995b28f527fd2b41ae0474ce29186f7edcb3bf545087bd61fbbebb2bf75362d1307fda09c2d288e1e499787ac12d4fcb617a974718a6051f2eee741c"
"packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800"
}

2616
interface/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,20 +24,26 @@ let bundleStats = {
other: { count: 0, uncompressed: 0, compressed: 0 }
};
const generateWWWClass =
() => `typedef std::function<void(const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash)> RouteRegistrationHandler;
// Bundle Statistics:
// AsyncWebHandler that performs the lookup.
const generateWWWClass = () => `// Bundle Statistics:
// - 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
// - 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()}
class WWWData {
${INDENT}public:
${INDENT.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
${fileInfo.map((f) => `${INDENT.repeat(3)}handler("${f.uri}", "${f.mimeType}", ${f.variable}, ${f.size}, ${f.hash});`).join('\n')}
${INDENT.repeat(2)}}
struct WWWAsset {
${INDENT}const char * uri;
${INDENT}const char * contentType;
${INDENT}const uint8_t * content;
${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 = []) => {
@@ -72,6 +78,7 @@ const writeFile = (relativeFilePath, buffer) => {
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
// const hash = crypto.createHash('sha256').update(zipBuffer).digest('hex');
const hash = etag(zipBuffer); // use smaller md5 instead of sha256
const rawHash = hash.replace(/^"|"$/g, '');
zipBuffer.forEach((b) => {
if (!(size % bytesPerLine)) {
@@ -94,7 +101,8 @@ const writeFile = (relativeFilePath, buffer) => {
mimeType,
variable,
size,
hash
hash,
rawHash
});
totalSize += size;

View File

@@ -8,7 +8,6 @@ import type { Locales } from 'i18n/i18n-types';
import { loadLocaleAsync } from 'i18n/i18n-util.async';
import { detectLocale, navigatorDetector } from 'typesafe-i18n/detectors';
// Memoize available locales to prevent recreation on every render
const AVAILABLE_LOCALES = [
'de',
'en',

View File

@@ -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 { toast } from 'react-toastify';
import {
LoadingSpinner,
RequireAuthenticated,
RequireUnauthenticated
} from 'components';
import AuthenticatedRouting from 'AuthenticatedRouting';
import SignIn from 'SignIn';
import { RequireAuthenticated, RequireUnauthenticated } from 'components';
import { Authentication, AuthenticationContext } from 'contexts/authentication';
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 {
readonly message: string;
readonly signOut?: boolean;
@@ -45,34 +39,32 @@ const AppRouting: FC = memo(() => {
return (
<Authentication>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route
path="/unauthorized"
element={<RootRedirect message={LL.PLEASE_SIGNIN()} signOut />}
/>
<Route
path="/fileUpdated"
element={<RootRedirect message={LL.UPLOAD_SUCCESSFUL()} />}
/>
<Route
path="/"
element={
<RequireUnauthenticated>
<SignIn />
</RequireUnauthenticated>
}
/>
<Route
path="/*"
element={
<RequireAuthenticated>
<AuthenticatedRouting />
</RequireAuthenticated>
}
/>
</Routes>
</Suspense>
<Routes>
<Route
path="/unauthorized"
element={<RootRedirect message={LL.PLEASE_SIGNIN()} signOut />}
/>
<Route
path="/fileUpdated"
element={<RootRedirect message={LL.UPLOAD_SUCCESSFUL()} />}
/>
<Route
path="/"
element={
<RequireUnauthenticated>
<SignIn />
</RequireUnauthenticated>
}
/>
<Route
path="/*"
element={
<RequireAuthenticated>
<AuthenticatedRouting />
</RequireAuthenticated>
}
/>
</Routes>
</Authentication>
);
});

View File

@@ -1,86 +1,77 @@
import { Suspense, lazy, memo, useContext } from 'react';
import { memo, useContext } from 'react';
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';
// 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 { me } = useContext(AuthenticatedContext);
return (
<Layout>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard/*" element={<Dashboard />} />
<Route path="/devices/*" element={<Devices />} />
<Route path="/sensors/*" element={<Sensors />} />
<Route path="/help/*" element={<Help />} />
<Route path="/user/*" element={<UserProfile />} />
<Routes>
<Route path="/dashboard/*" element={<Dashboard />} />
<Route path="/devices/*" element={<Devices />} />
<Route path="/sensors/*" element={<Sensors />} />
<Route path="/help/*" element={<Help />} />
<Route path="/user/*" element={<UserProfile />} />
<Route path="/status/*" element={<Status />} />
<Route path="/status/hardwarestatus/*" element={<HardwareStatus />} />
<Route path="/status/activity" element={<Activity />} />
<Route path="/status/log" element={<SystemLog />} />
<Route path="/status/mqtt" element={<MqttStatus />} />
<Route path="/status/ntp" element={<NTPStatus />} />
<Route path="/status/ap" element={<APStatus />} />
<Route path="/status/network" element={<NetworkStatus />} />
<Route path="/status/version" element={<Version />} />
<Route path="/status/*" element={<Status />} />
<Route path="/status/hardwarestatus/*" element={<HardwareStatus />} />
<Route path="/status/activity" element={<Activity />} />
<Route path="/status/log" element={<SystemLog />} />
<Route path="/status/mqtt" element={<MqttStatus />} />
<Route path="/status/ntp" element={<NTPStatus />} />
<Route path="/status/ap" element={<APStatus />} />
<Route path="/status/network" element={<NetworkStatus />} />
<Route path="/status/version" element={<Version />} />
{me.admin && (
<>
<Route path="/settings" element={<Settings />} />
<Route
path="/settings/application"
element={<ApplicationSettings />}
/>
<Route path="/settings/mqtt" element={<MqttSettings />} />
<Route path="/settings/ntp" element={<NTPSettings />} />
<Route path="/settings/ap" element={<APSettings />} />
<Route path="/settings/modules" element={<Modules />} />
<Route path="/settings/downloadUpload" element={<DownloadUpload />} />
{me.admin && (
<>
<Route path="/settings" element={<Settings />} />
<Route path="/settings/application" element={<ApplicationSettings />} />
<Route path="/settings/mqtt" element={<MqttSettings />} />
<Route path="/settings/ntp" element={<NTPSettings />} />
<Route path="/settings/ap" element={<APSettings />} />
<Route path="/settings/modules" element={<Modules />} />
<Route path="/settings/downloadUpload" element={<DownloadUpload />} />
<Route path="/settings/network/*" element={<Network />} />
<Route path="/settings/security/*" element={<Security />} />
<Route path="/settings/network/*" element={<Network />} />
<Route path="/settings/security/*" element={<Security />} />
<Route path="/customizations" element={<Customizations />} />
<Route path="/scheduler" element={<Scheduler />} />
<Route path="/customentities" element={<CustomEntities />} />
</>
)}
<Route path="/customizations" element={<Customizations />} />
<Route path="/scheduler" element={<Scheduler />} />
<Route path="/customentities" element={<CustomEntities />} />
</>
)}
<Route path="/*" element={<Navigate to="/" />} />
</Routes>
</Suspense>
<Route path="/*" element={<Navigate to="/" />} />
</Routes>
</Layout>
);
});

View File

@@ -11,17 +11,15 @@ import { createTheme } from '@mui/material/styles';
import type { RequiredChildrenProps } from 'utils';
// Memoize dialog style to prevent recreation
export const dialogStyle = {
'& .MuiDialog-paper': {
borderRadius: '8px',
borderColor: '#565656',
borderStyle: 'solid',
borderWidth: '1px'
borderWidth: '2px'
}
} as const;
// Memoize theme creation to prevent recreation
const theme = responsiveFontSizes(
createTheme({
typography: {

View File

@@ -1,8 +1,9 @@
import { memo, useCallback, useContext, useMemo, useState } from 'react';
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify';
import ForwardIcon from '@mui/icons-material/Forward';
import { Box, Button, Paper, Typography } from '@mui/material';
import type { Theme } from '@mui/material/styles';
import * as AuthenticationApi from 'components/routing/authentication';
import { useRequest } from 'alova/client';
@@ -17,7 +18,7 @@ import { PROJECT_NAME } from 'env';
import { useI18nContext } from 'i18n/i18n-react';
import type { SignInRequest } from 'types';
import { onEnterCallback, updateValue } from 'utils';
import { SIGN_IN_REQUEST_VALIDATOR, validate } from 'validators';
import { SIGN_IN_REQUEST_VALIDATOR, ValidationError, validate } from 'validators';
const SignIn = memo(() => {
const authenticationContext = useContext(AuthenticationContext);
@@ -36,7 +37,7 @@ const SignIn = memo(() => {
{
immediate: false
}
).onSuccess((response) => {
).onSuccess((response: { data: { access_token: string } }) => {
if (response.data) {
authenticationContext.signIn(response.data.access_token);
}
@@ -73,23 +74,33 @@ const SignIn = memo(() => {
await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest);
await signIn();
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
setProcessing(false);
}
}, [signInRequest, signIn, LL]);
// Memoize callback to prevent recreation on every render
const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]);
// get rid of scrollbar
useEffect(() => {
const originalOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => {
document.body.style.overflow = originalOverflow;
};
}, []);
return (
<Box
display="flex"
height="100vh"
margin="auto"
padding={2}
justifyContent="center"
flexDirection="column"
maxWidth={(theme) => theme.breakpoints.values.sm}
sx={(theme: Theme) => ({
display: 'flex',
height: '100vh',
margin: 'auto',
padding: 2,
justifyContent: 'center',
flexDirection: 'column',
maxWidth: theme.breakpoints.values.sm
})}
>
<Paper
sx={(theme) => ({
@@ -102,23 +113,29 @@ const SignIn = memo(() => {
width: '100%'
})}
>
<Typography variant="h4">{PROJECT_NAME}</Typography>
<Typography sx={{ mb: 1 }} variant="h4">
{PROJECT_NAME}
</Typography>
<LanguageSelector />
<Box display="flex" flexDirection="column" alignItems="center">
<Box
sx={{
mt: 1,
display: 'flex',
flexDirection: 'column',
gap: 1,
alignItems: 'center'
}}
>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
disabled={processing}
sx={{
width: 240
width: '32ch'
}}
name="username"
label={LL.USERNAME(0)}
value={signInRequest.username}
onChange={updateLoginRequestValue}
margin="normal"
variant="outlined"
slotProps={{
input: {
autoCapitalize: 'none',
@@ -130,14 +147,13 @@ const SignIn = memo(() => {
fieldErrors={fieldErrors || {}}
disabled={processing}
sx={{
width: 240
width: '32ch'
}}
name="password"
label={LL.PASSWORD()}
value={signInRequest.password}
onChange={updateLoginRequestValue}
onKeyDown={submitOnEnter}
variant="outlined"
/>
</Box>

View File

@@ -310,13 +310,15 @@ const CustomEntities = () => {
/>
)}
</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>
{ei.ram === 1 ? '' : showHex(ei.device_id as number, 2)}
</Cell>
<Cell>{ei.ram === 1 ? '' : showHex(ei.type_id as number, 3)}</Cell>
<Cell>{ei.ram === 1 ? '' : ei.offset}</Cell>
<Cell>
{ei.ram === 1 ? 'RAM' : DeviceValueTypeNames[ei.value_type]}
{ei.ram === 1
? 'RAM'
: ei.ram === 2
? 'NVS'
: DeviceValueTypeNames[ei.value_type]}
</Cell>
<Cell>{formatValue(ei.value, ei.uom)}</Cell>
</Row>
@@ -341,9 +343,9 @@ const CustomEntities = () => {
return (
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main">
<Typography variant="body1">{LL.ENTITIES_HELP_1()}.</Typography>
</Box>
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
{LL.ENTITIES_HELP_1()}.
</Typography>
{renderEntity()}
@@ -359,8 +361,8 @@ const CustomEntities = () => {
/>
)}
<Box mt={2} display="flex" flexWrap="wrap">
<Box flexGrow={1}>
<Box sx={{ mt: 2, display: 'flex', flexWrap: 'wrap' }}>
<Box sx={{ flexGrow: 1 }}>
{numChanges > 0 && (
<ButtonRow>
<Button
@@ -382,7 +384,7 @@ const CustomEntities = () => {
</ButtonRow>
)}
</Box>
<Box flexWrap="nowrap" whiteSpace="nowrap">
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<Button
startIcon={<AddIcon />}
variant="outlined"

View File

@@ -7,7 +7,7 @@ import DoneIcon from '@mui/icons-material/Done';
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import {
Box,
Button,
@@ -28,7 +28,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { BlockFormControlLabel, ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
import { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types';
import type { EntityItem } from './types';
@@ -136,7 +136,7 @@ const CustomEntitiesDialog = ({
}
onSave(processedItem);
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}, [validator, editItem, onSave]);
@@ -178,7 +178,7 @@ const CustomEntitiesDialog = ({
onChange={updateFormValue}
/>
</Grid>
<Grid mt={3}>
<Grid sx={{ mt: 3 }}>
<BlockFormControlLabel
control={
<Checkbox
@@ -205,16 +205,17 @@ const CustomEntitiesDialog = ({
>
<MenuItem value={0}>EMS-{LL.VALUE(1)}</MenuItem>
<MenuItem value={1}>RAM-{LL.VALUE(1)}</MenuItem>
<MenuItem value={2}>NVS-{LL.VALUE(1)}</MenuItem>
</TextField>
</Grid>
{editItem.ram === 1 && (
{editItem.ram > 0 && (
<>
<Grid>
<TextField
name="value"
label={LL.DEFAULT(0) + ' ' + LL.VALUE(0)}
type="string"
value={editItem.value as string}
value={editItem.value}
variant="outlined"
onChange={updateFormValue}
fullWidth
@@ -237,7 +238,7 @@ const CustomEntitiesDialog = ({
)}
{editItem.ram === 0 && (
<>
<Grid mt={3}>
<Grid sx={{ mt: 3 }}>
<BlockFormControlLabel
control={
<Checkbox
@@ -259,7 +260,7 @@ const CustomEntitiesDialog = ({
margin="normal"
sx={{ width: '11ch' }}
type="string"
value={editItem.device_id as string}
value={editItem.device_id}
onChange={updateFormValue}
slotProps={{
input: {
@@ -279,7 +280,7 @@ const CustomEntitiesDialog = ({
margin="normal"
sx={{ width: '11ch' }}
type="string"
value={editItem.type_id as string}
value={editItem.type_id}
onChange={updateFormValue}
slotProps={{
input: {
@@ -380,7 +381,7 @@ const CustomEntitiesDialog = ({
fieldErrors={fieldErrors || {}}
name="factor"
label={LL.BITMASK()}
value={editItem.factor as string}
value={editItem.factor}
sx={{ width: '11ch' }}
variant="outlined"
onChange={updateFormValue}
@@ -403,7 +404,7 @@ const CustomEntitiesDialog = ({
</DialogContent>
<DialogActions>
{!creating && (
<Box flexGrow={1}>
<Box sx={{ flexGrow: 1 }}>
<Button
startIcon={<RemoveIcon />}
variant="outlined"

View File

@@ -111,13 +111,14 @@ const Customizations = () => {
const [selectedDeviceTypeNameURL, setSelectedDeviceTypeNameURL] =
useState<string>(''); // needed for API URL
const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
const [selectedDeviceBrand, setSelectedDeviceBrand] = useState<string>('');
const { send: sendResetCustomizations } = useRequest(resetCustomizations(), {
immediate: false
});
const { send: sendDeviceName } = useRequest(
(data: { id: number; name: string }) => writeDeviceName(data),
(data: { id: number; name: string; brand: string }) => writeDeviceName(data),
{
immediate: false
}
@@ -267,6 +268,7 @@ const Customizations = () => {
if (device) {
setSelectedDeviceTypeNameURL(device.url || '');
setSelectedDeviceName(device.n);
setSelectedDeviceBrand(device.b);
}
setNumChanges(0);
setRestartNeeded(false);
@@ -285,13 +287,17 @@ const Customizations = () => {
return value as string;
}
const isCommand = useCallback((de: DeviceEntity) => {
return de.n && de.n[0] === '!';
}, []);
const formatName = useCallback(
(de: DeviceEntity, withShortname: boolean) => {
let name: string;
if (de.n && de.n[0] === '!') {
if (isCommand(de)) {
name = de.t
? `${LL.COMMAND(1)}: ${de.t} ${de.n.slice(1)}`
: `${LL.COMMAND(1)}: ${de.n.slice(1)}`;
? `${LL.COMMAND(1)}: ${de.t} ${de.n?.slice(1)}`
: `${LL.COMMAND(1)}: ${de.n?.slice(1)}`;
} else if (de.cn && de.cn !== '') {
name = de.t ? `${de.t} ${de.cn}` : de.cn;
} else {
@@ -438,7 +444,11 @@ const Customizations = () => {
}, [devices, deviceEntities, selectedDevice, sendCustomizationEntities, LL]);
const renameDevice = useCallback(async () => {
await sendDeviceName({ id: selectedDevice, name: selectedDeviceName })
await sendDeviceName({
id: selectedDevice,
name: selectedDeviceName,
brand: selectedDeviceBrand
})
.then(() => {
toast.success(LL.UPDATED_OF(LL.NAME(1)));
})
@@ -449,24 +459,42 @@ const Customizations = () => {
setRename(false);
await fetchCoreData();
});
}, [selectedDevice, selectedDeviceName, sendDeviceName, LL, fetchCoreData]);
}, [
selectedDevice,
selectedDeviceName,
selectedDeviceBrand,
sendDeviceName,
LL,
fetchCoreData
]);
const renderDeviceList = () => (
<>
<Box mb={1} color="warning.main">
<Typography variant="body1">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography>
</Box>
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
<Typography sx={{ mb: 1 }} color="warning" variant="body1">
{LL.CUSTOMIZATIONS_HELP_1()}.
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2 }}>
{rename ? (
<TextField
name="device"
label={LL.EMS_DEVICE()}
fullWidth
variant="outlined"
value={selectedDeviceName}
onChange={(e) => setSelectedDeviceName(e.target.value)}
margin="normal"
/>
<>
<TextField
name="device"
label={LL.EMS_DEVICE()}
style={{ minWidth: '48%' }}
variant="outlined"
value={selectedDeviceName}
onChange={(e) => setSelectedDeviceName(e.target.value)}
margin="normal"
/>
<TextField
name="brand"
label={LL.BRAND()}
style={{ minWidth: '48%' }}
variant="outlined"
value={selectedDeviceBrand}
onChange={(e) => setSelectedDeviceBrand(e.target.value)}
margin="normal"
/>
</>
) : (
<TextField
name="device"
@@ -542,27 +570,22 @@ const Customizations = () => {
const renderDeviceData = () => {
return (
<>
<Box color="warning.main">
<Typography variant="body2" mt={1}>
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
&nbsp;&nbsp;
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
&nbsp;&nbsp;
<OptionIcon type="api_mqtt_exclude" isSet={true} />=
{LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp;
<OptionIcon type="web_exclude" isSet={true} />=
{LL.CUSTOMIZATIONS_HELP_5()}&nbsp;&nbsp;
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
</Typography>
</Box>
<Typography sx={{ mt: 1, mb: 1 }} color="warning" variant="body2">
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
&nbsp;&nbsp;
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
&nbsp;&nbsp;
<OptionIcon type="api_mqtt_exclude" isSet={true} />=
{LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp;
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
&nbsp;&nbsp;
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
</Typography>
<Grid
container
mb={1}
mt={0}
spacing={2}
direction="row"
justifyContent="flex-start"
alignItems="center"
sx={{ mb: 1, mt: 0, justifyContent: 'flex-start', alignItems: 'center' }}
>
<Grid>
<TextField
@@ -666,14 +689,27 @@ const Customizations = () => {
<EntityMaskToggle onUpdate={updateDeviceEntity} de={de} />
</Cell>
<Cell>
{formatName(de, false)}&nbsp;(
<Link
target="_blank"
href={APIURL + selectedDeviceTypeNameURL + '/' + de.id}
<span
style={{
color:
de.v === undefined && !isCommand(de) ? 'grey' : 'inherit'
}}
>
{de.id}
</Link>
)
{formatName(de, false)}&nbsp;(
<Link
style={{
color:
de.v === undefined && !isCommand(de)
? 'grey'
: 'primary'
}}
target="_blank"
href={APIURL + selectedDeviceTypeNameURL + '/' + de.id}
>
{de.id}
</Link>
)
</span>
</Cell>
<Cell>
{!(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.mi)}
@@ -726,8 +762,9 @@ const Customizations = () => {
{devices && renderDeviceList()}
{selectedDevice !== -1 && !rename && renderDeviceData()}
{restartNeeded ? (
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT(0)}>
<MessageBox level="warning" message={LL.RESTART_TEXT(0)}>
<Button
sx={{ ml: 2 }}
startIcon={<PowerSettingsNewIcon />}
variant="contained"
color="error"
@@ -737,8 +774,8 @@ const Customizations = () => {
</Button>
</MessageBox>
) : (
<Box display="flex" flexWrap="wrap">
<Box flexGrow={1}>
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
<Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && (
<ButtonRow>
<Button
@@ -770,10 +807,12 @@ const Customizations = () => {
</>
);
return (
return restarting ? (
<SystemMonitor />
) : (
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{restarting ? <SystemMonitor /> : renderContent()}
{renderContent()}
{selectedDeviceEntity && (
<SettingsCustomizationsDialog
open={dialogOpen}

View File

@@ -37,7 +37,7 @@ interface LabelValueProps {
const LabelValue = memo(({ label, value }: LabelValueProps) => (
<Grid container direction="row">
<Typography variant="body2" color="warning.main">
<Typography variant="body2" color="warning">
{label}:&nbsp;
</Typography>
<Typography variant="body2">{value}</Typography>
@@ -131,7 +131,7 @@ const CustomizationsDialog = ({
/>
<LabelValue label={LL.WRITEABLE()} value={writeableIcon} />
<Box mt={1} mb={2}>
<Box sx={{ mt: 1, mb: 2 }}>
<EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} />
</Box>
@@ -172,7 +172,7 @@ const CustomizationsDialog = ({
</Grid>
{error && (
<Typography variant="body2" color="error" mt={2}>
<Typography sx={{ mt: 2 }} variant="body2" color="error">
Error: Check min and max values
</Typography>
)}

View File

@@ -6,7 +6,7 @@ import { toast } from 'react-toastify';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import EditIcon from '@mui/icons-material/Edit';
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 UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
import {
@@ -262,12 +262,8 @@ const Dashboard = memo(() => {
return (
<>
{!data.connected && (
<MessageBox mb={2} level="error" message={LL.EMS_BUS_WARNING()} />
)}
{data.connected && data.nodes.length > 0 && !hasFavEntities && (
<MessageBox mb={2} level="warning">
<MessageBox sx={{ mb: 2 }} level="warning">
<Typography>
{LL.NO_DATA_1()}&nbsp;
<Link to="/customizations" style={{ color: 'white' }}>
@@ -284,10 +280,12 @@ const Dashboard = memo(() => {
)}
<Box
display="flex"
justifyContent="flex-end"
flexWrap="nowrap"
whiteSpace="nowrap"
sx={{
display: 'flex',
justifyContent: 'flex-end',
flexWrap: 'nowrap',
whiteSpace: 'nowrap'
}}
>
<ToggleButtonGroup
size="small"
@@ -310,7 +308,7 @@ const Dashboard = memo(() => {
</Box>
{data.nodes.length > 0 ? (
<Box mt={1} justifyContent="center" flexDirection="column">
<Box sx={{ mt: 1, justifyContent: 'center', flexDirection: 'column' }}>
<IconContext.Provider
value={{
color: 'lightblue',
@@ -377,13 +375,8 @@ const Dashboard = memo(() => {
</IconContext.Provider>
</Box>
) : (
<Box
display="flex"
// justifyContent="flex-end"
// flexWrap="nowrap"
// whiteSpace="nowrap"
>
<Typography mt={1} color="warning.main" variant="body1">
<Box sx={{ display: 'flex' }}>
<Typography sx={{ mt: 1 }} color="warning" variant="body1">
no data
</Typography>
<Tooltip title={LL.DASHBOARD_1()}>

View File

@@ -1,4 +1,5 @@
import { memo } from 'react';
import type { IconType } from 'react-icons';
import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai';
import { CgSmartHomeBoiler } from 'react-icons/cg';
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 { VscVmConnect } from 'react-icons/vsc';
import type { SvgIconProps } from '@mui/material';
import { DeviceType } from './types';
const deviceIconLookup: Record<
DeviceType,
React.ComponentType<SvgIconProps> | null
> = {
const deviceIconLookup: Record<DeviceType, IconType | null> = {
[DeviceType.TEMPERATURESENSOR]: TiThermometer,
[DeviceType.ANALOGSENSOR]: PiGauge,
[DeviceType.BOILER]: CgSmartHomeBoiler,

View File

@@ -8,7 +8,7 @@ import {
useState
} from 'react';
import { IconContext } from 'react-icons';
import { useNavigate } from 'react-router';
import { Link, useNavigate } from 'react-router';
import { toast } from 'react-toastify';
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
@@ -533,19 +533,27 @@ const Devices = memo(() => {
const renderCoreData = () => (
<>
<Box justifyContent="center" flexDirection="column">
<IconContext.Provider
value={{
color: 'lightblue',
size: '18',
style: { verticalAlign: 'middle' }
}}
>
{!coreData.connected && (
<MessageBox my={2} level="error" message={LL.EMS_BUS_WARNING()} />
)}
{coreData.connected && (
{!coreData.connected ? (
<MessageBox level="error" message={LL.EMS_BUS_WARNING() + '.'}>
&nbsp;(
<Link
target="_blank"
to="https://docs.emsesp.org/Troubleshooting#ems-bus-is-not-connecting"
style={{ color: 'white' }}
>
{LL.ONLINE_HELP()}
</Link>
)
</MessageBox>
) : (
<Box sx={{ justifyContent: 'center', flexDirection: 'column' }}>
<IconContext.Provider
value={{
color: 'lightblue',
size: '18',
style: { verticalAlign: 'middle' }
}}
>
<Table
data={{ nodes: [...coreData.devices] }}
select={device_select}
@@ -581,9 +589,9 @@ const Devices = memo(() => {
</>
)}
</Table>
)}
</IconContext.Provider>
</Box>
</IconContext.Provider>
</Box>
)}
</>
);
@@ -662,12 +670,12 @@ const Devices = memo(() => {
}}
>
<Box sx={{ p: 1 }}>
<Grid container justifyContent="space-between">
<Typography noWrap variant="subtitle1" color="warning.main">
<Grid container sx={{ justifyContent: 'space-between' }}>
<Typography noWrap variant="subtitle1" color="warning">
{deviceInfo.n}&nbsp;(
{deviceInfo.tn})
</Typography>
<Grid justifyContent="flex-end">
<Grid sx={{ justifyContent: 'flex-end' }}>
<ButtonTooltip title={LL.CLOSE()}>
<IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}>
<HighlightOffIcon color="primary" sx={{ fontSize: 18 }} />

View File

@@ -24,7 +24,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
import { DeviceValueUOM, DeviceValueUOM_s } from './types';
import type { DeviceValue } from './types';
@@ -67,7 +67,7 @@ const DevicesDialog = ({
await validate(validator, editItem);
onSave(editItem);
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}, [validator, editItem, onSave]);
@@ -128,9 +128,9 @@ const DevicesDialog = ({
<Dialog sx={dialogStyle} open={open} onClose={onClose}>
<DialogTitle>{dialogTitle}</DialogTitle>
<DialogContent dividers>
<Box color="warning.main" mb={2}>
<Typography variant="body2">{editItem.id.slice(2)}</Typography>
</Box>
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
{editItem.id.slice(2)}
</Typography>
<Grid container>
<Grid size={12}>
{editItem.l ? (

View File

@@ -11,6 +11,7 @@ import {
Box,
Button,
Divider,
Grid,
Link,
List,
ListItem,
@@ -42,8 +43,7 @@ interface CustomSupport {
html: string | null;
}
// Constants moved outside component to prevent recreation
const DEFAULT_IMAGE_URL = 'https://docs.emsesp.org/_media/images/installer.jpeg';
const DEFAULT_IMAGE_URL = 'https://emsesp.org/media/images/installer.jpeg';
const SUPPORT_BOX_STYLES: SxProps<Theme> = {
borderRadius: 3,
@@ -72,7 +72,6 @@ const HelpComponent = () => {
});
const [imgError, setImgError] = useState<boolean>(false);
// Memoize the request method to prevent re-creation on every render
const getCustomSupportMethod = useMemo(
() => callAction({ action: 'getCustomSupport' }),
[]
@@ -116,12 +115,12 @@ const HelpComponent = () => {
const helpLinks: HelpLink[] = useMemo(
() => [
{
href: 'https://docs.emsesp.org',
href: 'https://emsesp.org',
icon: <MenuBookIcon />,
label: () => LL.HELP_INFORMATION_1()
},
{
href: 'https://discord.gg/3J3GgnzpyT',
href: 'https://discord.gg/GP9DPSgeJq',
icon: <CommentIcon />,
label: () => LL.HELP_INFORMATION_2()
},
@@ -147,11 +146,9 @@ const HelpComponent = () => {
<SectionContent>
{customSupport.html && (
<Stack
padding={1}
mb={2}
direction="row"
divider={<Divider orientation="vertical" flexItem />}
sx={SUPPORT_BOX_STYLES}
sx={{ padding: 1, mb: 2, ...SUPPORT_BOX_STYLES }}
>
<Typography variant="subtitle1">
<div dangerouslySetInnerHTML={{ __html: customSupport.html }} />
@@ -186,9 +183,9 @@ const HelpComponent = () => {
</List>
)}
<Box p={2} color="warning.main">
<Typography mb={1} variant="body1">
{LL.HELP_INFORMATION_4()}.
<Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
<Typography sx={{ mb: 1 }} color="warning" variant="body1">
{LL.HELP_INFORMATION_4()}:
</Typography>
<Button
startIcon={<DownloadIcon />}
@@ -198,11 +195,11 @@ const HelpComponent = () => {
>
{LL.SUPPORT_INFORMATION(0)}
</Button>
</Box>
</Grid>
<Divider sx={{ mt: 4 }} />
<Typography color="white" variant="subtitle1" align="center" mt={1}>
<Typography color="white" variant="subtitle1" align="center" sx={{ mt: 1 }}>
&copy;&nbsp;
<Link
target="_blank"

View File

@@ -186,9 +186,9 @@ const Modules = () => {
return (
<>
<Box mb={2} color="warning.main">
<Typography variant="body1">{LL.MODULES_DESCRIPTION()}.</Typography>
</Box>
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
{LL.MODULES_DESCRIPTION()}.
</Typography>
<Table
data={{ nodes: modules }}
theme={modules_theme}
@@ -236,8 +236,8 @@ const Modules = () => {
)}
</Table>
<Box mt={1} display="flex" flexWrap="wrap">
<Box flexGrow={1}>
<Box sx={{ mt: 1, display: 'flex', flexWrap: 'wrap' }}>
<Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && (
<ButtonRow>
<Button

View File

@@ -79,7 +79,7 @@ const ModulesDialog = ({
label="Enabled"
/>
</Grid>
<Box mt={2} mb={1}>
<Box sx={{ mt: 2, mb: 1 }}>
<TextField
name="license"
label="License Key"

View File

@@ -2,12 +2,12 @@ import { memo } from 'react';
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
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 EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
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 VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import type { SvgIconProps } from '@mui/material';

View File

@@ -41,6 +41,7 @@ const MIN_ID = -100;
const MAX_ID = 100;
const ICON_SIZE = 16;
const SCHEDULE_FLAG_THRESHOLD = 127;
const FLAG_ALL_DAYS = 127;
const REFERENCE_YEAR = 2017;
const REFERENCE_MONTH = '01';
const LOG_2 = Math.log(2);
@@ -51,7 +52,7 @@ const WEEK_DAYS = [1, 2, 3, 4, 5, 6, 7] as const;
const DEFAULT_SCHEDULE_ITEM: Omit<ScheduleItem, 'id' | 'o_id'> = {
active: false,
deleted: false,
flags: ScheduleFlag.SCHEDULE_DAY,
flags: FLAG_ALL_DAYS,
time: '',
cmd: '',
value: '',
@@ -357,9 +358,9 @@ const Scheduler = () => {
return (
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main">
<Typography variant="body1">{LL.SCHEDULER_HELP_1()}.</Typography>
</Box>
<Typography sx={{ mb: 2 }} color="warning" variant="body1">
{LL.SCHEDULER_HELP_1()}.
</Typography>
{renderSchedule()}
{selectedScheduleItem && (
@@ -374,8 +375,8 @@ const Scheduler = () => {
/>
)}
<Box display="flex" flexWrap="wrap">
<Box flexGrow={1}>
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
<Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && (
<ButtonRow>
<Button
@@ -397,7 +398,7 @@ const Scheduler = () => {
</ButtonRow>
)}
</Box>
<Box flexWrap="nowrap" whiteSpace="nowrap">
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow>
<Button
startIcon={<AddIcon />}

View File

@@ -4,7 +4,7 @@ import AddIcon from '@mui/icons-material/Add';
import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import {
Box,
Button,
@@ -26,14 +26,15 @@ import type { ValidateFieldsError } from 'async-validator';
import { BlockFormControlLabel, ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { updateValue } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
import { ScheduleFlag } from './types';
import type { ScheduleItem } from './types';
// Constants
const FLAG_MASK_127 = 127;
const SCHEDULE_TYPE_THRESHOLD = 128;
const SCHEDULE_TYPE_THRESHOLD = 127;
const FLAG_ALL_DAYS = 127;
const DEFAULT_TIME = '00:00';
const TYPOGRAPHY_FONT_SIZE = 10;
@@ -104,7 +105,7 @@ const SchedulerDialog = ({
// 130 is on condition
// 132 is immediate
setScheduleType(
selectedItem.flags < SCHEDULE_TYPE_THRESHOLD
selectedItem.flags <= SCHEDULE_TYPE_THRESHOLD
? ScheduleFlag.SCHEDULE_DAY
: selectedItem.flags
);
@@ -119,7 +120,7 @@ const SchedulerDialog = ({
await validate(validator, itemToSave);
onSave(itemToSave);
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
},
[validator, onSave]
@@ -181,7 +182,7 @@ const SchedulerDialog = ({
setScheduleType(flag);
// wipe the time field when changing the schedule type
// set the flags based on type
const newFlags = flag === ScheduleFlag.SCHEDULE_DAY ? 0 : flag;
const newFlags = flag === ScheduleFlag.SCHEDULE_DAY ? FLAG_ALL_DAYS : flag;
setEditItem((prev) => ({ ...prev, time: '', flags: newFlags }));
}
},
@@ -190,7 +191,8 @@ const SchedulerDialog = ({
const handleDOWChange = useCallback(
(_event: React.SyntheticEvent<HTMLElement>, flags: string[]) => {
const newFlags = getFlagDOWnumber(flags);
const newFlags =
getFlagDOWnumber(flags) === 0 ? FLAG_ALL_DAYS : getFlagDOWnumber(flags);
setEditItem((prev) => ({ ...prev, flags: newFlags }));
},
[getFlagDOWnumber]
@@ -336,11 +338,13 @@ const SchedulerDialog = ({
onChange={updateFormValue}
/>
{isTimerSchedule && (
<Box color="warning.main" ml={2} mt={4}>
<Typography variant="body2">
{LL.SCHEDULER_HELP_2()}
</Typography>
</Box>
<Typography
sx={{ ml: 2, mt: 4 }}
color="warning"
variant="body2"
>
{LL.SCHEDULER_HELP_2()}
</Typography>
)}
</>
) : (
@@ -389,7 +393,7 @@ const SchedulerDialog = ({
<DialogActions>
{!creating && (
<Box flexGrow={1}>
<Box sx={{ flexGrow: 1 }}>
<Button
startIcon={<RemoveIcon />}
variant="outlined"

View File

@@ -1,4 +1,4 @@
import { useCallback, useContext, useMemo, useState } from 'react';
import { useCallback, useContext, useMemo, useRef, useState } from 'react';
import { toast } from 'react-toastify';
import AddCircleOutlineOutlinedIcon from '@mui/icons-material/AddCircleOutlineOutlined';
@@ -54,7 +54,6 @@ const MS_PER_SECOND = 1000;
const MS_PER_MINUTE = 60 * MS_PER_SECOND;
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
const MS_PER_DAY = 24 * MS_PER_HOUR;
const DEFAULT_GPIO = 99; // not set
const MIN_TEMP_ID = -100;
const MAX_TEMP_ID = 100;
const GPIO_25 = 25;
@@ -128,15 +127,21 @@ const Sensors = () => {
const [temperatureDialogOpen, setTemperatureDialogOpen] = useState<boolean>(false);
const [analogDialogOpen, setAnalogDialogOpen] = useState<boolean>(false);
const [creating, setCreating] = useState<boolean>(false);
const firstAvailableGPIO = useRef<number>(undefined);
const { data: sensorData, send: fetchSensorData } = useRequest(readSensorData, {
initialData: {
ts: [],
as: [],
analog_enabled: false,
valid_gpio_list: [],
available_gpios: [] as number[],
platform: 'ESP32'
}
}).onSuccess((event) => {
// store the first available GPIO in a ref
if (event.data.available_gpios.length > 0) {
firstAvailableGPIO.current = event.data.available_gpios[0];
}
});
const { send: sendTemperatureSensor } = useRequest(
@@ -186,10 +191,14 @@ const Sensors = () => {
sortToggleType: SortToggleType.AlternateWithReset,
sortFns: {
GPIO: (array) =>
[...array].sort((a, b) => (a as AnalogSensor).g - (b as AnalogSensor).g),
[...array].sort(
(a, b) => ((a as AnalogSensor)?.g ?? 0) - ((b as AnalogSensor)?.g ?? 0)
),
NAME: (array) =>
[...array].sort((a, b) =>
(a as AnalogSensor).n.localeCompare((b as AnalogSensor).n)
((a as AnalogSensor)?.n ?? '').localeCompare(
(b as AnalogSensor)?.n ?? ''
)
),
TYPE: (array) =>
[...array].sort((a, b) => (a as AnalogSensor).t - (b as AnalogSensor).t),
@@ -338,19 +347,23 @@ const Sensors = () => {
}, [fetchSensorData]);
const addAnalogSensor = useCallback(() => {
if (firstAvailableGPIO.current === undefined) {
toast.error(LL.NO_GPIO());
return;
}
setCreating(true);
setSelectedAnalogSensor({
id: Math.floor(Math.random() * (MAX_TEMP_ID - MIN_TEMP_ID) + MIN_TEMP_ID),
n: '',
g: DEFAULT_GPIO,
u: 0,
g: firstAvailableGPIO.current,
u: DeviceValueUOM.NONE,
v: 0,
o: 0,
t: 0,
f: 1,
t: AnalogType.DIGITAL_IN, // default to digital in 1
d: false,
o_n: '',
s: false
s: false,
o_n: ''
});
setAnalogDialogOpen(true);
}, []);
@@ -447,23 +460,17 @@ const Sensors = () => {
item={as}
onClick={() => updateAnalogSensor(as)}
>
<Cell stiff>{as.g !== 99 ? as.g : ''}</Cell>
<Cell stiff>{as.g}</Cell>
<Cell>{as.n}</Cell>
<Cell stiff>{AnalogTypeNames[as.t]} </Cell>
<Cell stiff>{AnalogTypeNames[as.t - 1]} </Cell>
{(as.t === AnalogType.DIGITAL_OUT &&
as.g !== GPIO_25 &&
as.g !== GPIO_26) ||
as.t === AnalogType.DIGITAL_IN ||
as.t === AnalogType.PULSE ? (
<Cell stiff>
{as.g !== 99 ? (as.v ? LL.ON() : LL.OFF()) : ''}
</Cell>
<Cell stiff>{as.v ? LL.ON() : LL.OFF()}</Cell>
) : (
<Cell stiff>
{as.t !== AnalogType.NOTUSED && as.g !== 99
? formatValue(as.v, as.u)
: ''}
</Cell>
<Cell stiff>{formatValue(as.v, as.u)}</Cell>
)}
</Row>
))}
@@ -578,12 +585,20 @@ const Sensors = () => {
onSave={onAnalogDialogSave}
creating={creating}
selectedItem={selectedAnalogSensor}
analogGPIOList={sensorData.valid_gpio_list}
analogGPIOList={sensorData.available_gpios}
disabledTypeList={sensorData.exclude_types}
validator={analogSensorItemValidation(sensorData.as, selectedAnalogSensor)}
/>
)}
{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
variant="outlined"
color="primary"

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import CancelIcon from '@mui/icons-material/Cancel';
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 {
Box,
@@ -23,7 +23,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types';
import type { AnalogSensor } from './types';
@@ -35,6 +35,7 @@ interface DashboardSensorsAnalogDialogProps {
creating: boolean;
selectedItem: AnalogSensor;
analogGPIOList: number[];
disabledTypeList: number[];
validator: Schema;
}
@@ -45,6 +46,7 @@ const SensorsAnalogDialog = ({
creating,
selectedItem,
analogGPIOList,
disabledTypeList,
validator
}: DashboardSensorsAnalogDialogProps) => {
const { LL } = useI18nContext();
@@ -66,7 +68,16 @@ const SensorsAnalogDialog = ({
// Memoize helper functions to check sensor type conditions
const isCounterOrRate = useMemo(
() => editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE,
() =>
editItem.t === AnalogType.COUNTER ||
editItem.t === AnalogType.RATE ||
(editItem.t >= AnalogType.CNT_0 && editItem.t <= AnalogType.CNT_2),
[editItem.t]
);
const isCounter = useMemo(
() =>
editItem.t === AnalogType.COUNTER ||
(editItem.t >= AnalogType.CNT_0 && editItem.t <= AnalogType.CNT_2),
[editItem.t]
);
const isFreqType = useMemo(
@@ -80,13 +91,13 @@ const SensorsAnalogDialog = ({
editItem.t === AnalogType.PWM_2,
[editItem.t]
);
const isDigitalOutGPIO = useMemo(
const isDACOutGPIO = useMemo(
() =>
editItem.t === AnalogType.DIGITAL_OUT &&
(editItem.g === 25 || editItem.g === 26),
[editItem.t, editItem.g]
);
const isDigitalOutNonGPIO = useMemo(
const isDigitalOutGPIO = useMemo(
() =>
editItem.t === AnalogType.DIGITAL_OUT &&
editItem.g !== 25 &&
@@ -97,12 +108,18 @@ const SensorsAnalogDialog = ({
// Memoize menu items to avoid recreation on each render
const analogTypeMenuItems = useMemo(
() =>
AnalogTypeNames.map((val, i) => (
<MenuItem key={val} value={i}>
{val}
</MenuItem>
)),
[]
AnalogTypeNames.map((val, i) => ({ name: val, value: i + 1 }))
.sort((a, b) => a.name.localeCompare(b.name))
.map(({ name, value }) => (
<MenuItem
key={name}
value={value}
disabled={disabledTypeList?.includes(value)}
>
{name}
</MenuItem>
)),
[disabledTypeList]
);
const uomMenuItems = useMemo(
@@ -115,6 +132,23 @@ const SensorsAnalogDialog = ({
[]
);
const analogGPIOMenuItems = () =>
// add selectedItem.g to the list
[
...(analogGPIOList?.includes(selectedItem.g) || selectedItem.g === undefined
? analogGPIOList
: [selectedItem.g, ...analogGPIOList])
]
.filter((gpio, idx, arr) => arr.indexOf(gpio) === idx)
.sort((a, b) => a - b)
.map((gpio: number) => {
return (
<MenuItem key={gpio} value={gpio}>
{gpio}
</MenuItem>
);
});
// Reset form when dialog opens or selectedItem changes
useEffect(() => {
if (open) {
@@ -138,7 +172,7 @@ const SensorsAnalogDialog = ({
await validate(validator, editItem);
onSave(editItem);
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}, [validator, editItem, onSave]);
@@ -152,20 +186,6 @@ const SensorsAnalogDialog = ({
[creating, LL]
);
// Ensure the current GPIO is in the list when no creating
// note GPIO 99 means not set
const availableGPIOs = useMemo(() => {
const filteredList = analogGPIOList.filter((gpio) => gpio !== 99);
if (
editItem.g !== undefined &&
editItem.g !== 99 &&
!filteredList.includes(editItem.g)
) {
return [...filteredList, editItem.g].sort((a, b) => a - b);
}
return filteredList;
}, [analogGPIOList, editItem.g]);
return (
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
<DialogTitle>{dialogTitle}</DialogTitle>
@@ -175,16 +195,12 @@ const SensorsAnalogDialog = ({
name="g"
label="GPIO"
value={editItem.g}
sx={{ width: '8ch' }}
disabled={editItem.s}
sx={{ width: '9ch' }}
disabled={editItem.s || !creating}
select
onChange={updateFormValue}
>
{availableGPIOs?.map((gpio: number) => (
<MenuItem key={gpio} value={gpio}>
{gpio}
</MenuItem>
))}
{analogGPIOMenuItems()}
</ValidatedTextField>
<Grid>
<ValidatedTextField
@@ -208,7 +224,10 @@ const SensorsAnalogDialog = ({
{analogTypeMenuItems}
</ValidatedTextField>
</Grid>
{(isCounterOrRate || isFreqType) && (
{(isCounterOrRate ||
isFreqType ||
editItem.t === AnalogType.ADC ||
editItem.t === AnalogType.TIMER) && (
<Grid>
<ValidatedTextField
name="u"
@@ -223,6 +242,23 @@ const SensorsAnalogDialog = ({
</ValidatedTextField>
</Grid>
)}
{editItem.t === AnalogType.DIGITAL_IN && (
<Grid>
<ValidatedTextField
name="f"
label={LL.POLARITY()}
value={editItem.f}
sx={{ width: '15ch' }}
fullWidth
select
onChange={updateFormValue}
disabled={editItem.s}
>
<MenuItem value={1}>{LL.ACTIVEHIGH()}</MenuItem>
<MenuItem value={0}>{LL.ACTIVELOW()}</MenuItem>
</ValidatedTextField>
</Grid>
)}
{editItem.t === AnalogType.ADC && (
<Grid>
<ValidatedTextField
@@ -265,7 +301,7 @@ const SensorsAnalogDialog = ({
/>
</Grid>
)}
{editItem.t === AnalogType.COUNTER && (
{isCounter && (
<Grid>
<ValidatedTextField
name="o"
@@ -294,7 +330,10 @@ const SensorsAnalogDialog = ({
/>
</Grid>
)}
{isCounterOrRate && (
{(isCounterOrRate ||
isFreqType ||
editItem.t === AnalogType.ADC ||
editItem.t === AnalogType.TIMER) && (
<Grid>
<ValidatedTextField
name="f"
@@ -310,7 +349,7 @@ const SensorsAnalogDialog = ({
/>
</Grid>
)}
{isDigitalOutGPIO && (
{isDACOutGPIO && (
<Grid>
<ValidatedTextField
name="o"
@@ -326,7 +365,7 @@ const SensorsAnalogDialog = ({
/>
</Grid>
)}
{isDigitalOutNonGPIO && (
{isDigitalOutGPIO && (
<>
<Grid>
<ValidatedTextField
@@ -425,7 +464,7 @@ const SensorsAnalogDialog = ({
name="o"
label={LL.POLARITY()}
value={editItem.o}
sx={{ width: '11ch' }}
sx={{ width: '15ch' }}
select
onChange={updateFormValue}
disabled={editItem.s}
@@ -457,7 +496,7 @@ const SensorsAnalogDialog = ({
)}
</Grid>
{fieldErrors && Object.keys(fieldErrors).length > 0 && (
<Box mt={1}>
<Box sx={{ mt: 1 }}>
{Object.values(fieldErrors).map((errArr, idx) =>
Array.isArray(errArr)
? errArr.map((err, j) => (
@@ -465,7 +504,7 @@ const SensorsAnalogDialog = ({
key={`${idx}-${j}`}
color="error"
variant="caption"
display="block"
sx={{ display: 'block' }}
>
{err.message}
</Typography>
@@ -476,7 +515,7 @@ const SensorsAnalogDialog = ({
)}
{editItem.s && (
<Grid>
<Typography mt={1} color="warning.main" variant="body2">
<Typography sx={{ mt: 1 }} color="warning" variant="body2">
<WarningIcon
fontSize="small"
sx={{ mr: 1, verticalAlign: 'middle' }}
@@ -489,7 +528,7 @@ const SensorsAnalogDialog = ({
</DialogContent>
<DialogActions>
{!creating && (
<Box flexGrow={1} sx={{ '& button': { mt: 0 } }}>
<Box sx={{ flexGrow: 1, '& button': { mt: 0 } }}>
<Button
startIcon={<RemoveIcon />}
disabled={editItem.s}

View File

@@ -4,7 +4,6 @@ import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done';
import WarningIcon from '@mui/icons-material/Warning';
import {
Box,
Button,
Dialog,
DialogActions,
@@ -22,7 +21,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
import type { TemperatureSensor } from './types';
@@ -85,7 +84,7 @@ const SensorsTemperatureDialog = ({
await validate(validator, editItem);
onSave(editItem);
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}, [validator, editItem, onSave]);
@@ -111,11 +110,9 @@ const SensorsTemperatureDialog = ({
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
<DialogTitle>{dialogTitle}</DialogTitle>
<DialogContent dividers>
<Box color="warning.main" mb={2}>
<Typography variant="body2">
{LL.ID_OF(LL.SENSOR(0))}: {editItem.id}
</Typography>
</Box>
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
{LL.ID_OF(LL.SENSOR(0))}: {editItem.id}
</Typography>
<Grid container spacing={2}>
<Grid>
<ValidatedTextField
@@ -142,7 +139,7 @@ const SensorsTemperatureDialog = ({
</Grid>
{editItem.s && (
<Grid>
<Typography mt={1} color="warning.main" variant="body2">
<Typography sx={{ mt: 1 }} color="warning" variant="body2">
<WarningIcon
fontSize="small"
sx={{ mr: 1, verticalAlign: 'middle' }}

View File

@@ -41,8 +41,12 @@ const UserProfileComponent = () => {
/>
</ListItem>
</List>
<Box mt={2} mb={2} display="flex" alignItems="center">
<Typography mr={2} variant="body1" align="center">
<Box sx={{ mt: 2, mb: 2, display: 'flex', alignItems: 'center' }}>
<Typography
sx={{ mr: 2, textAlign: 'center' }}
color="warning"
variant="body1"
>
{LL.LANGUAGE()}:
</Typography>
<LanguageSelector />

View File

@@ -89,15 +89,15 @@ export interface TemperatureSensor {
export interface AnalogSensor {
id: number;
g: number; // GPIO
n: string;
v: number;
u: number;
o: number;
f: number;
t: number;
n: string; // name
v: number; // value
u: number; // uom
o: number; // offset
f: number; // factor
t: number; // type
d: boolean; // deleted flag
s: boolean; // system sensor flag
o_n?: string;
o_n?: string; // original name
}
export interface WriteTemperatureSensor {
@@ -111,7 +111,8 @@ export interface SensorData {
ts: TemperatureSensor[];
as: AnalogSensor[];
analog_enabled: boolean;
valid_gpio_list: number[];
available_gpios: number[];
exclude_types: number[];
platform: string;
}
@@ -195,13 +196,13 @@ export enum DeviceValueUOM {
MBAR,
LH,
CTKWH,
HZ
HERTZ
}
export const DeviceValueUOM_s = [
'',
'°C',
'°C',
'°C Rel',
'%',
'l/min',
'kWh',
@@ -231,7 +232,6 @@ export const DeviceValueUOM_s = [
export enum AnalogType {
REMOVED = -1,
NOTUSED = 0,
DIGITAL_IN = 1,
COUNTER = 2,
ADC = 3,
@@ -246,26 +246,31 @@ export enum AnalogType {
PULSE = 12,
FREQ_0 = 13,
FREQ_1 = 14,
FREQ_2 = 15
FREQ_2 = 15,
CNT_0 = 16,
CNT_1 = 17,
CNT_2 = 18
}
export const AnalogTypeNames = [
'(disabled)',
'Digital In',
'Counter',
'ADC In',
'Timer',
'Rate',
'Digital Out',
'PWM 0',
'PWM 1',
'PWM 2',
'NTC Temp.',
'RGB Led',
'Pulse',
'Freq 0',
'Freq 1',
'Freq 2'
'Digital In', // 1
'Counter', // 2
'ADC In', // 3
'Timer', // 4
'Rate', // 5
'Digital Out', // 6
'PWM 0', // 7
'PWM 1', // 8
'PWM 2', // 9
'NTC Temp', // 10
'RGB Led', // 11
'Pulse', // 12
'Freq 0', // 13
'Freq 1', // 14
'Freq 2', // 15
'Counter 0', // 16
'Counter 1', // 17
'Counter 2' // 18
] as const;
export const BOARD_PROFILES = {

View File

@@ -310,14 +310,6 @@ export const analogSensorItemValidation = (
{ required: true, message: 'Name is required' },
NAME_PATTERN,
uniqueAnalogNameValidator(sensors, sensor.o_n)
],
g: [
{
required: true,
type: 'number',
min: 1,
message: 'GPIO is required'
}
]
});
};

View File

@@ -21,7 +21,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import type { APSettingsType } from 'types';
import { APProvisionMode } from 'types';
import { numberValue, updateValueDirty, useRest } from 'utils';
import { createAPSettingsValidator, validate } from 'validators';
import { ValidationError, createAPSettingsValidator, validate } from 'validators';
export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
provision_mode === APProvisionMode.AP_MODE_ALWAYS ||
@@ -86,7 +86,7 @@ const APSettings = () => {
await validate(createAPSettingsValidator(data), data);
await saveData();
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}, [data, saveData]);

View File

@@ -33,7 +33,7 @@ import {
} from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValueDirty, useRest } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
import { BOARD_PROFILES } from '../main/types';
@@ -153,7 +153,7 @@ const ApplicationSettings = () => {
setFieldErrors(undefined);
await validate(createSettingsValidator(data), data);
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
} finally {
await saveData();
}
@@ -328,9 +328,9 @@ const ApplicationSettings = () => {
>
<MenuItem value={-1}>OFF</MenuItem>
<MenuItem value={3}>ERR</MenuItem>
<MenuItem value={4}>WARN</MenuItem>
<MenuItem value={5}>NOTICE</MenuItem>
<MenuItem value={6}>INFO</MenuItem>
<MenuItem value={7}>DEBUG</MenuItem>
<MenuItem value={9}>ALL</MenuItem>
</TextField>
</Grid>
@@ -489,17 +489,25 @@ const ApplicationSettings = () => {
name="board_profile"
label={LL.BOARD_PROFILE()}
value={data.board_profile}
disabled={processingBoard || hardwareData.model.startsWith('BBQKees')}
disabled={processingBoard}
variant="outlined"
onChange={changeBoardProfile}
margin="normal"
select
>
{boardProfileItems}
<Divider />
<MenuItem key={'CUSTOM'} value={'CUSTOM'}>
{LL.CUSTOM()}&hellip;
</MenuItem>
{hardwareData.model.startsWith('BBQKees') ? (
<MenuItem key={hardwareData.board} value={hardwareData.board}>
{BOARD_PROFILES[hardwareData.board as BoardProfileKey]}
</MenuItem>
) : (
boardProfileItems
)}
{(data.board_profile === 'CUSTOM' || data.developer_mode) && <Divider />}
{(data.board_profile === 'CUSTOM' || data.developer_mode) && (
<MenuItem key={'CUSTOM'} value={'CUSTOM'}>
{LL.CUSTOM()}&hellip;
</MenuItem>
)}
</TextField>
{data.board_profile === 'CUSTOM' && (
<>
@@ -602,6 +610,7 @@ const ApplicationSettings = () => {
<MenuItem value={0}>{LL.DISABLED(1)}</MenuItem>
<MenuItem value={1}>LAN8720</MenuItem>
<MenuItem value={2}>TLK110</MenuItem>
<MenuItem value={3}>RTL8201</MenuItem>
</TextField>
</Grid>
</Grid>
@@ -668,6 +677,7 @@ const ApplicationSettings = () => {
<MenuItem value={2}>EMS+</MenuItem>
<MenuItem value={3}>HT3</MenuItem>
<MenuItem value={4}>{LL.HARDWARE()}</MenuItem>
<MenuItem value={5}>Auto</MenuItem>
</TextField>
</Grid>
<Grid>
@@ -762,7 +772,7 @@ const ApplicationSettings = () => {
label={LL.REMOTE_TIMEOUT_EN()}
/>
{data.remote_timeout_en && (
<Box mt={2}>
<Box sx={{ mt: 2 }}>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="remote_timeout"
@@ -857,8 +867,9 @@ const ApplicationSettings = () => {
</Grid>
{restartNeeded && (
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT(0)}>
<MessageBox level="warning" message={LL.RESTART_TEXT(0)}>
<Button
sx={{ ml: 2 }}
startIcon={<PowerSettingsNewIcon />}
variant="contained"
color="error"
@@ -894,10 +905,12 @@ const ApplicationSettings = () => {
);
};
return (
return restarting ? (
<SystemMonitor />
) : (
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{restarting ? <SystemMonitor /> : content()}
{content()}
</SectionContent>
);
};

View File

@@ -1,12 +1,23 @@
import { useCallback, useMemo, useState } from 'react';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
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 { API, callAction } from 'api/app';
import { dialogStyle } from '@/CustomTheme';
import { useRequest } from 'alova/client';
import type { APIcall } from 'app/main/types';
import SystemMonitor from 'app/status/SystemMonitor';
@@ -19,16 +30,11 @@ import {
import { useI18nContext } from 'i18n/i18n-react';
import { saveFile } from 'utils';
interface DownloadButton {
key: string;
type: string;
label: string | number;
isGridButton: boolean;
}
const DownloadUpload = () => {
const { LL } = useI18nContext();
const [confirmBackup, setConfirmBackup] = useState<boolean>(false);
const [restarting, setRestarting] = useState<boolean>(false);
const { send: sendExportData } = useRequest(
@@ -63,45 +69,50 @@ const DownloadUpload = () => {
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
const downloadButtons: DownloadButton[] = useMemo(
() => [
{
key: 'settings',
type: 'settings',
label: LL.SETTINGS_OF(LL.APPLICATION()),
isGridButton: true
},
{
key: 'customizations',
type: 'customizations',
label: LL.CUSTOMIZATIONS(),
isGridButton: true
},
{
key: 'entities',
type: 'entities',
label: LL.CUSTOM_ENTITIES(0),
isGridButton: true
},
{
key: 'schedule',
type: 'schedule',
label: LL.SCHEDULE(0),
isGridButton: true
},
{
key: 'allvalues',
type: 'allvalues',
label: LL.ALLVALUES(),
isGridButton: false
}
],
[LL]
const handleCloseBackupDialog = useCallback(() => {
setConfirmBackup(false);
}, []);
const renderBackupDialog = useMemo(
() => (
<Dialog
sx={dialogStyle}
open={confirmBackup}
onClose={handleCloseBackupDialog}
>
<DialogTitle>{LL.DOWNLOAD_SYSTEM_BACKUP()}</DialogTitle>
<DialogContent dividers>
<WarningIcon color="warning" sx={{ fontSize: 18 }} />
&nbsp;
{LL.WARNING_SYSTEM_BACKUP()}
</DialogContent>
<DialogActions>
<Button
startIcon={<CancelIcon />}
variant="outlined"
onClick={handleCloseBackupDialog}
color="secondary"
>
{LL.CANCEL()}
</Button>
<Button
startIcon={<DownloadIcon />}
variant="outlined"
onClick={() => handleDownload('systembackup')()}
color="primary"
>
{LL.DOWNLOAD(0)}
</Button>
</DialogActions>
</Dialog>
),
[confirmBackup, handleCloseBackupDialog, LL]
);
const handleDownload = useCallback(
(type: string) => () => {
void sendExportData(type);
setConfirmBackup(false);
},
[sendExportData]
);
@@ -118,58 +129,57 @@ const DownloadUpload = () => {
);
}
const gridButtons = downloadButtons.filter((btn) => btn.isGridButton);
const standaloneButton = downloadButtons.find((btn) => !btn.isGridButton);
return (
<SectionContent>
{renderBackupDialog}
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
{LL.DOWNLOAD(0)}
</Typography>
<Typography mb={1} variant="body1" color="warning">
{LL.DOWNLOAD_SETTINGS_TEXT()}.
</Typography>
<Grid container spacing={2}>
{gridButtons.map((button) => (
<Grid key={button.key}>
<Button
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={handleDownload(button.type)}
>
{button.label}
</Button>
</Grid>
))}
</Grid>
<Typography mt={2} mb={1} variant="body1" color="warning">
{LL.DOWNLOAD_SETTINGS_TEXT2()}.
</Typography>
{standaloneButton && (
<Grid
container
spacing={2}
sx={{
alignItems: 'center'
}}
>
<Typography variant="body1" color="warning">
{LL.DOWNLOAD_SETTINGS_TEXT()}:
</Typography>
<Button
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={handleDownload(standaloneButton.type)}
onClick={() => setConfirmBackup(true)}
>
{standaloneButton.label}
{LL.DOWNLOAD_SYSTEM_BACKUP()}
</Button>
)}
</Grid>
<Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
<Typography variant="body1" color="warning">
{LL.DOWNLOAD_SETTINGS_TEXT2()}:
</Typography>
<Button
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={handleDownload('allvalues')}
>
{LL.ALLVALUES()}
</Button>
</Grid>
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
{LL.UPLOAD()}
</Typography>
<Box color="warning.main" sx={{ pb: 2 }}>
<Typography variant="body1">{LL.UPLOAD_TEXT()}.</Typography>
</Box>
<Typography sx={{ pb: 2 }} color="warning" variant="body1">
{LL.UPLOAD_TEXT()}:
</Typography>
<SingleUpload text={LL.UPLOAD_DRAG()} doRestart={doRestart} />
<SingleUpload doRestart={doRestart} />
</SectionContent>
);
};

View File

@@ -31,7 +31,7 @@ import {
import { useI18nContext } from 'i18n/i18n-react';
import type { MqttSettingsType } from 'types';
import { numberValue, updateValueDirty, useRest } from 'utils';
import { createMqttSettingsValidator, validate } from 'validators';
import { ValidationError, createMqttSettingsValidator, validate } from 'validators';
import { callAction } from '../../api/app';
@@ -94,7 +94,7 @@ const MqttSettings = () => {
await validate(createMqttSettingsValidator(data), data);
await saveData();
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}, [data, saveData]);
@@ -129,7 +129,7 @@ const MqttSettings = () => {
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
<>
<Box display="flex" gap={2} mb={1}>
<Box sx={{ display: 'flex', gap: 2, mb: 1 }}>
<BlockFormControlLabel
control={
<Checkbox
@@ -266,6 +266,7 @@ const MqttSettings = () => {
label={LL.CERT()}
variant="outlined"
value={data.rootCA}
sx={{ width: '50ch' }}
onChange={updateFormValue}
margin="normal"
/>
@@ -344,75 +345,91 @@ const MqttSettings = () => {
</Grid>
)}
</Grid>
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<BlockFormControlLabel
control={
<Checkbox
name="ha_enabled"
checked={data.ha_enabled}
onChange={updateFormValue}
disabled={data.publish_single}
/>
}
label={LL.MQTT_PUBLISH_TEXT_3()}
/>
</Grid>
{data.ha_enabled && (
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<TextField
name="discovery_type"
label={LL.MQTT_PUBLISH_TEXT_5()}
value={data.discovery_type}
variant="outlined"
onChange={updateFormValue}
margin="normal"
select
>
<MenuItem value={0}>Home Assistant</MenuItem>
<MenuItem value={1}>Domoticz</MenuItem>
<MenuItem value={2}>Domoticz (latest)</MenuItem>
</TextField>
</Grid>
<Grid>
<TextField
name="discovery_prefix"
label={LL.MQTT_PUBLISH_TEXT_4()}
variant="outlined"
value={data.discovery_prefix}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<TextField
name="entity_format"
label={LL.MQTT_ENTITY_FORMAT()}
value={data.entity_format}
variant="outlined"
onChange={updateFormValue}
margin="normal"
select
>
<MenuItem value={0}>{LL.MQTT_ENTITY_FORMAT_0()}</MenuItem>
<MenuItem value={3}>
{LL.MQTT_ENTITY_FORMAT_1()}&nbsp;(v3.5)
</MenuItem>
<MenuItem value={4}>
{LL.MQTT_ENTITY_FORMAT_2()}&nbsp;(v3.5)
</MenuItem>
<MenuItem value={1}>
{LL.MQTT_ENTITY_FORMAT_1()}&nbsp;(latest)
</MenuItem>
<MenuItem value={2}>
{LL.MQTT_ENTITY_FORMAT_2()}&nbsp;(latest)
</MenuItem>
</TextField>
</Grid>
</Grid>
)}
{/* <Grid container spacing={2} rowSpacing={0}> */}
<Grid>
<BlockFormControlLabel
control={
<Checkbox
name="ha_enabled"
checked={data.ha_enabled}
onChange={updateFormValue}
disabled={data.publish_single}
/>
}
label={LL.MQTT_PUBLISH_TEXT_3()}
/>
</Grid>
{data.ha_enabled && (
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<TextField
name="discovery_type"
label={LL.MQTT_PUBLISH_TEXT_5()}
value={data.discovery_type}
variant="outlined"
onChange={updateFormValue}
margin="normal"
select
>
<MenuItem value={0}>Home Assistant</MenuItem>
<MenuItem value={1}>Domoticz</MenuItem>
<MenuItem value={2}>Domoticz (latest)</MenuItem>
</TextField>
</Grid>
<Grid>
<TextField
name="discovery_prefix"
label={LL.MQTT_PUBLISH_TEXT_4()}
variant="outlined"
value={data.discovery_prefix}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<TextField
name="entity_format"
label={LL.MQTT_ENTITY_FORMAT()}
value={data.entity_format}
variant="outlined"
onChange={updateFormValue}
margin="normal"
select
>
<MenuItem value={0}>{LL.MQTT_ENTITY_FORMAT_0()}</MenuItem>
<MenuItem value={3}>
{LL.MQTT_ENTITY_FORMAT_1()}&nbsp;(v3.5)
</MenuItem>
<MenuItem value={4}>
{LL.MQTT_ENTITY_FORMAT_2()}&nbsp;(v3.5)
</MenuItem>
<MenuItem value={1}>
{LL.MQTT_ENTITY_FORMAT_1()}&nbsp;(latest)
</MenuItem>
<MenuItem value={2}>
{LL.MQTT_ENTITY_FORMAT_2()}&nbsp;(latest)
</MenuItem>
</TextField>
</Grid>
<Grid>
{data.discovery_type === 0 && (
<TextField
name="ha_number_mode"
label={LL.MQTT_INPUT_NUMBER_FORMAT()}
value={data.ha_number_mode}
variant="outlined"
onChange={updateFormValue}
sx={{ width: '20ch' }}
margin="normal"
select
>
<MenuItem value={0}>Box</MenuItem>
<MenuItem value={1}>Slider</MenuItem>
</TextField>
)}
</Grid>
</Grid>
)}
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
{LL.MQTT_PUBLISH_INTERVALS()}&nbsp;(0=auto)
</Typography>

View File

@@ -36,7 +36,7 @@ import {
import { useI18nContext } from 'i18n/i18n-react';
import type { NTPSettingsType, Time } from 'types';
import { formatLocalDateTime, updateValueDirty, useRest } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ';
@@ -133,7 +133,7 @@ const NTPSettings = () => {
await validate(NTP_SETTINGS_VALIDATOR, data);
await saveData();
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}, [data, saveData]);
@@ -193,9 +193,9 @@ const NTPSettings = () => {
{timeZoneItems}
</ValidatedTextField>
<Box display="flex" flexWrap="wrap">
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
{!data.enabled && !dirtyFlags.length && (
<Box flexWrap="nowrap" whiteSpace="nowrap">
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow>
<Button
onClick={openSetTime}
@@ -259,9 +259,9 @@ const NTPSettings = () => {
<Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}>
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
<DialogContent dividers>
<Box color="warning.main" p={0} pl={0} pr={0} mt={0} mb={2}>
<Typography variant="body2">{LL.SET_TIME_TEXT()}</Typography>
</Box>
<Typography color="warning" variant="body2">
{LL.SET_TIME_TEXT()}
</Typography>
<TextField
label={LL.LOCAL_TIME(0)}
type="datetime-local"

View File

@@ -156,11 +156,13 @@ const Settings = () => {
<Divider />
<Box
mt={2}
display="flex"
justifyContent="flex-end"
flexWrap="nowrap"
whiteSpace="nowrap"
sx={{
mt: 2,
display: 'flex',
justifyContent: 'flex-end',
flexWrap: 'nowrap',
whiteSpace: 'nowrap'
}}
>
<Button
startIcon={<SettingsBackupRestoreIcon />}
@@ -182,7 +184,7 @@ const Settings = () => {
restarting
]);
return <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
};
export default Settings;

View File

@@ -2,9 +2,7 @@ import { useMemo } from 'react';
import { MenuItem } from '@mui/material';
type TimeZones = Record<string, string>;
export const TIME_ZONES: Readonly<TimeZones> = {
export const TIME_ZONES: Record<string, string> = {
'Africa/Abidjan': 'GMT0',
'Africa/Accra': 'GMT0',
'Africa/Addis_Ababa': 'EAT-3',

View File

@@ -40,7 +40,7 @@ import {
import { useI18nContext } from 'i18n/i18n-react';
import type { NetworkSettingsType } from 'types';
import { updateValueDirty, useRest } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
import { createNetworkSettingsValidator } from 'validators/network';
import SystemMonitor from '../../status/SystemMonitor';
@@ -116,7 +116,7 @@ const NetworkSettings = () => {
await validate(createNetworkSettingsValidator(data), data);
await saveData();
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
deselectNetwork();
}, [data, saveData, deselectNetwork]);
@@ -355,8 +355,9 @@ const NetworkSettings = () => {
</>
)}
{restartNeeded && (
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT(0)}>
<MessageBox level="warning" message={LL.RESTART_TEXT(0)}>
<Button
sx={{ ml: 2 }}
startIcon={<PowerSettingsNewIcon />}
variant="contained"
color="error"
@@ -396,10 +397,12 @@ const NetworkSettings = () => {
);
};
return (
return restarting ? (
<SystemMonitor />
) : (
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{restarting ? <SystemMonitor /> : content()}
{content()}
</SectionContent>
);
};

View File

@@ -94,7 +94,7 @@ const WiFiNetworkSelector = ({ networkList }: { networkList: WiFiNetworkList })
);
if (networkList.networks.length === 0) {
return <MessageBox mt={2} mb={1} message={LL.NETWORK_NO_WIFI()} level="info" />;
return <MessageBox message={LL.NETWORK_NO_WIFI()} level="info" />;
}
return <List>{networkList.networks.map(renderNetwork)}</List>;

View File

@@ -54,19 +54,27 @@ const GenerateToken = ({ username, onClose }: GenerateTokenProps) => {
<DialogContent dividers>
{token ? (
<>
<MessageBox message={LL.ACCESS_TOKEN_TEXT()} level="info" my={2} />
<Box mt={2} mb={2}>
<MessageBox
message={LL.ACCESS_TOKEN_TEXT()}
level="info"
sx={{ mt: 2, mb: 2 }}
/>
<Box sx={{ mt: 2, mb: 2 }}>
<TextField
label="Token"
multiline
value={token.token}
fullWidth
contentEditable={false}
slotProps={{
input: {
readOnly: true
}
}}
/>
</Box>
</>
) : (
<Box m={4} textAlign="center">
<Box sx={{ m: 4, textAlign: 'center' }}>
<LinearProgress />
<Typography variant="h6">{LL.GENERATING_TOKEN()}&hellip;</Typography>
</Box>

View File

@@ -240,12 +240,16 @@ const ManageUsers = () => {
</Table>
{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 && (
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
<Box sx={{ flexGrow: 1, '& button': { mt: 2 } }}>
<ButtonRow>
<Button
startIcon={<CancelIcon />}
@@ -270,7 +274,7 @@ const ManageUsers = () => {
</ButtonRow>
</Box>
)}
<Box flexWrap="nowrap" whiteSpace="nowrap">
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow>
<Button
startIcon={<PersonAddIcon />}

View File

@@ -19,7 +19,7 @@ import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react';
import type { SecuritySettingsType } from 'types';
import { updateValueDirty, useRest } from 'utils';
import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators';
import { SECURITY_SETTINGS_VALIDATOR, ValidationError, validate } from 'validators';
const SecuritySettings = () => {
const { LL } = useI18nContext();
@@ -58,7 +58,7 @@ const SecuritySettings = () => {
await saveData();
await authenticatedContext.refresh();
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}, [data, saveData, authenticatedContext]);

View File

@@ -24,7 +24,7 @@ import {
import { useI18nContext } from 'i18n/i18n-react';
import type { UserType } from 'types';
import { updateValue } from 'utils';
import { validate } from 'validators';
import { ValidationError, validate } from 'validators';
interface UserFormProps {
creating: boolean;
@@ -69,7 +69,7 @@ const User: FC<UserFormProps> = ({
await validate(validator, user);
onDoneEditing();
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
setFieldErrors((error as ValidationError).fieldErrors);
}
}
}, [user, validator, onDoneEditing]);

View File

@@ -8,10 +8,10 @@ import DeviceHubIcon from '@mui/icons-material/DeviceHub';
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
import LogoDevIcon from '@mui/icons-material/LogoDev';
import MemoryIcon from '@mui/icons-material/Memory';
import MonitorHeartIcon from '@mui/icons-material/MonitorHeart';
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
import RouterIcon from '@mui/icons-material/Router';
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
import TimerIcon from '@mui/icons-material/Timer';
import WifiIcon from '@mui/icons-material/Wifi';
import {
Avatar,
@@ -37,7 +37,7 @@ import { FormLoader, SectionContent, useLayoutTitle } from 'components';
import ListMenuItem from 'components/layout/ListMenuItem';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react';
import { NTPSyncStatus, NetworkConnectionStatus } from 'types';
import { NTPSyncStatus, NetworkConnectionStatus, SystemStatusCodes } from 'types';
import { useInterval } from 'utils';
import { formatDateTime } from 'utils/time';
@@ -113,6 +113,27 @@ const SystemStatus = () => {
}
}, [data?.bus_status, data?.bus_uptime, LL]);
// Memoize derived status values to avoid recalculation on every render
const systemStatus = useMemo(() => {
if (!data) return '??';
switch (data.status) {
case SystemStatusCodes.SYSTEM_STATUS_PENDING_UPLOAD:
case SystemStatusCodes.SYSTEM_STATUS_UPLOADING:
return LL.WAIT_FIRMWARE();
case SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD:
return LL.ERROR();
case SystemStatusCodes.SYSTEM_STATUS_PENDING_RESTART:
case SystemStatusCodes.SYSTEM_STATUS_RESTART_REQUESTED:
return LL.RESTARTING_PRE();
case SystemStatusCodes.SYSTEM_STATUS_INVALID_GPIO:
return LL.GPIO_OF(LL.FAILED(0));
default:
// SystemStatusCodes.SYSTEM_STATUS_NORMAL
return 'OK';
}
}, [data?.status, LL]);
const busStatusHighlight = useMemo(() => {
if (!data) return theme.palette.warning.main;
@@ -313,10 +334,13 @@ const SystemStatus = () => {
<ListItem>
<ListItemAvatar>
<Avatar sx={{ bgcolor: '#c5572c', color: 'white' }}>
<TimerIcon />
<MonitorHeartIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={LL.UPTIME()} secondary={uptimeText} />
<ListItemText
primary={LL.STATUS_OF(LL.SYSTEM(0))}
secondary={`${systemStatus} (${LL.UPTIME()}: ${uptimeText})`}
/>
{me.admin && (
<Button
startIcon={<PowerSettingsNewIcon />}
@@ -419,7 +443,7 @@ const SystemStatus = () => {
renderRestartDialog
]);
return <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
};
export default SystemStatus;

View File

@@ -38,8 +38,6 @@ import type { LogEntry, LogSettings } from 'types';
import { LogLevel } from 'types';
import { updateValueDirty, useRest } from 'utils';
const MAX_LOG_ENTRIES = 1000; // Limit log entries to prevent memory issues
const TextColors: Record<LogLevel, string> = {
[LogLevel.ERROR]: '#ff0000', // red
[LogLevel.WARNING]: '#ff0000', // red
@@ -200,10 +198,6 @@ const SystemLog = () => {
}
}
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;
});
}, []);
@@ -272,7 +266,7 @@ const SystemLog = () => {
return (
<>
<Grid container spacing={2} alignItems="center">
<Grid container spacing={2} sx={{ alignItems: 'center' }}>
<Grid>
<TextField
name="level"
@@ -308,6 +302,8 @@ const SystemLog = () => {
<MenuItem value={50}>50</MenuItem>
<MenuItem value={75}>75</MenuItem>
<MenuItem value={100}>100</MenuItem>
<MenuItem value={500}>500</MenuItem>
<MenuItem value={1000}>1000</MenuItem>
</TextField>
</Grid>
)}

View File

@@ -60,18 +60,16 @@ const SystemMonitor = () => {
const { statusMessage, isUploading, progressValue } = useMemo(() => {
const status = data?.status;
let message = '';
if (status && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING) {
message = LL.WAIT_FIRMWARE();
} else if (status === SystemStatusCodes.SYSTEM_STATUS_PENDING_RESTART) {
message = LL.APPLICATION_RESTARTING();
} else if (status === SystemStatusCodes.SYSTEM_STATUS_NORMAL) {
message = LL.RESTARTING_PRE();
} else if (status === SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD) {
message = 'Upload Failed';
} else {
message = LL.RESTARTING_POST();
}
const message =
status && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING
? LL.WAIT_FIRMWARE()
: status === SystemStatusCodes.SYSTEM_STATUS_PENDING_RESTART
? LL.APPLICATION_RESTARTING()
: status === SystemStatusCodes.SYSTEM_STATUS_NORMAL
? LL.RESTARTING_PRE()
: status === SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD
? 'Upload Failed'
: LL.RESTARTING_POST();
const uploading =
status !== undefined && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING;
@@ -103,8 +101,8 @@ const SystemMonitor = () => {
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backdropFilter: 'blur(8px)'
justifyContent: 'center'
// backdropFilter: 'blur(8px)'
}}
>
<Box
@@ -113,33 +111,31 @@ const SystemMonitor = () => {
minWidth: '300px',
maxWidth: '500px',
backgroundColor: '#393939',
border: 3,
border: 2,
borderColor: '#565656',
borderRadius: '8px',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
p: 3
}}
>
<Box display="flex" alignItems="center" flexDirection="column">
<Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
<img
src="/app/icon.png"
alt="EMS-ESP"
style={{ width: '40px', height: '40px', marginBottom: '16px' }}
/>
<Typography
color="secondary"
sx={{ color: 'secondary', fontWeight: 400, textAlign: 'center' }}
variant="h6"
fontWeight={400}
textAlign="center"
>
{statusMessage}
</Typography>
{errorMessage ? (
<MessageBox my={2} level="error" message={errorMessage}>
<MessageBox level="error" message={errorMessage}>
<Button
size="small"
sx={{ ml: 2 }}
size="small"
startIcon={<CancelIcon />}
variant="contained"
color="error"
@@ -150,11 +146,14 @@ const SystemMonitor = () => {
</MessageBox>
) : (
<>
<Typography mt={2} variant="h6" fontWeight={400} textAlign="center">
<Typography
sx={{ mt: 2, fontWeight: 400, textAlign: 'center' }}
variant="h6"
>
{LL.PLEASE_WAIT()}&hellip;
</Typography>
{isUploading && (
<Box width="100%" pl={2} pr={2} py={2}>
<Box sx={{ width: '100%', pl: 2, pr: 2, py: 2 }}>
<LinearProgressWithLabel value={progressValue} />
</Box>
)}

View File

@@ -1,4 +1,13 @@
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import {
memo,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState
} from 'react';
import { Link } from 'react-router';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
@@ -10,15 +19,12 @@ import WarningIcon from '@mui/icons-material/Warning';
import {
Box,
Button,
Checkbox,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
FormControlLabel,
Grid,
IconButton,
Link,
Table,
TableBody,
TableCell,
@@ -54,6 +60,13 @@ const DEV_RELNOTES_URL =
'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md';
// Types for better type safety
interface PartitionData {
partition: string;
version: string;
install_date?: string;
size: number;
}
interface VersionData {
emsesp_version: string;
arduino_version: string;
@@ -61,6 +74,9 @@ interface VersionData {
flash_chip_size: number;
psram: boolean;
build_flags?: string;
partition: string;
partitions: PartitionData[];
developer_mode: boolean;
}
interface UpgradeCheckData {
@@ -80,6 +96,10 @@ const VersionInfoDialog = memo(
showVersionInfo,
latestVersion,
latestDevVersion,
partitionVersion,
partition,
currentPartition,
size,
locale,
LL,
onClose
@@ -87,6 +107,10 @@ const VersionInfoDialog = memo(
showVersionInfo: number;
latestVersion?: VersionInfo;
latestDevVersion?: VersionInfo;
partitionVersion?: VersionInfo | undefined;
partition: string;
currentPartition: string;
size: number;
locale: string;
LL: TranslationFunctions;
onClose: () => void;
@@ -94,8 +118,19 @@ const VersionInfoDialog = memo(
if (showVersionInfo === 0) return null;
const isStable = showVersionInfo === 1;
const version = isStable ? latestVersion : latestDevVersion;
const relNotesUrl = isStable ? STABLE_RELNOTES_URL : DEV_RELNOTES_URL;
const isDev = showVersionInfo === 2;
const isPartition = showVersionInfo === 3;
const version = isStable
? latestVersion
: isDev
? latestDevVersion
: partitionVersion;
const relNotesUrl = isStable
? STABLE_RELNOTES_URL
: isDev
? DEV_RELNOTES_URL
: '';
return (
<Dialog sx={dialogStyle} open={showVersionInfo !== 0} onClose={onClose}>
@@ -112,14 +147,17 @@ const VersionInfoDialog = memo(
borderBottom: 'none',
pr: 1,
py: 0.5,
fontSize: 13,
width: 90
fontSize: 13
}}
>
{LL.TYPE(0)}
{LL.VERSION()}
</TableCell>
<TableCell sx={{ borderBottom: 'none', py: 0.5, fontSize: 13 }}>
{isStable ? LL.STABLE() : LL.DEVELOPMENT()}
{isPartition
? typeof version === 'string'
? version
: version?.name
: version?.name}
</TableCell>
</TableRow>
<TableRow sx={{ height: 24, borderBottom: 'none' }}>
@@ -131,15 +169,61 @@ const VersionInfoDialog = memo(
borderBottom: 'none',
pr: 1,
py: 0.5,
fontSize: 13
fontSize: 13,
width: 140
}}
>
{LL.VERSION()}
{isPartition ? LL.TYPE(0) : LL.RELEASE_TYPE()}
</TableCell>
<TableCell sx={{ borderBottom: 'none', py: 0.5, fontSize: 13 }}>
{version?.name}
{partition === currentPartition && LL.ACTIVE() + ' '}
{isStable
? LL.STABLE()
: isDev
? LL.DEVELOPMENT()
: 'Partition ' + LL.VERSION()}
</TableCell>
</TableRow>
{isPartition && (
<TableRow sx={{ height: 24, borderBottom: 'none' }}>
<TableCell
component="th"
scope="row"
sx={{
color: 'lightblue',
borderBottom: 'none',
pr: 1,
py: 0.5,
fontSize: 13
}}
>
Partition
</TableCell>
<TableCell sx={{ borderBottom: 'none', py: 0.5, fontSize: 13 }}>
{partition}
</TableCell>
</TableRow>
)}
{isPartition && (
<TableRow sx={{ height: 24, borderBottom: 'none' }}>
<TableCell
component="th"
scope="row"
sx={{
color: 'lightblue',
borderBottom: 'none',
pr: 1,
py: 0.5,
fontSize: 13
}}
>
Size
</TableCell>
<TableCell sx={{ borderBottom: 'none', py: 0.5, fontSize: 13 }}>
{size} KB
</TableCell>
</TableRow>
)}
{version?.published_at && (
<TableRow sx={{ height: 24, borderBottom: 'none' }}>
<TableCell
@@ -153,7 +237,7 @@ const VersionInfoDialog = memo(
fontSize: 13
}}
>
Build Date
{isPartition ? 'Install Date' : 'Build Date'}
</TableCell>
<TableCell sx={{ borderBottom: 'none', py: 0.5, fontSize: 13 }}>
{prettyDateTime(locale, new Date(version.published_at))}
@@ -164,15 +248,17 @@ const VersionInfoDialog = memo(
</Table>
</DialogContent>
<DialogActions>
<Button
variant="outlined"
component="a"
href={relNotesUrl}
target="_blank"
color="primary"
>
Changelog
</Button>
{!isPartition && (
<Button
variant="outlined"
component="a"
href={relNotesUrl}
target="_blank"
color="primary"
>
Changelog
</Button>
)}
<Button variant="outlined" onClick={onClose} color="secondary">
{LL.CLOSE()}
</Button>
@@ -188,6 +274,7 @@ const InstallDialog = memo(
fetchDevVersion,
latestVersion,
latestDevVersion,
upgradeImportantMessageType,
downloadOnly,
platform,
LL,
@@ -198,6 +285,7 @@ const InstallDialog = memo(
fetchDevVersion: boolean;
latestVersion?: VersionInfo;
latestDevVersion?: VersionInfo;
upgradeImportantMessageType: number;
downloadOnly: boolean;
platform: string;
LL: TranslationFunctions;
@@ -218,15 +306,36 @@ const InstallDialog = memo(
return (
<Dialog sx={dialogStyle} open={openInstallDialog} onClose={onClose}>
<DialogTitle>
{`${LL.UPDATE()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`}
{`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`}
</DialogTitle>
<DialogContent dividers>
<Typography mb={2}>
<Typography sx={{ mb: 2 }}>
{LL.INSTALL_VERSION(
downloadOnly ? LL.DOWNLOAD(1) : LL.INSTALL(),
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
)}
</Typography>
{upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()}
{upgradeImportantMessageType === 1 && (
<>
{LL.UPGRADE_IMPORTANT_MESSAGES_1()}
<Typography sx={{ mt: 2 }}>
<Link to="/settings/downloadUpload" style={{ color: 'lightblue' }}>
{LL.DOWNLOAD_SYSTEM_BACKUP()}
</Link>
</Typography>
</>
)}
<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
@@ -243,7 +352,12 @@ const InstallDialog = memo(
onClick={onClose}
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)}
</Link>
</Button>
@@ -263,6 +377,56 @@ const InstallDialog = memo(
}
);
const InstallPartitionDialog = memo(
({
openInstallPartitionDialog,
version,
partition,
LL,
onClose,
onInstall
}: {
openInstallPartitionDialog: boolean;
version: string;
partition: string;
LL: TranslationFunctions;
onClose: () => void;
onInstall: (partition: string) => void;
}) => {
return (
<Dialog sx={dialogStyle} open={openInstallPartitionDialog} onClose={onClose}>
<DialogTitle>
{LL.INSTALL()} {LL.STORED_VERSIONS()}
</DialogTitle>
<DialogContent dividers>
<Typography sx={{ mb: 2 }}>
{LL.INSTALL_VERSION(LL.INSTALL(), version)}
</Typography>
</DialogContent>
<DialogActions>
<Button
startIcon={<CancelIcon />}
variant="outlined"
onClick={onClose}
color="secondary"
>
{LL.CANCEL()}
</Button>
<Button
startIcon={<WarningIcon color="warning" />}
variant="outlined"
onClick={() => onInstall(partition)}
color="primary"
>
{LL.INSTALL()}
</Button>
</DialogActions>
</Dialog>
);
}
);
// Helper function moved outside component
const getPlatform = (data: VersionData): string => {
return `${data.esp_platform}-${data.flash_chip_size >= 16384 ? '16MB' : '4MB'}${data.psram ? '+' : ''}`;
@@ -275,6 +439,14 @@ const Version = () => {
// State management
const [restarting, setRestarting] = useState<boolean>(false);
const [openInstallDialog, setOpenInstallDialog] = useState<boolean>(false);
const [partitionVersion, setPartitionVersion] = useState<VersionInfo | undefined>(
undefined
);
const [partition, setPartition] = useState<string>('');
const [openInstallPartitionDialog, setOpenInstallPartitionDialog] =
useState<boolean>(false);
const [usingDevVersion, setUsingDevVersion] = useState<boolean>(false);
const [fetchDevVersion, setFetchDevVersion] = useState<boolean>(false);
const [devUpgradeAvailable, setDevUpgradeAvailable] = useState<boolean>(false);
@@ -282,7 +454,8 @@ const Version = () => {
useState<boolean>(false);
const [internetLive, setInternetLive] = useState<boolean>(false);
const [downloadOnly, setDownloadOnly] = useState<boolean>(false);
const [showVersionInfo, setShowVersionInfo] = useState<number>(0);
const [showVersionInfo, setShowVersionInfo] = useState<number>(0); // 1 = stable, 2 = dev, 3 = partition
const [firmwareSize, setFirmwareSize] = useState<number>(0);
const { send: sendCheckUpgrade } = useRequest(
(versions: string) => callAction({ action: 'checkUpgrade', param: versions }),
@@ -293,6 +466,13 @@ const Version = () => {
setStableUpgradeAvailable(data.stable_upgradeable);
});
const { send: sendSetPartition } = useRequest(
(partition: string) => callAction({ action: 'setPartition', param: partition }),
{ immediate: false }
).onError((error) => {
toast.error(String(error.error?.message || 'An error occurred'));
});
const {
data,
send: loadData,
@@ -317,20 +497,60 @@ const Version = () => {
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
const platform = useMemo(() => (data ? getPlatform(data) : ''), [data]);
const isDev = useMemo(
() => data?.emsesp_version.includes('dev') ?? false,
[data?.emsesp_version]
// Memoize filtered partitions to avoid recomputing on every render
const otherPartitions = useMemo(
() => data?.partitions.filter((p) => p.partition !== data.partition) ?? [],
[data]
);
const setPartitionVersionInfo = useCallback(
(partition: string) => {
setShowVersionInfo(3);
// search for the partition in the data.partitions array
const partitionData = data?.partitions.find((p) => p.partition === partition);
if (partitionData) {
setPartitionVersion({
name: partitionData.version,
published_at: partitionData.install_date ?? ''
});
setPartition(partitionData.partition);
setFirmwareSize(partitionData.size);
}
},
[data]
);
const doRestart = useCallback(async () => {
setRestarting(true);
await sendAPI({ device: 'system', cmd: 'restart', id: 0 }).catch(
(error: Error) => {
toast.error(error.message);
}
);
setRestarting(true);
}, [sendAPI]);
const installFirmwareURL = useCallback(
@@ -338,27 +558,60 @@ const Version = () => {
await sendUploadURL(url).catch((error: Error) => {
toast.error(error.message);
});
setRestarting(true);
await doRestart();
},
[sendUploadURL]
[sendUploadURL, doRestart]
);
const showFirmwareDialog = useCallback((useDevVersion: boolean) => {
setFetchDevVersion(useDevVersion);
setOpenInstallDialog(true);
}, []);
const installPartitionFirmware = useCallback(
async (partition: string) => {
await sendSetPartition(partition).catch((error: Error) => {
toast.error(error.message);
});
setRestarting(true);
},
[sendSetPartition]
);
const showPartitionDialog = useCallback(
(version: string, partition: string, install_date: string) => {
setOpenInstallPartitionDialog(true);
setPartitionVersion({ name: version, published_at: install_date });
setPartition(partition);
},
[]
);
const showFirmwareDialog = useCallback(
(useDevVersion: boolean) => {
setFetchDevVersion(useDevVersion);
void checkUpgradeImportantMessages(
useDevVersion ? latestDevVersion?.name : latestVersion?.name
);
setOpenInstallDialog(true);
},
[latestDevVersion, latestVersion, fetchDevVersion]
);
const closeInstallDialog = useCallback(() => {
setOpenInstallDialog(false);
}, []);
const handleVersionInfoClose = useCallback(() => {
setShowVersionInfo(0);
const closeInstallPartitionDialog = useCallback(() => {
setOpenInstallPartitionDialog(false);
}, []);
// Effect for checking upgrades
const handleVersionInfoClose = useCallback(() => {
setShowVersionInfo(0);
setPartitionVersion(undefined);
setPartition('');
}, []);
// check upgrades - only once when both versions are available
const upgradeCheckedRef = useRef(false);
useEffect(() => {
if (latestVersion && latestDevVersion) {
if (latestVersion && latestDevVersion && !upgradeCheckedRef.current) {
upgradeCheckedRef.current = true;
const versions = `${latestDevVersion.name},${latestVersion.name}`;
sendCheckUpgrade(versions)
.catch((error: Error) => {
@@ -398,7 +651,7 @@ const Version = () => {
{LL.LATEST_VERSION(usingDevVersion ? LL.DEVELOPMENT() : LL.STABLE())}
</span>
<Button
sx={{ ml: 2 }}
sx={{ ml: 1 }}
variant="outlined"
size="small"
onClick={() => showFirmwareDialog(showingDev)}
@@ -413,7 +666,7 @@ const Version = () => {
return (
<Button
sx={{ ml: 2 }}
sx={{ ml: 1 }}
variant="outlined"
color={choice === LL.UPDATE_AVAILABLE() ? 'success' : 'warning'}
size="small"
@@ -440,15 +693,14 @@ const Version = () => {
return (
<>
<Box p={2} border="1px solid grey" borderRadius={2}>
<Typography mb={2} variant="h6" color="primary">
<Box sx={{ p: 2, border: '1px solid #565656', borderRadius: 2 }}>
<Typography sx={{ mb: 1 }} variant="h6" color="primary">
{LL.THIS_VERSION()}
</Typography>
<Grid
container
direction="row"
rowSpacing={1}
sx={{
justifyContent: 'flex-start',
alignItems: 'baseline'
@@ -465,6 +717,12 @@ const Version = () => {
&nbsp; &#40;{data.build_flags}&#41;
</Typography>
)}
<IconButton
onClick={() => setPartitionVersionInfo(data.partition)}
aria-label={LL.FIRMWARE_VERSION_INFO()}
>
<InfoOutlinedIcon color="primary" sx={{ fontSize: 18 }} />
</IconButton>
</Typography>
</Grid>
@@ -497,57 +755,11 @@ const Version = () => {
</Typography>
</Typography>
</Grid>
<Grid size={{ xs: 4, md: 2 }}>
<Typography color="secondary">{LL.RELEASE_TYPE()}</Typography>
</Grid>
<Grid size={{ xs: 8, md: 10 }}>
<FormControlLabel
disabled={!isDev}
control={
<Checkbox
sx={{
'&.Mui-checked': {
color: 'lightblue'
}
}}
/>
}
slotProps={{
typography: {
color: 'grey'
}
}}
checked={!isDev}
label={LL.STABLE()}
sx={{ '& .MuiSvgIcon-root': { fontSize: 16 } }}
/>
<FormControlLabel
disabled={isDev}
control={
<Checkbox
sx={{
'&.Mui-checked': {
color: 'lightblue'
}
}}
/>
}
slotProps={{
typography: {
color: 'grey'
}
}}
checked={isDev}
label={LL.DEVELOPMENT()}
sx={{ '& .MuiSvgIcon-root': { fontSize: 16 } }}
/>
</Grid>
</Grid>
{internetLive ? (
<>
<Typography mt={2} mb={2} variant="h6" color="primary">
<Typography sx={{ mt: 4, mb: 1 }} variant="h6" color="primary">
{LL.AVAILABLE_VERSION()}
</Typography>
@@ -560,6 +772,47 @@ const Version = () => {
alignItems: 'baseline'
}}
>
{otherPartitions.length > 0 && data.developer_mode && (
<>
<Grid size={{ xs: 4, md: 2 }}>
<Typography color="secondary">
{LL.STORED_VERSIONS()}
</Typography>
</Grid>
<Grid size={{ xs: 8, md: 10 }}>
{otherPartitions.map((partition) => (
<Typography key={partition.partition} sx={{ mb: 1 }}>
{partition.version}
<IconButton
onClick={() =>
setPartitionVersionInfo(partition.partition)
}
aria-label={LL.FIRMWARE_VERSION_INFO()}
>
<InfoOutlinedIcon
color="primary"
sx={{ fontSize: 18 }}
/>
</IconButton>
<Button
sx={{ ml: 0 }}
variant="outlined"
size="small"
onClick={() =>
showPartitionDialog(
partition.version,
partition.partition,
partition.install_date ?? ''
)
}
>
{LL.INSTALL()}
</Button>
</Typography>
))}
</Grid>
</>
)}
<Grid size={{ xs: 4, md: 2 }}>
<Typography color="secondary">{LL.STABLE()}</Typography>
</Grid>
@@ -594,7 +847,7 @@ const Version = () => {
</Grid>
</>
) : (
<Typography mt={2} color="warning">
<Typography sx={{ mt: 2 }} color="warning">
<WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} />
{LL.INTERNET_CONNECTION_REQUIRED()}
</Typography>
@@ -605,7 +858,11 @@ const Version = () => {
showVersionInfo={showVersionInfo}
latestVersion={latestVersion}
latestDevVersion={latestDevVersion}
partitionVersion={partitionVersion}
locale={locale}
partition={partition}
currentPartition={data?.partition ?? ''}
size={firmwareSize}
LL={LL}
onClose={handleVersionInfoClose}
/>
@@ -614,16 +871,25 @@ const Version = () => {
fetchDevVersion={fetchDevVersion}
latestVersion={latestVersion}
latestDevVersion={latestDevVersion}
upgradeImportantMessageType={upgradeImportantMessageType}
downloadOnly={downloadOnly}
platform={platform}
LL={LL}
onClose={closeInstallDialog}
onInstall={installFirmwareURL}
/>
<InstallPartitionDialog
openInstallPartitionDialog={openInstallPartitionDialog}
version={partitionVersion?.name || ''}
partition={partition}
LL={LL}
onClose={closeInstallPartitionDialog}
onInstall={installPartitionFirmware}
/>
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
{LL.UPLOAD()}
</Typography>
<SingleUpload text={LL.UPLOAD_DROP_TEXT()} doRestart={doRestart} />
<SingleUpload doRestart={doRestart} />
</>
)}
</Box>
@@ -635,7 +901,6 @@ const Version = () => {
loadData,
LL,
platform,
isDev,
internetLive,
latestVersion,
latestDevVersion,
@@ -649,10 +914,18 @@ const Version = () => {
handleVersionInfoClose,
closeInstallDialog,
installFirmwareURL,
doRestart
doRestart,
otherPartitions,
setPartitionVersionInfo,
showPartitionDialog,
partitionVersion,
partition,
firmwareSize,
closeInstallPartitionDialog,
installPartitionFirmware
]);
return <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
};
export default memo(Version);

View File

@@ -1,9 +1,9 @@
import { memo } from 'react';
import { type FC, type PropsWithChildren, memo } from 'react';
import { Box } from '@mui/material';
import type { BoxProps } from '@mui/material';
const ButtonRow = memo<BoxProps>(({ children, ...rest }) => (
const ButtonRow: FC<PropsWithChildren<BoxProps>> = memo(({ children, ...rest }) => (
<Box
sx={{
'& button, & a, & .MuiCard-root': {

View File

@@ -1,4 +1,4 @@
import { type FC, memo, useMemo } from 'react';
import { type FC, type PropsWithChildren, memo, useMemo } from 'react';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
import ErrorIcon from '@mui/icons-material/Error';
@@ -12,6 +12,7 @@ type MessageBoxLevel = 'warning' | 'success' | 'info' | 'error';
export interface MessageBoxProps extends BoxProps {
level: MessageBoxLevel;
message?: string;
children?: React.ReactNode;
}
const LEVEL_ICONS: Record<MessageBoxLevel, React.ComponentType<SvgIconProps>> = {
@@ -28,7 +29,7 @@ const LEVEL_PALETTE_PATHS: Record<MessageBoxLevel, string> = {
error: 'error.dark'
};
const MessageBox: FC<MessageBoxProps> = ({
const MessageBox: FC<PropsWithChildren<MessageBoxProps>> = ({
level,
message,
sx,
@@ -52,12 +53,16 @@ const MessageBox: FC<MessageBoxProps> = ({
return (
<Box
p={2}
display="flex"
alignItems="center"
borderRadius={1}
sx={{ backgroundColor, color: 'white', ...sx }}
{...rest}
sx={{
display: 'flex',
alignItems: 'center',
borderRadius: 1,
backgroundColor,
color: 'white',
p: 2,
...sx
}}
>
<Icon />
{(message || children) && (

View File

@@ -19,10 +19,8 @@ import { I18nContext } from 'i18n/i18n-react';
import type { Locales } from 'i18n/i18n-types';
import { loadLocaleAsync } from 'i18n/i18n-util.async';
// Extract style to constant to prevent recreation
const flagStyle: CSSProperties = { width: 16, verticalAlign: 'middle' };
// Define language options outside component to prevent recreation
interface LanguageOption {
key: Locales;
flag: string;

View File

@@ -15,6 +15,7 @@ export type ValidatedTextFieldProps = ValidatedFieldProps & TextFieldProps;
const ValidatedTextField: FC<ValidatedTextFieldProps> = ({
fieldErrors,
sx,
...rest
}) => {
const errors = fieldErrors?.[rest.name];
@@ -25,11 +26,23 @@ const ValidatedTextField: FC<ValidatedTextFieldProps> = ({
error={!!errors}
{...rest}
aria-label="Error"
slotProps={{
inputLabel: {
style: rest.disabled ? { color: 'grey' } : undefined
}
sx={{
'& .MuiInputBase-input.Mui-disabled': {
WebkitTextFillColor: 'grey'
},
...(sx || {})
}}
{...(rest.disabled && {
slotProps: {
select: {
IconComponent: () => null
},
inputLabel: {
style: { color: 'grey' }
}
}
})}
color={rest.disabled ? 'secondary' : 'primary'}
/>
{errors?.map((e) => (
<FormHelperText key={e.message} sx={{ color: 'rgb(250, 95, 84)' }}>

View File

@@ -29,7 +29,7 @@ const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
() => (
<>
<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} />
<Typography variant="h6">{PROJECT_NAME}</Typography>
</Box>

View File

@@ -51,9 +51,7 @@ const LayoutMenuComponent = () => {
sx={{ my: 0 }}
slotProps={{
primary: {
fontWeight: '600',
mb: '2px',
color: 'lightblue'
sx: { fontWeight: 600, mb: '2px', color: 'lightblue' }
}
}}
/>

View File

@@ -39,9 +39,8 @@ const LayoutMenuItemComponent = ({
left: 0,
top: 0,
bottom: 0,
width: selected ? '4px' : '0px',
width: selected ? '3px' : '0px',
backgroundColor: '#90caf9',
borderRadius: '0 2px 2px 0',
transition: 'width 0.05s cubic-bezier(0.55, 0.085, 0.68, 0.53)'
}
}),

View File

@@ -22,7 +22,6 @@ interface ListMenuItemProps {
disabled?: boolean;
}
// Extract styles to prevent recreation
const iconStyles: CSSProperties = {
justifyContent: 'right',
color: 'lightblue',

View File

@@ -16,7 +16,7 @@ const FormLoaderComponent = ({ errorMessage, onRetry }: FormLoaderProps) => {
if (errorMessage) {
return (
<MessageBox my={2} level="error" message={errorMessage}>
<MessageBox level="error" message={errorMessage}>
{onRetry && (
<Button
sx={{ ml: 2 }}
@@ -32,8 +32,16 @@ const FormLoaderComponent = ({ errorMessage, onRetry }: FormLoaderProps) => {
);
}
return (
<Box m={2} py={2} display="flex" alignItems="center" flexDirection="column">
<Box py={2}>
<Box
sx={{
m: 2,
py: 2,
display: 'flex',
alignItems: 'center',
flexDirection: 'column'
}}
>
<Box sx={{ p: 2 }}>
<CircularProgress size={100} />
</Box>
</Box>

View File

@@ -3,7 +3,6 @@ import { memo } from 'react';
import { Box, CircularProgress } from '@mui/material';
import type { SxProps, Theme } from '@mui/material';
// Extract styles to prevent recreation on every render
const containerStyles: SxProps<Theme> = {
display: 'flex',
justifyContent: 'center',

View File

@@ -7,7 +7,6 @@ interface LoadingSpinnerProps {
height?: number | string;
}
// Extract styles to prevent recreation on every render
const circularProgressStyles: SxProps<Theme> = (theme: Theme) => ({
margin: theme.spacing(4),
color: theme.palette.text.secondary
@@ -16,12 +15,14 @@ const circularProgressStyles: SxProps<Theme> = (theme: Theme) => ({
const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => {
return (
<Box
display="flex"
alignItems="center"
justifyContent="center"
flexDirection="column"
padding={2}
height={height}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
padding: 2,
height
}}
>
<CircularProgress sx={circularProgressStyles} size={100} />
</Box>

View File

@@ -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 {
type ChangeEvent,
type DragEvent,
@@ -6,12 +6,28 @@ import {
useRef,
useState
} from 'react';
import { Link } from 'react-router';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
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';
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
@@ -58,6 +74,29 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const [dragged, setDragged] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);
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 validExtensions = ['.json', '.bin', '.md5'];
@@ -97,9 +136,8 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (file) {
onFileSelected(file);
}
void checkUpgradeImportantMessages(file?.name || '');
setShowUpgradeDialog(true);
};
const handleBrowseClick = () => {
@@ -158,6 +196,56 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
{LL.UPLOAD()}
</Button>
</Box>
{showUpgradeDialog && upgradeImportantMessageType > 0 && (
<Dialog
sx={dialogStyle}
open={showUpgradeDialog}
onClose={() => setShowUpgradeDialog(false)}
>
<DialogTitle>
<WarningIcon
color="warning"
sx={{ fontSize: 18, verticalAlign: 'middle' }}
/>
&nbsp;&nbsp;
{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>

View File

@@ -13,11 +13,10 @@ import DragNdrop from './DragNdrop';
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
interface SingleUploadProps {
text: string;
doRestart: () => void;
}
const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
const SingleUpload = ({ doRestart }: SingleUploadProps) => {
const [md5, setMd5] = useState<string>();
const [file, setFile] = useState<File>();
const { LL } = useI18nContext();
@@ -58,7 +57,7 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
<>
{isUploading ? (
<>
<Box width="100%" pl={2} pr={2}>
<Box sx={{ width: '100%', pl: 2, pr: 2 }}>
<LinearProgressWithLabel
value={
progress.total === 0 || progress.loaded === 0
@@ -81,11 +80,11 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
</Button>
</>
) : (
<DragNdrop text={text} onFileSelected={setFile} />
<DragNdrop text={LL.UPLOAD_DROP_TEXT()} onFileSelected={setFile} />
)}
{md5 && (
<Box mt={2}>
<Box sx={{ mt: 2 }}>
<Typography variant="body2">{'MD5: ' + md5}</Typography>
</Box>
)}

View File

@@ -186,7 +186,8 @@ const cz: Translation = {
BUFFER_SIZE: 'Maximální velikost vyrovnávací paměti',
COMPACT: 'Kompaktní',
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_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',
@@ -219,6 +220,7 @@ const cz: Translation = {
MQTT_PUBLISH_TEXT_3: 'Povolit MQTT Discovery',
MQTT_PUBLISH_TEXT_4: 'Prefix pro Discovery témata',
MQTT_PUBLISH_TEXT_5: 'Typ Discovery',
MQTT_INPUT_NUMBER_FORMAT: 'Formát čísla',
MQTT_PUBLISH_INTERVALS: 'Intervaly publikování',
MQTT_INT_BOILER: 'Kotle a tepelná čerpadla',
MQTT_INT_THERMOSTATS: 'Termostaty',
@@ -348,14 +350,22 @@ const cz: Translation = {
NO_DATA_1: 'Nebyly nalezeny žádné oblíbené entity. Použijte modul',
NO_DATA_2: 'pro jejich výběr.',
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',
PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ sestavení',
INTERNET_CONNECTION_REQUIRED: 'Pro automatickou kontrolu a instalaci aktualizací je třeba internetové připojení',
SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi',
FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru',
NO_DATA: 'Žádná data',
USER_PROFILE: 'Uživatelský profil'
NO_DATA: 'žádné údaje',
USER_PROFILE: 'Uživatelský profil',
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;

View File

@@ -186,7 +186,8 @@ const de: Translation = {
BUFFER_SIZE: 'Max. Puffergröße',
COMPACT: 'Kompakte Darstellung',
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: 'Systemsicherung',
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',
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
@@ -219,6 +220,7 @@ const de: Translation = {
MQTT_PUBLISH_TEXT_3: 'Aktiviere `MQTT Discovery`',
MQTT_PUBLISH_TEXT_4: 'Prefix für die `Discovery`-Topics',
MQTT_PUBLISH_TEXT_5: 'Discovery Typ',
MQTT_INPUT_NUMBER_FORMAT: 'Zahlenformat',
MQTT_PUBLISH_INTERVALS: 'Veröffentlichungs-Intervalle',
MQTT_INT_BOILER: 'Boiler und Wärmepumpen',
MQTT_INT_THERMOSTATS: 'Thermostate',
@@ -348,6 +350,7 @@ const de: Translation = {
NO_DATA_1: 'Keine favorisierten EMS-Entitäten gefunden! Verwenden Sie das Modul',
NO_DATA_2: ', um sie zu markieren.',
NO_DATA_3: 'Um alle verfügbaren Entitäten anzuzeigen, gehen Sie zu',
NO_GPIO: 'Keine freien GPIO gefunden',
THIS_VERSION: 'Diese Version',
PLATFORM: 'Plattform',
RELEASE_TYPE: 'Release Typ',
@@ -355,7 +358,14 @@ const de: Translation = {
SWITCH_RELEASE_TYPE: 'Zum {0}-Release wechseln',
FIRMWARE_VERSION_INFO: 'Firmware-Versionsinformation',
NO_DATA: 'Keine Daten',
USER_PROFILE: 'Benutzerprofil'
USER_PROFILE: 'Benutzerprofil',
STORED_VERSIONS: 'Gespeicherte Versionen',
ONLINE_HELP: 'Online-Hilfe',
UPGRADE_IMPORTANT_MESSAGES: 'Wichtige Nachrichten aktualisieren',
UPGRADE_IMPORTANT_MESSAGES_1: 'Für diese Aktualisierung ist ein Werksreset erforderlich. 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 in dieser Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?'
};
export default de;

View File

@@ -186,7 +186,8 @@ const en: Translation = {
BUFFER_SIZE: 'Max Buffer Size',
COMPACT: 'Compact',
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_DROP_TEXT: 'Drop a firmware .bin file or click here',
ERROR: 'Unexpected Error, please try again',
@@ -219,6 +220,7 @@ const en: Translation = {
MQTT_PUBLISH_TEXT_3: 'Enable MQTT Discovery',
MQTT_PUBLISH_TEXT_4: 'Prefix for the Discovery topics',
MQTT_PUBLISH_TEXT_5: 'Discovery type',
MQTT_INPUT_NUMBER_FORMAT: 'Input Number Fomat',
MQTT_PUBLISH_INTERVALS: 'Publish Intervals',
MQTT_INT_BOILER: 'Boilers and Heat Pumps',
MQTT_INT_THERMOSTATS: 'Thermostats',
@@ -348,6 +350,7 @@ const en: Translation = {
NO_DATA_1: 'No favorite EMS entities found yet. Use the',
NO_DATA_2: 'module to mark them.',
NO_DATA_3: 'To see all available entities go to',
NO_GPIO: 'No available GPIO found',
THIS_VERSION: 'This Version',
PLATFORM: 'Platform',
RELEASE_TYPE: 'Release Type',
@@ -355,7 +358,14 @@ const en: Translation = {
SWITCH_RELEASE_TYPE: 'Switch to {0} release',
FIRMWARE_VERSION_INFO: 'Firmware Version Information',
NO_DATA: 'No data',
USER_PROFILE: 'User Profile'
USER_PROFILE: 'User Profile',
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;

View File

@@ -186,7 +186,8 @@ const fr: Translation = {
BUFFER_SIZE: 'Max taille du buffer',
COMPACT: 'Compact',
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_DROP_TEXT: 'Drop a firmware .bin file or click here',
ERROR: 'Erreur inattendue, veuillez réessayer',
@@ -219,6 +220,7 @@ const fr: Translation = {
MQTT_PUBLISH_TEXT_3: 'Activer la découverte MQTT',
MQTT_PUBLISH_TEXT_4: 'Préfixe pour les topics découverte',
MQTT_PUBLISH_TEXT_5: 'Type de découverte',
MQTT_INPUT_NUMBER_FORMAT: 'Format des nombres',
MQTT_PUBLISH_INTERVALS: 'Intervalles de publication',
MQTT_INT_BOILER: 'Chaudières et pompes à chaleur',
MQTT_INT_THERMOSTATS: 'Thermostats',
@@ -348,6 +350,7 @@ const fr: Translation = {
NO_DATA_1: 'Aucune entité EMS favorite trouvée. Utilisez le',
NO_DATA_2: 'module pour les marquer.',
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',
PLATFORM: 'Plateforme',
RELEASE_TYPE: 'Type de version',
@@ -355,7 +358,14 @@ const fr: Translation = {
SWITCH_RELEASE_TYPE: 'Passer à la version {0}',
FIRMWARE_VERSION_INFO: 'Informations sur la version du firmware',
NO_DATA: 'Aucune donnée',
USER_PROFILE: 'Profil utilisateur'
USER_PROFILE: 'Profil utilisateur',
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;

View File

@@ -186,7 +186,8 @@ const it: Translation = {
BUFFER_SIZE: 'Max Buffer Size',
COMPACT: 'Compatto',
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_DROP_TEXT: 'Drop a firmware .bin file or click here',
ERROR: 'Errore Inaspettato, prego tenta ancora',
@@ -219,6 +220,7 @@ const it: Translation = {
MQTT_PUBLISH_TEXT_3: 'Abilita rilevamento MQTT (Home Assistant, Domoticz)',
MQTT_PUBLISH_TEXT_4: 'Prefisso per gli argomenti di scoperta',
MQTT_PUBLISH_TEXT_5: 'Discovery type',
MQTT_INPUT_NUMBER_FORMAT: 'Formato numero di input',
MQTT_PUBLISH_INTERVALS: 'Pubblica intervalli',
MQTT_INT_BOILER: 'Caldaie e Pompe di Calore',
MQTT_INT_THERMOSTATS: 'Termostati',
@@ -348,6 +350,7 @@ const it: Translation = {
NO_DATA_1: 'Nessuna entità EMS preferita trovata. Usa il',
NO_DATA_2: 'modulo per marcarle.',
NO_DATA_3: 'Per vedere tutte le entità disponibili vai a',
NO_GPIO: 'Non è stato trovato alcun GPIO disponibile',
THIS_VERSION: 'Questa versione',
PLATFORM: 'Piattaforma',
RELEASE_TYPE: 'Tipo di rilascio',
@@ -355,7 +358,14 @@ const it: Translation = {
SWITCH_RELEASE_TYPE: 'Cambia in {0} rilascio',
FIRMWARE_VERSION_INFO: 'Informazioni sulla versione del firmware',
NO_DATA: 'Nessun dato',
USER_PROFILE: 'Profilo utente'
USER_PROFILE: 'Profilo utente',
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;

View File

@@ -27,7 +27,7 @@ const nl: Translation = {
REFRESH: 'Ververs',
EXPORT: 'Export',
FAVORITES: "Favorieten",
DEVICE_DETAILS: 'Device Gegevens',
DEVICE_DETAILS: 'Apparaat Gegevens',
ID_OF: '{0} ID',
DEVICE: 'Apparaat',
PRODUCT: 'Product',
@@ -65,7 +65,7 @@ const nl: Translation = {
TEMP_SENSOR: 'Temperatuur sensor',
TEMP_SENSORS: 'Temperatuur Sensoren',
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...',
CONNECTED: 'Verbonden',
TX_ISSUES: 'Tx bus probleem. Probeer een andere Tx verzendmodus',
@@ -75,7 +75,7 @@ const nl: Translation = {
EMS_DEVICE: 'EMS Apparaat',
SUCCESS: 'SUCCESS',
FAIL: 'MISLUKT',
QUALITY: 'QUALITEIT',
QUALITY: 'KWALITEIT',
SCAN: 'Scan',
STATUS_NAMES: [
'EMS Telegrammen ontvangen (Rx)',
@@ -120,7 +120,7 @@ const nl: Translation = {
ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)',
ENABLE_SHOWER_ALERT: 'Activeer Douchemelding',
TRIGGER_TIME: 'Trigger tijd',
COLD_SHOT_DURATION: 'Tijd Shot koud water',
COLD_SHOT_DURATION: 'Lengte koud water puls',
FORMATTING_OPTIONS: 'Formatteringsopties',
BOOLEAN_FORMAT_DASHBOARD: 'Boolean formaat web',
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_SAVED: 'Custom aanpassingen opgeslagen',
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_4: 'Uitsluiten van MQTT en API',
CUSTOMIZATIONS_HELP_5: 'verbergen voor apparaten',
@@ -186,12 +186,13 @@ const nl: Translation = {
BUFFER_SIZE: 'Max buffer grootte',
COMPACT: 'Compact',
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_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier',
ERROR: 'Onverwachte fout, probeer opnieuw',
TIME_SET: 'Tijd ingesteld',
MANAGE_USERS: 'Beheer Gebruikers',
MANAGE_USERS: 'Gebruikersbeheer',
IS_ADMIN: 'is Admin',
USER_WARNING: 'U dient tenminste 1 admin gebruiker te configureren',
ADD: 'Toevoegen',
@@ -200,7 +201,7 @@ const nl: Translation = {
GENERATING_TOKEN: 'Token aan het genereren',
USER: 'Gebruiker',
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',
ERRORS_OF: '{0} Foutmeldingen',
DISCONNECT_REASON: 'Verbinding verbroken vanwege',
@@ -219,6 +220,7 @@ const nl: Translation = {
MQTT_PUBLISH_TEXT_3: 'Activeer MQTT Discovery',
MQTT_PUBLISH_TEXT_4: 'Prefix voor de Discovery topics',
MQTT_PUBLISH_TEXT_5: 'Discovery type',
MQTT_INPUT_NUMBER_FORMAT: 'Getalformaat',
MQTT_PUBLISH_INTERVALS: 'Publicatie intervallen',
MQTT_INT_BOILER: 'CV ketels en warmtepompen',
MQTT_INT_THERMOSTATS: 'Thermostaten',
@@ -348,6 +350,7 @@ const nl: Translation = {
NO_DATA_1: 'Er zijn nog geen favoriete EMS-entiteiten gevonden. Gebruik de',
NO_DATA_2: 'module om ze te markeren.',
NO_DATA_3: 'Om alle beschikbare entiteiten te zien, ga naar',
NO_GPIO: 'Er is geen beschikbare GPIO gevonden',
THIS_VERSION: 'Deze Versie',
PLATFORM: 'Platform',
RELEASE_TYPE: 'Release Typ',
@@ -355,7 +358,14 @@ const nl: Translation = {
SWITCH_RELEASE_TYPE: 'Switch naar {0} release',
FIRMWARE_VERSION_INFO: 'Informatie over firmwareversie',
NO_DATA: 'Geen data',
USER_PROFILE: 'Gebruikersprofiel'
USER_PROFILE: 'Gebruikersprofiel',
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;

View File

@@ -186,7 +186,8 @@ const no: Translation = {
BUFFER_SIZE: 'Max Buffer Størrelse',
COMPACT: 'Komprimere',
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_DROP_TEXT: 'Dropp en firmware fil (.bin) eller klikk her',
ERROR: 'Ukjent feil, prøv igjen',
@@ -219,6 +220,7 @@ const no: Translation = {
MQTT_PUBLISH_TEXT_3: 'Aktiver MQTT Discovery',
MQTT_PUBLISH_TEXT_4: 'Prefiks for Discovery topics',
MQTT_PUBLISH_TEXT_5: 'Discovery type',
MQTT_INPUT_NUMBER_FORMAT: 'Nummerformat',
MQTT_PUBLISH_INTERVALS: 'Publiseringsintervall',
MQTT_INT_BOILER: 'Fyr/Varmepumpe',
MQTT_INT_THERMOSTATS: 'Termostat',
@@ -348,6 +350,7 @@ const no: Translation = {
NO_DATA_1: 'Ingen favoritte EMS enheter funnet enda. Bruk',
NO_DATA_2: 'modul for å markere dem.',
NO_DATA_3: 'For å se alle tilgjengelige enheter, gå til',
NO_GPIO: 'Det ble ikke funnet noen tilgjengelige GPIO-porter',
THIS_VERSION: 'Denne versjonen',
PLATFORM: 'Plattform',
RELEASE_TYPE: 'Utgivelses type',
@@ -355,7 +358,14 @@ const no: Translation = {
SWITCH_RELEASE_TYPE: 'Bytt til {0} utgivelse',
FIRMWARE_VERSION_INFO: 'Informasjon om firmwareversjon',
NO_DATA: 'Ingen data',
USER_PROFILE: 'Brukerprofil'
USER_PROFILE: 'Brukerprofil',
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;

View File

@@ -186,7 +186,8 @@ const pl: BaseTranslation = {
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
COMPACT: 'Kompaktowy',
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_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj',
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
@@ -219,6 +220,7 @@ const pl: BaseTranslation = {
MQTT_PUBLISH_TEXT_3: 'Włącz opcję "MQTT discovery"',
MQTT_PUBLISH_TEXT_4: 'Prefiks dla "MQTT discovery"',
MQTT_PUBLISH_TEXT_5: 'Typ "MQTT discovery"',
MQTT_INPUT_NUMBER_FORMAT: 'Format liczby',
MQTT_PUBLISH_INTERVALS: 'Interwały publikowania',
MQTT_INT_BOILER: 'Kotły i pompy ciepła',
MQTT_INT_THERMOSTATS: 'Termostaty',
@@ -331,7 +333,7 @@ const pl: BaseTranslation = {
SERVICES: 'Usługi',
ALLVALUES: 'Wszystkie wartości',
SPECIAL_FUNCTIONS: 'Specjalne funkcje',
WAIT_FIRMWARE: 'Firma jest wysyłana i instaluje się',
WAIT_FIRMWARE: 'Firmware ściąga się i instaluje',
INSTALL_VERSION: 'To zainstaluje wersję {1} {0}. Jesteś pewny?',
UPDATE_AVAILABLE: 'aktualizacja dostępna',
LATEST_VERSION: 'Jesteś używając najnowszej wersji firmware {0}',
@@ -348,6 +350,7 @@ const pl: BaseTranslation = {
NO_DATA_1: 'Brak ulubionych encji EMS. Użyj',
NO_DATA_2: 'moduł do ich oznaczenia.',
NO_DATA_3: 'Aby zobaczyć wszystkie dostępne encje przejdź do',
NO_GPIO: 'Nie znaleziono dostępnych pinów GPIO',
THIS_VERSION: 'Ta wersja',
PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ wydania',
@@ -355,7 +358,14 @@ const pl: BaseTranslation = {
SWITCH_RELEASE_TYPE: 'Zmień na {0} wydanie',
FIRMWARE_VERSION_INFO: 'Informacje o wersji firmware',
NO_DATA: 'Brak danych',
USER_PROFILE: 'Profil użytkownika'
USER_PROFILE: 'Profil użytkownika',
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;

View File

@@ -4,7 +4,7 @@ const sk: Translation = {
LANGUAGE: 'Jazyk',
RETRY: 'Opakovať',
LOADING: 'Načítanie',
IS_REQUIRED: '{0} je požadovaných',
IS_REQUIRED: '{0} je požadovaná',
SIGN_IN: 'Prihlásiť sa',
SIGN_OUT: 'Odhlásiť sa',
USERNAME: 'Užívateľské meno',
@@ -186,7 +186,8 @@ const sk: Translation = {
BUFFER_SIZE: 'Buffer-max. veľkosť',
COMPACT: 'Kompaktné',
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_DROP_TEXT: 'Presuňte súbor .bin firmvéru alebo kliknite sem',
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
@@ -219,6 +220,7 @@ const sk: Translation = {
MQTT_PUBLISH_TEXT_3: 'Povolenie zisťovania MQTT',
MQTT_PUBLISH_TEXT_4: 'Predpona tém Discovery',
MQTT_PUBLISH_TEXT_5: 'Typ zistenia',
MQTT_INPUT_NUMBER_FORMAT: 'Formát čísla',
MQTT_PUBLISH_INTERVALS: 'Intervaly zverejňovania',
MQTT_INT_BOILER: 'Kotly a tepelné čerpadlá',
MQTT_INT_THERMOSTATS: 'Termostaty',
@@ -274,11 +276,11 @@ const sk: Translation = {
NETWORK_SUBNET: 'Maska podsiete',
NETWORK_DNS: 'DNS servery',
ADDRESS_OF: '{0} adresa',
ADMINISTRATOR: 'Administrator',
ADMINISTRATOR: 'Administrátor',
GUEST: 'Hosť',
NEW: 'Nová',
NEW: 'Novú',
NEW_NAME_OF: 'Nový názov {0}',
ENTITY: 'entita',
ENTITY: 'entitu',
MIN: 'min',
MAX: 'max',
BLOCK_NAVIGATE_1: 'Máte neuložené zmeny',
@@ -348,6 +350,7 @@ const sk: Translation = {
NO_DATA_1: 'Nenašli sa žiadne obľúbené entity EMS. Použite',
NO_DATA_2: 'modul na ich označenie.',
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',
PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ vydania',
@@ -355,7 +358,14 @@ const sk: Translation = {
SWITCH_RELEASE_TYPE: 'Prepnúť na {0} verziu',
FIRMWARE_VERSION_INFO: 'Informácie o verzii firmware',
NO_DATA: 'Žiadne dáta',
USER_PROFILE: 'Profil používateľa'
USER_PROFILE: 'Profil používateľa',
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;

View File

@@ -186,7 +186,8 @@ const sv: Translation = {
BUFFER_SIZE: 'Max bufferstorlek',
COMPACT: 'Komprimerad',
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_DROP_TEXT: 'Droppa en firmware .bin fil eller klicka här',
ERROR: 'Okänt fel, var god försök igen',
@@ -219,6 +220,7 @@ const sv: Translation = {
MQTT_PUBLISH_TEXT_3: 'Aktivera MQTT Discovery',
MQTT_PUBLISH_TEXT_4: 'Prefix för Discovery topics',
MQTT_PUBLISH_TEXT_5: 'Discoverytyp',
MQTT_INPUT_NUMBER_FORMAT: 'Nummerformat',
MQTT_PUBLISH_INTERVALS: 'Publiceringsintervall',
MQTT_INT_BOILER: 'Värmepump/panna',
MQTT_INT_THERMOSTATS: 'Termostater',
@@ -348,6 +350,7 @@ const sv: Translation = {
NO_DATA_1: 'Inga favorit EMS enheter hittade än. Använd',
NO_DATA_2: 'modul för att markera dem.',
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',
PLATFORM: 'Plattform',
RELEASE_TYPE: 'Utgivelsestyp',
@@ -355,7 +358,14 @@ const sv: Translation = {
SWITCH_RELEASE_TYPE: 'Byt till {0} utgåva',
FIRMWARE_VERSION_INFO: 'Information om firmwareversion',
NO_DATA: 'Ingen data',
USER_PROFILE: 'Användarprofil'
USER_PROFILE: 'Användarprofil',
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;

View File

@@ -186,7 +186,8 @@ const tr: Translation = {
BUFFER_SIZE: 'En fazla bellek boyutu',
COMPACT: 'Sıkışık',
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_DROP_TEXT: 'Bir firmware .bin dosyası veya buraya tıklayın',
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
@@ -219,6 +220,7 @@ const tr: Translation = {
MQTT_PUBLISH_TEXT_3: 'MQTT keşfi etkinleştir (Home Assistant, Domoticz)',
MQTT_PUBLISH_TEXT_4: 'Keşif konuları için ön ek',
MQTT_PUBLISH_TEXT_5: 'Domoticz Format',
MQTT_INPUT_NUMBER_FORMAT: 'Sayı Biçimi',
MQTT_PUBLISH_INTERVALS: 'Yayınlama aralıkları',
MQTT_INT_BOILER: 'Kazanlar ve Isı Pompaları',
MQTT_INT_THERMOSTATS: 'Termostatlar',
@@ -348,6 +350,7 @@ const tr: Translation = {
NO_DATA_1: 'Henüz bir favori EMS varlığı bulunamadı. 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_GPIO: 'Kullanılabilir GPIO bulunamadı',
THIS_VERSION: 'Bu Sürüm',
PLATFORM: 'Platforma',
RELEASE_TYPE: 'Sürüm Tipi',
@@ -355,7 +358,14 @@ const tr: Translation = {
SWITCH_RELEASE_TYPE: '{0} sürümüne geç',
FIRMWARE_VERSION_INFO: 'Firmware Sürüm Bilgisi',
NO_DATA: 'Hiçbir veri yok',
USER_PROFILE: 'Kullanıcı Profili'
USER_PROFILE: 'Kullanıcı Profili',
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;

View File

@@ -91,12 +91,12 @@ function ErrorPage() {
<p style={errorPageStyles.message2}>
Please report on{' '}
<a
href="https://docs.emsesp.org/Support"
href="https://emsesp.org/Support"
target="_blank"
rel="noreferrer"
style={{ color: 'inherit', textDecoration: 'underline' }}
>
https://docs.emsesp.org/Support
https://emsesp.org/Support
</a>
</p>
</div>

View File

@@ -49,4 +49,5 @@ export interface MqttSettingsType {
publish_single2cmd: boolean;
discovery_prefix: string;
discovery_type: number;
ha_number_mode: number;
}

View File

@@ -2,13 +2,15 @@ import type { busConnectionStatus } from 'app/main/types';
import type { NetworkConnectionStatus } from './network';
// match SYSTEM_STATUS in System.h
export enum SystemStatusCodes {
SYSTEM_STATUS_NORMAL = 0,
SYSTEM_STATUS_PENDING_UPLOAD = 1,
SYSTEM_STATUS_UPLOADING = 100,
SYSTEM_STATUS_ERROR_UPLOAD = 3,
SYSTEM_STATUS_PENDING_RESTART = 4,
SYSTEM_STATUS_RESTART_REQUESTED = 5
SYSTEM_STATUS_RESTART_REQUESTED = 5,
SYSTEM_STATUS_INVALID_GPIO = 6
}
export interface SystemStatus {
@@ -48,9 +50,17 @@ export interface SystemStatus {
free_psram?: number;
free_caps: number;
model: string;
board: string;
has_loader: boolean;
has_partition: boolean;
status: number; // SystemStatusCodes which matches SYSTEM_STATUS in System.h
partitions: {
partition: string;
version: string;
size: number;
install_date?: string;
}[];
status: number; // System Status Codes which matches SYSTEM_STATUS in System.h
developer_mode: boolean;
temperature?: number;
}

View File

@@ -23,6 +23,8 @@ export const saveFile = (
}, 100);
} catch (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
});
}
};

View File

@@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react';
const DEFAULT_DELAY = 3000;
const DEFAULT_DELAY = 5000;
/**
* Custom hook for setting up an interval with proper cleanup

View File

@@ -60,21 +60,28 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
// Reset states before saving
setRestartNeeded(false);
setErrorMessage(undefined);
setDirtyFlags([]);
setOrigData(data as D);
try {
await writeData(data as D);
// Only update origData on successful save (dirtyFlags cleared by onSuccess handler)
setOrigData(data as D);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (message === REBOOT_ERROR_MESSAGE) {
setRestartNeeded(true);
} else {
toast.error(message);
setErrorMessage(message);
return; // Early return - save succeeded but needs reboot
}
// Restore original data on validation error
if (origData) {
updateData({ data: origData });
}
toast.error(message);
setErrorMessage(message);
setDirtyFlags([]); // Clear flags so user can retry
}
}, [data, writeData]);
}, [data, writeData, origData, updateData]);
return useMemo(
() => ({

View File

@@ -1,6 +1,20 @@
import type { InternalRuleItem, ValidateOption } from 'async-validator';
import type {
InternalRuleItem,
ValidateFieldsError,
ValidateOption
} from 'async-validator';
import type Schema from 'async-validator';
export class ValidationError extends Error {
readonly fieldErrors: ValidateFieldsError;
constructor(fieldErrors: ValidateFieldsError) {
super('Validation failed');
this.name = 'ValidationError';
this.fieldErrors = fieldErrors;
}
}
export const validate = <T extends object>(
validator: Schema,
source: Partial<T>,
@@ -8,7 +22,7 @@ export const validate = <T extends object>(
): Promise<T> =>
new Promise((resolve, reject) => {
void validator.validate(source, options ?? {}, (errors, fieldErrors) => {
errors ? reject(fieldErrors as Error) : resolve(source as T);
errors ? reject(new ValidationError(fieldErrors)) : resolve(source as T);
});
});

Some files were not shown because too many files have changed in this diff Show More