981 Commits

Author SHA1 Message Date
proddy
fc057d18c9 Merge remote-tracking branch 'origin/dev' 2022-09-18 14:33:23 +02:00
proddy
326f05d63f updated packages 2022-09-18 12:54:52 +02:00
proddy
eda203e350 formatting 2022-09-18 12:54:45 +02:00
Proddy
70efa4f4e6 Merge pull request #613 from MichaelDvP/dev
fix #42
2022-09-09 16:06:04 +01:00
MichaelDvP
a020a48e63 network/mqtt init fix #42 2022-09-09 15:28:57 +02:00
MichaelDvP
a56c847790 update packages 2022-09-09 15:28:13 +02:00
Proddy
763337db3f Merge pull request #603 from MichaelDvP/dev
AM200, RC100H, etc.
2022-08-20 09:31:44 +02:00
MichaelDvP
f6c5a4d064 fix #601 dhw modes for RC300 2022-08-18 12:40:29 +02:00
MichaelDvP
a20b6b59c9 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2022-08-18 12:37:13 +02:00
MichaelDvP
9a708a263c ignore tx-read errors for higher offsets 2022-08-11 12:47:07 +02:00
MichaelDvP
c78a42e42f fix offset of dummy reply 2022-08-11 11:22:49 +02:00
MichaelDvP
df555acc0e set wwFlowTempOffset to 100 2022-08-11 09:20:15 +02:00
MichaelDvP
bf1ce5150a map RC100H values to device and tag hc 2022-08-11 09:19:53 +02:00
MichaelDvP
b499d908ab handle telegrams from src only 2022-08-10 15:24:08 +02:00
MichaelDvP
029ef56edd hc checks for RC100H 2022-08-10 10:48:55 +02:00
MichaelDvP
70f52ce484 humidity, dewtemp as uint 2022-08-09 14:57:18 +02:00
MichaelDvP
a087974acb add RC100H remoteseltemp and temp correction 2022-08-09 08:36:46 +02:00
MichaelDvP
f96b13c3cd fix tag of wwStarts2 2022-08-09 08:11:59 +02:00
MichaelDvP
9cf89ee6f6 update comment, fix hp humidity scale 2022-08-08 17:36:14 +02:00
MichaelDvP
a5be7f4e38 reboot required when changing uart pins 2022-08-08 17:35:17 +02:00
MichaelDvP
d367c20a13 add disconnected banner to dashboard, #591 2022-08-08 09:58:13 +02:00
MichaelDvP
97132a4758 min/max settings for AM200 2022-08-08 09:57:28 +02:00
MichaelDvP
308b8fb779 fix position of am200 mixingvalves 2022-08-05 06:49:04 +02:00
MichaelDvP
79e0b80e51 no fetch for broadcasted remote telegrams 2022-08-05 06:48:09 +02:00
MichaelDvP
f79258f645 product_id 200 as thermostat RC100H, #590 2022-08-02 19:28:54 +02:00
Proddy
8d3bcde8d6 Merge pull request #589 from ichsteffen/dev
Update MqttSettingsForm.tsx
2022-07-30 16:55:36 +02:00
MichaelDvP
8ee70a1263 version/changelog 2022-07-30 14:50:40 +02:00
MichaelDvP
fb940269de AM200 settings #573 2022-07-30 14:50:23 +02:00
MichaelDvP
ec02e55635 Fix typo mqtt-base 2022-07-30 14:30:42 +02:00
ichsteffen
f87f18ca8e Update MqttSettingsForm.tsx 2022-07-30 14:14:52 +02:00
MichaelDvP
94afd8a3a6 rename telegrams am200 2022-07-27 07:34:16 +02:00
MichaelDvP
0d69a0a3db add ahs valve positions 0-100% 2022-07-26 07:06:25 +02:00
Proddy
beae2a2587 Merge pull request #586 from proddy/dev
link to device entity details
2022-07-25 19:40:16 +02:00
Proddy
a3a29132ab no need to add uique id to end of device type since it's unique anyway 2022-07-25 17:17:10 +02:00
Proddy
cdc04f987c update packages 2022-07-25 17:16:49 +02:00
Proddy
ba2ded1a5a wifi disconnect if Warning log 2022-07-24 15:20:23 +02:00
Proddy
d0a779b185 minor formatting 2022-07-24 13:40:08 +02:00
Proddy
d12879b07b link to device entity details from customization page 2022-07-24 13:39:52 +02:00
Proddy
d0bcbb8d1b Merge pull request #585 from MichaelDvP/dev
AM200, EM10 changes
2022-07-23 14:55:29 +02:00
Proddy
97257ac821 Merge pull request #583 from proddy/dev
minor changes when testing coldshot
2022-07-23 13:04:46 +02:00
Proddy
d5e19fdf5b updated mock doc 2022-07-23 13:03:42 +02:00
MichaelDvP
9c1d08c057 prevent reinitialization of all on first setting change 2022-07-23 10:40:44 +02:00
MichaelDvP
c4f4a440ac EM10 as gateway 2022-07-22 16:16:55 +02:00
MichaelDvP
8ebefebb0a don't show AM200 valves with unknown offset 2022-07-22 16:16:33 +02:00
Proddy
3b86d1b5aa minor text change 2022-07-22 15:56:54 +02:00
Proddy
505e339406 testing coldshot 2022-07-22 15:56:45 +02:00
Proddy
297134dd81 remove serial output if no serial selected during upload 2022-07-22 15:56:27 +02:00
Proddy
02989ec4b3 cleanup duplicate text 2022-07-22 15:56:11 +02:00
Proddy
9573869c7c fix compile errors with debug 2022-07-22 15:55:58 +02:00
MichaelDvP
295bbed4ae fix #581 2022-07-21 17:49:38 +02:00
MichaelDvP
c81e3e832a handle EM10 as switch 2022-07-21 17:48:28 +02:00
MichaelDvP
a018432b81 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2022-07-21 17:48:04 +02:00
MichaelDvP
6bc8a4b6c5 EM10 as switch 2022-07-21 08:57:48 +02:00
Proddy
32dcd7313b Merge pull request #579 from proddy/dev 2022-07-21 08:16:48 +02:00
Proddy
387c9c63f1 typo 2022-07-20 20:34:55 +02:00
Proddy
ca30b8233b fixes 2022-07-20 20:33:43 +02:00
Proddy
c3757f95e5 only send MQTT after shower finished 2022-07-20 12:04:01 +02:00
Proddy
66fbd2b359 show MQTT disconnects as warnings #543 2022-07-20 12:03:47 +02:00
Proddy
4420ae33b8 merge upload and download in webUI #577 2022-07-20 12:03:21 +02:00
Proddy
d707e92d59 formatting 2022-07-20 12:02:32 +02:00
MichaelDvP
273526dc16 check tags 2022-07-19 12:59:54 +02:00
MichaelDvP
ebc5cfa2d8 add tag "ahs" for alternative heating source 2022-07-19 07:50:37 +02:00
MichaelDvP
13430de3ba detect boiler with id 0x60 2022-07-18 21:04:53 +02:00
MichaelDvP
78dee6c7fe AM200 temperatures 2022-07-18 20:04:56 +02:00
MichaelDvP
43db536878 add EM10 error detection module 2022-07-18 19:43:59 +02:00
MichaelDvP
c765a274b2 AM200, first test 2022-07-18 17:20:05 +02:00
MichaelDvP
8101fcf95d button for help->info, file: .json.txt 2022-07-18 13:48:49 +02:00
Proddy
69568abb8a Merge pull request #572 from MichaelDvP/dev
fix automatic build
2022-07-13 00:26:22 +05:30
MichaelDvP
a14b43dbe2 update packages 2022-07-12 13:51:46 +02:00
Proddy
2fedd6da15 Merge pull request #571 from MichaelDvP/dev
FS_Buffer to 8k for customizations, fix#570
2022-07-12 14:36:20 +05:30
MichaelDvP
fc3224c9ea FS_Buffer to 8k for customizations 2022-07-12 10:27:35 +02:00
Proddy
39feb25600 Merge pull request #568 from proddy/dev
fix column size for temperature
2022-07-05 13:12:01 +02:00
proddy
eddbacf5a7 fix column size for temperature 2022-07-05 13:00:58 +02:00
Proddy
4e5b460447 Merge pull request #567 from proddy/dev
update table packages
2022-07-04 21:43:03 +02:00
Proddy
0e172ff06d update table packages 2022-07-04 21:42:35 +02:00
Proddy
40fa1d47bc Merge pull request #566 from MichaelDvP/dev 2022-06-30 11:56:58 +02:00
MichaelDvP
70fe08a3c8 fix #550 set_summermode 2022-06-27 16:45:32 +02:00
MichaelDvP
b840e2a6b5 Merge branch 'emsesp:dev' into dev 2022-06-27 14:38:58 +02:00
MichaelDvP
422657fbf7 fix #550
typo summersetmode/summermode
2022-06-27 14:26:56 +02:00
Proddy
da596a01b9 Merge pull request #563 from proddy/dev
optimize for mobile phone layout
2022-06-26 21:13:16 +02:00
Proddy
96d2fd2393 optimize for mobile phone layout 2022-06-26 21:12:21 +02:00
Proddy
42c9aeeed6 Merge pull request #562 from proddy/dev
fix multiline
2022-06-25 16:35:05 +02:00
Proddy
a9b32d8ba7 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2022-06-25 16:34:19 +02:00
Proddy
30da267db0 fix #547 2022-06-25 16:34:18 +02:00
Proddy
84f643e461 Merge pull request #561 from proddy/dev
table resizing fix
2022-06-25 14:28:07 +02:00
Proddy
6298fab94f Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2022-06-25 14:25:53 +02:00
Proddy
9639f9cee6 fix table column resizing #519 2022-06-25 14:25:51 +02:00
Proddy
4af1de4093 auto-formatting 2022-06-25 14:25:31 +02:00
Proddy
2c90a2c3aa 3.4.2b3 2022-06-25 14:25:11 +02:00
Proddy
254d4dd02d updated packages 2022-06-25 14:25:05 +02:00
Proddy
4eceeea4d1 3.4.2b3 2022-06-25 14:24:52 +02:00
MichaelDvP
efa5eb273c show summermode or operatingmode, fix #550 2022-06-25 09:51:15 +02:00
Proddy
8a05e11297 Merge pull request #560 from pswid/dev
Shorten "friendly names" in Home Assistant
2022-06-24 13:59:58 +02:00
pswid
1238e89084 add unit of measurement in HA for analog sensors 2022-06-21 20:39:52 +02:00
pswid
1c48d12167 Shorten "friendly names" in Home Assistant
see #555
2022-06-21 20:28:22 +02:00
Proddy
1fab47acd5 Merge pull request #558 from MichaelDvP/dev
burner max power to 254%, fix #553
2022-06-21 10:06:17 +02:00
MichaelDvP
0e563e91b1 burner max power to 254%, fix #553 2022-06-21 06:37:47 +02:00
Proddy
43b1a4572a Merge pull request #557 from MichaelDvP/dev 2022-06-20 22:21:54 +02:00
MichaelDvP
bf18718b9a shortname to wwVacations/wwHoliday to avoid conflict 2022-06-20 21:38:12 +02:00
Proddy
8790d81e03 Merge pull request #556 from MichaelDvP/dev
fix #554, add dhw vacation/holiday
2022-06-20 20:16:56 +02:00
MichaelDvP
0e5cb2df8e fix #554, add dhw vacation/holiday 2022-06-20 17:42:38 +02:00
Proddy
46c5913000 Merge pull request #552 from proddy/dev
update packages
2022-06-17 11:29:50 +02:00
Proddy
c35433856f update packages 2022-06-17 11:28:55 +02:00
Proddy
67f898bbec force removal of directories 2022-06-17 11:28:50 +02:00
Proddy
449c01d345 Merge pull request #551 from MichaelDvP/dev
fix #549, set minimum emergencyTemp to 15°C
2022-06-17 10:59:38 +02:00
MichaelDvP
cec1fce745 fix #549, set minimum emergencyTemp to 15°C 2022-06-15 09:56:25 +02:00
Proddy
a02d4ab259 Merge pull request #545 from MichaelDvP/dev
update OneWire lib #542
2022-06-07 21:19:25 +02:00
MichaelDvP
a22783a3e4 update OneWire lib 2022-06-07 08:18:56 +02:00
Proddy
9524ec1317 Merge pull request #544 from proddy/dev
minor fixes
2022-06-06 11:23:23 +02:00
proddy
64a7108e6d make it compile standalone 2022-06-06 11:20:16 +02:00
proddy
cf4818f09c update packages 2022-06-06 11:20:09 +02:00
proddy
98fb970dac add test for #541 2022-06-06 11:19:56 +02:00
Proddy
d162206f75 Merge pull request #540 from MichaelDvP/dev
WebStatus: add syslog count/fail, show only enabled services
2022-06-03 16:08:22 +02:00
MichaelDvP
1609b76f0a WebStatus:add syslog count/fail, show only enabled services 2022-06-03 13:39:26 +02:00
Proddy
794b3c0471 Merge pull request #538 from MichaelDvP/dev
fix #536 and uart task priority
2022-06-03 07:36:49 +02:00
MichaelDvP
e7bcc380e3 fix #536, FR100 datetime not writable 2022-06-02 18:34:02 +02:00
MichaelDvP
fe12f1903d uart higher prriority 2022-06-02 18:33:23 +02:00
Proddy
41ec4b9bcf Merge pull request #537 from proddy/dev
update packages
2022-06-02 14:54:01 +02:00
Proddy
f64dd00cce update packages 2022-06-02 14:53:38 +02:00
Proddy
5ef2f8292c Merge pull request #534 from MichaelDvP/dev
some small corrections
2022-05-30 12:11:14 +02:00
MichaelDvP
ae3ead6b10 some small corrections 2022-05-30 10:00:45 +02:00
Proddy
874f686882 Merge pull request #533 from proddy/dev
chamges for standalone compiling
2022-05-29 21:16:29 +02:00
Proddy
383c30f649 chamges for standalone compiling 2022-05-29 21:16:12 +02:00
Proddy
0b2c95fc4e Merge pull request #532 from MichaelDvP/idf4_no_master
Update to platformio2.3.0/IDF4/Arduino2.0, remove master themostat
2022-05-29 20:42:16 +02:00
MichaelDvP
eacdd62cc8 update changelog 2022-05-29 19:15:45 +02:00
MichaelDvP
a692eb557d Merge branch 'idf4' into idf4_no_master 2022-05-29 19:12:58 +02:00
MichaelDvP
99b6acfc07 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4 2022-05-29 19:07:32 +02:00
Proddy
fa486c8b60 Merge pull request #520 from tp1de/dev
Additions for RC310
2022-05-29 16:28:31 +02:00
Proddy
b19b3e5869 Merge branch 'dev' into dev 2022-05-29 16:28:15 +02:00
Proddy
e6bfd90235 Merge pull request #531 from proddy/dev
3.4.2b2
2022-05-29 16:20:12 +02:00
Proddy
072fe526ea 3.4.2b2 2022-05-29 16:19:55 +02:00
Proddy
18e9b99413 Merge remote-tracking branch 'origin/dev' into main 2022-05-29 16:16:38 +02:00
Proddy
e65f5072fe Merge pull request #527 from proddy/dev
various minor changes, including managing hidden entities in customization screen
2022-05-29 16:11:53 +02:00
Proddy
435218888a #513 2022-05-29 14:36:13 +02:00
Proddy
3e7b743dfa updated changelog for 3.4.1 2022-05-29 13:58:01 +02:00
Proddy
c693ef6307 update packages 2022-05-29 13:57:54 +02:00
Proddy
e4447ee1b9 improved icon toggling 2022-05-28 20:23:16 +02:00
Proddy
356390c92b fix standalone server for handling customizations 2022-05-28 15:27:08 +02:00
tp1de
ab6893adeb corrections nofrostmode 2022-05-28 13:16:40 +02:00
Proddy
55133b028a typo 2022-05-27 17:18:50 +02:00
Proddy
1fbd69b718 fixes to customizations 2022-05-27 17:16:23 +02:00
tp1de
97239268ac damping 2022-05-27 16:07:34 +02:00
tp1de
10bf065a2a Add damping to RC310 2022-05-27 16:00:18 +02:00
tp1de
107106d759 requested changes on PR from @MichaelDvP 2022-05-27 14:05:56 +02:00
tp1de
622a5db8d1 wwprio for RC310 thermostat 2022-05-27 11:07:21 +02:00
Proddy
1e082f941a customizations, swap shortname and fullname, using id as unique shortname 2022-05-27 09:49:32 +02:00
Proddy
8824f4f3da added formatName so hidden entities render nicely 2022-05-26 17:49:43 +02:00
Proddy
f8bf6b5cc8 fix isCmdOnly() function for commands like reset 2022-05-26 17:49:25 +02:00
Proddy
be844a5184 if no fullname, use shortname in customizations json 2022-05-26 17:49:04 +02:00
Proddy
6ec67f7417 rename ha_climate_config_creation 2022-05-26 17:48:34 +02:00
Proddy
2c468c7225 formatting 2022-05-26 17:48:18 +02:00
Proddy
af7cd7b009 rename generate_values_web_customization 2022-05-26 17:48:10 +02:00
Proddy
cba081379e rename generate_values_web_customization 2022-05-26 17:48:01 +02:00
Proddy
a9064baefc 3.4.1b1 2022-05-26 17:47:15 +02:00
Proddy
31627bb704 remove debug_flags 2022-05-26 17:47:04 +02:00
Proddy
c4cfabfbaf update test data for customizations 2022-05-26 17:46:51 +02:00
Proddy
9d25f8049c Merge pull request #525 from MichaelDvP/dev
fixes #522, #523, #524
2022-05-26 15:17:51 +02:00
MichaelDvP
2d50f18dcf changelog from 3.4.0 2022-05-26 13:34:51 +02:00
MichaelDvP
89b0711464 changelog 2022-05-26 13:05:37 +02:00
MichaelDvP
34cb3ad375 fix #524, free memory of json response 2022-05-26 12:43:10 +02:00
MichaelDvP
d2609e4291 fix #523, rename 'climate' to more explaning name 2022-05-26 12:42:55 +02:00
MichaelDvP
77a8857e2f fix #522, device-flag for IVT controller 2022-05-26 12:42:38 +02:00
tp1de
53d3bda326 Added switchonoptimization for RC310 2022-05-25 21:20:32 +02:00
tp1de
570588f498 delete obsolete reducetemp1 2022-05-25 20:01:56 +02:00
tp1de
60e1a93966 correct nofrostmodet1 & reducemode1 - just change enums ! 2022-05-25 14:22:12 +02:00
tp1de
3115fae807 Added enum_controlmode1 - Adjusted enum list for RC310 2022-05-25 13:38:50 +02:00
Thomas
c2767a866f Merge branch 'emsesp:dev' into dev 2022-05-25 09:54:53 +02:00
Proddy
60714b0b0b Merge pull request #521 from proddy/dev
React18 modifications
2022-05-24 22:50:52 +02:00
Proddy
c32cdf5d98 React18 modifications 2022-05-24 22:50:18 +02:00
tp1de
c1598f3d4e changelog 2022-05-24 20:08:10 +02:00
tp1de
94c45891b4 add nofrostmode1 for RC310 2022-05-24 19:51:37 +02:00
tp1de
9d3426877d reducemode1, reducetemp and noreducetemp for RC310 2022-05-24 16:53:01 +02:00
MichaelDvP
d3d9a9300b Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4_no_master 2022-05-24 07:15:23 +02:00
Proddy
472b97e89e update to 3.4.1b0 2022-05-23 21:34:00 +02:00
Proddy
a47e0e8266 update for 3.4.0 2022-05-23 21:20:45 +02:00
Proddy
f412ddc716 Merge remote-tracking branch 'origin/dev' into main 2022-05-23 21:20:36 +02:00
Proddy
859c5950f4 Merge pull request #518 from proddy/dev
autoformatting
2022-05-23 21:08:31 +02:00
Proddy
b9f63bcfb1 autoformatting 2022-05-23 21:08:00 +02:00
Proddy
86b79a2685 package update 2022-05-23 21:07:53 +02:00
MichaelDvP
c502aa7d1c Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4_no_master 2022-05-23 19:32:08 +02:00
Proddy
7f587070e5 Merge pull request #516 from proddy/dev
upgrade to react18
2022-05-23 17:56:59 +02:00
Proddy
e121b480ef Merge pull request #515 from tp1de/dev
emergencyops and emergencytemp for RC310
2022-05-23 09:22:19 +02:00
Proddy
22668d76ee upgrade to react18 2022-05-22 13:57:17 +02:00
tp1de
2c0c5ba425 emergencyops and emergencytemp for RC310 2022-05-21 13:57:31 +02:00
MichaelDvP
6670dfdf07 formatting 2022-05-21 11:36:38 +02:00
MichaelDvP
b426f4f904 Merge remote-tracking branch 'origin/dev' into idf4_no_master 2022-05-21 11:36:08 +02:00
Proddy
e8387b363f Merge pull request #514 from tp1de/dev
wwmaxtemp for RC310
2022-05-21 11:23:13 +02:00
MichaelDvP
a6e0280ac1 NPM update 2022-05-21 08:37:00 +02:00
tp1de
5ab22af9c3 correct boilers.h 2022-05-20 16:55:16 +02:00
tp1de
4263860760 Merge branch 'dev' of https://github.com/tp1de/EMS-ESP32 into dev 2022-05-20 15:14:10 +02:00
MichaelDvP
9a3668c16b Merge remote-tracking branch 'origin/dev' into idf4 2022-05-20 14:39:36 +02:00
MichaelDvP
3a538a97d6 Merge remote-tracking branch 'proddy/dev' into idf4_no_master 2022-05-20 13:24:05 +02:00
tp1de
c637dcbbd8 wwmaxtemp for RC310 2022-05-20 13:14:09 +02:00
Proddy
6c68f2950a Merge pull request #512 from proddy/dev
text changes, new screenshots
2022-05-20 12:48:44 +02:00
Proddy
9310f606ad change tooltips #504 2022-05-20 12:47:54 +02:00
Proddy
7ea6c542cf updated screenshots 2022-05-20 12:40:34 +02:00
Proddy
5b4c5b063d increase # devices supported 2022-05-20 12:40:27 +02:00
Proddy
8afb5b3a7c formatting 2022-05-20 12:40:07 +02:00
Proddy
8fbe2199f4 Merge pull request #510 from tp1de/dev
New entities for RC310 EMS+ Heating systems
2022-05-20 12:35:03 +02:00
Proddy
62a5254add Merge pull request #506 from MichaelDvP/dev
summermode for heatpump and HM200 hybridmodule
2022-05-20 10:42:27 +02:00
Proddy
ab5ba6fc80 Merge pull request #511 from proddy/dev
minor updates
2022-05-20 09:59:34 +02:00
Proddy
f9d46904f6 updated URL for api/system/info 2022-05-20 09:59:15 +02:00
Proddy
6545dbd483 updated packages 2022-05-20 09:58:53 +02:00
Proddy
353853af14 max must be a integer (changed 19.9 to 20) 2022-05-20 09:58:43 +02:00
tp1de
6fef3990a7 locale_EN.h 2022-05-20 00:13:56 +02:00
tp1de
571f5577d1 wwchargeoptimization UBAParameterWWPlus 2022-05-20 00:10:58 +02:00
Thomas
e2de89c763 Update locale_EN.h 2022-05-19 23:29:20 +02:00
tp1de
c481e1fe0f wwcomfort1 for UBAParameterWWPlus (RC310) 2022-05-19 16:59:03 +02:00
tp1de
9034c16e27 wwFlowTempOffset RC310 2022-05-18 18:11:17 +02:00
MichaelDvP
95927fc5d8 Merge branch 'idf4' into idf4_no_master 2022-05-18 18:03:31 +02:00
MichaelDvP
4023d7856b NPM update 2022-05-18 17:45:04 +02:00
MichaelDvP
8307753d53 fix uart crash with dev platform 2022-05-18 17:18:01 +02:00
MichaelDvP
c7fb339ee9 Merge branch 'dev' into idf4 2022-05-18 17:09:38 +02:00
MichaelDvP
ca50c5178a fix typos 2022-05-18 17:04:54 +02:00
MichaelDvP
33e58ec45a RC30 designtemp write, 2.fix #496 2022-05-18 07:05:11 +02:00
MichaelDvP
e175b5a5a2 heatpump summermode -> heatpump operatingsmode 2022-05-17 20:53:22 +02:00
MichaelDvP
6072606918 heatpump summer mode, fix #503 2022-05-17 12:50:34 +02:00
MichaelDvP
3a04e891b4 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2022-05-17 12:21:51 +02:00
Proddy
862cc8d982 Merge pull request #502 from proddy/dev
#501
2022-05-16 21:30:40 +02:00
Proddy
fc29a3ad95 replace api/settings and customizatons with secure REST calls #501 2022-05-16 21:30:06 +02:00
Proddy
ae9af3bf0b update packages 2022-05-16 21:29:39 +02:00
Proddy
1bdaaf0d0e Merge branch 'emsesp:dev' into dev 2022-05-15 10:44:52 +02:00
MichaelDvP
f3e99f9092 Add HM200 hybrid manager as heatpump 2022-05-15 08:58:28 +02:00
Proddy
ba315f2159 Merge pull request #497 from MichaelDvP/dev
fix #496, RC30 design temp
2022-05-14 21:06:39 +02:00
MichaelDvP
1863e57f5b fix #496, RC30 design temp 2022-05-12 18:21:30 +02:00
MichaelDvP
a0454943a7 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into idf4_no_master 2022-05-10 14:01:31 +02:00
MichaelDvP
a3130d498e Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into idf4 2022-05-10 13:44:22 +02:00
MichaelDvP
3168438d6f LITTLEFS->LittleFS 2022-05-10 13:26:50 +02:00
MichaelDvP
9202d799c6 npm update 2022-05-10 13:26:35 +02:00
MichaelDvP
c9f10fb5cc uart isr to IRAM 2022-05-10 12:42:46 +02:00
MichaelDvP
b3fde5a2da Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into idf4 2022-05-10 12:40:45 +02:00
MichaelDvP
8339fc735c uart in IRAM 2022-05-10 12:36:42 +02:00
MichaelDvP
3e2f7a5976 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4_no_master 2022-05-10 09:59:15 +02:00
MichaelDvP
9c732ae19f uart back 2022-05-10 09:59:04 +02:00
Proddy
7e26c0633a Merge branch 'emsesp:dev' into dev 2022-05-08 20:14:16 +02:00
Proddy
226a557d5c prevent build failing 2022-05-08 20:13:48 +02:00
Proddy
08021138ae Merge branch 'emsesp:dev' into dev 2022-05-08 20:11:32 +02:00
MichaelDvP
b16fa6d5c0 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4_no_master 2022-05-08 18:21:48 +02:00
MichaelDvP
6203eedbe5 uart 2022-05-08 17:27:56 +02:00
Proddy
db8c30ddbb fix for customizations after upload new file 2022-05-08 17:02:38 +02:00
Proddy
0e9202ae4d change to saving customization file after upload 2022-05-08 17:01:38 +02:00
Proddy
85f6628718 Merge pull request #495 from proddy/dev
standalone compiling fix
2022-05-08 15:02:25 +02:00
Proddy
8dab64b9bb Merge branch 'emsesp:dev' into dev 2022-05-08 15:01:44 +02:00
Proddy
8b521aa572 fix standalone compiling 2022-05-08 15:01:04 +02:00
Proddy
663fd22e03 Merge pull request #494 from proddy/dev
Feature: upload customization settings from a file #256
2022-05-08 14:59:16 +02:00
Proddy
1f933fb26a updates for #256 2022-05-08 14:58:41 +02:00
Proddy
0faa56f5ff Merge branch 'emsesp:dev' into dev 2022-05-08 12:57:33 +02:00
Proddy
9c2aac31b6 Merge pull request #493 from MichaelDvP/dev
api entitiy attributes with hcs
2022-05-08 12:57:12 +02:00
MichaelDvP
fdab56936f Merge branch 'dev' into idf4_no_master 2022-05-08 11:35:04 +02:00
MichaelDvP
c0aa263f2b parse_command checks prefix-numbers and prefix id 2022-05-08 11:19:12 +02:00
MichaelDvP
6c151654cc parse_command 2022-05-08 10:33:17 +02:00
Proddy
87c9d4d823 fix duplication 2022-05-07 18:41:42 +02:00
Proddy
fd53999777 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2022-05-07 18:40:21 +02:00
Proddy
ce60bd10b6 telnet and analog on by default 2022-05-07 17:50:36 +02:00
Proddy
ee0492771d text change 2022-05-07 17:50:22 +02:00
Proddy
964e75e191 minor change 2022-05-07 17:50:10 +02:00
Proddy
cb16be6b1d remove id from User 2022-05-07 17:50:00 +02:00
Proddy
84a2a7340c comments 2022-05-07 17:49:49 +02:00
Proddy
a383925661 fix axios error reporting 2022-05-07 17:49:36 +02:00
Proddy
c49928bf3b remove id in User 2022-05-07 17:49:27 +02:00
Proddy
02fc57961b remove id 2022-05-07 17:49:15 +02:00
Proddy
9923b60d64 Feature: upload customization settings from a file #256 2022-05-07 17:49:02 +02:00
Proddy
f243162724 comments 2022-05-07 17:46:49 +02:00
Proddy
b35ab94509 upload script example 2022-05-07 17:46:31 +02:00
Proddy
4f399b51f5 package updates 2022-05-07 17:46:13 +02:00
MichaelDvP
9e14b34e1f uart, tx-mode 1 check buffersize, change event-task 2022-05-07 15:34:58 +02:00
MichaelDvP
bf54ee2e9d Merge branch 'dev' into idf4 2022-05-07 15:31:54 +02:00
MichaelDvP
9d2ed1cdbd uart, tx-mode 1 check buffersize 2022-05-07 15:29:11 +02:00
MichaelDvP
db9237451f Merge branch 'dev' of https://github.com/MichaelDvP/EMS-ESP32 into idf4_no_master 2022-05-06 17:54:34 +02:00
MichaelDvP
6277fbbea1 api entitiy attributes with hcs 2022-05-06 13:32:18 +02:00
Proddy
1b21e02b7a Merge pull request #492 from MichaelDvP/dev
some small fixes
2022-05-06 12:42:43 +02:00
MichaelDvP
1a5b012545 csv utf-8 with semicolon and Intl.Number format 2022-05-06 12:15:25 +02:00
MichaelDvP
f4aa4ed912 allow id for attribute 2022-05-06 12:02:56 +02:00
MichaelDvP
d075ee3111 Merge branch 'dev_no_master_thermostat' into idf4_no_master 2022-05-06 12:01:38 +02:00
MichaelDvP
5917238a62 Merge branch 'dev_' into idf4 2022-05-06 08:12:02 +02:00
MichaelDvP
b2429e3cff charset for plain text output 2022-05-06 07:59:48 +02:00
MichaelDvP
90aa385326 shower-times in system/settings 2022-05-06 07:59:07 +02:00
MichaelDvP
39d9eec890 mdns in lib_standanlone 2022-05-06 07:58:22 +02:00
MichaelDvP
cfff3a4b4a enableMDNS to lib_standalone 2022-05-05 16:56:33 +02:00
MichaelDvP
5abbec9dea uart: larger queue, check buffer and telegram 2022-05-05 06:59:34 +02:00
MichaelDvP
7b6ca7cd73 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4 2022-05-04 16:04:46 +02:00
Proddy
b3f0ba348a Merge pull request #489 from MichaelDvP/dev
options for mDNS and dashboard boolean format
2022-05-04 15:54:11 +02:00
MichaelDvP
5d2f648d03 add option for rendering booleans on Dashboard #456 2022-05-04 15:46:23 +02:00
MichaelDvP
e00da5a721 add option to switch mDNS off/on 2022-05-04 14:14:17 +02:00
MichaelDvP
ca7bea3f24 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4 2022-05-04 07:10:13 +02:00
Proddy
d344924a3c Merge pull request #488 from proddy/dev
remove comments, render nicer on mobiles
2022-05-03 22:21:34 +02:00
Proddy
7f405e5212 Merge branch 'emsesp:dev' into dev 2022-05-03 22:21:13 +02:00
Proddy
c2f38396eb Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2022-05-03 22:20:57 +02:00
Proddy
501726c6ad remove comments, render nicer on mobiles 2022-05-03 22:20:55 +02:00
Proddy
d07f18bd41 Merge pull request #487 from proddy/dev
table formatting again
2022-05-03 19:51:00 +02:00
Proddy
931782df1c Merge branch 'emsesp:dev' into dev 2022-05-03 19:50:42 +02:00
Proddy
8e65e31ed6 table formatting 2022-05-03 19:50:07 +02:00
MichaelDvP
221e28c996 uart checks 2022-05-03 07:32:51 +02:00
MichaelDvP
579c869688 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4 2022-05-02 15:42:02 +02:00
Proddy
8a4decd48b Merge pull request #486 from proddy/dev
minor formatting changes to table headers
2022-05-02 15:12:43 +02:00
Proddy
66b3572dfe Merge branch 'emsesp:dev' into dev 2022-05-02 15:12:19 +02:00
Proddy
7172ed303e make table headers consistent 2022-05-02 15:11:57 +02:00
Proddy
883c81320b package updates 2022-05-02 15:11:42 +02:00
Proddy
7ad1251c89 Merge pull request #485 from proddy/dev
attributes via API #462
2022-05-02 13:10:40 +02:00
Proddy
75317d278d Merge branch 'emsesp:dev' into dev 2022-05-02 13:10:13 +02:00
Proddy
5438d7bbd9 API extract individual attributes - #462 2022-05-02 13:08:31 +02:00
MichaelDvP
8dc5f0a5ac use development platform 2022-05-02 13:01:10 +02:00
MichaelDvP
9c38987dd4 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4 2022-05-02 11:21:35 +02:00
MichaelDvP
0c6c7f999f replace OneWire 2022-05-02 11:19:48 +02:00
Proddy
2ae5d4dcf5 Merge pull request #484 from proddy/dev
added shower commands & some refactoring
2022-05-01 23:00:28 +02:00
Proddy
473b870035 Merge branch 'emsesp:dev' into dev 2022-05-01 22:59:56 +02:00
Proddy
5e9e995e4b added shower trigger and coldshot times - #436 2022-05-01 22:59:35 +02:00
Proddy
270b81fafd static code analysis warning fixes 2022-05-01 22:59:17 +02:00
Proddy
191e47eb14 rename value to stop static code analysis complaining 2022-05-01 22:58:50 +02:00
MichaelDvP
dc9c21fc65 packages, platform, typo 2022-05-01 19:10:25 +02:00
MichaelDvP
1afa21c199 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4 2022-05-01 18:15:34 +02:00
MichaelDvP
7dc65a8c98 tasmota forc of if2.0.3 2022-05-01 18:14:09 +02:00
Proddy
20ff1725e7 Merge pull request #483 from proddy/dev
update react dropzone
2022-05-01 17:54:29 +02:00
Proddy
a0759f68b2 Merge branch 'emsesp:dev' into dev 2022-05-01 17:53:59 +02:00
proddy
08135baae6 update dropzone 2022-05-01 17:53:41 +02:00
Proddy
bf40d8feaf Merge pull request #482 from proddy/dev
make value sortable, it doesn't really make sense but it fixes the formatting alignment issues
2022-05-01 16:41:29 +02:00
Proddy
2c54cafdb6 Merge branch 'emsesp:dev' into dev 2022-05-01 16:40:40 +02:00
Proddy
3ff75db00a add more minor formatting 2022-05-01 16:35:45 +02:00
Proddy
469677aba5 Merge pull request #481 from proddy/dev
table formatting, upgrade packages & arduinojson, fix mqtt retry
2022-05-01 16:04:46 +02:00
Proddy
da2c81818e table formatting 2022-05-01 16:02:48 +02:00
Proddy
c1a71afd77 update ArduinoJson to 6.19.4 2022-05-01 15:54:49 +02:00
Proddy
7fe2b843e6 b14 2022-05-01 15:54:07 +02:00
Proddy
2c490e4148 possible fix for #476 2022-05-01 15:43:36 +02:00
Proddy
6241999d2f more formatting 2022-05-01 15:38:58 +02:00
Proddy
6b315d52a8 update axioserror 2022-05-01 13:12:40 +02:00
Proddy
0143e89e27 table formatting 2022-05-01 13:12:30 +02:00
Proddy
7130513593 update packages 2022-05-01 13:12:05 +02:00
Proddy
a721826821 fix Updating table component in the WebUI to support sorting & filtering #470 2022-05-01 13:00:48 +02:00
MichaelDvP
53e3600af7 uart driver installed, events 2022-04-29 11:52:39 +02:00
MichaelDvP
43d838548b faster network reconnect 2022-04-29 11:51:55 +02:00
MichaelDvP
5b6c0317f6 set platform 2022-04-25 18:21:01 +02:00
MichaelDvP
9fe0ee119b 1. try to fix uart, irq called not only on brk-irq? 2022-04-25 17:21:27 +02:00
MichaelDvP
c70d0352ea DHCP (works with framewaork 3.5.0) 2022-04-25 17:09:40 +02:00
MichaelDvP
9a8449e4fe NTP Settings 2022-04-25 17:08:44 +02:00
MichaelDvP
fa166483bb SYSTEM_EVENT.. to ARDUINO_EVENT_... 2022-04-25 17:07:52 +02:00
MichaelDvP
986b4df997 use buildin LittleFS 2022-04-25 17:02:50 +02:00
MichaelDvP
fbec88f6c8 OneWire check arduino version 2022-04-25 16:56:35 +02:00
MichaelDvP
adc88d53d3 update mbedtsl (works also for framework 3.5.0) 2022-04-25 16:55:51 +02:00
MichaelDvP
ab17dd5812 Cell height for multiline values 2022-04-25 16:23:48 +02:00
Proddy
cc42ac3584 Merge pull request #478 from MichaelDvP/dev
fix #476 and some names
2022-04-25 09:02:54 -04:00
MichaelDvP
4bba52a09e native boolean values in commands, fix #476 2022-04-25 14:38:12 +02:00
MichaelDvP
00abae10ac network security names fixed 2022-04-25 14:37:15 +02:00
MichaelDvP
eafec3045d hp full names lower case (see #450) 2022-04-25 14:36:21 +02:00
Proddy
343d0a7baa fix arduino core to 3.5.0 for ci jobs 2022-04-24 19:28:40 -04:00
Proddy
b688f6f7d0 b13 2022-04-24 19:21:44 -04:00
Proddy
9cc03948fa Merge pull request #475 from proddy/dev
Changes to Table (sorting, format, search)
2022-04-24 19:20:16 -04:00
proddy
c80d5c6bbf fix sort when click on icon and 3-way sort cycle 2022-04-24 19:18:19 -04:00
proddy
271b36c602 update packages 2022-04-24 19:18:02 -04:00
proddy
b2a885bf3f hide web also deselects favourite 2022-04-24 14:20:49 -04:00
proddy
c0c33d80c7 fix typo in sorting 2022-04-24 14:20:30 -04:00
proddy
9ff497914e fix to arduino 3.5.0 2022-04-24 14:20:16 -04:00
proddy
726b4fcc0b more updates to tables 2022-04-20 18:06:35 -04:00
proddy
56860da4af remove TODO on sorting 2022-04-20 18:06:26 -04:00
Proddy
929a97622c Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2022-04-19 17:22:21 +02:00
Proddy
09d25c04bc add search 2022-04-19 17:22:20 +02:00
Proddy
15f5f55833 Merge branch 'emsesp:dev' into dev 2022-04-19 12:25:11 +02:00
Proddy
a243b0a853 Merge pull request #472 from bavo/fix-selected-temp-range
Increase range for the selected_room_temperature variable
2022-04-19 12:24:56 +02:00
bavo
d76a838165 Increase range for the selected_room_temperature variable 2022-04-19 12:14:38 +02:00
Proddy
22595b0f24 show resize column header bars, disable default sorting 2022-04-18 12:54:44 +02:00
Proddy
9f6bbac8ce formatting 2022-04-18 11:05:38 +02:00
Proddy
101f94bea6 Merge pull request #5 from proddy/newtable
Newtable
2022-04-17 20:58:26 +02:00
proddy
52847b06f4 text changes 2022-04-17 20:53:53 +02:00
proddy
3c9cad2717 add set all enabled button 2022-04-17 20:52:24 +02:00
proddy
3acb9c456e formatting 2022-04-16 18:16:01 +02:00
Proddy
615e86d177 update 2022-04-16 16:58:49 +02:00
proddy
9d7820d155 togglebutton 2022-04-16 15:31:46 +02:00
Proddy
275044bd78 customizations table part 1 2022-04-16 09:04:34 +02:00
Proddy
2f21c896a6 fix dallas sensor checking 2022-04-15 18:40:35 +02:00
Proddy
04a374c380 more tables 2022-04-15 16:22:05 +02:00
Proddy
4b3b9524ef changes to backend, some refactoring 2022-04-15 13:04:52 +02:00
Proddy
9e293136b9 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into newtable 2022-04-15 08:11:20 +02:00
Proddy
45cda9b0cc Merge pull request #471 from MichaelDvP/dev
Additions for #437, #459, #464
2022-04-14 18:26:46 +02:00
Proddy
cd5fef6891 add export button 2022-04-14 18:25:50 +02:00
MichaelDvP
a9a11f464b add comment to boiler: hybrid heatpump 2022-04-14 11:59:44 +02:00
Proddy
2a4288e11d initial commit with new table code 2022-04-14 11:44:01 +02:00
MichaelDvP
47410ce6cc Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev_ 2022-04-14 07:38:32 +02:00
MichaelDvP
f9207ecd0f Merge branch 'dev_' into dev_no_master_thermostat 2022-04-14 07:27:07 +02:00
Proddy
fdefd02812 Merge pull request #467 from kwertie01/feature/moduline400WWandTimer
Feature Moduline 400 WW settings and timer clock program
2022-04-13 23:21:51 +02:00
Friso
4cdeef212d fixed T1 to T4 for RC20 2022-04-13 11:18:28 +02:00
Friso
258db5c7e8 disable usage of T0 for RC30 in set_switchtime 2022-04-13 11:09:31 +02:00
Friso
b59ef15cae fixed set_switchtemp, process_RC20Timer 2022-04-13 09:17:22 +02:00
MichaelDvP
94f5d4d503 fix set_switchtime 2022-04-12 18:56:23 +02:00
Friso
9ed2a99f50 fix duplicate functions after rebase 2022-04-12 07:00:41 +02:00
Friso
9f86444e0a Merge branch 'feature/moduline400WWandTimer' of https://github.com/kwertie01/EMS-ESP32 into feature/moduline400WWandTimer 2022-04-12 06:35:33 +02:00
kwertie01
7bad5d10d8 fix sunday in dayofweek enum 2022-04-12 06:34:13 +02:00
kwertie01
f23bf270d0 fix wrong offset in set_party 2022-04-12 06:34:13 +02:00
kwertie01
8839909425 Added Moduline 400 WW settings and clock program 2022-04-12 06:34:13 +02:00
MichaelDvP
ec1b0b3641 add IPM ww-parameters to mixer 2022-04-11 15:03:07 +02:00
kwertie01
5882218cff fix sunday in dayofweek enum 2022-04-11 11:59:29 +02:00
kwertie01
cffc2695dd fix wrong offset in set_party 2022-04-11 11:57:42 +02:00
kwertie01
49eb049533 Added Moduline 400 WW settings and clock program 2022-04-11 11:43:15 +02:00
MichaelDvP
4009a1a25c b12, cleanup, formatting, small fixes 2022-04-11 10:20:40 +02:00
MichaelDvP
eec4d7863d move Hybrid settings to thermostat 2022-04-10 18:25:11 +02:00
MichaelDvP
0d79138e0b product-id 100 is mixer with max wwc10, add handlers_ignored 2022-04-10 14:37:43 +02:00
MichaelDvP
75914da36a thermostat switchpoints allow T1-T4 2022-04-10 14:35:55 +02:00
MichaelDvP
cf876acc5d rename hybrid entities 2022-04-10 14:34:10 +02:00
MichaelDvP
6d6cb755e2 update hybrid heatpump #459 2022-04-10 10:03:36 +02:00
Proddy
555b1ad996 Merge pull request #460 from MichaelDvP/dev
add Hybrid Heatpump
2022-04-08 17:25:52 +02:00
MichaelDvP
15a8d6dbf1 add Hybrid Heatpump 2022-04-08 15:30:34 +02:00
Proddy
c3256824b2 Merge pull request #458 from MichaelDvP/dev
mqtt HA uses only json, disable single
2022-04-08 13:42:15 +02:00
MichaelDvP
9c68142a0d mqtt HA uses only json, disable single 2022-04-08 11:58:48 +02:00
Proddy
b496f7731a Merge pull request #457 from MichaelDvP/dev
Changes #450
2022-04-08 11:13:00 +02:00
MichaelDvP
d203092ba7 Don't show banner while uploading 2022-04-08 10:03:05 +02:00
MichaelDvP
f771bb8043 add clock to controller #439 2022-04-08 10:02:24 +02:00
MichaelDvP
f9b2a71b86 tag to wwstarts2, thermostat clock format, formatting 2022-04-08 10:01:46 +02:00
Proddy
8479be5bd4 Merge pull request #455 from proddy/dev
mqtt clean session default off #441
2022-04-08 07:47:01 +02:00
Proddy
4eddad2cf1 mqtt clean session default off #441 2022-04-08 07:46:18 +02:00
Proddy
8b31e8a9c4 Merge pull request #453 from proddy/dev
updated changelog for Moduline updates
2022-04-06 20:23:35 +02:00
Proddy
d5245f7e7c added missing v3.3.1 to the changelog history 2022-04-06 20:22:14 +02:00
Proddy
56bb5433a0 updated packages 2022-04-06 20:20:24 +02:00
Proddy
3d8396a35a added moduline 400 additions 2022-04-06 20:20:18 +02:00
Proddy
1a58f76ab7 Merge pull request #449 from kwertie01/feature/moduline400
added moduline 400 installation parameters
2022-04-06 10:15:26 +02:00
Proddy
4279962e59 Merge pull request #452 from proddy/dev
saving customizations would overwrite previous settings
2022-04-05 23:29:30 +02:00
Proddy
503df0842b fix saving customizatons #450 2022-04-05 23:28:34 +02:00
Proddy
4a93adb1f8 remove aliases 2022-04-05 23:28:10 +02:00
kwertie01
2674b3a20e added moduline 400 installation parameters 2022-04-05 08:23:30 +02:00
Proddy
3d072f73b7 Merge pull request #447 from proddy/dev
changes per #444
2022-04-04 21:47:12 +02:00
Proddy
4fc5932899 only send back changed entities, put in limit at 50 #444 2022-04-04 21:44:51 +02:00
Proddy
077c20fd34 minor formatting 2022-04-04 21:44:23 +02:00
Proddy
d9716ceb42 added debug line for customization 2022-04-04 21:43:30 +02:00
Proddy
51af4b32d2 removed comments 2022-04-04 21:43:08 +02:00
Proddy
08cae822f1 Merge pull request #445 from MichaelDvP/dev
Status of changes for the last issues
2022-04-04 15:24:59 +02:00
MichaelDvP
30d6fc0fc1 customization buffer 4k, log(debug) buffer size 2022-04-04 12:03:39 +02:00
MichaelDvP
462bf81be1 ntp status and time button 2022-04-04 11:58:53 +02:00
MichaelDvP
26758b965d ivt clock check dst 2022-04-04 11:11:37 +02:00
MichaelDvP
917e4f5cbf check/format solar 2022-04-04 11:10:52 +02:00
MichaelDvP
f02621d1d8 formatting 2022-04-03 19:18:05 +02:00
MichaelDvP
45e0998172 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2022-04-03 18:35:00 +02:00
MichaelDvP
755368440b customization advice with selected icons 2022-04-03 18:34:38 +02:00
MichaelDvP
728ccaefa7 dont set ivt clock automaically 2022-04-03 18:34:09 +02:00
Proddy
ecaa0ec4bb Merge pull request #442 from proddy/dev
fix changing masks for customizations
2022-04-03 17:50:13 +02:00
MichaelDvP
d94eb26fa8 solar mqtt 2022-04-03 13:49:17 +02:00
Proddy
9774661a40 send complete list of entites to server 2022-04-03 13:44:54 +02:00
Proddy
38128f7864 changes to mask_entity() 2022-04-03 13:44:37 +02:00
Proddy
1f8213e315 changes for handling entities in customization 2022-04-03 13:44:02 +02:00
Proddy
fce9a25280 add const function 2022-04-03 13:43:37 +02:00
Proddy
b2de5d47dd small code changes 2022-04-03 13:43:24 +02:00
Proddy
77f583f91f only add entities who's mask has changed to list 2022-04-03 13:43:08 +02:00
Proddy
4a56726f61 bump version 2022-04-03 13:42:32 +02:00
Proddy
4f3dacf81a make deviceentity.w mandatory 2022-04-03 13:42:18 +02:00
Proddy
14593f6a51 reset some test data 2022-04-03 13:41:57 +02:00
Proddy
ae16d01734 add comment about has_cmd 2022-04-03 13:41:38 +02:00
MichaelDvP
6069af3e90 ISM2 missing values, ISM2dhw in solar class 2022-04-03 09:43:53 +02:00
MichaelDvP
4c2408dfda add ISM2 and DHW module #437 2022-04-02 19:10:04 +02:00
Proddy
0bd122b643 Merge pull request #435 from MichaelDvP/dev
check readonly before set datetime to thermostat
2022-04-02 10:19:13 +02:00
MichaelDvP
d747ba0b14 check readonly before set datetime to thermostat 2022-04-02 10:12:08 +02:00
Proddy
91484fd09f Merge pull request #434 from proddy/dev
smaller icons in customization table
2022-04-01 09:17:12 +02:00
Proddy
d427a4bd6c smaller icons 2022-04-01 09:16:12 +02:00
Proddy
b455827c17 Merge branch 'emsesp:dev' into dev 2022-04-01 09:15:48 +02:00
Proddy
b6363957a8 Merge pull request #432 from MichaelDvP/dev
fix/optimization for mode/temperature set
2022-04-01 09:12:25 +02:00
Proddy
ad27f76c4a update packages 2022-03-31 21:50:19 +02:00
MichaelDvP
eeec7142c7 fix mode preset after change for different thermostats 2022-03-31 13:59:57 +02:00
MichaelDvP
efd758d627 seltemp command, check mode first for RC30_n and Junkers 2022-03-31 12:44:39 +02:00
Proddy
608d5e332d Merge pull request #431 from proddy/dev
minor updates to customization test data and layout
2022-03-30 21:05:29 +02:00
proddy
cf4fa9e76d formatting icon table 2022-03-30 20:59:57 +02:00
proddy
2ebb77ff37 don't prefix a count with more than 1 thermostats 2022-03-30 20:59:47 +02:00
proddy
744154ccf4 make customization change test data 2022-03-30 20:59:30 +02:00
Proddy
90c38d0403 Merge pull request #430 from MichaelDvP/dev
Errormessage
2022-03-30 17:51:53 +02:00
MichaelDvP
55d7ef1036 boiler Errormessage 0xBF to trigger 0xC2 reading 2022-03-30 15:56:05 +02:00
MichaelDvP
7c37c9a4c8 recreate ha config on readonly changes 2022-03-30 15:55:24 +02:00
Proddy
54d09ceb51 Merge pull request #429 from proddy/dev
updates to customization screen
2022-03-30 13:16:40 +02:00
proddy
84e21ab992 layout changes to help text 2022-03-30 13:15:48 +02:00
proddy
cb9ddb299a show commands differently 2022-03-30 13:15:38 +02:00
proddy
9e5fdb7cb1 use boolean rendering to fix Web errors with enums 2022-03-30 13:15:25 +02:00
Proddy
97c1d6d73a Merge pull request #427 from MichaelDvP/dev
add NTP status to mqtt/ha (heartbeat) and log
2022-03-29 20:28:47 +02:00
Proddy
0f0fc2a8f7 Merge pull request #426 from proddy/dev
#418
2022-03-29 18:41:24 +02:00
Proddy
c41344e2a5 Merge branch 'emsesp:dev' into dev 2022-03-29 18:40:59 +02:00
MichaelDvP
07b438f6f0 add NTP status to mqtt/ha (heartbeat) and log 2022-03-29 18:40:32 +02:00
proddy
2bf7bf3071 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2022-03-29 18:38:25 +02:00
proddy
5096964825 put total at start of description #418 2022-03-29 18:38:23 +02:00
Proddy
b96c2a731c Merge pull request #425 from proddy/dev
more icons to dashboard
2022-03-29 18:33:58 +02:00
Proddy
4f1b768b6c Merge branch 'emsesp:dev' into dev 2022-03-29 18:33:17 +02:00
proddy
d3002ce415 show all mask icons 2022-03-29 18:32:47 +02:00
proddy
a5ba70a6d7 some cosmetic changes 2022-03-29 18:32:28 +02:00
proddy
4118a576de enum for dv flags 2022-03-29 18:15:55 +02:00
proddy
4bcbcf4e3a update test data 2022-03-29 18:15:38 +02:00
proddy
9775cc796a update packages 2022-03-29 18:15:29 +02:00
Proddy
d269029bb1 Merge pull request #424 from MichaelDvP/dev
try to fix #408 and #412
2022-03-29 16:35:12 +02:00
MichaelDvP
786a94b448 try to fix #408 and #412 2022-03-29 15:30:39 +02:00
Proddy
80c87c8c43 Merge pull request #423 from MichaelDvP/dev
remove `(hidden)` text, force exclude from web icon
2022-03-29 12:16:16 +02:00
MichaelDvP
39d78fb444 remove (hidden) text, force exclude from web icon 2022-03-29 10:38:05 +02:00
Proddy
ac1de4c995 Merge pull request #422 from proddy/dev
Michael's PR merged (dv sort, remove id)
2022-03-29 09:06:53 +02:00
proddy
6f347bd49e Michael's PR merged (dv sort, remove id) 2022-03-29 09:06:09 +02:00
Proddy
cc31143a6c Merge pull request #421 from proddy/dev
fixes #411
2022-03-28 17:52:44 +02:00
proddy
86430c6408 updated packages 2022-03-28 17:52:07 +02:00
proddy
1cc031b27b minor optimizations 2022-03-28 17:50:51 +02:00
proddy
592c5ca778 fix devicevalue sorting the Michael way (thanks) 2022-03-28 17:50:33 +02:00
proddy
bad6346e7a update test data 2022-03-28 17:50:06 +02:00
proddy
cf10791c95 remove tooltip, add icons for fav and cmd 2022-03-28 17:49:57 +02:00
proddy
3f68c0001e formatting 2022-03-28 17:49:38 +02:00
Proddy
07ec5c3b12 Merge pull request #417 from MichaelDvP/dev
fix #412, daylightsaving with manual time setting
2022-03-28 10:38:53 +02:00
MichaelDvP
2d8f97ff35 fix #412, daylightsaving with manual time setting 2022-03-28 09:47:56 +02:00
Proddy
28ae1ae3d4 Merge pull request #416 from proddy/dev
Dev
2022-03-27 23:19:52 +02:00
Proddy
b9e1c0ba56 Merge branch 'emsesp:dev' into dev 2022-03-27 23:18:14 +02:00
proddy
317cc8eeee ignore lint error so build works 2022-03-27 23:16:12 +02:00
proddy
3ddee386b2 code optimization 2022-03-27 23:16:02 +02:00
Proddy
9f946f3514 Merge pull request #414 from proddy/dev
code optimizations
2022-03-27 22:48:27 +02:00
Proddy
904198013e Merge branch 'emsesp:dev' into dev 2022-03-27 22:47:51 +02:00
proddy
ac7c7cee84 code optimizations 2022-03-27 22:47:16 +02:00
Proddy
6c3d33d4a1 Merge pull request #413 from proddy/dev
rename exclude_entities to masked_entities - fixes for #411
2022-03-27 22:19:35 +02:00
proddy
3853c6ca18 added tooltip back to be consistent with other table actions 2022-03-27 22:14:20 +02:00
proddy
f3b7d33372 bump b10 2022-03-27 22:13:13 +02:00
proddy
7a67577aa1 added w (writeable) 2022-03-27 22:13:04 +02:00
proddy
fe3e02cb2e added tooltip 2022-03-27 22:12:49 +02:00
proddy
821d7845f0 updated b10 2022-03-27 22:12:21 +02:00
proddy
f7709e19fd added w (Writable) to generate_values_web and sort by favorite 2022-03-27 22:12:11 +02:00
proddy
fdfc5e0e68 include name of device in options 2022-03-27 19:57:11 +02:00
proddy
b331a07b69 add masks to customize screen 2022-03-27 19:56:54 +02:00
proddy
f5bf566e66 remove aria-label 2022-03-27 19:56:35 +02:00
proddy
f80d796333 use mask flags 2022-03-27 16:21:12 +02:00
proddy
afd55c52e5 formatting 2022-03-27 16:20:42 +02:00
proddy
b4cc300190 fix lint warning 2022-03-27 16:20:32 +02:00
proddy
03af305761 send state to WebUI (only the high nibble mask bits) 2022-03-27 16:20:19 +02:00
proddy
079f4e5ac0 replace exclude_entities with masked_entities 2022-03-27 16:19:55 +02:00
proddy
ff075a4f56 use m for mask instead of x 2022-03-27 16:18:56 +02:00
Proddy
b031e1a232 Merge pull request #409 from MichaelDvP:dev
Customizations to names with more options
2022-03-26 21:29:21 +00:00
proddy
175412774c pass cmd string as reference 2022-03-26 22:28:50 +01:00
proddy
d0efe8eb47 changes for standalone compilation 2022-03-26 22:17:25 +01:00
proddy
be0d5557a6 update packages 2022-03-26 22:17:10 +01:00
MichaelDvP
24f6fcd2d4 version b9, update changelog 2022-03-23 12:56:47 +01:00
MichaelDvP
9bef53c16c add #386 and #408, sync time with thermostat 2022-03-23 12:36:54 +01:00
MichaelDvP
9b70985d32 readonly to HA config 2022-03-23 12:22:49 +01:00
MichaelDvP
a0a3d8ef3a min/max to web value edit 2022-03-21 20:21:55 +01:00
MichaelDvP
4ae406b3e1 Customizations to names with more options 2022-03-21 19:24:11 +01:00
MichaelDvP
51f2009a2c npm update 2022-03-21 14:56:41 +01:00
MichaelDvP
8d172e0b57 set mode in advance after command, #395 2022-03-21 13:55:01 +01:00
MichaelDvP
4a132e769c fix publish reset 2022-03-21 13:50:54 +01:00
MichaelDvP
786110359a fix #339 maxJsonBuffrSize 2022-03-21 13:44:40 +01:00
MichaelDvP
3b5560b741 fix console show with empty full name 2022-03-21 13:40:54 +01:00
proddy
0351f4fbb3 update packages 2022-03-19 12:05:38 +01:00
Proddy
a695f85359 Merge pull request #401 from MichaelDvP/dev_
add RC35 values #398
2022-03-14 15:06:21 +01:00
MichaelDvP
d0aa601301 add RC35 values #398 2022-03-13 19:25:48 +01:00
proddy
508e98e3b6 update packages again 2022-03-12 17:40:59 +01:00
proddy
1227772696 remove comments 2022-03-12 17:40:51 +01:00
Proddy
0aa5c73635 Update README.md
added sonarcloud badge
2022-03-11 17:00:09 +01:00
Proddy
afda97870a Merge pull request #394 from MichaelDvP/dev
adds for #392 and #393
2022-03-11 16:57:02 +01:00
MichaelDvP
d56cdeec77 add burner stage 2 working time, #392 2022-03-11 13:25:18 +01:00
MichaelDvP
657914db26 add devices #393 2022-03-11 13:04:47 +01:00
MichaelDvP
0495aecf4c Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev_no_master_thermostat 2022-03-11 11:51:12 +01:00
Proddy
f539a236f1 Merge pull request #391 from MichaelDvP/dev
fix #390, fetching summer2_ids
2022-03-11 08:51:45 +01:00
MichaelDvP
52a9e500df fix #390, fetching summer2_ids 2022-03-11 07:20:52 +01:00
proddy
df0da84b9f updated packages again, why not 2022-03-10 09:53:56 +01:00
proddy
daaad1ce42 remove obsolete render function 2022-03-10 09:53:28 +01:00
Proddy
26b7075696 Merge pull request #389 from proddy/dev
update arduinojson 6.19.3
2022-03-09 09:15:03 +01:00
proddy
b526734e4b update arduinojson 6.19.3 2022-03-08 18:19:26 +01:00
Proddy
eaca7df527 Merge pull request #385 from MichaelDvP/dev
show correct hostname in startup message
2022-03-08 18:00:03 +01:00
MichaelDvP
1413bb7fbf fix typo in loading dallas names 2022-03-08 17:04:11 +01:00
MichaelDvP
5a09de002a show correct hostname in startup message 2022-03-08 12:02:18 +01:00
MichaelDvP
5634f46bd5 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev_no_master_thermostat 2022-03-08 10:53:13 +01:00
MichaelDvP
a2f8435204 fix mDNS on network reconnect 2022-03-08 07:07:52 +01:00
MichaelDvP
f076829b9b fix mDNS 2022-03-07 18:53:25 +01:00
Proddy
47aab98964 Merge pull request #384 from proddy/dev
update static code analysis from codacy to sonar #381
2022-03-07 12:50:42 +01:00
proddy
4f696a4947 bufsize const in messagetime() 2022-03-07 12:46:27 +01:00
proddy
828e769b3b add bufsize to messagetime() 2022-03-07 12:30:50 +01:00
proddy
69dc26005a fix strcpy length 2022-03-06 11:33:59 +01:00
MichaelDvP
6bbe2687ef autoformatting, check HS circuits 2022-03-06 10:41:04 +01:00
proddy
f19bf17d21 3.4.0b8 2022-03-05 17:27:22 +01:00
proddy
d09e2237ee sonar recommendations 2022-03-05 16:21:00 +01:00
proddy
10830dee36 update build dir 2022-03-05 14:51:18 +01:00
proddy
5f7f670517 add GH action 2022-03-05 14:40:29 +01:00
proddy
c1edbbf047 sonar fixes 2022-03-05 14:25:35 +01:00
MichaelDvP
7168f6c75e version b8, package update 2022-03-01 17:31:30 +01:00
MichaelDvP
57582aa90d add boiler_data_ww 2022-03-01 14:47:57 +01:00
MichaelDvP
732dced999 resort tags, boiler ww extra 2022-02-28 16:13:32 +01:00
MichaelDvP
eb9df59f15 remove master_thermostat 2022-02-28 12:44:29 +01:00
Proddy
e247b9e5f9 Merge pull request #379 from MichaelDvP:dev_
climate hidden, customization of hidden and commands, cleanup
2022-02-27 14:24:27 +01:00
MichaelDvP
c1fd964344 climate hidden, customization of hidden and commands, cleanup 2022-02-27 13:10:53 +01:00
proddy
f206ce7114 auto formatting 2022-02-26 18:12:42 +01:00
proddy
7438385729 upgrade packages 2022-02-26 18:12:33 +01:00
Proddy
142ca1e32c Merge pull request #378 from MichaelDvP:dev_
check selTemp, roomTemp visibility for climate creation
2022-02-26 17:58:53 +01:00
MichaelDvP
cde761fadd check selTemp, roomTemp visibility for climate creation 2022-02-26 17:15:42 +01:00
Proddy
16eb3f021d Merge pull request #375 from MichaelDvP/dev_
move HA climate to mqtt
2022-02-26 15:55:38 +01:00
MichaelDvP
9c15ddf952 HA climate as extra value, can be disabled 2022-02-26 14:23:45 +01:00
MichaelDvP
24216d7b4f move HA climate to mqtt 2022-02-23 10:29:56 +01:00
MichaelDvP
833ddf15d5 Merge pull request #369 from proddy/dev
optimized create/remove mqtt discovery topics
2022-02-22 08:27:17 +01:00
proddy
63a94dcef1 fix HA climate 2022-02-21 10:29:10 +01:00
Proddy
807bf4c061 Merge pull request #370 from MichaelDvP/dev_
add ww to thermostat single publish
2022-02-20 20:36:12 +01:00
MichaelDvP
a4ba130e5f add ww to thermostat single publish 2022-02-20 16:33:17 +01:00
proddy
d06145bb3a formatting 2022-02-20 13:41:12 +01:00
proddy
935e04b266 fixes Excluding thermostat entities should also remove the climate discovery topic #368 2022-02-20 13:40:58 +01:00
proddy
35fec3150b remove obsolete create_device_config and icons 2022-02-20 13:39:36 +01:00
proddy
493238e696 bump 3.4.0b7 2022-02-20 13:38:59 +01:00
proddy
cffc44b471 formatting 2022-02-20 13:38:42 +01:00
proddy
fcab3b1d46 update packages 2022-02-20 13:38:28 +01:00
proddy
1badaa725e update test for ha 2022-02-20 13:38:18 +01:00
Proddy
76579c426e Merge pull request #367 from MichaelDvP/dev_
add missing subscriptions to dallas/analogsensor
2022-02-19 15:15:25 +01:00
MichaelDvP
5f69395522 add missing subscriptions to dallas/analog 2022-02-19 12:25:01 +01:00
Proddy
9ceec6e306 Merge pull request #365 from MichaelDvP/dev_
publish all mixers to one json
2022-02-19 11:02:27 +01:00
MichaelDvP
a67913d660 publish all mixers to one json 2022-02-19 10:54:27 +01:00
Proddy
7da6b1925d Merge pull request #364 from proddy:dev
fix static code analysis warnings
2022-02-18 21:03:15 +01:00
proddy
fb05558ab9 fix static analysis warnings 2022-02-18 20:28:15 +01:00
Proddy
33835fef7b Merge pull request #362 from MichaelDvP/dev_mqtt
Fahrenheit for HA-climate min/max
2022-02-17 10:47:20 +01:00
proddy
f1f089baa0 v is always <= 255 2022-02-17 10:46:21 +01:00
proddy
d8f32d6ade hard code 128 as topic length to prevent compiler warnings 2022-02-17 10:46:02 +01:00
proddy
ca04ebccd2 formatting warning 2022-02-17 10:45:44 +01:00
proddy
d11a67527f make standalone build compile again 2022-02-17 10:45:29 +01:00
proddy
625fb41352 update packages to fix build 2022-02-17 10:44:56 +01:00
MichaelDvP
e0e90e3cab Fahrenheit for HA-climate min/max 2022-02-17 10:38:22 +01:00
Proddy
4c4a6f668d Merge pull request #361 from MichaelDvP/dev_mqtt
mqtt, analog, fahrenheit and other changes
2022-02-17 10:04:25 +01:00
MichaelDvP
b7d8447f73 version 3.4.0b6 2022-02-17 09:17:17 +01:00
MichaelDvP
18651bdaf4 changelog, typo 2022-02-16 21:00:57 +01:00
MichaelDvP
9046a6578a telegram read length depends on ems/ems+ 2022-02-16 19:37:14 +01:00
MichaelDvP
4219842088 fetch devices one by one 2022-02-16 19:36:17 +01:00
MichaelDvP
3b41d6fff6 fahrenheit uom 2022-02-16 19:14:15 +01:00
MichaelDvP
b2eaca27de remove unused flag 2022-02-16 18:58:45 +01:00
MichaelDvP
9ccb04489b log detection of devcies without values 2022-02-16 18:58:15 +01:00
MichaelDvP
6b164b5487 burner up to 130% 2022-02-16 18:57:43 +01:00
MichaelDvP
073493cba2 analogsensors, outputs PWM, DAC, digital 2022-02-16 18:35:25 +01:00
MichaelDvP
7f5e0f7244 Mqtt: remove all HA if not active, timeout QoS, option single2cmd 2022-02-16 17:59:53 +01:00
MichaelDvP
7bb6f55153 solar fix SM10 energy, remove unknowns 2022-02-16 17:02:54 +01:00
MichaelDvP
7f21bea8a6 Thermostat: RC20 temperatures, RC300 roominflfactor, fetch monitor 2022-02-16 15:47:49 +01:00
Proddy
802e7a080f Merge branch 'dev' into dev 2022-02-13 22:31:09 +01:00
proddy
a7c1a75996 added serial debug flag 2022-02-13 21:44:36 +01:00
proddy
70803b1a6d text change 2022-02-13 21:44:25 +01:00
proddy
005b9a88c3 update packages 2022-02-13 21:44:16 +01:00
proddy
7214b5beea fixes Excluding entities from the Customization page does not remove the MQTT Discovery entry #357 2022-02-13 21:44:02 +01:00
Proddy
e2163546fe Merge branch 'emsesp:dev' into dev 2022-02-13 13:01:18 +01:00
proddy
17bc9c231a dropzone minor upgrade 2022-02-13 09:59:39 +01:00
proddy
0b4e9da5d5 fix mqtt nested thermostat data 2022-02-13 09:59:20 +01:00
Proddy
002fa82afb Merge branch 'emsesp:dev' into dev 2022-02-12 17:38:38 +01:00
proddy
482baef360 update with latest fixes 2022-02-12 15:08:03 +01:00
proddy
20b876bdd6 fixes #354 2022-02-12 15:05:22 +01:00
proddy
d00ac1fa86 formatting 2022-02-12 15:04:37 +01:00
proddy
02e5b6e975 extend test for nested mqtt 2022-02-12 15:04:06 +01:00
proddy
1f17eda56f update packages 2022-02-12 15:03:45 +01:00
proddy
718f9a4f11 fixes #314 2022-02-12 15:03:31 +01:00
Proddy
668276e7d0 Merge branch 'emsesp:dev' into dev 2022-02-10 22:10:40 +01:00
proddy
3de769c7e7 add comment 2022-02-07 09:02:55 +01:00
proddy
984bbd493d move common time functions 2022-02-07 09:02:48 +01:00
proddy
74eabba641 remove uptime. we show it in 2 places already in the app 2022-02-07 09:02:17 +01:00
proddy
81d54ca69f tidy up sequence of services, start log and serial console first, catch any board profile errors first 2022-02-06 15:48:57 +01:00
proddy
ddee63b718 fix osx firmware upload - #345 2022-02-06 15:47:20 +01:00
Proddy
99743e8e0f Merge pull request #352 from MichaelDvP/dev_
fix telegram_last dest read flag
2022-02-06 12:19:12 +01:00
MichaelDvP
7d11539827 fix telegram_last dest read flag 2022-02-06 10:44:47 +01:00
proddy
7079098810 remove txmode off as option 2022-02-05 13:15:27 +01:00
proddy
e12ac26406 bump to b3 2022-02-05 13:12:03 +01:00
proddy
64e755bc15 update packages 2022-02-05 13:11:58 +01:00
proddy
a65b6bf19d fix compile warning 2022-02-05 13:11:42 +01:00
Proddy
5143040ba9 Merge pull request #332 from MichaelDvP/dev_
fix #329, numberformat in Edge, Chrome
2022-02-05 12:51:14 +01:00
Proddy
01c75ad5c0 Merge pull request #348 from MichaelDvP/dev_327
fix #327, analogsensor sending multiple mqtt messages
2022-02-05 12:50:43 +01:00
Proddy
43d0197a60 Merge pull request #347 from MichaelDvP/dev_340
fix #340, refresh only device/sensor-data if open
2022-02-05 12:48:18 +01:00
Proddy
35013a71d4 Merge pull request #346 from MichaelDvP/dev_336
fix #336, map Junkers hc3/4 to masterthermostat
2022-02-05 12:47:54 +01:00
Proddy
381acbf00f Merge pull request #323 from MichaelDvP/dev
always show RC300 tempautotemp with minimum to -1°C #321
2022-02-05 12:32:52 +01:00
proddy
47fb13aa4a allow larger exclude lists 2022-02-05 09:51:34 +01:00
proddy
f5aca6aa93 fix test compiling 2022-02-05 09:51:16 +01:00
MichaelDvP
95f4670b47 use numberValue for input to make all number inputs uniform 2022-01-31 17:00:11 +01:00
MichaelDvP
acc2412742 fix #327, analogsensor sending multiple mqtt messages 2022-01-31 13:39:30 +01:00
MichaelDvP
77df8cc69b fix #340, refresh only device/sensor-data if open 2022-01-31 10:23:10 +01:00
MichaelDvP
eaf651a4e2 fix #336, map Junkers hc3/4 to masterthermostat 2022-01-31 10:12:50 +01:00
proddy
3d1a050e22 HA roomtemp optional - #325 2022-01-26 23:18:36 +01:00
MichaelDvP
2db053ab04 fix #329, numberformat in Edge, Chrome 2022-01-26 21:32:45 +01:00
Proddy
915418eca8 Merge pull request #330 from MichaelDvP/dev_
fixes #327 #329
2022-01-26 20:27:02 +01:00
MichaelDvP
508a707c6e add steps to web input of numbers, #329 2022-01-26 18:35:55 +01:00
MichaelDvP
233d82805b add analog counter command, fix #327 "value" 2022-01-26 18:35:12 +01:00
Proddy
8c4e5e5185 Merge pull request #324 from MichaelDvP/dev_ 2022-01-25 13:24:29 +01:00
MichaelDvP
d3ca556914 fix #300 moduline 300 seltemp, next try 2022-01-25 12:26:55 +01:00
MichaelDvP
325a92f40d always show RC300 tempautotemp with minimum to -1°C #321 2022-01-25 08:08:46 +01:00
proddy
a89481cf14 bump mui libs 2022-01-24 20:59:11 +01:00
proddy
65be8ba6f7 auto formatting (npm run format) 2022-01-24 20:58:54 +01:00
proddy
a2b9b9e0c8 update with latest system/info changes 2022-01-24 20:58:35 +01:00
proddy
20bd5327f8 bump 3.4.0b1 2022-01-24 20:58:21 +01:00
proddy
4ac045afcf Please ad "ENTITIES" like on dashboard within api call ems/api/system per device #322 2022-01-24 20:58:10 +01:00
Proddy
4569ee50a5 Merge pull request #320 from MichaelDvP/dev
helptext for string commands
2022-01-24 20:31:13 +01:00
MichaelDvP
cd2ea1d5fc helptext for string commands 2022-01-24 18:02:57 +01:00
Proddy
ab34f7c056 Merge pull request #319 from MichaelDvP/dev 2022-01-24 13:30:09 +01:00
MichaelDvP
a27a5ebf4c sync DE-string (not completly translated) 2022-01-24 13:14:28 +01:00
MichaelDvP
be20fcf021 remove unused strings 2022-01-24 13:06:07 +01:00
MichaelDvP
f62317a338 system info: lower case and underscores 2022-01-24 12:54:41 +01:00
MichaelDvP
d27243eb34 rename Wired->Ethernet 2022-01-24 12:51:13 +01:00
MichaelDvP
8f5e26acd1 fix read command with length 2022-01-24 12:50:27 +01:00
MichaelDvP
e02f20d74e fix refresh sensorData 2022-01-24 12:49:57 +01:00
MichaelDvP
b0111d6653 formatting 2022-01-24 12:49:16 +01:00
proddy
a38d8c14fa Function parameter 'sensor' should be passed by const reference 2022-01-23 18:08:52 +01:00
proddy
77e1898512 Merge remote-tracking branch 'origin/v3.4' into dev 2022-01-23 17:56:52 +01:00
proddy
29110e96e5 Merge remote-tracking branch 'origin/dev' 2022-01-20 10:51:40 +01:00
Proddy
02e2b51814 Merge pull request #307 from MichaelDvP/dev
check received status before toggling fetch on empty telegram
2022-01-20 10:04:03 +01:00
MichaelDvP
e9588cc7a1 check received status before toggling fetch on empty telegram 2022-01-20 08:41:41 +01:00
Proddy
3d8e1b8f86 Merge pull request #245 from proddy:dev
Add back RC30 - #243
2021-12-14 20:23:34 +00:00
proddy
312f969364 add back RC30 - Setting mode on a RC30/Moduline400 via MQTT in HA doesn't work #243 2021-12-14 21:19:54 +01:00
Proddy
0f41079803 Merge pull request #235 from pswid:dev
fix overlapping 0xC2 and overflow of offset
2021-12-14 19:49:37 +00:00
pswid
7f30e8dadc fix overlaping while reading sequence of EMS1.0 telegrams 2021-12-14 10:32:31 +01:00
pswid
9cd20cfc05 fix overlaping while reading sequence of EMS1.0 telegrams 2021-12-14 10:29:15 +01:00
pswid
1343bbf6ea fix overlapping 0xC2 and overflow of offset 2021-12-03 11:50:52 +01:00
Proddy
90c39d1f85 Merge pull request #234 from pswid/dev 2021-12-03 09:38:40 +01:00
pswid
92da61376b Update boiler.cpp 2021-12-03 09:04:39 +01:00
pswid
69976c2caf Update CHANGELOG_LATEST.md 2021-12-03 09:02:36 +01:00
pswid
3c6fd0c83a Update boiler.cpp 2021-12-03 08:58:19 +01:00
proddy
b0a09747d4 3.3.1b0 2021-11-28 23:06:55 +01:00
proddy
b65866217a 3.4.0 2021-11-28 23:03:28 +01:00
proddy
611e3b1243 Merge remote-tracking branch 'origin/dev' 2021-11-28 23:03:15 +01:00
Proddy
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
591 changed files with 45145 additions and 54725 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

@@ -29,7 +29,7 @@ assignees: ''
*If applicable, add screenshots to help explain your problem.*
**Device information**
*Copy-paste here the information as it is outputted by the device. You can get this information by from http://ems-esp.local/api?device=system&cmd=info*
*Copy-paste here the information as it is outputted by the device. You can get this information by from http://ems-esp.local/api/system*
**Additional context**
*Add any other context about the problem here.*

View File

@@ -23,7 +23,7 @@ assignees: ''
*If applicable, add screenshots to help explain your problem.*
**Device information**
*Copy-paste here the information as it is outputted by the device. You can get this information from http://ems-esp.local/api?device=system&cmd=info*
*Copy-paste here the information as it is outputted by the device. You can get this information from http://ems-esp.local/api/system*
**Additional context**
*Add any other context about the problem here.*

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: |

57
.github/workflows/sonar_check.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: Sonar Check
on:
push:
branches:
- dev
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build
runs-on: ubuntu-latest
if: github.repository_owner == 'emsesp'
# if: github.repository == 'emsesp/EMS-ESP32'
env:
# https://binaries.sonarsource.com/?prefix=Distribution/sonar-scanner-cli/
SONAR_SCANNER_VERSION: 4.7.0.2747
SONAR_SERVER_URL: "https://sonarcloud.io"
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Cache SonarCloud packages
uses: actions/cache@v1
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Download and set up sonar-scanner
env:
SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip
run: |
mkdir -p $HOME/.sonar
curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }}
unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH
- name: Download and set up build-wrapper
env:
BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip
run: |
curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }}
unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/
echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH
- name: Run build-wrapper
run: |
make clean
build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} make clean all
- name: Run sonar-scanner
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
sonar-scanner

