306 Commits

Author SHA1 Message Date
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
84d3d42306 Merge pull request #220 from proddy:dev
minor updates
2021-11-25 09:16:03 +01:00
proddy
61e2739ef7 updated for b11 2021-11-25 09:13:48 +01:00
proddy
a3391afd27 bump to b11 2021-11-25 09:13:38 +01:00
proddy
ce9b7d1468 added test for lastcode 2021-11-25 09:13:31 +01:00
proddy
1b86314a14 formatting 2021-11-25 09:13:22 +01:00
proddy
b2a0519f83 change font type and size 2021-11-25 09:13:09 +01:00
proddy
3dec4bda8c fix double click 2021-11-25 09:12:57 +01:00
Proddy
5de529cbb2 Merge pull request #218 from pswid:dev
timestamp in boiler last error code
2021-11-25 08:49:44 +01:00
pswid
4fd1d8e08a Boiler last error code change 2021-11-23 19:11:35 +01:00
pswid
0847ccc602 Revert "fix boiler last error code timestamp"
This reverts commit 969803569e.
2021-11-23 19:09:46 +01:00
pswid
969803569e fix boiler last error code timestamp 2021-11-23 18:59:37 +01:00
Proddy
6532abd870 Merge pull request #217 from proddy:dev
text changes, renamed status to bus_status in heartbeat, improved WebUI table layout, added Ethernet phy to custom profile board
2021-11-22 09:11:41 +01:00
proddy
07dabb4ceb fix name of status -> bus_status in heartbeat 2021-11-22 08:49:53 +01:00
proddy
1c6a683015 rename bus to bus_status in info command 2021-11-22 08:49:33 +01:00
proddy
bb38458ac4 stripped table rows 2021-11-22 00:36:15 +01:00
proddy
53de2ca25b added ethernet phy type as an option in settings - #210 2021-11-21 13:51:02 +01:00
Proddy
5a9122f8be Merge pull request #215 from proddy/dev 2021-11-20 21:25:02 +01:00
proddy
dc84f91044 debug comments 2021-11-20 21:23:04 +01:00
proddy
4be6626470 fixed edge case in shower logic when state didn't change 2021-11-20 21:22:55 +01:00
proddy
7e4494aae1 rename status to bus_status in MQTT heartbeat 2021-11-20 21:22:28 +01:00
proddy
50e54e6a1c fix removing old HA config topics 2021-11-20 21:22:10 +01:00
Proddy
30b111b986 Merge pull request #208 from proddy:dev
fixes #207
2021-11-19 13:00:58 +01:00
proddy
a63f2e6131 fixes #207 - allow both data and value as MQTT keys 2021-11-19 12:59:52 +01:00
proddy
51d487b938 formatting 2021-11-19 12:57:48 +01:00
Proddy
96180c837d Merge pull request #203 from proddy:dev
Add Network to MQTT topic info and system/info command
2021-11-18 12:11:40 +01:00
proddy
da20cf1ed2 add Network to MQTT info topic and system/info command 2021-11-18 12:10:20 +01:00
proddy
bffad5e3c8 allow thermostat hc3 on device_id 0x1A - #200 2021-11-17 22:22:34 +01:00
Proddy
74287ebb99 Merge pull request #201 from proddy:dev
fixes #199 - change value validation fails
2021-11-17 19:03:27 +01:00
proddy
8b40c92f7e fixes #199 - change value formats degrees to strings 2021-11-17 19:01:29 +01:00
Proddy
5f6033cac1 Merge pull request #197 from proddy/dev
fixes #196 (HA missing entities), add row click to devices, change max limit on integer types to avoid NaN, refactored generation of device value JSON objects
2021-11-15 17:45:52 +01:00
proddy
24582407d4 b7 2021-11-15 15:27:25 +01:00
proddy
22c9e3ee1f tidy up generate_values_json_web 2021-11-15 15:23:01 +01:00
proddy
cb2c898b7e click row to edit 2021-11-15 15:22:46 +01:00
proddy
c0bf623266 rename publish_ha* functions 2021-11-15 14:28:14 +01:00
proddy
18f22d3951 add state to dv, using DeviceValueState 2021-11-15 14:27:53 +01:00
proddy
e2dad610b0 publish HA config topic after device values 2021-11-15 14:27:18 +01:00
proddy
5ed3cbee2e add days 2021-11-15 14:26:42 +01:00
proddy
27712badb6 allow empty payloads, refactor to also delete a HA topic 2021-11-15 14:26:30 +01:00
proddy
72c032adff replace read_flash_string 2021-11-15 14:25:49 +01:00
proddy
7197df9812 replace read_flash_string 2021-11-15 14:25:04 +01:00
proddy
898e2e5f21 bump b7 2021-11-15 14:24:33 +01:00
proddy
cde3b7541f update test for 196 2021-11-15 14:24:21 +01:00
proddy
822f55497e tidy up, mention HA enttity fix 2021-11-15 14:23:58 +01:00
Proddy
7c16870294 Merge pull request #194 from kpschaper/bugfix_settemp
bugfix revert auto mode -> the mode is hardcoded for the thermostat_ha_cmd #183
2021-11-14 18:17:18 +01:00
Proddy
e368f422f4 Merge pull request #195 from proddy/dev
Fix console UOM, set temperature to show 1 decimal place in web
2021-11-14 18:04:39 +01:00
proddy
9d9ac4ed9e force temperatures in degrees to always show 1 decimal place 2021-11-14 16:14:29 +01:00
proddy
d7576ebda1 fix bug showing UOM in Console 2021-11-14 16:14:04 +01:00
proddy
fd810ff01c add comments on format param 2021-11-14 16:10:32 +01:00
proddy
999a05f7ff bump to b6 2021-11-14 16:10:20 +01:00
proddy
a19f2f19c5 update test data 2021-11-14 16:10:10 +01:00
Koen Schaper
f54ccd40e6 revert auto mode -> the mode is hardcoded for the thermostat_ha_cmd #183 2021-11-14 15:32:12 +01:00
Proddy
5893487d4a Merge pull request #189 from proddy:dev
fix MQTT non-JSON payloads
2021-11-14 12:56:20 +01:00
Proddy
3c8e20d4e4 Merge pull request #193 from kpschaper/fix_rc10_mode
Mode update for #183
2021-11-14 12:55:37 +01:00
Koen Schaper
bf68a5523b Update changelog 2021-11-14 12:14:55 +01:00
Koen Schaper
9c5c27152c Allow mode "off" to be set from home assistant + remove non available "auto" mode from home assistant 2021-11-14 11:11:37 +01:00
proddy
c229371c68 fix typo 2021-11-14 10:26:13 +01:00
proddy
805cef68a2 fix handling on non json payloads - #173 2021-11-13 15:59:04 +01:00
proddy
09addcb975 with the release of espressif32 3.4.0 we don't need the delay anymore for telnet 2021-11-12 14:33:22 +01:00
Proddy
409d382ff9 Merge pull request #188 from MichaelDvP:dev
Mode update for #183
2021-11-11 20:04:25 +01:00
MichaelDvP
27bfc14438 set mode and heatingPID for RC10 #183 2021-11-11 17:30:17 +01:00
MichaelDvP
6c20a5f4f9 prevent double messages in weblog 2021-11-11 08:22:20 +01:00
MichaelDvP
ffd61a9f67 removed unused pragma 2021-11-11 08:21:12 +01:00
MichaelDvP
4f2da0347c add hc to console calls 2021-11-10 14:06:00 +01:00
MichaelDvP
3c13c144d5 fix wwc ids 2021-11-10 14:05:31 +01:00
proddy
d4eaedef3d text changes 2021-11-10 12:31:01 +01:00
proddy
a8f1892d48 rename system/info text, added MQTT status 2021-11-10 12:27:18 +01:00
proddy
e347ac5742 formatting and text changes 2021-11-10 12:26:59 +01:00
proddy
5f2a9b093d improve error handling, fix crash on empty /api URL 2021-11-10 12:26:29 +01:00
proddy
e821e8d082 update 2021-11-10 12:25:38 +01:00
proddy
05f56be2d8 bump version to 3.3.0b4 2021-11-09 21:59:44 +01:00
proddy
88f78f6541 fix authentication check for GET commands that need admin - Refactor MQTT subscriptions and API calls #173 2021-11-09 20:54:41 +01:00
MichaelDvP
234533f241 fix RC35 program no. #182 2021-11-09 15:44:41 +01:00
MichaelDvP
b1f72b0e3e fix crash on empty mqtt-payload 2021-11-08 18:30:33 +01:00
MichaelDvP
a3022f6f20 RC35 switchtime for prog 1 and prog 2 2021-11-08 18:29:25 +01:00
MichaelDvP
95f7583511 fix wrong RC10 tag 2021-11-08 07:57:08 +01:00
MichaelDvP
578ba386e6 add moduline200 values #183, RC35 holidays/vacations #182 2021-11-07 20:49:58 +01:00
proddy
df7be9d11e update to 3.3.0b3 2021-11-07 18:24:33 +01:00
Proddy
72a59917fb Merge pull request #185 from kpschaper/fix_shower_alert
Fix shower timer; doing_cold_shot is never being reset
2021-11-07 17:42:49 +01:00
Koen Schaper
9406c76e55 fix shower timer; doing_cold_shot is never being reset + remove unnecessary conditions 2021-11-07 16:34:40 +01:00
proddy
ea550b1656 read command checks device id - #184 2021-11-07 13:50:39 +01:00
proddy
d20741c0f0 add a little more buffer for concurrent web sockets 2021-11-07 13:49:55 +01:00
Proddy
91b7fd59d1 Merge pull request #181 from kpschaper/moduline200
Show mode (off / heat) in Home Assistant for Moduline 200
2021-11-06 20:56:57 +01:00
proddy
95e81ad824 auto-fetch 0x14 which is not broadcasted on all boilers - https://github.com/emsesp/EMS-ESP32/issues/160#issuecomment-962444738 2021-11-06 13:31:32 +01:00
Koen Schaper
316832fc4f Show mode (off / heat) in Home Assistant for Moduline 200 2021-11-06 10:07:04 +01:00
proddy
23b6d81c47 3.3.0b2 2021-11-05 21:48:11 +01:00
proddy
8284520733 increase tcp event queue from 32 to 64 - fixes Launching the WebUI from crashes EMS-ESP on some environments #177 2021-11-05 21:46:15 +01:00
MichaelDvP
57f53818e1 add RC20_N write extmintemp, formatting 2021-11-05 14:14:04 +01:00
proddy
7bca3fb2ed rename "name" to "entity" for API key - Refactor MQTT subscriptions and API calls #173 2021-11-05 13:22:39 +01:00
MichaelDvP
ae4a1358af show rf sensor temperature 2021-11-05 11:57:58 +01:00
MichaelDvP
95e3a11a11 add missing RC25 parameters 2021-11-05 11:53:41 +01:00
MichaelDvP
f1a859c650 show devices with product-id zero, m200 wait improved 2021-11-05 11:53:02 +01:00
MichaelDvP
8d9fd95e85 remove useless extra subscriptions #173 2021-11-04 13:13:41 +01:00
proddy
452921d198 MQTT subscribe to devices - Refactor MQTT subscriptions and API calls #173 2021-11-03 22:12:21 +01:00
proddy
3e0f6f55fb bump to 3.3.0b1 2021-11-03 22:11:44 +01:00
MichaelDvP
0e480bbd94 add known devices without product-id or version-info #174 2021-11-03 21:08:52 +01:00
proddy
7a079d866f put/# back - Refactor MQTT subscriptions and API calls #173 2021-11-03 19:22:42 +01:00
MichaelDvP
af2710125e mqtt subscriptions include device #173 2021-11-03 18:43:46 +01:00
MichaelDvP
fb3de2e36d add RC300 wwdisinfect #175 2021-11-03 12:17:00 +01:00
proddy
bf40222105 validate devices to see if they are present - Refactor MQTT subscriptions #173 2021-11-02 21:00:16 +01:00
proddy
e1419edb15 more fixes - Refactor MQTT subscriptions #173 2021-11-02 18:15:57 +01:00
proddy
131b936a69 siwtch info with list - #176 2021-11-02 16:02:49 +01:00
proddy
5850a82d80 error handling improvements - Refactor MQTT subscriptions #173 2021-11-02 13:59:36 +01:00
proddy
b76b6be3d1 logic cleanup 2021-11-02 10:50:29 +01:00
proddy
b8f69eeaa8 lint warning fix 2021-11-02 10:47:46 +01:00
proddy
d78fb53845 fix for mqtt base paths - Refactor MQTT subscriptions #173 2021-11-02 10:42:34 +01:00
proddy
54889fec41 fix issue where OK was not sent on successfull API call 2021-11-02 10:42:20 +01:00
proddy
2c5c4d6e04 update test for different mqtt base paths 2021-11-02 10:41:56 +01:00
proddy
e6a44c9c82 be able to set mqtt base 2021-11-02 10:41:39 +01:00
proddy
7cba52d77e bump to 3.3.0 2021-11-01 23:32:28 +01:00
proddy
a79a67e4b2 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2021-11-01 23:31:33 +01:00
proddy
01bace4048 Refactor MQTT subscriptions #173 2021-11-01 23:31:30 +01:00
MichaelDvP
d5f8419157 do not fetch CRF200 monitor #138 2021-10-30 20:31:49 +02:00
Proddy
40a7026d4c update example with ESP32 core debug levels 2021-10-26 13:06:29 +02:00
proddy
47cb296cc4 updates to #168 2021-10-25 13:01:56 +02:00
MichaelDvP
be27033d41 typo 2021-10-24 18:32:50 +02:00
MichaelDvP
3001a2d66f fix #169, prod-id:111 settings use telegram 0x179 2021-10-24 18:15:39 +02:00
proddy
becaff0711 updated with #168 2021-10-22 22:17:15 +02:00
proddy
aaab9f409f text change 2021-10-22 22:15:30 +02:00
proddy
068bb5cbeb fix obsolete JS in padding & justification 2021-10-22 22:15:23 +02:00
proddy
337c07d7bc firmware version checker in Web - #168 2021-10-22 22:14:39 +02:00
proddy
c387f65b4a some minor refactor 2021-10-21 22:56:56 +02:00
proddy
df13081f97 update URL 2021-10-21 22:56:45 +02:00
proddy
50f6d0ab26 fixes #166 2021-10-21 22:56:35 +02:00
proddy
fb7bafdb87 fix display of mqtt subscriptions 2021-10-20 09:34:50 +02:00
proddy
b3472c3919 add test for 'send' command 2021-10-20 09:21:12 +02:00
proddy
4f47712d52 minor formatting 2021-10-20 09:21:03 +02:00
MichaelDvP
547ccb96c9 Thermostat 0x65 to RC20_N, #160 2021-10-20 09:00:57 +02:00
proddy
7f3ff434ea add test for Dan "ems-esp/boiler/wwcircpump with payload off" 2021-10-19 18:36:35 +02:00
proddy
aad4b0ade3 change text about MQTT subscribe formats 2021-10-19 18:35:48 +02:00
proddy
d587f44ec9 updated 2021-10-19 18:35:33 +02:00
proddy
f30d3cf637 update test data for standalone 2021-10-19 18:35:24 +02:00
proddy
8fed47f39b fix case fall through for HA state_class 2021-10-19 17:40:46 +02:00
proddy
48cedbd0fb fix standalone building 2021-10-19 17:40:12 +02:00
proddy
752530a381 auto formatting 2021-10-19 17:39:44 +02:00
proddy
45fc6daa4a Add support for mDNS #161 2021-10-18 13:02:22 +02:00
MichaelDvP
e17ce9c3b5 Junkers modetype #163 2021-10-18 12:05:04 +02:00
MichaelDvP
2657b9d1a5 do not fetch broadcasted telegrams 0x14/0x19 (#160) 2021-10-18 11:29:50 +02:00
MichaelDvP
b77a56ade2 timeout 60s for KM200 wait #160, reset-reason to log 2021-10-18 11:28:43 +02:00
MichaelDvP
4d5f588748 reset reason to system info 2021-10-18 11:25:41 +02:00
Proddy
6582ba6317 added another Buderus RC10 - #160 2021-10-17 21:40:26 +02:00
Proddy
b3453d9d02 no need to recompile C++ code if Web code doesn't change 2021-10-16 12:50:56 +02:00
MichaelDvP
e4b73140c8 enlarge filesystem buffer 2021-10-14 18:55:50 +02:00
Proddy
235f789228 increase max dallas sensors from 10 to 20 - #157 2021-10-14 10:09:52 +02:00
Proddy
c029cf79f7 use smaller json key names, increase buffer size. fixes #157 2021-10-14 10:09:28 +02:00
Proddy
0b796a85a8 use DeviceValueUOM::TIMES for all 'starts' which count numerically 2021-10-12 15:00:42 +02:00
Proddy
d8191f79a4 removed # from comments 2021-10-12 15:00:16 +02:00
Proddy
6a259f7cca removed # from comments 2021-10-12 15:00:05 +02:00
Proddy
22a2b92022 added number formatting and pluralization for uom times and seconds 2021-10-12 14:59:53 +02:00
Proddy
25616ae3b4 added time to UOM and replaced seconds with second (pluralization handled in code) 2021-10-12 14:59:27 +02:00
Proddy
372aee30cd removed # in front of telegram count 2021-10-12 14:58:51 +02:00
Proddy
0c5023323a removed # (e.g in front of starts) 2021-10-12 14:58:30 +02:00
Proddy
2d1126b9e4 updated comment 2021-10-12 14:57:42 +02:00
Proddy
3ecf92fa41 add times as a new HA UOM - #156 2021-10-12 12:59:00 +02:00
Proddy
50befd8991 add HA device_class 2021-10-12 12:58:29 +02:00
Proddy
676268d7af rename rxread/txread to rxreads/txreads 2021-10-12 12:58:05 +02:00
Proddy
f2457a7050 auto-formatting 2021-10-11 16:53:00 +02:00
Proddy
ded90dc4ce minor text changes 2021-10-11 16:42:47 +02:00
Proddy
cddadcfae2 changes to allow restart command from API to complete before rebooting 2021-10-11 16:42:36 +02:00
Proddy
3113d392ac minor text changes 2021-10-11 16:41:44 +02:00
Proddy
6433d5f744 formatting 2021-10-11 16:41:24 +02:00
Proddy
f42c265714 minor text changes 2021-10-11 16:41:14 +02:00
Proddy
d6711ac850 always show a maintenancemessage even if there isn't one, so its not missing in the MQTT boiler_data payload 2021-10-10 20:29:03 +02:00
Proddy
9d7c2de1d5 rename HA icon mdi:flash-circle to mdi:lightning-bolt-circle 2021-10-10 20:11:52 +02:00
MichaelDvP
b1e1c44e77 add divider #136 2021-10-10 17:43:47 +02:00
MichaelDvP
85f54dd210 add current room influence #136 2021-10-10 09:18:52 +02:00
MichaelDvP
eea32ad134 add RC30 ww parameters, #117 2021-10-01 09:39:57 +02:00
MichaelDvP
4f24035082 add id to solar ww-circuit 2021-10-01 09:24:38 +02:00
MichaelDvP
0b0b1d9ca4 rename internal names wW/warmwater to ww 2021-09-29 14:42:34 +02:00
MichaelDvP
98828ed848 another typo 2021-09-29 12:55:51 +02:00
MichaelDvP
7b0f7cd32c fix typo wW->ww 2021-09-29 12:43:07 +02:00
Proddy
67cb778039 Merge pull request #135 from Sunbuzz/ft_add_connect
Added CONNECT device 0x236 "Wireless sensor base"
2021-09-29 12:11:25 +02:00
MichaelDvP
9f49afae0a read wwSelTemp/wwDisinfectionTemp from 0xEA, #96 2021-09-29 11:57:24 +02:00
sunbuzz
49c7c7aa2d Added CONNECT device 0x236 "Wireless sensor base" 2021-09-29 11:23:22 +02:00
Proddy
42d89d1d10 Merge pull request #134 from proddy/dev
Dev
2021-09-28 16:18:30 +02:00
proddy
24fae0d03e updated comment 2021-09-28 16:15:53 +02:00
proddy
2c337f1d03 fixed #129 2021-09-28 16:15:47 +02:00
MichaelDvP
fcc521d5ed enum thermostat programs, add junkers remote, program 2021-09-28 14:06:26 +02:00
MichaelDvP
91005876eb use device names from flash 2021-09-28 13:08:56 +02:00
proddy
da5b4aa79d add test for MQTT heatingactivated 2021-09-25 22:04:42 +02:00
proddy
ced440392b fix possible buffer overflow 2021-09-25 21:52:19 +02:00
proddy
33d7ba1fda reduce variable scope 2021-09-25 21:52:03 +02:00
proddy
a6095fc305 init poolShuntStatus__ as its not done in the constructor 2021-09-25 21:51:47 +02:00
proddy
84cc964a7a 3.2.2b8 2021-09-25 18:41:16 +02:00
Proddy
9e856b28a9 Merge pull request #130 from MichaelDvP/dev
use TAG_DEVICE_DATA_WW
2021-09-25 18:13:37 +02:00
MichaelDvP
9378fdf2b6 use TAG_DEVICE_DATA_WW 2021-09-25 10:00:06 +02:00
proddy
6a134dda1f Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2021-09-23 10:08:08 +02:00
MichaelDvP
4f927ee571 remove unused type from form 2021-09-23 08:48:59 +02:00
MichaelDvP
b111869422 remove invalid UOMs 2021-09-23 08:30:31 +02:00
proddy
89a249eae4 add breaking changes 2021-09-22 20:18:37 +02:00
MichaelDvP
7942d52843 npm audit fix 2021-09-22 15:13:11 +02:00
MichaelDvP
b6310302d2 add C2 error message, fix a uom 2021-09-22 15:12:58 +02:00
MichaelDvP
87774e73e1 fix some thermstat uoms 2021-09-22 15:12:05 +02:00
MichaelDvP
548b5ff4a1 remove unused check 2021-09-22 15:11:32 +02:00
proddy
80fedf3fa3 fix not showing dv is UOM is NONE 2021-09-22 14:53:50 +02:00
proddy
8523cdffa3 Merge branch 'ft_reacthooks' into dev 2021-09-21 18:11:55 +02:00
proddy
29f2335935 remove DeviceValueUOM::TEXT 2021-09-21 18:06:55 +02:00
proddy
eba6324d18 more API tests 2021-09-21 18:06:36 +02:00
proddy
c3874d7c95 remove DeviceValueUOM::TEXT 2021-09-21 18:06:25 +02:00
proddy
3086342d2b fix bug in nested json and added output targets 2021-09-21 18:05:36 +02:00
proddy
f836209249 added output target when rendering json 2021-09-21 18:04:48 +02:00
proddy
d4b06cf0c0 formatting 2021-09-21 18:04:25 +02:00
proddy
54199affc1 removed DeviceValueUOM::TEXT 2021-09-21 18:04:07 +02:00
proddy
4d88c6a90b add comments about nesting 2021-09-21 18:03:43 +02:00
proddy
906813b8f5 don't show handlers if there arn't any 2021-09-21 18:03:30 +02:00
proddy
30d1e7ecb4 bump version 2021-09-21 18:03:14 +02:00
proddy
8fab4e29bf use float instead of double 2021-09-21 18:03:05 +02:00
proddy
252554ea87 added comment about nesting 2021-09-21 18:02:52 +02:00
proddy
ba3d49172c updated packages 2021-09-21 18:02:40 +02:00
proddy
6ef8ff757a add check to prevent crash on null strings 2021-09-21 18:02:14 +02:00
MichaelDvP
fcc2c0b3de fastheatup as percent-value, enlarge parse-buffer #122 2021-09-21 08:04:11 +02:00
proddy
16e390f849 v3.3 2021-09-20 21:44:23 +02:00
proddy
a31cf53863 replace class components (HOCs) with React Hooks 2021-09-20 21:43:56 +02:00
proddy
09d8a6360b only use UOM of NONE for hidden Device Values 2021-09-20 21:43:05 +02:00
proddy
b1f59d4727 ignore UOM if its TEXT 2021-09-20 21:42:45 +02:00
proddy
d8add7edcb rename DeviceValueType::TEXT to STRING and ignore UOM if its NONE 2021-09-20 21:42:24 +02:00
proddy
1a71921fd6 remove debug 2021-09-20 21:41:48 +02:00
proddy
6d12fff4fe add API test 2021-09-20 21:41:29 +02:00
proddy
769301c804 rename boiltemp to 'actual boiler temperature' https://github.com/emsesp/EMS-ESP32/discussions/115 2021-09-20 13:52:37 +02:00
MichaelDvP
6a828e9ca5 Merge branch 'dev_' into dev 2021-09-20 09:04:57 +02:00
MichaelDvP
2516d2d6de add boiler disinfect command 2021-09-20 08:53:03 +02:00
MichaelDvP
e92a3ad025 add MM10 valvetime 2021-09-20 08:51:16 +02:00
MichaelDvP
5686094151 factor for charge duration 2021-09-20 08:49:57 +02:00
proddy
915749dd69 updated 2021-09-19 21:38:09 +02:00
proddy
258bc2b544 increase json before from 4KB to 16KB (EMSESP_JSON_SIZE_XXLARGE_DYN) 2021-09-19 21:33:49 +02:00
proddy
a9748f5b46 snprintf_P to snprintf() again 2021-09-19 21:33:15 +02:00
proddy
56f1c7946e snprintf_P rename 2021-09-19 21:32:51 +02:00
proddy
88a427578f rename snprintf_P, rename unit to uom 2021-09-19 21:32:35 +02:00
proddy
0ebd9e1fe1 make sure all std::strings are consts 2021-09-19 21:31:23 +02:00
proddy
bea0b4ba01 add entities command, only show master thermostat if >1 thermostats 2021-09-19 21:31:02 +02:00
proddy
f8bee9b5c5 formatting 2021-09-19 21:29:53 +02:00
proddy
500e089b97 for standalone ncrease mem due to compiler 2021-09-19 21:29:42 +02:00
proddy
2adef52abf add "entitied" as new command 2021-09-19 21:29:20 +02:00
proddy
e01bfe1bdf rename duplicate function publish_mqtt_ha_sensor 2021-09-19 21:29:03 +02:00
proddy
418b1b3d68 bump 2021-09-19 21:28:30 +02:00
proddy
401929d992 round2 use float instead of double (6 point precision is enough) 2021-09-19 21:28:21 +02:00
proddy
ee34dd72f7 rename snprintf_P to snprintf 2021-09-19 21:27:39 +02:00
proddy
b214d7a662 extended test for ha and entities command 2021-09-19 21:26:48 +02:00
proddy
079a40cd32 update to 16.18.4 2021-09-19 21:26:15 +02:00
proddy
adebea1561 add pseudo for snprintf_P 2021-09-19 21:25:54 +02:00
proddy
28bf7c3225 formatting 2021-09-19 11:21:01 +02:00
proddy
78b2efd148 add rounding test 2021-09-19 11:20:54 +02:00
MichaelDvP
11590061a3 fix wwcharge-command #112 2021-09-18 13:58:08 +02:00
proddy
09847ee33c fix: only set thermostat commands for master thermostat #110 2021-09-18 09:52:02 +02:00
MichaelDvP
db34785be0 add missing offset to telgram::to_string() 2021-09-16 17:14:22 +02:00
proddy
538a9cf642 mention RC25 support 2021-09-15 20:56:52 +02:00
proddy
d0346e436a remove PSTR() 2021-09-15 18:04:58 +02:00
Proddy
442202d978 Merge pull request #109 from MichaelDvP/dev
add RC300 second summermode telegram
2021-09-15 17:53:04 +02:00
MichaelDvP
3b2a89d9f4 solar turnOnOffDiff allow setting decimals #107 2021-09-15 10:54:57 +02:00
MichaelDvP
0bf366ac75 Divider for solar pumpOnOffDiff #107 2021-09-15 08:27:50 +02:00
proddy
c62e73d21e added HA state_class https://github.com/emsesp/EMS-ESP/issues/776 2021-09-13 20:45:56 +02:00
MichaelDvP
2889899bfd also add summertemp setting 2021-09-12 19:03:59 +02:00
MichaelDvP
f18ac2e48b add RC300 second summermode telegram 2021-09-12 18:48:33 +02:00
MichaelDvP
1d259d14c8 thermostat RC25 additions 2021-09-09 18:44:43 +02:00
MichaelDvP
b8c07e31cf mixer: add id to pool, sort registering, add commands 2021-09-09 11:33:57 +02:00
proddy
3f27ed7b18 auto-formatting 2021-09-08 19:53:07 +02:00
Proddy
d7678b340f Merge pull request #104 from Sunbuzz/pool
Pool
2021-09-08 12:18:21 +02:00
MichaelDvP
b697356465 add RC25, #106 2021-09-07 08:25:20 +02:00
sunbuzz
1b9a2f21d2 Increased EMSESP_JSON_SIZE_XXLARGE_DYN to 16384 (at 10635 right now) + small fixes 2021-09-03 15:01:05 +02:00
sunbuzz
e26451fcc0 reverted to TAG_NONE, added HP activities, added some json space 2021-09-02 15:25:37 +02:00
sunbuzz
17db542775 Fixed support for M P mixer i ha_config 2021-09-02 12:58:23 +02:00
sunbuzz
dd865a2db5 Fixed issues regarding pull request "Pool #104" 2021-09-02 12:40:39 +02:00
sunbuzz
834c7cb87f Added support for MP100 pool mixer (telegram 05BA), added brand IVT, added some parameters to telegram 0x48D & 0x48F 2021-09-01 22:06:50 +02:00
sunbuzz
a9a015cb5b Added support for MP100 pool mixer (telegram 05BA), added brand IVT, added some parameters to telegram 0x48D & 0x48F 2021-09-01 22:05:18 +02:00
MichaelDvP
94a71998a7 add some thermostat values 2021-08-30 14:11:55 +02:00
proddy
aa212924bc 3.2.2b2 2021-08-28 16:41:03 +02:00
Proddy
edef2f1bf6 Merge pull request #102 from Sunbuzz/pool
Added pool data to telegrams 0x494 & 0x495
2021-08-28 16:36:56 +02:00
proddy
912f53762e fix formatting (WARNING didnt fit) 2021-08-28 15:10:29 +02:00
proddy
02c04c8f41 updated images 2021-08-28 15:04:30 +02:00
sunbuzz
3ad90a6e34 Added pool data to telegrams 0x494 & 0x495 2021-08-28 12:03:05 +02:00
proddy
0f83870db0 auto-formatting 2021-08-26 10:42:40 +02:00
proddy
7529f3a73e minor text changes 2021-08-26 10:42:34 +02:00
MichaelDvP
77d559ac8c syslog re-add colon after message.id 2021-08-26 08:02:46 +02:00
MichaelDvP
1311cad913 add some boiler and thermostat parameters 2021-08-23 16:14:06 +02:00
MichaelDvP
4d1ba9bede remove tx-delay, wait for KM200 poll, v3.2.2b1 2021-08-23 13:43:32 +02:00
MichaelDvP
6b07651ed3 remove weblog download filter 2021-08-21 10:58:47 +02:00
MichaelDvP
50ddfc0437 store weblog settings, no log filtering, prevent double messages 2021-08-20 17:42:07 +02:00
MichaelDvP
c0ac485772 add date/time to Easy thermostat 2021-08-20 10:04:43 +02:00
MichaelDvP
8d4ae6971d fix #100 add missing hamode to EasyMonitor 2021-08-20 09:48:01 +02:00
Proddy
88d0dfee2d update changelog 2021-08-19 20:58:05 +02:00
Proddy
551a479c6b fix custom build options 2021-08-19 20:12:43 +02:00
MichaelDvP
3f85541c9a add system commands for syslog level and watch 2021-08-19 16:19:32 +02:00
MichaelDvP
74cdb610d8 fix#99, mqtt reconnect for IPv4 and IPv6 2021-08-19 15:27:41 +02:00
MichaelDvP
165dd3b418 fix #99, mqtt reconnect after wifi drop 2021-08-18 20:47:37 +02:00
MichaelDvP
7421f3e345 fix crash on response for empty telegrams 2021-08-17 16:06:20 +02:00
MichaelDvP
d00559bcd5 Fix compact weblog level-lable for trace 2021-08-16 07:32:35 +02:00
MichaelDvP
9774051fab Syslog BOM only for utf-8 messages, #91
Tested with extra message with udf-characters:
```
Aug 15 09:30:12 ems-esp32 emsesp 000+00:00:00.000 I 0 Starting Syslog
Aug 15 09:30:13 ems-esp32 emsesp 000+00:00:00.000 I 2 EMS Device library loaded with 79 records
Aug 15 09:30:13 ems-esp32 emsesp <BOM>000+00:00:00.000 I 3 Testing syslog with udf-8-chars: €öäü߀öäü߀öäüß
Aug 15 09:30:13 ems-esp32 emsesp 000+00:00:03.741 I 5 Starting NTP
```
2021-08-15 10:24:57 +02:00
proddy
c50692bae0 version 3.2.2b0 2021-08-14 13:45:26 +02:00
proddy
6348283001 fomatting 2021-08-14 13:41:35 +02:00
proddy
1cbd34d94e rename get_toggle_fetch -> is_fetch 2021-08-14 13:41:11 +02:00
proddy
f9d768a7a7 added clang-tidy 2021-08-14 13:40:21 +02:00
proddy
6c1bfccfaf text change 2021-08-10 09:23:04 +02:00
proddy
2ca0a0c634 v3.2.1 merged from dev 2021-08-08 14:46:14 +02:00
proddy
30ce3f1dc3 3.2.1 2021-08-08 14:45:24 +02:00
proddy
15541c52ce ww selected temperature sets the boilers target temp, not the wwSetTemp 2021-08-08 14:29:20 +02:00
proddy
20c3e8c7bd no need to set new temp in condition 2021-08-08 09:15:03 +02:00
proddy
59797fb89c comment cleanup 2021-08-07 21:47:38 +02:00
proddy
849cc85398 added FSTR_ and MAKE_STR for non-flash macros 2021-08-07 21:47:24 +02:00
proddy
dd318a1c8e always show tags, don't treat BOILER as a special case 2021-08-07 21:46:59 +02:00
proddy
2edf2a4231 disable mqtt function 2021-08-07 21:46:38 +02:00
proddy
d58ee1e693 renamed ww names 2021-08-07 21:46:22 +02:00
proddy
088cb7fe40 support for device in json body 2021-08-07 21:46:09 +02:00
proddy
38498a5587 updated API tests 2021-08-07 21:45:52 +02:00
proddy
6e3b30b03c renamed product ID, changed case for all ww names 2021-08-07 21:45:43 +02:00
proddy
4e9cf72816 3.2.1 2021-08-07 21:45:12 +02:00
146 changed files with 6298 additions and 3282 deletions

152
.clang-tidy Normal file
View File

@@ -0,0 +1,152 @@
---
Checks: >-
*,
-abseil-*,
-android-*,
-boost-*,
-bugprone-branch-clone,
-bugprone-narrowing-conversions,
-bugprone-signed-char-misuse,
-bugprone-too-small-loop-variable,
-cert-dcl50-cpp,
-cert-err58-cpp,
-cert-oop57-cpp,
-cert-str34-c,
-clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-osx.*,
-clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-shadow-field,
-clang-diagnostic-sign-compare,
-clang-diagnostic-unused-variable,
-clang-diagnostic-unused-const-variable,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-const-cast,
-cppcoreguidelines-pro-type-cstyle-cast,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-static-cast-downcast,
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions,
-fuchsia-default-arguments,
-fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects,
-fuchsia-default-arguments-declarations,
-fuchsia-default-arguments-calls,
-google-build-using-namespace,
-google-explicit-constructor,
-google-readability-braces-around-statements,
-google-readability-casting,
-google-readability-todo,
-google-runtime-references,
-hicpp-*,
-llvm-else-after-return,
-llvm-header-guard,
-llvm-include-order,
-llvm-qualified-auto,
-llvmlibc-*,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-misc-unused-parameters,
-modernize-avoid-c-arrays,
-modernize-return-braced-init-list,
-modernize-use-auto,
-modernize-use-default-member-init,
-modernize-use-equals-default,
-modernize-use-trailing-return-type,
-mpi-*,
-objc-*,
-readability-braces-around-statements,
-readability-const-return-type,
-readability-convert-member-functions-to-static,
-readability-else-after-return,
-readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-make-member-function-const,
-readability-named-parameter,
-readability-qualified-auto,
-readability-redundant-access-specifiers,
-readability-redundant-member-init,
-readability-redundant-string-init,
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-warnings-as-errors
WarningsAsErrors: '*'
AnalyzeTemporaryDtors: false
FormatStyle: google
CheckOptions:
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: readability-identifier-naming.LocalVariableCase
value: 'lower_case'
- key: readability-identifier-naming.ClassCase
value: 'CamelCase'
- key: readability-identifier-naming.StructCase
value: 'CamelCase'
- key: readability-identifier-naming.EnumCase
value: 'CamelCase'
- key: readability-identifier-naming.EnumConstantCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.StaticConstantCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.StaticVariableCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.GlobalConstantCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.ParameterCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMemberPrefix
value: 'NO_PRIVATE_MEMBERS_ALWAYS_USE_PROTECTED'
- key: readability-identifier-naming.PrivateMethodPrefix
value: 'NO_PRIVATE_METHODS_ALWAYS_USE_PROTECTED'
- key: readability-identifier-naming.ClassMemberCase
value: 'lower_case'
- key: readability-identifier-naming.ClassMemberCase
value: 'lower_case'
- key: readability-identifier-naming.ProtectedMemberCase
value: 'lower_case'
- key: readability-identifier-naming.ProtectedMemberSuffix
value: '_'
- key: readability-identifier-naming.FunctionCase
value: 'lower_case'
- key: readability-identifier-naming.ClassMethodCase
value: 'lower_case'
- key: readability-identifier-naming.ProtectedMethodCase
value: 'lower_case'
- key: readability-identifier-naming.ProtectedMethodSuffix
value: '_'
- key: readability-identifier-naming.VirtualMethodCase
value: 'lower_case'
- key: readability-identifier-naming.VirtualMethodSuffix
value: ''

View File

@@ -48,7 +48,7 @@ jobs:
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
title: ESP32 Development Build v${{steps.build_info.outputs.version}}
title: Development Build v${{steps.build_info.outputs.version}}
automatic_release_tag: "latest"
prerelease: true
files: |

4
.gitignore vendored
View File

@@ -27,4 +27,6 @@ emsesp
/interface/build
node_modules
/interface/.eslintcache
test.sh
test.sh
scripts/__pycache__
.temp

View File

@@ -5,6 +5,89 @@ 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).
# Changelog
# [3.3.0] November 28 2021
## Added
- Add system commands for syslog level and watch [#98](https://github.com/emsesp/EMS-ESP32/issues/98)
- Added pool data to telegrams 0x494 & 0x495 [#102](https://github.com/emsesp/EMS-ESP32/issues/102)
- Add RC300 second summermode telegram [#108](https://github.com/emsesp/EMS-ESP32/issues/108)
- Add support for the RC25 thermostat [#106](https://github.com/emsesp/EMS-ESP32/issues/106)
- Add new command 'entities' for a device, e.g. http://ems-esp/api/boiler/entities to show the shortname, description and HA Entity name (if HA enabled) [#116](https://github.com/emsesp/EMS-ESP32/issues/116)
- Support for Junkers program and remote (fb10/fb110) temperature
- Home Assistant `state_class` attribute for Wh, kWh, W and KW [#129](https://github.com/emsesp/EMS-ESP32/issues/129)
- Add current room influence for RC300 [#136](https://github.com/emsesp/EMS-ESP32/issues/136)
- Added Home Assistant device_class to sensor entities
- Added another Buderus RC10 thermostat with Product ID 65 [#160](https://github.com/emsesp/EMS-ESP32/issues/160)
- Added support for mDNS [#161](https://github.com/emsesp/EMS-ESP32/issues/161)
- Added last system ESP32 reset code to log (and `system info` output)
- Firmware Checker in WebUI [#168](https://github.com/emsesp/EMS-ESP32/issues/168)
- Added new MQTT setting for enabling 'response' topic
- Support for non-standard Thermostats like Tado [#174](https://github.com/emsesp/EMS-ESP32/issues/174)
- Include MQTT connection status in 'api/system/info'
- Include Network status in 'api/system/info' and also the MQTT topic `info` [#202](https://github.com/emsesp/EMS-ESP32/issues/202)
- Added Ethernet PHY module as an option in the Board Profile [#210](https://github.com/emsesp/EMS-ESP32/issues/210)
## Fixed
- MQTT reconnecting after WiFi reconnect [#99](https://github.com/emsesp/EMS-ESP32/issues/99)
- Manually Controlling Solar Circuit [#107](https://github.com/emsesp/EMS-ESP32/issues/107)
- Fix thermostat commands not defaulting to the master thermostat [#110](https://github.com/emsesp/EMS-ESP32/issues/110)
- Enlarge parse-buffer for long names like `cylinderpumpmodulation`
- MQTT not subscribing to all device entities [#166](https://github.com/emsesp/EMS-ESP32/issues/166)
- Help fix issues with WebUI unable to fully load UI over Ethernet [#177](https://github.com/emsesp/EMS-ESP32/issues/177)
- Shower alert never reset after limit reached when enabled [(PR #185)]
- Remove HA entity entries when a device value goes dormant [#196](https://github.com/emsesp/EMS-ESP32/issues/196)
- deciphering last error code dates on 0xC2 telegram [#204](https://github.com/emsesp/EMS-ESP32/issues/204)
## Changed
- Syslog BOM only for utf-8 messages [#91](https://github.com/emsesp/EMS-ESP32/issues/91)
- Check for KM200 by device-id 0x48, remove tx-delay [#90](https://github.com/emsesp/EMS-ESP32/issues/90)
- rename `fastheatupfactor` to `fastheatup` and add percent [#122](https://github.com/emsesp/EMS-ESP32/issues/122)
- "unit" renamed to "uom" in API call to recall a Device Value
- initial backend React changes to replace the class components (HOCs) with React Hooks
- Use program-names instead of numbers
- Boiler's maintenancemessage always published in MQTT (to prevent HA missing entity)
- Unit of Measure 'times' added to MQTT Fails, Rx fails, Rx received, Tx fails, Tx reads & Tx writes
- Improved API. Restful HTTP API works in the same way as MQTT calls
- Removed settings for MQTT subscribe format [#173](https://github.com/emsesp/EMS-ESP32/issues/173)
- Improve Nefit Moduline 200 functionality [#183](https://github.com/emsesp/EMS-ESP32/issues/183)
- `status` in the MQTT heartbeat renamed to `bus_status`
- Layout changes in the WebUI, showing stripped table rows in Dashboard
- Alternative font for log window [#219](https://github.com/emsesp/EMS-ESP32/issues/219)
## **BREAKING CHANGES**
- API: "unit" renamed to "uom" in API call to recall a Device Value
- HA: `sensor.boiler_boiler_temperature` renamed to `sensor.actual_boiler_temperature`
- HA: `binary_sensor.boiler_ww_disinfecting` renamed to `binary_sensor.boiler_ww_disinfection`
- HA: # removed from counts in MQTT Fails, Rx fails, Rx received, Tx fails, Tx reads & Tx writes
- `txread` renamed to `txreads` and `txwrite` renamed to `txwrites` in MQTT heartbeat payload
- 'dallas sensors' in api/system/info moved to the "System" section. Renamed "uptime (seconds)" and "reset reason"
- `status` in the MQTT heartbeat renamed to `bus_status`
# [3.2.1] August 8 2021
## Added
- json body in API can now take device, name, cmd, hc and id
- added example of how to use API directly to control values from Home Assistant
- API calls are shown in debug log (For troubleshooting)
## Fixed
- fixed issue with Home Assistant entity naming where boiler's ww was duplicated in entity name
- fixed issue where wwSetTemp was written too instead of wwSelTemp
## Changed
- fixed case on mqtt names, like 'wwtankmiddletemp'
- renamed Product ID to 'EMS Product ID' in Home Assistant
- removed brackets around tags, e.g. (hc1) selected room temperature" is now just "hc1 selected room temperature"
# [3.2.0] August 6 2021
## Added
@@ -43,7 +126,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# [3.1.1] June 26 2021
## Changed
## Added
- new command called `commands` which lists all available commands. `ems-esp/api/{device}/commands`
- More Home Assistant icons to match the UOMs
@@ -65,7 +148,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# [3.1.0] May 4 2021
## Changed
## Added
- Mock API to simulate an ESP, for testing web
- Able to write values from the Web UI

View File

@@ -1,7 +0,0 @@
# Changelog
## Added
## Fixed
## Changed

View File

@@ -33,7 +33,7 @@ CXX_STANDARD := -std=c++11
#----------------------------------------------------------------------
# Defined Symbols
#----------------------------------------------------------------------
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_DEFAULT_BOARD_PROFILE=\"LOLIN\"
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_DEFAULT_BOARD_PROFILE=\"LOLIN\"
#----------------------------------------------------------------------
# Sources & Files

View File

@@ -8,13 +8,13 @@
"name": "emsesp-react",
"version": "0.1.0",
"dependencies": {
"@material-ui/core": "^4.11.4",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@msgpack/msgpack": "^2.7.0",
"@types/lodash": "^4.14.168",
"@types/node": "^15.0.1",
"@types/react": "^17.0.4",
"@types/react-dom": "^17.0.3",
"@types/lodash": "^4.14.172",
"@types/node": "^12.20.20",
"@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9",
"@types/react-material-ui-form-validator": "^2.1.0",
"@types/react-router": "^5.1.13",
"@types/react-router-dom": "^5.1.7",
@@ -35,7 +35,7 @@
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"sockette": "^2.0.6",
"typescript": "4.2.4",
"typescript": "4.3.5",
"zlib": "^1.0.5"
},
"devDependencies": {
@@ -2050,13 +2050,13 @@
}
},
"node_modules/@material-ui/core": {
"version": "4.11.4",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.4.tgz",
"integrity": "sha512-oqb+lJ2Dl9HXI9orc6/aN8ZIAMkeThufA5iZELf2LQeBn2NtjVilF5D2w7e9RpntAzDb4jK5DsVhkfOvFY/8fg==",
"version": "4.12.3",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
"integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==",
"dependencies": {
"@babel/runtime": "^7.4.4",
"@material-ui/styles": "^4.11.4",
"@material-ui/system": "^4.11.3",
"@material-ui/system": "^4.12.1",
"@material-ui/types": "5.1.0",
"@material-ui/utils": "^4.11.2",
"@types/react-transition-group": "^4.2.0",
@@ -2137,9 +2137,9 @@
}
},
"node_modules/@material-ui/system": {
"version": "4.11.3",
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.3.tgz",
"integrity": "sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz",
"integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==",
"dependencies": {
"@babel/runtime": "^7.4.4",
"@material-ui/utils": "^4.11.2",
@@ -2148,6 +2148,20 @@
},
"engines": {
"node": ">=8.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/material-ui"
},
"peerDependencies": {
"@types/react": "^16.8.6 || ^17.0.0",
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@material-ui/types": {
@@ -2639,9 +2653,9 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
},
"node_modules/@types/lodash": {
"version": "4.14.168",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz",
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q=="
"version": "4.14.173",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.173.tgz",
"integrity": "sha512-vv0CAYoaEjCw/mLy96GBTnRoZrSxkGE0BKzKimdR8P3OzrNYNvBgtW7p055A+E8C31vXNUhWKoFCbhq7gbyhFg=="
},
"node_modules/@types/material-ui": {
"version": "0.21.8",
@@ -2658,9 +2672,9 @@
"integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA=="
},
"node_modules/@types/node": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
"integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA=="
"version": "12.20.25",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.25.tgz",
"integrity": "sha512-hcTWqk7DR/HrN9Xe7AlJwuCaL13Vcd9/g/T54YrJz4Q3ESM5mr33YCzW2bOfzSIc3aZMeGBvbLGvgN6mIJ0I5Q=="
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.0",
@@ -2688,9 +2702,9 @@
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
},
"node_modules/@types/react": {
"version": "17.0.4",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
"integrity": "sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==",
"version": "17.0.22",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.22.tgz",
"integrity": "sha512-kq/BMeaAVLJM6Pynh8C2rnr/drCK+/5ksH0ch9asz+8FW3DscYCIEFtCeYTFeIx/ubvOsMXmRfy7qEJ76gM96A==",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -2706,9 +2720,9 @@
}
},
"node_modules/@types/react-dom": {
"version": "17.0.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
"integrity": "sha512-4NnJbCeWE+8YBzupn/YrJxZ8VnjcJq5iR1laqQ1vkpQgBiA7bwk0Rp24fxsdNinzJY2U+HHS4dJJDPdoMjdJ7w==",
"version": "17.0.9",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz",
"integrity": "sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==",
"dependencies": {
"@types/react": "*"
}
@@ -4339,6 +4353,15 @@
"node": ">=8"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"optional": true,
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -4764,9 +4787,9 @@
}
},
"node_modules/cacheable-request/node_modules/normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
"dev": true,
"engines": {
"node": ">=8"
@@ -8419,6 +8442,12 @@
"node": ">= 10"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"optional": true
},
"node_modules/filesize": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
@@ -8860,6 +8889,19 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -13252,6 +13294,12 @@
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
},
"node_modules/nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
"optional": true
},
"node_modules/nanoid": {
"version": "3.1.23",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
@@ -14247,9 +14295,9 @@
}
},
"node_modules/path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
@@ -14499,9 +14547,9 @@
}
},
"node_modules/postcss": {
"version": "7.0.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
"version": "7.0.36",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
"integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
"dependencies": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
@@ -14509,6 +14557,10 @@
},
"engines": {
"node": ">=6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
}
},
"node_modules/postcss-attribute-case-insensitive": {
@@ -16948,9 +17000,9 @@
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
},
"node_modules/resolve-url-loader": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.3.tgz",
"integrity": "sha512-WbDSNFiKPPLem1ln+EVTE+bFUBdTTytfQZWbmghroaFNFaAVmGq0Saqw6F/306CwgPXsGwXVxbODE+3xAo/YbA==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.4.tgz",
"integrity": "sha512-D3sQ04o0eeQEySLrcz4DsX3saHfsr8/N6tfhblxgZKXxMT2Louargg12oGNfoTRLV09GXhVUe5/qgA5vdgNigg==",
"dependencies": {
"adjust-sourcemap-loader": "3.0.0",
"camelcase": "5.3.1",
@@ -16958,7 +17010,7 @@
"convert-source-map": "1.7.0",
"es6-iterator": "2.0.3",
"loader-utils": "1.2.3",
"postcss": "7.0.21",
"postcss": "7.0.36",
"rework": "1.0.1",
"rework-visit": "1.0.0",
"source-map": "0.6.1"
@@ -17007,19 +17059,6 @@
"node": ">=4.0.0"
}
},
"node_modules/resolve-url-loader/node_modules/postcss": {
"version": "7.0.21",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz",
"integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==",
"dependencies": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
"supports-color": "^6.1.0"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/resolve-url-loader/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -17028,17 +17067,6 @@
"node": ">=0.10.0"
}
},
"node_modules/resolve-url-loader/node_modules/supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/responselike": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
@@ -18669,9 +18697,9 @@
}
},
"node_modules/tar": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
"integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
"version": "6.1.11",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@@ -19249,9 +19277,9 @@
}
},
"node_modules/typescript": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -19570,9 +19598,9 @@
}
},
"node_modules/url-parse": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@@ -20086,6 +20114,24 @@
"node": ">=6"
}
},
"node_modules/webpack-dev-server/node_modules/fsevents": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"dependencies": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
},
"engines": {
"node": ">= 4.0"
}
},
"node_modules/webpack-dev-server/node_modules/glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@@ -23150,13 +23196,13 @@
}
},
"@material-ui/core": {
"version": "4.11.4",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.4.tgz",
"integrity": "sha512-oqb+lJ2Dl9HXI9orc6/aN8ZIAMkeThufA5iZELf2LQeBn2NtjVilF5D2w7e9RpntAzDb4jK5DsVhkfOvFY/8fg==",
"version": "4.12.3",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
"integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==",
"requires": {
"@babel/runtime": "^7.4.4",
"@material-ui/styles": "^4.11.4",
"@material-ui/system": "^4.11.3",
"@material-ui/system": "^4.12.1",
"@material-ui/types": "5.1.0",
"@material-ui/utils": "^4.11.2",
"@types/react-transition-group": "^4.2.0",
@@ -23200,9 +23246,9 @@
}
},
"@material-ui/system": {
"version": "4.11.3",
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.3.tgz",
"integrity": "sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz",
"integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==",
"requires": {
"@babel/runtime": "^7.4.4",
"@material-ui/utils": "^4.11.2",
@@ -23611,9 +23657,9 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
},
"@types/lodash": {
"version": "4.14.168",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz",
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q=="
"version": "4.14.173",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.173.tgz",
"integrity": "sha512-vv0CAYoaEjCw/mLy96GBTnRoZrSxkGE0BKzKimdR8P3OzrNYNvBgtW7p055A+E8C31vXNUhWKoFCbhq7gbyhFg=="
},
"@types/material-ui": {
"version": "0.21.8",
@@ -23630,9 +23676,9 @@
"integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA=="
},
"@types/node": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
"integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA=="
"version": "12.20.25",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.25.tgz",
"integrity": "sha512-hcTWqk7DR/HrN9Xe7AlJwuCaL13Vcd9/g/T54YrJz4Q3ESM5mr33YCzW2bOfzSIc3aZMeGBvbLGvgN6mIJ0I5Q=="
},
"@types/normalize-package-data": {
"version": "2.4.0",
@@ -23660,9 +23706,9 @@
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
},
"@types/react": {
"version": "17.0.4",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
"integrity": "sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==",
"version": "17.0.22",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.22.tgz",
"integrity": "sha512-kq/BMeaAVLJM6Pynh8C2rnr/drCK+/5ksH0ch9asz+8FW3DscYCIEFtCeYTFeIx/ubvOsMXmRfy7qEJ76gM96A==",
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -23685,9 +23731,9 @@
}
},
"@types/react-dom": {
"version": "17.0.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
"integrity": "sha512-4NnJbCeWE+8YBzupn/YrJxZ8VnjcJq5iR1laqQ1vkpQgBiA7bwk0Rp24fxsdNinzJY2U+HHS4dJJDPdoMjdJ7w==",
"version": "17.0.9",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz",
"integrity": "sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==",
"requires": {
"@types/react": "*"
}
@@ -25105,6 +25151,15 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -25476,9 +25531,9 @@
"dev": true
},
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
"dev": true
}
}
@@ -28464,6 +28519,12 @@
"tslib": "^2.0.3"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"optional": true
},
"filesize": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
@@ -28843,6 +28904,12 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -32383,6 +32450,12 @@
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
},
"nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
"optional": true
},
"nanoid": {
"version": "3.1.23",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
@@ -33197,9 +33270,9 @@
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"path-to-regexp": {
"version": "0.1.7",
@@ -33389,9 +33462,9 @@
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
},
"postcss": {
"version": "7.0.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
"version": "7.0.36",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
"integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
@@ -35421,9 +35494,9 @@
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
},
"resolve-url-loader": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.3.tgz",
"integrity": "sha512-WbDSNFiKPPLem1ln+EVTE+bFUBdTTytfQZWbmghroaFNFaAVmGq0Saqw6F/306CwgPXsGwXVxbODE+3xAo/YbA==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.4.tgz",
"integrity": "sha512-D3sQ04o0eeQEySLrcz4DsX3saHfsr8/N6tfhblxgZKXxMT2Louargg12oGNfoTRLV09GXhVUe5/qgA5vdgNigg==",
"requires": {
"adjust-sourcemap-loader": "3.0.0",
"camelcase": "5.3.1",
@@ -35431,7 +35504,7 @@
"convert-source-map": "1.7.0",
"es6-iterator": "2.0.3",
"loader-utils": "1.2.3",
"postcss": "7.0.21",
"postcss": "7.0.36",
"rework": "1.0.1",
"rework-visit": "1.0.0",
"source-map": "0.6.1"
@@ -35465,28 +35538,10 @@
"json5": "^1.0.1"
}
},
"postcss": {
"version": "7.0.21",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz",
"integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==",
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
"supports-color": "^6.1.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
@@ -36905,9 +36960,9 @@
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA=="
},
"tar": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
"integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
"version": "6.1.11",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
"requires": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@@ -37376,9 +37431,9 @@
}
},
"typescript": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg=="
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA=="
},
"unbox-primitive": {
"version": "1.0.1",
@@ -37647,9 +37702,9 @@
}
},
"url-parse": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"requires": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@@ -38299,6 +38354,16 @@
"locate-path": "^3.0.0"
}
},
"fsevents": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
},
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",