7
.gitignore vendored
View File

@@ -28,3 +28,10 @@ emsesp
node_modules
/interface/.eslintcache
test.sh
scripts/__pycache__
.temp
# sonar
.scannerwork/
sonar/
build_wrapper_output_directory/

View File

@@ -5,6 +5,203 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# [3.4.2]
## Added
- RC310 additions [#520](https://github.com/emsesp/EMS-ESP32/pull/520)
- damping
- wwprio for RC310 heating circuits
- switchonoptimization for RC310 heating circuits
- enum_controlmode for RC310 (new enum list)
- nofrostmode, reducemode, reducetemp & noreducetemp for RC310
- emergencyops and emergencytemp, wwmaxtemp, wwflowtempoffset and wwcomfort1 for RC310
- HM200 hybrid module [#500](https://github.com/emsesp/EMS-ESP32/issues/500)
- AM200 alternative heatsource module [#573](https://github.com/emsesp/EMS-ESP32/issues/573)
- EM10 error module as gateway [#575](https://github.com/emsesp/EMS-ESP32/issues/575)
## Fixed
- fix Table resizing in WebUI [#519](https://github.com/emsesp/EMS-ESP32/issues/519)
- allow larger customization files [#570](https://github.com/emsesp/EMS-ESP32/issues/570)
- losing entitiy wwcomfort [#581](https://github.com/emsesp/EMS-ESP32/issues/581)
## Changed
- Shorten "friendly names" in Home Assistant [#555](https://github.com/emsesp/EMS-ESP32/issues/555)
- platformio 2.3.0 (IDF 4, Arduino 2)
- remove master-thermostat, support multiple thermostats
- merge up- and download in webui [#577](https://github.com/emsesp/EMS-ESP32/issues/577)
# [3.4.1] May 29 2022
## Fixed
- Fix memory leak in api [#524](https://github.com/emsesp/EMS-ESP32/issues/524)
## Changed
# [3.4.0] May 23 2022
## Added
- WebUI optimizations, updated look&feel and better performance [#124](https://github.com/emsesp/EMS-ESP32/issues/124)
- Auto refresh of WebUI after successful firmware upload [#178](https://github.com/emsesp/EMS-ESP32/issues/178)
- New Customization Service in WebUI. First feature is the ability to enable/disabled Enitites (device values) from EMS devices [#206](https://github.com/emsesp/EMS-ESP32/issues/206)
- Option to disable Telnet Console [#209](https://github.com/emsesp/EMS-ESP32/issues/209)
- Added Hide SSID, Max Clients and Preferred Channel to Access Point
- Merged in MichaelDvP's changes like Fahrenheit conversion, publish single (for IOBroker) and a few other critical optimizations
- Enabled bi-directional read/write with Home Assistant, so values can be changed automatically from the UI without scripting [#265](https://github.com/emsesp/EMS-ESP32/issues/265)
- Added GC7000F Boiler [#270](https://github.com/emsesp/EMS-ESP32/issues/270)
- Revised LED flash sequence on boot up to show system health (1 flash=no ems, 2 flashes=no wifi) [#224](https://github.com/emsesp/EMS-ESP32/issues/224)
- Analog Sensor support [#271](https://github.com/emsesp/EMS-ESP32/issues/271)
- Solar cylinder priority [#247](https://github.com/emsesp/EMS-ESP32/issues/247)
- Read only mode in Settings, where EMS Tx/Write commands are blocked [#286](https://github.com/emsesp/EMS-ESP32/issues/286)
- Added 8700i Boiler device
- Added Cascade CM10 Controller device
- Add Olimex ESP32-POE-ISO to board profiles plus settings to customize Ethernet modules [#301](https://github.com/emsesp/EMS-ESP32/issues/301)
- Help text for string commands in WebUI [#320](https://github.com/emsesp/EMS-ESP32/issues/320)
- Germany translations (at compile time)
- #entities added to system/info` endpoint [#322](https://github.com/emsesp/EMS-ESP32/issues/322)
- analog outputs digital/pwm/dac
- remove MQTT retained configs if discovery is disabled
- timeout 10 min for MQTT-QoS wait
- Moduline 300 auto-temperatures T1-T4, RC300 romminfluencefactor
- RC35 parameters [#392](https://github.com/emsesp/EMS-ESP32/issues/392), [#398](https://github.com/emsesp/EMS-ESP32/issues/398)
- sync time with thermostat [#386](https://github.com/emsesp/EMS-ESP32/issues/386), [#408](https://github.com/emsesp/EMS-ESP32/issues/408)
- set mode has immediate effect [#395](https://github.com/emsesp/EMS-ESP32/issues/395)
- min/max in web value setting
- Extend customization to select if an entity is to be shown in the WebUI or forced as read-only [#317](https://github.com/emsesp/EMS-ESP32/issues/317)
- Added Moduline 400 installation parameters [PR #449 by @kwertie01](https://github.com/emsesp/EMS-ESP32/pull/449)
- Read time from IVT-controller [#439](https://github.com/emsesp/EMS-ESP32/issues/439)
- Hybrid Heatpump product-id 168 [#459](https://github.com/emsesp/EMS-ESP32/issues/459), thermostat settings
- Junkers ISM2 and IPM in warm water mode [#437](https://github.com/emsesp/EMS-ESP32/issues/437)
- Added Shower Alert trigger time and cold shot time [#436](https://github.com/emsesp/EMS-ESP32/issues/436)
- Improved Table layout in Web UI (searching, filtering, sorting, exporting to CSV)
- API fetch individual attributes from an entity [#462](https://github.com/emsesp/EMS-ESP32/issues/462)
- Option to disable mDNS
- Option for rendering booleans on dashboard [#456](https://github.com/emsesp/EMS-ESP32/issues/456)
- Upload customization settings from a file [#256](https://github.com/emsesp/EMS-ESP32/issues/256)
## Fixed
- lastcode broke MQTT JSON structure [#228](https://github.com/emsesp/EMS-ESP32/issues/228)
- fixed issue with overlapping while reading sequence of EMS1.0 telegrams
- fixed redundant telegram readings (because of offset overflow)
- added missing RC30/Moduline 400 [#243](https://github.com/emsesp/EMS-ESP32/issues/243)
- Correct modes for RC25 [#106](https://github.com/emsesp/EMS-ESP32/issues/106)
- Clean up old HA config's in MQTT before publishing data. This will prevent HA giving the 'dict' warnings [#229](https://github.com/emsesp/EMS-ESP32/issues/229)
- RC25 temperature setting [#272](https://github.com/emsesp/EMS-ESP32/issues/272)
- Buderus RC25 - "hc1 mode type" incorrect value [#273](https://github.com/emsesp/EMS-ESP32/issues/273)
- Increased number of Mixers and Heating Circuits [#294](https://github.com/emsesp/EMS-ESP32/issues/294)
- Check receive status before removing a telegram fetch [#268](https://github.com/emsesp/EMS-ESP32/issues/268), [#282](https://github.com/emsesp/EMS-ESP32/issues/282)
- Fix uploading firmware on OSX [#345](https://github.com/emsesp/EMS-ESP32/issues/345)
- Non-nested MQTT would corrupt the json [#354](https://github.com/emsesp/EMS-ESP32/issues/354)
- Burner selected max power can have a value higher than 100% [#314](https://github.com/emsesp/EMS-ESP32/issues/314)
- some missing fahrenheit calculations
- limited number of exclusions [#339](https://github.com/emsesp/EMS-ESP32/issues/339)
- MQTT sometimes would not reconnect after a WiFi outage
## Changed
- Use flash system to show system health (1 flash=no ems, 2 flashes=no wifi) [#224](https://github.com/emsesp/EMS-ESP32/issues/224)
- Renamed Dallas Sensor to Temperature Sensor in UI
- Dallas Format removed. Use the name to give each sensor an alias
- No longer MQTT subscribes to topic `/thermostat_hc<n>` as it supports a path similar to the API endpoint construct
- Show Sensors quality in WebUI
- Controller not shown in WebUI dashboard
- renamed "Home Assistant Integration" to "MQTT Discovery" in MQTT Settings [#290](https://github.com/emsesp/EMS-ESP32/issues/290)
- Show ems tx reads and writes separately
- Show ems device handlers separated for received, fetched and pending handlers.
- Wired renamed to Ethernet
- removed system/pin command, new commands in analogsensors
- system/info device-info split to name/version/brand
- exclude list uses short-names, possible flags for web/api/mqtt excludes, readonly and favorite (selection not yet implemented)
- thermostat clock formate date-time: dd.mm.yyyy hh:mm
- RC300 summermode as other thermostats `winter/summer` instead of `off/on`
## **BREAKING CHANGES:**
- Settings:
- order of Boolean Format has changed in Application Settings - check your settings
- Dallas Format setting removed. Now customize name of each Dallas sensor via the UI
- MQTT/API
- Boiler `wwheat` renamed to `ww3wayon` [#211](https://github.com/emsesp/EMS-ESP32/issues/211)
- Boiler `ww` tag renamed to `dhw`. Any custom Home Assistant lovelace dashboards will need updating.
- Renamed description of `wwtapactivated` to "turn on/off DHW". Otherwise would have looked like "boiler_dhw_turn_on_off_dhw" in HA.
- `/api/system/info` endpoint has updated keys. Now lowercase, no underscores and not capitalized. Replace "handlers" with "handlers received", "handlers fetched" and "handlers pending".
# [3.3.1] January 20 2022
- lastcode broke MQTT JSON structure [#228](https://github.com/emsesp/EMS-ESP32/issues/228)
- overlapping while reading sequence of EMS1.0 telegrams
- redundant telegram readings (because of offset overflow)
- added missing RC30/Moduline400 [#243](https://github.com/emsesp/EMS-ESP32/issues/243)
- check received status before toggling fetch on empty telegram [#268][#282]
# [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
@@ -62,7 +259,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
@@ -84,7 +281,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
@@ -110,51 +307,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Added
- power settings, disabling BLE and turning off Wifi sleep
- Rx and Tx counts to Heartbeat MQTT payload
- ethernet support
- id to info command to show only a heatingcircuit
- add sending devices that are not listed to 0x07
- extra MQTT boolean option for "ON" and "OFF"
- support for chunked MQTT payloads to allow large data sets > 2kb
- external Button support (#708) for resetting to factory defaults and other actions
- new console set command in `system`, `set board_profile <profile>` for quickly enabling cabled ethernet connections without using the captive wifi portal
- added in MQTT nested mode, for thermostat and mixer, like we had back in v2
- cascade MC400 (product-id 210) (3.0.0b6), power values for heating sources (3.0.1b1)
- values for wwMaxPower, wwFlowtempOffset
- RC300 `thermostat temp -1` to clear temporary setpoint in auto mode
- syslog port selectable (#744)
- individual mqtt commands (#31)
- board Profiles (#11)
## Fixed
- telegrams matched to masterthermostat 0x18
- multiple roomcontrollers
- readback after write with delay (give ems-devices time to set the value)
- thermostat ES72/RC20 device 66 to command-set RC20_2
- MQTT payloads not adding to queue when MQTT is re-connecting (fixes #369)
- fix for HA topics with invalid command formats (#728)
- wrong position of values #723, #732
- OTA Upload via Web on OSX
- Rx and Tx quality % would sometimes show > 100
## Changed
- changed how telegram parameters are rendered for mqtt, console and web (#632)
- split `show values` in smaller packages (edited)
- extended length of IP/hostname from 32 to 48 chars (#676)
- check flowsensor for `tap_water_active`
- mqtt prefixed with `Base`
- count Dallas sensor fails
- switch from SPIFFS to LITTLEFS
- added ID to MQTT payloads which is the Device's product ID and used in HA to identify a unique HA device
- increased MQTT buffer and reduced wait time between publishes
- updated to the latest ArduinoJson library
- some names of mqtt-tags like in v2.2.1
- new ESP32 partition side to allow for smoother OTA and fallback
- network Gateway IP is optional (#682)emsesp/EMS-ESP
- moved to a new GitHub repo https://github.com/emsesp/EMS-ESP32
- invert LED changed to Hide LED. Default is off.
- renamed Scan Network to Scan WiFi Network
- added version to cmd=settings

View File

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

View File

@@ -1,5 +1,3 @@
<img src="/media/EMS-ESP_logo_dark.png" alt="Logo" align="right" height="76"/>
# Contributing
**Any contribution helps EMS-ESP get better for the entire community!**
@@ -94,7 +92,7 @@ References:
- <https://www.conventionalcommits.org/>
--------------------------------------
---
## Contributor License Agreement (CLA)
@@ -133,9 +131,9 @@ A CLA is a legal document in which you state _you are entitled to contribute the
CLA is a safety because it also ensures that once you have provided a contribution, you cannot try to withdraw permission for its use at a later date. People can therefore use that software, confident that they will not be asked to stop using pieces of the code at a later date.
A __license__ grants "outbound" rights to the user of project.
A **license** grants "outbound" rights to the user of project.
A __CLA__ enables a contributor to grant "inbound" rights to a project.
A **CLA** enables a contributor to grant "inbound" rights to a project.
<Other>
<A table should be maintained for relating maintainers and components. When triaging, this is essential to figure out if someone in particular should be consulted about specific changes.>

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 += -DFACTORY_WIFI_HOSTNAME=\"ems-esp\" -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_USE_SERIAL -DEMSESP_DEFAULT_BOARD_PROFILE=\"LOLIN\"
#----------------------------------------------------------------------
# Sources & Files
@@ -73,8 +73,8 @@ CPPFLAGS += -Os
CFLAGS += $(CPPFLAGS)
CFLAGS += -Wall
CFLAGS += -Wno-unused -Wno-restrict
CFLAGS += -Wextra
CFLAGS += -Wno-unused-parameter
CXXFLAGS += $(CFLAGS) -MMD
@@ -113,6 +113,7 @@ COMPILE.cpp = $(CXX) $(CXX_STANDARD) $(CXXFLAGS) $(DEPFLAGS) -c $< -o $@
#----------------------------------------------------------------------
# Targets
#----------------------------------------------------------------------
.PHONY: all
all: $(OUTPUT)
$(OUTPUT): $(OBJS)
@@ -138,8 +139,9 @@ cppcheck: $(SOURCES)
run: $(OUTPUT)
@$<
.PHONY: clean
clean:
@$(RM) -r $(BUILD) $(OUTPUT)
@$(RM) -rf $(BUILD) $(OUTPUT)
help:
@echo available targets: all run clean

View File

@@ -15,6 +15,7 @@ This project is the specifically for the ESP32. Compared with the previous ESP82
[![version](https://img.shields.io/github/release/emsesp/EMS-ESP32.svg?label=Latest%20Release)](https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md)
[![release-date](https://img.shields.io/github/release-date/emsesp/EMS-ESP32.svg?label=Released)](https://github.com/emsesp/EMS-ESP32/commits/main)
[![license](https://img.shields.io/github/license/emsesp/EMS-ESP32.svg)](LICENSE)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=emsesp_EMS-ESP32&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=emsesp_EMS-ESP32)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9441142f49424ef891e8f5251866ee6b)](https://www.codacy.com/gh/emsesp/EMS-ESP32/dashboard?utm_source=github.com&utm_medium=referral&utm_content=emsesp/EMS-ESP32&utm_campaign=Badge_Grade)
[![downloads](https://img.shields.io/github/downloads/emsesp/EMS-ESP32/total.svg)](https://github.com/emsesp/EMS-ESP32/releases)
[![chat](https://img.shields.io/discord/816637840644505620.svg?style=flat-square&color=blueviolet)](https://discord.gg/3J3GgnzpyT)
@@ -33,16 +34,16 @@ Note, EMS-ESP requires a small hardware circuit that can convert the EMS bus dat
# **Features**
- A multi-user secure web interface to change settings and monitor the data
- A console, accessible via Serial and Telnet for more monitoring
- Native support for Home Assistant via [MQTT Discovery](https://www.home-assistant.io/docs/mqtt/discovery/)
- A multi-user secure web interface to change settings and monitor incoming data
- A console, accessible via Serial and Telnet for more advanced monitoring
- Native support for Home Assistant and Domoticz via [MQTT Discovery](https://www.home-assistant.io/docs/mqtt/discovery/)
- Can run standalone as an independent WiFi Access Point or join an existing WiFi network
- Easy first-time configuration via a web Captive Portal
- Support for more than [80 EMS devices](https://emsesp.github.io/docs/#/Supported-EMS-Devices) (boilers, thermostats, solar modules, mixer modules, heat pumps, gateways)
- Support for more than [100 EMS devices](https://emsesp.github.io/docs/#/Supported-EMS-Devices) (boilers, thermostats, solar modules, mixer modules, heat pumps, gateways)
## **Demo**
See a live demo [here](https://ems-esp.derbyshire.nl) using fake data. Log in with any username/password.
See a demo [here](https://ems-esp.derbyshire.nl). Log in with any username/password.
# **Screenshots**

View File

@@ -3,4 +3,3 @@
# Firmware Installation
Follow the instructions in the [documentation](https://emsesp.github.io/docs) on how to install the firmware binaries in the Assets below.

View File

@@ -5,4 +5,3 @@ This is a snapshot of the current "beta" development code and firmware binaries
# Firmware Installation
Follow the instructions in the [documentation](https://emsesp.github.io/docs) on how to install the firmware binaries in the Assets below.

View File

@@ -38,7 +38,7 @@ build_flags =
-D FACTORY_MQTT_PASSWORD=\"\"
-D FACTORY_MQTT_CLIENT_ID=\"ems-esp\"
-D FACTORY_MQTT_KEEP_ALIVE=60
-D FACTORY_MQTT_CLEAN_SESSION=true
-D FACTORY_MQTT_CLEAN_SESSION=false
-D FACTORY_MQTT_MAX_TOPIC_LENGTH=128
; JWT Secret

View File

@@ -1,3 +1,6 @@
# This enables lint extensions
EXTEND_ESLINT=true
# This is the name of your project. It appears on the sign-in page and in the menu bar.
REACT_APP_PROJECT_NAME=EMS-ESP

View File

@@ -1,5 +0,0 @@
# Change the IP address to that of your ESP device to enable local development of the UI
# REACT_APP_HTTP_ROOT=http://localhost:3000
# REACT_APP_WEB_SOCKET_ROOT=ws://localhost:3000

View File

@@ -1,2 +0,0 @@
# don't ever lint node_modules
node_modules

View File

@@ -1,27 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"prettier"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
// 0 = ignore, 1 = warning, 2 = error
"no-console": 0,
"prettier/prettier": ["error", { endOfLine: "auto" }],
"explicit-function-return-type": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/ban-types": 0,
"@typescript-eslint/no-non-null-asserted-optional-chain": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-explicit-any": 0
}
}

View File

@@ -2,5 +2,5 @@
"singleQuote": true,
"semi": true,
"trailingComma": "none",
"printWidth": 80
"printWidth": 120
}

View File

@@ -1,52 +1,30 @@
const ManifestPlugin = require('webpack-manifest-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const ProgmemGenerator = require('./progmem-generator.js');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = function override(config, env) {
const hosted = process.env.REACT_APP_HOSTED;
if (env === 'production' && !hosted) {
console.log('Custom webpack...');
// rename the output file, we need it's path to be short for LittleFS
// rename the ouput file, we need it's path to be short, for embedded FS
config.output.filename = 'js/[id].[chunkhash:4].js';
config.output.chunkFilename = 'js/[id].[chunkhash:4].js';
// take out the manifest and service worker plugins
config.plugins = config.plugins.filter(
(plugin) => !(plugin instanceof ManifestPlugin)
);
config.plugins = config.plugins.filter(
(plugin) => !(plugin instanceof WorkboxWebpackPlugin.GenerateSW)
);
// take out the manifest plugin
config.plugins = config.plugins.filter((plugin) => !(plugin instanceof WebpackManifestPlugin));
// shorten css filenames
const miniCssExtractPlugin = config.plugins.find(
(plugin) => plugin instanceof MiniCssExtractPlugin
);
const miniCssExtractPlugin = config.plugins.find((plugin) => plugin instanceof MiniCssExtractPlugin);
miniCssExtractPlugin.options.filename = 'css/[id].[contenthash:4].css';
miniCssExtractPlugin.options.chunkFilename =
'css/[id].[contenthash:4].c.css';
miniCssExtractPlugin.options.chunkFilename = 'css/[id].[contenthash:4].c.css';
// don't emit license file
const terserPlugin = config.optimization.minimizer.find((plugin) => plugin instanceof TerserPlugin);
terserPlugin.options.extractComments = false;
// build progmem data files
config.plugins.push(
new ProgmemGenerator({
outputPath: '../lib/framework/WWWData.h',
bytesPerLine: 20
})
);
// add compression plugin, compress javascript
config.plugins.push(
new CompressionPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js)$/,
deleteOriginalAssets: true
})
);
config.plugins.push(new ProgmemGenerator({ outputPath: '../lib/framework/WWWData.h', bytesPerLine: 20 }));
}
return config;
};

40999
interface/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +1,42 @@
{
"name": "emsesp-react",
"version": "0.1.0",
"name": "EMS-ESP",
"version": "3.4.0",
"private": true,
"proxy": "http://localhost:3080",
"dependencies": {
"@material-ui/core": "^4.11.4",
"@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/react-material-ui-form-validator": "^2.1.0",
"@types/react-router": "^5.1.13",
"@types/react-router-dom": "^5.1.7",
"compression-webpack-plugin": "^5.0.2",
"env-cmd": "^10.1.0",
"express": "^4.17.1",
"@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4",
"@msgpack/msgpack": "^2.8.0",
"@mui/icons-material": "^5.10.3",
"@mui/material": "^5.10.5",
"@table-library/react-table-library": "4.0.18",
"@types/lodash": "^4.14.185",
"@types/node": "^18.7.18",
"@types/react": "^18.0.20",
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
"async-validator": "^4.2.5",
"axios": "^0.27.2",
"http-proxy-middleware": "^2.0.6",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"mime-types": "^2.1.30",
"notistack": "^1.0.6",
"notistack": "^2.0.5",
"parse-ms": "^3.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-dropzone": "^11.3.2",
"react-form-validator-core": "^1.1.1",
"react-material-ui-form-validator": "^2.1.4",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react": "^18.2.0",
"react-app-rewired": "^2.2.1",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.2",
"react-icons": "^4.4.0",
"react-router-dom": "^6.4.0",
"react-scripts": "5.0.1",
"sockette": "^2.0.6",
"typescript": "4.2.4",
"zlib": "^1.0.5"
"typescript": "^4.8.3"
},
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject",
"format": "prettier --write '**/*.{ts,tsx,js,css,json,md}'",
"build-hosted": "env-cmd -f .env.hosted npm run build",
"build-localhost": "PUBLIC_URL=/ react-app-rewired build",
@@ -44,7 +45,44 @@
"lint": "eslint . --ext .ts,.tsx"
},
"eslintConfig": {
"extends": "react-app"
"extends": [
"react-app",
"react-app/jest"
],
"rules": {
"eol-last": 1,
"react/jsx-closing-bracket-location": 1,
"react/jsx-closing-tag-location": 1,
"react/jsx-wrap-multilines": 1,
"react/jsx-curly-newline": 1,
"no-multiple-empty-lines": [
1,
{
"max": 1
}
],
"no-trailing-spaces": 1,
"semi": 1,
"no-extra-semi": 1,
"react/jsx-max-props-per-line": [
1,
{
"when": "multiline"
}
],
"react/jsx-first-prop-new-line": [
1,
"multiline"
],
"@typescript-eslint/no-shadow": 1,
"max-len": [
1,
{
"code": 200
}
],
"arrow-parens": 1
}
},
"browserslist": {
"production": [
@@ -59,13 +97,7 @@
]
},
"devDependencies": {
"concurrently": "^6.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"http-proxy-middleware": "^1.1.1",
"nodemon": "^2.0.7",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.5",
"react-app-rewired": "^2.1.8"
"nodemon": "^2.0.20",
"npm-run-all": "^4.1.5"
}
}

View File

@@ -1,11 +1,5 @@
const { resolve, relative, sep } = require('path');
const {
readdirSync,
existsSync,
unlinkSync,
readFileSync,
createWriteStream
} = require('fs');
const { readdirSync, existsSync, unlinkSync, readFileSync, createWriteStream } = require('fs');
var zlib = require('zlib');
var mime = require('mime-types');
@@ -36,24 +30,15 @@ function cleanAndOpen(path) {
class ProgmemGenerator {
constructor(options = {}) {
const {
outputPath,
bytesPerLine = 20,
indent = ' ',
includes = ARDUINO_INCLUDES
} = options;
const { outputPath, bytesPerLine = 20, indent = ' ', includes = ARDUINO_INCLUDES } = options;
this.options = { outputPath, bytesPerLine, indent, includes };
}
apply(compiler) {
compiler.hooks.emit.tapAsync(
{ name: 'ProgmemGenerator' },
(compilation, callback) => {
compiler.hooks.emit.tapAsync({ name: 'ProgmemGenerator' }, (compilation, callback) => {
const { outputPath, bytesPerLine, indent, includes } = this.options;
const fileInfo = [];
const writeStream = cleanAndOpen(
resolve(compilation.options.context, outputPath)
);
const writeStream = cleanAndOpen(resolve(compilation.options.context, outputPath));
try {
const writeIncludes = () => {
writeStream.write(includes);
@@ -70,9 +55,7 @@ class ProgmemGenerator {
writeStream.write('\n');
writeStream.write(indent);
}
writeStream.write(
'0x' + ('00' + b.toString(16).toUpperCase()).substr(-2) + ','
);
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).substr(-2) + ',');
size++;
});
if (size % bytesPerLine) {
@@ -98,28 +81,19 @@ class ProgmemGenerator {
// process assets
const { assets } = compilation;
Object.keys(assets).forEach((relativeFilePath) => {
writeFile(
relativeFilePath,
coherseToBuffer(assets[relativeFilePath].source())
);
writeFile(relativeFilePath, coherseToBuffer(assets[relativeFilePath].source()));
});
};
const generateWWWClass = () => {
// eslint-disable-next-line max-len
return `typedef std::function<void(const String& uri, const String& contentType, const uint8_t * content, size_t len)> RouteRegistrationHandler;
class WWWData {
${indent}public:
${indent.repeat(
2
)}static void registerRoutes(RouteRegistrationHandler handler) {
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
${fileInfo
.map(
(file) =>
`${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${
file.variable
}, ${file.size});`
)
.map((file) => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size});`)
.join('\n')}
${indent.repeat(2)}}
};
@@ -140,8 +114,7 @@ ${indent.repeat(2)}}
} finally {
writeStream.end();
}
}
);
});
}
}

View File

@@ -1,28 +1,24 @@
/* Just supporting latin due to size constrains on the esp chip */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'),
url(../fonts/li.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
}
/*
* Just supporting latin due to size constrains on the esp chip
*
* The framework only makes use of 400 (regular) + 500 (medium) weight fonts.
*
* If using light or strong typography variants you will need to add additional fonts.
*/
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'),
url(../fonts/re.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/re.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'),
url(../fonts/me.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/md.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,16 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="%PUBLIC_URL%/css/roboto.css">
<link rel="manifest" href="%PUBLIC_URL%/app/manifest.json">
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, user-scalable=0, maximum-scale=1, minimum-scale=1"
/>
<link rel="stylesheet" href="%PUBLIC_URL%/css/roboto.css" />
<link rel="manifest" href="%PUBLIC_URL%/app/manifest.json" />
<title>EMS-ESP</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
</body>
</html>

View File

@@ -1,57 +1,45 @@
import React, { Component, RefObject } from 'react';
import { Redirect, Route, Switch } from 'react-router';
import { FC, createRef, createContext, useContext, RefObject } from 'react';
import { SnackbarProvider } from 'notistack';
import { IconButton } from '@material-ui/core';
import CloseIcon from '@material-ui/icons/Close';
import { IconButton } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { FeaturesLoader } from './contexts/features';
import CustomTheme from './CustomTheme';
import AppRouting from './AppRouting';
import CustomMuiTheme from './CustomMuiTheme';
import { PROJECT_NAME } from './api';
import FeaturesWrapper from './features/FeaturesWrapper';
// this redirect forces a call to authenticationContext.refresh() which invalidates the JWT if it is invalid.
const unauthorizedRedirect = () => <Redirect to="/" />;
const App: FC = () => {
const notistackRef: RefObject<any> = createRef();
class App extends Component {
notistackRef: RefObject<any> = React.createRef();
componentDidMount() {
document.title = PROJECT_NAME;
}
onClickDismiss = (key: string | number | undefined) => () => {
this.notistackRef.current.closeSnackbar(key);
const onClickDismiss = (key: string | number | undefined) => () => {
notistackRef.current.closeSnackbar(key);
};
render() {
const ColorModeContext = createContext({ toggleColorMode: () => {} });
const colorMode = useContext(ColorModeContext);
return (
<CustomMuiTheme>
<ColorModeContext.Provider value={colorMode}>
<CustomTheme>
<SnackbarProvider
autoHideDuration={3000}
maxSnack={3}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
ref={this.notistackRef}
ref={notistackRef}
action={(key) => (
<IconButton onClick={this.onClickDismiss(key)} size="small">
<IconButton onClick={onClickDismiss(key)} size="small">
<CloseIcon />
</IconButton>
)}
>
<FeaturesWrapper>
<Switch>
<Route
exact
path="/unauthorized"
component={unauthorizedRedirect}
/>
<Route component={AppRouting} />
</Switch>
</FeaturesWrapper>
<FeaturesLoader>
<AppRouting />
</FeaturesLoader>
</SnackbarProvider>
</CustomMuiTheme>
</CustomTheme>
</ColorModeContext.Provider>
);
}
}
};
export default App;

View File

@@ -1,67 +1,74 @@
import React, { Component } from 'react';
import { Switch, Redirect } from 'react-router';
import { FC, useContext, useEffect } from 'react';
import { Navigate, Routes, Route, useLocation } from 'react-router-dom';
import { useSnackbar, VariantType } from 'notistack';
import * as Authentication from './authentication/Authentication';
import AuthenticationWrapper from './authentication/AuthenticationWrapper';
import UnauthenticatedRoute from './authentication/UnauthenticatedRoute';
import AuthenticatedRoute from './authentication/AuthenticatedRoute';
import { Authentication, AuthenticationContext } from './contexts/authentication';
import { FeaturesContext } from './contexts/features';
import { RequireAuthenticated, RequireUnauthenticated } from './components';
import SignIn from './SignIn';
import ProjectRouting from './project/ProjectRouting';
import NetworkConnection from './network/NetworkConnection';
import AccessPoint from './ap/AccessPoint';
import NetworkTime from './ntp/NetworkTime';
import Security from './security/Security';
import System from './system/System';
import AuthenticatedRouting from './AuthenticatedRouting';
import { PROJECT_PATH } from './api';
import Mqtt from './mqtt/Mqtt';
import { withFeatures, WithFeaturesProps } from './features/FeaturesContext';
import { Features } from './features/types';
export const getDefaultRoute = (features: Features) =>
features.project ? `/${PROJECT_PATH}/` : '/network/';
class AppRouting extends Component<WithFeaturesProps> {
componentDidMount() {
Authentication.clearLoginRedirect();
interface SecurityRedirectProps {
message: string;
variant?: VariantType;
signOut?: boolean;
}
render() {
const { features } = this.props;
const RootRedirect: FC<SecurityRedirectProps> = ({ message, variant, signOut }) => {
const authenticationContext = useContext(AuthenticationContext);
const { enqueueSnackbar } = useSnackbar();
useEffect(() => {
signOut && authenticationContext.signOut(false);
enqueueSnackbar(message, { variant });
}, [message, variant, signOut, authenticationContext, enqueueSnackbar]);
return <Navigate to="/" />;
};
export const RemoveTrailingSlashes = () => {
const location = useLocation();
return (
<AuthenticationWrapper>
<Switch>
{features.security && (
<UnauthenticatedRoute exact path="/" component={SignIn} />
)}
{features.project && (
<AuthenticatedRoute
exact
path={`/${PROJECT_PATH}/*`}
component={ProjectRouting}
location.pathname.match('/.*/$') && (
<Navigate
to={{
pathname: location.pathname.replace(/\/+$/, ''),
search: location.search
}}
/>
)}
<AuthenticatedRoute
exact
path="/network/*"
component={NetworkConnection}
/>
<AuthenticatedRoute exact path="/ap/*" component={AccessPoint} />
{features.ntp && (
<AuthenticatedRoute exact path="/ntp/*" component={NetworkTime} />
)}
{features.mqtt && (
<AuthenticatedRoute exact path="/mqtt/*" component={Mqtt} />
)}
{features.security && (
<AuthenticatedRoute exact path="/security/*" component={Security} />
)}
<AuthenticatedRoute exact path="/system/*" component={System} />
<Redirect to={getDefaultRoute(features)} />
</Switch>
</AuthenticationWrapper>
)
);
}
}
};
export default withFeatures(AppRouting);
const AppRouting: FC = () => {
const { features } = useContext(FeaturesContext);
return (
<Authentication>
<RemoveTrailingSlashes />
<Routes>
<Route path="/unauthorized" element={<RootRedirect message="Please sign in to continue" signOut />} />
<Route path="/fileUpdated" element={<RootRedirect message="Upload successful" variant="success" />} />
{features.security && (
<Route
path="/"
element={
<RequireUnauthenticated>
<SignIn />
</RequireUnauthenticated>
}
/>
)}
<Route
path="/*"
element={
<RequireAuthenticated>
<AuthenticatedRouting />
</RequireAuthenticated>
}
/>
</Routes>
</Authentication>
);
};
export default AppRouting;

View File

@@ -0,0 +1,66 @@
import { FC, useCallback, useContext, useEffect } from 'react';
import { Navigate, Routes, Route, useNavigate, useLocation } from 'react-router-dom';
import { AxiosError } from 'axios';
import { FeaturesContext } from './contexts/features';
import * as AuthenticationApi from './api/authentication';
import { PROJECT_PATH } from './api/env';
import { AXIOS } from './api/endpoints';
import { Layout, RequireAdmin } from './components';
import ProjectRouting from './project/ProjectRouting';
import NetworkConnection from './framework/network/NetworkConnection';
import AccessPoint from './framework/ap/AccessPoint';
import NetworkTime from './framework/ntp/NetworkTime';
import Mqtt from './framework/mqtt/Mqtt';
import System from './framework/system/System';
import Security from './framework/security/Security';
const AuthenticatedRouting: FC = () => {
const { features } = useContext(FeaturesContext);
const location = useLocation();
const navigate = useNavigate();
const handleApiResponseError = useCallback(
(error: AxiosError) => {
if (error.response && error.response.status === 401) {
AuthenticationApi.storeLoginRedirect(location);
navigate('/unauthorized');
}
return Promise.reject(error);
},
[location, navigate]
);
useEffect(() => {
const axiosHandlerId = AXIOS.interceptors.response.use((response) => response, handleApiResponseError);
return () => AXIOS.interceptors.response.eject(axiosHandlerId);
}, [handleApiResponseError]);
return (
<Layout>
<Routes>
{features.project && <Route path={`/${PROJECT_PATH}/*`} element={<ProjectRouting />} />}
<Route path="/network/*" element={<NetworkConnection />} />
<Route path="/ap/*" element={<AccessPoint />} />
{features.ntp && <Route path="/ntp/*" element={<NetworkTime />} />}
{features.mqtt && <Route path="/mqtt/*" element={<Mqtt />} />}
{features.security && (
<Route
path="/security/*"
element={
<RequireAdmin>
<Security />
</RequireAdmin>
}
/>
)}
<Route path="/system/*" element={<System />} />
<Route path="/*" element={<Navigate to={AuthenticationApi.getDefaultRoute(features)} />} />
</Routes>
</Layout>
);
};
export default AuthenticatedRouting;

View File

@@ -1,46 +0,0 @@
import { Component } from 'react';
import { CssBaseline } from '@material-ui/core';
import {
MuiThemeProvider,
createMuiTheme,
StylesProvider
} from '@material-ui/core/styles';
import { blueGrey, orange, red, green } from '@material-ui/core/colors';
const theme = createMuiTheme({
palette: {
type: 'dark',
primary: {
main: '#33bfff'
},
secondary: {
main: '#3d5afe'
},
info: {
main: blueGrey[500]
},
warning: {
main: orange[500]
},
error: {
main: red[500]
},
success: {
main: green[500]
}
}
});
export default class CustomMuiTheme extends Component {
render() {
return (
<StylesProvider>
<MuiThemeProvider theme={theme}>
<CssBaseline />
{this.props.children}
</MuiThemeProvider>
</StylesProvider>
);
}
}

View File

@@ -0,0 +1,33 @@
import { FC } from 'react';
import { CssBaseline } from '@mui/material';
import { createTheme, responsiveFontSizes, ThemeProvider } from '@mui/material/styles';
import { blueGrey, blue } from '@mui/material/colors';
import { RequiredChildrenProps } from './utils';
const theme = responsiveFontSizes(
createTheme({
typography: {
fontSize: 13
},
palette: {
mode: 'dark',
secondary: {
main: blue[500]
},
info: {
main: blueGrey[500]
}
}
})
);
const CustomTheme: FC<RequiredChildrenProps> = ({ children }) => (
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
);
export default CustomTheme;

View File

@@ -1,165 +1,117 @@
import React, { Component } from 'react';
import { withSnackbar, WithSnackbarProps } from 'notistack';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import { FC, useContext, useState } from 'react';
import { ValidateFieldsError } from 'async-validator';
import { useSnackbar } from 'notistack';
import {
withStyles,
createStyles,
Theme,
WithStyles
} from '@material-ui/core/styles';
import { Paper, Typography, Fab } from '@material-ui/core';
import ForwardIcon from '@material-ui/icons/Forward';
import { Box, Fab, Paper, Typography } from '@mui/material';
import ForwardIcon from '@mui/icons-material/Forward';
import {
withAuthenticationContext,
AuthenticationContextProps
} from './authentication/AuthenticationContext';
import { PasswordValidator } from './components';
import { PROJECT_NAME, SIGN_IN_ENDPOINT } from './api';
import * as AuthenticationApi from './api/authentication';
import { PROJECT_NAME } from './api/env';
import { AuthenticationContext } from './contexts/authentication';
const styles = (theme: Theme) =>
createStyles({
signInPage: {
display: 'flex',
height: '100vh',
margin: 'auto',
padding: theme.spacing(2),
justifyContent: 'center',
flexDirection: 'column',
maxWidth: theme.breakpoints.values.sm
},
signInPanel: {
import { AxiosError } from 'axios';
import { extractErrorMessage, onEnterCallback, updateValue } from './utils';
import { SignInRequest } from './types';
import { ValidatedTextField } from './components';
import { SIGN_IN_REQUEST_VALIDATOR, validate } from './validators';
const SignIn: FC = () => {
const authenticationContext = useContext(AuthenticationContext);
const { enqueueSnackbar } = useSnackbar();
const [signInRequest, setSignInRequest] = useState<SignInRequest>({
username: '',
password: ''
});
const [processing, setProcessing] = useState<boolean>(false);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
const updateLoginRequestValue = updateValue(setSignInRequest);
const validateAndSignIn = async () => {
setProcessing(true);
try {
await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest);
signIn();
} catch (errors: any) {
setFieldErrors(errors);
setProcessing(false);
}
};
const signIn = async () => {
try {
const { data: loginResponse } = await AuthenticationApi.signIn(signInRequest);
authenticationContext.signIn(loginResponse.access_token);
} catch (error: unknown) {
if (error instanceof AxiosError) {
if (error.response?.status === 401) {
enqueueSnackbar('Invalid login details', { variant: 'warning' });
}
} else {
enqueueSnackbar(extractErrorMessage(error, 'Unexpected error, please try again'), { variant: 'error' });
}
setProcessing(false);
}
};
const submitOnEnter = onEnterCallback(signIn);
return (
<Box
display="flex"
height="100vh"
margin="auto"
padding={2}
justifyContent="center"
flexDirection="column"
maxWidth={(theme) => theme.breakpoints.values.sm}
>
<Paper
sx={(theme) => ({
textAlign: 'center',
padding: theme.spacing(2),
paddingTop: '200px',
backgroundImage: 'url("/app/icon.png")',
backgroundRepeat: 'no-repeat',
backgroundPosition: '50% ' + theme.spacing(2) + 'px',
backgroundPosition: '50% ' + theme.spacing(2),
backgroundSize: 'auto 150px',
width: '100%'
},
extendedIcon: {
marginRight: theme.spacing(0.5)
},
button: {
marginRight: theme.spacing(2),
marginTop: theme.spacing(2)
}
});
type SignInProps = WithSnackbarProps &
WithStyles<typeof styles> &
AuthenticationContextProps;
interface SignInState {
username: string;
password: string;
processing: boolean;
}
class SignIn extends Component<SignInProps, SignInState> {
constructor(props: SignInProps) {
super(props);
this.state = {
username: '',
password: '',
processing: false
};
}
updateInputElement = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { name, value } = event.currentTarget;
this.setState((prevState) => ({
...prevState,
[name]: value
}));
};
onSubmit = () => {
const { username, password } = this.state;
const { authenticationContext } = this.props;
this.setState({ processing: true });
fetch(SIGN_IN_ENDPOINT, {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({
'Content-Type': 'application/json'
})
})
.then((response) => {
if (response.status === 200) {
return response.json();
} else if (response.status === 401) {
throw Error('Invalid credentials.');
} else {
throw Error('Invalid status code: ' + response.status);
}
})
.then((json) => {
authenticationContext.signIn(json.access_token);
})
.catch((error) => {
this.props.enqueueSnackbar(error.message, {
variant: 'warning'
});
this.setState({ processing: false });
});
};
render() {
const { username, password, processing } = this.state;
const { classes } = this.props;
return (
<div className={classes.signInPage}>
<Paper className={classes.signInPanel}>
})}
>
<Typography variant="h4">{PROJECT_NAME}</Typography>
<ValidatorForm onSubmit={this.onSubmit}>
<TextValidator
<ValidatedTextField
fieldErrors={fieldErrors}
disabled={processing}
validators={['required']}
errorMessages={['Username is required']}
name="username"
label="Username"
fullWidth
variant="outlined"
value={username}
onChange={this.updateInputElement}
value={signInRequest.username}
onChange={updateLoginRequestValue}
margin="normal"
inputProps={{
autoCapitalize: 'none',
autoCorrect: 'off'
}}
variant="outlined"
fullWidth
/>
<PasswordValidator
<ValidatedTextField
fieldErrors={fieldErrors}
disabled={processing}
validators={['required']}
errorMessages={['Password is required']}
type="password"
name="password"
label="Password"
fullWidth
variant="outlined"
value={password}
onChange={this.updateInputElement}
value={signInRequest.password}
onChange={updateLoginRequestValue}
onKeyDown={submitOnEnter}
margin="normal"
variant="outlined"
fullWidth
/>
<Fab
variant="extended"
color="primary"
className={classes.button}
type="submit"
disabled={processing}
>
<ForwardIcon className={classes.extendedIcon} />
<Fab variant="extended" color="primary" sx={{ mt: 2 }} onClick={validateAndSignIn} disabled={processing}>
<ForwardIcon sx={{ mr: 1 }} />
Sign In
</Fab>
</ValidatorForm>
</Paper>
</div>
</Box>
);
}
}
};
export default withAuthenticationContext(
withSnackbar(withStyles(styles)(SignIn))
);
export default SignIn;

View File

@@ -1,8 +0,0 @@
import { APSettings, APProvisionMode } from './types';
export const isAPEnabled = ({ provision_mode }: APSettings) => {
return (
provision_mode === APProvisionMode.AP_MODE_ALWAYS ||
provision_mode === APProvisionMode.AP_MODE_DISCONNECTED
);
};

View File

@@ -1,33 +0,0 @@
import { Component } from 'react';
import { AP_SETTINGS_ENDPOINT } from '../api';
import {
restController,
RestControllerProps,
RestFormLoader,
SectionContent
} from '../components';
import APSettingsForm from './APSettingsForm';
import { APSettings } from './types';
type APSettingsControllerProps = RestControllerProps<APSettings>;
class APSettingsController extends Component<APSettingsControllerProps> {
componentDidMount() {
this.props.loadData();
}
render() {
return (
<SectionContent title="Access Point Settings" titleGutter>
<RestFormLoader
{...this.props}
render={(formProps) => <APSettingsForm {...formProps} />}
/>
</SectionContent>
);
}
}
export default restController(AP_SETTINGS_ENDPOINT, APSettingsController);

View File

@@ -1,134 +0,0 @@
import React, { Fragment } from 'react';
import {
TextValidator,
ValidatorForm,
SelectValidator
} from 'react-material-ui-form-validator';
import MenuItem from '@material-ui/core/MenuItem';
import SaveIcon from '@material-ui/icons/Save';
import {
PasswordValidator,
RestFormProps,
FormActions,
FormButton
} from '../components';
import { isAPEnabled } from './APModes';
import { APSettings, APProvisionMode } from './types';
import { isIP } from '../validators';
type APSettingsFormProps = RestFormProps<APSettings>;
class APSettingsForm extends React.Component<APSettingsFormProps> {
componentDidMount() {
ValidatorForm.addValidationRule('isIP', isIP);
}
render() {
const { data, handleValueChange, saveData } = this.props;
return (
<ValidatorForm onSubmit={saveData} ref="APSettingsForm">
<SelectValidator
name="provision_mode"
label="Provide Access Point&hellip;"
value={data.provision_mode}
fullWidth
variant="outlined"
onChange={handleValueChange('provision_mode')}
margin="normal"
>
<MenuItem value={APProvisionMode.AP_MODE_ALWAYS}>Always</MenuItem>
<MenuItem value={APProvisionMode.AP_MODE_DISCONNECTED}>
When Network Disconnected
</MenuItem>
<MenuItem value={APProvisionMode.AP_NEVER}>Never</MenuItem>
</SelectValidator>
{isAPEnabled(data) && (
<Fragment>
<TextValidator
validators={['required', 'matchRegexp:^.{1,32}$']}
errorMessages={[
'Access Point SSID is required',
'Access Point SSID must be 32 characters or less'
]}
name="ssid"
label="Access Point SSID"
fullWidth
variant="outlined"
value={data.ssid}
onChange={handleValueChange('ssid')}
margin="normal"
/>
<PasswordValidator
validators={['required', 'matchRegexp:^.{8,64}$']}
errorMessages={[
'Access Point Password is required',
'Access Point Password must be 8-64 characters'
]}
name="password"
label="Access Point Password"
fullWidth
variant="outlined"
value={data.password}
onChange={handleValueChange('password')}
margin="normal"
/>
<TextValidator
validators={['required', 'isIP']}
errorMessages={['Local IP is required', 'Must be an IP address']}
name="local_ip"
label="Local IP"
fullWidth
variant="outlined"
value={data.local_ip}
onChange={handleValueChange('local_ip')}
margin="normal"
/>
<TextValidator
validators={['required', 'isIP']}
errorMessages={[
'Gateway IP is required',
'Must be an IP address'
]}
name="gateway_ip"
label="Gateway"
fullWidth
variant="outlined"
value={data.gateway_ip}
onChange={handleValueChange('gateway_ip')}
margin="normal"
/>
<TextValidator
validators={['required', 'isIP']}
errorMessages={[
'Subnet mask is required',
'Must be an IP address'
]}
name="subnet_mask"
label="Subnet"
fullWidth
variant="outlined"
value={data.subnet_mask}
onChange={handleValueChange('subnet_mask')}
margin="normal"
/>
</Fragment>
)}
<FormActions>
<FormButton
startIcon={<SaveIcon />}
variant="contained"
color="primary"
type="submit"
>
Save
</FormButton>
</FormActions>
</ValidatorForm>
);
}
}
export default APSettingsForm;

View File

@@ -1,28 +0,0 @@
import { Theme } from '@material-ui/core';
import { APStatus, APNetworkStatus } from './types';
export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => {
switch (status) {
case APNetworkStatus.ACTIVE:
return theme.palette.success.main;
case APNetworkStatus.INACTIVE:
return theme.palette.info.main;
case APNetworkStatus.LINGERING:
return theme.palette.warning.main;
default:
return theme.palette.warning.main;
}
};
export const apStatus = ({ status }: APStatus) => {
switch (status) {
case APNetworkStatus.ACTIVE:
return 'Active';
case APNetworkStatus.INACTIVE:
return 'Inactive';
case APNetworkStatus.LINGERING:
return 'Lingering until idle';
default:
return 'Unknown';
}
};

View File

@@ -1,33 +0,0 @@
import { Component } from 'react';
import {
restController,
RestControllerProps,
RestFormLoader,
SectionContent
} from '../components';
import { AP_STATUS_ENDPOINT } from '../api';
import APStatusForm from './APStatusForm';
import { APStatus } from './types';
type APStatusControllerProps = RestControllerProps<APStatus>;
class APStatusController extends Component<APStatusControllerProps> {
componentDidMount() {
this.props.loadData();
}
render() {
return (
<SectionContent title="Access Point Status">
<RestFormLoader
{...this.props}
render={(formProps) => <APStatusForm {...formProps} />}
/>
</SectionContent>
);
}
}
export default restController(AP_STATUS_ENDPOINT, APStatusController);

View File

@@ -1,91 +0,0 @@
import React, { Component, Fragment } from 'react';
import { WithTheme, withTheme } from '@material-ui/core/styles';
import {
Avatar,
Divider,
List,
ListItem,
ListItemAvatar,
ListItemText
} from '@material-ui/core';
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
import ComputerIcon from '@material-ui/icons/Computer';
import RefreshIcon from '@material-ui/icons/Refresh';
import {
RestFormProps,
FormActions,
FormButton,
HighlightAvatar
} from '../components';
import { apStatusHighlight, apStatus } from './APStatus';
import { APStatus } from './types';
type APStatusFormProps = RestFormProps<APStatus> & WithTheme;
class APStatusForm extends Component<APStatusFormProps> {
createListItems() {
const { data, theme } = this.props;
return (
<Fragment>
<ListItem>
<ListItemAvatar>
<HighlightAvatar color={apStatusHighlight(data, theme)}>
<SettingsInputAntennaIcon />
</HighlightAvatar>
</ListItemAvatar>
<ListItemText primary="Status" secondary={apStatus(data)} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>IP</Avatar>
</ListItemAvatar>
<ListItemText primary="IP Address" secondary={data.ip_address} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
<DeviceHubIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="MAC Address" secondary={data.mac_address} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
<ComputerIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="AP Clients" secondary={data.station_num} />
</ListItem>
<Divider variant="inset" component="li" />
</Fragment>
);
}
render() {
return (
<Fragment>
<List>{this.createListItems()}</List>
<FormActions>
<FormButton
startIcon={<RefreshIcon />}
variant="contained"
color="secondary"
onClick={this.props.loadData}
>
Refresh
</FormButton>
</FormActions>
</Fragment>
);
}
}
export default withTheme(APStatusForm);

View File

@@ -1,57 +0,0 @@
import { Component } from 'react';
import { Redirect, Switch, RouteComponentProps } from 'react-router-dom';
import { Tabs, Tab } from '@material-ui/core';
import {
AuthenticatedContextProps,
withAuthenticatedContext,
AuthenticatedRoute
} from '../authentication';
import { MenuAppBar } from '../components';
import APSettingsController from './APSettingsController';
import APStatusController from './APStatusController';
type AccessPointProps = AuthenticatedContextProps & RouteComponentProps;
class AccessPoint extends Component<AccessPointProps> {
handleTabChange = (path: string) => {
this.props.history.push(path);
};
render() {
const { authenticatedContext } = this.props;
return (
<MenuAppBar sectionTitle="Access Point">
<Tabs
value={this.props.match.url}
onChange={(e, path) => this.handleTabChange(path)}
variant="fullWidth"
>
<Tab value="/ap/status" label="Access Point Status" />
<Tab
value="/ap/settings"
label="Access Point Settings"
disabled={!authenticatedContext.me.admin}
/>
</Tabs>
<Switch>
<AuthenticatedRoute
exact
path="/ap/status"
component={APStatusController}
/>
<AuthenticatedRoute
exact
path="/ap/settings"
component={APSettingsController}
/>
<Redirect to="/ap/status" />
</Switch>
</MenuAppBar>
);
}
}
export default withAuthenticatedContext(AccessPoint);

View File

@@ -1,24 +0,0 @@
import { ENDPOINT_ROOT } from './Env';
export const FEATURES_ENDPOINT = ENDPOINT_ROOT + 'features';
export const NTP_STATUS_ENDPOINT = ENDPOINT_ROOT + 'ntpStatus';
export const NTP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'ntpSettings';
export const TIME_ENDPOINT = ENDPOINT_ROOT + 'time';
export const AP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'apSettings';
export const AP_STATUS_ENDPOINT = ENDPOINT_ROOT + 'apStatus';
export const SCAN_NETWORKS_ENDPOINT = ENDPOINT_ROOT + 'scanNetworks';
export const LIST_NETWORKS_ENDPOINT = ENDPOINT_ROOT + 'listNetworks';
export const NETWORK_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'networkSettings';
export const NETWORK_STATUS_ENDPOINT = ENDPOINT_ROOT + 'networkStatus';
export const OTA_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'otaSettings';
export const UPLOAD_FIRMWARE_ENDPOINT = ENDPOINT_ROOT + 'uploadFirmware';
export const MQTT_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'mqttSettings';
export const MQTT_STATUS_ENDPOINT = ENDPOINT_ROOT + 'mqttStatus';
export const SYSTEM_STATUS_ENDPOINT = ENDPOINT_ROOT + 'systemStatus';
export const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + 'signIn';
export const VERIFY_AUTHORIZATION_ENDPOINT =
ENDPOINT_ROOT + 'verifyAuthorization';
export const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'securitySettings';
export const GENERATE_TOKEN_ENDPOINT = ENDPOINT_ROOT + 'generateToken';
export const RESTART_ENDPOINT = ENDPOINT_ROOT + 'restart';
export const FACTORY_RESET_ENDPOINT = ENDPOINT_ROOT + 'factoryReset';

View File

@@ -1,26 +0,0 @@
export const PROJECT_NAME = process.env.REACT_APP_PROJECT_NAME!;
export const PROJECT_PATH = process.env.REACT_APP_PROJECT_PATH!;
export const ENDPOINT_ROOT = calculateEndpointRoot('/rest/');
export const WEB_SOCKET_ROOT = calculateWebSocketRoot('/ws/');
export const EVENT_SOURCE_ROOT = calculateEndpointRoot('/es/');
export const API_ENDPOINT_ROOT = calculateEndpointRoot('/api/');
function calculateEndpointRoot(endpointPath: string) {
const httpRoot = process.env.REACT_APP_HTTP_ROOT;
if (httpRoot) {
return httpRoot + endpointPath;
}
const location = window.location;
return location.protocol + '//' + location.host + endpointPath;
}
function calculateWebSocketRoot(webSocketPath: string) {
const webSocketRoot = process.env.REACT_APP_WEB_SOCKET_ROOT;
if (webSocketRoot) {
return webSocketRoot + webSocketPath;
}
const location = window.location;
const webProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
return webProtocol + '//' + location.host + webSocketPath;
}

16
interface/src/api/ap.ts Normal file
View File

@@ -0,0 +1,16 @@
import { AxiosPromise } from 'axios';
import { APSettings, APStatus } from '../types';
import { AXIOS } from './endpoints';
export function readAPStatus(): AxiosPromise<APStatus> {
return AXIOS.get('/apStatus');
}
export function readAPSettings(): AxiosPromise<APSettings> {
return AXIOS.get('/apSettings');
}
export function updateAPSettings(apSettings: APSettings): AxiosPromise<APSettings> {
return AXIOS.post('/apSettings', apSettings);
}

View File

@@ -0,0 +1,64 @@
import { AxiosPromise } from 'axios';
import * as H from 'history';
import jwtDecode from 'jwt-decode';
import { Path } from 'react-router-dom';
import { Features, Me, SignInRequest, SignInResponse } from '../types';
import { ACCESS_TOKEN, AXIOS } from './endpoints';
import { PROJECT_PATH } from './env';
export const SIGN_IN_PATHNAME = 'loginPathname';
export const SIGN_IN_SEARCH = 'loginSearch';
export const getDefaultRoute = (features: Features) => (features.project ? `/${PROJECT_PATH}` : '/wifi');
export function verifyAuthorization(): AxiosPromise<void> {
return AXIOS.get('/verifyAuthorization');
}
export function signIn(request: SignInRequest): AxiosPromise<SignInResponse> {
return AXIOS.post('/signIn', request);
}
/**
* Fallback to sessionStorage if localStorage is absent. WebView may not have local storage enabled.
*/
export function getStorage() {
return localStorage || sessionStorage;
}
export function storeLoginRedirect(location?: H.Location) {
if (location) {
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname);
getStorage().setItem(SIGN_IN_SEARCH, location.search);
}
}
export function clearLoginRedirect() {
getStorage().removeItem(SIGN_IN_PATHNAME);
getStorage().removeItem(SIGN_IN_SEARCH);
}
export function fetchLoginRedirect(features: Features): Partial<Path> {
const signInPathname = getStorage().getItem(SIGN_IN_PATHNAME);
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH);
clearLoginRedirect();
return {
pathname: signInPathname || getDefaultRoute(features),
search: (signInPathname && signInSearch) || undefined
};
}
export const clearAccessToken = () => localStorage.removeItem(ACCESS_TOKEN);
export const decodeMeJWT = (accessToken: string): Me => jwtDecode(accessToken) as Me;
export function addAccessTokenParameter(url: string) {
const accessToken = getStorage().getItem(ACCESS_TOKEN);
if (!accessToken) {
return url;
}
const parsedUrl = new URL(url);
parsedUrl.searchParams.set(ACCESS_TOKEN, accessToken);
return parsedUrl.toString();
}

View File

@@ -0,0 +1,105 @@
import axios, { AxiosPromise, CancelToken } from 'axios';
import { decode } from '@msgpack/msgpack';
export const WS_BASE_URL = '/ws/';
export const API_BASE_URL = '/rest/';
export const ES_BASE_URL = '/es/';
export const EMSESP_API_BASE_URL = '/api/';
export const ACCESS_TOKEN = 'access_token';
export const WEB_SOCKET_ROOT = calculateWebSocketRoot(WS_BASE_URL);
export const EVENT_SOURCE_ROOT = calculateEventSourceRoot(ES_BASE_URL);
export const AXIOS = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json'
},
transformRequest: [
(data, headers) => {
if (headers) {
if (localStorage.getItem(ACCESS_TOKEN)) {
headers.Authorization = 'Bearer ' + localStorage.getItem(ACCESS_TOKEN);
}
if (headers['Content-Type'] !== 'application/json') {
return data;
}
}
return JSON.stringify(data);
}
]
});
export const AXIOS_API = axios.create({
baseURL: EMSESP_API_BASE_URL,
headers: {
'Content-Type': 'application/json'
},
transformRequest: [
(data, headers) => {
if (headers) {
if (localStorage.getItem(ACCESS_TOKEN)) {
headers.Authorization = 'Bearer ' + localStorage.getItem(ACCESS_TOKEN);
}
if (headers['Content-Type'] !== 'application/json') {
return data;
}
}
return JSON.stringify(data);
}
]
});
export const AXIOS_BIN = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json'
},
responseType: 'arraybuffer',
transformRequest: [
(data, headers) => {
if (headers) {
if (localStorage.getItem(ACCESS_TOKEN)) {
headers.Authorization = 'Bearer ' + localStorage.getItem(ACCESS_TOKEN);
}
if (headers['Content-Type'] !== 'application/json') {
return data;
}
}
return JSON.stringify(data);
}
],
transformResponse: [
(data) => {
return decode(data);
}
]
});
function calculateWebSocketRoot(webSocketPath: string) {
const location = window.location;
const webProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
return webProtocol + '//' + location.host + webSocketPath;
}
function calculateEventSourceRoot(endpointPath: string) {
const location = window.location;
return location.protocol + '//' + location.host + endpointPath;
}
export interface FileUploadConfig {
cancelToken?: CancelToken;
onUploadProgress?: (progressEvent: ProgressEvent) => void;
}
export const startUploadFile = (url: string, file: File, config?: FileUploadConfig): AxiosPromise<void> => {
const formData = new FormData();
formData.append('file', file);
return AXIOS.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
...(config || {})
});
};

2
interface/src/api/env.ts Normal file
View File

@@ -0,0 +1,2 @@
export const PROJECT_NAME = process.env.REACT_APP_PROJECT_NAME || 'EMS-ESP';
export const PROJECT_PATH = process.env.REACT_APP_PROJECT_PATH || 'project';

View File

@@ -0,0 +1,9 @@
import { AxiosPromise } from 'axios';
import { Features } from '../types';
import { AXIOS } from './endpoints';
export function readFeatures(): AxiosPromise<Features> {
return AXIOS.get('/features');
}

View File

@@ -1,2 +0,0 @@
export * from './Env';
export * from './Endpoints';

16
interface/src/api/mqtt.ts Normal file
View File

@@ -0,0 +1,16 @@
import { AxiosPromise } from 'axios';
import { MqttSettings, MqttStatus } from '../types';
import { AXIOS } from './endpoints';
export function readMqttStatus(): AxiosPromise<MqttStatus> {
return AXIOS.get('/mqttStatus');
}
export function readMqttSettings(): AxiosPromise<MqttSettings> {
return AXIOS.get('/mqttSettings');
}
export function updateMqttSettings(ntpSettings: MqttSettings): AxiosPromise<MqttSettings> {
return AXIOS.post('/mqttSettings', ntpSettings);
}

View File

@@ -0,0 +1,25 @@
import { AxiosPromise } from 'axios';
import { WiFiNetworkList, NetworkSettings, NetworkStatus } from '../types';
import { AXIOS } from './endpoints';
export function readNetworkStatus(): AxiosPromise<NetworkStatus> {
return AXIOS.get('/networkStatus');
}
export function scanNetworks(): AxiosPromise<void> {
return AXIOS.get('/scanNetworks');
}
export function listNetworks(): AxiosPromise<WiFiNetworkList> {
return AXIOS.get('/listNetworks');
}
export function readNetworkSettings(): AxiosPromise<NetworkSettings> {
return AXIOS.get('/networkSettings');
}
export function updateNetworkSettings(wifiSettings: NetworkSettings): AxiosPromise<NetworkSettings> {
return AXIOS.post('/networkSettings', wifiSettings);
}

20
interface/src/api/ntp.ts Normal file
View File

@@ -0,0 +1,20 @@
import { AxiosPromise } from 'axios';
import { NTPSettings, NTPStatus, Time } from '../types';
import { AXIOS } from './endpoints';
export function readNTPStatus(): AxiosPromise<NTPStatus> {
return AXIOS.get('/ntpStatus');
}
export function readNTPSettings(): AxiosPromise<NTPSettings> {
return AXIOS.get('/ntpSettings');
}
export function updateNTPSettings(ntpSettings: NTPSettings): AxiosPromise<NTPSettings> {
return AXIOS.post('/ntpSettings', ntpSettings);
}
export function updateTime(time: Time): AxiosPromise<Time> {
return AXIOS.post('/time', time);
}

View File

@@ -0,0 +1,17 @@
import { AxiosPromise } from 'axios';
import { SecuritySettings, Token } from '../types';
import { AXIOS } from './endpoints';
export function readSecuritySettings(): AxiosPromise<SecuritySettings> {
return AXIOS.get('/securitySettings');
}
export function updateSecuritySettings(securitySettings: SecuritySettings): AxiosPromise<SecuritySettings> {
return AXIOS.post('/securitySettings', securitySettings);
}
export function generateToken(username?: string): AxiosPromise<Token> {
return AXIOS.get('/generateToken', { params: { username } });
}

View File

@@ -0,0 +1,41 @@
import { AxiosPromise } from 'axios';
import { OTASettings, SystemStatus, LogSettings, LogEntries } from '../types';
import { AXIOS, AXIOS_BIN, FileUploadConfig, startUploadFile } from './endpoints';
export function readSystemStatus(timeout?: number): AxiosPromise<SystemStatus> {
return AXIOS.get('/systemStatus', { timeout });
}
export function restart(): AxiosPromise<void> {
return AXIOS.post('/restart');
}
export function factoryReset(): AxiosPromise<void> {
return AXIOS.post('/factoryReset');
}
export function readOTASettings(): AxiosPromise<OTASettings> {
return AXIOS.get('/otaSettings');
}
export function updateOTASettings(otaSettings: OTASettings): AxiosPromise<OTASettings> {
return AXIOS.post('/otaSettings', otaSettings);
}
export const uploadFile = (file: File, config?: FileUploadConfig): AxiosPromise<void> =>
startUploadFile('/uploadFile', file, config);
export function readLogSettings(): AxiosPromise<LogSettings> {
return AXIOS.get('/logSettings');
}
export function updateLogSettings(logSettings: LogSettings): AxiosPromise<LogSettings> {
return AXIOS.post('/logSettings', logSettings);
}
export function readLogEntries(): AxiosPromise<LogEntries> {
return AXIOS_BIN.get('/fetchLog');
}

View File

@@ -1,56 +0,0 @@
import * as React from 'react';
import {
Redirect,
Route,
RouteProps,
RouteComponentProps
} from 'react-router-dom';
import { withSnackbar, WithSnackbarProps } from 'notistack';
import * as Authentication from './Authentication';
import {
withAuthenticationContext,
AuthenticationContextProps,
AuthenticatedContext,
AuthenticatedContextValue
} from './AuthenticationContext';
interface AuthenticatedRouteProps
extends RouteProps,
WithSnackbarProps,
AuthenticationContextProps {
component:
| React.ComponentType<RouteComponentProps<any>>
| React.ComponentType<any>;
}
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
export class AuthenticatedRoute extends React.Component<AuthenticatedRouteProps> {
render() {
const {
enqueueSnackbar,
authenticationContext,
component: Component,
...rest
} = this.props;
const { location } = this.props;
const renderComponent: RenderComponent = (props) => {
if (authenticationContext.me) {
return (
<AuthenticatedContext.Provider
value={authenticationContext as AuthenticatedContextValue}
>
<Component {...props} />
</AuthenticatedContext.Provider>
);
}
Authentication.storeLoginRedirect(location);
enqueueSnackbar('Please sign in to continue', { variant: 'info' });
return <Redirect to="/" />;
};
return <Route {...rest} render={renderComponent} />;
}
}
export default withSnackbar(withAuthenticationContext(AuthenticatedRoute));

View File

@@ -1,129 +0,0 @@
import * as H from 'history';
import history from '../history';
import { Features } from '../features/types';
import { getDefaultRoute } from '../AppRouting';
export const ACCESS_TOKEN = 'access_token';
export const SIGN_IN_PATHNAME = 'signInPathname';
export const SIGN_IN_SEARCH = 'signInSearch';
/**
* Fallback to sessionStorage if localStorage is absent. WebView may not have local storage enabled.
*/
export function getStorage() {
return localStorage || sessionStorage;
}
export function storeLoginRedirect(location?: H.Location) {
if (location) {
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname);
getStorage().setItem(SIGN_IN_SEARCH, location.search);
}
}
export function clearLoginRedirect() {
getStorage().removeItem(SIGN_IN_PATHNAME);
getStorage().removeItem(SIGN_IN_SEARCH);
}
export function fetchLoginRedirect(
features: Features
): H.LocationDescriptorObject {
const signInPathname = getStorage().getItem(SIGN_IN_PATHNAME);
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH);
clearLoginRedirect();
return {
pathname: signInPathname || getDefaultRoute(features),
search: (signInPathname && signInSearch) || undefined
};
}
/**
* Wraps the normal fetch routine with one with provides the access token if present.
*/
export function authorizedFetch(
url: RequestInfo,
params?: RequestInit
): Promise<Response> {
const accessToken = getStorage().getItem(ACCESS_TOKEN);
if (accessToken) {
params = params || {};
params.credentials = 'include';
params.headers = {
...params.headers,
Authorization: 'Bearer ' + accessToken
};
}
return fetch(url, params);
}
/**
* fetch() does not yet support upload progress, this wrapper allows us to configure the xhr request
* for a single file upload and takes care of adding the Authorization header and redirecting on
* authorization errors as we do for normal fetch operations.
*/
export function redirectingAuthorizedUpload(
xhr: XMLHttpRequest,
url: string,
file: File,
onProgress: (event: ProgressEvent<EventTarget>) => void
): Promise<void> {
return new Promise((resolve, reject) => {
xhr.open('POST', url, true);
const accessToken = getStorage().getItem(ACCESS_TOKEN);
if (accessToken) {
xhr.withCredentials = true;
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
}
xhr.upload.onprogress = onProgress;
xhr.onload = function () {
if (xhr.status === 401 || xhr.status === 403) {
history.push('/unauthorized');
} else {
resolve();
}
};
xhr.onerror = function () {
reject(new DOMException('Error', 'UploadError'));
};
xhr.onabort = function () {
reject(new DOMException('Aborted', 'AbortError'));
};
const formData = new FormData();
formData.append('file', file);
xhr.send(formData);
});
}
/**
* Wraps the normal fetch routine which redirects on 401 response.
*/
export function redirectingAuthorizedFetch(
url: RequestInfo,
params?: RequestInit
): Promise<Response> {
return new Promise<Response>((resolve, reject) => {
authorizedFetch(url, params)
.then((response) => {
if (response.status === 401 || response.status === 403) {
history.push('/unauthorized');
} else {
resolve(response);
}
})
.catch((error) => {
reject(error);
});
});
}
export function addAccessTokenParameter(url: string) {
const accessToken = getStorage().getItem(ACCESS_TOKEN);
if (!accessToken) {
return url;
}
const parsedUrl = new URL(url);
parsedUrl.searchParams.set(ACCESS_TOKEN, accessToken);
return parsedUrl.toString();
}

View File

@@ -1,77 +0,0 @@
import * as React from 'react';
export interface Me {
username: string;
admin: boolean;
}
export interface AuthenticationContextValue {
refresh: () => void;
signIn: (accessToken: string) => void;
signOut: () => void;
me?: Me;
}
const AuthenticationContextDefaultValue = {} as AuthenticationContextValue;
export const AuthenticationContext = React.createContext(
AuthenticationContextDefaultValue
);
export interface AuthenticationContextProps {
authenticationContext: AuthenticationContextValue;
}
export function withAuthenticationContext<T extends AuthenticationContextProps>(
Component: React.ComponentType<T>
) {
return class extends React.Component<
Omit<T, keyof AuthenticationContextProps>
> {
render() {
return (
<AuthenticationContext.Consumer>
{(authenticationContext) => (
<Component
{...(this.props as T)}
authenticationContext={authenticationContext}
/>
)}
</AuthenticationContext.Consumer>
);
}
};
}
export interface AuthenticatedContextValue extends AuthenticationContextValue {
me: Me;
}
const AuthenticatedContextDefaultValue = {} as AuthenticatedContextValue;
export const AuthenticatedContext = React.createContext(
AuthenticatedContextDefaultValue
);
export interface AuthenticatedContextProps {
authenticatedContext: AuthenticatedContextValue;
}
export function withAuthenticatedContext<T extends AuthenticatedContextProps>(
Component: React.ComponentType<T>
) {
return class extends React.Component<
Omit<T, keyof AuthenticatedContextProps>
> {
render() {
return (
<AuthenticatedContext.Consumer>
{(authenticatedContext) => (
<Component
{...(this.props as T)}
authenticatedContext={authenticatedContext}
/>
)}
</AuthenticatedContext.Consumer>
);
}
};
}

View File

@@ -1,135 +0,0 @@
import * as React from 'react';
import { withSnackbar, WithSnackbarProps } from 'notistack';
import jwtDecode from 'jwt-decode';
import history from '../history';
import { VERIFY_AUTHORIZATION_ENDPOINT } from '../api';
import { ACCESS_TOKEN, authorizedFetch, getStorage } from './Authentication';
import {
AuthenticationContext,
AuthenticationContextValue,
Me
} from './AuthenticationContext';
import FullScreenLoading from '../components/FullScreenLoading';
import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext';
export const decodeMeJWT = (accessToken: string): Me =>
jwtDecode(accessToken) as Me;
interface AuthenticationWrapperState {
context: AuthenticationContextValue;
initialized: boolean;
}
type AuthenticationWrapperProps = WithSnackbarProps & WithFeaturesProps;
class AuthenticationWrapper extends React.Component<
AuthenticationWrapperProps,
AuthenticationWrapperState
> {
constructor(props: AuthenticationWrapperProps) {
super(props);
this.state = {
context: {
refresh: this.refresh,
signIn: this.signIn,
signOut: this.signOut
},
initialized: false
};
}
componentDidMount() {
this.refresh();
}
render() {
return (
<React.Fragment>
{this.state.initialized
? this.renderContent()
: this.renderContentLoading()}
</React.Fragment>
);
}
renderContent() {
return (
<AuthenticationContext.Provider value={this.state.context}>
{this.props.children}
</AuthenticationContext.Provider>
);
}
renderContentLoading() {
return <FullScreenLoading />;
}
refresh = () => {
// commented out, always need security - proddy
// if (!this.props.features.security) {
// this.setState({ initialized: true, context: { ...this.state.context, me: { admin: true, username: "admin" } } });
// return;
// }
const accessToken = getStorage().getItem(ACCESS_TOKEN);
if (accessToken) {
authorizedFetch(VERIFY_AUTHORIZATION_ENDPOINT)
.then((response) => {
const me =
response.status === 200 ? decodeMeJWT(accessToken) : undefined;
this.setState({
initialized: true,
context: { ...this.state.context, me }
});
})
.catch((error) => {
this.setState({
initialized: true,
context: { ...this.state.context, me: undefined }
});
this.props.enqueueSnackbar(
'Error verifying authorization: ' + error.message,
{
variant: 'error'
}
);
});
} else {
this.setState({
initialized: true,
context: { ...this.state.context, me: undefined }
});
}
};
signIn = (accessToken: string) => {
try {
getStorage().setItem(ACCESS_TOKEN, accessToken);
const me: Me = decodeMeJWT(accessToken);
this.setState({ context: { ...this.state.context, me } });
this.props.enqueueSnackbar(`Logged in as ${me.username}`, {
variant: 'success'
});
} catch (err) {
this.setState({
initialized: true,
context: { ...this.state.context, me: undefined }
});
throw new Error('Failed to parse JWT ' + err.message);
}
};
signOut = () => {
getStorage().removeItem(ACCESS_TOKEN);
this.setState({
context: {
...this.state.context,
me: undefined
}
});
this.props.enqueueSnackbar('You have signed out', { variant: 'success' });
history.push('/');
};
}
export default withFeatures(withSnackbar(AuthenticationWrapper));

View File

@@ -1,47 +0,0 @@
import * as React from 'react';
import {
Redirect,
Route,
RouteProps,
RouteComponentProps
} from 'react-router-dom';
import {
withAuthenticationContext,
AuthenticationContextProps
} from './AuthenticationContext';
import * as Authentication from './Authentication';
import { WithFeaturesProps, withFeatures } from '../features/FeaturesContext';
interface UnauthenticatedRouteProps
extends RouteProps,
AuthenticationContextProps,
WithFeaturesProps {
component:
| React.ComponentType<RouteComponentProps<any>>
| 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} />;
}
}
export default withFeatures(withAuthenticationContext(UnauthenticatedRoute));

View File

@@ -1,6 +0,0 @@
export { default as AuthenticatedRoute } from './AuthenticatedRoute';
export { default as AuthenticationWrapper } from './AuthenticationWrapper';
export { default as UnauthenticatedRoute } from './UnauthenticatedRoute';
export * from './Authentication';
export * from './AuthenticationContext';

View File

@@ -1,59 +0,0 @@
import React, { FC } from 'react';
import { makeStyles } from '@material-ui/styles';
import { Paper, Typography, Box, CssBaseline } from '@material-ui/core';
import WarningIcon from '@material-ui/icons/Warning';
const styles = makeStyles({
siteErrorPage: {
display: 'flex',
height: '100vh',
justifyContent: 'center',
flexDirection: 'column'
},
siteErrorPagePanel: {
textAlign: 'center',
padding: '280px 0 40px 0',
backgroundImage: 'url("/app/icon.png")',
backgroundRepeat: 'no-repeat',
backgroundPosition: '50% 40px',
backgroundSize: '200px auto',
width: '100%'
}
});
interface ApplicationErrorProps {
error?: string;
}
const ApplicationError: FC<ApplicationErrorProps> = ({ error }) => {
const classes = styles();
return (
<div className={classes.siteErrorPage}>
<CssBaseline />
<Paper className={classes.siteErrorPagePanel} elevation={10}>
<Box
display="flex"
flexDirection="row"
justifyContent="center"
alignItems="center"
mb={2}
>
<WarningIcon fontSize="large" color="error" />
<Box ml={2}>
<Typography variant="h4">Application error</Typography>
</Box>
</Box>
<Typography variant="subtitle1" gutterBottom>
Failed to configure the application, please refresh to try again.
</Typography>
{error && (
<Typography variant="subtitle2" gutterBottom>
Error: {error}
</Typography>
)}
</Paper>
</div>
);
};
export default ApplicationError;

View File

@@ -0,0 +1,26 @@
import { FC } from 'react';
import { Box, BoxProps } from '@mui/material';
const ButtonRow: FC<BoxProps> = ({ children, ...rest }) => {
return (
<Box
sx={{
'& button, & a, & .MuiCard-root': {
mt: 2,
mx: 0.6,
'&:last-child': {
mr: 0
},
'&:first-of-type': {
ml: 0
}
}
}}
{...rest}
>
{children}
</Box>
);
};
export default ButtonRow;

View File

@@ -1,11 +0,0 @@
import { Button, styled } from '@material-ui/core';
const ErrorButton = styled(Button)(({ theme }) => ({
color: theme.palette.getContrastText(theme.palette.error.main),
backgroundColor: theme.palette.error.main,
'&:hover': {
backgroundColor: theme.palette.error.dark
}
}));
export default ErrorButton;

View File

@@ -1,7 +0,0 @@
import { styled, Box } from '@material-ui/core';
const FormActions = styled(Box)(({ theme }) => ({
marginTop: theme.spacing(1)
}));
export default FormActions;

View File

@@ -1,13 +0,0 @@
import { Button, styled } from '@material-ui/core';
const FormButton = styled(Button)(({ theme }) => ({
margin: theme.spacing(0, 1),
'&:last-child': {
marginRight: 0
},
'&:first-child': {
marginLeft: 0
}
}));
export default FormButton;

View File

@@ -1,32 +0,0 @@
import React from 'react';
import CircularProgress from '@material-ui/core/CircularProgress';
import { Typography, Theme } from '@material-ui/core';
import { makeStyles, createStyles } from '@material-ui/styles';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
fullScreenLoading: {
padding: theme.spacing(2),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
flexDirection: 'column'
},
progress: {
margin: theme.spacing(4)
}
})
);
const FullScreenLoading = () => {
const classes = useStyles();
return (
<div className={classes.fullScreenLoading}>
<CircularProgress className={classes.progress} size={100} />
<Typography variant="h4">Loading&hellip;</Typography>
</div>
);
};
export default FullScreenLoading;

View File

@@ -1,19 +0,0 @@
import { Avatar, makeStyles } from '@material-ui/core';
import { FC } from 'react';
interface HighlightAvatarProps {
color: string;
}
const useStyles = makeStyles({
root: (props: HighlightAvatarProps) => ({
backgroundColor: props.color
})
});
const HighlightAvatar: FC<HighlightAvatarProps> = (props) => {
const classes = useStyles(props);
return <Avatar className={classes.root}>{props.children}</Avatar>;
};
export default HighlightAvatar;

View File

@@ -1,390 +0,0 @@
import React, { RefObject, Fragment } from 'react';
import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
import {
Drawer,
AppBar,
Toolbar,
Avatar,
Divider,
Button,
Box,
IconButton
} from '@material-ui/core';
import {
ClickAwayListener,
Popper,
Hidden,
Typography
} from '@material-ui/core';
import {
List,
ListItem,
ListItemIcon,
ListItemText,
ListItemAvatar
} from '@material-ui/core';
import { Card, CardContent, CardActions } from '@material-ui/core';
import {
withStyles,
createStyles,
Theme,
WithTheme,
WithStyles,
withTheme
} from '@material-ui/core/styles';
import SettingsEthernetIcon from '@material-ui/icons/SettingsEthernet';
import SettingsIcon from '@material-ui/icons/Settings';
import AccessTimeIcon from '@material-ui/icons/AccessTime';
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
import LockIcon from '@material-ui/icons/Lock';
import MenuIcon from '@material-ui/icons/Menu';
import ProjectMenu from '../project/ProjectMenu';
import { PROJECT_NAME } from '../api';
import {
withAuthenticatedContext,
AuthenticatedContextProps
} from '../authentication';
import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext';
const drawerWidth = 290;
const styles = (theme: Theme) =>
createStyles({
root: {
display: 'flex'
},
drawer: {
[theme.breakpoints.up('md')]: {
width: drawerWidth,
flexShrink: 0
}
},
title: {
flexGrow: 1
},
appBar: {
marginLeft: drawerWidth,
[theme.breakpoints.up('md')]: {
width: `calc(100% - ${drawerWidth}px)`
}
},
toolbarImage: {
[theme.breakpoints.up('xs')]: {
height: 24,
marginRight: theme.spacing(2)
},
[theme.breakpoints.up('sm')]: {
height: 36,
marginRight: theme.spacing(3)
}
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('md')]: {
display: 'none'
}
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth
},
content: {
flexGrow: 1
},
authMenu: {
zIndex: theme.zIndex.tooltip,
maxWidth: 400
},
authMenuActions: {
padding: theme.spacing(2),
'& > * + *': {
marginLeft: theme.spacing(2)
}
}
});
interface MenuAppBarState {
mobileOpen: boolean;
authMenuOpen: boolean;
}
interface MenuAppBarProps
extends WithFeaturesProps,
AuthenticatedContextProps,
WithTheme,
WithStyles<typeof styles>,
RouteComponentProps {
sectionTitle: string;
}
class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
constructor(props: MenuAppBarProps) {
super(props);
this.state = {
mobileOpen: false,
authMenuOpen: false
};
}
anchorRef: RefObject<HTMLButtonElement> = React.createRef();
handleToggle = () => {
this.setState({ authMenuOpen: !this.state.authMenuOpen });
};
handleClose = (event: React.MouseEvent<Document>) => {
if (
this.anchorRef.current &&
this.anchorRef.current.contains(event.currentTarget)
) {
return;
}
this.setState({ authMenuOpen: false });
};
handleDrawerToggle = () => {
this.setState({ mobileOpen: !this.state.mobileOpen });
};
render() {
const {
classes,
theme,
children,
sectionTitle,
authenticatedContext,
features
} = this.props;
const { mobileOpen, authMenuOpen } = this.state;
const path = this.props.match.url;
const drawer = (
<div>
<Toolbar>
<Box display="flex">
<img
src="/app/icon.png"
className={classes.toolbarImage}
alt={PROJECT_NAME}
/>
</Box>
<Typography variant="h6" color="textPrimary">
{PROJECT_NAME}
</Typography>
<Divider absolute />
</Toolbar>
{features.project && (
<Fragment>
<ProjectMenu />
<Divider />
</Fragment>
)}
<List>
<ListItem
to="/network/"
selected={path.startsWith('/network/')}
button
component={Link}
>
<ListItemIcon>
<SettingsEthernetIcon />
</ListItemIcon>
<ListItemText primary="Network Connection" />
</ListItem>
<ListItem
to="/ap/"
selected={path.startsWith('/ap/')}
button
component={Link}
>
<ListItemIcon>
<SettingsInputAntennaIcon />
</ListItemIcon>
<ListItemText primary="Access Point" />
</ListItem>
{features.ntp && (
<ListItem
to="/ntp/"
selected={path.startsWith('/ntp/')}
button
component={Link}
>
<ListItemIcon>
<AccessTimeIcon />
</ListItemIcon>
<ListItemText primary="Network Time" />
</ListItem>
)}
{features.mqtt && (
<ListItem
to="/mqtt/"
selected={path.startsWith('/mqtt/')}
button
component={Link}
>
<ListItemIcon>
<DeviceHubIcon />
</ListItemIcon>
<ListItemText primary="MQTT" />
</ListItem>
)}
{features.security && (
<ListItem
to="/security/"
selected={path.startsWith('/security/')}
button
component={Link}
disabled={!authenticatedContext.me.admin}
>
<ListItemIcon>
<LockIcon />
</ListItemIcon>
<ListItemText primary="Security" />
</ListItem>
)}
<ListItem
to="/system/"
selected={path.startsWith('/system/')}
button
component={Link}
>
<ListItemIcon>
<SettingsIcon />
</ListItemIcon>
<ListItemText primary="System" />
</ListItem>
</List>
</div>
);
const userMenu = (
<div>
<IconButton
ref={this.anchorRef}
aria-owns={authMenuOpen ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={this.handleToggle}
color="inherit"
>
<AccountCircleIcon />
</IconButton>
<Popper
open={authMenuOpen}
anchorEl={this.anchorRef.current}
transition
className={classes.authMenu}
>
<ClickAwayListener onClickAway={this.handleClose}>
<Card id="menu-list-grow">
<CardContent>
<List disablePadding>
<ListItem disableGutters>
<ListItemAvatar>
<Avatar>
<AccountCircleIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={
'Signed in as: ' + authenticatedContext.me.username
}
secondary={
authenticatedContext.me.admin ? 'Admin User' : undefined
}
/>
</ListItem>
</List>
</CardContent>
<Divider />
<CardActions className={classes.authMenuActions}>
<Button
variant="contained"
fullWidth
color="primary"
onClick={authenticatedContext.signOut}
>
Sign Out
</Button>
</CardActions>
</Card>
</ClickAwayListener>
</Popper>
</div>
);
return (
<div className={classes.root}>
<AppBar position="fixed" className={classes.appBar} elevation={0}>
<Toolbar>
<IconButton
color="inherit"
aria-label="Open drawer"
edge="start"
onClick={this.handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
color="inherit"
noWrap
className={classes.title}
>
{sectionTitle}
</Typography>
{features.security && userMenu}
</Toolbar>
</AppBar>
<nav className={classes.drawer}>
<Hidden mdUp implementation="css">
<Drawer
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={this.handleDrawerToggle}
classes={{
paper: classes.drawerPaper
}}
ModalProps={{
keepMounted: true
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden smDown implementation="css">
<Drawer
classes={{
paper: classes.drawerPaper
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
<div className={classes.toolbar} />
{children}
</main>
</div>
);
}
}
export default withRouter(
withTheme(
withFeatures(withAuthenticatedContext(withStyles(styles)(MenuAppBar)))
)
);

View File

@@ -0,0 +1,47 @@
import { FC } from 'react';
import { Box, BoxProps, SvgIconProps, Theme, Typography, useTheme } from '@mui/material';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import ReportProblemOutlinedIcon from '@mui/icons-material/ReportProblemOutlined';
import ErrorIcon from '@mui/icons-material/Error';
type MessageBoxLevel = 'warning' | 'success' | 'info' | 'error';
export interface MessageBoxProps extends BoxProps {
level: MessageBoxLevel;
message: string;
}
const LEVEL_ICONS: { [type in MessageBoxLevel]: React.ComponentType<SvgIconProps> } = {
success: CheckCircleOutlineOutlinedIcon,
info: InfoOutlinedIcon,
warning: ReportProblemOutlinedIcon,
error: ErrorIcon
};
const LEVEL_BACKGROUNDS: { [type in MessageBoxLevel]: (theme: Theme) => string } = {
success: (theme: Theme) => theme.palette.success.dark,
info: (theme: Theme) => theme.palette.info.main,
warning: (theme: Theme) => theme.palette.warning.dark,
error: (theme: Theme) => theme.palette.error.dark
};
const MessageBox: FC<MessageBoxProps> = ({ level, message, sx, children, ...rest }) => {
const theme = useTheme();
const Icon = LEVEL_ICONS[level];
const backgroundColor = LEVEL_BACKGROUNDS[level](theme);
const color = 'white';
return (
<Box p={2} display="flex" alignItems="center" borderRadius={1} sx={{ backgroundColor, color, ...sx }} {...rest}>
<Icon />
<Typography sx={{ ml: 2, flexGrow: 1 }} variant="body1">
{message}
</Typography>
{children}
</Box>
);
};
export default MessageBox;

View File

@@ -1,64 +0,0 @@
import React from 'react';
import {
TextValidator,
ValidatorComponentProps
} from 'react-material-ui-form-validator';
import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
import { InputAdornment, IconButton } from '@material-ui/core';
import { Visibility, VisibilityOff } from '@material-ui/icons';
const styles = createStyles({
input: {
'&::-ms-reveal': {
display: 'none'
}
}
});
type PasswordValidatorProps = WithStyles<typeof styles> &
Exclude<ValidatorComponentProps, 'type' | 'InputProps'>;
interface PasswordValidatorState {
showPassword: boolean;
}
class PasswordValidator extends React.Component<
PasswordValidatorProps,
PasswordValidatorState
> {
state = {
showPassword: false
};
toggleShowPassword = () => {
this.setState({
showPassword: !this.state.showPassword
});
};
render() {
const { classes, ...rest } = this.props;
return (
<TextValidator
{...rest}
type={this.state.showPassword ? 'text' : 'password'}
InputProps={{
classes,
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="Toggle password visibility"
onClick={this.toggleShowPassword}
>
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
)
}}
/>
);
}
}
export default withStyles(styles)(PasswordValidator);

View File

@@ -1,141 +0,0 @@
import React from 'react';
import { withSnackbar, WithSnackbarProps } from 'notistack';
import { redirectingAuthorizedFetch } from '../authentication';
export interface RestControllerProps<D> extends WithSnackbarProps {
handleValueChange: (
name: keyof D
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
setData: (data: D, callback?: () => void) => void;
saveData: () => void;
loadData: () => void;
data?: D;
loading: boolean;
errorMessage?: string;
}
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;
}
};
interface RestControllerState<D> {
data?: D;
loading: boolean;
errorMessage?: string;
}
export function restController<D, P extends RestControllerProps<D>>(
endpointUrl: string,
RestController: React.ComponentType<P & RestControllerProps<D>>
) {
return withSnackbar(
class extends React.Component<
Omit<P, keyof RestControllerProps<D>> & WithSnackbarProps,
RestControllerState<D>
> {
state: RestControllerState<D> = {
data: undefined,
loading: false,
errorMessage: undefined
};
setData = (data: D, callback?: () => void) => {
this.setState(
{
data,
loading: false,
errorMessage: undefined
},
callback
);
};
loadData = () => {
this.setState({
data: undefined,
loading: true,
errorMessage: undefined
});
redirectingAuthorizedFetch(endpointUrl)
.then((response) => {
if (response.status === 200) {
return response.json();
}
throw Error('Invalid status code: ' + response.status);
})
.then((json) => {
this.setState({ data: json, loading: false });
})
.catch((error) => {
const errorMessage = error.message || 'Unknown error';
this.props.enqueueSnackbar('Problem fetching: ' + errorMessage, {
variant: 'error'
});
this.setState({ data: undefined, loading: false, errorMessage });
});
};
saveData = () => {
this.setState({ loading: true });
redirectingAuthorizedFetch(endpointUrl, {
method: 'POST',
body: JSON.stringify(this.state.data),
headers: {
'Content-Type': 'application/json'
}
})
.then((response) => {
if (response.status === 200) {
return response.json();
}
throw Error('Invalid status code: ' + response.status);
})
.then((json) => {
this.props.enqueueSnackbar('Update successful.', {
variant: 'success'
});
this.setState({ data: json, loading: false });
})
.catch((error) => {
const errorMessage = error.message || 'Unknown error';
this.props.enqueueSnackbar('Problem updating: ' + errorMessage, {
variant: 'error'
});
this.setState({ data: undefined, loading: false, errorMessage });
});
};
handleValueChange = (name: keyof D) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
const data = { ...this.state.data!, [name]: extractEventValue(event) };
this.setState({ data });
};
render() {
return (
<RestController
{...this.state}
{...(this.props as P)}
handleValueChange={this.handleValueChange}
setData={this.setData}
saveData={this.saveData}
loadData={this.loadData}
/>
);
}
}
);
}

View File

@@ -1,64 +0,0 @@
import React from 'react';
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
import { Button, LinearProgress, Typography } from '@material-ui/core';
import { RestControllerProps } from '.';
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)
}
})
);
export type RestFormProps<D> = Omit<
RestControllerProps<D>,
'loading' | 'errorMessage'
> & { data: D };
interface RestFormLoaderProps<D> extends RestControllerProps<D> {
render: (props: RestFormProps<D>) => JSX.Element;
}
export default function RestFormLoader<D>(props: RestFormLoaderProps<D>) {
const { loading, errorMessage, loadData, render, data, ...rest } = props;
const classes = useStyles();
if (loading || !data) {
return (
<div className={classes.loadingSettings}>
<LinearProgress className={classes.loadingSettingsDetails} />
<Typography variant="h6" className={classes.loadingSettingsDetails}>
Loading&hellip;
</Typography>
</div>
);
}
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 render({ ...rest, loadData, data });
}

View File

@@ -1,31 +1,20 @@
import React from 'react';
import { FC } from 'react';
import { Typography, Paper } from '@material-ui/core';
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles';
import { Paper, Divider } from '@mui/material';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
content: {
padding: theme.spacing(2),
margin: theme.spacing(3)
}
})
);
import { RequiredChildrenProps } from '../utils';
interface SectionContentProps {
interface SectionContentProps extends RequiredChildrenProps {
title: string;
titleGutter?: boolean;
id?: string;
}
const SectionContent: React.FC<SectionContentProps> = (props) => {
const { children, title, titleGutter, id } = props;
const classes = useStyles();
const SectionContent: FC<SectionContentProps> = (props) => {
const { children, title, id } = props;
return (
<Paper id={id} className={classes.content}>
<Typography variant="h6" gutterBottom={titleGutter}>
{title}
</Typography>
<Paper id={id} sx={{ p: 2, m: 2 }}>
<Divider sx={{ pb: 2, borderColor: 'primary.main', fontSize: 20, color: 'primary.main' }}>{title}</Divider>
{children}
</Paper>
);

View File

@@ -1,128 +0,0 @@
import React, { FC, Fragment } from 'react';
import { useDropzone, DropzoneState } from 'react-dropzone';
import { makeStyles, createStyles } from '@material-ui/styles';
import CloudUploadIcon from '@material-ui/icons/CloudUpload';
import CancelIcon from '@material-ui/icons/Cancel';
import {
Theme,
Box,
Typography,
LinearProgress,
Button
} from '@material-ui/core';
interface SingleUploadStyleProps extends DropzoneState {
uploading: boolean;
}
const progressPercentage = (progress: ProgressEvent) =>
Math.round((progress.loaded * 100) / progress.total);
const getBorderColor = (theme: Theme, props: SingleUploadStyleProps) => {
if (props.isDragAccept) {
return theme.palette.success.main;
}
if (props.isDragReject) {
return theme.palette.error.main;
}
if (props.isDragActive) {
return theme.palette.info.main;
}
return theme.palette.grey[700];
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
dropzone: {
padding: theme.spacing(8, 2),
borderWidth: 2,
borderRadius: 2,
borderStyle: 'dashed',
color: theme.palette.grey[700],
transition: 'border .24s ease-in-out',
cursor: (props: SingleUploadStyleProps) =>
props.uploading ? 'default' : 'pointer',
width: '100%',
borderColor: (props: SingleUploadStyleProps) =>
getBorderColor(theme, props)
}
})
);
export interface SingleUploadProps {
onDrop: (acceptedFiles: File[]) => void;
onCancel: () => void;
accept?: string | string[];
uploading: boolean;
progress?: ProgressEvent;
}
const SingleUpload: FC<SingleUploadProps> = ({
onDrop,
onCancel,
accept,
uploading,
progress
}) => {
const dropzoneState = useDropzone({
onDrop,
accept,
disabled: uploading,
multiple: false
});
const { getRootProps, getInputProps } = dropzoneState;
const classes = useStyles({ ...dropzoneState, uploading });
const renderProgressText = () => {
if (uploading) {
if (progress?.lengthComputable) {
return `Uploading: ${progressPercentage(progress)}%`;
}
return 'Uploading\u2026';
}
return 'Drop file or click here';
};
const renderProgress = (progress?: ProgressEvent) => (
<LinearProgress
variant={
!progress || progress.lengthComputable ? 'determinate' : 'indeterminate'
}
value={
!progress
? 0
: progress.lengthComputable
? progressPercentage(progress)
: 0
}
/>
);
return (
<div {...getRootProps({ className: classes.dropzone })}>
<input {...getInputProps()} />
<Box flexDirection="column" display="flex" alignItems="center">
<CloudUploadIcon fontSize="large" />
<Typography variant="h6">{renderProgressText()}</Typography>
{uploading && (
<Fragment>
<Box width="100%" p={2}>
{renderProgress(progress)}
</Box>
<Button
startIcon={<CancelIcon />}
variant="contained"
color="secondary"
onClick={onCancel}
>
Cancel
</Button>
</Fragment>
)}
</Box>
</div>
);
};
export default SingleUpload;

View File

@@ -1,158 +0,0 @@
import React from 'react';
import Sockette from 'sockette';
import throttle from 'lodash/throttle';
import { withSnackbar, WithSnackbarProps } from 'notistack';
import { addAccessTokenParameter } from '../authentication';
import { extractEventValue } from '.';
export interface WebSocketControllerProps<D> extends WithSnackbarProps {
handleValueChange: (
name: keyof D
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
setData: (data: D, callback?: () => void) => void;
saveData: () => void;
saveDataAndClear(): () => void;
connected: boolean;
data?: D;
}
interface WebSocketControllerState<D> {
ws: Sockette;
connected: boolean;
clientId?: string;
data?: D;
}
enum WebSocketMessageType {
ID = 'id',
PAYLOAD = 'payload'
}
interface WebSocketIdMessage {
type: typeof WebSocketMessageType.ID;
id: string;
}
interface WebSocketPayloadMessage<D> {
type: typeof WebSocketMessageType.PAYLOAD;
origin_id: string;
payload: D;
}
export type WebSocketMessage<D> =
| WebSocketIdMessage
| WebSocketPayloadMessage<D>;
export function webSocketController<D, P extends WebSocketControllerProps<D>>(
wsUrl: string,
wsThrottle: number,
WebSocketController: React.ComponentType<P & WebSocketControllerProps<D>>
) {
return withSnackbar(
class extends React.Component<
Omit<P, keyof WebSocketControllerProps<D>> & WithSnackbarProps,
WebSocketControllerState<D>
> {
constructor(
props: Omit<P, keyof WebSocketControllerProps<D>> & WithSnackbarProps
) {
super(props);
this.state = {
ws: new Sockette(addAccessTokenParameter(wsUrl), {
onmessage: this.onMessage,
onopen: this.onOpen,
onclose: this.onClose
}),
connected: false
};
}
componentWillUnmount() {
this.state.ws.close();
}
onMessage = (event: MessageEvent) => {
const rawData = event.data;
if (typeof rawData === 'string' || rawData instanceof String) {
this.handleMessage(
JSON.parse(rawData as string) as WebSocketMessage<D>
);
}
};
handleMessage = (message: WebSocketMessage<D>) => {
const { clientId, data } = this.state;
switch (message.type) {
case WebSocketMessageType.ID:
this.setState({ clientId: message.id });
break;
case WebSocketMessageType.PAYLOAD:
if (clientId && (!data || clientId !== message.origin_id)) {
this.setState({ data: message.payload });
}
break;
}
};
onOpen = () => {
this.setState({ connected: true });
};
onClose = () => {
this.setState({
connected: false,
clientId: undefined,
data: undefined
});
};
setData = (data: D, callback?: () => void) => {
this.setState({ data }, callback);
};
saveData = throttle(() => {
const { ws, connected, data } = this.state;
if (connected) {
ws.json(data);
}
}, wsThrottle);
saveDataAndClear = throttle(() => {
const { ws, connected, data } = this.state;
if (connected) {
this.setState(
{
data: undefined
},
() => ws.json(data)
);
}
}, wsThrottle);
handleValueChange = (name: keyof D) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
const data = { ...this.state.data!, [name]: extractEventValue(event) };
this.setState({ data });
};
render() {
return (
<WebSocketController
{...(this.props as P)}
handleValueChange={this.handleValueChange}
setData={this.setData}
saveData={this.saveData}
saveDataAndClear={this.saveDataAndClear}
connected={this.state.connected}
data={this.state.data}
/>
);
}
}
);
}

View File

@@ -1,43 +0,0 @@
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
import { LinearProgress, Typography } from '@material-ui/core';
import { WebSocketControllerProps } from '.';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
loadingSettings: {
margin: theme.spacing(0.5)
},
loadingSettingsDetails: {
margin: theme.spacing(4),
textAlign: 'center'
}
})
);
export type WebSocketFormProps<D> = Omit<
WebSocketControllerProps<D>,
'connected'
> & { data: D };
interface WebSocketFormLoaderProps<D> extends WebSocketControllerProps<D> {
render: (props: WebSocketFormProps<D>) => JSX.Element;
}
export default function WebSocketFormLoader<D>(
props: WebSocketFormLoaderProps<D>
) {
const { connected, render, data, ...rest } = props;
const classes = useStyles();
if (!connected || !data) {
return (
<div className={classes.loadingSettings}>
<LinearProgress className={classes.loadingSettingsDetails} />
<Typography variant="h6" className={classes.loadingSettingsDetails}>
Connecting to WebSocket...
</Typography>
</div>
);
}
return render({ ...rest, data });
}

View File

@@ -1,14 +0,0 @@
import { useLayoutEffect, useState } from 'react';
export function useWindowSize() {
const [size, setSize] = useState([0, 0]);
useLayoutEffect(() => {
function updateSize() {
setSize([window.innerWidth, window.innerHeight]);
}
window.addEventListener('resize', updateSize);
updateSize();
return () => window.removeEventListener('resize', updateSize);
}, []);
return size;
}

View File

@@ -1,19 +1,8 @@
export { default as BlockFormControlLabel } from './BlockFormControlLabel';
export { default as FormActions } from './FormActions';
export { default as FormButton } from './FormButton';
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 * from './inputs';
export * from './layout';
export * from './loading';
export * from './routing';
export * from './upload';
export { default as SectionContent } from './SectionContent';
export { default as WebSocketFormLoader } from './WebSocketFormLoader';
export { default as ErrorButton } from './ErrorButton';
export { default as SingleUpload } from './SingleUpload';
export * from './RestFormLoader';
export * from './RestController';
export * from './WebSocketFormLoader';
export * from './WebSocketController';
export * from './WindowSize';
export { default as ButtonRow } from './ButtonRow';
export { default as MessageBox } from './MessageBox';

View File

@@ -1,5 +1,5 @@
import { FC } from 'react';
import { FormControlLabel, FormControlLabelProps } from '@material-ui/core';
import { FormControlLabel, FormControlLabelProps } from '@mui/material';
const BlockFormControlLabel: FC<FormControlLabelProps> = (props) => (
<div>

View File

@@ -0,0 +1,36 @@
import { FC, useState } from 'react';
import { IconButton, InputAdornment } from '@mui/material';
import VisibilityIcon from '@mui/icons-material/Visibility';
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import ValidatedTextField, { ValidatedTextFieldProps } from './ValidatedTextField';
type ValidatedPasswordFieldProps = Omit<ValidatedTextFieldProps, 'type'>;
const ValidatedPasswordField: FC<ValidatedPasswordFieldProps> = ({ InputProps, ...props }) => {
const [showPassword, setShowPassword] = useState<boolean>(false);
return (
<ValidatedTextField
{...props}
type={showPassword ? 'text' : 'password'}
InputProps={{
...InputProps,
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={() => setShowPassword(!showPassword)}
edge="end"
>
{showPassword ? <VisibilityIcon /> : <VisibilityOffIcon />}
</IconButton>
</InputAdornment>
)
}}
/>
);
};
export default ValidatedPasswordField;

View File

@@ -0,0 +1,24 @@
import { FC } from 'react';
import { ValidateFieldsError } from 'async-validator';
import { FormHelperText, TextField, TextFieldProps } from '@mui/material';
interface ValidatedFieldProps {
fieldErrors?: ValidateFieldsError;
name: string;
}
export type ValidatedTextFieldProps = ValidatedFieldProps & TextFieldProps;
const ValidatedTextField: FC<ValidatedTextFieldProps> = ({ fieldErrors, ...rest }) => {
const errors = fieldErrors && fieldErrors[rest.name];
const renderErrors = () => errors && errors.map((e, i) => <FormHelperText key={i}>{e.message}</FormHelperText>);
return (
<>
<TextField error={!!errors} {...rest} />
{renderErrors()}
</>
);
};
export default ValidatedTextField;

View File

@@ -0,0 +1,3 @@
export { default as BlockFormControlLabel } from './BlockFormControlLabel';
export { default as ValidatedPasswordField } from './ValidatedPasswordField';
export { default as ValidatedTextField } from './ValidatedTextField';

View File

@@ -0,0 +1,38 @@
import { FC, useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { Box, Toolbar } from '@mui/material';
import { PROJECT_NAME } from '../../api/env';
import { RequiredChildrenProps } from '../../utils';
import LayoutDrawer from './LayoutDrawer';
import LayoutAppBar from './LayoutAppBar';
import { LayoutContext } from './context';
export const DRAWER_WIDTH = 240;
const Layout: FC<RequiredChildrenProps> = ({ children }) => {
const [mobileOpen, setMobileOpen] = useState(false);
const [title, setTitle] = useState(PROJECT_NAME);
const { pathname } = useLocation();
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
useEffect(() => setMobileOpen(false), [pathname]);
return (
<LayoutContext.Provider value={{ title, setTitle }}>
<LayoutAppBar title={title} onToggleDrawer={handleDrawerToggle} />
<LayoutDrawer mobileOpen={mobileOpen} onClose={handleDrawerToggle} />
<Box component="main" sx={{ marginLeft: { md: `${DRAWER_WIDTH}px` } }}>
<Toolbar />
{children}
</Box>
</LayoutContext.Provider>
);
};
export default Layout;

View File

@@ -0,0 +1,50 @@
import { FC, useContext } from 'react';
import { AppBar, Box, IconButton, Toolbar, Typography } from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import LayoutAuthMenu from './LayoutAuthMenu';
import { FeaturesContext } from '../../contexts/features';
export const DRAWER_WIDTH = 240;
interface LayoutAppBarProps {
title: string;
onToggleDrawer: () => void;
}
const LayoutAppBar: FC<LayoutAppBarProps> = ({ title, onToggleDrawer }) => {
const { features } = useContext(FeaturesContext);
return (
<AppBar
position="fixed"
sx={{
width: { md: `calc(100% - ${DRAWER_WIDTH}px)` },
ml: { md: `${DRAWER_WIDTH}px` },
boxShadow: 'none',
backgroundColor: '#2e586a'
}}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={onToggleDrawer}
sx={{ mr: 2, display: { md: 'none' } }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
{title}
</Typography>
<Box flexGrow={1} />
{features.security && <LayoutAuthMenu />}
</Toolbar>
</AppBar>
);
};
export default LayoutAppBar;

View File

@@ -0,0 +1,73 @@
import { FC, useState, useContext } from 'react';
import { Box, Button, Divider, IconButton, Popover, Typography, Avatar, styled, TypographyProps } from '@mui/material';
import PersonIcon from '@mui/icons-material/Person';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import { AuthenticatedContext } from '../../contexts/authentication';
const ItemTypography = styled(Typography)<TypographyProps>({
maxWidth: '250px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
});
const LayoutAuthMenu: FC = () => {
const { me, signOut } = useContext(AuthenticatedContext);
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = anchorEl ? 'app-menu-popover' : undefined;
return (
<>
<IconButton id="open-auth-menu" sx={{ padding: 0 }} aria-describedby={id} color="inherit" onClick={handleClick}>
<AccountCircleIcon />
</IconButton>
<Popover
id="app-menu-popover"
sx={{ mt: 1 }}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center'
}}
>
<Box display="flex" flexDirection="row" alignItems="center" p={2}>
<Avatar sx={{ width: 80, height: 80 }}>
<PersonIcon fontSize="large" />
</Avatar>
<Box pl={2}>
<ItemTypography variant="h6">{me.username}</ItemTypography>
<ItemTypography variant="body1">{me.admin ? 'Admin User' : 'Guest User'}</ItemTypography>
</Box>
</Box>
<Divider />
<Box p={1.5}>
<Button variant="outlined" fullWidth color="primary" onClick={() => signOut(true)}>
Sign Out
</Button>
</Box>
</Popover>
</>
);
};
export default LayoutAuthMenu;

View File

@@ -0,0 +1,73 @@
import { FC } from 'react';
import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material';
import { PROJECT_NAME } from '../../api/env';
import LayoutMenu from './LayoutMenu';
import { DRAWER_WIDTH } from './Layout';
const LayoutDrawerLogo = styled('img')(({ theme }) => ({
[theme.breakpoints.down('sm')]: {
height: 24,
marginRight: theme.spacing(2)
},
[theme.breakpoints.up('sm')]: {
height: 36,
marginRight: theme.spacing(2)
}
}));
interface LayoutDrawerProps {
mobileOpen: boolean;
onClose: () => void;
}
const LayoutDrawer: FC<LayoutDrawerProps> = ({ mobileOpen, onClose }) => {
const drawer = (
<>
<Toolbar disableGutters>
<Box display="flex" alignItems="center" px={2}>
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
<Typography variant="h6" color="textPrimary">
{PROJECT_NAME}
</Typography>
</Box>
<Divider absolute />
</Toolbar>
<Divider />
<LayoutMenu />
</>
);
return (
<Box component="nav" sx={{ width: { md: DRAWER_WIDTH }, flexShrink: { md: 0 } }}>
<Drawer
variant="temporary"
open={mobileOpen}
onClose={onClose}
ModalProps={{
keepMounted: true
}}
sx={{
display: { xs: 'block', md: 'none' },
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: DRAWER_WIDTH }
}}
>
{drawer}
</Drawer>
<Drawer
variant="permanent"
sx={{
display: { xs: 'none', md: 'block' },
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: DRAWER_WIDTH }
}}
open
>
{drawer}
</Drawer>
</Box>
);
};
export default LayoutDrawer;

View File

@@ -0,0 +1,42 @@
import { FC, useContext } from 'react';
import { Divider, List } from '@mui/material';
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
import SettingsIcon from '@mui/icons-material/Settings';
import LockIcon from '@mui/icons-material/Lock';
import SettingsEthernetIcon from '@mui/icons-material/SettingsEthernet';
import { FeaturesContext } from '../../contexts/features';
import ProjectMenu from '../../project/ProjectMenu';
import LayoutMenuItem from './LayoutMenuItem';
import { AuthenticatedContext } from '../../contexts/authentication';
const LayoutMenu: FC = () => {
const { features } = useContext(FeaturesContext);
const authenticatedContext = useContext(AuthenticatedContext);
return (
<>
{features.project && (
<List disablePadding component="nav">
<ProjectMenu />
<Divider />
</List>
)}
<List disablePadding component="nav">
<LayoutMenuItem icon={SettingsEthernetIcon} label="Network Connection" to="/network" />
<LayoutMenuItem icon={SettingsInputAntennaIcon} label="Access Point" to="/ap" />
{features.ntp && <LayoutMenuItem icon={AccessTimeIcon} label="Network Time" to="/ntp" />}
{features.mqtt && <LayoutMenuItem icon={DeviceHubIcon} label="MQTT" to="/mqtt" />}
<LayoutMenuItem icon={LockIcon} label="Security" to="/security" disabled={!authenticatedContext.me.admin} />
<LayoutMenuItem icon={SettingsIcon} label="System" to="/system" />
</List>
</>
);
};
export default LayoutMenu;

View File

@@ -0,0 +1,32 @@
import { FC } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { ListItem, ListItemButton, ListItemIcon, ListItemText, SvgIconProps } from '@mui/material';
import { grey } from '@mui/material/colors';
import { routeMatches } from '../../utils';
interface LayoutMenuItemProps {
icon: React.ComponentType<SvgIconProps>;
label: string;
to: string;
disabled?: boolean;
}
const LayoutMenuItem: FC<LayoutMenuItemProps> = ({ icon: Icon, label, to, disabled }) => {
const { pathname } = useLocation();
return (
<ListItem disablePadding selected={routeMatches(to, pathname)}>
<ListItemButton component={Link} to={to} disabled={disabled}>
<ListItemIcon sx={{ color: grey[500] }}>
<Icon />
</ListItemIcon>
<ListItemText>{label}</ListItemText>
</ListItemButton>
</ListItem>
);
};
export default LayoutMenuItem;

View File

@@ -0,0 +1,25 @@
import { useRef, useEffect, createContext, useContext } from 'react';
export interface LayoutContextValue {
title: string;
setTitle: (title: string) => void;
}
const LayoutContextDefaultValue = {} as LayoutContextValue;
export const LayoutContext = createContext(LayoutContextDefaultValue);
export const useLayoutTitle = (myTitle: string) => {
const { title, setTitle } = useContext(LayoutContext);
const previousTitle = useRef(title);
useEffect(() => {
setTitle(myTitle);
}, [setTitle, myTitle]);
useEffect(
() => () => {
setTitle(previousTitle.current);
},
[setTitle]
);
};

View File

@@ -0,0 +1,2 @@
export * from './context';
export { default as Layout } from './Layout';

View File

@@ -0,0 +1,43 @@
import { FC } from 'react';
import { Box, Paper, Typography } from '@mui/material';
import WarningIcon from '@mui/icons-material/Warning';
interface ApplicationErrorProps {
message?: string;
}
const ApplicationError: FC<ApplicationErrorProps> = ({ message }) => (
<Box display="flex" height="100vh" justifyContent="center" flexDirection="column">
<Paper
elevation={10}
sx={{
textAlign: 'center',
padding: '280px 0 40px 0',
backgroundImage: 'url("/app/icon.png")',
backgroundRepeat: 'no-repeat',
backgroundPosition: '50% 40px',
backgroundSize: '200px auto',
width: '100%',
borderRadius: 0
}}
>
<Box display="flex" flexDirection="row" justifyContent="center" alignItems="center" mb={2}>
<WarningIcon fontSize="large" color="error" />
<Box ml={2}>
<Typography variant="h4">Application Error</Typography>
</Box>
</Box>
<Typography variant="subtitle1" gutterBottom>
Failed to configure the application, please refresh to try again.
</Typography>
{message && (
<Typography variant="subtitle2" gutterBottom>
{message}
</Typography>
)}
</Paper>
</Box>
);
export default ApplicationError;

View File

@@ -0,0 +1,38 @@
import { FC } from 'react';
import { Box, Button, CircularProgress, Typography } from '@mui/material';
import RefreshIcon from '@mui/icons-material/Refresh';
import { MessageBox } from '..';
interface FormLoaderProps {
message?: string;
errorMessage?: string;
onRetry?: () => void;
}
const FormLoader: FC<FormLoaderProps> = ({ errorMessage, onRetry, message = 'Loading…' }) => {
if (errorMessage) {
return (
<MessageBox my={2} level="error" message={errorMessage}>
{onRetry && (
<Button startIcon={<RefreshIcon />} variant="contained" color="error" onClick={onRetry}>
Retry
</Button>
)}
</MessageBox>
);
}
return (
<Box m={2} py={2} display="flex" alignItems="center" flexDirection="column">
<Box py={2}>
<CircularProgress size={100} />
</Box>
<Typography variant="h6" fontWeight={400} textAlign="center">
{message}
</Typography>
</Box>
);
};
export default FormLoader;

View File

@@ -0,0 +1,24 @@
import { FC } from 'react';
import { CircularProgress, Box, Typography, Theme } from '@mui/material';
interface LoadingSpinnerProps {
height?: number | string;
}
const LoadingSpinner: FC<LoadingSpinnerProps> = ({ height = '100%' }) => (
<Box display="flex" alignItems="center" justifyContent="center" flexDirection="column" padding={2} height={height}>
<CircularProgress
sx={(theme: Theme) => ({
margin: theme.spacing(4),
color: theme.palette.text.secondary
})}
size={100}
/>
<Typography variant="h4" color="textSecondary">
Loading&hellip;
</Typography>
</Box>
);
export default LoadingSpinner;

View File

@@ -0,0 +1,3 @@
export { default as ApplicationError } from './ApplicationError';
export { default as LoadingSpinner } from './LoadingSpinner';
export { default as FormLoader } from './FormLoader';

View File

@@ -0,0 +1,12 @@
import { FC, useContext } from 'react';
import { Navigate } from 'react-router-dom';
import { AuthenticatedContext } from '../../contexts/authentication';
import { RequiredChildrenProps } from '../../utils';
const RequireAdmin: FC<RequiredChildrenProps> = ({ children }) => {
const authenticatedContext = useContext(AuthenticatedContext);
return authenticatedContext.me.admin ? <>{children}</> : <Navigate replace to="/" />;
};
export default RequireAdmin;

View File

@@ -0,0 +1,32 @@
import { FC, useContext, useEffect } from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import {
AuthenticatedContext,
AuthenticatedContextValue,
AuthenticationContext
} from '../../contexts/authentication/context';
import { storeLoginRedirect } from '../../api/authentication';
import { RequiredChildrenProps } from '../../utils';
const RequireAuthenticated: FC<RequiredChildrenProps> = ({ children }) => {
const authenticationContext = useContext(AuthenticationContext);
const location = useLocation();
useEffect(() => {
if (!authenticationContext.me) {
storeLoginRedirect(location);
}
});
return authenticationContext.me ? (
<AuthenticatedContext.Provider value={authenticationContext as AuthenticatedContextValue}>
{children}
</AuthenticatedContext.Provider>
) : (
<Navigate to="/unauthorized" />
);
};
export default RequireAuthenticated;

View File

@@ -0,0 +1,16 @@
import { FC, useContext } from 'react';
import { Navigate } from 'react-router-dom';
import * as AuthenticationApi from '../../api/authentication';
import { AuthenticationContext } from '../../contexts/authentication';
import { RequiredChildrenProps } from '../../utils';
import { FeaturesContext } from '../../contexts/features';
const RequireUnauthenticated: FC<RequiredChildrenProps> = ({ children }) => {
const { features } = useContext(FeaturesContext);
const authenticationContext = useContext(AuthenticationContext);
return authenticationContext.me ? <Navigate to={AuthenticationApi.fetchLoginRedirect(features)} /> : <>{children}</>;
};
export default RequireUnauthenticated;

View File

@@ -0,0 +1,29 @@
import React, { FC } from 'react';
import { useNavigate } from 'react-router-dom';
import { Tabs, useMediaQuery, useTheme } from '@mui/material';
import { RequiredChildrenProps } from '../../utils';
interface RouterTabsProps extends RequiredChildrenProps {
value: string | false;
}
const RouterTabs: FC<RouterTabsProps> = ({ value, children }) => {
const navigate = useNavigate();
const theme = useTheme();
const smallDown = useMediaQuery(theme.breakpoints.down('sm'));
const handleTabChange = (event: React.ChangeEvent<{}>, path: string) => {
navigate(path);
};
return (
<Tabs value={value} onChange={handleTabChange} variant={smallDown ? 'scrollable' : 'fullWidth'}>
{children}
</Tabs>
);
};
export default RouterTabs;

View File

@@ -0,0 +1,6 @@
export { default as RouterTabs } from './RouterTabs';
export { default as RequireAdmin } from './RequireAdmin';
export { default as RequireAuthenticated } from './RequireAuthenticated';
export { default as RequireUnauthenticated } from './RequireUnauthenticated';
export * from './useRouterTab';

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