View File

@@ -3,13 +3,13 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.11.4",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@msgpack/msgpack": "^2.7.0",
"@types/lodash": "^4.14.168",
"@types/node": "^15.0.1",
"@types/react": "^17.0.4",
"@types/react-dom": "^17.0.3",
"@types/lodash": "^4.14.172",
"@types/node": "^12.20.20",
"@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9",
"@types/react-material-ui-form-validator": "^2.1.0",
"@types/react-router": "^5.1.13",
"@types/react-router-dom": "^5.1.7",
@@ -30,7 +30,7 @@
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"sockette": "^2.0.6",
"typescript": "4.2.4",
"typescript": "4.3.5",
"zlib": "^1.0.5"
},
"scripts": {

View File

@@ -3,12 +3,12 @@ import { Component } from 'react';
import { CssBaseline } from '@material-ui/core';
import {
MuiThemeProvider,
createMuiTheme,
createTheme,
StylesProvider
} from '@material-ui/core/styles';
import { blueGrey, orange, red, green } from '@material-ui/core/colors';
const theme = createMuiTheme({
const theme = createTheme({
palette: {
type: 'dark',
primary: {

View File

@@ -22,25 +22,13 @@ interface UnauthenticatedRouteProps
| React.ComponentType<any>;
}
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
class UnauthenticatedRoute extends Route<UnauthenticatedRouteProps> {
public render() {
const {
authenticationContext,
component: Component,
features,
...rest
} = this.props;
const renderComponent: RenderComponent = (props) => {
if (authenticationContext.me) {
return <Redirect to={Authentication.fetchLoginRedirect(features)} />;
}
if (Component) {
return <Component {...props} />;
}
};
return <Route {...rest} render={renderComponent} />;
const { authenticationContext, features, ...rest } = this.props;
if (authenticationContext.me) {
return <Redirect to={Authentication.fetchLoginRedirect(features)} />;
}
return <Route {...rest} />;
}
}

View File

@@ -0,0 +1,56 @@
import { FC } from 'react';
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
import { Button, LinearProgress, Typography } from '@material-ui/core';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
loadingSettings: {
margin: theme.spacing(0.5)
},
loadingSettingsDetails: {
margin: theme.spacing(4),
textAlign: 'center'
},
button: {
marginRight: theme.spacing(2),
marginTop: theme.spacing(2)
}
})
);
interface FormLoaderProps {
errorMessage?: string;
loadData: () => void;
}
const FormLoader: FC<FormLoaderProps> = ({ errorMessage, loadData }) => {
const classes = useStyles();
if (errorMessage) {
return (
<div className={classes.loadingSettings}>
<Typography variant="h6" className={classes.loadingSettingsDetails}>
{errorMessage}
</Typography>
<Button
variant="contained"
color="secondary"
className={classes.button}
onClick={loadData}
>
Retry
</Button>
</div>
);
}
return (
<div className={classes.loadingSettings}>
<LinearProgress className={classes.loadingSettingsDetails} />
<Typography variant="h6" className={classes.loadingSettingsDetails}>
Loading&hellip;
</Typography>
</div>
);
};
export default FormLoader;

View File

@@ -5,6 +5,7 @@ export { default as HighlightAvatar } from './HighlightAvatar';
export { default as MenuAppBar } from './MenuAppBar';
export { default as PasswordValidator } from './PasswordValidator';
export { default as RestFormLoader } from './RestFormLoader';
export { default as FormLoader } from './FormLoader';
export { default as SectionContent } from './SectionContent';
export { default as WebSocketFormLoader } from './WebSocketFormLoader';
export { default as ErrorButton } from './ErrorButton';

View File

@@ -1,58 +1,31 @@
import { Component } from 'react';
import { FC } from 'react';
import { Features } from './types';
import { FeaturesContext } from './FeaturesContext';
import FullScreenLoading from '../components/FullScreenLoading';
import ApplicationError from '../components/ApplicationError';
import { FEATURES_ENDPOINT } from '../api';
import { useRest } from '../hooks';
interface FeaturesWrapperState {
features?: Features;
error?: string;
}
import { Features } from './types';
import { FeaturesContext } from './FeaturesContext';
class FeaturesWrapper extends Component<{}, FeaturesWrapperState> {
state: FeaturesWrapperState = {};
const FeaturesWrapper: FC = ({ children }) => {
const { data: features, errorMessage: error } = useRest<Features>({
endpoint: FEATURES_ENDPOINT
});
componentDidMount() {
this.fetchFeaturesDetails();
if (features) {
return (
<FeaturesContext.Provider value={{ features }}>
{children}
</FeaturesContext.Provider>
);
}
fetchFeaturesDetails = () => {
fetch(FEATURES_ENDPOINT)
.then((response) => {
if (response.status === 200) {
return response.json();
} else {
throw Error('Unexpected status code: ' + response.status);
}
})
.then((features) => {
this.setState({ features });
})
.catch((error) => {
this.setState({ error: error.message });
});
};
render() {
const { features, error } = this.state;
if (features) {
return (
<FeaturesContext.Provider
value={{
features
}}
>
{this.props.children}
</FeaturesContext.Provider>
);
}
if (error) {
return <ApplicationError error={error} />;
}
return <FullScreenLoading />;
if (error) {
return <ApplicationError error={error} />;
}
}
return <FullScreenLoading />;
};
export default FeaturesWrapper;

View File

@@ -0,0 +1,2 @@
export { default as useRest } from './useRest';
export { default as useAuthorizedRest } from './useAuthorizedRest';

View File

@@ -0,0 +1,12 @@
import { redirectingAuthorizedFetch } from '../authentication';
import useRest, { RestRequestOptions } from './useRest';
const useAuthorizedRest = <D>({
endpoint
}: Omit<RestRequestOptions, 'fetchFunction'>) =>
useRest<D>({
endpoint,
fetchFunction: redirectingAuthorizedFetch
});
export default useAuthorizedRest;

View File

@@ -0,0 +1,79 @@
import { useCallback, useEffect, useState } from 'react';
import { useSnackbar } from 'notistack';
export interface RestRequestOptions {
endpoint: string;
fetchFunction?: typeof fetch;
}
const useRest = <D>({
endpoint,
fetchFunction = fetch
}: RestRequestOptions) => {
const { enqueueSnackbar } = useSnackbar();
const [saving, setSaving] = useState<boolean>(false);
const [data, setData] = useState<D>();
const [errorMessage, setErrorMessage] = useState<string>();
const handleError = useCallback(
(error: any) => {
const errorMessage = error.message || 'Unknown error';
enqueueSnackbar('Problem fetching: ' + errorMessage, {
variant: 'error'
});
setErrorMessage(errorMessage);
},
[enqueueSnackbar]
);
const loadData = useCallback(async () => {
setData(undefined);
setErrorMessage(undefined);
try {
const response = await fetchFunction(endpoint);
if (response.status !== 200) {
throw Error('Invalid status code: ' + response.status);
}
setData(await response.json());
} catch (error) {
handleError(error);
}
}, [handleError, fetchFunction, endpoint]);
const save = useCallback(
async (data: D) => {
setSaving(true);
setErrorMessage(undefined);
try {
const response = await fetchFunction(endpoint, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
});
if (response.status !== 200) {
throw Error('Invalid status code: ' + response.status);
}
enqueueSnackbar('Update successful.', { variant: 'success' });
setData(await response.json());
} catch (error) {
handleError(error);
} finally {
setSaving(false);
}
},
[enqueueSnackbar, handleError, fetchFunction, endpoint]
);
const saveData = () => data && save(data);
useEffect(() => {
loadData();
}, [loadData]);
return { loadData, saveData, saving, setData, data, errorMessage } as const;
};
export default useRest;

View File

@@ -187,19 +187,16 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
<MenuItem value={1}>Nested on a single topic</MenuItem>
<MenuItem value={2}>As individual topics</MenuItem>
</SelectValidator>
<SelectValidator
name="subscribe_format"
label="Subscribe Format"
value={data.subscribe_format}
fullWidth
variant="outlined"
onChange={handleValueChange('subscribe_format')}
margin="normal"
>
<MenuItem value={0}>General device topic</MenuItem>
<MenuItem value={1}>Individual topics, main heating circuit</MenuItem>
<MenuItem value={2}>Individual topics, all heating circuits</MenuItem>
</SelectValidator>
<BlockFormControlLabel
control={
<Checkbox
checked={data.send_response}
onChange={handleValueChange('send_response')}
value="send_response"
/>
}
label="Publish command output to a 'response' topic"
/>
<BlockFormControlLabel
control={
<Checkbox

View File

@@ -39,5 +39,5 @@ export interface MqttSettings {
ha_enabled: boolean;
ha_climate_format: number;
nested_format: number;
subscribe_format: number;
send_response: boolean;
}

View File

@@ -10,9 +10,7 @@ export const BOARD_PROFILES: BoardProfiles = {
NODEMCU: 'NodeMCU 32S',
'MH-ET': 'MH-ET Live D1 Mini',
LOLIN: 'Lolin D32',
OLIMEX: 'Olimex ESP32-EVB',
TLK110: 'Generic Ethernet (TLK110)',
LAN8720: 'Generic Ethernet (LAN8720)'
OLIMEX: 'Olimex ESP32-EVB'
};
export function boardProfileSelectItems() {

View File

@@ -59,19 +59,32 @@ export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + 'deviceData';
export const WRITE_VALUE_ENDPOINT = ENDPOINT_ROOT + 'writeValue';
export const WRITE_SENSOR_ENDPOINT = ENDPOINT_ROOT + 'writeSensor';
const StyledTableCell = withStyles((theme: Theme) =>
const StyledTableRow = withStyles((theme: Theme) =>
createStyles({
head: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white
root: {
'&:nth-child(even)': {
backgroundColor: '#4e4e4e'
},
'&:hover': {
backgroundColor: theme.palette.info.light
}
},
body: {
fontSize: 14
selected: {
backgroundColor: theme.palette.common.white
}
})
)(TableCell);
)(TableRow);
const CustomTooltip = withStyles((theme: Theme) => ({
const StyledTableRowHeader = withStyles((theme: Theme) =>
createStyles({
head: {
backgroundColor: theme.palette.common.black
}
})
)(TableRow);
const StyledTooltip = withStyles((theme: Theme) => ({
tooltip: {
backgroundColor: theme.palette.secondary.main,
color: 'white',
@@ -82,10 +95,10 @@ const CustomTooltip = withStyles((theme: Theme) => ({
}))(Tooltip);
function compareDevices(a: Device, b: Device) {
if (a.type < b.type) {
if (a.t < b.t) {
return -1;
}
if (a.type > b.type) {
if (a.t > b.t) {
return 1;
}
return 0;
@@ -104,6 +117,9 @@ type EMSESPDataFormProps = RestFormProps<EMSESPData> &
AuthenticatedContextProps &
WithWidthProps;
const pluralize = (count: number, noun: string, suffix = 's') =>
` ${Intl.NumberFormat().format(count)} ${noun}${count !== 1 ? suffix : ''} `;
export const formatDuration = (duration_min: number) => {
const { days, hours, minutes } = parseMilliseconds(duration_min * 60000);
let formatted = '';
@@ -119,30 +135,29 @@ export const formatDuration = (duration_min: number) => {
return formatted;
};
const pluralize = (count: number, noun: string, suffix = 's') =>
` ${count} ${noun}${count !== 1 ? suffix : ''} `;
function formatValue(value: any, uom: number, digit: number) {
function formatValue(value: any, uom: number) {
switch (uom) {
case DeviceValueUOM.HOURS:
return value ? formatDuration(value * 60) : '0 hours';
case DeviceValueUOM.MINUTES:
return value ? formatDuration(value) : '0 minutes';
case DeviceValueUOM.NONE:
case DeviceValueUOM.LIST:
if (typeof value === 'number') {
return new Intl.NumberFormat().format(value);
}
return value;
case DeviceValueUOM.NUM:
return new Intl.NumberFormat().format(value);
case DeviceValueUOM.BOOLEAN:
return value ? 'on' : 'off';
case DeviceValueUOM.DEGREES:
// always show with one decimal place
return (
new Intl.NumberFormat(undefined, {
minimumFractionDigits: digit
minimumFractionDigits: 1
}).format(value) +
' ' +
DeviceValueUOM_s[uom]
);
case DeviceValueUOM.TIMES:
case DeviceValueUOM.SECONDS:
return pluralize(value, DeviceValueUOM_s[uom]);
default:
return (
new Intl.NumberFormat().format(value) + ' ' + DeviceValueUOM_s[uom]
@@ -217,7 +232,9 @@ class EMSESPDataForm extends Component<
};
sendCommand = (dv: DeviceValue) => {
this.setState({ edit_devicevalue: dv });
if (dv.c && this.props.authenticatedContext.me.admin) {
this.setState({ edit_devicevalue: dv });
}
};
handleSensorChange = (name: keyof Sensor) => (
@@ -243,10 +260,10 @@ class EMSESPDataForm extends Component<
body: JSON.stringify({
// because input field with type=number doens't like negative values, force it here
sensor: {
no: edit_Sensor?.no,
id: edit_Sensor?.id,
temp: edit_Sensor?.temp,
offset: Number(edit_Sensor?.offset)
no: edit_Sensor?.n, // no
id: edit_Sensor?.i, // id
temp: edit_Sensor?.t, // temp
offset: Number(edit_Sensor?.o) // offset
}
}),
headers: {
@@ -283,7 +300,9 @@ class EMSESPDataForm extends Component<
};
sendSensor = (sn: Sensor) => {
this.setState({ edit_Sensor: sn });
if (this.props.authenticatedContext.me.admin) {
this.setState({ edit_Sensor: sn });
}
};
noDevices = () => {
@@ -298,7 +317,7 @@ class EMSESPDataForm extends Component<
return (this.state.deviceData?.data || []).length === 0;
};
renderDeviceItems() {
renderDevices() {
const { width, data } = this.props;
return (
<TableContainer>
@@ -309,26 +328,24 @@ class EMSESPDataForm extends Component<
{!this.noDevices() && (
<Table
size="small"
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
padding={isWidthDown('xs', width!) ? 'none' : 'normal'}
>
<TableBody>
{data.devices.sort(compareDevices).map((device) => (
<TableRow
hover
key={device.id}
onClick={() => this.handleRowClick(device.id)}
key={device.i}
onClick={() => this.handleRowClick(device.i)}
>
<TableCell>
<CustomTooltip
<StyledTooltip
title={
'DeviceID:0x' +
(
'00' + device.deviceid.toString(16).toUpperCase()
).slice(-2) +
('00' + device.d.toString(16).toUpperCase()).slice(-2) +
' ProductID:' +
device.productid +
device.p +
' Version:' +
device.version
device.v
}
placement="right-end"
>
@@ -337,12 +354,12 @@ class EMSESPDataForm extends Component<
size="small"
variant="outlined"
>
{device.type}
{device.t}
</Button>
</CustomTooltip>
</StyledTooltip>
</TableCell>
<TableCell align="right">
{device.brand + ' ' + device.name}{' '}
{device.b + ' ' + device.n}{' '}
</TableCell>
</TableRow>
))}
@@ -367,7 +384,7 @@ class EMSESPDataForm extends Component<
);
}
renderSensorItems() {
renderSensorData() {
const { data } = this.props;
const me = this.props.authenticatedContext.me;
return (
@@ -377,43 +394,38 @@ class EMSESPDataForm extends Component<
Sensors
</Typography>
{!this.noSensors() && (
<Table size="small" padding="default">
<Table size="small" padding="normal">
<TableHead>
<TableRow>
<StyledTableCell
padding="checkbox"
style={{ width: 18 }}
></StyledTableCell>
<StyledTableCell>Sensor #</StyledTableCell>
<StyledTableCell align="left">ID / Name</StyledTableCell>
<StyledTableCell align="right">Temperature</StyledTableCell>
</TableRow>
<StyledTableRowHeader>
<TableCell padding="checkbox" style={{ width: 18 }}></TableCell>
<TableCell>Dallas Sensor #</TableCell>
<TableCell align="left">ID / Name</TableCell>
<TableCell align="right">Temperature</TableCell>
</StyledTableRowHeader>
</TableHead>
<TableBody>
{data.sensors.map((sensorData) => (
<TableRow key={sensorData.no} hover>
<TableCell padding="checkbox" style={{ width: 18 }}>
<StyledTableRow
key={sensorData.n}
onClick={() => this.sendSensor(sensorData)}
>
<TableCell padding="checkbox">
{me.admin && (
<CustomTooltip title="edit" placement="left-end">
<IconButton
edge="start"
size="small"
aria-label="Edit"
onClick={() => this.sendSensor(sensorData)}
>
<StyledTooltip title="edit" placement="left-end">
<IconButton edge="start" size="small" aria-label="Edit">
<EditIcon color="primary" fontSize="small" />
</IconButton>
</CustomTooltip>
</StyledTooltip>
)}
</TableCell>
<TableCell component="th" scope="row">
{sensorData.no}
{sensorData.n}
</TableCell>
<TableCell align="left">{sensorData.id}</TableCell>
<TableCell align="left">{sensorData.i}</TableCell>
<TableCell align="right">
{formatValue(sensorData.temp, DeviceValueUOM.DEGREES, 1)}
{formatValue(sensorData.t, DeviceValueUOM.DEGREES)}
</TableCell>
</TableRow>
</StyledTableRow>
))}
</TableBody>
</Table>
@@ -421,7 +433,7 @@ class EMSESPDataForm extends Component<
{this.noSensors() && (
<Box color="warning.main" p={0} mt={0} mb={0}>
<Typography variant="body1">
<i>no connected Dallas temperature sensors were detected</i>
<i>no Dallas temperature sensors were detected</i>
</Typography>
</Box>
)}
@@ -429,25 +441,28 @@ class EMSESPDataForm extends Component<
);
}
renderAnalog() {
renderAnalogData() {
const { data } = this.props;
return (
<TableContainer>
<p></p>
{data.analog > 0 && (
<Table size="small" padding="default">
<Table size="small" padding="normal">
<TableHead>
<TableRow>
<StyledTableCell>Sensortype</StyledTableCell>
<StyledTableCell align="right">Value</StyledTableCell>
</TableRow>
<StyledTableRowHeader>
<TableCell padding="normal" style={{ width: 18 }}></TableCell>
<TableCell>Sensor Type</TableCell>
<TableCell align="right">Value</TableCell>
</StyledTableRowHeader>
</TableHead>
<TableBody>
<TableRow>
<TableCell padding="normal">&nbsp;&nbsp;</TableCell>
<TableCell component="th" scope="row">
Analog Input
</TableCell>
<TableCell align="right">
{formatValue(data.analog, DeviceValueUOM.MV, 0)}
{formatValue(data.analog, DeviceValueUOM.MV)}
</TableCell>
</TableRow>
</TableBody>
@@ -553,7 +568,6 @@ class EMSESPDataForm extends Component<
renderDeviceData() {
const { deviceData } = this.state;
const { width } = this.props;
const me = this.props.authenticatedContext.me;
if (this.noDevices()) {
return;
@@ -568,22 +582,24 @@ class EMSESPDataForm extends Component<
<p></p>
<Box bgcolor="info.main" p={1} mt={1} mb={1}>
<Typography variant="body1" color="initial">
{deviceData.name}
{deviceData.type}&nbsp;Data
</Typography>
</Box>
{!this.noDeviceData() && (
<TableContainer>
<Table
size="small"
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
padding={isWidthDown('xs', width!) ? 'none' : 'normal'}
>
<TableHead></TableHead>
<TableBody>
{deviceData.data.map((item, i) => (
<TableRow hover key={i}>
<StyledTableRow
key={i}
onClick={() => this.sendCommand(item)}
>
<TableCell padding="checkbox" style={{ width: 18 }}>
{item.c && me.admin && (
<CustomTooltip
{item.c && this.props.authenticatedContext.me.admin && (
<StyledTooltip
title="change value"
placement="left-end"
>
@@ -591,20 +607,19 @@ class EMSESPDataForm extends Component<
edge="start"
size="small"
aria-label="Edit"
onClick={() => this.sendCommand(item)}
>
<EditIcon color="primary" fontSize="small" />
</IconButton>
</CustomTooltip>
</StyledTooltip>
)}
</TableCell>
<TableCell padding="none" component="th" scope="row">
{item.n}
</TableCell>
<TableCell padding="none" align="right">
{formatValue(item.v, item.u, 0)}
{formatValue(item.v, item.u)}
</TableCell>
</TableRow>
</StyledTableRow>
))}
</TableBody>
</Table>
@@ -613,7 +628,7 @@ class EMSESPDataForm extends Component<
{this.noDeviceData() && (
<Box color="warning.main" p={0} mt={0} mb={0}>
<Typography variant="body1">
<i>No data available for this device</i>
<i>no data available for this device</i>
</Typography>
</Box>
)}
@@ -626,10 +641,10 @@ class EMSESPDataForm extends Component<
return (
<Fragment>
<br></br>
{this.renderDeviceItems()}
{this.renderDevices()}
{this.renderDeviceData()}
{this.renderSensorItems()}
{this.renderAnalog()}
{this.renderSensorData()}
{this.renderAnalogData()}
<br></br>
<Box display="flex" flexWrap="wrap">
<Box flexGrow={1} padding={1}>

View File

@@ -93,6 +93,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
rx_gpio: json.rx_gpio,
tx_gpio: json.tx_gpio,
pbutton_gpio: json.pbutton_gpio,
phy_type: json.phy_type,
board_profile: event.target.value
});
this.setState({ processing: false });
@@ -113,15 +114,15 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
<Box bgcolor="info.main" p={2} mt={2} mb={2}>
<Typography variant="body1">
<i>
Refer to the
visit the&nbsp;
<Link
target="_blank"
href="https://emsesp.github.io/docs/#/Configure-firmware32?id=ems-esp-settings"
href="https://emsesp.github.io/docs/#/Configure-firmware?id=ems-esp-settings"
color="primary"
>
{' documentation'}
{'online documentation'}
</Link>
&nbsp;for information on each setting
&nbsp;for details explaining each setting
</i>
</Typography>
</Box>
@@ -135,7 +136,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
container
spacing={1}
direction="row"
justify="flex-start"
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid item xs={5}>
@@ -172,30 +173,6 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
<MenuItem value={0x12}>Alarm Module (0x12)</MenuItem>
</SelectValidator>
</Grid>
<Grid item xs={6}>
<TextValidator
validators={[
'required',
'isNumber',
'minNumber:0',
'maxNumber:120'
]}
errorMessages={[
'Tx delay is required',
'Must be a number',
'Must be 0 or higher',
'Max value is 120'
]}
name="tx_delay"
label="Tx start delay (seconds)"
fullWidth
variant="outlined"
value={data.tx_delay}
type="number"
onChange={handleValueChange('tx_delay')}
margin="normal"
/>
</Grid>
</Grid>
<br></br>
@@ -232,7 +209,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
container
spacing={1}
direction="row"
justify="flex-start"
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid item xs={4}>
@@ -330,7 +307,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
'Not a valid GPIO'
]}
name="dallas_gpio"
label="Dallas GPIO (0=none)"
label="Dallas GPIO (0=disabled)"
fullWidth
variant="outlined"
value={data.dallas_gpio}
@@ -356,7 +333,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
'Not a valid GPIO'
]}
name="led_gpio"
label="LED GPIO (0=none)"
label="LED GPIO (0=disabled)"
fullWidth
variant="outlined"
value={data.led_gpio}
@@ -365,6 +342,21 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
margin="normal"
/>
</Grid>
<Grid item xs={4}>
<SelectValidator
name="phy_type"
label="PHY Module Type"
value={data.phy_type}
fullWidth
variant="outlined"
onChange={handleValueChange('phy_type')}
margin="normal"
>
<MenuItem value={0}>No Ethernet</MenuItem>
<MenuItem value={1}>LAN8720</MenuItem>
<MenuItem value={2}>TLK110</MenuItem>
</SelectValidator>
</Grid>
</Grid>
)}
@@ -433,7 +425,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
container
spacing={0}
direction="row"
justify="flex-start"
justifyContent="flex-start"
alignItems="flex-start"
>
<BlockFormControlLabel
@@ -467,7 +459,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
container
spacing={1}
direction="row"
justify="flex-start"
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid item xs={4}>
@@ -538,7 +530,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
container
spacing={1}
direction="row"
justify="flex-start"
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid item xs={5}>

View File

@@ -58,18 +58,18 @@ class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
<TableContainer>
<Table
size="small"
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
padding={isWidthDown('xs', width!) ? 'none' : 'normal'}
>
<TableBody>
<TableRow>
<TableCell># Telegrams Received</TableCell>
<TableCell>Telegrams Received</TableCell>
<TableCell align="right">
{formatNumber(data.rx_received)}&nbsp;(quality{' '}
{data.rx_quality}%)
</TableCell>
</TableRow>
<TableRow>
<TableCell># Telegrams Sent</TableCell>
<TableCell>Telegrams Sent</TableCell>
<TableCell align="right">
{formatNumber(data.tx_sent)}&nbsp;(quality {data.tx_quality}
%)

View File

@@ -1,6 +1,5 @@
export interface EMSESPSettings {
tx_mode: number;
tx_delay: number;
ems_bus_id: number;
syslog_enabled: boolean;
syslog_level: number;
@@ -12,6 +11,7 @@ export interface EMSESPSettings {
shower_alert: boolean;
rx_gpio: number;
tx_gpio: number;
phy_type: number;
dallas_gpio: number;
dallas_parasite: boolean;
led_gpio: number;
@@ -42,20 +42,20 @@ export interface EMSESPStatus {
}
export interface Device {
id: number;
type: string;
brand: string;
name: string;
deviceid: number;
productid: number;
version: string;
i: number; // id
t: string; // type
b: string; // brand
n: string; // name
d: number; // deviceid
p: number; // productid
v: string; // version
}
export interface Sensor {
no: number;
id: string;
temp: number;
offset: number;
n: number; // np
i: string; // id
t: number; // temp
o: number; // offset
}
export interface EMSESPData {
@@ -65,15 +65,15 @@ export interface EMSESPData {
}
export interface DeviceValue {
v: any;
u: number;
n: string;
c: string;
l: string[];
v: any; // value, in any format
u: number; // uom
n: string; // name
c: string; // command
l: string[]; // list
}
export interface EMSESPDeviceData {
name: string;
type: string;
data: DeviceValue[];
}
@@ -93,10 +93,9 @@ export enum DeviceValueUOM {
KB,
SECONDS,
DBM,
NUM,
BOOLEAN,
LIST,
MV
MV,
TIMES,
OCLOCK
}
export const DeviceValueUOM_s = [
@@ -113,10 +112,9 @@ export const DeviceValueUOM_s = [
'kW',
'W',
'KB',
'seconds',
'second',
'dBm',
'number',
'on/off',
'',
'mV'
'mV',
'time',
"o'clock"
];

View File

@@ -43,7 +43,7 @@ class SensorForm extends React.Component<SensorFormProps> {
open
>
<DialogTitle id="user-form-dialog-title">
Editing Sensor #{sensor.no}
Editing Sensor #{sensor.n}
</DialogTitle>
<DialogContent dividers>
<TextValidator
@@ -51,8 +51,8 @@ class SensorForm extends React.Component<SensorFormProps> {
errorMessages={['Not a valid name']}
fullWidth
variant="outlined"
value={sensor.id}
onChange={handleSensorChange('id')}
value={sensor.i}
onChange={handleSensorChange('i')}
margin="normal"
label="Name"
name="id"
@@ -67,12 +67,12 @@ class SensorForm extends React.Component<SensorFormProps> {
label="Custom Offset (°C)"
name="offset"
type="number"
value={sensor.offset}
value={sensor.o}
fullWidth
variant="outlined"
InputProps={{ inputProps: { min: '-5', max: '5', step: '0.1' } }}
margin="normal"
onChange={handleSensorChange('offset')}
onChange={handleSensorChange('o')}
/>
</DialogContent>
<DialogActions>

View File

@@ -15,7 +15,7 @@ import {
} from '@material-ui/core';
import { FormButton } from '../components';
import { DeviceValue, DeviceValueUOM, DeviceValueUOM_s } from './EMSESPtypes';
import { DeviceValue, DeviceValueUOM_s } from './EMSESPtypes';
interface ValueFormProps {
devicevalue: DeviceValue;
@@ -25,7 +25,6 @@ interface ValueFormProps {
data: keyof DeviceValue
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
}
class ValueForm extends React.Component<ValueFormProps> {
formRef: RefObject<any> = React.createRef();
@@ -51,7 +50,7 @@ class ValueForm extends React.Component<ValueFormProps> {
>
<DialogTitle id="user-form-dialog-title">Change Value</DialogTitle>
<DialogContent dividers>
{devicevalue.u === DeviceValueUOM.LIST && (
{devicevalue.l && (
<TextField
id="outlined-select-value"
select
@@ -66,34 +65,19 @@ class ValueForm extends React.Component<ValueFormProps> {
))}
</TextField>
)}
{devicevalue.u !== DeviceValueUOM.BOOLEAN &&
devicevalue.u !== DeviceValueUOM.LIST && (
<OutlinedInput
id="value"
value={devicevalue.v}
autoFocus
fullWidth
onChange={handleValueChange('v')}
endAdornment={
<InputAdornment position="end">
{DeviceValueUOM_s[devicevalue.u]}
</InputAdornment>
}
/>
)}
{devicevalue.u === DeviceValueUOM.BOOLEAN && (
<TextField
id="selected-value"
select
{!devicevalue.l && (
<OutlinedInput
id="value"
value={devicevalue.v}
autoFocus
fullWidth
onChange={handleValueChange('v')}
variant="outlined"
>
<MenuItem value="true">on</MenuItem>
<MenuItem value="false">off</MenuItem>
</TextField>
endAdornment={
<InputAdornment position="end">
{DeviceValueUOM_s[devicevalue.u]}
</InputAdornment>
}
/>
)}
<FormHelperText>{devicevalue.n}</FormHelperText>
<Box color="warning.main" p={0} pl={0} pr={0} mt={4} mb={0}>

View File

@@ -150,7 +150,7 @@ class ManageUsersForm extends React.Component<
<ValidatorForm onSubmit={this.onSubmit}>
<Table
size="small"
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
padding={isWidthDown('xs', width!) ? 'none' : 'normal'}
>
<TableHead>
<TableRow>
@@ -197,7 +197,7 @@ class ManageUsersForm extends React.Component<
<TableFooter>
<TableRow>
<TableCell colSpan={2} />
<TableCell align="center" padding="default">
<TableCell align="center" padding="normal">
<Button
startIcon={<PersonAddIcon />}
variant="contained"

View File

@@ -34,8 +34,8 @@ const useStyles = makeStyles((theme: Theme) => ({
},
entry: {
color: '#bbbbbb',
fontFamily: 'Courier New, monospace',
fontSize: '13px',
fontFamily: 'monospace',
fontSize: '14px',
letterSpacing: 'normal',
whiteSpace: 'nowrap'
},
@@ -65,9 +65,7 @@ const useStyles = makeStyles((theme: Theme) => ({
const LogEventConsole: FC<LogEventConsoleProps> = (props) => {
useWindowSize();
const classes = useStyles({ topOffset, leftOffset });
const { events, compact, level } = props;
const filter_events = events.filter((e) => e.l <= level);
const { events, compact } = props;
const styleLevel = (level: LogLevel) => {
switch (level) {
@@ -124,7 +122,7 @@ const LogEventConsole: FC<LogEventConsoleProps> = (props) => {
return (
<Box id="log-window" className={classes.console}>
{filter_events.map((e) => (
{events.map((e) => (
<div className={classes.entry} key={e.i}>
<span>{e.t}</span>
{compact && <span>{paddedLevelLabel(e.l, compact)} </span>}

View File

@@ -45,6 +45,7 @@ interface LogEventControllerState {
compact: boolean;
level: number;
max_messages: number;
last_id: number;
}
type LogEventControllerProps = RestControllerProps<LogSettings>;
@@ -62,7 +63,8 @@ class LogEventController extends Component<
events: [],
compact: false,
level: 6,
max_messages: 25
max_messages: 25,
last_id: 0
};
}
@@ -88,6 +90,11 @@ class LogEventController extends Component<
this.setState({
compact: checked
});
this.send_data(
this.state.level,
this.state.max_messages,
checked as boolean
);
};
fetchLog = () => {
@@ -118,7 +125,11 @@ class LogEventController extends Component<
throw Error('Unexpected status code: ' + response.status);
})
.then((json) => {
this.setState({ level: json.level, max_messages: json.max_messages });
this.setState({
level: json.level,
max_messages: json.max_messages,
compact: json.compact
});
})
.catch((error) => {
const errorMessage = error.message || 'Unknown error';
@@ -148,7 +159,10 @@ class LogEventController extends Component<
const rawData = event.data;
if (typeof rawData === 'string' || rawData instanceof String) {
const event = JSON.parse(rawData as string) as LogEvent;
this.setState((state) => ({ events: [...state.events, event] }));
if (event.i > this.state.last_id) {
this.setState({ last_id: event.i });
this.setState((state) => ({ events: [...state.events, event] }));
}
}
};
@@ -159,22 +173,27 @@ class LogEventController extends Component<
this.setState({
max_messages: value as number
});
this.send_data(this.state.level, value as number);
this.send_data(this.state.level, value as number, this.state.compact);
};
changeLevel = (event: React.ChangeEvent<HTMLSelectElement>) => {
this.setState({
level: parseInt(event.target.value)
});
this.send_data(parseInt(event.target.value), this.state.max_messages);
this.send_data(
parseInt(event.target.value),
this.state.max_messages,
this.state.compact
);
};
send_data = (level: number, max_messages: number) => {
send_data = (level: number, max_messages: number, compact: boolean) => {
redirectingAuthorizedFetch(LOG_SETTINGS_ENDPOINT, {
method: 'POST',
body: JSON.stringify({
level: level,
max_messages: max_messages
max_messages: max_messages,
compact: compact
}),
headers: {
'Content-Type': 'application/json'
@@ -206,29 +225,27 @@ class LogEventController extends Component<
case LogLevel.DEBUG:
return 'D';
case LogLevel.TRACE:
return 'TRACE';
return 'T';
default:
return '';
}
};
onDownload = () => {
const { events, level } = this.state;
const { events } = this.state;
let result = '';
for (const i in events) {
if (events[i].l <= level) {
result +=
events[i].t +
' ' +
this.levelLabel(events[i].l) +
' ' +
events[i].i +
': [' +
events[i].n +
'] ' +
events[i].m +
'\n';
}
result +=
events[i].t +
' ' +
this.levelLabel(events[i].l) +
' ' +
events[i].i +
': [' +
events[i].n +
'] ' +
events[i].m +
'\n';
}
const a = document.createElement('a');
a.setAttribute(
@@ -250,7 +267,7 @@ class LogEventController extends Component<
container
spacing={3}
direction="row"
justify="flex-start"
justifyContent="flex-start"
alignItems="center"
>
<Grid item xs={2}>
@@ -280,11 +297,12 @@ class LogEventController extends Component<
marks={[
{ value: 25, label: '25' },
{ value: 50, label: '50' },
{ value: 75, label: '75' }
{ value: 75, label: '75' },
{ value: 100, label: '100' }
]}
step={25}
min={25}
max={75}
max={100}
onChange={this.changeMaxMessages}
/>
</Grid>

View File

@@ -33,15 +33,19 @@ import {
AuthenticatedContextProps,
withAuthenticatedContext
} from '../authentication';
import { RestFormProps, FormButton, ErrorButton } from '../components';
import { FACTORY_RESET_ENDPOINT, RESTART_ENDPOINT } from '../api';
import { SystemStatus, EspPlatform } from './types';
import VersionCheck from './VersionCheck';
interface SystemStatusFormState {
confirmRestart: boolean;
confirmFactoryReset: boolean;
processing: boolean;
currentVersion?: string;
}
type SystemStatusFormProps = AuthenticatedContextProps &
@@ -61,6 +65,16 @@ class SystemStatusForm extends Component<
processing: false
};
onVersionCheck = (version: string) => {
this.setState({ currentVersion: version });
};
closeVersionCheck = () => {
this.setState({
currentVersion: undefined
});
};
createListItems() {
const { data } = this.props;
return (
@@ -75,7 +89,14 @@ class SystemStatusForm extends Component<
primary="EMS-ESP Version"
secondary={'v' + data.emsesp_version}
/>
<Button
color="primary"
onClick={() => this.onVersionCheck(data.emsesp_version)}
>
Check for updates
</Button>
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
@@ -304,9 +325,16 @@ class SystemStatusForm extends Component<
render() {
const me = this.props.authenticatedContext.me;
const { currentVersion } = this.state;
return (
<Fragment>
<List>{this.createListItems()}</List>
{currentVersion && (
<VersionCheck
currentVersion={currentVersion}
onClose={this.closeVersionCheck}
/>
)}
<Box display="flex" flexWrap="wrap">
<Box flexGrow={1} padding={1}>
<FormButton

View File

@@ -0,0 +1,211 @@
import React, { Fragment } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Box,
Link,
LinearProgress,
Typography
} from '@material-ui/core';
import { FormButton } from '../components';
import { withSnackbar, WithSnackbarProps } from 'notistack';
export const VERSIONCHECK_ENDPOINT =
'https://api.github.com/repos/emsesp/EMS-ESP32/releases/latest';
export const VERSIONCHECK_DEV_ENDPOINT =
'https://api.github.com/repos/emsesp/EMS-ESP32/releases/tags/latest';
export const uploadURL = window.location.origin + '/system/upload';
interface VersionCheckProps extends WithSnackbarProps {
currentVersion: string;
onClose: () => void;
}
interface VersionCheckState {
latestVersion?: string;
latestVersionUrl?: string;
latestVersionChangelog?: string;
latestDevVersion?: string;
latestDevVersionUrl?: string;
latestDevVersionChangelog?: string;
}
class VersionCheck extends React.Component<
VersionCheckProps,
VersionCheckState
> {
state: VersionCheckState = {};
componentDidMount() {
fetch(VERSIONCHECK_ENDPOINT)
.then((response) => {
if (response.status === 200) {
return response.json();
} else {
throw Error(
'Unable to get version information. Check internet connection. (' +
response.status +
')'
);
}
})
.then((data) => {
this.setState({
latestVersion: data.name,
latestVersionUrl: data.assets[1].browser_download_url,
latestVersionChangelog: data.html_url
});
})
.catch((error) => {
this.props.enqueueSnackbar(
error.message || 'Problem getting response',
{ variant: 'error' }
);
this.setState({ latestVersion: undefined });
this.props.onClose();
});
fetch(VERSIONCHECK_DEV_ENDPOINT)
.then((response) => {
if (response.status === 200) {
return response.json();
} else {
throw Error(
'Unable to get version information. Check internet connection. (' +
response.status +
')'
);
}
})
.then((data) => {
this.setState({
latestDevVersion: data.name.split(/\s+/).splice(-1),
latestDevVersionUrl: data.assets[1].browser_download_url,
latestDevVersionChangelog: data.assets[0].browser_download_url
});
})
.catch((error) => {
this.props.enqueueSnackbar(
error.message || 'Problem getting response',
{ variant: 'error' }
);
this.setState({ latestDevVersion: undefined });
this.props.onClose();
});
}
render() {
const { onClose, currentVersion } = this.props;
const {
latestVersion,
latestVersionUrl,
latestDevVersion,
latestDevVersionUrl,
latestVersionChangelog,
latestDevVersionChangelog
} = this.state;
return (
<Dialog
onClose={onClose}
aria-labelledby="version-check-dialog-title"
open
fullWidth
maxWidth="sm"
>
<DialogTitle id="version-check-dialog-title">
Firmware Update Check
</DialogTitle>
<DialogContent dividers>
{latestVersion ? (
<Fragment>
<Box
bgcolor="primary.main"
color="primary.contrastText"
p={2}
mt={2}
mb={2}
>
<Typography variant="body1">
You are currently running EMS-ESP version{' '}
<b>v{currentVersion}</b>
</Typography>
</Box>
<Box mt={2} mb={2}>
The latest <u>stable</u> version is <b>{latestVersion}</b>
&nbsp;(
<Link
target="_blank"
href={latestVersionChangelog}
color="primary"
>
{'release notes'}
</Link>
)&nbsp;(
<Link target="_blank" href={latestVersionUrl} color="primary">
{'download'}
</Link>
)
</Box>
<Box mt={2} mb={2}>
The latest <u>development</u> version is&nbsp;
<b>{latestDevVersion}</b>
&nbsp;(
<Link
target="_blank"
href={latestDevVersionChangelog}
color="primary"
>
{'release notes'}
</Link>
)&nbsp;(
<Link
target="_blank"
href={latestDevVersionUrl}
color="primary"
>
{'download'}
</Link>
)
</Box>
<Box color="warning.main" p={0} pl={0} pr={0} mt={4} mb={0}>
<Typography variant="body2">
<i>
Use&nbsp;
<Link target="_blank" href={uploadURL} color="primary">
{'UPLOAD FIRMWARE'}
</Link>
&nbsp;to install any new firmware versions.
</i>
</Typography>
</Box>
</Fragment>
) : (
<Box m={4} textAlign="center">
<LinearProgress />
<Typography variant="h6">
Fetching version details&hellip;
</Typography>
</Box>
)}
</DialogContent>
<DialogActions>
<FormButton
variant="contained"
color="primary"
type="submit"
onClick={onClose}
>
Close
</FormButton>
</DialogActions>
</Dialog>
);
}
}
export default withSnackbar(VersionCheck);

View File

@@ -58,4 +58,5 @@ export interface LogEvent {
export interface LogSettings {
level: LogLevel;
max_messages: number;
compact: boolean;
}

View File

@@ -0,0 +1,33 @@
type UpdateEntity<S> = (state: (prevState: Readonly<S>) => S) => void;
export const extractEventValue = (
event: React.ChangeEvent<HTMLInputElement>
) => {
switch (event.target.type) {
case 'number':
return event.target.valueAsNumber;
case 'checkbox':
return event.target.checked;
default:
return event.target.value;
}
};
export const updateValue = <S>(updateEntity: UpdateEntity<S>) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
updateEntity((prevState) => ({
...prevState,
[event.target.name]: extractEventValue(event)
}));
};
export const updateBooleanValue = <S>(updateEntity: UpdateEntity<S>) => (
name: string,
value?: boolean
) => {
updateEntity((prevState) => ({
...prevState,
[name]: value
}));
};

View File

@@ -0,0 +1 @@
export * from './binding';

View File

@@ -1,6 +1,14 @@
ArduinoJson: change log
=======================
v6.18.4 (2021-09-06)
-------
* Fixed error `'dummy' may be used uninitialized` on GCC 11
* Fixed error `expected unqualified-id before 'const'` on GCC 11 (issue #1622)
* Filter: exact match takes precedence over wildcard (issue #1628)
* Fixed deserialization of `\u0000` (issue #1646)
v6.18.3 (2021-07-27)
-------

View File

@@ -2,7 +2,7 @@
---
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.18.3)](https://www.ardu-badge.com/ArduinoJson/6.18.3)
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.18.4)](https://www.ardu-badge.com/ArduinoJson/6.18.4)
[![Continuous Integration](https://github.com/bblanchon/ArduinoJson/workflows/Continuous%20Integration/badge.svg?branch=6.x)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A6.x)
[![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
@@ -78,7 +78,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
* [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)
* Continuously tested on
* [Visual Studio 2010, 2012, 2013, 2015, 2017, 2019](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
* [GCC 4.4, 4.6, 4.7, 4.8, 4.9, 5, 6, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [GCC 4.4, 4.6, 4.7, 4.8, 4.9, 5, 6, 7, 8, 9, 10, 11](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [Clang 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 5.0, 6.0, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
* Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/)

View File

@@ -32,8 +32,8 @@ class Filter {
Filter operator[](const TKey& key) const {
if (_variant == true) // "true" means "allow recursively"
return *this;
else
return Filter(_variant[key] | _variant["*"]);
VariantConstRef member = _variant[key];
return Filter(member.isNull() ? _variant["*"] : member);
}
private:

View File

@@ -17,7 +17,7 @@ struct Reader {
Reader(TSource& source) : _source(&source) {}
int read() {
return _source->read();
return _source->read(); // Error here? You passed an unsupported input type
}
size_t readBytes(char* buffer, size_t length) {

View File

@@ -13,14 +13,14 @@ template <typename TStringBuilder>
inline void encodeCodepoint(uint32_t codepoint32, TStringBuilder& str) {
// this function was optimize for code size on AVR
// a buffer to store the string in reverse
char buf[5];
char* p = buf;
*(p++) = 0;
if (codepoint32 < 0x80) {
*(p++) = char((codepoint32));
str.append(char(codepoint32));
} else {
// a buffer to store the string in reverse
char buf[5];
char* p = buf;
*(p++) = 0;
*(p++) = char((codepoint32 | 0x80) & 0xBF);
uint16_t codepoint16 = uint16_t(codepoint32 >> 6);
if (codepoint16 < 0x20) { // 0x800
@@ -36,10 +36,10 @@ inline void encodeCodepoint(uint32_t codepoint32, TStringBuilder& str) {
*(p++) = char(codepoint16 | 0xF0);
}
}
}
while (*(--p)) {
str.append(*p);
while (*(--p)) {
str.append(*p);
}
}
}
} // namespace Utf8

View File

@@ -45,8 +45,7 @@ class StringAdapter< ::String> {
template <>
class StringAdapter< ::StringSumHelper> : public StringAdapter< ::String> {
public:
StringAdapter< ::StringSumHelper>(const ::String& s)
: StringAdapter< ::String>(s) {}
StringAdapter(const ::String& s) : StringAdapter< ::String>(s) {}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@@ -45,7 +45,7 @@ class StringAdapter<const char*> {
template <int N>
class StringAdapter<const char[N]> : public StringAdapter<const char*> {
public:
StringAdapter<const char[N]>(const char* s) : StringAdapter<const char*>(s) {}
StringAdapter(const char* s) : StringAdapter<const char*>(s) {}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@@ -27,7 +27,7 @@ struct Converter {
}
static bool checkJson(VariantConstRef src) {
T dummy;
T dummy = T();
// clang-format off
return canConvertFromJson(src, dummy); // Error here? See https://arduinojson.org/v6/unsupported-is/
// clang-format on

View File

@@ -12,7 +12,6 @@
namespace ARDUINOJSON_NAMESPACE {
//
enum {
VALUE_MASK = 0x7F,

View File

@@ -4,7 +4,7 @@
#pragma once
#define ARDUINOJSON_VERSION "6.18.3"
#define ARDUINOJSON_VERSION "6.18.4"
#define ARDUINOJSON_VERSION_MAJOR 6
#define ARDUINOJSON_VERSION_MINOR 18
#define ARDUINOJSON_VERSION_REVISION 3
#define ARDUINOJSON_VERSION_REVISION 4

View File

@@ -103,7 +103,7 @@ static uint32_t _closed_index = []() {
static inline bool _init_async_event_queue() {
if (!_async_queue) {
_async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
_async_queue = xQueueCreate(128, sizeof(lwip_event_packet_t *)); // double queue see https://github.com/emsesp/EMS-ESP32/issues/177
if (!_async_queue) {
return false;
}

View File

@@ -78,6 +78,8 @@ class MsgpackAsyncJsonResponse : public AsyncAbstractResponse {
}
size_t setLength() {
_contentLength = measureMsgPack(_root);
//_headers.add(new AsyncWebHeader("Json-Length", String(_jsonBuffer.memoryUsage()))); // For determening size of EMSESP_JSON_SIZE_XXLARGE_DYN (Sunbuzz)
// Json-Length: 10635
if (_contentLength) {
_isValid = true;
}

View File

@@ -130,7 +130,8 @@ bool PButton::check(void) {
}
// Test for normal click event: DblClickDelay expired
if (state_ == pullMode_ && (millisRes - upTime_) >= DblClickDelay_ && dblClickWaiting_ == true && dblClickOnNextUp_ == false && singleClickOK_ == true && resultEvent != 2) {
if (state_ == pullMode_ && (millisRes - upTime_) >= DblClickDelay_ && dblClickWaiting_ == true && dblClickOnNextUp_ == false && singleClickOK_ == true
&& resultEvent != 2) {
// Serial.println("*single click pressed*");
resultEvent = 1;
dblClickWaiting_ = false;

View File

@@ -57,7 +57,8 @@ class APSettings {
IPAddress subnetMask;
bool operator==(const APSettings & settings) const {
return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && localIP == settings.localIP && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask;
return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && localIP == settings.localIP
&& gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask;
}
static void read(APSettings & settings, JsonObject & root) {

View File

@@ -4,7 +4,9 @@ using namespace std::placeholders; // for `_1` etc
APStatus::APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService)
: _apSettingsService(apSettingsService) {
server->on(AP_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
server->on(AP_STATUS_SERVICE_PATH,
HTTP_GET,
securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
}
void APStatus::apStatus(AsyncWebServerRequest * request) {

View File

@@ -1,5 +1,7 @@
#include <ESP8266React.h>
#include <WWWData.h>
ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
: _featureService(server)
, _securitySettingsService(server, fs)

View File

@@ -24,8 +24,6 @@
#include <NetworkSettingsService.h>
#include <NetworkStatus.h>
#include <WWWData.h>
class ESP8266React {
public:
ESP8266React(AsyncWebServer * server, FS * fs);

View File

@@ -7,7 +7,12 @@
template <class T>
class FSPersistence {
public:
FSPersistence(JsonStateReader<T> stateReader, JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, FS * fs, const char * filePath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
FSPersistence(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
FS * fs,
const char * filePath,
size_t bufferSize = FS_BUFFER_SIZE)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)

View File

@@ -4,7 +4,9 @@ using namespace std::placeholders;
FactoryResetService::FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: fs(fs) {
server->on(FACTORY_RESET_SERVICE_PATH, HTTP_POST, securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), AuthenticationPredicates::IS_ADMIN));
server->on(FACTORY_RESET_SERVICE_PATH,
HTTP_POST,
securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), AuthenticationPredicates::IS_ADMIN));
}
void FactoryResetService::handleRequest(AsyncWebServerRequest * request) {

View File

@@ -28,7 +28,11 @@ class HttpGetEndpoint {
server->on(servicePath.c_str(), HTTP_GET, securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, _1), authenticationPredicate));
}
HttpGetEndpoint(JsonStateReader<T> stateReader, StatefulService<T> * statefulService, AsyncWebServer * server, const String & servicePath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader)
, _statefulService(statefulService)
, _bufferSize(bufferSize) {
@@ -70,7 +74,12 @@ class HttpPostEndpoint {
server->addHandler(&_updateHandler);
}
HttpPostEndpoint(JsonStateReader<T> stateReader, JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncWebServer * server, const String & servicePath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)
@@ -124,7 +133,12 @@ class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> {
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, securityManager, authenticationPredicate, bufferSize) {
}
HttpEndpoint(JsonStateReader<T> stateReader, JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncWebServer * server, const String & servicePath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, bufferSize)
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) {
}

View File

@@ -33,7 +33,11 @@ class MqttConnector {
template <class T>
class MqttPub : virtual public MqttConnector<T> {
public:
MqttPub(JsonStateReader<T> stateReader, StatefulService<T> * statefulService, AsyncMqttClient * mqttClient, const String & pubTopic = "", size_t bufferSize = DEFAULT_BUFFER_SIZE)
MqttPub(JsonStateReader<T> stateReader,
StatefulService<T> * statefulService,
AsyncMqttClient * mqttClient,
const String & pubTopic = "",
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: MqttConnector<T>(statefulService, mqttClient, bufferSize)
, _stateReader(stateReader)
, _pubTopic(pubTopic) {
@@ -74,7 +78,11 @@ class MqttPub : virtual public MqttConnector<T> {
template <class T>
class MqttSub : virtual public MqttConnector<T> {
public:
MqttSub(JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncMqttClient * mqttClient, const String & subTopic = "", size_t bufferSize = DEFAULT_BUFFER_SIZE)
MqttSub(JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncMqttClient * mqttClient,
const String & subTopic = "",
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: MqttConnector<T>(statefulService, mqttClient, bufferSize)
, _stateUpdater(stateUpdater)
, _subTopic(subTopic) {

View File

@@ -101,18 +101,25 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
switch (event) {
case SYSTEM_EVENT_STA_GOT_IP:
case SYSTEM_EVENT_ETH_GOT_IP:
emsesp::EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
if (!networkSettings.enableIPv6 && _state.enabled) {
// emsesp::EMSESP::logger().info(F("IPv4 Network connection found, starting MQTT client"));
onConfigUpdated();
}
});
break;
case SYSTEM_EVENT_GOT_IP6:
if (_state.enabled) {
// emsesp::EMSESP::logger().info(F("Network connection found, starting MQTT client"));
// emsesp::EMSESP::logger().info(F("IPv6 Network connection found, starting MQTT client"));
onConfigUpdated();
}
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
case SYSTEM_EVENT_ETH_DISCONNECTED:
if (_state.enabled) {
// emsesp::EMSESP::logger().info(F("Network connection dropped, stopping MQTT client"));
onConfigUpdated();
_mqttClient.disconnect();
// onConfigUpdated();
}
break;
@@ -122,9 +129,11 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
}
void MqttSettingsService::configureMqtt() {
// disconnect if connected
_mqttClient.disconnect();
// only connect if WiFi is connected and MQTT is enabled
if (_state.enabled && emsesp::EMSESP::system_.network_connected()) {
_mqttClient.disconnect();
// emsesp::EMSESP::logger().info(F("Configuring Mqtt client"));
_mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
if (_state.username.length() > 0) {
_mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername),
@@ -137,6 +146,8 @@ void MqttSettingsService::configureMqtt() {
_mqttClient.setCleanSession(_state.cleanSession);
_mqttClient.setMaxTopicLength(_state.maxTopicLength);
_mqttClient.connect();
// } else {
// emsesp::EMSESP::logger().info(F("Error configuring Mqtt client"));
}
}
@@ -164,7 +175,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
root["ha_climate_format"] = settings.ha_climate_format;
root["ha_enabled"] = settings.ha_enabled;
root["nested_format"] = settings.nested_format;
root["subscribe_format"] = settings.subscribe_format;
root["send_response"] = settings.send_response;
}
StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & settings) {
@@ -194,7 +205,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
newSettings.ha_climate_format = root["ha_climate_format"] | EMSESP_DEFAULT_HA_CLIMATE_FORMAT;
newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
newSettings.subscribe_format = root["subscribe_format"] | EMSESP_DEFAULT_SUBSCRIBE_FORMAT;
newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE;
if (newSettings.enabled != settings.enabled) {
changed = true;
@@ -209,7 +220,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
changed = true;
}
if (newSettings.subscribe_format != settings.subscribe_format) {
if (newSettings.send_response != settings.send_response) {
changed = true;
}

View File

@@ -89,7 +89,7 @@ class MqttSettings {
uint8_t ha_climate_format;
bool ha_enabled;
uint8_t nested_format;
uint8_t subscribe_format;
bool send_response;
static void read(MqttSettings & settings, JsonObject & root);
static StateUpdateResult update(JsonObject & root, MqttSettings & settings);

View File

@@ -3,7 +3,9 @@
using namespace std::placeholders; // for `_1` etc
NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(NTP_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
server->on(NTP_STATUS_SERVICE_PATH,
HTTP_GET,
securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
}
/*

View File

@@ -61,7 +61,8 @@ void NetworkSettingsService::manageSTA() {
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); // configure for DHCP
}
WiFi.setHostname(_state.hostname.c_str()); // set hostname
WiFi.setHostname(_state.hostname.c_str()); // set hostname
// www.esp32.com/viewtopic.php?t=12055
read([&](NetworkSettings & networkSettings) {
if (networkSettings.bandwidth20) {
@@ -73,33 +74,18 @@ void NetworkSettingsService::manageSTA() {
if (networkSettings.nosleep) {
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
}
});
WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); // attempt to connect to the network
}
}
// handles both WiFI and Ethernet
// handles if wifi stopped
void NetworkSettingsService::WiFiEvent(WiFiEvent_t event) {
switch (event) {
case SYSTEM_EVENT_STA_DISCONNECTED:
WiFi.disconnect(true);
break;
case SYSTEM_EVENT_STA_STOP:
if (event == SYSTEM_EVENT_STA_STOP) {
if (_stopping) {
_lastConnectionAttempt = 0;
_stopping = false;
}
break;
case SYSTEM_EVENT_ETH_START:
if (_state.staticIPConfig) {
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); // configure for static IP
}
break;
default:
break;
}
}

View File

@@ -5,7 +5,9 @@
using namespace std::placeholders; // for `_1` etc
NetworkStatus::NetworkStatus(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(NETWORK_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&NetworkStatus::networkStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
server->on(NETWORK_STATUS_SERVICE_PATH,
HTTP_GET,
securityManager->wrapRequest(std::bind(&NetworkStatus::networkStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
}
void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {

View File

@@ -59,6 +59,7 @@ void OTASettingsService::configureArduinoOTA() {
Serial.println(F("End Failed"));
});
_arduinoOTA->setMdnsEnabled(false); // disable as handled in NetworkSettingsService.cpp. https://github.com/emsesp/EMS-ESP32/issues/161
_arduinoOTA->begin();
}
}

View File

@@ -4,8 +4,6 @@
#include <HttpEndpoint.h>
#include <FSPersistence.h>
#include <ESPmDNS.h>
#include <ArduinoOTA.h>
#include <WiFiUdp.h>

View File

@@ -9,7 +9,9 @@ SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * f
, _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
, _jwtHandler(FACTORY_JWT_SECRET) {
addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false);
server->on(GENERATE_TOKEN_PATH, HTTP_GET, wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
server->on(GENERATE_TOKEN_PATH,
HTTP_GET,
wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
}
void SecuritySettingsService::begin() {
@@ -109,19 +111,19 @@ ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequest
};
}
void SecuritySettingsService::generateToken(AsyncWebServerRequest* request) {
AsyncWebParameter* usernameParam = request->getParam("username");
for (User _user : _state.users) {
if (_user.username == usernameParam->value()) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, GENERATE_TOKEN_SIZE);
JsonObject root = response->getRoot();
root["token"] = generateJWT(&_user);
response->setLength();
request->send(response);
return;
void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) {
AsyncWebParameter * usernameParam = request->getParam("username");
for (User _user : _state.users) {
if (_user.username == usernameParam->value()) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, GENERATE_TOKEN_SIZE);
JsonObject root = response->getRoot();
root["token"] = generateJWT(&_user);
response->setLength();
request->send(response);
return;
}
}
}
request->send(401);
request->send(401);
}
#else

View File

@@ -13,6 +13,10 @@
#define DEFAULT_BUFFER_SIZE 2048
#endif
#ifndef FS_BUFFER_SIZE
#define FS_BUFFER_SIZE 4096
#endif
enum class StateUpdateResult {
CHANGED = 0, // The update changed the state and propagation should take place if required
UNCHANGED, // The state was unchanged, propagation should not take place

View File

@@ -4,7 +4,10 @@ using namespace std::placeholders; // for `_1` etc
UploadFirmwareService::UploadFirmwareService(AsyncWebServer * server, SecurityManager * securityManager)
: _securityManager(securityManager) {
server->on(UPLOAD_FIRMWARE_PATH, HTTP_POST, std::bind(&UploadFirmwareService::uploadComplete, this, _1), std::bind(&UploadFirmwareService::handleUpload, this, _1, _2, _3, _4, _5, _6));
server->on(UPLOAD_FIRMWARE_PATH,
HTTP_POST,
std::bind(&UploadFirmwareService::uploadComplete, this, _1),
std::bind(&UploadFirmwareService::handleUpload, this, _1, _2, _3, _4, _5, _6));
}
void UploadFirmwareService::handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {

View File

@@ -20,7 +20,12 @@ class WebSocketConnector {
AsyncWebSocket _webSocket;
size_t _bufferSize;
WebSocketConnector(StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, SecurityManager * securityManager, AuthenticationPredicate authenticationPredicate, size_t bufferSize)
WebSocketConnector(StatefulService<T> * statefulService,
AsyncWebServer * server,
const char * webSocketPath,
SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate,
size_t bufferSize)
: _statefulService(statefulService)
, _server(server)
, _webSocket(webSocketPath)
@@ -67,7 +72,11 @@ class WebSocketTx : virtual public WebSocketConnector<T> {
WebSocketConnector<T>::_statefulService->addUpdateHandler([&](const String & originId) { transmitData(nullptr, originId); }, false);
}
WebSocketTx(JsonStateReader<T> stateReader, StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
WebSocketTx(JsonStateReader<T> stateReader,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const char * webSocketPath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
, _stateReader(stateReader) {
WebSocketConnector<T>::_statefulService->addUpdateHandler([&](const String & originId) { transmitData(nullptr, originId); }, false);
@@ -140,7 +149,11 @@ class WebSocketRx : virtual public WebSocketConnector<T> {
, _stateUpdater(stateUpdater) {
}
WebSocketRx(JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
WebSocketRx(JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const char * webSocketPath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
, _stateUpdater(stateUpdater) {
}
@@ -182,7 +195,12 @@ class WebSocketTxRx : public WebSocketTx<T>, public WebSocketRx<T> {
, WebSocketRx<T>(stateUpdater, statefulService, server, webSocketPath, securityManager, authenticationPredicate, bufferSize) {
}
WebSocketTxRx(JsonStateReader<T> stateReader, JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
WebSocketTxRx(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
AsyncWebServer * server,
const char * webSocketPath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
, WebSocketTx<T>(stateReader, statefulService, server, webSocketPath, bufferSize)
, WebSocketRx<T>(stateUpdater, statefulService, server, webSocketPath, bufferSize) {

View File

@@ -3,8 +3,12 @@
using namespace std::placeholders; // for `_1` etc
WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(SCAN_NETWORKS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
server->on(LIST_NETWORKS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
server->on(SCAN_NETWORKS_SERVICE_PATH,
HTTP_GET,
securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
server->on(LIST_NETWORKS_SERVICE_PATH,
HTTP_GET,
securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
};
void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {

View File

@@ -25,6 +25,10 @@
namespace uuid {
std::string read_flash_string(const __FlashStringHelper * flash_str) {
if (flash_str == nullptr) {
return std::string(""); // prevent crash
}
std::string str(::strlen_P(reinterpret_cast<PGM_P>(flash_str)), '\0');
::strncpy_P(&str[0], reinterpret_cast<PGM_P>(flash_str), str.capacity() + 1);

View File

@@ -434,11 +434,23 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
udp_.print('-');
}
udp_.printf_P(PSTR(" %s %s - - - \xEF\xBB\xBF"), hostname_.c_str(), uuid::read_flash_string(message.content_->name).c_str());
udp_.printf_P(PSTR(" %s %s - - - "), hostname_.c_str(), uuid::read_flash_string(message.content_->name).c_str());
udp_.print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3).c_str());
udp_.printf_P(PSTR(" %c %lu: "), uuid::log::format_level_char(message.content_->level), message.id_);
udp_.print(message.content_->text.c_str());
char id_c_str[15];
snprintf_P(id_c_str, sizeof(id_c_str), PSTR(" %lu: "), message.id_);
std::string msgstr = uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3) +
' ' +
uuid::log::format_level_char(message.content_->level) +
id_c_str +
message.content_->text;
for (uint16_t i = 0; i < msgstr.length(); i++) {
if (msgstr.at(i) & 0x80) {
udp_.print("\xEF\xBB\xBF");
// udp_.print("<BOM>"); // marker for testing if BOM is created for udf-8
break;
}
}
udp_.print(msgstr.c_str());
bool ok = (udp_.endPacket() == 1);
last_transmit_ = uuid::get_uptime_ms();

View File

@@ -30,7 +30,9 @@
#include <iostream>
#define IPAddress std::string
// #define IPAddress std::string
#define IPAddress String
#define ICACHE_FLASH_ATTR
#define ICACHE_RAM_ATTR
#define os_event_t void
@@ -49,6 +51,8 @@
#define OUTPUT 1
#define INPUT_PULLUP 2
#define snprintf snprintf_P // to keep backwards compatibility
void pinMode(uint8_t pin, uint8_t mode);
void digitalWrite(uint8_t pin, uint8_t value);
int digitalRead(uint8_t pin);

View File

@@ -35,11 +35,11 @@ class DummySettings {
uint8_t mqtt_qos = 0;
bool mqtt_retain = false;
bool enabled = true;
uint8_t nested_format = 1;
uint8_t nested_format = 1; // 1=nested 2=single
uint8_t ha_climate_format = 1;
bool ha_enabled = true;
String base = "ems-esp";
uint8_t subscribe_format = 0;
bool send_response = true;
String hostname = "ems-esp";
String jwtSecret = "ems-esp";
@@ -48,7 +48,7 @@ class DummySettings {
String localIP = "";
String gatewayIP = "";
String subnetMask = "";
String staticIPConfig = "";
bool staticIPConfig = false;
String dnsIP1 = "";
String dnsIP2 = "";
String board_profile = "CUSTOM";

0
lib_standalone/ESPmDNS.h Normal file
View File

View File

@@ -12,7 +12,7 @@ class FSPersistence {
StatefulService<T> * statefulService,
FS * fs,
const char * filePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
size_t bufferSize = FS_BUFFER_SIZE)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)

View File

@@ -158,9 +158,12 @@ class WiFiClass {
return 0;
};
void disconnect(bool v){};
char * getHostname() {
return nullptr;
}
char * localIP() {
return nullptr;
}
@@ -172,16 +175,23 @@ class ETHClass {
return false;
};
void setHostname(const char * s){};
void setHostname(const char * s){};
char * getHostname() {
return nullptr;
}
char * localIP() {
return nullptr;
}
int linkSpeed() {
return 100;
}
bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) {
return false;
}
};

View File

@@ -11,6 +11,10 @@
#define DEFAULT_BUFFER_SIZE 2048
#endif
#ifndef FS_BUFFER_SIZE
#define FS_BUFFER_SIZE 4096
#endif
enum class StateUpdateResult {
CHANGED = 0, // The update changed the state and propagation should take place if required
UNCHANGED, // The state was unchanged, propagation should not take place

Binary file not shown.

Before

Width:  |  Height:  |  Size: 548 KiB

After

Width:  |  Height:  |  Size: 548 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -225,7 +225,7 @@ const mqtt_settings = {
ha_climate_format: 1,
ha_enabled: true,
nested_format: 1,
subscribe_format: 0,
send_response: true,
}
const mqtt_status = {
enabled: true,
@@ -291,9 +291,9 @@ const EMSESP_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'emsespStatus'
const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile'
const WRITE_VALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeValue'
const WRITE_SENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeSensor'
const emsesp_settings = {
emsesp_settings = {
tx_mode: 1,
tx_delay: 0,
ems_bus_id: 11,
syslog_enabled: false,
syslog_level: 3,
@@ -306,6 +306,7 @@ const emsesp_settings = {
shower_alert: false,
rx_gpio: 23,
tx_gpio: 5,
phy_type: 0,
dallas_gpio: 3,
dallas_parasite: false,
led_gpio: 2,
@@ -321,37 +322,38 @@ const emsesp_settings = {
const emsesp_data = {
devices: [
{
id: 1,
type: 'Thermostat',
brand: '',
name: 'RC20/Moduline 300',
deviceid: 23,
productid: 77,
version: '03.03',
i: 1,
t: 'Thermostat',
b: '',
n: 'RC20/Moduline 300',
d: 23,
p: 77,
v: '03.03',
},
{
id: 2,
type: 'Boiler',
brand: 'Nefit',
name: 'GBx72/Trendline/Cerapur/Greenstar Si/27i',
deviceid: 8,
productid: 123,
version: '06.01',
i: 2,
t: 'Boiler',
b: 'Nefit',
n: 'GBx72/Trendline/Cerapur/Greenstar Si/27i',
d: 8,
p: 123,
v: '06.01',
},
{
id: 3,
type: 'Controller',
brand: '',
name: 'BC10',
deviceid: 9,
productid: 190,
version: '01.03',
i: 3,
t: 'Controller',
b: '',
n: 'BC10',
d: 9,
p: 190,
v: '01.03',
},
],
sensors: [
{ no: 1, id: '28-233D-9497-0C03', temp: 25.7, offset: 1.2 },
{ no: 2, id: '28-243D-7437-1E3A', temp: 26.1, offset: 0 },
{ n: 1, i: '28-233D-9497-0C03', t: 25.7, o: 1.2 },
{ n: 2, i: '28-243D-7437-1E3A', t: 26.1, o: 0 },
],
analog: 12,
}
const emsesp_status = {
@@ -363,7 +365,7 @@ const emsesp_status = {
}
const emsesp_devicedata_1 = {
name: 'Thermostat: RC20/Moduline 300',
type: 'Thermostat',
data: [
{
v: '(0)',
@@ -380,386 +382,151 @@ const emsesp_devicedata_1 = {
{
v: 18,
u: 1,
n: '(hc1) selected room temperature',
n: 'hc1 selected room temperature',
c: 'hc1/seltemp',
},
{
v: 22.6,
u: 1,
n: '(hc1) current room temperature',
n: 'hc1 current room temperature',
c: '',
},
{
v: 'auto',
u: 0,
n: '(hc1) mode',
n: 'hc1 mode',
c: 'hc1/mode',
},
],
}
const emsesp_devicedata_2 = {
name: 'Boiler: Nefit GBx72/Trendline/Cerapur/Greenstar Si/27i',
type: 'Boiler',
data: [
{ v: 'off', u: 0, n: 'heating active' },
{ v: 'off', u: 0, n: 'warm water active' },
{ v: 5, u: 1, n: 'selected flow temperature', c: 'selflowtemp' },
{ v: 0, u: 2, n: 'burner selected max power', c: 'selburnpow' },
{ v: 0, u: 2, n: 'heating pump modulation' },
{ v: 42.9, u: 1, n: 'current flow temperature' },
{ v: 41.8, u: 1, n: 'return temperature' },
{ v: 1.6, u: 9, n: 'system pressure' },
{ v: 45, u: 1, n: 'actual boiler temperature' },
{ v: 'off', u: 0, n: 'gas' },
{ v: 0, u: 8, n: 'flame current' },
{ v: 'off', u: 0, n: 'heating pump' },
{ v: 'off', u: 0, n: 'fan' },
{ v: 'off', u: 0, n: 'ignition' },
{
v: false,
u: 16,
n: 'heating active',
c: '',
},
{
v: false,
u: 16,
n: 'warm water active',
c: '',
},
{
v: 5,
u: 1,
n: 'selected flow temperature',
c: 'selflowtemp',
},
{
v: 0,
u: 2,
n: 'burner selected max power',
c: 'selburnpow',
},
{
v: 0,
u: 2,
n: 'heating pump modulation',
c: '',
},
{
v: 51,
u: 1,
n: 'current flow temperature',
c: '',
},
{
v: 49.8,
u: 1,
n: 'return temperature',
c: '',
},
{
v: 1.1,
u: 9,
n: 'system pressure',
c: '',
},
{
v: 52.7,
u: 1,
n: 'boiler temperature',
c: '',
},
{
v: false,
u: 16,
n: 'gas',
c: '',
},
{
v: 0,
u: 8,
n: 'flame current',
c: '',
},
{
v: false,
u: 16,
n: 'heating pump',
c: '',
},
{
v: false,
u: 16,
n: 'fan',
c: '',
},
{
v: false,
u: 16,
n: 'ignition',
c: '',
},
{
v: true,
u: 16,
v: 'on',
u: 0,
n: 'heating activated',
c: 'heatingactivated',
l: ['off', 'on'],
},
{
v: 75,
u: 1,
n: 'heating temperature',
c: 'heatingtemp',
},
{
v: 90,
u: 2,
n: 'burner pump max power',
c: 'pumpmodmax',
},
{
v: 55,
u: 2,
n: 'burner pump min power',
c: 'pumpmodmin',
},
{
v: 1,
u: 7,
n: 'pump delay',
c: 'pumpdelay',
},
{
v: 10,
u: 7,
n: 'burner min period',
c: 'burnminperiod',
},
{
v: 0,
u: 2,
n: 'burner min power',
c: 'burnminpower',
},
{
v: 75,
u: 2,
n: 'burner max power',
c: 'burnmaxpower',
},
{
v: -6,
u: 1,
n: 'hysteresis on temperature',
c: 'boilhyston',
},
{
v: 6,
u: 1,
n: 'hysteresis off temperature',
c: 'boilhystoff',
},
{
v: 0,
u: 2,
n: 'burner current power',
c: '',
},
{
v: 303226,
u: 15,
n: '# burner starts',
c: '',
},
{
v: 510634,
u: 7,
n: 'total burner operating time',
c: '',
},
{
v: 415235,
u: 7,
n: 'total heat operating time',
c: '',
},
{
v: 4338730,
u: 7,
n: 'total UBA operating time',
c: '',
},
{
v: '1C(210) 06.06.2020 12:07',
u: 0,
n: 'last error code',
c: '',
},
{
v: '0H',
u: 0,
n: 'service code',
c: '',
},
{
v: 203,
u: 0,
n: 'service code number',
c: '',
},
{
v: '01.01.2012',
u: 0,
n: 'maintenance set date',
c: '',
},
{ v: 75, u: 1, n: 'heating temperature', c: 'heatingtemp' },
{ v: 90, u: 2, n: 'burner pump max power', c: 'pumpmodmax' },
{ v: 55, u: 2, n: 'burner pump min power', c: 'pumpmodmin' },
{ v: 1, u: 7, n: 'pump delay', c: 'pumpdelay' },
{ v: 10, u: 7, n: 'burner min period', c: 'burnminperiod' },
{ v: 0, u: 2, n: 'burner min power', c: 'burnminpower' },
{ v: 77, u: 2, n: 'burner max power', c: 'burnmaxpower' },
{ v: -6, u: 1, n: 'hysteresis on temperature', c: 'boilhyston' },
{ v: 6, u: 1, n: 'hysteresis off temperature', c: 'boilhystoff' },
{ v: 0, u: 2, n: 'burner current power' },
{ v: 317694, u: 16, n: 'burner starts' },
{ v: 524115, u: 7, n: 'total burner operating time' },
{ v: 424286, u: 7, n: 'total heat operating time' },
{ v: 4571225, u: 7, n: 'total UBA operating time' },
{ v: '1C(210) 06.06.2020 12:07', u: 0, n: 'last error code' },
{ v: '0H', u: 0, n: 'service code' },
{ v: 203, u: 0, n: 'service code number' },
{ v: ' ', u: 0, n: 'maintenance message' },
{
v: 'off',
u: 0,
n: 'maintenance scheduled',
c: 'maintenance',
l: ['off', 'time', 'date'],
},
{ v: 6000, u: 6, n: 'maintenance set time', c: 'maintenancetime' },
{ v: '01.01.2012', u: 0, n: 'maintenance set date', c: 'maintenancedate' },
{ v: 60, u: 1, n: 'ww selected temperature', c: 'wwseltemp' },
{ v: 62, u: 1, n: 'ww set temperature' },
{ v: 'flow', u: 0, n: 'ww type' },
{
v: 6000,
u: 6,
n: 'maintenance set time',
c: '',
},
{
v: 60,
u: 1,
n: '(ww) selected temperature',
c: '',
},
{
v: 62,
u: 1,
n: '(ww) set temperature',
c: 'wwsettemp',
},
{
v: 'flow',
v: 'eco',
u: 0,
n: '(ww) type',
c: '',
},
{
v: 'hot',
u: 0,
n: '(ww) comfort',
n: 'ww comfort',
c: 'wwcomfort',
l: ['hot', 'eco', 'intelligent'],
},
{ v: 40, u: 0, n: 'ww flow temperature offset', c: 'wwflowtempoffset' },
{ v: 100, u: 2, n: 'ww max power', c: 'wwmaxpower' },
{
v: 40,
v: 'off',
u: 0,
n: '(ww) flow temperature offset',
c: 'wwflowtempoffset',
},
{
v: 100,
u: 2,
n: '(ww) max power',
c: 'wwmaxpower',
},
{
v: false,
u: 16,
n: '(ww) circulation pump available',
n: 'ww circulation pump available',
c: 'wwcircpump',
l: ['off', 'on'],
},
{ v: '3-way valve', u: 0, n: 'ww charging type' },
{ v: -5, u: 1, n: 'ww hysteresis on temperature', c: 'wwhyston' },
{ v: 0, u: 1, n: 'ww hysteresis off temperature', c: 'wwhystoff' },
{ v: 70, u: 1, n: 'ww disinfection temperature', c: 'wwdisinfectiontemp' },
{
v: 'charge pump',
v: 'off',
u: 0,
n: '(ww) charging type',
c: '',
},
{
v: 70,
u: 1,
n: '(ww) disinfection temperature',
c: 'wwdisinfectiontemp',
n: 'ww circulation pump frequency',
c: 'wwcircmode',
l: [
'off',
'1x3min',
'2x3min',
'3x3min',
'4x3min',
'5x3min',
'6x3min',
'continuous',
],
},
{
v: 'off',
u: 0,
n: '(ww) circulation pump frequency',
c: 'wwcircmode',
},
{
v: false,
u: 16,
n: '(ww) circulation active',
n: 'ww circulation active',
c: 'wwcirc',
l: ['off', 'on'],
},
{ v: 37.1, u: 1, n: 'ww current intern temperature' },
{ v: 0, u: 3, n: 'ww current tap water flow' },
{ v: 37.2, u: 1, n: 'ww storage intern temperature' },
{ v: 'on', u: 0, n: 'ww activated', c: 'wwactivated', l: ['off', 'on'] },
{
v: 44.4,
u: 1,
n: '(ww) current intern temperature',
c: '',
},
{
v: 0,
u: 3,
n: '(ww) current tap water flow',
c: '',
},
{
v: 44.4,
u: 1,
n: '(ww) storage intern temperature',
c: '',
},
{
v: true,
u: 16,
n: '(ww) activated',
c: 'wwactivated',
},
{
v: false,
u: 16,
n: '(ww) one time charging',
v: 'off',
u: 0,
n: 'ww one time charging',
c: 'wwonetime',
l: ['off', 'on'],
},
{
v: false,
u: 16,
n: '(ww) disinfecting',
c: '',
},
{
v: false,
u: 16,
n: '(ww) charging',
c: '',
},
{
v: false,
u: 16,
n: '(ww) recharging',
c: '',
},
{
v: true,
u: 16,
n: '(ww) temperature ok',
c: '',
},
{
v: false,
u: 16,
n: '(ww) active',
c: '',
},
{
v: true,
u: 16,
n: '(ww) heating',
c: '',
},
{
v: 268671,
u: 15,
n: '(ww) # starts',
c: '',
},
{
v: 95399,
u: 7,
n: '(ww) active time',
c: '',
v: 'off',
u: 0,
n: 'ww disinfection',
c: 'wwdisinfect',
l: ['off', 'on'],
},
{ v: 'off', u: 0, n: 'ww charging' },
{ v: 'off', u: 0, n: 'ww recharging' },
{ v: 'on', u: 0, n: 'ww temperature ok' },
{ v: 'off', u: 0, n: 'ww active' },
{ v: 'on', u: 0, n: 'ww heating' },
{ v: 282323, u: 16, n: 'ww starts' },
{ v: 99829, u: 7, n: 'ww active time' },
],
}
const emsesp_devicedata_3 = {
name: 'Controller: BC1',
type: 'Controller',
data: [],
}
@@ -886,6 +653,8 @@ app.get(EMSESP_SETTINGS_ENDPOINT, (req, res) => {
res.json(emsesp_settings)
})
app.post(EMSESP_SETTINGS_ENDPOINT, (req, res) => {
console.log(req.body)
emsesp_settings = req.body
res.json(emsesp_settings)
})
app.get(EMSESP_DATA_ENDPOINT, (req, res) => {
@@ -941,6 +710,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
rx_gpio: 3,
tx_gpio: 4,
pbutton_gpio: 5,
phy_type: 0,
}
if (board_profile == 'S32') {
@@ -950,6 +720,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
data.rx_gpio = 23
data.tx_gpio = 5
data.pbutton_gpio = 0
data.phy_type = 0
} else if (board_profile == 'E32') {
// BBQKees Gateway E32
data.led_gpio = 2
@@ -957,6 +728,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
data.rx_gpio = 5
data.tx_gpio = 17
data.pbutton_gpio = 33
data.phy_type = 1
} else if (board_profile == 'MH-ET') {
// MH-ET Live D1 Mini
data.led_gpio = 2
@@ -964,6 +736,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
data.rx_gpio = 23
data.tx_gpio = 5
data.pbutton_gpio = 0
data.phy_type = 0
} else if (board_profile == 'NODEMCU') {
// NodeMCU 32S
data.led_gpio = 2
@@ -971,6 +744,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
data.rx_gpio = 23
data.tx_gpio = 5
data.pbutton_gpio = 0
data.phy_type = 0
} else if (board_profile == 'LOLIN') {
// Lolin D32
data.led_gpio = 2
@@ -978,6 +752,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
data.rx_gpio = 17
data.tx_gpio = 16
data.pbutton_gpio = 0
data.phy_type = 0
} else if (board_profile == 'OLIMEX') {
// Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Dallas)
data.led_gpio = 0
@@ -985,21 +760,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
data.rx_gpio = 36
data.tx_gpio = 4
data.pbutton_gpio = 34
// data = { 0, 0, 36, 4, 34};
} else if (board_profile == 'TLK110') {
// Generic Ethernet (TLK110)
data.led_gpio = 2
data.dallas_gpio = 4
data.rx_gpio = 5
data.tx_gpio = 17
data.pbutton_gpio = 33
} else if (board_profile == 'LAN8720') {
// Generic Ethernet (LAN8720)
data.led_gpio = 2
data.dallas_gpio = 4
data.rx_gpio = 5
data.tx_gpio = 17
data.pbutton_gpio = 33
data.phy_type = 1
}
res.json(data)

View File

@@ -1,9 +1,14 @@
; example custom platformio.ini file for EMS-ESP
[common]
; e.g. use build_flags = -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
debug_flags = -DEMSESP_WIFI_TWEAK -DEMSESP_DEBUG
; debug_flags =
; custom build flags
; EMSESP_DEBUG, EMSESP_UART_DEBUG, EMSESP_DEBUG_SENSOR, EMSESP_WIFI_TWEAK, EMSESP_DEFAULT_BOARD_PROFILE
; ; e.g. -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
; my_build_flags = -DEMSESP_DEBUG -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
my_build_flags =
; 5=verbose, 4=debug, 3=info
debug_flags = -DCORE_DEBUG_LEVEL=5
[env:esp32]
; if using OTA enter your details below
@@ -11,9 +16,10 @@ upload_protocol = espota
upload_flags =
--port=8266
--auth=ems-esp-neo
upload_port = 10.10.10.101
; to prevent the web UI from building each time, uncomment this next line
; extra_scripts =
upload_port = ems-esp.local
; use this when you don't want to re-build the WebUI
extra_scripts = scripts/rename_fw.py
; pio run -e debug
; or from Visual Studio Code do PIO -> Project Tasks -> debug -> General -> Upload and Monitor
@@ -28,4 +34,4 @@ monitor_filters = esp32_exception_decoder
debug_tool = esp-prog
debug_init_break = tbreak setup
build_flags = ${factory_settings.build_flags} ${common.debug_flags} -DONEWIRE_CRC16=0 -DNO_GLOBAL_ARDUINOOTA -DARDUINOJSON_ENABLE_STD_STRING=1 -DESP32=1 -DARDUINO_ARCH_ESP32=1
extra_scripts = pre:scripts/build_interface.py
extra_scripts = pre:scripts/build_interface.py

View File

@@ -18,9 +18,13 @@ core_build_flags =
core_unbuild_flags =
; my_build_flags is set in pio_local.ini
my_build_flags =
build_flags =
${common.core_build_flags}
${factory_settings.build_flags}
${common.my_build_flags}
-D ONEWIRE_CRC16=0
-D NO_GLOBAL_ARDUINOOTA
-D ARDUINOJSON_ENABLE_STD_STRING=1

118
scripts/clang-format.py Executable file
View File

@@ -0,0 +1,118 @@
#!/usr/bin/env python3
# copied from esphome
# run from Linux using ./scripts/clang-forrmat.py
import argparse
import multiprocessing
import os
import queue
import re
import subprocess
import sys
import threading
import click
sys.path.append(os.path.dirname(__file__))
from helpers import get_output, src_files, filter_changed
def run_format(args, queue, lock, failed_files):
"""Takes filenames out of queue and runs clang-format on them."""
while True:
path = queue.get()
invocation = ['clang-format']
if args.inplace:
invocation.append('-i')
else:
invocation.extend(['--dry-run', '-Werror'])
invocation.append(path)
proc = subprocess.run(invocation, capture_output=True, encoding='utf-8')
if proc.returncode != 0:
with lock:
print()
print("\033[0;32m************* File \033[1;32m{}\033[0m".format(path))
print(proc.stdout)
print(proc.stderr)
print()
failed_files.append(path)
queue.task_done()
def progress_bar_show(value):
return value if value is not None else ''
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-j', '--jobs', type=int,
default=multiprocessing.cpu_count(),
help='number of format instances to be run in parallel.')
parser.add_argument('files', nargs='*', default=[],
help='files to be processed (regex on path)')
parser.add_argument('-i', '--inplace', action='store_true',
help='reformat files in-place')
parser.add_argument('-c', '--changed', action='store_true',
help='only run on changed files')
args = parser.parse_args()
try:
get_output('clang-format', '-version')
except:
print("""
Oops. It looks like clang-format is not installed.
Please check you can run "clang-format -version" in your terminal and install
clang-format (v11 or v12) if necessary.
Note you can also upload your code as a pull request on GitHub and see the CI check
output to apply clang-format.
""")
return 1
files = []
# all files
# for path in git_ls_files(['*.cpp', '*.h', '*.tcc']):
# files.append(os.path.relpath(path, os.getcwd()))
# just under src
for path in src_files(['.cpp', '.h']):
files.append(os.path.relpath(path, os.getcwd()))
if args.files:
file_name_re = re.compile('|'.join(args.files))
files = [p for p in files if file_name_re.search(p)]
if args.changed:
files = filter_changed(files)
files.sort()
failed_files = []
try:
task_queue = queue.Queue(args.jobs)
lock = threading.Lock()
for _ in range(args.jobs):
t = threading.Thread(target=run_format,
args=(args, task_queue, lock, failed_files))
t.daemon = True
t.start()
# Fill the queue with files.
with click.progressbar(files, width=30, file=sys.stderr,
item_show_func=progress_bar_show) as bar:
for name in bar:
task_queue.put(name)
# Wait for all threads to be done.
task_queue.join()
except KeyboardInterrupt:
print()
print('Ctrl-C detected, goodbye.')
os.kill(0, 9)
sys.exit(len(failed_files))
if __name__ == '__main__':
main()

190
scripts/clang-tidy.py Executable file
View File

@@ -0,0 +1,190 @@
#!/usr/bin/env python3
# copied from esphome
# run from Linux using ./scripts/clang-forrmat.py
import argparse
import multiprocessing
import os
import queue
import shutil
import subprocess
import sys
import tempfile
import threading
import click
import pexpect
sys.path.append(os.path.dirname(__file__))
from helpers import shlex_quote, get_output, \
build_all_include, temp_header_file, filter_changed, load_idedata, src_files
def clang_options(idedata):
cmd = [
# target 32-bit arch (this prevents size mismatch errors on a 64-bit host)
'-m32',
# disable built-in include directories from the host
'-nostdinc',
'-nostdinc++',
# allow to condition code on the presence of clang-tidy
'-DCLANG_TIDY'
]
# copy compiler flags, except those clang doesn't understand.
cmd.extend(flag for flag in idedata['cxx_flags'].split(' ')
if flag not in ('-free', '-fipa-pta', '-mlongcalls', '-mtext-section-literals'))
# defines
cmd.extend(f'-D{define}' for define in idedata['defines'])
# add include directories, using -isystem for dependencies to suppress their errors
for directory in idedata['includes']['toolchain']:
cmd.extend(['-isystem', directory])
for directory in sorted(set(idedata['includes']['build'])):
dependency = "framework-arduino" in directory or "/libdeps/" in directory
cmd.extend(['-isystem' if dependency else '-I', directory])
return cmd
def run_tidy(args, options, tmpdir, queue, lock, failed_files):
while True:
path = queue.get()
invocation = ['clang-tidy']
if tmpdir is not None:
invocation.append('--export-fixes')
# Get a temporary file. We immediately close the handle so clang-tidy can
# overwrite it.
(handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir)
os.close(handle)
invocation.append(name)
if args.quiet:
invocation.append('-quiet')
invocation.append(os.path.abspath(path))
invocation.append('--')
invocation.extend(options)
invocation_s = ' '.join(shlex_quote(x) for x in invocation)
# Use pexpect for a pseudy-TTY with colored output
output, rc = pexpect.run(invocation_s, withexitstatus=True, encoding='utf-8',
timeout=15 * 60)
if rc != 0:
with lock:
print()
print("\033[0;32m************* File \033[1;32m{}\033[0m".format(path))
print(output)
print()
failed_files.append(path)
queue.task_done()
def progress_bar_show(value):
if value is None:
return ''
def split_list(a, n):
k, m = divmod(len(a), n)
return [a[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)]
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-j', '--jobs', type=int,
default=multiprocessing.cpu_count(),
help='number of tidy instances to be run in parallel.')
parser.add_argument('files', nargs='*', default=[],
help='files to be processed (regex on path)')
parser.add_argument('--fix', action='store_true', help='apply fix-its')
parser.add_argument('-q', '--quiet', action='store_false',
help='run clang-tidy in quiet mode')
parser.add_argument('-c', '--changed', action='store_true',
help='only run on changed files')
parser.add_argument('--split-num', type=int, help='split the files into X jobs.',
default=None)
parser.add_argument('--split-at', type=int, help='which split is this? starts at 1',
default=None)
parser.add_argument('--all-headers', action='store_true',
help='create a dummy file that checks all headers')
args = parser.parse_args()
try:
get_output('clang-tidy', '-version')
except:
print("""
Oops. It looks like clang-tidy is not installed.
Please check you can run "clang-tidy -version" in your terminal and install
clang-tidy (v11 or v12) if necessary.
Note you can also upload your code as a pull request on GitHub and see the CI check
output to apply clang-tidy.
""")
return 1
idedata = load_idedata("clang-tidy")
options = clang_options(idedata)
files = []
for path in src_files(['.cpp']):
files.append(os.path.relpath(path, os.getcwd()))
if args.files:
# Match against files specified on command-line
file_name_re = re.compile('|'.join(args.files))
files = [p for p in files if file_name_re.search(p)]
if args.changed:
files = filter_changed(files)
files.sort()
if args.split_num:
files = split_list(files, args.split_num)[args.split_at - 1]
if args.all_headers and args.split_at in (None, 1):
build_all_include()
files.insert(0, temp_header_file)
tmpdir = None
if args.fix:
tmpdir = tempfile.mkdtemp()
failed_files = []
try:
task_queue = queue.Queue(args.jobs)
lock = threading.Lock()
for _ in range(args.jobs):
t = threading.Thread(target=run_tidy,
args=(args, options, tmpdir, task_queue, lock, failed_files))
t.daemon = True
t.start()
# Fill the queue with files.
with click.progressbar(files, width=30, file=sys.stderr,
item_show_func=progress_bar_show) as bar:
for name in bar:
task_queue.put(name)
# Wait for all threads to be done.
task_queue.join()
except KeyboardInterrupt:
print()
print('Ctrl-C detected, goodbye.')
if tmpdir:
shutil.rmtree(tmpdir)
os.kill(0, 9)
if args.fix and failed_files:
print('Applying fixes ...')
try:
subprocess.call(['clang-apply-replacements-12', tmpdir])
except:
print('Error applying fixes.\n', file=sys.stderr)
raise
sys.exit(len(failed_files))
if __name__ == '__main__':
main()

122
scripts/helpers.py Executable file
View File

@@ -0,0 +1,122 @@
import os.path
import re
import subprocess
import json
from pathlib import Path
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", "..")))
basepath = os.path.join(root_path, "src")
temp_folder = os.path.join(root_path, ".temp")
temp_header_file = os.path.join(temp_folder, "all-include.cpp")
def shlex_quote(s):
if not s:
return "''"
if re.search(r"[^\w@%+=:,./-]", s) is None:
return s
return "'" + s.replace("'", "'\"'\"'") + "'"
def build_all_include():
# Build a cpp file that includes all header files in this repo.
# Otherwise header-only integrations would not be tested by clang-tidy
headers = []
for path in walk_files(basepath):
filetypes = (".h",)
ext = os.path.splitext(path)[1]
if ext in filetypes:
path = os.path.relpath(path, root_path)
include_p = path.replace(os.path.sep, "/")
headers.append(f'#include "{include_p}"')
headers.sort()
headers.append("")
content = "\n".join(headers)
p = Path(temp_header_file)
p.parent.mkdir(exist_ok=True)
p.write_text(content)
def src_files(filetypes=None):
file_list = []
for path in walk_files(basepath):
ext = os.path.splitext(path)[1]
if ext in filetypes:
path = os.path.relpath(path, root_path)
file_list.append(path)
return file_list
def walk_files(path):
for root, _, files in os.walk(path):
for name in files:
yield os.path.join(root, name)
def get_output(*args):
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = proc.communicate()
return output.decode("utf-8")
def get_err(*args):
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = proc.communicate()
return err.decode("utf-8")
def splitlines_no_ends(string):
return [s.strip() for s in string.splitlines()]
def changed_files():
check_remotes = ["upstream", "origin"]
check_remotes.extend(splitlines_no_ends(get_output("git", "remote")))
for remote in check_remotes:
command = ["git", "merge-base", f"refs/remotes/{remote}/dev", "HEAD"]
try:
merge_base = splitlines_no_ends(get_output(*command))[0]
break
except:
pass
else:
raise ValueError("Git not configured")
command = ["git", "diff", merge_base, "--name-only"]
changed = splitlines_no_ends(get_output(*command))
changed = [os.path.relpath(f, os.getcwd()) for f in changed]
changed.sort()
return changed
def filter_changed(files):
changed = changed_files()
files = [f for f in files if f in changed]
print("Changed files:")
if not files:
print(" No changed files!")
for c in files:
print(f" {c}")
return files
def git_ls_files(patterns=None):
command = ["git", "ls-files", "-s"]
if patterns is not None:
command.extend(patterns)
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
output, err = proc.communicate()
lines = [x.split() for x in output.decode("utf-8").splitlines()]
return {s[3].strip(): int(s[0]) for s in lines}
def load_idedata(environment):
platformio_ini = Path(root_path) / "platformio.ini"
temp_idedata = Path(temp_folder) / f"idedata-{environment}.json"
if not platformio_ini.is_file() or not temp_idedata.is_file():
changed = True
elif platformio_ini.stat().st_mtime >= temp_idedata.stat().st_mtime:
changed = True
else:
changed = False
if not changed:
return json.loads(temp_idedata.read_text())
stdout = subprocess.check_output(["pio", "run", "-t", "idedata", "-e", environment])
match = re.search(r'{\s*".*}', stdout.decode("utf-8"))
data = json.loads(match.group())
temp_idedata.parent.mkdir(exist_ok=True)
temp_idedata.write_text(json.dumps(data, indent=2) + "\n")
return data

0
scripts/rename_fw.py Normal file → Executable file
View File

0
scripts/upload_fw.py Normal file → Executable file
View File

View File

@@ -26,150 +26,277 @@ uuid::log::Logger Command::logger_{F_(command), uuid::log::Facility::DAEMON};
std::vector<Command::CmdFunction> Command::cmdfunctions_;
// calls a command
// id may be used to represent a heating circuit for example, it's optional
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id) {
int8_t id_new = id;
char cmd_new[20] = {'\0'};
strlcpy(cmd_new, cmd, 20);
// takes a path and a json body, parses the data and calls the command
// the path is leading so if duplicate keys are in the input JSON it will be ignored
// the entry point will be either via the Web API (api/) or MQTT (<base>/)
// returns a return code and json output
uint8_t Command::process(const char * path, const bool is_admin, const JsonObject & input, JsonObject & output) {
SUrlParser p; // parse URL for the path names
p.parse(path);
// find the command
auto cf = find_command(device_type, cmd_new, id_new);
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
LOG_WARNING(F("Command %s on %s not found"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
return CommandRet::NOT_FOUND;
if (!p.paths().size()) {
return message(CommandRet::ERROR, "invalid path", output);
}
// check if we're allowed to call it
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) {
LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
return CommandRet::NOT_ALLOWED;
}
std::string dname = EMSdevice::device_type_2_device_name(device_type);
if (value == nullptr) {
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
} else if (id == -1) {
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
// check first if it's from API, if so strip the "api/"
if ((p.paths().front() == "api")) {
p.paths().erase(p.paths().begin());
} else {
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
// not /api, so must be MQTT path. Check for base and remove it.
if (!strncmp(path, Mqtt::base().c_str(), Mqtt::base().length())) {
char new_path[Mqtt::MQTT_TOPIC_MAX_SIZE];
strncpy(new_path, path, sizeof(new_path));
p.parse(new_path + Mqtt::base().length() + 1); // re-parse the stripped path
} else {
return message(CommandRet::ERROR, "unrecognized path", output); // error
}
}
return ((cf->cmdfunction_)(value, id_new)) ? CommandRet::OK : CommandRet::ERROR;
// Serial.println(p.path().c_str()); // dump paths, for debugging
// re-calculate new path
// if there is only a path (URL) and no body then error!
size_t num_paths = p.paths().size();
if (!num_paths && !input.size()) {
return message(CommandRet::ERROR, "missing command in path", output);
}
std::string cmd_s;
int8_t id_n = -1; // default hc
// check for a device as first item in the path
// if its not a known device (thermostat, boiler etc) look for any special MQTT subscriptions
const char * device_s = nullptr;
if (!num_paths) {
// we must look for the device in the JSON body
if (input.containsKey("device")) {
device_s = input["device"];
}
} else {
// extract it from the path
device_s = p.paths().front().c_str(); // get the device (boiler, thermostat, system etc)
}
// validate the device, make sure it exists
uint8_t device_type = EMSdevice::device_name_2_device_type(device_s);
if (!device_has_commands(device_type)) {
LOG_DEBUG(F("Command failed: unknown device '%s'"), device_s);
return message(CommandRet::ERROR, "unknown device", output);
}
// the next value on the path should be the command
const char * command_p = nullptr;
if (num_paths == 2) {
command_p = p.paths()[1].c_str();
} else if (num_paths >= 3) {
// concatenate the path into one string as it could be in the format 'hc/XXX'
char command[50];
snprintf(command, sizeof(command), "%s/%s", p.paths()[1].c_str(), p.paths()[2].c_str());
command_p = command;
} else {
// take it from the JSON
if (input.containsKey("entity")) {
command_p = input["entity"];
} else if (input.containsKey("cmd")) {
command_p = input["cmd"];
}
}
// some commands may be prefixed with hc. or wwc. so extract these if they exist
// parse_command_string returns the extracted command
command_p = parse_command_string(command_p, id_n);
if (command_p == nullptr) {
// handle dead endpoints like api/system or api/boiler
// default to 'info' for SYSTEM and DALLASENSOR, the other devices to 'values' for shortname version
if (num_paths < 3) {
if (device_type < EMSdevice::DeviceType::BOILER) {
command_p = "info";
} else {
command_p = "values";
}
} else {
return message(CommandRet::NOT_FOUND, "missing or bad command", output);
}
}
// if we don't have an id/hc/wwc try and get it from the JSON input
// it's allowed to have no id, and then keep the default to -1
if (id_n == -1) {
if (input.containsKey("hc")) {
id_n = input["hc"];
} else if (input.containsKey("wwc")) {
id_n = input["wwc"];
id_n += 7; // wwc1 has id 8
} else if (input.containsKey("id")) {
id_n = input["id"];
}
}
// the value must always come from the input JSON. It's allowed to be empty.
JsonVariant data;
if (input.containsKey("data")) {
data = input["data"];
} else if (input.containsKey("value")) {
data = input["value"];
}
// call the command based on the type
uint8_t return_code = CommandRet::ERROR;
if (data.is<const char *>()) {
return_code = Command::call(device_type, command_p, data.as<const char *>(), is_admin, id_n, output);
} else if (data.is<int>()) {
char data_str[10];
return_code = Command::call(device_type, command_p, Helpers::itoa(data_str, (int16_t)data.as<int>()), is_admin, id_n, output);
} else if (data.is<float>()) {
char data_str[10];
return_code = Command::call(device_type, command_p, Helpers::render_value(data_str, (float)data.as<float>(), 2), is_admin, id_n, output);
} else if (data.isNull()) {
return_code = Command::call(device_type, command_p, "", is_admin, id_n, output); // empty, will do a query instead
} else {
return message(CommandRet::ERROR, "cannot parse command", output); // can't process
}
return return_code;
}
const std::string Command::return_code_string(const uint8_t return_code) {
switch (return_code) {
case CommandRet::ERROR:
return read_flash_string(F("Error"));
break;
case CommandRet::OK:
return read_flash_string(F("OK"));
break;
case CommandRet::NOT_FOUND:
return read_flash_string(F("Not Found"));
break;
case CommandRet::NOT_ALLOWED:
return read_flash_string(F("Not Authorized"));
break;
case CommandRet::FAIL:
return read_flash_string(F("Failed"));
break;
}
char s[4];
return Helpers::smallitoa(s, return_code);
}
// takes a string like "hc1/seltemp" or "seltemp" or "wwc2.seltemp" and tries to get the id and cmd
// returns start position of the command string
const char * Command::parse_command_string(const char * command, int8_t & id) {
if (command == nullptr) {
return nullptr;
}
// make a copy of the string command for parsing
char command_s[100];
strncpy(command_s, command, sizeof(command_s));
// look for a delimeter and split the string
char * p = command_s;
char * breakp = strchr(p, '.');
if (!breakp) {
p = command_s; // reset and look for /
breakp = strchr(p, '/');
if (!breakp) {
p = command_s; // reset and look for _
breakp = strchr(p, '_');
if (!breakp) {
return command;
}
}
}
// extract the hc or wwc number
uint8_t start_pos = breakp - p + 1;
if (!strncmp(command, "hc", 2) && start_pos == 4) {
id = command[start_pos - 2] - '0';
} else if (!strncmp(command, "wwc", 3) && start_pos == 5) {
id = command[start_pos - 2] - '0' + 7; // wwc1 has id 8
} else {
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] Command parse error, unknown hc/wwc in %s"), command_s);
#endif
return nullptr;
}
return (command + start_pos);
}
// calls a command directly
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value) {
// create a temporary buffer
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> output_doc;
JsonObject output = output_doc.to<JsonObject>();
// authenticated is always true and ID is the default value
return call(device_type, cmd, value, true, -1, output);
}
// calls a command. Takes a json object for output.
// id may be used to represent a heating circuit for example
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & json) {
int8_t id_new = id;
char cmd_new[20] = {'\0'};
strlcpy(cmd_new, cmd, 20);
auto cf = find_command(device_type, cmd_new, id_new);
// check if we're allowed to call it
if (cf != nullptr) {
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated && value != nullptr) {
LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
return CommandRet::NOT_ALLOWED; // command not allowed
}
}
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject & output) {
uint8_t return_code = CommandRet::OK;
std::string dname = EMSdevice::device_type_2_device_name(device_type);
if (value == nullptr) {
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
} else if (id == -1) {
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
} else {
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
}
// check if json object is empty, if so quit
if (json.isNull()) {
LOG_WARNING(F("Ignore call for command %s in %s because it has no json body"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
return CommandRet::ERROR;
}
// see if there is a command registered
auto cf = find_command(device_type, cmd);
// this is for endpoints that don't have commands, i.e not writable (e.g. boiler/syspress)
if (cf == nullptr) {
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type) ? CommandRet::OK : CommandRet::ERROR;
}
// check if its a call to and end-point to a device, i.e. has no value
// except for system commands as this is a special device without any queryable entities (device values)
// exclude SYSTEM and DALLASSENSOR
if (cf->cmdfunction_json_) {
return ((cf->cmdfunction_json_)(value, id_new, json)) ? CommandRet::OK : CommandRet::ERROR;
} else {
if ((device_type != EMSdevice::DeviceType::SYSTEM) && (value == nullptr || strlen(value) == 0 || strcmp(value, "?") == 0 || strcmp(value, "*") == 0)) {
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type) ? CommandRet::OK : CommandRet::ERROR;
}
return ((cf->cmdfunction_)(value, id_new)) ? CommandRet::OK : CommandRet::ERROR;
}
}
// strip prefixes, check, and find command
Command::CmdFunction * Command::find_command(const uint8_t device_type, char * cmd, int8_t & id) {
// TODO special cases for id=0 and id=-1 will be removed in V3 API
// no command for id0
if (id == 0) {
return nullptr;
}
// empty command is info with id0
if (cmd[0] == '\0') {
strcpy(cmd, "info");
id = 0;
}
// convert cmd to lowercase
for (char * p = cmd; *p; p++) {
*p = tolower(*p);
}
// TODO hack for commands that could have hc or wwc prefixed. will be removed in new API V3 eventually
// scan for prefix hc.
for (uint8_t i = DeviceValueTAG::TAG_HC1; i <= DeviceValueTAG::TAG_HC4; i++) {
const char * tag = EMSdevice::tag_to_string(i).c_str();
uint8_t len = strlen(tag);
if (strncmp(cmd, tag, len) == 0) {
if (cmd[len] != '\0') {
strcpy(cmd, &cmd[len + 1]);
} else {
strcpy(cmd, &cmd[len]);
}
id = 1 + i - DeviceValueTAG::TAG_HC1;
break;
if ((device_type >= EMSdevice::DeviceType::BOILER) && (!value || !strlen(value))) {
if (!cf || !cf->cmdfunction_json_) {
#if defined(EMSESP_DEBUG)
LOG_INFO(F("[DEBUG] Calling %s command '%s' to retrieve values"), dname.c_str(), cmd);
#endif
return EMSESP::get_device_value_info(output, cmd, id, device_type) ? CommandRet::OK : CommandRet::ERROR; // entity = cmd
}
}
// scan for prefix wwc.
for (uint8_t i = DeviceValueTAG::TAG_WWC1; i <= DeviceValueTAG::TAG_WWC4; i++) {
const char * tag = EMSdevice::tag_to_string(i).c_str();
uint8_t len = strlen(tag);
if (strncmp(cmd, tag, len) == 0) {
if (cmd[len] != '\0') {
strcpy(cmd, &cmd[len + 1]);
} else {
strcpy(cmd, &cmd[len]);
}
id = 8 + i - DeviceValueTAG::TAG_WWC1;
break;
if (cf) {
// we have a matching command
if ((value == nullptr) || !strlen(value)) {
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
} else if (id == -1) {
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
} else {
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
}
// check permissions
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !is_admin) {
output["message"] = "authentication failed";
return CommandRet::NOT_ALLOWED; // command not allowed
}
// call the function
if (cf->cmdfunction_json_) {
return_code = ((cf->cmdfunction_json_)(value, id, output)) ? CommandRet::OK : CommandRet::ERROR;
}
if (cf->cmdfunction_) {
return_code = ((cf->cmdfunction_)(value, id)) ? CommandRet::OK : CommandRet::ERROR;
}
// report error if call failed
if (return_code != CommandRet::OK) {
return message(return_code, "callback function failed", output);
}
return return_code;
}
// empty command after processing prefix is info
if (cmd[0] == '\0') {
strlcpy(cmd, "info", 20);
}
return find_command(device_type, cmd);
// we didn't find the command and its not an endpoint, report error
LOG_DEBUG(F("Command failed: invalid command '%s'"), cmd);
return message(CommandRet::NOT_FOUND, "invalid command", output);
}
// add a command to the list, which does not return json
// these commands are not callable directly via MQTT subscriptions either
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, const cmd_function_p cb, const __FlashStringHelper * description, uint8_t flags) {
// if the command already exists for that device type don't add it
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
if (find_command(device_type, read_flash_string(cmd).c_str()) != nullptr) {
return;
}
@@ -179,26 +306,17 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, co
}
cmdfunctions_.emplace_back(device_type, flags, cmd, cb, nullptr, description); // callback for json is nullptr
// see if we need to subscribe
if (Mqtt::enabled()) {
Mqtt::register_command(device_type, cmd, cb, flags);
}
}
// add a command to the list, which does return a json object as output
// flag is fixed to MqttSubFlag::FLAG_NOSUB
void Command::add_json(const uint8_t device_type,
const __FlashStringHelper * cmd,
const cmd_json_function_p cb,
const __FlashStringHelper * description,
uint8_t flags) {
// flag is fixed to MqttSubFlag::MQTT_SUB_FLAG_NOSUB so there will be no topic subscribed to this
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, const cmd_json_function_p cb, const __FlashStringHelper * description, uint8_t flags) {
// if the command already exists for that device type don't add it
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
if (find_command(device_type, read_flash_string(cmd).c_str()) != nullptr) {
return;
}
cmdfunctions_.emplace_back(device_type, CommandFlag::MQTT_SUB_FLAG_NOSUB | flags, cmd, nullptr, cb, description); // callback for json is included
cmdfunctions_.emplace_back(device_type, (CommandFlag::MQTT_SUB_FLAG_NOSUB | flags), cmd, nullptr, cb, description); // callback for json is included
}
// see if a command exists for that device type
@@ -209,14 +327,14 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
}
// convert cmd to lowercase and compare
char lowerCmd[20];
char lowerCmd[30];
strlcpy(lowerCmd, cmd, sizeof(lowerCmd));
for (char * p = lowerCmd; *p; p++) {
*p = tolower(*p);
}
for (auto & cf : cmdfunctions_) {
if (!strcmp(lowerCmd, Helpers::toLower(uuid::read_flash_string(cf.cmd_)).c_str()) && (cf.device_type_ == device_type)) {
if (!strcmp(lowerCmd, Helpers::toLower(read_flash_string(cf.cmd_)).c_str()) && (cf.device_type_ == device_type)) {
return &cf;
}
}
@@ -225,9 +343,9 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
}
// list all commands for a specific device, output as json
bool Command::list(const uint8_t device_type, JsonObject & json) {
bool Command::list(const uint8_t device_type, JsonObject & output) {
if (cmdfunctions_.empty()) {
json["message"] = "no commands available";
output["message"] = "no commands available";
return false;
}
@@ -235,15 +353,15 @@ bool Command::list(const uint8_t device_type, JsonObject & json) {
std::list<std::string> sorted_cmds;
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
sorted_cmds.push_back(read_flash_string(cf.cmd_));
}
}
sorted_cmds.sort();
for (auto & cl : sorted_cmds) {
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
json[cl] = cf.description_;
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) {
output[cl] = cf.description_;
}
}
}
@@ -262,7 +380,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
std::list<std::string> sorted_cmds;
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
sorted_cmds.push_back(read_flash_string(cf.cmd_));
}
}
sorted_cmds.sort();
@@ -282,15 +400,15 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
for (auto & cl : sorted_cmds) {
// find and print the description
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) {
uint8_t i = cl.length();
shell.print(" ");
if (cf.has_flags(MQTT_SUB_FLAG_HC)) {
shell.print("[hc] ");
i += 5;
shell.print("[hc<n>.]");
i += 8;
} else if (cf.has_flags(MQTT_SUB_FLAG_WWC)) {
shell.print("[wwc] ");
i += 6;
shell.print("[wwc<n>.]");
i += 9;
}
shell.print(cl);
// pad with spaces
@@ -298,8 +416,12 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
shell.print(' ');
}
shell.print(COLOR_BRIGHT_CYAN);
shell.print(uuid::read_flash_string(cf.description_));
if (cf.has_flags(CommandFlag::ADMIN_ONLY)) {
if (cf.has_flags(MQTT_SUB_FLAG_WW)) {
shell.print(EMSdevice::tag_to_string(TAG_DEVICE_DATA_WW));
shell.print(' ');
}
shell.print(read_flash_string(cf.description_));
if (!cf.has_flags(CommandFlag::ADMIN_ONLY)) {
shell.print(' ');
shell.print(COLOR_BRIGHT_RED);
shell.print('*');
@@ -325,7 +447,7 @@ bool Command::device_has_commands(const uint8_t device_type) {
}
if (device_type == EMSdevice::DeviceType::DALLASSENSOR) {
return true; // we always have Sensor, but should check if there are actual sensors attached!
return (EMSESP::sensor_devices().size() != 0);
}
for (const auto & emsdevice : EMSESP::emsdevices) {
@@ -363,10 +485,11 @@ void Command::show_devices(uuid::console::Shell & shell) {
// output list of all commands to console
// calls show with verbose mode set
void Command::show_all(uuid::console::Shell & shell) {
shell.println(F("Available commands: "));
shell.println(F("Available commands (*=do not need authorization): "));
// show system first
shell.print(COLOR_BOLD_ON);
shell.print(COLOR_YELLOW);
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM).c_str());
shell.print(COLOR_RESET);
show(shell, EMSdevice::DeviceType::SYSTEM, true);
@@ -374,6 +497,7 @@ void Command::show_all(uuid::console::Shell & shell) {
// show sensor
if (EMSESP::have_sensors()) {
shell.print(COLOR_BOLD_ON);
shell.print(COLOR_YELLOW);
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR).c_str());
shell.print(COLOR_RESET);
show(shell, EMSdevice::DeviceType::DALLASSENSOR, true);
@@ -383,6 +507,7 @@ void Command::show_all(uuid::console::Shell & shell) {
for (const auto & device_class : EMSFactory::device_handlers()) {
if (Command::device_has_commands(device_class.first)) {
shell.print(COLOR_BOLD_ON);
shell.print(COLOR_YELLOW);
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(device_class.first).c_str());
shell.print(COLOR_RESET);
show(shell, device_class.first, true);
@@ -390,4 +515,81 @@ void Command::show_all(uuid::console::Shell & shell) {
}
}
// Extract only the path component from the passed URI and normalized it
// e.g. //one/two////three/// becomes /one/two/three
std::string SUrlParser::path() {
std::string s = "/"; // set up the beginning slash
for (std::string & f : m_folders) {
s += f;
s += "/";
}
s.pop_back(); // deleting last letter, that is slash '/'
return std::string(s);
}
SUrlParser::SUrlParser(const char * uri) {
parse(uri);
}
bool SUrlParser::parse(const char * uri) {
m_folders.clear();
m_keysvalues.clear();
enum Type { begin, folder, param, value };
std::string s;
const char * c = uri;
enum Type t = Type::begin;
std::string last_param;
if (c != nullptr || *c != '\0') {
do {
if (*c == '/') {
if (s.length() > 0) {
m_folders.push_back(s);
s.clear();
}
t = Type::folder;
} else if (*c == '?' && (t == Type::folder || t == Type::begin)) {
if (s.length() > 0) {
m_folders.push_back(s);
s.clear();
}
t = Type::param;
} else if (*c == '=' && (t == Type::param || t == Type::begin)) {
m_keysvalues[s] = "";
last_param = s;
s.clear();
t = Type::value;
} else if (*c == '&' && (t == Type::value || t == Type::param || t == Type::begin)) {
if (t == Type::value) {
m_keysvalues[last_param] = s;
} else if ((t == Type::param || t == Type::begin) && (s.length() > 0)) {
m_keysvalues[s] = "";
last_param = s;
}
t = Type::param;
s.clear();
} else if (*c == '\0' && s.length() > 0) {
if (t == Type::value) {
m_keysvalues[last_param] = s;
} else if (t == Type::folder || t == Type::begin) {
m_folders.push_back(s);
} else if (t == Type::param) {
m_keysvalues[s] = "";
last_param = s;
}
s.clear();
} else if (*c == '\0' && s.length() == 0) {
if (t == Type::param && last_param.length() > 0) {
m_keysvalues[last_param] = "";
}
s.clear();
} else {
s += *c;
}
} while (*c++ != '\0');
}
return true;
}
} // namespace emsesp

View File

@@ -25,6 +25,7 @@
#include <string>
#include <vector>
#include <functional>
#include <unordered_map>
#include "console.h"
@@ -36,18 +37,19 @@ namespace emsesp {
// mqtt flags for command subscriptions
enum CommandFlag : uint8_t {
MQTT_SUB_FLAG_NORMAL = 0, // 0
MQTT_SUB_FLAG_HC = (1 << 0), // 1
MQTT_SUB_FLAG_WWC = (1 << 1), // 2
MQTT_SUB_FLAG_NOSUB = (1 << 2), // 4
HIDDEN = (1 << 3), // 8
ADMIN_ONLY = (1 << 4) // 16
MQTT_SUB_FLAG_DEFAULT = 0, // 0 no flags set, always subscribe to MQTT
MQTT_SUB_FLAG_HC = (1 << 0), // 1 TAG_HC1 - TAG_HC4
MQTT_SUB_FLAG_WWC = (1 << 1), // 2 TAG_WWC1 - TAG_WWC4
MQTT_SUB_FLAG_NOSUB = (1 << 2), // 4
MQTT_SUB_FLAG_WW = (1 << 3), // 8 TAG_DEVICE_DATA_WW
HIDDEN = (1 << 4), // 16 do not show in API or Web
ADMIN_ONLY = (1 << 5) // 32 requires authentication
};
// return status after calling a Command
enum CommandRet : uint8_t {
ERRORED = 0,
FAIL = 0, // 0 or FALSE
OK, // 1 or TRUE
NOT_FOUND, // 2
ERROR, // 3
@@ -56,7 +58,7 @@ enum CommandRet : uint8_t {
};
using cmd_function_p = std::function<bool(const char * data, const int8_t id)>;
using cmd_json_function_p = std::function<bool(const char * data, const int8_t id, JsonObject & json)>;
using cmd_json_function_p = std::function<bool(const char * data, const int8_t id, JsonObject & output)>;
class Command {
public:
@@ -100,35 +102,75 @@ class Command {
return cmdfunctions_;
}
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & json);
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id = -1);
#define add_
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject & output);
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value);
// with normal call back function taking a value and id
static void add(const uint8_t device_type,
const __FlashStringHelper * cmd,
const cmd_function_p cb,
const __FlashStringHelper * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL);
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
static void add_json(const uint8_t device_type,
const __FlashStringHelper * cmd,
const cmd_json_function_p cb,
const __FlashStringHelper * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL);
// callback function taking value, id and a json object for its output
static void add(const uint8_t device_type,
const __FlashStringHelper * cmd,
const cmd_json_function_p cb,
const __FlashStringHelper * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
static void show_all(uuid::console::Shell & shell);
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
static Command::CmdFunction * find_command(const uint8_t device_type, char * cmd, int8_t & id);
static void show(uuid::console::Shell & shell, uint8_t device_type, bool verbose);
static void show_devices(uuid::console::Shell & shell);
static bool device_has_commands(const uint8_t device_type);
static bool list(const uint8_t device_type, JsonObject & json);
static bool list(const uint8_t device_type, JsonObject & output);
static uint8_t process(const char * path, const bool is_admin, const JsonObject & input, JsonObject & output);
static const char * parse_command_string(const char * command, int8_t & id);
static const std::string return_code_string(const uint8_t return_code);
private:
static uuid::log::Logger logger_;
static std::vector<CmdFunction> cmdfunctions_; // list of commands
static std::vector<CmdFunction> cmdfunctions_; // the list of commands
inline static uint8_t message(uint8_t error_code, const char * message, JsonObject & output) {
output.clear();
output["message"] = (const char *)message;
return error_code;
}
};
typedef std::unordered_map<std::string, std::string> KeyValueMap_t;
typedef std::vector<std::string> Folder_t;
class SUrlParser {
private:
KeyValueMap_t m_keysvalues;
Folder_t m_folders;
public:
SUrlParser(){};
SUrlParser(const char * url);
bool parse(const char * url);
Folder_t & paths() {
return m_folders;
};
KeyValueMap_t & params() {
return m_keysvalues;
};
std::string path();
};
} // namespace emsesp

View File

@@ -77,7 +77,7 @@ void EMSESPShell::display_banner() {
if (console_hostname_.empty()) {
console_hostname_.resize(16, '\0');
snprintf_P(&console_hostname_[0], console_hostname_.capacity() + 1, PSTR("ems-esp"));
snprintf(&console_hostname_[0], console_hostname_.capacity() + 1, "ems-esp");
}
// load the list of commands
@@ -235,19 +235,25 @@ void EMSESPShell::add_console_commands() {
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
char buffer[4];
shell.printfln(F_(master_thermostat_fmt),
settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str()
settings.master_thermostat == 0 ? read_flash_string(F_(auto)).c_str()
: Helpers::hextoa(buffer, settings.master_thermostat));
shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str());
});
});
commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
CommandFlags::USER,
flash_string_vector{F_(read)},
flash_string_vector{F_(deviceid_mandatory), F_(typeid_mandatory), F_(offset_optional)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
uint16_t type_id = Helpers::hextoint(arguments[1].c_str());
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
if (!EMSESP::valid_device(device_id)) {
shell.printfln(F("Invalid device ID"));
return;
}
uint16_t type_id = Helpers::hextoint(arguments[1].c_str());
if (arguments.size() == 4) {
uint16_t offset = Helpers::hextoint(arguments[2].c_str());
uint8_t length = Helpers::hextoint(arguments.back().c_str());
@@ -272,8 +278,7 @@ void EMSESPShell::add_console_commands() {
settings.master_thermostat = value;
EMSESP::actual_master_thermostat(value); // set the internal value too
char buffer[5];
shell.printfln(F_(master_thermostat_fmt),
!value ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
shell.printfln(F_(master_thermostat_fmt), !value ? read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
return StateUpdateResult::CHANGED;
},
"local");
@@ -373,8 +378,6 @@ void EMSESPShell::add_console_commands() {
return;
}
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
JsonObject json = doc.to<JsonObject>();
// validate that a command is present
if (arguments.size() < 2) {
@@ -383,37 +386,45 @@ void EMSESPShell::add_console_commands() {
return;
}
const char * cmd = arguments[1].c_str();
uint8_t cmd_return = CommandRet::OK;
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN);
int8_t id = -1;
const char * cmd = Command::parse_command_string(arguments[1].c_str(), id);
uint8_t return_code = CommandRet::OK;
JsonObject json = doc.to<JsonObject>();
if (arguments.size() == 2) {
// no value specified, just the cmd
cmd_return = Command::call(device_type, cmd, nullptr, true, -1, json);
return_code = Command::call(device_type, cmd, nullptr, true, id, json);
} else if (arguments.size() == 3) {
if (strncmp(cmd, "info", 4) == 0) {
// info has a id but no value
cmd_return = Command::call(device_type, cmd, nullptr, true, atoi(arguments.back().c_str()), json);
return_code = Command::call(device_type, cmd, nullptr, true, atoi(arguments.back().c_str()), json);
} else if (arguments[2] == "?") {
return_code = Command::call(device_type, cmd, nullptr, true, id, json);
} else {
// has a value but no id so use -1
cmd_return = Command::call(device_type, cmd, arguments.back().c_str(), true, -1, json);
return_code = Command::call(device_type, cmd, arguments.back().c_str(), true, id, json);
}
} else {
// use value, which could be an id or hc
cmd_return = Command::call(device_type, cmd, arguments[2].c_str(), true, atoi(arguments[3].c_str()), json);
if (arguments[2] == "?") {
return_code = Command::call(device_type, cmd, nullptr, true, atoi(arguments[3].c_str()), json);
} else {
return_code = Command::call(device_type, cmd, arguments[2].c_str(), true, atoi(arguments[3].c_str()), json);
}
}
if (cmd_return == CommandRet::OK && json.size()) {
if (return_code == CommandRet::OK && json.size()) {
serializeJsonPretty(doc, shell);
shell.println();
return;
}
if (cmd_return == CommandRet::NOT_FOUND) {
if (return_code == CommandRet::NOT_FOUND) {
shell.println(F("Unknown command"));
shell.print(F("Available commands are: "));
Command::show(shell, device_type, false); // non-verbose mode
} else if (cmd_return != CommandRet::OK) {
} else if (return_code != CommandRet::OK) {
shell.println(F("Bad syntax"));
}
},
@@ -434,7 +445,7 @@ void EMSESPShell::add_console_commands() {
if (Command::device_has_commands(device_type)) {
for (const auto & cf : Command::commands()) {
if (cf.device_type_ == device_type) {
command_list.emplace_back(uuid::read_flash_string(cf.cmd_));
command_list.emplace_back(read_flash_string(cf.cmd_));
}
}
return command_list;
@@ -596,7 +607,7 @@ void Console::load_system_commands(unsigned int context) {
CommandFlags::ADMIN,
flash_string_vector{F_(restart)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.restart();
EMSESP::system_.system_restart();
});
EMSESPShell::commands->add_command(context,
@@ -717,7 +728,7 @@ void Console::load_system_commands(unsigned int context) {
[](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.size() == 0) {
EMSESP::webSettingsService.read([&](WebSettings & settings) {
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
if (!settings.sensor[i].id.isEmpty()) {
shell.print(settings.sensor[i].id);
shell.print(" : ");
@@ -751,32 +762,33 @@ void Console::load_system_commands(unsigned int context) {
EMSESP::dallassensor_.update(arguments.front().c_str(), arguments[1].c_str(), offset);
});
EMSESPShell::commands
->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(board_profile)},
flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
std::vector<uint8_t> data; // led, dallas, rx, tx, button
std::string board_profile = Helpers::toUpper(arguments.front());
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, TLK110, LAN8720, CUSTOM)"));
return;
}
EMSESP::webSettingsService.update(
[&](WebSettings & settings) {
settings.board_profile = board_profile.c_str();
settings.led_gpio = data[0];
settings.dallas_gpio = data[1];
settings.rx_gpio = data[2];
settings.tx_gpio = data[3];
settings.pbutton_gpio = data[4];
return StateUpdateResult::CHANGED;
},
"local");
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4]);
EMSESP::system_.network_init(true);
});
EMSESPShell::commands->add_command(
context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(board_profile)},
flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
std::vector<uint8_t> data; // led, dallas, rx, tx, button
std::string board_profile = Helpers::toUpper(arguments.front());
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, CUSTOM)"));
return;
}
EMSESP::webSettingsService.update(
[&](WebSettings & settings) {
settings.board_profile = board_profile.c_str();
settings.led_gpio = data[0];
settings.dallas_gpio = data[1];
settings.rx_gpio = data[2];
settings.tx_gpio = data[3];
settings.pbutton_gpio = data[4];
settings.phy_type = data[5];
return StateUpdateResult::CHANGED;
},
"local");
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4], data[5]);
EMSESP::system_.network_init(true);
});
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(show), F_(users)},
@@ -811,14 +823,14 @@ std::string EMSESPShell::prompt_suffix() {
}
void EMSESPShell::end_of_transmission() {
invoke_command(uuid::read_flash_string(F_(exit)));
invoke_command(read_flash_string(F_(exit)));
}
EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, bool local)
: uuid::console::Shell(commands, ShellContext::MAIN, local ? (CommandFlags::USER | CommandFlags::LOCAL) : CommandFlags::USER)
, uuid::console::StreamConsole(stream)
, EMSESPShell()
, name_(uuid::read_flash_string(F("Serial")))
, name_(read_flash_string(F("Serial")))
, pty_(SIZE_MAX)
, addr_()
, port_(0) {
@@ -841,7 +853,7 @@ EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, const IPAddress & addr
ptys_[pty_] = true;
}
snprintf_P(text.data(), text.size(), PSTR("pty%u"), pty_);
snprintf(text.data(), text.size(), "pty%u", pty_);
name_ = text.data();
#ifndef EMSESP_STANDALONE
logger().info(F("Allocated console %s for connection from [%s]:%u"), name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);

View File

@@ -46,9 +46,11 @@ using uuid::log::Level;
// clang-format off
// strings stored 32 bit aligned on ESP8266/ESP32
#define MAKE_STR(string_name, string_literal) static constexpr const char * __str__##string_name = string_literal;
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = string_literal;
#define MAKE_PSTR_WORD(string_name) MAKE_PSTR(string_name, #string_name)
#define F_(string_name) FPSTR(__pstr__##string_name)
#define FSTR_(string_name) __str__##string_name
#define MAKE_PSTR_LIST(list_name, ...) static const __FlashStringHelper * const __pstr__##list_name[] PROGMEM = {__VA_ARGS__, nullptr};
#define FL_(list_name) (__pstr__##list_name)
// clang-format on

View File

@@ -41,15 +41,15 @@ void DallasSensor::start() {
bus_.begin(dallas_gpio_);
#endif
// API calls
Command::add_json(
Command::add(
EMSdevice::DeviceType::DALLASSENSOR,
F_(info),
[&](const char * value, const int8_t id, JsonObject & json) { return command_info(value, id, json); },
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
F_(info_cmd));
Command::add_json(
Command::add(
EMSdevice::DeviceType::DALLASSENSOR,
F_(commands),
[&](const char * value, const int8_t id, JsonObject & json) { return command_commands(value, id, json); },
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
F_(commands_cmd));
}
}
@@ -79,7 +79,7 @@ void DallasSensor::loop() {
if (state_ == State::IDLE) {
if (time_now - last_activity_ >= READ_INTERVAL_MS) {
#ifdef EMSESP_DEBUG_SENSOR
LOG_DEBUG(F("Read sensor temperature"));
LOG_DEBUG(F("[DEBUG] Read sensor temperature"));
#endif
if (bus_.reset() || parasite_) {
YIELD;
@@ -142,7 +142,6 @@ void DallasSensor::loop() {
if (sensor.id() == get_id(addr)) {
t += sensor.offset();
if (t != sensor.temperature_c) {
sensor.temperature_c = t;
changed_ |= true;
}
sensor.temperature_c = t;
@@ -211,9 +210,6 @@ bool DallasSensor::temperature_convert_complete() {
#endif
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
#ifndef EMSESP_STANDALONE
if (!bus_.reset()) {
@@ -277,8 +273,6 @@ int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
#endif
}
#pragma GCC diagnostic pop
const std::vector<DallasSensor::Sensor> DallasSensor::sensors() const {
return sensors_;
}
@@ -300,13 +294,13 @@ uint64_t DallasSensor::Sensor::id() const {
std::string DallasSensor::Sensor::id_string() const {
std::string str(20, '\0');
snprintf_P(&str[0],
str.capacity() + 1,
PSTR("%02X-%04X-%04X-%04X"),
(unsigned int)(id_ >> 48) & 0xFF,
(unsigned int)(id_ >> 32) & 0xFFFF,
(unsigned int)(id_ >> 16) & 0xFFFF,
(unsigned int)(id_)&0xFFFF);
snprintf(&str[0],
str.capacity() + 1,
"%02X-%04X-%04X-%04X",
(unsigned int)(id_ >> 48) & 0xFF,
(unsigned int)(id_ >> 32) & 0xFFFF,
(unsigned int)(id_ >> 16) & 0xFFFF,
(unsigned int)(id_)&0xFFFF);
return str;
}
@@ -314,7 +308,7 @@ std::string DallasSensor::Sensor::to_string(const bool name) const {
std::string str = id_string();
EMSESP::webSettingsService.read([&](WebSettings & settings) {
if (settings.dallas_format == Dallas_Format::NAME || name) {
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) {
str = settings.sensor[i].name.c_str();
}
@@ -329,7 +323,7 @@ int16_t DallasSensor::Sensor::offset() const {
std::string str = id_string();
int16_t offset = 0; // default value
EMSESP::webSettingsService.read([&](WebSettings & settings) {
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) {
offset = settings.sensor[i].offset;
}
@@ -348,7 +342,7 @@ void DallasSensor::delete_ha_config(uint8_t index, const char * name) {
std::string topicname = name;
std::replace(topicname.begin(), topicname.end(), '-', '_');
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/dallassensor_%s/config"), Mqtt::base().c_str(), topicname.c_str());
snprintf(topic, sizeof(topic), "homeassistant/sensor/%s/dallassensor_%s/config", Mqtt::base().c_str(), topicname.c_str());
Mqtt::publish(topic);
registered_ha_[index] = false; // forces a recreate of the HA config topic
}
@@ -377,7 +371,7 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset)
EMSESP::webSettingsService.update(
[&](WebSettings & settings) {
// check for new name of stored id
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
if (strcmp(id, settings.sensor[i].id.c_str()) == 0) {
if (strlen(name) == 0 && offset == 0) { // delete entry if name and offset is empty
LOG_INFO(F("Deleting entry for sensor %s"), id);
@@ -398,7 +392,7 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset)
}
// check for free place
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
if (settings.sensor[i].id.isEmpty()) {
settings.sensor[i].id = id;
settings.sensor[i].name = (strlen(name) == 0) ? id : name;
@@ -411,7 +405,7 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset)
}
// check if there is a unused id and overwrite it
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
bool found = false;
for (const auto & sensor : sensors_) {
if (strcmp(sensor.id_string().c_str(), settings.sensor[i].id.c_str()) == 0) {
@@ -447,14 +441,14 @@ bool DallasSensor::updated_values() {
}
// list commands
bool DallasSensor::command_commands(const char * value, const int8_t id, JsonObject & json) {
return Command::list(EMSdevice::DeviceType::DALLASSENSOR, json);
bool DallasSensor::command_commands(const char * value, const int8_t id, JsonObject & output) {
return Command::list(EMSdevice::DeviceType::DALLASSENSOR, output);
}
// creates JSON doc from values
// returns false if empty
// e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":23.30},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":24.0}}
bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject & json) {
bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject & output) {
if (sensors_.size() == 0) {
return false;
}
@@ -462,23 +456,23 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
uint8_t i = 1; // sensor count
for (const auto & sensor : sensors_) {
char sensorID[10]; // sensor{1-n}
snprintf_P(sensorID, 10, PSTR("sensor%d"), i++);
snprintf(sensorID, 10, "sensor%d", i++);
if (id == -1) { // show number and id
JsonObject dataSensor = json.createNestedObject(sensorID);
JsonObject dataSensor = output.createNestedObject(sensorID);
dataSensor["id"] = sensor.to_string();
if (Helpers::hasValue(sensor.temperature_c)) {
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
}
} else { // show according to format
if (dallas_format_ == Dallas_Format::NUMBER && Helpers::hasValue(sensor.temperature_c)) {
json[sensorID] = (float)(sensor.temperature_c) / 10;
output[sensorID] = (float)(sensor.temperature_c) / 10;
} else if (Helpers::hasValue(sensor.temperature_c)) {
json[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
output[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
}
}
}
return (json.size() > 0);
return (output.size() > 0);
}
// send all dallas sensor values as a JSON package to MQTT
@@ -494,7 +488,7 @@ void DallasSensor::publish_values(const bool force) {
for (const auto & sensor : sensors_) {
char sensorID[10]; // sensor{1-n}
snprintf_P(sensorID, 10, PSTR("sensor%d"), sensor_no);
snprintf(sensorID, 10, "sensor%d", sensor_no);
if (dallas_format_ == Dallas_Format::NUMBER) {
// e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03","temp":23.3},"sensor2":{"id":"28-233D-9497-0C03","temp":24.0}}
JsonObject dataSensor = doc.createNestedObject(sensorID);
@@ -514,28 +508,28 @@ void DallasSensor::publish_values(const bool force) {
config["dev_cla"] = FJSON("temperature");
char stat_t[50];
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/dallassensor_data"), Mqtt::base().c_str());
snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str());
config["stat_t"] = stat_t;
config["unit_of_meas"] = FJSON("°C");
char str[50];
if (dallas_format_ != Dallas_Format::NUMBER) {
snprintf_P(str, sizeof(str), PSTR("{{value_json['%s']}}"), sensor.to_string().c_str());
snprintf(str, sizeof(str), "{{value_json['%s']}}", sensor.to_string().c_str());
} else {
snprintf_P(str, sizeof(str), PSTR("{{value_json.sensor%d.temp}}"), sensor_no);
snprintf(str, sizeof(str), "{{value_json.sensor%d.temp}}", sensor_no);
}
config["val_tpl"] = str;
// name as sensor number not the long unique ID
if (dallas_format_ != Dallas_Format::NUMBER) {
snprintf_P(str, sizeof(str), PSTR("Dallas Sensor %s"), sensor.to_string().c_str());
snprintf(str, sizeof(str), "Dallas Sensor %s", sensor.to_string().c_str());
} else {
snprintf_P(str, sizeof(str), PSTR("Dallas Sensor %d"), sensor_no);
snprintf(str, sizeof(str), "Dallas Sensor %d", sensor_no);
}
config["name"] = str;
snprintf_P(str, sizeof(str), PSTR("dallasensor_%s"), sensor.to_string().c_str());
snprintf(str, sizeof(str), "dallasensor_%s", sensor.to_string().c_str());
config["uniq_id"] = str;
JsonObject dev = config.createNestedObject("dev");
@@ -547,12 +541,12 @@ void DallasSensor::publish_values(const bool force) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
if (dallas_format_ == Dallas_Format::NUMBER) {
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallassensor_%d/config"), Mqtt::base().c_str(), sensor_no);
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%d/config", Mqtt::base().c_str(), sensor_no);
} else {
// use '_' as HA doesn't like '-' in the topic name
std::string topicname = sensor.to_string();
std::replace(topicname.begin(), topicname.end(), '-', '_');
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallassensor_%s/config"), Mqtt::base().c_str(), topicname.c_str());
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::base().c_str(), topicname.c_str());
}
Mqtt::publish_ha(topic, config.as<JsonObject>());

View File

@@ -130,8 +130,8 @@ class DallasSensor {
int16_t get_temperature_c(const uint8_t addr[]);
uint64_t get_id(const uint8_t addr[]);
bool command_info(const char * value, const int8_t id, JsonObject & json);
bool command_commands(const char * value, const int8_t id, JsonObject & json);
bool command_info(const char * value, const int8_t id, JsonObject & output);
bool command_commands(const char * value, const int8_t id, JsonObject & output);
void delete_ha_config(uint8_t index, const char * name);

View File

@@ -24,10 +24,6 @@
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
#endif
#ifndef EMSESP_DEFAULT_TX_DELAY
#define EMSESP_DEFAULT_TX_DELAY 0 // no delay
#endif
#ifndef EMSESP_DEFAULT_EMS_BUS_ID
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
#endif
@@ -96,7 +92,7 @@
#define EMSESP_DEFAULT_BOARD_PROFILE "S32" // Gateway S32
#endif
// Default GPIO PIN definitions - based on Wemos/Nodemcu
// Default GPIO PIN definitions
#ifndef EMSESP_DEFAULT_RX_GPIO
#define EMSESP_DEFAULT_RX_GPIO 23 // D7
@@ -118,6 +114,10 @@
#define EMSESP_DEFAULT_PBUTTON_GPIO 0
#endif
#ifndef EMSESP_DEFAULT_PHY_TYPE
#define EMSESP_DEFAULT_PHY_TYPE 0 // No Ethernet, just Wifi
#endif
// MQTT
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
@@ -152,8 +152,8 @@
#define EMSESP_DEFAULT_NESTED_FORMAT 1
#endif
#ifndef EMSESP_DEFAULT_SUBSCRIBE_FORMAT
#define EMSESP_DEFAULT_SUBSCRIBE_FORMAT 0
#ifndef EMSESP_DEFAULT_SEND_RESPONSE
#define EMSESP_DEFAULT_SEND_RESPONSE false
#endif
#ifndef EMSESP_DEFAULT_SOLAR_MAXFLOW
@@ -164,4 +164,16 @@
#define EMSESP_DEFAULT_SENSOR_NAME ""
#endif
#ifndef EMSESP_DEFAULT_WEBLOG_LEVEL
#define EMSESP_DEFAULT_WEBLOG_LEVEL 6 // INFO
#endif
#ifndef EMSESP_DEFAULT_WEBLOG_BUFFER
#define EMSESP_DEFAULT_WEBLOG_BUFFER 50
#endif
#ifndef EMSESP_DEFAULT_WEBLOG_COMPACT
#define EMSESP_DEFAULT_WEBLOG_COMPACT true
#endif
#endif

View File

@@ -35,7 +35,8 @@
{133, DeviceType::BOILER, F("Logano GB125/KB195i/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{167, DeviceType::BOILER, F("Cerapur Aero"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco/Geo 5xx"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{173, DeviceType::BOILER, F("Geo 5xx"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{195, DeviceType::BOILER, F("Condens 5000i/Greenstar 8000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{208, DeviceType::BOILER, F("Logamax Plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
@@ -61,13 +62,15 @@
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
{224, DeviceType::CONTROLLER, F("9000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{240, DeviceType::CONTROLLER, F("Rego 3000"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{241, DeviceType::CONTROLLER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
// Thermostat - not currently supporting write operations, like the Easy/100 types - 0x18
{202, DeviceType::THERMOSTAT, F("Logamatic TC100/Moduline Easy"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{203, DeviceType::THERMOSTAT, F("EasyControl CT200"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19 / 0x38
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19-0x1B for hc2-4 / 0x38
{ 65, DeviceType::THERMOSTAT, F("RC10"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N},// 0x17
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_N},// 0x10 - based on RC35
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
@@ -77,14 +80,16 @@
{ 90, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
{ 94, DeviceType::THERMOSTAT, F("RFM20 Remote"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
{151, DeviceType::THERMOSTAT, F("RC25"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
{158, DeviceType::THERMOSTAT, F("RC300/RC310/Moduline 3000/1010H/CW400/Sense II"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
{172, DeviceType::THERMOSTAT, F("Rego 2000/3000"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
{216, DeviceType::THERMOSTAT, F("CRF200S"), DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
// Thermostat - Sieger - 0x10 / 0x17
{ 66, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC30_N}, // 0x10
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
// Thermostat - Junkers - 0x10
@@ -92,7 +97,7 @@
{106, DeviceType::THERMOSTAT, F("FW200"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{107, DeviceType::THERMOSTAT, F("FR100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{108, DeviceType::THERMOSTAT, F("FR110"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{111, DeviceType::THERMOSTAT, F("FR10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{111, DeviceType::THERMOSTAT, F("FR10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{147, DeviceType::THERMOSTAT, F("FR50"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD},
{191, DeviceType::THERMOSTAT, F("FR120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
{192, DeviceType::THERMOSTAT, F("FW120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
@@ -110,6 +115,7 @@
{159, DeviceType::MIXER, F("MM50"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{160, DeviceType::MIXER, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{161, DeviceType::MIXER, F("MM200"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{204, DeviceType::MIXER, F("MP100"), DeviceFlags::EMS_DEVICE_FLAG_MP},
// Heat Pumps - 0x38
{200, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
@@ -120,10 +126,16 @@
{205, DeviceType::CONNECT, F("Moduline Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{206, DeviceType::CONNECT, F("Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
// wireless sensor base- 0x50
{236, DeviceType::CONNECT, F("Wireless sensor base"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Switches - 0x11
{ 71, DeviceType::SWITCH, F("WM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Gateways - 0x48
{189, DeviceType::GATEWAY, F("KM200/MB LAN 2"), DeviceFlags::EMS_DEVICE_FLAG_NONE}
{189, DeviceType::GATEWAY, F("KM200/MB LAN 2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
// generic - 0x40 or other with no product-id and no version
{0, DeviceType::GENERIC, F("unknown"), DeviceFlags::EMS_DEVICE_FLAG_NONE}
// clang-format on

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,7 @@ class Boiler : public EMSdevice {
public:
Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
virtual bool publish_ha_config();
virtual bool publish_ha_device_config();
private:
static uuid::log::Logger logger_;
@@ -55,40 +55,42 @@ class Boiler : public EMSdevice {
static constexpr uint8_t EMS_BOILER_SELFLOWTEMP_HEATING = 20; // was originally 70, changed to 30 for issue #193, then to 20 with issue #344
// ww
uint8_t wWSetTemp_; // Warm Water set temperature
uint8_t wWSelTemp_; // Warm Water selected temperature
uint8_t wWType_; // 0-off, 1-flow, 2-flowbuffer, 3-buffer, 4-layered buffer
uint8_t wWComfort_; // WW comfort mode
uint8_t wWCircPump_; // Warm Water circulation pump available
uint8_t wWChargeType_; // Warm Water charge type (pump or 3-way-valve)
uint8_t wWDisinfectionTemp_; // Warm Water disinfection temperature to prevent infection
uint8_t wWCircMode_; // Warm Water circulation pump mode
uint8_t wWCirc_; // Circulation on/off
uint16_t wWCurTemp_; // Warm Water current temperature
uint16_t wWCurTemp2_; // Warm Water current temperature storage
uint8_t wWCurFlow_; // Warm Water current flow temp in l/min
uint16_t wWStorageTemp1_; // warm water storage temp 1
uint16_t wWStorageTemp2_; // warm water storage temp 2
uint8_t wWActivated_; // Warm Water activated
uint8_t wWOneTime_; // Warm Water one time function on/off
uint8_t wWDisinfecting_; // Warm Water disinfection on/off
uint8_t wWCharging_; // Warm Water charging on/off
uint8_t wWRecharging_; // Warm Water recharge on/off
uint8_t wWTempOK_; // Warm Water temperature ok on/off
uint8_t wWActive_; //
uint8_t wWHeat_; // 3-way valve on WW
uint8_t wWSetPumpPower_; // ww pump speed/power?
uint8_t wWFlowTempOffset_; // Boiler offset for ww heating
uint8_t wWMaxPower_; // Warm Water maximum power
uint32_t wWStarts_; // Warm Water # starts
uint32_t wWStarts2_; // Warm water control starts
uint32_t wWWorkM_; // Warm Water # minutes
int8_t wWHystOn_;
int8_t wWHystOff_;
uint8_t wWTapActivated_; // maintenance-mode to switch DHW off
uint16_t mixerTemp_; // mixing temperature
uint16_t tankMiddleTemp_; // Tank middle temperature (TS3)
uint8_t wwSetTemp_; // Warm Water set temperature
uint8_t wwSelTemp_; // Warm Water selected temperature
uint8_t wwSelTempLow_; // Warm Water lower selected temperature
uint8_t wwSelTempOff_; // Warm Water selected temperature for off position
uint8_t wwSelTempSingle_; // Warm Water single charge temperature
uint8_t wwType_; // 0-off, 1-flow, 2-flowbuffer, 3-buffer, 4-layered buffer
uint8_t wwComfort_; // WW comfort mode
uint8_t wwCircPump_; // Warm Water circulation pump available
uint8_t wwChargeType_; // Warm Water charge type (pump or 3-way-valve)
uint8_t wwDisinfectionTemp_; // Warm Water disinfection temperature to prevent infection
uint8_t wwCircMode_; // Warm Water circulation pump mode
uint8_t wwCirc_; // Circulation on/off
uint16_t wwCurTemp_; // Warm Water current temperature
uint16_t wwCurTemp2_; // Warm Water current temperature storage
uint8_t wwCurFlow_; // Warm Water current flow temp in l/min
uint16_t wwStorageTemp1_; // warm water storage temp 1
uint16_t wwStorageTemp2_; // warm water storage temp 2
uint8_t wwActivated_; // Warm Water activated
uint8_t wwOneTime_; // Warm Water one time function on/off
uint8_t wwDisinfect_; // Warm Water disinfection on/off
uint8_t wwCharging_; // Warm Water charging on/off
uint8_t wwRecharging_; // Warm Water recharge on/off
uint8_t wwTempOK_; // Warm Water temperature ok on/off
uint8_t wwActive_; //
uint8_t wwHeat_; // 3-way valve on WW
uint8_t wwSetPumpPower_; // ww pump speed/power?
uint8_t wwFlowTempOffset_; // Boiler offset for ww heating
uint8_t wwMaxPower_; // Warm Water maximum power
uint32_t wwStarts_; // Warm Water starts
uint32_t wwStarts2_; // Warm water control starts
uint32_t wwWorkM_; // Warm Water minutes
int8_t wwHystOn_;
int8_t wwHystOff_;
uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
uint16_t wwMixerTemp_; // mixing temperature
uint16_t wwTankMiddleTemp_; // Tank middle temperature (TS3)
// main
uint8_t id_; // product id
@@ -125,11 +127,11 @@ class Boiler : public EMSdevice {
uint8_t setFlowTemp_; // boiler setpoint temp
uint8_t curBurnPow_; // Burner current power %
uint8_t setBurnPow_; // max output power in %
uint32_t burnStarts_; // # burner restarts
uint32_t burnStarts_; // burner restarts
uint32_t burnWorkMin_; // Total burner operating time
uint32_t heatWorkMin_; // Total heat operating time
uint32_t UBAuptime_; // Total UBA working hours
char lastCode_[30]; // last error code
char lastCode_[60]; // last error code
char serviceCode_[4]; // 3 character status/service code
uint16_t serviceCodeNumber_; // error/service code
@@ -138,38 +140,62 @@ class Boiler : public EMSdevice {
uint32_t upTimeCompHeating_; // Operating time compressor heating
uint32_t upTimeCompCooling_; // Operating time compressor cooling
uint32_t upTimeCompWw_; // Operating time compressor warm water
uint32_t upTimeCompPool_; // Operating time compressor pool
uint32_t totalCompStarts_; // Total Commpressor control starts
uint32_t heatingStarts_; // Heating control starts
uint32_t coolingStarts_; // Cooling control starts
uint32_t poolStarts_; // Warm water control starts
uint32_t nrgConsTotal_; // Energy consumption total
uint32_t nrgConsCompTotal_; // Energy consumption compressor total
uint32_t nrgConsCompHeating_; // Energy consumption compressor heating
uint32_t nrgConsCompWw_; // Energy consumption compressor warm water
uint32_t nrgConsCompCooling_; // Energy consumption compressor cooling
uint32_t nrgConsCompPool_; // Energy consumption compressor pool
uint32_t nrgSuppTotal_; // Energy supplied total
uint32_t nrgSuppHeating_; // Energy supplied heating
uint32_t nrgSuppWw_; // Energy supplied warm water
uint32_t nrgSuppCooling_; // Energy supplied cooling
uint32_t nrgSuppPool_; // Energy supplied pool
uint32_t auxElecHeatNrgConsTotal_; // Auxiliary electrical heater energy consumption total
uint32_t auxElecHeatNrgConsHeating_; // Auxiliary electrical heater energy consumption heating
uint32_t auxElecHeatNrgConsWW_; // Auxiliary electrical heater energy consumption DHW
uint32_t auxElecHeatNrgConsPool_; // Auxiliary electrical heater energy consumption Pool
char maintenanceMessage_[4];
char maintenanceDate_[12];
uint8_t maintenanceType_;
uint16_t maintenanceTime_;
// heatpump
uint8_t hpPower_;
int16_t hpTc0_;
int16_t hpTc1_;
int16_t hpTc3_;
int16_t hpTr3_;
int16_t hpTr4_;
int16_t hpTr5_;
int16_t hpTr6_;
int16_t hpTr7_;
int16_t hpTl2_;
int16_t hpPl1_;
int16_t hpPh1_;
uint8_t hpPower_;
uint8_t hpCompOn_;
uint8_t hpBrinePumpSpd_;
uint8_t hpCompSpd_;
uint8_t hpCircSpd_;
uint16_t hpBrineIn_;
uint16_t hpBrineOut_;
uint16_t hpSuctionGas_;
uint16_t hpHotGas_;
uint8_t hpSwitchValve_;
uint8_t hpActivity_;
uint8_t hpHeatingOn_;
uint8_t hpCoolingOn_;
uint8_t hpWwOn_;
uint8_t hpPoolOn_;
uint8_t hpHeatingOn;
int16_t hpTc0_;
int16_t hpTc1_;
int16_t hpTc3_;
int16_t hpTr3_;
int16_t hpTr4_;
int16_t hpTr5_;
int16_t hpTr6_;
int16_t hpTr7_;
int16_t hpTl2_;
int16_t hpPl1_;
int16_t hpPh1_;
// Pool unit
int8_t poolSetTemp_;
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);
@@ -189,6 +215,7 @@ class Boiler : public EMSdevice {
void process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram);
void process_UBAMaintenanceData(std::shared_ptr<const Telegram> telegram);
void process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram);
void process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorWWPlus(std::shared_ptr<const Telegram> telegram);
void process_UBAInformation(std::shared_ptr<const Telegram> telegram);
void process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram);
@@ -196,19 +223,23 @@ class Boiler : public EMSdevice {
void process_UBASettingsWW(std::shared_ptr<const Telegram> telegram);
void process_HpPower(std::shared_ptr<const Telegram> telegram);
void process_HpOutdoor(std::shared_ptr<const Telegram> telegram);
void process_HpPool(std::shared_ptr<const Telegram> telegram);
// commands - none of these use the additional id parameter
bool set_warmwater_mode(const char * value, const int8_t id);
bool set_warmwater_activated(const char * value, const int8_t id);
bool set_ww_mode(const char * value, const int8_t id);
bool set_ww_activated(const char * value, const int8_t id);
bool set_tapwarmwater_activated(const char * value, const int8_t id);
bool set_warmwater_onetime(const char * value, const int8_t id);
bool set_warmwater_circulation(const char * value, const int8_t id);
bool set_warmwater_circulation_pump(const char * value, const int8_t id);
bool set_warmwater_circulation_mode(const char * value, const int8_t id);
bool set_warmwater_temp(const char * value, const int8_t id);
bool set_disinfect_temp(const char * value, const int8_t id);
bool set_warmwater_maxpower(const char * value, const int8_t id);
bool set_wWFlowTempOffset(const char * value, const int8_t id);
bool set_ww_onetime(const char * value, const int8_t id);
bool set_ww_disinfect(const char * value, const int8_t id);
bool set_ww_circulation(const char * value, const int8_t id);
bool set_ww_circulation_pump(const char * value, const int8_t id);
bool set_ww_circulation_mode(const char * value, const int8_t id);
bool set_ww_temp(const char * value, const int8_t id);
bool set_ww_temp_low(const char * value, const int8_t id);
bool set_ww_temp_single(const char * value, const int8_t id);
bool set_ww_disinfect_temp(const char * value, const int8_t id);
bool set_ww_maxpower(const char * value, const int8_t id);
bool set_ww_flowTempOffset(const char * value, const int8_t id);
bool set_flow_temp(const char * value, const int8_t id);
bool set_burn_power(const char * value, const int8_t id);
bool set_heating_activated(const char * value, const int8_t id);
@@ -227,6 +258,7 @@ class Boiler : public EMSdevice {
bool set_maintenancedate(const char * value, const int8_t id);
bool set_ww_hyst_on(const char * value, const int8_t id);
bool set_ww_hyst_off(const char * value, const int8_t id);
bool set_pool_temp(const char * value, const int8_t id);
};
} // namespace emsesp

View File

@@ -29,7 +29,7 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
}
// publish HA config
bool Connect::publish_ha_config() {
bool Connect::publish_ha_device_config() {
return true;
}

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