Compare commits
293 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b65866217a | ||
|
|
611e3b1243 | ||
|
|
84d3d42306 | ||
|
|
61e2739ef7 | ||
|
|
a3391afd27 | ||
|
|
ce9b7d1468 | ||
|
|
1b86314a14 | ||
|
|
b2a0519f83 | ||
|
|
3dec4bda8c | ||
|
|
5de529cbb2 | ||
|
|
4fd1d8e08a | ||
|
|
0847ccc602 | ||
|
|
969803569e | ||
|
|
6532abd870 | ||
|
|
07dabb4ceb | ||
|
|
1c6a683015 | ||
|
|
bb38458ac4 | ||
|
|
53de2ca25b | ||
|
|
5a9122f8be | ||
|
|
dc84f91044 | ||
|
|
4be6626470 | ||
|
|
7e4494aae1 | ||
|
|
50e54e6a1c | ||
|
|
30b111b986 | ||
|
|
a63f2e6131 | ||
|
|
51d487b938 | ||
|
|
96180c837d | ||
|
|
da20cf1ed2 | ||
|
|
bffad5e3c8 | ||
|
|
74287ebb99 | ||
|
|
8b40c92f7e | ||
|
|
5f6033cac1 | ||
|
|
24582407d4 | ||
|
|
22c9e3ee1f | ||
|
|
cb2c898b7e | ||
|
|
c0bf623266 | ||
|
|
18f22d3951 | ||
|
|
e2dad610b0 | ||
|
|
5ed3cbee2e | ||
|
|
27712badb6 | ||
|
|
72c032adff | ||
|
|
7197df9812 | ||
|
|
898e2e5f21 | ||
|
|
cde3b7541f | ||
|
|
822f55497e | ||
|
|
7c16870294 | ||
|
|
e368f422f4 | ||
|
|
9d9ac4ed9e | ||
|
|
d7576ebda1 | ||
|
|
fd810ff01c | ||
|
|
999a05f7ff | ||
|
|
a19f2f19c5 | ||
|
|
f54ccd40e6 | ||
|
|
5893487d4a | ||
|
|
3c8e20d4e4 | ||
|
|
bf68a5523b | ||
|
|
9c5c27152c | ||
|
|
c229371c68 | ||
|
|
805cef68a2 | ||
|
|
09addcb975 | ||
|
|
409d382ff9 | ||
|
|
27bfc14438 | ||
|
|
6c20a5f4f9 | ||
|
|
ffd61a9f67 | ||
|
|
4f2da0347c | ||
|
|
3c13c144d5 | ||
|
|
d4eaedef3d | ||
|
|
a8f1892d48 | ||
|
|
e347ac5742 | ||
|
|
5f2a9b093d | ||
|
|
e821e8d082 | ||
|
|
05f56be2d8 | ||
|
|
88f78f6541 | ||
|
|
234533f241 | ||
|
|
b1f72b0e3e | ||
|
|
a3022f6f20 | ||
|
|
95f7583511 | ||
|
|
578ba386e6 | ||
|
|
df7be9d11e | ||
|
|
72a59917fb | ||
|
|
9406c76e55 | ||
|
|
ea550b1656 | ||
|
|
d20741c0f0 | ||
|
|
91b7fd59d1 | ||
|
|
95e81ad824 | ||
|
|
316832fc4f | ||
|
|
23b6d81c47 | ||
|
|
8284520733 | ||
|
|
57f53818e1 | ||
|
|
7bca3fb2ed | ||
|
|
ae4a1358af | ||
|
|
95e3a11a11 | ||
|
|
f1a859c650 | ||
|
|
8d9fd95e85 | ||
|
|
452921d198 | ||
|
|
3e0f6f55fb | ||
|
|
0e480bbd94 | ||
|
|
7a079d866f | ||
|
|
af2710125e | ||
|
|
fb3de2e36d | ||
|
|
bf40222105 | ||
|
|
e1419edb15 | ||
|
|
131b936a69 | ||
|
|
5850a82d80 | ||
|
|
b76b6be3d1 | ||
|
|
b8f69eeaa8 | ||
|
|
d78fb53845 | ||
|
|
54889fec41 | ||
|
|
2c5c4d6e04 | ||
|
|
e6a44c9c82 | ||
|
|
7cba52d77e | ||
|
|
a79a67e4b2 | ||
|
|
01bace4048 | ||
|
|
d5f8419157 | ||
|
|
40a7026d4c | ||
|
|
47cb296cc4 | ||
|
|
be27033d41 | ||
|
|
3001a2d66f | ||
|
|
becaff0711 | ||
|
|
aaab9f409f | ||
|
|
068bb5cbeb | ||
|
|
337c07d7bc | ||
|
|
c387f65b4a | ||
|
|
df13081f97 | ||
|
|
50f6d0ab26 | ||
|
|
fb7bafdb87 | ||
|
|
b3472c3919 | ||
|
|
4f47712d52 | ||
|
|
547ccb96c9 | ||
|
|
7f3ff434ea | ||
|
|
aad4b0ade3 | ||
|
|
d587f44ec9 | ||
|
|
f30d3cf637 | ||
|
|
8fed47f39b | ||
|
|
48cedbd0fb | ||
|
|
752530a381 | ||
|
|
45fc6daa4a | ||
|
|
e17ce9c3b5 | ||
|
|
2657b9d1a5 | ||
|
|
b77a56ade2 | ||
|
|
4d5f588748 | ||
|
|
6582ba6317 | ||
|
|
b3453d9d02 | ||
|
|
e4b73140c8 | ||
|
|
235f789228 | ||
|
|
c029cf79f7 | ||
|
|
0b796a85a8 | ||
|
|
d8191f79a4 | ||
|
|
6a259f7cca | ||
|
|
22a2b92022 | ||
|
|
25616ae3b4 | ||
|
|
372aee30cd | ||
|
|
0c5023323a | ||
|
|
2d1126b9e4 | ||
|
|
3ecf92fa41 | ||
|
|
50befd8991 | ||
|
|
676268d7af | ||
|
|
f2457a7050 | ||
|
|
ded90dc4ce | ||
|
|
cddadcfae2 | ||
|
|
3113d392ac | ||
|
|
6433d5f744 | ||
|
|
f42c265714 | ||
|
|
d6711ac850 | ||
|
|
9d7c2de1d5 | ||
|
|
b1e1c44e77 | ||
|
|
85f54dd210 | ||
|
|
eea32ad134 | ||
|
|
4f24035082 | ||
|
|
0b0b1d9ca4 | ||
|
|
98828ed848 | ||
|
|
7b0f7cd32c | ||
|
|
67cb778039 | ||
|
|
9f49afae0a | ||
|
|
49c7c7aa2d | ||
|
|
42d89d1d10 | ||
|
|
24fae0d03e | ||
|
|
2c337f1d03 | ||
|
|
fcc521d5ed | ||
|
|
91005876eb | ||
|
|
da5b4aa79d | ||
|
|
ced440392b | ||
|
|
33d7ba1fda | ||
|
|
a6095fc305 | ||
|
|
84cc964a7a | ||
|
|
9e856b28a9 | ||
|
|
9378fdf2b6 | ||
|
|
6a134dda1f | ||
|
|
4f927ee571 | ||
|
|
b111869422 | ||
|
|
89a249eae4 | ||
|
|
7942d52843 | ||
|
|
b6310302d2 | ||
|
|
87774e73e1 | ||
|
|
548b5ff4a1 | ||
|
|
80fedf3fa3 | ||
|
|
8523cdffa3 | ||
|
|
29f2335935 | ||
|
|
eba6324d18 | ||
|
|
c3874d7c95 | ||
|
|
3086342d2b | ||
|
|
f836209249 | ||
|
|
d4b06cf0c0 | ||
|
|
54199affc1 | ||
|
|
4d88c6a90b | ||
|
|
906813b8f5 | ||
|
|
30d1e7ecb4 | ||
|
|
8fab4e29bf | ||
|
|
252554ea87 | ||
|
|
ba3d49172c | ||
|
|
6ef8ff757a | ||
|
|
fcc2c0b3de | ||
|
|
16e390f849 | ||
|
|
a31cf53863 | ||
|
|
09d8a6360b | ||
|
|
b1f59d4727 | ||
|
|
d8add7edcb | ||
|
|
1a71921fd6 | ||
|
|
6d12fff4fe | ||
|
|
769301c804 | ||
|
|
6a828e9ca5 | ||
|
|
2516d2d6de | ||
|
|
e92a3ad025 | ||
|
|
5686094151 | ||
|
|
915749dd69 | ||
|
|
258bc2b544 | ||
|
|
a9748f5b46 | ||
|
|
56f1c7946e | ||
|
|
88a427578f | ||
|
|
0ebd9e1fe1 | ||
|
|
bea0b4ba01 | ||
|
|
f8bee9b5c5 | ||
|
|
500e089b97 | ||
|
|
2adef52abf | ||
|
|
e01bfe1bdf | ||
|
|
418b1b3d68 | ||
|
|
401929d992 | ||
|
|
ee34dd72f7 | ||
|
|
b214d7a662 | ||
|
|
079a40cd32 | ||
|
|
adebea1561 | ||
|
|
28bf7c3225 | ||
|
|
78b2efd148 | ||
|
|
11590061a3 | ||
|
|
09847ee33c | ||
|
|
db34785be0 | ||
|
|
538a9cf642 | ||
|
|
d0346e436a | ||
|
|
442202d978 | ||
|
|
3b2a89d9f4 | ||
|
|
0bf366ac75 | ||
|
|
c62e73d21e | ||
|
|
2889899bfd | ||
|
|
f18ac2e48b | ||
|
|
1d259d14c8 | ||
|
|
b8c07e31cf | ||
|
|
3f27ed7b18 | ||
|
|
d7678b340f | ||
|
|
b697356465 | ||
|
|
1b9a2f21d2 | ||
|
|
e26451fcc0 | ||
|
|
17db542775 | ||
|
|
dd865a2db5 | ||
|
|
834c7cb87f | ||
|
|
a9a015cb5b | ||
|
|
94a71998a7 | ||
|
|
aa212924bc | ||
|
|
edef2f1bf6 | ||
|
|
912f53762e | ||
|
|
02c04c8f41 | ||
|
|
3ad90a6e34 | ||
|
|
0f83870db0 | ||
|
|
7529f3a73e | ||
|
|
77d559ac8c | ||
|
|
1311cad913 | ||
|
|
4d1ba9bede | ||
|
|
6b07651ed3 | ||
|
|
50ddfc0437 | ||
|
|
c0ac485772 | ||
|
|
8d4ae6971d | ||
|
|
88d0dfee2d | ||
|
|
551a479c6b | ||
|
|
3f85541c9a | ||
|
|
74cdb610d8 | ||
|
|
165dd3b418 | ||
|
|
7421f3e345 | ||
|
|
d00559bcd5 | ||
|
|
9774051fab | ||
|
|
c50692bae0 | ||
|
|
6348283001 | ||
|
|
1cbd34d94e | ||
|
|
f9d768a7a7 | ||
|
|
6c1bfccfaf |
152
.clang-tidy
Normal 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: ''
|
||||
2
.github/workflows/pre_release.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
title: ESP32 Development Build v${{steps.build_info.outputs.version}}
|
||||
title: Development Build v${{steps.build_info.outputs.version}}
|
||||
automatic_release_tag: "latest"
|
||||
prerelease: true
|
||||
files: |
|
||||
|
||||
4
.gitignore
vendored
@@ -27,4 +27,6 @@ emsesp
|
||||
/interface/build
|
||||
node_modules
|
||||
/interface/.eslintcache
|
||||
test.sh
|
||||
test.sh
|
||||
scripts/__pycache__
|
||||
.temp
|
||||
|
||||
68
CHANGELOG.md
@@ -5,6 +5,70 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
# Changelog
|
||||
|
||||
# [3.3.0] November 28 2021
|
||||
|
||||
## Added
|
||||
|
||||
- Add system commands for syslog level and watch [#98](https://github.com/emsesp/EMS-ESP32/issues/98)
|
||||
- Added pool data to telegrams 0x494 & 0x495 [#102](https://github.com/emsesp/EMS-ESP32/issues/102)
|
||||
- Add RC300 second summermode telegram [#108](https://github.com/emsesp/EMS-ESP32/issues/108)
|
||||
- Add support for the RC25 thermostat [#106](https://github.com/emsesp/EMS-ESP32/issues/106)
|
||||
- Add new command 'entities' for a device, e.g. http://ems-esp/api/boiler/entities to show the shortname, description and HA Entity name (if HA enabled) [#116](https://github.com/emsesp/EMS-ESP32/issues/116)
|
||||
- Support for Junkers program and remote (fb10/fb110) temperature
|
||||
- Home Assistant `state_class` attribute for Wh, kWh, W and KW [#129](https://github.com/emsesp/EMS-ESP32/issues/129)
|
||||
- Add current room influence for RC300 [#136](https://github.com/emsesp/EMS-ESP32/issues/136)
|
||||
- Added Home Assistant device_class to sensor entities
|
||||
- Added another Buderus RC10 thermostat with Product ID 65 [#160](https://github.com/emsesp/EMS-ESP32/issues/160)
|
||||
- Added support for mDNS [#161](https://github.com/emsesp/EMS-ESP32/issues/161)
|
||||
- Added last system ESP32 reset code to log (and `system info` output)
|
||||
- Firmware Checker in WebUI [#168](https://github.com/emsesp/EMS-ESP32/issues/168)
|
||||
- Added new MQTT setting for enabling 'response' topic
|
||||
- Support for non-standard Thermostats like Tado [#174](https://github.com/emsesp/EMS-ESP32/issues/174)
|
||||
- Include MQTT connection status in 'api/system/info'
|
||||
- Include Network status in 'api/system/info' and also the MQTT topic `info` [#202](https://github.com/emsesp/EMS-ESP32/issues/202)
|
||||
- Added Ethernet PHY module as an option in the Board Profile [#210](https://github.com/emsesp/EMS-ESP32/issues/210)
|
||||
|
||||
## Fixed
|
||||
|
||||
- MQTT reconnecting after WiFi reconnect [#99](https://github.com/emsesp/EMS-ESP32/issues/99)
|
||||
- Manually Controlling Solar Circuit [#107](https://github.com/emsesp/EMS-ESP32/issues/107)
|
||||
- Fix thermostat commands not defaulting to the master thermostat [#110](https://github.com/emsesp/EMS-ESP32/issues/110)
|
||||
- Enlarge parse-buffer for long names like `cylinderpumpmodulation`
|
||||
- MQTT not subscribing to all device entities [#166](https://github.com/emsesp/EMS-ESP32/issues/166)
|
||||
- Help fix issues with WebUI unable to fully load UI over Ethernet [#177](https://github.com/emsesp/EMS-ESP32/issues/177)
|
||||
- Shower alert never reset after limit reached when enabled [(PR #185)]
|
||||
- Remove HA entity entries when a device value goes dormant [#196](https://github.com/emsesp/EMS-ESP32/issues/196)
|
||||
- deciphering last error code dates on 0xC2 telegram [#204](https://github.com/emsesp/EMS-ESP32/issues/204)
|
||||
|
||||
## Changed
|
||||
|
||||
- Syslog BOM only for utf-8 messages [#91](https://github.com/emsesp/EMS-ESP32/issues/91)
|
||||
- Check for KM200 by device-id 0x48, remove tx-delay [#90](https://github.com/emsesp/EMS-ESP32/issues/90)
|
||||
- rename `fastheatupfactor` to `fastheatup` and add percent [#122](https://github.com/emsesp/EMS-ESP32/issues/122)
|
||||
- "unit" renamed to "uom" in API call to recall a Device Value
|
||||
- initial backend React changes to replace the class components (HOCs) with React Hooks
|
||||
- Use program-names instead of numbers
|
||||
- Boiler's maintenancemessage always published in MQTT (to prevent HA missing entity)
|
||||
- Unit of Measure 'times' added to MQTT Fails, Rx fails, Rx received, Tx fails, Tx reads & Tx writes
|
||||
- Improved API. Restful HTTP API works in the same way as MQTT calls
|
||||
- Removed settings for MQTT subscribe format [#173](https://github.com/emsesp/EMS-ESP32/issues/173)
|
||||
- Improve Nefit Moduline 200 functionality [#183](https://github.com/emsesp/EMS-ESP32/issues/183)
|
||||
- `status` in the MQTT heartbeat renamed to `bus_status`
|
||||
- Layout changes in the WebUI, showing stripped table rows in Dashboard
|
||||
- Alternative font for log window [#219](https://github.com/emsesp/EMS-ESP32/issues/219)
|
||||
|
||||
## **BREAKING CHANGES**
|
||||
|
||||
- API: "unit" renamed to "uom" in API call to recall a Device Value
|
||||
- HA: `sensor.boiler_boiler_temperature` renamed to `sensor.actual_boiler_temperature`
|
||||
- HA: `binary_sensor.boiler_ww_disinfecting` renamed to `binary_sensor.boiler_ww_disinfection`
|
||||
- HA: # removed from counts in MQTT Fails, Rx fails, Rx received, Tx fails, Tx reads & Tx writes
|
||||
- `txread` renamed to `txreads` and `txwrite` renamed to `txwrites` in MQTT heartbeat payload
|
||||
- 'dallas sensors' in api/system/info moved to the "System" section. Renamed "uptime (seconds)" and "reset reason"
|
||||
- `status` in the MQTT heartbeat renamed to `bus_status`
|
||||
|
||||
# [3.2.1] August 8 2021
|
||||
|
||||
## Added
|
||||
@@ -62,7 +126,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
# [3.1.1] June 26 2021
|
||||
|
||||
## Changed
|
||||
## Added
|
||||
|
||||
- new command called `commands` which lists all available commands. `ems-esp/api/{device}/commands`
|
||||
- More Home Assistant icons to match the UOMs
|
||||
@@ -84,7 +148,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
# [3.1.0] May 4 2021
|
||||
|
||||
## Changed
|
||||
## Added
|
||||
|
||||
- Mock API to simulate an ESP, for testing web
|
||||
- Able to write values from the Web UI
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## Added
|
||||
|
||||
## Fixed
|
||||
|
||||
## Changed
|
||||
|
||||
2
Makefile
@@ -33,7 +33,7 @@ CXX_STANDARD := -std=c++11
|
||||
#----------------------------------------------------------------------
|
||||
# Defined Symbols
|
||||
#----------------------------------------------------------------------
|
||||
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_DEFAULT_BOARD_PROFILE=\"LOLIN\"
|
||||
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_DEFAULT_BOARD_PROFILE=\"LOLIN\"
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Sources & Files
|
||||
|
||||
325
interface/package-lock.json
generated
@@ -8,13 +8,13 @@
|
||||
"name": "emsesp-react",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.4",
|
||||
"@material-ui/core": "^4.12.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@msgpack/msgpack": "^2.7.0",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/node": "^15.0.1",
|
||||
"@types/react": "^17.0.4",
|
||||
"@types/react-dom": "^17.0.3",
|
||||
"@types/lodash": "^4.14.172",
|
||||
"@types/node": "^12.20.20",
|
||||
"@types/react": "^17.0.19",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react-material-ui-form-validator": "^2.1.0",
|
||||
"@types/react-router": "^5.1.13",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
@@ -35,7 +35,7 @@
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"sockette": "^2.0.6",
|
||||
"typescript": "4.2.4",
|
||||
"typescript": "4.3.5",
|
||||
"zlib": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -2050,13 +2050,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@material-ui/core": {
|
||||
"version": "4.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.4.tgz",
|
||||
"integrity": "sha512-oqb+lJ2Dl9HXI9orc6/aN8ZIAMkeThufA5iZELf2LQeBn2NtjVilF5D2w7e9RpntAzDb4jK5DsVhkfOvFY/8fg==",
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
|
||||
"integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@material-ui/system": "^4.11.3",
|
||||
"@material-ui/system": "^4.12.1",
|
||||
"@material-ui/types": "5.1.0",
|
||||
"@material-ui/utils": "^4.11.2",
|
||||
"@types/react-transition-group": "^4.2.0",
|
||||
@@ -2137,9 +2137,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@material-ui/system": {
|
||||
"version": "4.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.3.tgz",
|
||||
"integrity": "sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==",
|
||||
"version": "4.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz",
|
||||
"integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/utils": "^4.11.2",
|
||||
@@ -2148,6 +2148,20 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/material-ui"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.6 || ^17.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@material-ui/types": {
|
||||
@@ -2639,9 +2653,9 @@
|
||||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.168",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz",
|
||||
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q=="
|
||||
"version": "4.14.173",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.173.tgz",
|
||||
"integrity": "sha512-vv0CAYoaEjCw/mLy96GBTnRoZrSxkGE0BKzKimdR8P3OzrNYNvBgtW7p055A+E8C31vXNUhWKoFCbhq7gbyhFg=="
|
||||
},
|
||||
"node_modules/@types/material-ui": {
|
||||
"version": "0.21.8",
|
||||
@@ -2658,9 +2672,9 @@
|
||||
"integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
|
||||
"integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA=="
|
||||
"version": "12.20.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.25.tgz",
|
||||
"integrity": "sha512-hcTWqk7DR/HrN9Xe7AlJwuCaL13Vcd9/g/T54YrJz4Q3ESM5mr33YCzW2bOfzSIc3aZMeGBvbLGvgN6mIJ0I5Q=="
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
@@ -2688,9 +2702,9 @@
|
||||
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "17.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
|
||||
"integrity": "sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==",
|
||||
"version": "17.0.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.22.tgz",
|
||||
"integrity": "sha512-kq/BMeaAVLJM6Pynh8C2rnr/drCK+/5ksH0ch9asz+8FW3DscYCIEFtCeYTFeIx/ubvOsMXmRfy7qEJ76gM96A==",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
@@ -2706,9 +2720,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "17.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
|
||||
"integrity": "sha512-4NnJbCeWE+8YBzupn/YrJxZ8VnjcJq5iR1laqQ1vkpQgBiA7bwk0Rp24fxsdNinzJY2U+HHS4dJJDPdoMjdJ7w==",
|
||||
"version": "17.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz",
|
||||
"integrity": "sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@@ -4339,6 +4353,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@@ -4764,9 +4787,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cacheable-request/node_modules/normalize-url": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
|
||||
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
|
||||
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -8419,6 +8442,12 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/filesize": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
|
||||
@@ -8860,6 +8889,19 @@
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
@@ -13252,6 +13294,12 @@
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
|
||||
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
|
||||
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.1.23",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
|
||||
@@ -14247,9 +14295,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
@@ -14499,9 +14547,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "7.0.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
|
||||
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
|
||||
"version": "7.0.36",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
|
||||
"integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
@@ -14509,6 +14557,10 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-attribute-case-insensitive": {
|
||||
@@ -16948,9 +17000,9 @@
|
||||
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
|
||||
},
|
||||
"node_modules/resolve-url-loader": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.3.tgz",
|
||||
"integrity": "sha512-WbDSNFiKPPLem1ln+EVTE+bFUBdTTytfQZWbmghroaFNFaAVmGq0Saqw6F/306CwgPXsGwXVxbODE+3xAo/YbA==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.4.tgz",
|
||||
"integrity": "sha512-D3sQ04o0eeQEySLrcz4DsX3saHfsr8/N6tfhblxgZKXxMT2Louargg12oGNfoTRLV09GXhVUe5/qgA5vdgNigg==",
|
||||
"dependencies": {
|
||||
"adjust-sourcemap-loader": "3.0.0",
|
||||
"camelcase": "5.3.1",
|
||||
@@ -16958,7 +17010,7 @@
|
||||
"convert-source-map": "1.7.0",
|
||||
"es6-iterator": "2.0.3",
|
||||
"loader-utils": "1.2.3",
|
||||
"postcss": "7.0.21",
|
||||
"postcss": "7.0.36",
|
||||
"rework": "1.0.1",
|
||||
"rework-visit": "1.0.0",
|
||||
"source-map": "0.6.1"
|
||||
@@ -17007,19 +17059,6 @@
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-url-loader/node_modules/postcss": {
|
||||
"version": "7.0.21",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz",
|
||||
"integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==",
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
"supports-color": "^6.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-url-loader/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -17028,17 +17067,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-url-loader/node_modules/supports-color": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
|
||||
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/responselike": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
|
||||
@@ -18669,9 +18697,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
|
||||
"integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
|
||||
"version": "6.1.11",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
|
||||
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
@@ -19249,9 +19277,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
|
||||
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
||||
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -19570,9 +19598,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
|
||||
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
@@ -20086,6 +20114,24 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/fsevents": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
|
||||
"deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"dependencies": {
|
||||
"bindings": "^1.5.0",
|
||||
"nan": "^2.12.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/glob-parent": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||
@@ -23150,13 +23196,13 @@
|
||||
}
|
||||
},
|
||||
"@material-ui/core": {
|
||||
"version": "4.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.4.tgz",
|
||||
"integrity": "sha512-oqb+lJ2Dl9HXI9orc6/aN8ZIAMkeThufA5iZELf2LQeBn2NtjVilF5D2w7e9RpntAzDb4jK5DsVhkfOvFY/8fg==",
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
|
||||
"integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@material-ui/system": "^4.11.3",
|
||||
"@material-ui/system": "^4.12.1",
|
||||
"@material-ui/types": "5.1.0",
|
||||
"@material-ui/utils": "^4.11.2",
|
||||
"@types/react-transition-group": "^4.2.0",
|
||||
@@ -23200,9 +23246,9 @@
|
||||
}
|
||||
},
|
||||
"@material-ui/system": {
|
||||
"version": "4.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.3.tgz",
|
||||
"integrity": "sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==",
|
||||
"version": "4.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz",
|
||||
"integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/utils": "^4.11.2",
|
||||
@@ -23611,9 +23657,9 @@
|
||||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.168",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz",
|
||||
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q=="
|
||||
"version": "4.14.173",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.173.tgz",
|
||||
"integrity": "sha512-vv0CAYoaEjCw/mLy96GBTnRoZrSxkGE0BKzKimdR8P3OzrNYNvBgtW7p055A+E8C31vXNUhWKoFCbhq7gbyhFg=="
|
||||
},
|
||||
"@types/material-ui": {
|
||||
"version": "0.21.8",
|
||||
@@ -23630,9 +23676,9 @@
|
||||
"integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
|
||||
"integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA=="
|
||||
"version": "12.20.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.25.tgz",
|
||||
"integrity": "sha512-hcTWqk7DR/HrN9Xe7AlJwuCaL13Vcd9/g/T54YrJz4Q3ESM5mr33YCzW2bOfzSIc3aZMeGBvbLGvgN6mIJ0I5Q=="
|
||||
},
|
||||
"@types/normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
@@ -23660,9 +23706,9 @@
|
||||
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "17.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
|
||||
"integrity": "sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==",
|
||||
"version": "17.0.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.22.tgz",
|
||||
"integrity": "sha512-kq/BMeaAVLJM6Pynh8C2rnr/drCK+/5ksH0ch9asz+8FW3DscYCIEFtCeYTFeIx/ubvOsMXmRfy7qEJ76gM96A==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
@@ -23685,9 +23731,9 @@
|
||||
}
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"version": "17.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
|
||||
"integrity": "sha512-4NnJbCeWE+8YBzupn/YrJxZ8VnjcJq5iR1laqQ1vkpQgBiA7bwk0Rp24fxsdNinzJY2U+HHS4dJJDPdoMjdJ7w==",
|
||||
"version": "17.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz",
|
||||
"integrity": "sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@@ -25105,6 +25151,15 @@
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@@ -25476,9 +25531,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"normalize-url": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
|
||||
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
|
||||
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -28464,6 +28519,12 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"optional": true
|
||||
},
|
||||
"filesize": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
|
||||
@@ -28843,6 +28904,12 @@
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
@@ -32383,6 +32450,12 @@
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
|
||||
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
|
||||
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
|
||||
"optional": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.1.23",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
|
||||
@@ -33197,9 +33270,9 @@
|
||||
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
@@ -33389,9 +33462,9 @@
|
||||
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
|
||||
},
|
||||
"postcss": {
|
||||
"version": "7.0.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
|
||||
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
|
||||
"version": "7.0.36",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
|
||||
"integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
@@ -35421,9 +35494,9 @@
|
||||
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
|
||||
},
|
||||
"resolve-url-loader": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.3.tgz",
|
||||
"integrity": "sha512-WbDSNFiKPPLem1ln+EVTE+bFUBdTTytfQZWbmghroaFNFaAVmGq0Saqw6F/306CwgPXsGwXVxbODE+3xAo/YbA==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.4.tgz",
|
||||
"integrity": "sha512-D3sQ04o0eeQEySLrcz4DsX3saHfsr8/N6tfhblxgZKXxMT2Louargg12oGNfoTRLV09GXhVUe5/qgA5vdgNigg==",
|
||||
"requires": {
|
||||
"adjust-sourcemap-loader": "3.0.0",
|
||||
"camelcase": "5.3.1",
|
||||
@@ -35431,7 +35504,7 @@
|
||||
"convert-source-map": "1.7.0",
|
||||
"es6-iterator": "2.0.3",
|
||||
"loader-utils": "1.2.3",
|
||||
"postcss": "7.0.21",
|
||||
"postcss": "7.0.36",
|
||||
"rework": "1.0.1",
|
||||
"rework-visit": "1.0.0",
|
||||
"source-map": "0.6.1"
|
||||
@@ -35465,28 +35538,10 @@
|
||||
"json5": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "7.0.21",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz",
|
||||
"integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
"supports-color": "^6.1.0"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
|
||||
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -36905,9 +36960,9 @@
|
||||
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA=="
|
||||
},
|
||||
"tar": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
|
||||
"integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
|
||||
"version": "6.1.11",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
|
||||
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
|
||||
"requires": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
@@ -37376,9 +37431,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
|
||||
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg=="
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
|
||||
"integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA=="
|
||||
},
|
||||
"unbox-primitive": {
|
||||
"version": "1.0.1",
|
||||
@@ -37647,9 +37702,9 @@
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
|
||||
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
@@ -38299,6 +38354,16 @@
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"bindings": "^1.5.0",
|
||||
"nan": "^2.12.1"
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.4",
|
||||
"@material-ui/core": "^4.12.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@msgpack/msgpack": "^2.7.0",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/node": "^15.0.1",
|
||||
"@types/react": "^17.0.4",
|
||||
"@types/react-dom": "^17.0.3",
|
||||
"@types/lodash": "^4.14.172",
|
||||
"@types/node": "^12.20.20",
|
||||
"@types/react": "^17.0.19",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react-material-ui-form-validator": "^2.1.0",
|
||||
"@types/react-router": "^5.1.13",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
@@ -30,7 +30,7 @@
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"sockette": "^2.0.6",
|
||||
"typescript": "4.2.4",
|
||||
"typescript": "4.3.5",
|
||||
"zlib": "^1.0.5"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -3,12 +3,12 @@ import { Component } from 'react';
|
||||
import { CssBaseline } from '@material-ui/core';
|
||||
import {
|
||||
MuiThemeProvider,
|
||||
createMuiTheme,
|
||||
createTheme,
|
||||
StylesProvider
|
||||
} from '@material-ui/core/styles';
|
||||
import { blueGrey, orange, red, green } from '@material-ui/core/colors';
|
||||
|
||||
const theme = createMuiTheme({
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
type: 'dark',
|
||||
primary: {
|
||||
|
||||
@@ -22,25 +22,13 @@ interface UnauthenticatedRouteProps
|
||||
| React.ComponentType<any>;
|
||||
}
|
||||
|
||||
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
|
||||
|
||||
class UnauthenticatedRoute extends Route<UnauthenticatedRouteProps> {
|
||||
public render() {
|
||||
const {
|
||||
authenticationContext,
|
||||
component: Component,
|
||||
features,
|
||||
...rest
|
||||
} = this.props;
|
||||
const renderComponent: RenderComponent = (props) => {
|
||||
if (authenticationContext.me) {
|
||||
return <Redirect to={Authentication.fetchLoginRedirect(features)} />;
|
||||
}
|
||||
if (Component) {
|
||||
return <Component {...props} />;
|
||||
}
|
||||
};
|
||||
return <Route {...rest} render={renderComponent} />;
|
||||
const { authenticationContext, features, ...rest } = this.props;
|
||||
if (authenticationContext.me) {
|
||||
return <Redirect to={Authentication.fetchLoginRedirect(features)} />;
|
||||
}
|
||||
return <Route {...rest} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
56
interface/src/components/FormLoader.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import { FC } from 'react';
|
||||
|
||||
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
|
||||
import { Button, LinearProgress, Typography } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
loadingSettings: {
|
||||
margin: theme.spacing(0.5)
|
||||
},
|
||||
loadingSettingsDetails: {
|
||||
margin: theme.spacing(4),
|
||||
textAlign: 'center'
|
||||
},
|
||||
button: {
|
||||
marginRight: theme.spacing(2),
|
||||
marginTop: theme.spacing(2)
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
interface FormLoaderProps {
|
||||
errorMessage?: string;
|
||||
loadData: () => void;
|
||||
}
|
||||
|
||||
const FormLoader: FC<FormLoaderProps> = ({ errorMessage, loadData }) => {
|
||||
const classes = useStyles();
|
||||
if (errorMessage) {
|
||||
return (
|
||||
<div className={classes.loadingSettings}>
|
||||
<Typography variant="h6" className={classes.loadingSettingsDetails}>
|
||||
{errorMessage}
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
className={classes.button}
|
||||
onClick={loadData}
|
||||
>
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={classes.loadingSettings}>
|
||||
<LinearProgress className={classes.loadingSettingsDetails} />
|
||||
<Typography variant="h6" className={classes.loadingSettingsDetails}>
|
||||
Loading…
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormLoader;
|
||||
@@ -5,6 +5,7 @@ export { default as HighlightAvatar } from './HighlightAvatar';
|
||||
export { default as MenuAppBar } from './MenuAppBar';
|
||||
export { default as PasswordValidator } from './PasswordValidator';
|
||||
export { default as RestFormLoader } from './RestFormLoader';
|
||||
export { default as FormLoader } from './FormLoader';
|
||||
export { default as SectionContent } from './SectionContent';
|
||||
export { default as WebSocketFormLoader } from './WebSocketFormLoader';
|
||||
export { default as ErrorButton } from './ErrorButton';
|
||||
|
||||
@@ -1,58 +1,31 @@
|
||||
import { Component } from 'react';
|
||||
import { FC } from 'react';
|
||||
|
||||
import { Features } from './types';
|
||||
import { FeaturesContext } from './FeaturesContext';
|
||||
import FullScreenLoading from '../components/FullScreenLoading';
|
||||
import ApplicationError from '../components/ApplicationError';
|
||||
import { FEATURES_ENDPOINT } from '../api';
|
||||
import { useRest } from '../hooks';
|
||||
|
||||
interface FeaturesWrapperState {
|
||||
features?: Features;
|
||||
error?: string;
|
||||
}
|
||||
import { Features } from './types';
|
||||
import { FeaturesContext } from './FeaturesContext';
|
||||
|
||||
class FeaturesWrapper extends Component<{}, FeaturesWrapperState> {
|
||||
state: FeaturesWrapperState = {};
|
||||
const FeaturesWrapper: FC = ({ children }) => {
|
||||
const { data: features, errorMessage: error } = useRest<Features>({
|
||||
endpoint: FEATURES_ENDPOINT
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchFeaturesDetails();
|
||||
if (features) {
|
||||
return (
|
||||
<FeaturesContext.Provider value={{ features }}>
|
||||
{children}
|
||||
</FeaturesContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
fetchFeaturesDetails = () => {
|
||||
fetch(FEATURES_ENDPOINT)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw Error('Unexpected status code: ' + response.status);
|
||||
}
|
||||
})
|
||||
.then((features) => {
|
||||
this.setState({ features });
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setState({ error: error.message });
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { features, error } = this.state;
|
||||
if (features) {
|
||||
return (
|
||||
<FeaturesContext.Provider
|
||||
value={{
|
||||
features
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
</FeaturesContext.Provider>
|
||||
);
|
||||
}
|
||||
if (error) {
|
||||
return <ApplicationError error={error} />;
|
||||
}
|
||||
return <FullScreenLoading />;
|
||||
if (error) {
|
||||
return <ApplicationError error={error} />;
|
||||
}
|
||||
}
|
||||
|
||||
return <FullScreenLoading />;
|
||||
};
|
||||
|
||||
export default FeaturesWrapper;
|
||||
|
||||
2
interface/src/hooks/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as useRest } from './useRest';
|
||||
export { default as useAuthorizedRest } from './useAuthorizedRest';
|
||||
12
interface/src/hooks/useAuthorizedRest.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { redirectingAuthorizedFetch } from '../authentication';
|
||||
import useRest, { RestRequestOptions } from './useRest';
|
||||
|
||||
const useAuthorizedRest = <D>({
|
||||
endpoint
|
||||
}: Omit<RestRequestOptions, 'fetchFunction'>) =>
|
||||
useRest<D>({
|
||||
endpoint,
|
||||
fetchFunction: redirectingAuthorizedFetch
|
||||
});
|
||||
|
||||
export default useAuthorizedRest;
|
||||
79
interface/src/hooks/useRest.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useSnackbar } from 'notistack';
|
||||
|
||||
export interface RestRequestOptions {
|
||||
endpoint: string;
|
||||
fetchFunction?: typeof fetch;
|
||||
}
|
||||
|
||||
const useRest = <D>({
|
||||
endpoint,
|
||||
fetchFunction = fetch
|
||||
}: RestRequestOptions) => {
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
const [data, setData] = useState<D>();
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
|
||||
const handleError = useCallback(
|
||||
(error: any) => {
|
||||
const errorMessage = error.message || 'Unknown error';
|
||||
enqueueSnackbar('Problem fetching: ' + errorMessage, {
|
||||
variant: 'error'
|
||||
});
|
||||
setErrorMessage(errorMessage);
|
||||
},
|
||||
[enqueueSnackbar]
|
||||
);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
setData(undefined);
|
||||
setErrorMessage(undefined);
|
||||
try {
|
||||
const response = await fetchFunction(endpoint);
|
||||
if (response.status !== 200) {
|
||||
throw Error('Invalid status code: ' + response.status);
|
||||
}
|
||||
setData(await response.json());
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
}
|
||||
}, [handleError, fetchFunction, endpoint]);
|
||||
|
||||
const save = useCallback(
|
||||
async (data: D) => {
|
||||
setSaving(true);
|
||||
setErrorMessage(undefined);
|
||||
try {
|
||||
const response = await fetchFunction(endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
if (response.status !== 200) {
|
||||
throw Error('Invalid status code: ' + response.status);
|
||||
}
|
||||
enqueueSnackbar('Update successful.', { variant: 'success' });
|
||||
setData(await response.json());
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
},
|
||||
[enqueueSnackbar, handleError, fetchFunction, endpoint]
|
||||
);
|
||||
|
||||
const saveData = () => data && save(data);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, [loadData]);
|
||||
|
||||
return { loadData, saveData, saving, setData, data, errorMessage } as const;
|
||||
};
|
||||
|
||||
export default useRest;
|
||||
@@ -187,19 +187,16 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||
<MenuItem value={1}>Nested on a single topic</MenuItem>
|
||||
<MenuItem value={2}>As individual topics</MenuItem>
|
||||
</SelectValidator>
|
||||
<SelectValidator
|
||||
name="subscribe_format"
|
||||
label="Subscribe Format"
|
||||
value={data.subscribe_format}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('subscribe_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={0}>General device topic</MenuItem>
|
||||
<MenuItem value={1}>Individual topics, main heating circuit</MenuItem>
|
||||
<MenuItem value={2}>Individual topics, all heating circuits</MenuItem>
|
||||
</SelectValidator>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={data.send_response}
|
||||
onChange={handleValueChange('send_response')}
|
||||
value="send_response"
|
||||
/>
|
||||
}
|
||||
label="Publish command output to a 'response' topic"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
|
||||
@@ -39,5 +39,5 @@ export interface MqttSettings {
|
||||
ha_enabled: boolean;
|
||||
ha_climate_format: number;
|
||||
nested_format: number;
|
||||
subscribe_format: number;
|
||||
send_response: boolean;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,7 @@ export const BOARD_PROFILES: BoardProfiles = {
|
||||
NODEMCU: 'NodeMCU 32S',
|
||||
'MH-ET': 'MH-ET Live D1 Mini',
|
||||
LOLIN: 'Lolin D32',
|
||||
OLIMEX: 'Olimex ESP32-EVB',
|
||||
TLK110: 'Generic Ethernet (TLK110)',
|
||||
LAN8720: 'Generic Ethernet (LAN8720)'
|
||||
OLIMEX: 'Olimex ESP32-EVB'
|
||||
};
|
||||
|
||||
export function boardProfileSelectItems() {
|
||||
|
||||
@@ -59,19 +59,32 @@ export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + 'deviceData';
|
||||
export const WRITE_VALUE_ENDPOINT = ENDPOINT_ROOT + 'writeValue';
|
||||
export const WRITE_SENSOR_ENDPOINT = ENDPOINT_ROOT + 'writeSensor';
|
||||
|
||||
const StyledTableCell = withStyles((theme: Theme) =>
|
||||
const StyledTableRow = withStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
head: {
|
||||
backgroundColor: theme.palette.common.black,
|
||||
color: theme.palette.common.white
|
||||
root: {
|
||||
'&:nth-child(even)': {
|
||||
backgroundColor: '#4e4e4e'
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.info.light
|
||||
}
|
||||
},
|
||||
body: {
|
||||
fontSize: 14
|
||||
|
||||
selected: {
|
||||
backgroundColor: theme.palette.common.white
|
||||
}
|
||||
})
|
||||
)(TableCell);
|
||||
)(TableRow);
|
||||
|
||||
const CustomTooltip = withStyles((theme: Theme) => ({
|
||||
const StyledTableRowHeader = withStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
head: {
|
||||
backgroundColor: theme.palette.common.black
|
||||
}
|
||||
})
|
||||
)(TableRow);
|
||||
|
||||
const StyledTooltip = withStyles((theme: Theme) => ({
|
||||
tooltip: {
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
color: 'white',
|
||||
@@ -82,10 +95,10 @@ const CustomTooltip = withStyles((theme: Theme) => ({
|
||||
}))(Tooltip);
|
||||
|
||||
function compareDevices(a: Device, b: Device) {
|
||||
if (a.type < b.type) {
|
||||
if (a.t < b.t) {
|
||||
return -1;
|
||||
}
|
||||
if (a.type > b.type) {
|
||||
if (a.t > b.t) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -104,6 +117,9 @@ type EMSESPDataFormProps = RestFormProps<EMSESPData> &
|
||||
AuthenticatedContextProps &
|
||||
WithWidthProps;
|
||||
|
||||
const pluralize = (count: number, noun: string, suffix = 's') =>
|
||||
` ${Intl.NumberFormat().format(count)} ${noun}${count !== 1 ? suffix : ''} `;
|
||||
|
||||
export const formatDuration = (duration_min: number) => {
|
||||
const { days, hours, minutes } = parseMilliseconds(duration_min * 60000);
|
||||
let formatted = '';
|
||||
@@ -119,30 +135,29 @@ export const formatDuration = (duration_min: number) => {
|
||||
return formatted;
|
||||
};
|
||||
|
||||
const pluralize = (count: number, noun: string, suffix = 's') =>
|
||||
` ${count} ${noun}${count !== 1 ? suffix : ''} `;
|
||||
|
||||
function formatValue(value: any, uom: number, digit: number) {
|
||||
function formatValue(value: any, uom: number) {
|
||||
switch (uom) {
|
||||
case DeviceValueUOM.HOURS:
|
||||
return value ? formatDuration(value * 60) : '0 hours';
|
||||
case DeviceValueUOM.MINUTES:
|
||||
return value ? formatDuration(value) : '0 minutes';
|
||||
case DeviceValueUOM.NONE:
|
||||
case DeviceValueUOM.LIST:
|
||||
if (typeof value === 'number') {
|
||||
return new Intl.NumberFormat().format(value);
|
||||
}
|
||||
return value;
|
||||
case DeviceValueUOM.NUM:
|
||||
return new Intl.NumberFormat().format(value);
|
||||
case DeviceValueUOM.BOOLEAN:
|
||||
return value ? 'on' : 'off';
|
||||
case DeviceValueUOM.DEGREES:
|
||||
// always show with one decimal place
|
||||
return (
|
||||
new Intl.NumberFormat(undefined, {
|
||||
minimumFractionDigits: digit
|
||||
minimumFractionDigits: 1
|
||||
}).format(value) +
|
||||
' ' +
|
||||
DeviceValueUOM_s[uom]
|
||||
);
|
||||
case DeviceValueUOM.TIMES:
|
||||
case DeviceValueUOM.SECONDS:
|
||||
return pluralize(value, DeviceValueUOM_s[uom]);
|
||||
default:
|
||||
return (
|
||||
new Intl.NumberFormat().format(value) + ' ' + DeviceValueUOM_s[uom]
|
||||
@@ -217,7 +232,9 @@ class EMSESPDataForm extends Component<
|
||||
};
|
||||
|
||||
sendCommand = (dv: DeviceValue) => {
|
||||
this.setState({ edit_devicevalue: dv });
|
||||
if (dv.c && this.props.authenticatedContext.me.admin) {
|
||||
this.setState({ edit_devicevalue: dv });
|
||||
}
|
||||
};
|
||||
|
||||
handleSensorChange = (name: keyof Sensor) => (
|
||||
@@ -243,10 +260,10 @@ class EMSESPDataForm extends Component<
|
||||
body: JSON.stringify({
|
||||
// because input field with type=number doens't like negative values, force it here
|
||||
sensor: {
|
||||
no: edit_Sensor?.no,
|
||||
id: edit_Sensor?.id,
|
||||
temp: edit_Sensor?.temp,
|
||||
offset: Number(edit_Sensor?.offset)
|
||||
no: edit_Sensor?.n, // no
|
||||
id: edit_Sensor?.i, // id
|
||||
temp: edit_Sensor?.t, // temp
|
||||
offset: Number(edit_Sensor?.o) // offset
|
||||
}
|
||||
}),
|
||||
headers: {
|
||||
@@ -283,7 +300,9 @@ class EMSESPDataForm extends Component<
|
||||
};
|
||||
|
||||
sendSensor = (sn: Sensor) => {
|
||||
this.setState({ edit_Sensor: sn });
|
||||
if (this.props.authenticatedContext.me.admin) {
|
||||
this.setState({ edit_Sensor: sn });
|
||||
}
|
||||
};
|
||||
|
||||
noDevices = () => {
|
||||
@@ -298,7 +317,7 @@ class EMSESPDataForm extends Component<
|
||||
return (this.state.deviceData?.data || []).length === 0;
|
||||
};
|
||||
|
||||
renderDeviceItems() {
|
||||
renderDevices() {
|
||||
const { width, data } = this.props;
|
||||
return (
|
||||
<TableContainer>
|
||||
@@ -309,26 +328,24 @@ class EMSESPDataForm extends Component<
|
||||
{!this.noDevices() && (
|
||||
<Table
|
||||
size="small"
|
||||
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
|
||||
padding={isWidthDown('xs', width!) ? 'none' : 'normal'}
|
||||
>
|
||||
<TableBody>
|
||||
{data.devices.sort(compareDevices).map((device) => (
|
||||
<TableRow
|
||||
hover
|
||||
key={device.id}
|
||||
onClick={() => this.handleRowClick(device.id)}
|
||||
key={device.i}
|
||||
onClick={() => this.handleRowClick(device.i)}
|
||||
>
|
||||
<TableCell>
|
||||
<CustomTooltip
|
||||
<StyledTooltip
|
||||
title={
|
||||
'DeviceID:0x' +
|
||||
(
|
||||
'00' + device.deviceid.toString(16).toUpperCase()
|
||||
).slice(-2) +
|
||||
('00' + device.d.toString(16).toUpperCase()).slice(-2) +
|
||||
' ProductID:' +
|
||||
device.productid +
|
||||
device.p +
|
||||
' Version:' +
|
||||
device.version
|
||||
device.v
|
||||
}
|
||||
placement="right-end"
|
||||
>
|
||||
@@ -337,12 +354,12 @@ class EMSESPDataForm extends Component<
|
||||
size="small"
|
||||
variant="outlined"
|
||||
>
|
||||
{device.type}
|
||||
{device.t}
|
||||
</Button>
|
||||
</CustomTooltip>
|
||||
</StyledTooltip>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{device.brand + ' ' + device.name}{' '}
|
||||
{device.b + ' ' + device.n}{' '}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
@@ -367,7 +384,7 @@ class EMSESPDataForm extends Component<
|
||||
);
|
||||
}
|
||||
|
||||
renderSensorItems() {
|
||||
renderSensorData() {
|
||||
const { data } = this.props;
|
||||
const me = this.props.authenticatedContext.me;
|
||||
return (
|
||||
@@ -377,43 +394,38 @@ class EMSESPDataForm extends Component<
|
||||
Sensors
|
||||
</Typography>
|
||||
{!this.noSensors() && (
|
||||
<Table size="small" padding="default">
|
||||
<Table size="small" padding="normal">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<StyledTableCell
|
||||
padding="checkbox"
|
||||
style={{ width: 18 }}
|
||||
></StyledTableCell>
|
||||
<StyledTableCell>Sensor #</StyledTableCell>
|
||||
<StyledTableCell align="left">ID / Name</StyledTableCell>
|
||||
<StyledTableCell align="right">Temperature</StyledTableCell>
|
||||
</TableRow>
|
||||
<StyledTableRowHeader>
|
||||
<TableCell padding="checkbox" style={{ width: 18 }}></TableCell>
|
||||
<TableCell>Dallas Sensor #</TableCell>
|
||||
<TableCell align="left">ID / Name</TableCell>
|
||||
<TableCell align="right">Temperature</TableCell>
|
||||
</StyledTableRowHeader>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{data.sensors.map((sensorData) => (
|
||||
<TableRow key={sensorData.no} hover>
|
||||
<TableCell padding="checkbox" style={{ width: 18 }}>
|
||||
<StyledTableRow
|
||||
key={sensorData.n}
|
||||
onClick={() => this.sendSensor(sensorData)}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
{me.admin && (
|
||||
<CustomTooltip title="edit" placement="left-end">
|
||||
<IconButton
|
||||
edge="start"
|
||||
size="small"
|
||||
aria-label="Edit"
|
||||
onClick={() => this.sendSensor(sensorData)}
|
||||
>
|
||||
<StyledTooltip title="edit" placement="left-end">
|
||||
<IconButton edge="start" size="small" aria-label="Edit">
|
||||
<EditIcon color="primary" fontSize="small" />
|
||||
</IconButton>
|
||||
</CustomTooltip>
|
||||
</StyledTooltip>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell component="th" scope="row">
|
||||
{sensorData.no}
|
||||
{sensorData.n}
|
||||
</TableCell>
|
||||
<TableCell align="left">{sensorData.id}</TableCell>
|
||||
<TableCell align="left">{sensorData.i}</TableCell>
|
||||
<TableCell align="right">
|
||||
{formatValue(sensorData.temp, DeviceValueUOM.DEGREES, 1)}
|
||||
{formatValue(sensorData.t, DeviceValueUOM.DEGREES)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</StyledTableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
@@ -421,7 +433,7 @@ class EMSESPDataForm extends Component<
|
||||
{this.noSensors() && (
|
||||
<Box color="warning.main" p={0} mt={0} mb={0}>
|
||||
<Typography variant="body1">
|
||||
<i>no connected Dallas temperature sensors were detected</i>
|
||||
<i>no Dallas temperature sensors were detected</i>
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
@@ -429,25 +441,28 @@ class EMSESPDataForm extends Component<
|
||||
);
|
||||
}
|
||||
|
||||
renderAnalog() {
|
||||
renderAnalogData() {
|
||||
const { data } = this.props;
|
||||
return (
|
||||
<TableContainer>
|
||||
<p></p>
|
||||
{data.analog > 0 && (
|
||||
<Table size="small" padding="default">
|
||||
<Table size="small" padding="normal">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<StyledTableCell>Sensortype</StyledTableCell>
|
||||
<StyledTableCell align="right">Value</StyledTableCell>
|
||||
</TableRow>
|
||||
<StyledTableRowHeader>
|
||||
<TableCell padding="normal" style={{ width: 18 }}></TableCell>
|
||||
<TableCell>Sensor Type</TableCell>
|
||||
<TableCell align="right">Value</TableCell>
|
||||
</StyledTableRowHeader>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell padding="normal"> </TableCell>
|
||||
<TableCell component="th" scope="row">
|
||||
Analog Input
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{formatValue(data.analog, DeviceValueUOM.MV, 0)}
|
||||
{formatValue(data.analog, DeviceValueUOM.MV)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
@@ -553,7 +568,6 @@ class EMSESPDataForm extends Component<
|
||||
renderDeviceData() {
|
||||
const { deviceData } = this.state;
|
||||
const { width } = this.props;
|
||||
const me = this.props.authenticatedContext.me;
|
||||
|
||||
if (this.noDevices()) {
|
||||
return;
|
||||
@@ -568,22 +582,24 @@ class EMSESPDataForm extends Component<
|
||||
<p></p>
|
||||
<Box bgcolor="info.main" p={1} mt={1} mb={1}>
|
||||
<Typography variant="body1" color="initial">
|
||||
{deviceData.name}
|
||||
{deviceData.type} Data
|
||||
</Typography>
|
||||
</Box>
|
||||
{!this.noDeviceData() && (
|
||||
<TableContainer>
|
||||
<Table
|
||||
size="small"
|
||||
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
|
||||
padding={isWidthDown('xs', width!) ? 'none' : 'normal'}
|
||||
>
|
||||
<TableHead></TableHead>
|
||||
<TableBody>
|
||||
{deviceData.data.map((item, i) => (
|
||||
<TableRow hover key={i}>
|
||||
<StyledTableRow
|
||||
key={i}
|
||||
onClick={() => this.sendCommand(item)}
|
||||
>
|
||||
<TableCell padding="checkbox" style={{ width: 18 }}>
|
||||
{item.c && me.admin && (
|
||||
<CustomTooltip
|
||||
{item.c && this.props.authenticatedContext.me.admin && (
|
||||
<StyledTooltip
|
||||
title="change value"
|
||||
placement="left-end"
|
||||
>
|
||||
@@ -591,20 +607,19 @@ class EMSESPDataForm extends Component<
|
||||
edge="start"
|
||||
size="small"
|
||||
aria-label="Edit"
|
||||
onClick={() => this.sendCommand(item)}
|
||||
>
|
||||
<EditIcon color="primary" fontSize="small" />
|
||||
</IconButton>
|
||||
</CustomTooltip>
|
||||
</StyledTooltip>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell padding="none" component="th" scope="row">
|
||||
{item.n}
|
||||
</TableCell>
|
||||
<TableCell padding="none" align="right">
|
||||
{formatValue(item.v, item.u, 0)}
|
||||
{formatValue(item.v, item.u)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</StyledTableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
@@ -613,7 +628,7 @@ class EMSESPDataForm extends Component<
|
||||
{this.noDeviceData() && (
|
||||
<Box color="warning.main" p={0} mt={0} mb={0}>
|
||||
<Typography variant="body1">
|
||||
<i>No data available for this device</i>
|
||||
<i>no data available for this device</i>
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
@@ -626,10 +641,10 @@ class EMSESPDataForm extends Component<
|
||||
return (
|
||||
<Fragment>
|
||||
<br></br>
|
||||
{this.renderDeviceItems()}
|
||||
{this.renderDevices()}
|
||||
{this.renderDeviceData()}
|
||||
{this.renderSensorItems()}
|
||||
{this.renderAnalog()}
|
||||
{this.renderSensorData()}
|
||||
{this.renderAnalogData()}
|
||||
<br></br>
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
<Box flexGrow={1} padding={1}>
|
||||
|
||||
@@ -93,6 +93,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
rx_gpio: json.rx_gpio,
|
||||
tx_gpio: json.tx_gpio,
|
||||
pbutton_gpio: json.pbutton_gpio,
|
||||
phy_type: json.phy_type,
|
||||
board_profile: event.target.value
|
||||
});
|
||||
this.setState({ processing: false });
|
||||
@@ -113,15 +114,15 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
<Box bgcolor="info.main" p={2} mt={2} mb={2}>
|
||||
<Typography variant="body1">
|
||||
<i>
|
||||
Refer to the
|
||||
visit the
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://emsesp.github.io/docs/#/Configure-firmware32?id=ems-esp-settings"
|
||||
href="https://emsesp.github.io/docs/#/Configure-firmware?id=ems-esp-settings"
|
||||
color="primary"
|
||||
>
|
||||
{' documentation'}
|
||||
{'online documentation'}
|
||||
</Link>
|
||||
for information on each setting
|
||||
for details explaining each setting
|
||||
</i>
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -135,7 +136,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item xs={5}>
|
||||
@@ -172,30 +173,6 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
<MenuItem value={0x12}>Alarm Module (0x12)</MenuItem>
|
||||
</SelectValidator>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<TextValidator
|
||||
validators={[
|
||||
'required',
|
||||
'isNumber',
|
||||
'minNumber:0',
|
||||
'maxNumber:120'
|
||||
]}
|
||||
errorMessages={[
|
||||
'Tx delay is required',
|
||||
'Must be a number',
|
||||
'Must be 0 or higher',
|
||||
'Max value is 120'
|
||||
]}
|
||||
name="tx_delay"
|
||||
label="Tx start delay (seconds)"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.tx_delay}
|
||||
type="number"
|
||||
onChange={handleValueChange('tx_delay')}
|
||||
margin="normal"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<br></br>
|
||||
@@ -232,7 +209,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item xs={4}>
|
||||
@@ -330,7 +307,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
'Not a valid GPIO'
|
||||
]}
|
||||
name="dallas_gpio"
|
||||
label="Dallas GPIO (0=none)"
|
||||
label="Dallas GPIO (0=disabled)"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.dallas_gpio}
|
||||
@@ -356,7 +333,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
'Not a valid GPIO'
|
||||
]}
|
||||
name="led_gpio"
|
||||
label="LED GPIO (0=none)"
|
||||
label="LED GPIO (0=disabled)"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.led_gpio}
|
||||
@@ -365,6 +342,21 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
margin="normal"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<SelectValidator
|
||||
name="phy_type"
|
||||
label="PHY Module Type"
|
||||
value={data.phy_type}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('phy_type')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={0}>No Ethernet</MenuItem>
|
||||
<MenuItem value={1}>LAN8720</MenuItem>
|
||||
<MenuItem value={2}>TLK110</MenuItem>
|
||||
</SelectValidator>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
@@ -433,7 +425,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
container
|
||||
spacing={0}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<BlockFormControlLabel
|
||||
@@ -467,7 +459,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item xs={4}>
|
||||
@@ -538,7 +530,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item xs={5}>
|
||||
|
||||
@@ -58,18 +58,18 @@ class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
|
||||
<TableContainer>
|
||||
<Table
|
||||
size="small"
|
||||
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
|
||||
padding={isWidthDown('xs', width!) ? 'none' : 'normal'}
|
||||
>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell># Telegrams Received</TableCell>
|
||||
<TableCell>Telegrams Received</TableCell>
|
||||
<TableCell align="right">
|
||||
{formatNumber(data.rx_received)} (quality{' '}
|
||||
{data.rx_quality}%)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell># Telegrams Sent</TableCell>
|
||||
<TableCell>Telegrams Sent</TableCell>
|
||||
<TableCell align="right">
|
||||
{formatNumber(data.tx_sent)} (quality {data.tx_quality}
|
||||
%)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export interface EMSESPSettings {
|
||||
tx_mode: number;
|
||||
tx_delay: number;
|
||||
ems_bus_id: number;
|
||||
syslog_enabled: boolean;
|
||||
syslog_level: number;
|
||||
@@ -12,6 +11,7 @@ export interface EMSESPSettings {
|
||||
shower_alert: boolean;
|
||||
rx_gpio: number;
|
||||
tx_gpio: number;
|
||||
phy_type: number;
|
||||
dallas_gpio: number;
|
||||
dallas_parasite: boolean;
|
||||
led_gpio: number;
|
||||
@@ -42,20 +42,20 @@ export interface EMSESPStatus {
|
||||
}
|
||||
|
||||
export interface Device {
|
||||
id: number;
|
||||
type: string;
|
||||
brand: string;
|
||||
name: string;
|
||||
deviceid: number;
|
||||
productid: number;
|
||||
version: string;
|
||||
i: number; // id
|
||||
t: string; // type
|
||||
b: string; // brand
|
||||
n: string; // name
|
||||
d: number; // deviceid
|
||||
p: number; // productid
|
||||
v: string; // version
|
||||
}
|
||||
|
||||
export interface Sensor {
|
||||
no: number;
|
||||
id: string;
|
||||
temp: number;
|
||||
offset: number;
|
||||
n: number; // np
|
||||
i: string; // id
|
||||
t: number; // temp
|
||||
o: number; // offset
|
||||
}
|
||||
|
||||
export interface EMSESPData {
|
||||
@@ -65,15 +65,15 @@ export interface EMSESPData {
|
||||
}
|
||||
|
||||
export interface DeviceValue {
|
||||
v: any;
|
||||
u: number;
|
||||
n: string;
|
||||
c: string;
|
||||
l: string[];
|
||||
v: any; // value, in any format
|
||||
u: number; // uom
|
||||
n: string; // name
|
||||
c: string; // command
|
||||
l: string[]; // list
|
||||
}
|
||||
|
||||
export interface EMSESPDeviceData {
|
||||
name: string;
|
||||
type: string;
|
||||
data: DeviceValue[];
|
||||
}
|
||||
|
||||
@@ -93,10 +93,9 @@ export enum DeviceValueUOM {
|
||||
KB,
|
||||
SECONDS,
|
||||
DBM,
|
||||
NUM,
|
||||
BOOLEAN,
|
||||
LIST,
|
||||
MV
|
||||
MV,
|
||||
TIMES,
|
||||
OCLOCK
|
||||
}
|
||||
|
||||
export const DeviceValueUOM_s = [
|
||||
@@ -113,10 +112,9 @@ export const DeviceValueUOM_s = [
|
||||
'kW',
|
||||
'W',
|
||||
'KB',
|
||||
'seconds',
|
||||
'second',
|
||||
'dBm',
|
||||
'number',
|
||||
'on/off',
|
||||
'',
|
||||
'mV'
|
||||
'mV',
|
||||
'time',
|
||||
"o'clock"
|
||||
];
|
||||
|
||||
@@ -43,7 +43,7 @@ class SensorForm extends React.Component<SensorFormProps> {
|
||||
open
|
||||
>
|
||||
<DialogTitle id="user-form-dialog-title">
|
||||
Editing Sensor #{sensor.no}
|
||||
Editing Sensor #{sensor.n}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<TextValidator
|
||||
@@ -51,8 +51,8 @@ class SensorForm extends React.Component<SensorFormProps> {
|
||||
errorMessages={['Not a valid name']}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={sensor.id}
|
||||
onChange={handleSensorChange('id')}
|
||||
value={sensor.i}
|
||||
onChange={handleSensorChange('i')}
|
||||
margin="normal"
|
||||
label="Name"
|
||||
name="id"
|
||||
@@ -67,12 +67,12 @@ class SensorForm extends React.Component<SensorFormProps> {
|
||||
label="Custom Offset (°C)"
|
||||
name="offset"
|
||||
type="number"
|
||||
value={sensor.offset}
|
||||
value={sensor.o}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
InputProps={{ inputProps: { min: '-5', max: '5', step: '0.1' } }}
|
||||
margin="normal"
|
||||
onChange={handleSensorChange('offset')}
|
||||
onChange={handleSensorChange('o')}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { FormButton } from '../components';
|
||||
import { DeviceValue, DeviceValueUOM, DeviceValueUOM_s } from './EMSESPtypes';
|
||||
import { DeviceValue, DeviceValueUOM_s } from './EMSESPtypes';
|
||||
|
||||
interface ValueFormProps {
|
||||
devicevalue: DeviceValue;
|
||||
@@ -25,7 +25,6 @@ interface ValueFormProps {
|
||||
data: keyof DeviceValue
|
||||
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
class ValueForm extends React.Component<ValueFormProps> {
|
||||
formRef: RefObject<any> = React.createRef();
|
||||
|
||||
@@ -51,7 +50,7 @@ class ValueForm extends React.Component<ValueFormProps> {
|
||||
>
|
||||
<DialogTitle id="user-form-dialog-title">Change Value</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
{devicevalue.u === DeviceValueUOM.LIST && (
|
||||
{devicevalue.l && (
|
||||
<TextField
|
||||
id="outlined-select-value"
|
||||
select
|
||||
@@ -66,34 +65,19 @@ class ValueForm extends React.Component<ValueFormProps> {
|
||||
))}
|
||||
</TextField>
|
||||
)}
|
||||
{devicevalue.u !== DeviceValueUOM.BOOLEAN &&
|
||||
devicevalue.u !== DeviceValueUOM.LIST && (
|
||||
<OutlinedInput
|
||||
id="value"
|
||||
value={devicevalue.v}
|
||||
autoFocus
|
||||
fullWidth
|
||||
onChange={handleValueChange('v')}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
{DeviceValueUOM_s[devicevalue.u]}
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{devicevalue.u === DeviceValueUOM.BOOLEAN && (
|
||||
<TextField
|
||||
id="selected-value"
|
||||
select
|
||||
{!devicevalue.l && (
|
||||
<OutlinedInput
|
||||
id="value"
|
||||
value={devicevalue.v}
|
||||
autoFocus
|
||||
fullWidth
|
||||
onChange={handleValueChange('v')}
|
||||
variant="outlined"
|
||||
>
|
||||
<MenuItem value="true">on</MenuItem>
|
||||
<MenuItem value="false">off</MenuItem>
|
||||
</TextField>
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
{DeviceValueUOM_s[devicevalue.u]}
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<FormHelperText>{devicevalue.n}</FormHelperText>
|
||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={4} mb={0}>
|
||||
|
||||
@@ -150,7 +150,7 @@ class ManageUsersForm extends React.Component<
|
||||
<ValidatorForm onSubmit={this.onSubmit}>
|
||||
<Table
|
||||
size="small"
|
||||
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
|
||||
padding={isWidthDown('xs', width!) ? 'none' : 'normal'}
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
@@ -197,7 +197,7 @@ class ManageUsersForm extends React.Component<
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell align="center" padding="default">
|
||||
<TableCell align="center" padding="normal">
|
||||
<Button
|
||||
startIcon={<PersonAddIcon />}
|
||||
variant="contained"
|
||||
|
||||
@@ -34,8 +34,8 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||
},
|
||||
entry: {
|
||||
color: '#bbbbbb',
|
||||
fontFamily: 'Courier New, monospace',
|
||||
fontSize: '13px',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '14px',
|
||||
letterSpacing: 'normal',
|
||||
whiteSpace: 'nowrap'
|
||||
},
|
||||
@@ -65,9 +65,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||
const LogEventConsole: FC<LogEventConsoleProps> = (props) => {
|
||||
useWindowSize();
|
||||
const classes = useStyles({ topOffset, leftOffset });
|
||||
const { events, compact, level } = props;
|
||||
|
||||
const filter_events = events.filter((e) => e.l <= level);
|
||||
const { events, compact } = props;
|
||||
|
||||
const styleLevel = (level: LogLevel) => {
|
||||
switch (level) {
|
||||
@@ -124,7 +122,7 @@ const LogEventConsole: FC<LogEventConsoleProps> = (props) => {
|
||||
|
||||
return (
|
||||
<Box id="log-window" className={classes.console}>
|
||||
{filter_events.map((e) => (
|
||||
{events.map((e) => (
|
||||
<div className={classes.entry} key={e.i}>
|
||||
<span>{e.t}</span>
|
||||
{compact && <span>{paddedLevelLabel(e.l, compact)} </span>}
|
||||
|
||||
@@ -45,6 +45,7 @@ interface LogEventControllerState {
|
||||
compact: boolean;
|
||||
level: number;
|
||||
max_messages: number;
|
||||
last_id: number;
|
||||
}
|
||||
|
||||
type LogEventControllerProps = RestControllerProps<LogSettings>;
|
||||
@@ -62,7 +63,8 @@ class LogEventController extends Component<
|
||||
events: [],
|
||||
compact: false,
|
||||
level: 6,
|
||||
max_messages: 25
|
||||
max_messages: 25,
|
||||
last_id: 0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,6 +90,11 @@ class LogEventController extends Component<
|
||||
this.setState({
|
||||
compact: checked
|
||||
});
|
||||
this.send_data(
|
||||
this.state.level,
|
||||
this.state.max_messages,
|
||||
checked as boolean
|
||||
);
|
||||
};
|
||||
|
||||
fetchLog = () => {
|
||||
@@ -118,7 +125,11 @@ class LogEventController extends Component<
|
||||
throw Error('Unexpected status code: ' + response.status);
|
||||
})
|
||||
.then((json) => {
|
||||
this.setState({ level: json.level, max_messages: json.max_messages });
|
||||
this.setState({
|
||||
level: json.level,
|
||||
max_messages: json.max_messages,
|
||||
compact: json.compact
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorMessage = error.message || 'Unknown error';
|
||||
@@ -148,7 +159,10 @@ class LogEventController extends Component<
|
||||
const rawData = event.data;
|
||||
if (typeof rawData === 'string' || rawData instanceof String) {
|
||||
const event = JSON.parse(rawData as string) as LogEvent;
|
||||
this.setState((state) => ({ events: [...state.events, event] }));
|
||||
if (event.i > this.state.last_id) {
|
||||
this.setState({ last_id: event.i });
|
||||
this.setState((state) => ({ events: [...state.events, event] }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -159,22 +173,27 @@ class LogEventController extends Component<
|
||||
this.setState({
|
||||
max_messages: value as number
|
||||
});
|
||||
this.send_data(this.state.level, value as number);
|
||||
this.send_data(this.state.level, value as number, this.state.compact);
|
||||
};
|
||||
|
||||
changeLevel = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
this.setState({
|
||||
level: parseInt(event.target.value)
|
||||
});
|
||||
this.send_data(parseInt(event.target.value), this.state.max_messages);
|
||||
this.send_data(
|
||||
parseInt(event.target.value),
|
||||
this.state.max_messages,
|
||||
this.state.compact
|
||||
);
|
||||
};
|
||||
|
||||
send_data = (level: number, max_messages: number) => {
|
||||
send_data = (level: number, max_messages: number, compact: boolean) => {
|
||||
redirectingAuthorizedFetch(LOG_SETTINGS_ENDPOINT, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
level: level,
|
||||
max_messages: max_messages
|
||||
max_messages: max_messages,
|
||||
compact: compact
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -206,29 +225,27 @@ class LogEventController extends Component<
|
||||
case LogLevel.DEBUG:
|
||||
return 'D';
|
||||
case LogLevel.TRACE:
|
||||
return 'TRACE';
|
||||
return 'T';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
onDownload = () => {
|
||||
const { events, level } = this.state;
|
||||
const { events } = this.state;
|
||||
let result = '';
|
||||
for (const i in events) {
|
||||
if (events[i].l <= level) {
|
||||
result +=
|
||||
events[i].t +
|
||||
' ' +
|
||||
this.levelLabel(events[i].l) +
|
||||
' ' +
|
||||
events[i].i +
|
||||
': [' +
|
||||
events[i].n +
|
||||
'] ' +
|
||||
events[i].m +
|
||||
'\n';
|
||||
}
|
||||
result +=
|
||||
events[i].t +
|
||||
' ' +
|
||||
this.levelLabel(events[i].l) +
|
||||
' ' +
|
||||
events[i].i +
|
||||
': [' +
|
||||
events[i].n +
|
||||
'] ' +
|
||||
events[i].m +
|
||||
'\n';
|
||||
}
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute(
|
||||
@@ -250,7 +267,7 @@ class LogEventController extends Component<
|
||||
container
|
||||
spacing={3}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid item xs={2}>
|
||||
@@ -280,11 +297,12 @@ class LogEventController extends Component<
|
||||
marks={[
|
||||
{ value: 25, label: '25' },
|
||||
{ value: 50, label: '50' },
|
||||
{ value: 75, label: '75' }
|
||||
{ value: 75, label: '75' },
|
||||
{ value: 100, label: '100' }
|
||||
]}
|
||||
step={25}
|
||||
min={25}
|
||||
max={75}
|
||||
max={100}
|
||||
onChange={this.changeMaxMessages}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
@@ -33,15 +33,19 @@ import {
|
||||
AuthenticatedContextProps,
|
||||
withAuthenticatedContext
|
||||
} from '../authentication';
|
||||
|
||||
import { RestFormProps, FormButton, ErrorButton } from '../components';
|
||||
import { FACTORY_RESET_ENDPOINT, RESTART_ENDPOINT } from '../api';
|
||||
|
||||
import { SystemStatus, EspPlatform } from './types';
|
||||
|
||||
import VersionCheck from './VersionCheck';
|
||||
|
||||
interface SystemStatusFormState {
|
||||
confirmRestart: boolean;
|
||||
confirmFactoryReset: boolean;
|
||||
processing: boolean;
|
||||
currentVersion?: string;
|
||||
}
|
||||
|
||||
type SystemStatusFormProps = AuthenticatedContextProps &
|
||||
@@ -61,6 +65,16 @@ class SystemStatusForm extends Component<
|
||||
processing: false
|
||||
};
|
||||
|
||||
onVersionCheck = (version: string) => {
|
||||
this.setState({ currentVersion: version });
|
||||
};
|
||||
|
||||
closeVersionCheck = () => {
|
||||
this.setState({
|
||||
currentVersion: undefined
|
||||
});
|
||||
};
|
||||
|
||||
createListItems() {
|
||||
const { data } = this.props;
|
||||
return (
|
||||
@@ -75,7 +89,14 @@ class SystemStatusForm extends Component<
|
||||
primary="EMS-ESP Version"
|
||||
secondary={'v' + data.emsesp_version}
|
||||
/>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => this.onVersionCheck(data.emsesp_version)}
|
||||
>
|
||||
Check for updates
|
||||
</Button>
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
@@ -304,9 +325,16 @@ class SystemStatusForm extends Component<
|
||||
|
||||
render() {
|
||||
const me = this.props.authenticatedContext.me;
|
||||
const { currentVersion } = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
<List>{this.createListItems()}</List>
|
||||
{currentVersion && (
|
||||
<VersionCheck
|
||||
currentVersion={currentVersion}
|
||||
onClose={this.closeVersionCheck}
|
||||
/>
|
||||
)}
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
<Box flexGrow={1} padding={1}>
|
||||
<FormButton
|
||||
|
||||
211
interface/src/system/VersionCheck.tsx
Normal file
@@ -0,0 +1,211 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Box,
|
||||
Link,
|
||||
LinearProgress,
|
||||
Typography
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { FormButton } from '../components';
|
||||
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
||||
|
||||
export const VERSIONCHECK_ENDPOINT =
|
||||
'https://api.github.com/repos/emsesp/EMS-ESP32/releases/latest';
|
||||
|
||||
export const VERSIONCHECK_DEV_ENDPOINT =
|
||||
'https://api.github.com/repos/emsesp/EMS-ESP32/releases/tags/latest';
|
||||
|
||||
export const uploadURL = window.location.origin + '/system/upload';
|
||||
|
||||
interface VersionCheckProps extends WithSnackbarProps {
|
||||
currentVersion: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
interface VersionCheckState {
|
||||
latestVersion?: string;
|
||||
latestVersionUrl?: string;
|
||||
latestVersionChangelog?: string;
|
||||
latestDevVersion?: string;
|
||||
latestDevVersionUrl?: string;
|
||||
latestDevVersionChangelog?: string;
|
||||
}
|
||||
|
||||
class VersionCheck extends React.Component<
|
||||
VersionCheckProps,
|
||||
VersionCheckState
|
||||
> {
|
||||
state: VersionCheckState = {};
|
||||
|
||||
componentDidMount() {
|
||||
fetch(VERSIONCHECK_ENDPOINT)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw Error(
|
||||
'Unable to get version information. Check internet connection. (' +
|
||||
response.status +
|
||||
')'
|
||||
);
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
this.setState({
|
||||
latestVersion: data.name,
|
||||
latestVersionUrl: data.assets[1].browser_download_url,
|
||||
latestVersionChangelog: data.html_url
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.props.enqueueSnackbar(
|
||||
error.message || 'Problem getting response',
|
||||
{ variant: 'error' }
|
||||
);
|
||||
this.setState({ latestVersion: undefined });
|
||||
this.props.onClose();
|
||||
});
|
||||
|
||||
fetch(VERSIONCHECK_DEV_ENDPOINT)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw Error(
|
||||
'Unable to get version information. Check internet connection. (' +
|
||||
response.status +
|
||||
')'
|
||||
);
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
this.setState({
|
||||
latestDevVersion: data.name.split(/\s+/).splice(-1),
|
||||
latestDevVersionUrl: data.assets[1].browser_download_url,
|
||||
latestDevVersionChangelog: data.assets[0].browser_download_url
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.props.enqueueSnackbar(
|
||||
error.message || 'Problem getting response',
|
||||
{ variant: 'error' }
|
||||
);
|
||||
this.setState({ latestDevVersion: undefined });
|
||||
this.props.onClose();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onClose, currentVersion } = this.props;
|
||||
const {
|
||||
latestVersion,
|
||||
latestVersionUrl,
|
||||
latestDevVersion,
|
||||
latestDevVersionUrl,
|
||||
latestVersionChangelog,
|
||||
latestDevVersionChangelog
|
||||
} = this.state;
|
||||
return (
|
||||
<Dialog
|
||||
onClose={onClose}
|
||||
aria-labelledby="version-check-dialog-title"
|
||||
open
|
||||
fullWidth
|
||||
maxWidth="sm"
|
||||
>
|
||||
<DialogTitle id="version-check-dialog-title">
|
||||
Firmware Update Check
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
{latestVersion ? (
|
||||
<Fragment>
|
||||
<Box
|
||||
bgcolor="primary.main"
|
||||
color="primary.contrastText"
|
||||
p={2}
|
||||
mt={2}
|
||||
mb={2}
|
||||
>
|
||||
<Typography variant="body1">
|
||||
You are currently running EMS-ESP version{' '}
|
||||
<b>v{currentVersion}</b>
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box mt={2} mb={2}>
|
||||
The latest <u>stable</u> version is <b>{latestVersion}</b>
|
||||
(
|
||||
<Link
|
||||
target="_blank"
|
||||
href={latestVersionChangelog}
|
||||
color="primary"
|
||||
>
|
||||
{'release notes'}
|
||||
</Link>
|
||||
) (
|
||||
<Link target="_blank" href={latestVersionUrl} color="primary">
|
||||
{'download'}
|
||||
</Link>
|
||||
)
|
||||
</Box>
|
||||
<Box mt={2} mb={2}>
|
||||
The latest <u>development</u> version is
|
||||
<b>{latestDevVersion}</b>
|
||||
(
|
||||
<Link
|
||||
target="_blank"
|
||||
href={latestDevVersionChangelog}
|
||||
color="primary"
|
||||
>
|
||||
{'release notes'}
|
||||
</Link>
|
||||
) (
|
||||
<Link
|
||||
target="_blank"
|
||||
href={latestDevVersionUrl}
|
||||
color="primary"
|
||||
>
|
||||
{'download'}
|
||||
</Link>
|
||||
)
|
||||
</Box>
|
||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={4} mb={0}>
|
||||
<Typography variant="body2">
|
||||
<i>
|
||||
Use
|
||||
<Link target="_blank" href={uploadURL} color="primary">
|
||||
{'UPLOAD FIRMWARE'}
|
||||
</Link>
|
||||
to install any new firmware versions.
|
||||
</i>
|
||||
</Typography>
|
||||
</Box>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Box m={4} textAlign="center">
|
||||
<LinearProgress />
|
||||
<Typography variant="h6">
|
||||
Fetching version details…
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<FormButton
|
||||
variant="contained"
|
||||
color="primary"
|
||||
type="submit"
|
||||
onClick={onClose}
|
||||
>
|
||||
Close
|
||||
</FormButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withSnackbar(VersionCheck);
|
||||
@@ -58,4 +58,5 @@ export interface LogEvent {
|
||||
export interface LogSettings {
|
||||
level: LogLevel;
|
||||
max_messages: number;
|
||||
compact: boolean;
|
||||
}
|
||||
|
||||
33
interface/src/utils/binding.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
type UpdateEntity<S> = (state: (prevState: Readonly<S>) => S) => void;
|
||||
|
||||
export const extractEventValue = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
switch (event.target.type) {
|
||||
case 'number':
|
||||
return event.target.valueAsNumber;
|
||||
case 'checkbox':
|
||||
return event.target.checked;
|
||||
default:
|
||||
return event.target.value;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateValue = <S>(updateEntity: UpdateEntity<S>) => (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
updateEntity((prevState) => ({
|
||||
...prevState,
|
||||
[event.target.name]: extractEventValue(event)
|
||||
}));
|
||||
};
|
||||
|
||||
export const updateBooleanValue = <S>(updateEntity: UpdateEntity<S>) => (
|
||||
name: string,
|
||||
value?: boolean
|
||||
) => {
|
||||
updateEntity((prevState) => ({
|
||||
...prevState,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
1
interface/src/utils/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './binding';
|
||||
@@ -1,6 +1,14 @@
|
||||
ArduinoJson: change log
|
||||
=======================
|
||||
|
||||
v6.18.4 (2021-09-06)
|
||||
-------
|
||||
|
||||
* Fixed error `'dummy' may be used uninitialized` on GCC 11
|
||||
* Fixed error `expected unqualified-id before 'const'` on GCC 11 (issue #1622)
|
||||
* Filter: exact match takes precedence over wildcard (issue #1628)
|
||||
* Fixed deserialization of `\u0000` (issue #1646)
|
||||
|
||||
v6.18.3 (2021-07-27)
|
||||
-------
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
---
|
||||
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.18.3)
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.18.4)
|
||||
[](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A6.x)
|
||||
[](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
|
||||
@@ -78,7 +78,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
|
||||
* [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)
|
||||
* Continuously tested on
|
||||
* [Visual Studio 2010, 2012, 2013, 2015, 2017, 2019](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
|
||||
* [GCC 4.4, 4.6, 4.7, 4.8, 4.9, 5, 6, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
|
||||
* [GCC 4.4, 4.6, 4.7, 4.8, 4.9, 5, 6, 7, 8, 9, 10, 11](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
|
||||
* [Clang 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 5.0, 6.0, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
|
||||
* [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
|
||||
* Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/)
|
||||
|
||||
@@ -32,8 +32,8 @@ class Filter {
|
||||
Filter operator[](const TKey& key) const {
|
||||
if (_variant == true) // "true" means "allow recursively"
|
||||
return *this;
|
||||
else
|
||||
return Filter(_variant[key] | _variant["*"]);
|
||||
VariantConstRef member = _variant[key];
|
||||
return Filter(member.isNull() ? _variant["*"] : member);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -17,7 +17,7 @@ struct Reader {
|
||||
Reader(TSource& source) : _source(&source) {}
|
||||
|
||||
int read() {
|
||||
return _source->read();
|
||||
return _source->read(); // Error here? You passed an unsupported input type
|
||||
}
|
||||
|
||||
size_t readBytes(char* buffer, size_t length) {
|
||||
|
||||
@@ -13,14 +13,14 @@ template <typename TStringBuilder>
|
||||
inline void encodeCodepoint(uint32_t codepoint32, TStringBuilder& str) {
|
||||
// this function was optimize for code size on AVR
|
||||
|
||||
// a buffer to store the string in reverse
|
||||
char buf[5];
|
||||
char* p = buf;
|
||||
|
||||
*(p++) = 0;
|
||||
if (codepoint32 < 0x80) {
|
||||
*(p++) = char((codepoint32));
|
||||
str.append(char(codepoint32));
|
||||
} else {
|
||||
// a buffer to store the string in reverse
|
||||
char buf[5];
|
||||
char* p = buf;
|
||||
|
||||
*(p++) = 0;
|
||||
*(p++) = char((codepoint32 | 0x80) & 0xBF);
|
||||
uint16_t codepoint16 = uint16_t(codepoint32 >> 6);
|
||||
if (codepoint16 < 0x20) { // 0x800
|
||||
@@ -36,10 +36,10 @@ inline void encodeCodepoint(uint32_t codepoint32, TStringBuilder& str) {
|
||||
*(p++) = char(codepoint16 | 0xF0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (*(--p)) {
|
||||
str.append(*p);
|
||||
while (*(--p)) {
|
||||
str.append(*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Utf8
|
||||
|
||||
@@ -45,8 +45,7 @@ class StringAdapter< ::String> {
|
||||
template <>
|
||||
class StringAdapter< ::StringSumHelper> : public StringAdapter< ::String> {
|
||||
public:
|
||||
StringAdapter< ::StringSumHelper>(const ::String& s)
|
||||
: StringAdapter< ::String>(s) {}
|
||||
StringAdapter(const ::String& s) : StringAdapter< ::String>(s) {}
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -45,7 +45,7 @@ class StringAdapter<const char*> {
|
||||
template <int N>
|
||||
class StringAdapter<const char[N]> : public StringAdapter<const char*> {
|
||||
public:
|
||||
StringAdapter<const char[N]>(const char* s) : StringAdapter<const char*>(s) {}
|
||||
StringAdapter(const char* s) : StringAdapter<const char*>(s) {}
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -27,7 +27,7 @@ struct Converter {
|
||||
}
|
||||
|
||||
static bool checkJson(VariantConstRef src) {
|
||||
T dummy;
|
||||
T dummy = T();
|
||||
// clang-format off
|
||||
return canConvertFromJson(src, dummy); // Error here? See https://arduinojson.org/v6/unsupported-is/
|
||||
// clang-format on
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
//
|
||||
enum {
|
||||
VALUE_MASK = 0x7F,
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ARDUINOJSON_VERSION "6.18.3"
|
||||
#define ARDUINOJSON_VERSION "6.18.4"
|
||||
#define ARDUINOJSON_VERSION_MAJOR 6
|
||||
#define ARDUINOJSON_VERSION_MINOR 18
|
||||
#define ARDUINOJSON_VERSION_REVISION 3
|
||||
#define ARDUINOJSON_VERSION_REVISION 4
|
||||
|
||||
@@ -103,7 +103,7 @@ static uint32_t _closed_index = []() {
|
||||
|
||||
static inline bool _init_async_event_queue() {
|
||||
if (!_async_queue) {
|
||||
_async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
|
||||
_async_queue = xQueueCreate(128, sizeof(lwip_event_packet_t *)); // double queue see https://github.com/emsesp/EMS-ESP32/issues/177
|
||||
if (!_async_queue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -78,6 +78,8 @@ class MsgpackAsyncJsonResponse : public AsyncAbstractResponse {
|
||||
}
|
||||
size_t setLength() {
|
||||
_contentLength = measureMsgPack(_root);
|
||||
//_headers.add(new AsyncWebHeader("Json-Length", String(_jsonBuffer.memoryUsage()))); // For determening size of EMSESP_JSON_SIZE_XXLARGE_DYN (Sunbuzz)
|
||||
// Json-Length: 10635
|
||||
if (_contentLength) {
|
||||
_isValid = true;
|
||||
}
|
||||
|
||||
@@ -130,7 +130,8 @@ bool PButton::check(void) {
|
||||
}
|
||||
|
||||
// Test for normal click event: DblClickDelay expired
|
||||
if (state_ == pullMode_ && (millisRes - upTime_) >= DblClickDelay_ && dblClickWaiting_ == true && dblClickOnNextUp_ == false && singleClickOK_ == true && resultEvent != 2) {
|
||||
if (state_ == pullMode_ && (millisRes - upTime_) >= DblClickDelay_ && dblClickWaiting_ == true && dblClickOnNextUp_ == false && singleClickOK_ == true
|
||||
&& resultEvent != 2) {
|
||||
// Serial.println("*single click pressed*");
|
||||
resultEvent = 1;
|
||||
dblClickWaiting_ = false;
|
||||
|
||||
@@ -57,7 +57,8 @@ class APSettings {
|
||||
IPAddress subnetMask;
|
||||
|
||||
bool operator==(const APSettings & settings) const {
|
||||
return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && localIP == settings.localIP && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask;
|
||||
return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && localIP == settings.localIP
|
||||
&& gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask;
|
||||
}
|
||||
|
||||
static void read(APSettings & settings, JsonObject & root) {
|
||||
|
||||
@@ -4,7 +4,9 @@ using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
APStatus::APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService)
|
||||
: _apSettingsService(apSettingsService) {
|
||||
server->on(AP_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
server->on(AP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void APStatus::apStatus(AsyncWebServerRequest * request) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <ESP8266React.h>
|
||||
|
||||
#include <WWWData.h>
|
||||
|
||||
ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
: _featureService(server)
|
||||
, _securitySettingsService(server, fs)
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#include <NetworkSettingsService.h>
|
||||
#include <NetworkStatus.h>
|
||||
|
||||
#include <WWWData.h>
|
||||
|
||||
class ESP8266React {
|
||||
public:
|
||||
ESP8266React(AsyncWebServer * server, FS * fs);
|
||||
|
||||
@@ -7,7 +7,12 @@
|
||||
template <class T>
|
||||
class FSPersistence {
|
||||
public:
|
||||
FSPersistence(JsonStateReader<T> stateReader, JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, FS * fs, const char * filePath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
FSPersistence(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
FS * fs,
|
||||
const char * filePath,
|
||||
size_t bufferSize = FS_BUFFER_SIZE)
|
||||
: _stateReader(stateReader)
|
||||
, _stateUpdater(stateUpdater)
|
||||
, _statefulService(statefulService)
|
||||
|
||||
@@ -4,7 +4,9 @@ using namespace std::placeholders;
|
||||
|
||||
FactoryResetService::FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: fs(fs) {
|
||||
server->on(FACTORY_RESET_SERVICE_PATH, HTTP_POST, securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(FACTORY_RESET_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
void FactoryResetService::handleRequest(AsyncWebServerRequest * request) {
|
||||
|
||||
@@ -28,7 +28,11 @@ class HttpGetEndpoint {
|
||||
server->on(servicePath.c_str(), HTTP_GET, securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, _1), authenticationPredicate));
|
||||
}
|
||||
|
||||
HttpGetEndpoint(JsonStateReader<T> stateReader, StatefulService<T> * statefulService, AsyncWebServer * server, const String & servicePath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
HttpGetEndpoint(JsonStateReader<T> stateReader,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: _stateReader(stateReader)
|
||||
, _statefulService(statefulService)
|
||||
, _bufferSize(bufferSize) {
|
||||
@@ -70,7 +74,12 @@ class HttpPostEndpoint {
|
||||
server->addHandler(&_updateHandler);
|
||||
}
|
||||
|
||||
HttpPostEndpoint(JsonStateReader<T> stateReader, JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncWebServer * server, const String & servicePath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
HttpPostEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: _stateReader(stateReader)
|
||||
, _stateUpdater(stateUpdater)
|
||||
, _statefulService(statefulService)
|
||||
@@ -124,7 +133,12 @@ class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> {
|
||||
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, securityManager, authenticationPredicate, bufferSize) {
|
||||
}
|
||||
|
||||
HttpEndpoint(JsonStateReader<T> stateReader, JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncWebServer * server, const String & servicePath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
HttpEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, bufferSize)
|
||||
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) {
|
||||
}
|
||||
|
||||
@@ -33,7 +33,11 @@ class MqttConnector {
|
||||
template <class T>
|
||||
class MqttPub : virtual public MqttConnector<T> {
|
||||
public:
|
||||
MqttPub(JsonStateReader<T> stateReader, StatefulService<T> * statefulService, AsyncMqttClient * mqttClient, const String & pubTopic = "", size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
MqttPub(JsonStateReader<T> stateReader,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncMqttClient * mqttClient,
|
||||
const String & pubTopic = "",
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: MqttConnector<T>(statefulService, mqttClient, bufferSize)
|
||||
, _stateReader(stateReader)
|
||||
, _pubTopic(pubTopic) {
|
||||
@@ -74,7 +78,11 @@ class MqttPub : virtual public MqttConnector<T> {
|
||||
template <class T>
|
||||
class MqttSub : virtual public MqttConnector<T> {
|
||||
public:
|
||||
MqttSub(JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncMqttClient * mqttClient, const String & subTopic = "", size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
MqttSub(JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncMqttClient * mqttClient,
|
||||
const String & subTopic = "",
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: MqttConnector<T>(statefulService, mqttClient, bufferSize)
|
||||
, _stateUpdater(stateUpdater)
|
||||
, _subTopic(subTopic) {
|
||||
|
||||
@@ -101,18 +101,25 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
switch (event) {
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
case SYSTEM_EVENT_ETH_GOT_IP:
|
||||
emsesp::EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
if (!networkSettings.enableIPv6 && _state.enabled) {
|
||||
// emsesp::EMSESP::logger().info(F("IPv4 Network connection found, starting MQTT client"));
|
||||
onConfigUpdated();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case SYSTEM_EVENT_GOT_IP6:
|
||||
if (_state.enabled) {
|
||||
// emsesp::EMSESP::logger().info(F("Network connection found, starting MQTT client"));
|
||||
// emsesp::EMSESP::logger().info(F("IPv6 Network connection found, starting MQTT client"));
|
||||
onConfigUpdated();
|
||||
}
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||
if (_state.enabled) {
|
||||
// emsesp::EMSESP::logger().info(F("Network connection dropped, stopping MQTT client"));
|
||||
onConfigUpdated();
|
||||
_mqttClient.disconnect();
|
||||
// onConfigUpdated();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -122,9 +129,11 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
}
|
||||
|
||||
void MqttSettingsService::configureMqtt() {
|
||||
// disconnect if connected
|
||||
_mqttClient.disconnect();
|
||||
// only connect if WiFi is connected and MQTT is enabled
|
||||
if (_state.enabled && emsesp::EMSESP::system_.network_connected()) {
|
||||
_mqttClient.disconnect();
|
||||
// emsesp::EMSESP::logger().info(F("Configuring Mqtt client"));
|
||||
_mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
|
||||
if (_state.username.length() > 0) {
|
||||
_mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername),
|
||||
@@ -137,6 +146,8 @@ void MqttSettingsService::configureMqtt() {
|
||||
_mqttClient.setCleanSession(_state.cleanSession);
|
||||
_mqttClient.setMaxTopicLength(_state.maxTopicLength);
|
||||
_mqttClient.connect();
|
||||
// } else {
|
||||
// emsesp::EMSESP::logger().info(F("Error configuring Mqtt client"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +175,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
|
||||
root["ha_climate_format"] = settings.ha_climate_format;
|
||||
root["ha_enabled"] = settings.ha_enabled;
|
||||
root["nested_format"] = settings.nested_format;
|
||||
root["subscribe_format"] = settings.subscribe_format;
|
||||
root["send_response"] = settings.send_response;
|
||||
}
|
||||
|
||||
StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & settings) {
|
||||
@@ -194,7 +205,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
|
||||
newSettings.ha_climate_format = root["ha_climate_format"] | EMSESP_DEFAULT_HA_CLIMATE_FORMAT;
|
||||
newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
|
||||
newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
|
||||
newSettings.subscribe_format = root["subscribe_format"] | EMSESP_DEFAULT_SUBSCRIBE_FORMAT;
|
||||
newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE;
|
||||
|
||||
if (newSettings.enabled != settings.enabled) {
|
||||
changed = true;
|
||||
@@ -209,7 +220,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (newSettings.subscribe_format != settings.subscribe_format) {
|
||||
if (newSettings.send_response != settings.send_response) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class MqttSettings {
|
||||
uint8_t ha_climate_format;
|
||||
bool ha_enabled;
|
||||
uint8_t nested_format;
|
||||
uint8_t subscribe_format;
|
||||
bool send_response;
|
||||
|
||||
static void read(MqttSettings & settings, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, MqttSettings & settings);
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(NTP_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
server->on(NTP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -61,7 +61,8 @@ void NetworkSettingsService::manageSTA() {
|
||||
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); // configure for DHCP
|
||||
}
|
||||
|
||||
WiFi.setHostname(_state.hostname.c_str()); // set hostname
|
||||
WiFi.setHostname(_state.hostname.c_str()); // set hostname
|
||||
|
||||
// www.esp32.com/viewtopic.php?t=12055
|
||||
read([&](NetworkSettings & networkSettings) {
|
||||
if (networkSettings.bandwidth20) {
|
||||
@@ -73,33 +74,18 @@ void NetworkSettingsService::manageSTA() {
|
||||
if (networkSettings.nosleep) {
|
||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); // attempt to connect to the network
|
||||
}
|
||||
}
|
||||
|
||||
// handles both WiFI and Ethernet
|
||||
// handles if wifi stopped
|
||||
void NetworkSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
switch (event) {
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
WiFi.disconnect(true);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_STOP:
|
||||
if (event == SYSTEM_EVENT_STA_STOP) {
|
||||
if (_stopping) {
|
||||
_lastConnectionAttempt = 0;
|
||||
_stopping = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_ETH_START:
|
||||
if (_state.staticIPConfig) {
|
||||
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); // configure for static IP
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
NetworkStatus::NetworkStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(NETWORK_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&NetworkStatus::networkStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
server->on(NETWORK_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&NetworkStatus::networkStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
|
||||
@@ -59,6 +59,7 @@ void OTASettingsService::configureArduinoOTA() {
|
||||
Serial.println(F("End Failed"));
|
||||
});
|
||||
|
||||
_arduinoOTA->setMdnsEnabled(false); // disable as handled in NetworkSettingsService.cpp. https://github.com/emsesp/EMS-ESP32/issues/161
|
||||
_arduinoOTA->begin();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
|
||||
#include <ESPmDNS.h>
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * f
|
||||
, _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
|
||||
, _jwtHandler(FACTORY_JWT_SECRET) {
|
||||
addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false);
|
||||
server->on(GENERATE_TOKEN_PATH, HTTP_GET, wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(GENERATE_TOKEN_PATH,
|
||||
HTTP_GET,
|
||||
wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
void SecuritySettingsService::begin() {
|
||||
@@ -109,19 +111,19 @@ ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequest
|
||||
};
|
||||
}
|
||||
|
||||
void SecuritySettingsService::generateToken(AsyncWebServerRequest* request) {
|
||||
AsyncWebParameter* usernameParam = request->getParam("username");
|
||||
for (User _user : _state.users) {
|
||||
if (_user.username == usernameParam->value()) {
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, GENERATE_TOKEN_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
root["token"] = generateJWT(&_user);
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) {
|
||||
AsyncWebParameter * usernameParam = request->getParam("username");
|
||||
for (User _user : _state.users) {
|
||||
if (_user.username == usernameParam->value()) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, GENERATE_TOKEN_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
root["token"] = generateJWT(&_user);
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
request->send(401);
|
||||
request->send(401);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
#define DEFAULT_BUFFER_SIZE 2048
|
||||
#endif
|
||||
|
||||
#ifndef FS_BUFFER_SIZE
|
||||
#define FS_BUFFER_SIZE 4096
|
||||
#endif
|
||||
|
||||
enum class StateUpdateResult {
|
||||
CHANGED = 0, // The update changed the state and propagation should take place if required
|
||||
UNCHANGED, // The state was unchanged, propagation should not take place
|
||||
|
||||
@@ -4,7 +4,10 @@ using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
UploadFirmwareService::UploadFirmwareService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager) {
|
||||
server->on(UPLOAD_FIRMWARE_PATH, HTTP_POST, std::bind(&UploadFirmwareService::uploadComplete, this, _1), std::bind(&UploadFirmwareService::handleUpload, this, _1, _2, _3, _4, _5, _6));
|
||||
server->on(UPLOAD_FIRMWARE_PATH,
|
||||
HTTP_POST,
|
||||
std::bind(&UploadFirmwareService::uploadComplete, this, _1),
|
||||
std::bind(&UploadFirmwareService::handleUpload, this, _1, _2, _3, _4, _5, _6));
|
||||
}
|
||||
|
||||
void UploadFirmwareService::handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
|
||||
|
||||
@@ -20,7 +20,12 @@ class WebSocketConnector {
|
||||
AsyncWebSocket _webSocket;
|
||||
size_t _bufferSize;
|
||||
|
||||
WebSocketConnector(StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, SecurityManager * securityManager, AuthenticationPredicate authenticationPredicate, size_t bufferSize)
|
||||
WebSocketConnector(StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const char * webSocketPath,
|
||||
SecurityManager * securityManager,
|
||||
AuthenticationPredicate authenticationPredicate,
|
||||
size_t bufferSize)
|
||||
: _statefulService(statefulService)
|
||||
, _server(server)
|
||||
, _webSocket(webSocketPath)
|
||||
@@ -67,7 +72,11 @@ class WebSocketTx : virtual public WebSocketConnector<T> {
|
||||
WebSocketConnector<T>::_statefulService->addUpdateHandler([&](const String & originId) { transmitData(nullptr, originId); }, false);
|
||||
}
|
||||
|
||||
WebSocketTx(JsonStateReader<T> stateReader, StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
WebSocketTx(JsonStateReader<T> stateReader,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const char * webSocketPath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
|
||||
, _stateReader(stateReader) {
|
||||
WebSocketConnector<T>::_statefulService->addUpdateHandler([&](const String & originId) { transmitData(nullptr, originId); }, false);
|
||||
@@ -140,7 +149,11 @@ class WebSocketRx : virtual public WebSocketConnector<T> {
|
||||
, _stateUpdater(stateUpdater) {
|
||||
}
|
||||
|
||||
WebSocketRx(JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
WebSocketRx(JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const char * webSocketPath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
|
||||
, _stateUpdater(stateUpdater) {
|
||||
}
|
||||
@@ -182,7 +195,12 @@ class WebSocketTxRx : public WebSocketTx<T>, public WebSocketRx<T> {
|
||||
, WebSocketRx<T>(stateUpdater, statefulService, server, webSocketPath, securityManager, authenticationPredicate, bufferSize) {
|
||||
}
|
||||
|
||||
WebSocketTxRx(JsonStateReader<T> stateReader, JsonStateUpdater<T> stateUpdater, StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
WebSocketTxRx(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const char * webSocketPath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
|
||||
, WebSocketTx<T>(stateReader, statefulService, server, webSocketPath, bufferSize)
|
||||
, WebSocketRx<T>(stateUpdater, statefulService, server, webSocketPath, bufferSize) {
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(SCAN_NETWORKS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(LIST_NETWORKS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(SCAN_NETWORKS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(LIST_NETWORKS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
};
|
||||
|
||||
void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
namespace uuid {
|
||||
|
||||
std::string read_flash_string(const __FlashStringHelper * flash_str) {
|
||||
if (flash_str == nullptr) {
|
||||
return std::string(""); // prevent crash
|
||||
}
|
||||
|
||||
std::string str(::strlen_P(reinterpret_cast<PGM_P>(flash_str)), '\0');
|
||||
|
||||
::strncpy_P(&str[0], reinterpret_cast<PGM_P>(flash_str), str.capacity() + 1);
|
||||
|
||||
@@ -434,11 +434,23 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
|
||||
udp_.print('-');
|
||||
}
|
||||
|
||||
udp_.printf_P(PSTR(" %s %s - - - \xEF\xBB\xBF"), hostname_.c_str(), uuid::read_flash_string(message.content_->name).c_str());
|
||||
udp_.printf_P(PSTR(" %s %s - - - "), hostname_.c_str(), uuid::read_flash_string(message.content_->name).c_str());
|
||||
|
||||
udp_.print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3).c_str());
|
||||
udp_.printf_P(PSTR(" %c %lu: "), uuid::log::format_level_char(message.content_->level), message.id_);
|
||||
udp_.print(message.content_->text.c_str());
|
||||
char id_c_str[15];
|
||||
snprintf_P(id_c_str, sizeof(id_c_str), PSTR(" %lu: "), message.id_);
|
||||
std::string msgstr = uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3) +
|
||||
' ' +
|
||||
uuid::log::format_level_char(message.content_->level) +
|
||||
id_c_str +
|
||||
message.content_->text;
|
||||
for (uint16_t i = 0; i < msgstr.length(); i++) {
|
||||
if (msgstr.at(i) & 0x80) {
|
||||
udp_.print("\xEF\xBB\xBF");
|
||||
// udp_.print("<BOM>"); // marker for testing if BOM is created for udf-8
|
||||
break;
|
||||
}
|
||||
}
|
||||
udp_.print(msgstr.c_str());
|
||||
bool ok = (udp_.endPacket() == 1);
|
||||
|
||||
last_transmit_ = uuid::get_uptime_ms();
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define IPAddress std::string
|
||||
// #define IPAddress std::string
|
||||
#define IPAddress String
|
||||
|
||||
#define ICACHE_FLASH_ATTR
|
||||
#define ICACHE_RAM_ATTR
|
||||
#define os_event_t void
|
||||
@@ -49,6 +51,8 @@
|
||||
#define OUTPUT 1
|
||||
#define INPUT_PULLUP 2
|
||||
|
||||
#define snprintf snprintf_P // to keep backwards compatibility
|
||||
|
||||
void pinMode(uint8_t pin, uint8_t mode);
|
||||
void digitalWrite(uint8_t pin, uint8_t value);
|
||||
int digitalRead(uint8_t pin);
|
||||
|
||||
@@ -35,11 +35,11 @@ class DummySettings {
|
||||
uint8_t mqtt_qos = 0;
|
||||
bool mqtt_retain = false;
|
||||
bool enabled = true;
|
||||
uint8_t nested_format = 1;
|
||||
uint8_t nested_format = 1; // 1=nested 2=single
|
||||
uint8_t ha_climate_format = 1;
|
||||
bool ha_enabled = true;
|
||||
String base = "ems-esp";
|
||||
uint8_t subscribe_format = 0;
|
||||
bool send_response = true;
|
||||
|
||||
String hostname = "ems-esp";
|
||||
String jwtSecret = "ems-esp";
|
||||
@@ -48,7 +48,7 @@ class DummySettings {
|
||||
String localIP = "";
|
||||
String gatewayIP = "";
|
||||
String subnetMask = "";
|
||||
String staticIPConfig = "";
|
||||
bool staticIPConfig = false;
|
||||
String dnsIP1 = "";
|
||||
String dnsIP2 = "";
|
||||
String board_profile = "CUSTOM";
|
||||
|
||||
0
lib_standalone/ESPmDNS.h
Normal file
@@ -12,7 +12,7 @@ class FSPersistence {
|
||||
StatefulService<T> * statefulService,
|
||||
FS * fs,
|
||||
const char * filePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
size_t bufferSize = FS_BUFFER_SIZE)
|
||||
: _stateReader(stateReader)
|
||||
, _stateUpdater(stateUpdater)
|
||||
, _statefulService(statefulService)
|
||||
|
||||
@@ -158,9 +158,12 @@ class WiFiClass {
|
||||
return 0;
|
||||
};
|
||||
|
||||
void disconnect(bool v){};
|
||||
|
||||
char * getHostname() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char * localIP() {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -172,16 +175,23 @@ class ETHClass {
|
||||
return false;
|
||||
};
|
||||
|
||||
void setHostname(const char * s){};
|
||||
void setHostname(const char * s){};
|
||||
|
||||
char * getHostname() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char * localIP() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int linkSpeed() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#define DEFAULT_BUFFER_SIZE 2048
|
||||
#endif
|
||||
|
||||
#ifndef FS_BUFFER_SIZE
|
||||
#define FS_BUFFER_SIZE 4096
|
||||
#endif
|
||||
|
||||
enum class StateUpdateResult {
|
||||
CHANGED = 0, // The update changed the state and propagation should take place if required
|
||||
UNCHANGED, // The state was unchanged, propagation should not take place
|
||||
|
||||
|
Before Width: | Height: | Size: 548 KiB After Width: | Height: | Size: 548 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 242 KiB After Width: | Height: | Size: 242 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
@@ -225,7 +225,7 @@ const mqtt_settings = {
|
||||
ha_climate_format: 1,
|
||||
ha_enabled: true,
|
||||
nested_format: 1,
|
||||
subscribe_format: 0,
|
||||
send_response: true,
|
||||
}
|
||||
const mqtt_status = {
|
||||
enabled: true,
|
||||
@@ -291,9 +291,9 @@ const EMSESP_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'emsespStatus'
|
||||
const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile'
|
||||
const WRITE_VALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeValue'
|
||||
const WRITE_SENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeSensor'
|
||||
const emsesp_settings = {
|
||||
|
||||
emsesp_settings = {
|
||||
tx_mode: 1,
|
||||
tx_delay: 0,
|
||||
ems_bus_id: 11,
|
||||
syslog_enabled: false,
|
||||
syslog_level: 3,
|
||||
@@ -306,6 +306,7 @@ const emsesp_settings = {
|
||||
shower_alert: false,
|
||||
rx_gpio: 23,
|
||||
tx_gpio: 5,
|
||||
phy_type: 0,
|
||||
dallas_gpio: 3,
|
||||
dallas_parasite: false,
|
||||
led_gpio: 2,
|
||||
@@ -321,37 +322,38 @@ const emsesp_settings = {
|
||||
const emsesp_data = {
|
||||
devices: [
|
||||
{
|
||||
id: 1,
|
||||
type: 'Thermostat',
|
||||
brand: '',
|
||||
name: 'RC20/Moduline 300',
|
||||
deviceid: 23,
|
||||
productid: 77,
|
||||
version: '03.03',
|
||||
i: 1,
|
||||
t: 'Thermostat',
|
||||
b: '',
|
||||
n: 'RC20/Moduline 300',
|
||||
d: 23,
|
||||
p: 77,
|
||||
v: '03.03',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 'Boiler',
|
||||
brand: 'Nefit',
|
||||
name: 'GBx72/Trendline/Cerapur/Greenstar Si/27i',
|
||||
deviceid: 8,
|
||||
productid: 123,
|
||||
version: '06.01',
|
||||
i: 2,
|
||||
t: 'Boiler',
|
||||
b: 'Nefit',
|
||||
n: 'GBx72/Trendline/Cerapur/Greenstar Si/27i',
|
||||
d: 8,
|
||||
p: 123,
|
||||
v: '06.01',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'Controller',
|
||||
brand: '',
|
||||
name: 'BC10',
|
||||
deviceid: 9,
|
||||
productid: 190,
|
||||
version: '01.03',
|
||||
i: 3,
|
||||
t: 'Controller',
|
||||
b: '',
|
||||
n: 'BC10',
|
||||
d: 9,
|
||||
p: 190,
|
||||
v: '01.03',
|
||||
},
|
||||
],
|
||||
sensors: [
|
||||
{ no: 1, id: '28-233D-9497-0C03', temp: 25.7, offset: 1.2 },
|
||||
{ no: 2, id: '28-243D-7437-1E3A', temp: 26.1, offset: 0 },
|
||||
{ n: 1, i: '28-233D-9497-0C03', t: 25.7, o: 1.2 },
|
||||
{ n: 2, i: '28-243D-7437-1E3A', t: 26.1, o: 0 },
|
||||
],
|
||||
analog: 12,
|
||||
}
|
||||
|
||||
const emsesp_status = {
|
||||
@@ -363,7 +365,7 @@ const emsesp_status = {
|
||||
}
|
||||
|
||||
const emsesp_devicedata_1 = {
|
||||
name: 'Thermostat: RC20/Moduline 300',
|
||||
type: 'Thermostat',
|
||||
data: [
|
||||
{
|
||||
v: '(0)',
|
||||
@@ -380,386 +382,151 @@ const emsesp_devicedata_1 = {
|
||||
{
|
||||
v: 18,
|
||||
u: 1,
|
||||
n: '(hc1) selected room temperature',
|
||||
n: 'hc1 selected room temperature',
|
||||
c: 'hc1/seltemp',
|
||||
},
|
||||
{
|
||||
v: 22.6,
|
||||
u: 1,
|
||||
n: '(hc1) current room temperature',
|
||||
n: 'hc1 current room temperature',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 'auto',
|
||||
u: 0,
|
||||
n: '(hc1) mode',
|
||||
n: 'hc1 mode',
|
||||
c: 'hc1/mode',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const emsesp_devicedata_2 = {
|
||||
name: 'Boiler: Nefit GBx72/Trendline/Cerapur/Greenstar Si/27i',
|
||||
type: 'Boiler',
|
||||
data: [
|
||||
{ v: 'off', u: 0, n: 'heating active' },
|
||||
{ v: 'off', u: 0, n: 'warm water active' },
|
||||
{ v: 5, u: 1, n: 'selected flow temperature', c: 'selflowtemp' },
|
||||
{ v: 0, u: 2, n: 'burner selected max power', c: 'selburnpow' },
|
||||
{ v: 0, u: 2, n: 'heating pump modulation' },
|
||||
{ v: 42.9, u: 1, n: 'current flow temperature' },
|
||||
{ v: 41.8, u: 1, n: 'return temperature' },
|
||||
{ v: 1.6, u: 9, n: 'system pressure' },
|
||||
{ v: 45, u: 1, n: 'actual boiler temperature' },
|
||||
{ v: 'off', u: 0, n: 'gas' },
|
||||
{ v: 0, u: 8, n: 'flame current' },
|
||||
{ v: 'off', u: 0, n: 'heating pump' },
|
||||
{ v: 'off', u: 0, n: 'fan' },
|
||||
{ v: 'off', u: 0, n: 'ignition' },
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: 'heating active',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: 'warm water active',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 5,
|
||||
u: 1,
|
||||
n: 'selected flow temperature',
|
||||
c: 'selflowtemp',
|
||||
},
|
||||
{
|
||||
v: 0,
|
||||
u: 2,
|
||||
n: 'burner selected max power',
|
||||
c: 'selburnpow',
|
||||
},
|
||||
{
|
||||
v: 0,
|
||||
u: 2,
|
||||
n: 'heating pump modulation',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 51,
|
||||
u: 1,
|
||||
n: 'current flow temperature',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 49.8,
|
||||
u: 1,
|
||||
n: 'return temperature',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 1.1,
|
||||
u: 9,
|
||||
n: 'system pressure',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 52.7,
|
||||
u: 1,
|
||||
n: 'boiler temperature',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: 'gas',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 0,
|
||||
u: 8,
|
||||
n: 'flame current',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: 'heating pump',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: 'fan',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: 'ignition',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: true,
|
||||
u: 16,
|
||||
v: 'on',
|
||||
u: 0,
|
||||
n: 'heating activated',
|
||||
c: 'heatingactivated',
|
||||
l: ['off', 'on'],
|
||||
},
|
||||
{
|
||||
v: 75,
|
||||
u: 1,
|
||||
n: 'heating temperature',
|
||||
c: 'heatingtemp',
|
||||
},
|
||||
{
|
||||
v: 90,
|
||||
u: 2,
|
||||
n: 'burner pump max power',
|
||||
c: 'pumpmodmax',
|
||||
},
|
||||
{
|
||||
v: 55,
|
||||
u: 2,
|
||||
n: 'burner pump min power',
|
||||
c: 'pumpmodmin',
|
||||
},
|
||||
{
|
||||
v: 1,
|
||||
u: 7,
|
||||
n: 'pump delay',
|
||||
c: 'pumpdelay',
|
||||
},
|
||||
{
|
||||
v: 10,
|
||||
u: 7,
|
||||
n: 'burner min period',
|
||||
c: 'burnminperiod',
|
||||
},
|
||||
{
|
||||
v: 0,
|
||||
u: 2,
|
||||
n: 'burner min power',
|
||||
c: 'burnminpower',
|
||||
},
|
||||
{
|
||||
v: 75,
|
||||
u: 2,
|
||||
n: 'burner max power',
|
||||
c: 'burnmaxpower',
|
||||
},
|
||||
{
|
||||
v: -6,
|
||||
u: 1,
|
||||
n: 'hysteresis on temperature',
|
||||
c: 'boilhyston',
|
||||
},
|
||||
{
|
||||
v: 6,
|
||||
u: 1,
|
||||
n: 'hysteresis off temperature',
|
||||
c: 'boilhystoff',
|
||||
},
|
||||
{
|
||||
v: 0,
|
||||
u: 2,
|
||||
n: 'burner current power',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 303226,
|
||||
u: 15,
|
||||
n: '# burner starts',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 510634,
|
||||
u: 7,
|
||||
n: 'total burner operating time',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 415235,
|
||||
u: 7,
|
||||
n: 'total heat operating time',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 4338730,
|
||||
u: 7,
|
||||
n: 'total UBA operating time',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: '1C(210) 06.06.2020 12:07',
|
||||
u: 0,
|
||||
n: 'last error code',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: '0H',
|
||||
u: 0,
|
||||
n: 'service code',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 203,
|
||||
u: 0,
|
||||
n: 'service code number',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: '01.01.2012',
|
||||
u: 0,
|
||||
n: 'maintenance set date',
|
||||
c: '',
|
||||
},
|
||||
{ v: 75, u: 1, n: 'heating temperature', c: 'heatingtemp' },
|
||||
{ v: 90, u: 2, n: 'burner pump max power', c: 'pumpmodmax' },
|
||||
{ v: 55, u: 2, n: 'burner pump min power', c: 'pumpmodmin' },
|
||||
{ v: 1, u: 7, n: 'pump delay', c: 'pumpdelay' },
|
||||
{ v: 10, u: 7, n: 'burner min period', c: 'burnminperiod' },
|
||||
{ v: 0, u: 2, n: 'burner min power', c: 'burnminpower' },
|
||||
{ v: 77, u: 2, n: 'burner max power', c: 'burnmaxpower' },
|
||||
{ v: -6, u: 1, n: 'hysteresis on temperature', c: 'boilhyston' },
|
||||
{ v: 6, u: 1, n: 'hysteresis off temperature', c: 'boilhystoff' },
|
||||
{ v: 0, u: 2, n: 'burner current power' },
|
||||
{ v: 317694, u: 16, n: 'burner starts' },
|
||||
{ v: 524115, u: 7, n: 'total burner operating time' },
|
||||
{ v: 424286, u: 7, n: 'total heat operating time' },
|
||||
{ v: 4571225, u: 7, n: 'total UBA operating time' },
|
||||
{ v: '1C(210) 06.06.2020 12:07', u: 0, n: 'last error code' },
|
||||
{ v: '0H', u: 0, n: 'service code' },
|
||||
{ v: 203, u: 0, n: 'service code number' },
|
||||
{ v: ' ', u: 0, n: 'maintenance message' },
|
||||
{
|
||||
v: 'off',
|
||||
u: 0,
|
||||
n: 'maintenance scheduled',
|
||||
c: 'maintenance',
|
||||
l: ['off', 'time', 'date'],
|
||||
},
|
||||
{ v: 6000, u: 6, n: 'maintenance set time', c: 'maintenancetime' },
|
||||
{ v: '01.01.2012', u: 0, n: 'maintenance set date', c: 'maintenancedate' },
|
||||
{ v: 60, u: 1, n: 'ww selected temperature', c: 'wwseltemp' },
|
||||
{ v: 62, u: 1, n: 'ww set temperature' },
|
||||
{ v: 'flow', u: 0, n: 'ww type' },
|
||||
{
|
||||
v: 6000,
|
||||
u: 6,
|
||||
n: 'maintenance set time',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 60,
|
||||
u: 1,
|
||||
n: '(ww) selected temperature',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 62,
|
||||
u: 1,
|
||||
n: '(ww) set temperature',
|
||||
c: 'wwsettemp',
|
||||
},
|
||||
{
|
||||
v: 'flow',
|
||||
v: 'eco',
|
||||
u: 0,
|
||||
n: '(ww) type',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 'hot',
|
||||
u: 0,
|
||||
n: '(ww) comfort',
|
||||
n: 'ww comfort',
|
||||
c: 'wwcomfort',
|
||||
l: ['hot', 'eco', 'intelligent'],
|
||||
},
|
||||
{ v: 40, u: 0, n: 'ww flow temperature offset', c: 'wwflowtempoffset' },
|
||||
{ v: 100, u: 2, n: 'ww max power', c: 'wwmaxpower' },
|
||||
{
|
||||
v: 40,
|
||||
v: 'off',
|
||||
u: 0,
|
||||
n: '(ww) flow temperature offset',
|
||||
c: 'wwflowtempoffset',
|
||||
},
|
||||
{
|
||||
v: 100,
|
||||
u: 2,
|
||||
n: '(ww) max power',
|
||||
c: 'wwmaxpower',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: '(ww) circulation pump available',
|
||||
n: 'ww circulation pump available',
|
||||
c: 'wwcircpump',
|
||||
l: ['off', 'on'],
|
||||
},
|
||||
{ v: '3-way valve', u: 0, n: 'ww charging type' },
|
||||
{ v: -5, u: 1, n: 'ww hysteresis on temperature', c: 'wwhyston' },
|
||||
{ v: 0, u: 1, n: 'ww hysteresis off temperature', c: 'wwhystoff' },
|
||||
{ v: 70, u: 1, n: 'ww disinfection temperature', c: 'wwdisinfectiontemp' },
|
||||
{
|
||||
v: 'charge pump',
|
||||
v: 'off',
|
||||
u: 0,
|
||||
n: '(ww) charging type',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 70,
|
||||
u: 1,
|
||||
n: '(ww) disinfection temperature',
|
||||
c: 'wwdisinfectiontemp',
|
||||
n: 'ww circulation pump frequency',
|
||||
c: 'wwcircmode',
|
||||
l: [
|
||||
'off',
|
||||
'1x3min',
|
||||
'2x3min',
|
||||
'3x3min',
|
||||
'4x3min',
|
||||
'5x3min',
|
||||
'6x3min',
|
||||
'continuous',
|
||||
],
|
||||
},
|
||||
{
|
||||
v: 'off',
|
||||
u: 0,
|
||||
n: '(ww) circulation pump frequency',
|
||||
c: 'wwcircmode',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: '(ww) circulation active',
|
||||
n: 'ww circulation active',
|
||||
c: 'wwcirc',
|
||||
l: ['off', 'on'],
|
||||
},
|
||||
{ v: 37.1, u: 1, n: 'ww current intern temperature' },
|
||||
{ v: 0, u: 3, n: 'ww current tap water flow' },
|
||||
{ v: 37.2, u: 1, n: 'ww storage intern temperature' },
|
||||
{ v: 'on', u: 0, n: 'ww activated', c: 'wwactivated', l: ['off', 'on'] },
|
||||
{
|
||||
v: 44.4,
|
||||
u: 1,
|
||||
n: '(ww) current intern temperature',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 0,
|
||||
u: 3,
|
||||
n: '(ww) current tap water flow',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 44.4,
|
||||
u: 1,
|
||||
n: '(ww) storage intern temperature',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: true,
|
||||
u: 16,
|
||||
n: '(ww) activated',
|
||||
c: 'wwactivated',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: '(ww) one time charging',
|
||||
v: 'off',
|
||||
u: 0,
|
||||
n: 'ww one time charging',
|
||||
c: 'wwonetime',
|
||||
l: ['off', 'on'],
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: '(ww) disinfecting',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: '(ww) charging',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: '(ww) recharging',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: true,
|
||||
u: 16,
|
||||
n: '(ww) temperature ok',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: false,
|
||||
u: 16,
|
||||
n: '(ww) active',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: true,
|
||||
u: 16,
|
||||
n: '(ww) heating',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 268671,
|
||||
u: 15,
|
||||
n: '(ww) # starts',
|
||||
c: '',
|
||||
},
|
||||
{
|
||||
v: 95399,
|
||||
u: 7,
|
||||
n: '(ww) active time',
|
||||
c: '',
|
||||
v: 'off',
|
||||
u: 0,
|
||||
n: 'ww disinfection',
|
||||
c: 'wwdisinfect',
|
||||
l: ['off', 'on'],
|
||||
},
|
||||
{ v: 'off', u: 0, n: 'ww charging' },
|
||||
{ v: 'off', u: 0, n: 'ww recharging' },
|
||||
{ v: 'on', u: 0, n: 'ww temperature ok' },
|
||||
{ v: 'off', u: 0, n: 'ww active' },
|
||||
{ v: 'on', u: 0, n: 'ww heating' },
|
||||
{ v: 282323, u: 16, n: 'ww starts' },
|
||||
{ v: 99829, u: 7, n: 'ww active time' },
|
||||
],
|
||||
}
|
||||
|
||||
const emsesp_devicedata_3 = {
|
||||
name: 'Controller: BC1',
|
||||
type: 'Controller',
|
||||
data: [],
|
||||
}
|
||||
|
||||
@@ -886,6 +653,8 @@ app.get(EMSESP_SETTINGS_ENDPOINT, (req, res) => {
|
||||
res.json(emsesp_settings)
|
||||
})
|
||||
app.post(EMSESP_SETTINGS_ENDPOINT, (req, res) => {
|
||||
console.log(req.body)
|
||||
emsesp_settings = req.body
|
||||
res.json(emsesp_settings)
|
||||
})
|
||||
app.get(EMSESP_DATA_ENDPOINT, (req, res) => {
|
||||
@@ -941,6 +710,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
rx_gpio: 3,
|
||||
tx_gpio: 4,
|
||||
pbutton_gpio: 5,
|
||||
phy_type: 0,
|
||||
}
|
||||
|
||||
if (board_profile == 'S32') {
|
||||
@@ -950,6 +720,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
data.rx_gpio = 23
|
||||
data.tx_gpio = 5
|
||||
data.pbutton_gpio = 0
|
||||
data.phy_type = 0
|
||||
} else if (board_profile == 'E32') {
|
||||
// BBQKees Gateway E32
|
||||
data.led_gpio = 2
|
||||
@@ -957,6 +728,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
data.rx_gpio = 5
|
||||
data.tx_gpio = 17
|
||||
data.pbutton_gpio = 33
|
||||
data.phy_type = 1
|
||||
} else if (board_profile == 'MH-ET') {
|
||||
// MH-ET Live D1 Mini
|
||||
data.led_gpio = 2
|
||||
@@ -964,6 +736,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
data.rx_gpio = 23
|
||||
data.tx_gpio = 5
|
||||
data.pbutton_gpio = 0
|
||||
data.phy_type = 0
|
||||
} else if (board_profile == 'NODEMCU') {
|
||||
// NodeMCU 32S
|
||||
data.led_gpio = 2
|
||||
@@ -971,6 +744,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
data.rx_gpio = 23
|
||||
data.tx_gpio = 5
|
||||
data.pbutton_gpio = 0
|
||||
data.phy_type = 0
|
||||
} else if (board_profile == 'LOLIN') {
|
||||
// Lolin D32
|
||||
data.led_gpio = 2
|
||||
@@ -978,6 +752,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
data.rx_gpio = 17
|
||||
data.tx_gpio = 16
|
||||
data.pbutton_gpio = 0
|
||||
data.phy_type = 0
|
||||
} else if (board_profile == 'OLIMEX') {
|
||||
// Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Dallas)
|
||||
data.led_gpio = 0
|
||||
@@ -985,21 +760,7 @@ app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
data.rx_gpio = 36
|
||||
data.tx_gpio = 4
|
||||
data.pbutton_gpio = 34
|
||||
// data = { 0, 0, 36, 4, 34};
|
||||
} else if (board_profile == 'TLK110') {
|
||||
// Generic Ethernet (TLK110)
|
||||
data.led_gpio = 2
|
||||
data.dallas_gpio = 4
|
||||
data.rx_gpio = 5
|
||||
data.tx_gpio = 17
|
||||
data.pbutton_gpio = 33
|
||||
} else if (board_profile == 'LAN8720') {
|
||||
// Generic Ethernet (LAN8720)
|
||||
data.led_gpio = 2
|
||||
data.dallas_gpio = 4
|
||||
data.rx_gpio = 5
|
||||
data.tx_gpio = 17
|
||||
data.pbutton_gpio = 33
|
||||
data.phy_type = 1
|
||||
}
|
||||
|
||||
res.json(data)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
; example custom platformio.ini file for EMS-ESP
|
||||
|
||||
[common]
|
||||
; e.g. use build_flags = -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
|
||||
debug_flags = -DEMSESP_WIFI_TWEAK -DEMSESP_DEBUG
|
||||
; debug_flags =
|
||||
; custom build flags
|
||||
; EMSESP_DEBUG, EMSESP_UART_DEBUG, EMSESP_DEBUG_SENSOR, EMSESP_WIFI_TWEAK, EMSESP_DEFAULT_BOARD_PROFILE
|
||||
; ; e.g. -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
|
||||
; my_build_flags = -DEMSESP_DEBUG -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
|
||||
my_build_flags =
|
||||
|
||||
; 5=verbose, 4=debug, 3=info
|
||||
debug_flags = -DCORE_DEBUG_LEVEL=5
|
||||
|
||||
[env:esp32]
|
||||
; if using OTA enter your details below
|
||||
@@ -11,9 +16,10 @@ upload_protocol = espota
|
||||
upload_flags =
|
||||
--port=8266
|
||||
--auth=ems-esp-neo
|
||||
upload_port = 10.10.10.101
|
||||
; to prevent the web UI from building each time, uncomment this next line
|
||||
; extra_scripts =
|
||||
upload_port = ems-esp.local
|
||||
|
||||
; use this when you don't want to re-build the WebUI
|
||||
extra_scripts = scripts/rename_fw.py
|
||||
|
||||
; pio run -e debug
|
||||
; or from Visual Studio Code do PIO -> Project Tasks -> debug -> General -> Upload and Monitor
|
||||
@@ -28,4 +34,4 @@ monitor_filters = esp32_exception_decoder
|
||||
debug_tool = esp-prog
|
||||
debug_init_break = tbreak setup
|
||||
build_flags = ${factory_settings.build_flags} ${common.debug_flags} -DONEWIRE_CRC16=0 -DNO_GLOBAL_ARDUINOOTA -DARDUINOJSON_ENABLE_STD_STRING=1 -DESP32=1 -DARDUINO_ARCH_ESP32=1
|
||||
extra_scripts = pre:scripts/build_interface.py
|
||||
extra_scripts = pre:scripts/build_interface.py
|
||||
|
||||
@@ -18,9 +18,13 @@ core_build_flags =
|
||||
|
||||
core_unbuild_flags =
|
||||
|
||||
; my_build_flags is set in pio_local.ini
|
||||
my_build_flags =
|
||||
|
||||
build_flags =
|
||||
${common.core_build_flags}
|
||||
${factory_settings.build_flags}
|
||||
${common.my_build_flags}
|
||||
-D ONEWIRE_CRC16=0
|
||||
-D NO_GLOBAL_ARDUINOOTA
|
||||
-D ARDUINOJSON_ENABLE_STD_STRING=1
|
||||
|
||||
118
scripts/clang-format.py
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# copied from esphome
|
||||
# run from Linux using ./scripts/clang-forrmat.py
|
||||
|
||||
import argparse
|
||||
import multiprocessing
|
||||
import os
|
||||
import queue
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import click
|
||||
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
from helpers import get_output, src_files, filter_changed
|
||||
|
||||
def run_format(args, queue, lock, failed_files):
|
||||
"""Takes filenames out of queue and runs clang-format on them."""
|
||||
while True:
|
||||
path = queue.get()
|
||||
invocation = ['clang-format']
|
||||
if args.inplace:
|
||||
invocation.append('-i')
|
||||
else:
|
||||
invocation.extend(['--dry-run', '-Werror'])
|
||||
invocation.append(path)
|
||||
|
||||
proc = subprocess.run(invocation, capture_output=True, encoding='utf-8')
|
||||
if proc.returncode != 0:
|
||||
with lock:
|
||||
print()
|
||||
print("\033[0;32m************* File \033[1;32m{}\033[0m".format(path))
|
||||
print(proc.stdout)
|
||||
print(proc.stderr)
|
||||
print()
|
||||
failed_files.append(path)
|
||||
queue.task_done()
|
||||
|
||||
|
||||
def progress_bar_show(value):
|
||||
return value if value is not None else ''
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-j', '--jobs', type=int,
|
||||
default=multiprocessing.cpu_count(),
|
||||
help='number of format instances to be run in parallel.')
|
||||
parser.add_argument('files', nargs='*', default=[],
|
||||
help='files to be processed (regex on path)')
|
||||
parser.add_argument('-i', '--inplace', action='store_true',
|
||||
help='reformat files in-place')
|
||||
parser.add_argument('-c', '--changed', action='store_true',
|
||||
help='only run on changed files')
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
get_output('clang-format', '-version')
|
||||
except:
|
||||
print("""
|
||||
Oops. It looks like clang-format is not installed.
|
||||
|
||||
Please check you can run "clang-format -version" in your terminal and install
|
||||
clang-format (v11 or v12) if necessary.
|
||||
|
||||
Note you can also upload your code as a pull request on GitHub and see the CI check
|
||||
output to apply clang-format.
|
||||
""")
|
||||
return 1
|
||||
|
||||
files = []
|
||||
# all files
|
||||
# for path in git_ls_files(['*.cpp', '*.h', '*.tcc']):
|
||||
# files.append(os.path.relpath(path, os.getcwd()))
|
||||
|
||||
# just under src
|
||||
for path in src_files(['.cpp', '.h']):
|
||||
files.append(os.path.relpath(path, os.getcwd()))
|
||||
|
||||
if args.files:
|
||||
file_name_re = re.compile('|'.join(args.files))
|
||||
files = [p for p in files if file_name_re.search(p)]
|
||||
|
||||
if args.changed:
|
||||
files = filter_changed(files)
|
||||
|
||||
files.sort()
|
||||
|
||||
failed_files = []
|
||||
try:
|
||||
task_queue = queue.Queue(args.jobs)
|
||||
lock = threading.Lock()
|
||||
for _ in range(args.jobs):
|
||||
t = threading.Thread(target=run_format,
|
||||
args=(args, task_queue, lock, failed_files))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
# Fill the queue with files.
|
||||
with click.progressbar(files, width=30, file=sys.stderr,
|
||||
item_show_func=progress_bar_show) as bar:
|
||||
for name in bar:
|
||||
task_queue.put(name)
|
||||
|
||||
# Wait for all threads to be done.
|
||||
task_queue.join()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
print('Ctrl-C detected, goodbye.')
|
||||
os.kill(0, 9)
|
||||
|
||||
sys.exit(len(failed_files))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
190
scripts/clang-tidy.py
Executable file
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# copied from esphome
|
||||
# run from Linux using ./scripts/clang-forrmat.py
|
||||
|
||||
import argparse
|
||||
import multiprocessing
|
||||
import os
|
||||
import queue
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import click
|
||||
import pexpect
|
||||
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
from helpers import shlex_quote, get_output, \
|
||||
build_all_include, temp_header_file, filter_changed, load_idedata, src_files
|
||||
|
||||
def clang_options(idedata):
|
||||
cmd = [
|
||||
# target 32-bit arch (this prevents size mismatch errors on a 64-bit host)
|
||||
'-m32',
|
||||
# disable built-in include directories from the host
|
||||
'-nostdinc',
|
||||
'-nostdinc++',
|
||||
# allow to condition code on the presence of clang-tidy
|
||||
'-DCLANG_TIDY'
|
||||
]
|
||||
|
||||
# copy compiler flags, except those clang doesn't understand.
|
||||
cmd.extend(flag for flag in idedata['cxx_flags'].split(' ')
|
||||
if flag not in ('-free', '-fipa-pta', '-mlongcalls', '-mtext-section-literals'))
|
||||
|
||||
# defines
|
||||
cmd.extend(f'-D{define}' for define in idedata['defines'])
|
||||
|
||||
# add include directories, using -isystem for dependencies to suppress their errors
|
||||
for directory in idedata['includes']['toolchain']:
|
||||
cmd.extend(['-isystem', directory])
|
||||
for directory in sorted(set(idedata['includes']['build'])):
|
||||
dependency = "framework-arduino" in directory or "/libdeps/" in directory
|
||||
cmd.extend(['-isystem' if dependency else '-I', directory])
|
||||
|
||||
return cmd
|
||||
|
||||
def run_tidy(args, options, tmpdir, queue, lock, failed_files):
|
||||
while True:
|
||||
path = queue.get()
|
||||
invocation = ['clang-tidy']
|
||||
|
||||
if tmpdir is not None:
|
||||
invocation.append('--export-fixes')
|
||||
# Get a temporary file. We immediately close the handle so clang-tidy can
|
||||
# overwrite it.
|
||||
(handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir)
|
||||
os.close(handle)
|
||||
invocation.append(name)
|
||||
|
||||
if args.quiet:
|
||||
invocation.append('-quiet')
|
||||
|
||||
invocation.append(os.path.abspath(path))
|
||||
invocation.append('--')
|
||||
invocation.extend(options)
|
||||
invocation_s = ' '.join(shlex_quote(x) for x in invocation)
|
||||
|
||||
# Use pexpect for a pseudy-TTY with colored output
|
||||
output, rc = pexpect.run(invocation_s, withexitstatus=True, encoding='utf-8',
|
||||
timeout=15 * 60)
|
||||
if rc != 0:
|
||||
with lock:
|
||||
print()
|
||||
print("\033[0;32m************* File \033[1;32m{}\033[0m".format(path))
|
||||
print(output)
|
||||
print()
|
||||
failed_files.append(path)
|
||||
queue.task_done()
|
||||
|
||||
def progress_bar_show(value):
|
||||
if value is None:
|
||||
return ''
|
||||
|
||||
def split_list(a, n):
|
||||
k, m = divmod(len(a), n)
|
||||
return [a[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)]
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-j', '--jobs', type=int,
|
||||
default=multiprocessing.cpu_count(),
|
||||
help='number of tidy instances to be run in parallel.')
|
||||
parser.add_argument('files', nargs='*', default=[],
|
||||
help='files to be processed (regex on path)')
|
||||
parser.add_argument('--fix', action='store_true', help='apply fix-its')
|
||||
parser.add_argument('-q', '--quiet', action='store_false',
|
||||
help='run clang-tidy in quiet mode')
|
||||
parser.add_argument('-c', '--changed', action='store_true',
|
||||
help='only run on changed files')
|
||||
parser.add_argument('--split-num', type=int, help='split the files into X jobs.',
|
||||
default=None)
|
||||
parser.add_argument('--split-at', type=int, help='which split is this? starts at 1',
|
||||
default=None)
|
||||
parser.add_argument('--all-headers', action='store_true',
|
||||
help='create a dummy file that checks all headers')
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
get_output('clang-tidy', '-version')
|
||||
except:
|
||||
print("""
|
||||
Oops. It looks like clang-tidy is not installed.
|
||||
|
||||
Please check you can run "clang-tidy -version" in your terminal and install
|
||||
clang-tidy (v11 or v12) if necessary.
|
||||
|
||||
Note you can also upload your code as a pull request on GitHub and see the CI check
|
||||
output to apply clang-tidy.
|
||||
""")
|
||||
return 1
|
||||
|
||||
idedata = load_idedata("clang-tidy")
|
||||
options = clang_options(idedata)
|
||||
|
||||
files = []
|
||||
for path in src_files(['.cpp']):
|
||||
files.append(os.path.relpath(path, os.getcwd()))
|
||||
|
||||
if args.files:
|
||||
# Match against files specified on command-line
|
||||
file_name_re = re.compile('|'.join(args.files))
|
||||
files = [p for p in files if file_name_re.search(p)]
|
||||
|
||||
if args.changed:
|
||||
files = filter_changed(files)
|
||||
|
||||
files.sort()
|
||||
|
||||
if args.split_num:
|
||||
files = split_list(files, args.split_num)[args.split_at - 1]
|
||||
|
||||
if args.all_headers and args.split_at in (None, 1):
|
||||
build_all_include()
|
||||
files.insert(0, temp_header_file)
|
||||
|
||||
tmpdir = None
|
||||
if args.fix:
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
|
||||
failed_files = []
|
||||
try:
|
||||
task_queue = queue.Queue(args.jobs)
|
||||
lock = threading.Lock()
|
||||
for _ in range(args.jobs):
|
||||
t = threading.Thread(target=run_tidy,
|
||||
args=(args, options, tmpdir, task_queue, lock, failed_files))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
# Fill the queue with files.
|
||||
with click.progressbar(files, width=30, file=sys.stderr,
|
||||
item_show_func=progress_bar_show) as bar:
|
||||
for name in bar:
|
||||
task_queue.put(name)
|
||||
|
||||
# Wait for all threads to be done.
|
||||
task_queue.join()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
print('Ctrl-C detected, goodbye.')
|
||||
if tmpdir:
|
||||
shutil.rmtree(tmpdir)
|
||||
os.kill(0, 9)
|
||||
|
||||
if args.fix and failed_files:
|
||||
print('Applying fixes ...')
|
||||
try:
|
||||
subprocess.call(['clang-apply-replacements-12', tmpdir])
|
||||
except:
|
||||
print('Error applying fixes.\n', file=sys.stderr)
|
||||
raise
|
||||
|
||||
sys.exit(len(failed_files))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
122
scripts/helpers.py
Executable file
@@ -0,0 +1,122 @@
|
||||
import os.path
|
||||
import re
|
||||
import subprocess
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", "..")))
|
||||
basepath = os.path.join(root_path, "src")
|
||||
temp_folder = os.path.join(root_path, ".temp")
|
||||
temp_header_file = os.path.join(temp_folder, "all-include.cpp")
|
||||
|
||||
|
||||
def shlex_quote(s):
|
||||
if not s:
|
||||
return "''"
|
||||
if re.search(r"[^\w@%+=:,./-]", s) is None:
|
||||
return s
|
||||
|
||||
return "'" + s.replace("'", "'\"'\"'") + "'"
|
||||
|
||||
def build_all_include():
|
||||
# Build a cpp file that includes all header files in this repo.
|
||||
# Otherwise header-only integrations would not be tested by clang-tidy
|
||||
headers = []
|
||||
for path in walk_files(basepath):
|
||||
filetypes = (".h",)
|
||||
ext = os.path.splitext(path)[1]
|
||||
if ext in filetypes:
|
||||
path = os.path.relpath(path, root_path)
|
||||
include_p = path.replace(os.path.sep, "/")
|
||||
headers.append(f'#include "{include_p}"')
|
||||
headers.sort()
|
||||
headers.append("")
|
||||
content = "\n".join(headers)
|
||||
p = Path(temp_header_file)
|
||||
p.parent.mkdir(exist_ok=True)
|
||||
p.write_text(content)
|
||||
|
||||
def src_files(filetypes=None):
|
||||
file_list = []
|
||||
for path in walk_files(basepath):
|
||||
ext = os.path.splitext(path)[1]
|
||||
if ext in filetypes:
|
||||
path = os.path.relpath(path, root_path)
|
||||
file_list.append(path)
|
||||
return file_list
|
||||
|
||||
def walk_files(path):
|
||||
for root, _, files in os.walk(path):
|
||||
for name in files:
|
||||
yield os.path.join(root, name)
|
||||
|
||||
def get_output(*args):
|
||||
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
output, err = proc.communicate()
|
||||
return output.decode("utf-8")
|
||||
|
||||
def get_err(*args):
|
||||
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
output, err = proc.communicate()
|
||||
return err.decode("utf-8")
|
||||
|
||||
def splitlines_no_ends(string):
|
||||
return [s.strip() for s in string.splitlines()]
|
||||
|
||||
def changed_files():
|
||||
check_remotes = ["upstream", "origin"]
|
||||
check_remotes.extend(splitlines_no_ends(get_output("git", "remote")))
|
||||
for remote in check_remotes:
|
||||
command = ["git", "merge-base", f"refs/remotes/{remote}/dev", "HEAD"]
|
||||
try:
|
||||
merge_base = splitlines_no_ends(get_output(*command))[0]
|
||||
break
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
raise ValueError("Git not configured")
|
||||
command = ["git", "diff", merge_base, "--name-only"]
|
||||
changed = splitlines_no_ends(get_output(*command))
|
||||
changed = [os.path.relpath(f, os.getcwd()) for f in changed]
|
||||
changed.sort()
|
||||
return changed
|
||||
|
||||
def filter_changed(files):
|
||||
changed = changed_files()
|
||||
files = [f for f in files if f in changed]
|
||||
print("Changed files:")
|
||||
if not files:
|
||||
print(" No changed files!")
|
||||
for c in files:
|
||||
print(f" {c}")
|
||||
return files
|
||||
|
||||
def git_ls_files(patterns=None):
|
||||
command = ["git", "ls-files", "-s"]
|
||||
if patterns is not None:
|
||||
command.extend(patterns)
|
||||
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
|
||||
output, err = proc.communicate()
|
||||
lines = [x.split() for x in output.decode("utf-8").splitlines()]
|
||||
return {s[3].strip(): int(s[0]) for s in lines}
|
||||
|
||||
def load_idedata(environment):
|
||||
platformio_ini = Path(root_path) / "platformio.ini"
|
||||
temp_idedata = Path(temp_folder) / f"idedata-{environment}.json"
|
||||
if not platformio_ini.is_file() or not temp_idedata.is_file():
|
||||
changed = True
|
||||
elif platformio_ini.stat().st_mtime >= temp_idedata.stat().st_mtime:
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
|
||||
if not changed:
|
||||
return json.loads(temp_idedata.read_text())
|
||||
|
||||
stdout = subprocess.check_output(["pio", "run", "-t", "idedata", "-e", environment])
|
||||
match = re.search(r'{\s*".*}', stdout.decode("utf-8"))
|
||||
data = json.loads(match.group())
|
||||
|
||||
temp_idedata.parent.mkdir(exist_ok=True)
|
||||
temp_idedata.write_text(json.dumps(data, indent=2) + "\n")
|
||||
return data
|
||||
0
scripts/rename_fw.py
Normal file → Executable file
0
scripts/upload_fw.py
Normal file → Executable file
496
src/command.cpp
@@ -26,150 +26,277 @@ uuid::log::Logger Command::logger_{F_(command), uuid::log::Facility::DAEMON};
|
||||
|
||||
std::vector<Command::CmdFunction> Command::cmdfunctions_;
|
||||
|
||||
// calls a command
|
||||
// id may be used to represent a heating circuit for example, it's optional
|
||||
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
|
||||
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id) {
|
||||
int8_t id_new = id;
|
||||
char cmd_new[20] = {'\0'};
|
||||
strlcpy(cmd_new, cmd, 20);
|
||||
// takes a path and a json body, parses the data and calls the command
|
||||
// the path is leading so if duplicate keys are in the input JSON it will be ignored
|
||||
// the entry point will be either via the Web API (api/) or MQTT (<base>/)
|
||||
// returns a return code and json output
|
||||
uint8_t Command::process(const char * path, const bool is_admin, const JsonObject & input, JsonObject & output) {
|
||||
SUrlParser p; // parse URL for the path names
|
||||
p.parse(path);
|
||||
|
||||
// find the command
|
||||
auto cf = find_command(device_type, cmd_new, id_new);
|
||||
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
|
||||
LOG_WARNING(F("Command %s on %s not found"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||
return CommandRet::NOT_FOUND;
|
||||
if (!p.paths().size()) {
|
||||
return message(CommandRet::ERROR, "invalid path", output);
|
||||
}
|
||||
|
||||
// check if we're allowed to call it
|
||||
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) {
|
||||
LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||
return CommandRet::NOT_ALLOWED;
|
||||
}
|
||||
|
||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||
if (value == nullptr) {
|
||||
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
|
||||
} else if (id == -1) {
|
||||
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
||||
// check first if it's from API, if so strip the "api/"
|
||||
if ((p.paths().front() == "api")) {
|
||||
p.paths().erase(p.paths().begin());
|
||||
} else {
|
||||
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||
// not /api, so must be MQTT path. Check for base and remove it.
|
||||
if (!strncmp(path, Mqtt::base().c_str(), Mqtt::base().length())) {
|
||||
char new_path[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
strncpy(new_path, path, sizeof(new_path));
|
||||
p.parse(new_path + Mqtt::base().length() + 1); // re-parse the stripped path
|
||||
} else {
|
||||
return message(CommandRet::ERROR, "unrecognized path", output); // error
|
||||
}
|
||||
}
|
||||
|
||||
return ((cf->cmdfunction_)(value, id_new)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
// Serial.println(p.path().c_str()); // dump paths, for debugging
|
||||
|
||||
// re-calculate new path
|
||||
// if there is only a path (URL) and no body then error!
|
||||
size_t num_paths = p.paths().size();
|
||||
if (!num_paths && !input.size()) {
|
||||
return message(CommandRet::ERROR, "missing command in path", output);
|
||||
}
|
||||
|
||||
std::string cmd_s;
|
||||
int8_t id_n = -1; // default hc
|
||||
|
||||
// check for a device as first item in the path
|
||||
// if its not a known device (thermostat, boiler etc) look for any special MQTT subscriptions
|
||||
const char * device_s = nullptr;
|
||||
if (!num_paths) {
|
||||
// we must look for the device in the JSON body
|
||||
if (input.containsKey("device")) {
|
||||
device_s = input["device"];
|
||||
}
|
||||
} else {
|
||||
// extract it from the path
|
||||
device_s = p.paths().front().c_str(); // get the device (boiler, thermostat, system etc)
|
||||
}
|
||||
|
||||
// validate the device, make sure it exists
|
||||
uint8_t device_type = EMSdevice::device_name_2_device_type(device_s);
|
||||
if (!device_has_commands(device_type)) {
|
||||
LOG_DEBUG(F("Command failed: unknown device '%s'"), device_s);
|
||||
return message(CommandRet::ERROR, "unknown device", output);
|
||||
}
|
||||
|
||||
// the next value on the path should be the command
|
||||
const char * command_p = nullptr;
|
||||
if (num_paths == 2) {
|
||||
command_p = p.paths()[1].c_str();
|
||||
} else if (num_paths >= 3) {
|
||||
// concatenate the path into one string as it could be in the format 'hc/XXX'
|
||||
char command[50];
|
||||
snprintf(command, sizeof(command), "%s/%s", p.paths()[1].c_str(), p.paths()[2].c_str());
|
||||
command_p = command;
|
||||
} else {
|
||||
// take it from the JSON
|
||||
if (input.containsKey("entity")) {
|
||||
command_p = input["entity"];
|
||||
} else if (input.containsKey("cmd")) {
|
||||
command_p = input["cmd"];
|
||||
}
|
||||
}
|
||||
|
||||
// some commands may be prefixed with hc. or wwc. so extract these if they exist
|
||||
// parse_command_string returns the extracted command
|
||||
command_p = parse_command_string(command_p, id_n);
|
||||
if (command_p == nullptr) {
|
||||
// handle dead endpoints like api/system or api/boiler
|
||||
// default to 'info' for SYSTEM and DALLASENSOR, the other devices to 'values' for shortname version
|
||||
if (num_paths < 3) {
|
||||
if (device_type < EMSdevice::DeviceType::BOILER) {
|
||||
command_p = "info";
|
||||
} else {
|
||||
command_p = "values";
|
||||
}
|
||||
} else {
|
||||
return message(CommandRet::NOT_FOUND, "missing or bad command", output);
|
||||
}
|
||||
}
|
||||
|
||||
// if we don't have an id/hc/wwc try and get it from the JSON input
|
||||
// it's allowed to have no id, and then keep the default to -1
|
||||
if (id_n == -1) {
|
||||
if (input.containsKey("hc")) {
|
||||
id_n = input["hc"];
|
||||
} else if (input.containsKey("wwc")) {
|
||||
id_n = input["wwc"];
|
||||
id_n += 7; // wwc1 has id 8
|
||||
} else if (input.containsKey("id")) {
|
||||
id_n = input["id"];
|
||||
}
|
||||
}
|
||||
|
||||
// the value must always come from the input JSON. It's allowed to be empty.
|
||||
JsonVariant data;
|
||||
if (input.containsKey("data")) {
|
||||
data = input["data"];
|
||||
} else if (input.containsKey("value")) {
|
||||
data = input["value"];
|
||||
}
|
||||
|
||||
// call the command based on the type
|
||||
uint8_t return_code = CommandRet::ERROR;
|
||||
if (data.is<const char *>()) {
|
||||
return_code = Command::call(device_type, command_p, data.as<const char *>(), is_admin, id_n, output);
|
||||
} else if (data.is<int>()) {
|
||||
char data_str[10];
|
||||
return_code = Command::call(device_type, command_p, Helpers::itoa(data_str, (int16_t)data.as<int>()), is_admin, id_n, output);
|
||||
} else if (data.is<float>()) {
|
||||
char data_str[10];
|
||||
return_code = Command::call(device_type, command_p, Helpers::render_value(data_str, (float)data.as<float>(), 2), is_admin, id_n, output);
|
||||
} else if (data.isNull()) {
|
||||
return_code = Command::call(device_type, command_p, "", is_admin, id_n, output); // empty, will do a query instead
|
||||
} else {
|
||||
return message(CommandRet::ERROR, "cannot parse command", output); // can't process
|
||||
}
|
||||
return return_code;
|
||||
}
|
||||
|
||||
const std::string Command::return_code_string(const uint8_t return_code) {
|
||||
switch (return_code) {
|
||||
case CommandRet::ERROR:
|
||||
return read_flash_string(F("Error"));
|
||||
break;
|
||||
case CommandRet::OK:
|
||||
return read_flash_string(F("OK"));
|
||||
break;
|
||||
case CommandRet::NOT_FOUND:
|
||||
return read_flash_string(F("Not Found"));
|
||||
break;
|
||||
case CommandRet::NOT_ALLOWED:
|
||||
return read_flash_string(F("Not Authorized"));
|
||||
break;
|
||||
case CommandRet::FAIL:
|
||||
return read_flash_string(F("Failed"));
|
||||
break;
|
||||
}
|
||||
char s[4];
|
||||
return Helpers::smallitoa(s, return_code);
|
||||
}
|
||||
|
||||
// takes a string like "hc1/seltemp" or "seltemp" or "wwc2.seltemp" and tries to get the id and cmd
|
||||
// returns start position of the command string
|
||||
const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
if (command == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// make a copy of the string command for parsing
|
||||
char command_s[100];
|
||||
strncpy(command_s, command, sizeof(command_s));
|
||||
|
||||
// look for a delimeter and split the string
|
||||
char * p = command_s;
|
||||
char * breakp = strchr(p, '.');
|
||||
if (!breakp) {
|
||||
p = command_s; // reset and look for /
|
||||
breakp = strchr(p, '/');
|
||||
if (!breakp) {
|
||||
p = command_s; // reset and look for _
|
||||
breakp = strchr(p, '_');
|
||||
if (!breakp) {
|
||||
return command;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extract the hc or wwc number
|
||||
uint8_t start_pos = breakp - p + 1;
|
||||
if (!strncmp(command, "hc", 2) && start_pos == 4) {
|
||||
id = command[start_pos - 2] - '0';
|
||||
} else if (!strncmp(command, "wwc", 3) && start_pos == 5) {
|
||||
id = command[start_pos - 2] - '0' + 7; // wwc1 has id 8
|
||||
} else {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG(F("[DEBUG] Command parse error, unknown hc/wwc in %s"), command_s);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (command + start_pos);
|
||||
}
|
||||
|
||||
// calls a command directly
|
||||
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value) {
|
||||
// create a temporary buffer
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> output_doc;
|
||||
JsonObject output = output_doc.to<JsonObject>();
|
||||
|
||||
// authenticated is always true and ID is the default value
|
||||
return call(device_type, cmd, value, true, -1, output);
|
||||
}
|
||||
|
||||
// calls a command. Takes a json object for output.
|
||||
// id may be used to represent a heating circuit for example
|
||||
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
|
||||
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & json) {
|
||||
int8_t id_new = id;
|
||||
char cmd_new[20] = {'\0'};
|
||||
strlcpy(cmd_new, cmd, 20);
|
||||
|
||||
auto cf = find_command(device_type, cmd_new, id_new);
|
||||
|
||||
// check if we're allowed to call it
|
||||
if (cf != nullptr) {
|
||||
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated && value != nullptr) {
|
||||
LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||
return CommandRet::NOT_ALLOWED; // command not allowed
|
||||
}
|
||||
}
|
||||
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject & output) {
|
||||
uint8_t return_code = CommandRet::OK;
|
||||
|
||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||
if (value == nullptr) {
|
||||
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
|
||||
} else if (id == -1) {
|
||||
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
||||
} else {
|
||||
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||
}
|
||||
|
||||
// check if json object is empty, if so quit
|
||||
if (json.isNull()) {
|
||||
LOG_WARNING(F("Ignore call for command %s in %s because it has no json body"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||
return CommandRet::ERROR;
|
||||
}
|
||||
// see if there is a command registered
|
||||
auto cf = find_command(device_type, cmd);
|
||||
|
||||
// this is for endpoints that don't have commands, i.e not writable (e.g. boiler/syspress)
|
||||
if (cf == nullptr) {
|
||||
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type) ? CommandRet::OK : CommandRet::ERROR;
|
||||
}
|
||||
// check if its a call to and end-point to a device, i.e. has no value
|
||||
// except for system commands as this is a special device without any queryable entities (device values)
|
||||
// exclude SYSTEM and DALLASSENSOR
|
||||
|
||||
if (cf->cmdfunction_json_) {
|
||||
return ((cf->cmdfunction_json_)(value, id_new, json)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
} else {
|
||||
if ((device_type != EMSdevice::DeviceType::SYSTEM) && (value == nullptr || strlen(value) == 0 || strcmp(value, "?") == 0 || strcmp(value, "*") == 0)) {
|
||||
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type) ? CommandRet::OK : CommandRet::ERROR;
|
||||
}
|
||||
return ((cf->cmdfunction_)(value, id_new)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// strip prefixes, check, and find command
|
||||
Command::CmdFunction * Command::find_command(const uint8_t device_type, char * cmd, int8_t & id) {
|
||||
// special cases for id=0 and id=-1 will be removed in V3 API
|
||||
// no command for id0
|
||||
if (id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// empty command is info with id0
|
||||
if (cmd[0] == '\0') {
|
||||
strcpy(cmd, "info");
|
||||
id = 0;
|
||||
}
|
||||
|
||||
// convert cmd to lowercase
|
||||
for (char * p = cmd; *p; p++) {
|
||||
*p = tolower(*p);
|
||||
}
|
||||
|
||||
// hack for commands that could have hc or wwc prefixed. will be removed in new API V3 eventually
|
||||
// scan for prefix hc.
|
||||
for (uint8_t i = DeviceValueTAG::TAG_HC1; i <= DeviceValueTAG::TAG_HC4; i++) {
|
||||
const char * tag = EMSdevice::tag_to_string(i).c_str();
|
||||
uint8_t len = strlen(tag);
|
||||
if (strncmp(cmd, tag, len) == 0) {
|
||||
if (cmd[len] != '\0') {
|
||||
strcpy(cmd, &cmd[len + 1]);
|
||||
} else {
|
||||
strcpy(cmd, &cmd[len]);
|
||||
}
|
||||
id = 1 + i - DeviceValueTAG::TAG_HC1;
|
||||
break;
|
||||
if ((device_type >= EMSdevice::DeviceType::BOILER) && (!value || !strlen(value))) {
|
||||
if (!cf || !cf->cmdfunction_json_) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_INFO(F("[DEBUG] Calling %s command '%s' to retrieve values"), dname.c_str(), cmd);
|
||||
#endif
|
||||
return EMSESP::get_device_value_info(output, cmd, id, device_type) ? CommandRet::OK : CommandRet::ERROR; // entity = cmd
|
||||
}
|
||||
}
|
||||
|
||||
// scan for prefix wwc.
|
||||
for (uint8_t i = DeviceValueTAG::TAG_WWC1; i <= DeviceValueTAG::TAG_WWC4; i++) {
|
||||
const char * tag = EMSdevice::tag_to_string(i).c_str();
|
||||
uint8_t len = strlen(tag);
|
||||
if (strncmp(cmd, tag, len) == 0) {
|
||||
if (cmd[len] != '\0') {
|
||||
strcpy(cmd, &cmd[len + 1]);
|
||||
} else {
|
||||
strcpy(cmd, &cmd[len]);
|
||||
}
|
||||
id = 8 + i - DeviceValueTAG::TAG_WWC1;
|
||||
break;
|
||||
if (cf) {
|
||||
// we have a matching command
|
||||
if ((value == nullptr) || !strlen(value)) {
|
||||
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
|
||||
} else if (id == -1) {
|
||||
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
||||
} else {
|
||||
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||
}
|
||||
|
||||
// check permissions
|
||||
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !is_admin) {
|
||||
output["message"] = "authentication failed";
|
||||
return CommandRet::NOT_ALLOWED; // command not allowed
|
||||
}
|
||||
|
||||
// call the function
|
||||
if (cf->cmdfunction_json_) {
|
||||
return_code = ((cf->cmdfunction_json_)(value, id, output)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
}
|
||||
if (cf->cmdfunction_) {
|
||||
return_code = ((cf->cmdfunction_)(value, id)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
}
|
||||
|
||||
// report error if call failed
|
||||
if (return_code != CommandRet::OK) {
|
||||
return message(return_code, "callback function failed", output);
|
||||
}
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
||||
// empty command after processing prefix is info
|
||||
if (cmd[0] == '\0') {
|
||||
strlcpy(cmd, "info", 20);
|
||||
}
|
||||
|
||||
return find_command(device_type, cmd);
|
||||
// we didn't find the command and its not an endpoint, report error
|
||||
LOG_DEBUG(F("Command failed: invalid command '%s'"), cmd);
|
||||
return message(CommandRet::NOT_FOUND, "invalid command", output);
|
||||
}
|
||||
|
||||
// add a command to the list, which does not return json
|
||||
// these commands are not callable directly via MQTT subscriptions either
|
||||
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, const cmd_function_p cb, const __FlashStringHelper * description, uint8_t flags) {
|
||||
// if the command already exists for that device type don't add it
|
||||
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
||||
if (find_command(device_type, read_flash_string(cmd).c_str()) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -179,26 +306,17 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, co
|
||||
}
|
||||
|
||||
cmdfunctions_.emplace_back(device_type, flags, cmd, cb, nullptr, description); // callback for json is nullptr
|
||||
|
||||
// see if we need to subscribe
|
||||
if (Mqtt::enabled()) {
|
||||
Mqtt::register_command(device_type, cmd, cb, flags);
|
||||
}
|
||||
}
|
||||
|
||||
// add a command to the list, which does return a json object as output
|
||||
// flag is fixed to MqttSubFlag::FLAG_NOSUB
|
||||
void Command::add_json(const uint8_t device_type,
|
||||
const __FlashStringHelper * cmd,
|
||||
const cmd_json_function_p cb,
|
||||
const __FlashStringHelper * description,
|
||||
uint8_t flags) {
|
||||
// flag is fixed to MqttSubFlag::MQTT_SUB_FLAG_NOSUB so there will be no topic subscribed to this
|
||||
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, const cmd_json_function_p cb, const __FlashStringHelper * description, uint8_t flags) {
|
||||
// if the command already exists for that device type don't add it
|
||||
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
||||
if (find_command(device_type, read_flash_string(cmd).c_str()) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdfunctions_.emplace_back(device_type, CommandFlag::MQTT_SUB_FLAG_NOSUB | flags, cmd, nullptr, cb, description); // callback for json is included
|
||||
cmdfunctions_.emplace_back(device_type, (CommandFlag::MQTT_SUB_FLAG_NOSUB | flags), cmd, nullptr, cb, description); // callback for json is included
|
||||
}
|
||||
|
||||
// see if a command exists for that device type
|
||||
@@ -209,14 +327,14 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
|
||||
}
|
||||
|
||||
// convert cmd to lowercase and compare
|
||||
char lowerCmd[20];
|
||||
char lowerCmd[30];
|
||||
strlcpy(lowerCmd, cmd, sizeof(lowerCmd));
|
||||
for (char * p = lowerCmd; *p; p++) {
|
||||
*p = tolower(*p);
|
||||
}
|
||||
|
||||
for (auto & cf : cmdfunctions_) {
|
||||
if (!strcmp(lowerCmd, Helpers::toLower(uuid::read_flash_string(cf.cmd_)).c_str()) && (cf.device_type_ == device_type)) {
|
||||
if (!strcmp(lowerCmd, Helpers::toLower(read_flash_string(cf.cmd_)).c_str()) && (cf.device_type_ == device_type)) {
|
||||
return &cf;
|
||||
}
|
||||
}
|
||||
@@ -225,9 +343,9 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
|
||||
}
|
||||
|
||||
// list all commands for a specific device, output as json
|
||||
bool Command::list(const uint8_t device_type, JsonObject & json) {
|
||||
bool Command::list(const uint8_t device_type, JsonObject & output) {
|
||||
if (cmdfunctions_.empty()) {
|
||||
json["message"] = "no commands available";
|
||||
output["message"] = "no commands available";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -235,15 +353,15 @@ bool Command::list(const uint8_t device_type, JsonObject & json) {
|
||||
std::list<std::string> sorted_cmds;
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
|
||||
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
|
||||
sorted_cmds.push_back(read_flash_string(cf.cmd_));
|
||||
}
|
||||
}
|
||||
sorted_cmds.sort();
|
||||
|
||||
for (auto & cl : sorted_cmds) {
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
|
||||
json[cl] = cf.description_;
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) {
|
||||
output[cl] = cf.description_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,7 +380,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
std::list<std::string> sorted_cmds;
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
|
||||
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
|
||||
sorted_cmds.push_back(read_flash_string(cf.cmd_));
|
||||
}
|
||||
}
|
||||
sorted_cmds.sort();
|
||||
@@ -282,15 +400,15 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
for (auto & cl : sorted_cmds) {
|
||||
// find and print the description
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) {
|
||||
uint8_t i = cl.length();
|
||||
shell.print(" ");
|
||||
if (cf.has_flags(MQTT_SUB_FLAG_HC)) {
|
||||
shell.print("[hc] ");
|
||||
i += 5;
|
||||
shell.print("[hc<n>.]");
|
||||
i += 8;
|
||||
} else if (cf.has_flags(MQTT_SUB_FLAG_WWC)) {
|
||||
shell.print("[wwc] ");
|
||||
i += 6;
|
||||
shell.print("[wwc<n>.]");
|
||||
i += 9;
|
||||
}
|
||||
shell.print(cl);
|
||||
// pad with spaces
|
||||
@@ -298,8 +416,12 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
shell.print(' ');
|
||||
}
|
||||
shell.print(COLOR_BRIGHT_CYAN);
|
||||
shell.print(uuid::read_flash_string(cf.description_));
|
||||
if (cf.has_flags(CommandFlag::ADMIN_ONLY)) {
|
||||
if (cf.has_flags(MQTT_SUB_FLAG_WW)) {
|
||||
shell.print(EMSdevice::tag_to_string(TAG_DEVICE_DATA_WW));
|
||||
shell.print(' ');
|
||||
}
|
||||
shell.print(read_flash_string(cf.description_));
|
||||
if (!cf.has_flags(CommandFlag::ADMIN_ONLY)) {
|
||||
shell.print(' ');
|
||||
shell.print(COLOR_BRIGHT_RED);
|
||||
shell.print('*');
|
||||
@@ -325,7 +447,7 @@ bool Command::device_has_commands(const uint8_t device_type) {
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::DALLASSENSOR) {
|
||||
return true; // we always have Sensor, but should check if there are actual sensors attached!
|
||||
return (EMSESP::sensor_devices().size() != 0);
|
||||
}
|
||||
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
@@ -363,10 +485,11 @@ void Command::show_devices(uuid::console::Shell & shell) {
|
||||
// output list of all commands to console
|
||||
// calls show with verbose mode set
|
||||
void Command::show_all(uuid::console::Shell & shell) {
|
||||
shell.println(F("Available commands: "));
|
||||
shell.println(F("Available commands (*=do not need authorization): "));
|
||||
|
||||
// show system first
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM).c_str());
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::SYSTEM, true);
|
||||
@@ -374,6 +497,7 @@ void Command::show_all(uuid::console::Shell & shell) {
|
||||
// show sensor
|
||||
if (EMSESP::have_sensors()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR).c_str());
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::DALLASSENSOR, true);
|
||||
@@ -383,6 +507,7 @@ void Command::show_all(uuid::console::Shell & shell) {
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
if (Command::device_has_commands(device_class.first)) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(device_class.first).c_str());
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, device_class.first, true);
|
||||
@@ -390,4 +515,81 @@ void Command::show_all(uuid::console::Shell & shell) {
|
||||
}
|
||||
}
|
||||
|
||||
// Extract only the path component from the passed URI and normalized it
|
||||
// e.g. //one/two////three/// becomes /one/two/three
|
||||
std::string SUrlParser::path() {
|
||||
std::string s = "/"; // set up the beginning slash
|
||||
for (std::string & f : m_folders) {
|
||||
s += f;
|
||||
s += "/";
|
||||
}
|
||||
s.pop_back(); // deleting last letter, that is slash '/'
|
||||
return std::string(s);
|
||||
}
|
||||
|
||||
SUrlParser::SUrlParser(const char * uri) {
|
||||
parse(uri);
|
||||
}
|
||||
|
||||
bool SUrlParser::parse(const char * uri) {
|
||||
m_folders.clear();
|
||||
m_keysvalues.clear();
|
||||
enum Type { begin, folder, param, value };
|
||||
std::string s;
|
||||
|
||||
const char * c = uri;
|
||||
enum Type t = Type::begin;
|
||||
std::string last_param;
|
||||
|
||||
if (c != nullptr || *c != '\0') {
|
||||
do {
|
||||
if (*c == '/') {
|
||||
if (s.length() > 0) {
|
||||
m_folders.push_back(s);
|
||||
s.clear();
|
||||
}
|
||||
t = Type::folder;
|
||||
} else if (*c == '?' && (t == Type::folder || t == Type::begin)) {
|
||||
if (s.length() > 0) {
|
||||
m_folders.push_back(s);
|
||||
s.clear();
|
||||
}
|
||||
t = Type::param;
|
||||
} else if (*c == '=' && (t == Type::param || t == Type::begin)) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
s.clear();
|
||||
t = Type::value;
|
||||
} else if (*c == '&' && (t == Type::value || t == Type::param || t == Type::begin)) {
|
||||
if (t == Type::value) {
|
||||
m_keysvalues[last_param] = s;
|
||||
} else if ((t == Type::param || t == Type::begin) && (s.length() > 0)) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
}
|
||||
t = Type::param;
|
||||
s.clear();
|
||||
} else if (*c == '\0' && s.length() > 0) {
|
||||
if (t == Type::value) {
|
||||
m_keysvalues[last_param] = s;
|
||||
} else if (t == Type::folder || t == Type::begin) {
|
||||
m_folders.push_back(s);
|
||||
} else if (t == Type::param) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
}
|
||||
s.clear();
|
||||
} else if (*c == '\0' && s.length() == 0) {
|
||||
if (t == Type::param && last_param.length() > 0) {
|
||||
m_keysvalues[last_param] = "";
|
||||
}
|
||||
s.clear();
|
||||
} else {
|
||||
s += *c;
|
||||
}
|
||||
} while (*c++ != '\0');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "console.h"
|
||||
|
||||
@@ -36,18 +37,19 @@ namespace emsesp {
|
||||
|
||||
// mqtt flags for command subscriptions
|
||||
enum CommandFlag : uint8_t {
|
||||
MQTT_SUB_FLAG_NORMAL = 0, // 0
|
||||
MQTT_SUB_FLAG_HC = (1 << 0), // 1
|
||||
MQTT_SUB_FLAG_WWC = (1 << 1), // 2
|
||||
MQTT_SUB_FLAG_NOSUB = (1 << 2), // 4
|
||||
HIDDEN = (1 << 3), // 8
|
||||
ADMIN_ONLY = (1 << 4) // 16
|
||||
MQTT_SUB_FLAG_DEFAULT = 0, // 0 no flags set, always subscribe to MQTT
|
||||
MQTT_SUB_FLAG_HC = (1 << 0), // 1 TAG_HC1 - TAG_HC4
|
||||
MQTT_SUB_FLAG_WWC = (1 << 1), // 2 TAG_WWC1 - TAG_WWC4
|
||||
MQTT_SUB_FLAG_NOSUB = (1 << 2), // 4
|
||||
MQTT_SUB_FLAG_WW = (1 << 3), // 8 TAG_DEVICE_DATA_WW
|
||||
HIDDEN = (1 << 4), // 16 do not show in API or Web
|
||||
ADMIN_ONLY = (1 << 5) // 32 requires authentication
|
||||
|
||||
};
|
||||
|
||||
// return status after calling a Command
|
||||
enum CommandRet : uint8_t {
|
||||
ERRORED = 0,
|
||||
FAIL = 0, // 0 or FALSE
|
||||
OK, // 1 or TRUE
|
||||
NOT_FOUND, // 2
|
||||
ERROR, // 3
|
||||
@@ -56,7 +58,7 @@ enum CommandRet : uint8_t {
|
||||
};
|
||||
|
||||
using cmd_function_p = std::function<bool(const char * data, const int8_t id)>;
|
||||
using cmd_json_function_p = std::function<bool(const char * data, const int8_t id, JsonObject & json)>;
|
||||
using cmd_json_function_p = std::function<bool(const char * data, const int8_t id, JsonObject & output)>;
|
||||
|
||||
class Command {
|
||||
public:
|
||||
@@ -100,35 +102,75 @@ class Command {
|
||||
return cmdfunctions_;
|
||||
}
|
||||
|
||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & json);
|
||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id = -1);
|
||||
#define add_
|
||||
|
||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject & output);
|
||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value);
|
||||
|
||||
// with normal call back function taking a value and id
|
||||
static void add(const uint8_t device_type,
|
||||
const __FlashStringHelper * cmd,
|
||||
const cmd_function_p cb,
|
||||
const __FlashStringHelper * description,
|
||||
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL);
|
||||
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
|
||||
|
||||
static void add_json(const uint8_t device_type,
|
||||
const __FlashStringHelper * cmd,
|
||||
const cmd_json_function_p cb,
|
||||
const __FlashStringHelper * description,
|
||||
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL);
|
||||
// callback function taking value, id and a json object for its output
|
||||
static void add(const uint8_t device_type,
|
||||
const __FlashStringHelper * cmd,
|
||||
const cmd_json_function_p cb,
|
||||
const __FlashStringHelper * description,
|
||||
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
|
||||
|
||||
static void show_all(uuid::console::Shell & shell);
|
||||
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
|
||||
static Command::CmdFunction * find_command(const uint8_t device_type, char * cmd, int8_t & id);
|
||||
|
||||
static void show(uuid::console::Shell & shell, uint8_t device_type, bool verbose);
|
||||
static void show_devices(uuid::console::Shell & shell);
|
||||
static bool device_has_commands(const uint8_t device_type);
|
||||
|
||||
static bool list(const uint8_t device_type, JsonObject & json);
|
||||
static bool list(const uint8_t device_type, JsonObject & output);
|
||||
|
||||
static uint8_t process(const char * path, const bool is_admin, const JsonObject & input, JsonObject & output);
|
||||
|
||||
static const char * parse_command_string(const char * command, int8_t & id);
|
||||
|
||||
static const std::string return_code_string(const uint8_t return_code);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static std::vector<CmdFunction> cmdfunctions_; // list of commands
|
||||
static std::vector<CmdFunction> cmdfunctions_; // the list of commands
|
||||
|
||||
inline static uint8_t message(uint8_t error_code, const char * message, JsonObject & output) {
|
||||
output.clear();
|
||||
output["message"] = (const char *)message;
|
||||
return error_code;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unordered_map<std::string, std::string> KeyValueMap_t;
|
||||
typedef std::vector<std::string> Folder_t;
|
||||
|
||||
class SUrlParser {
|
||||
private:
|
||||
KeyValueMap_t m_keysvalues;
|
||||
Folder_t m_folders;
|
||||
|
||||
public:
|
||||
SUrlParser(){};
|
||||
SUrlParser(const char * url);
|
||||
|
||||
bool parse(const char * url);
|
||||
|
||||
Folder_t & paths() {
|
||||
return m_folders;
|
||||
};
|
||||
|
||||
KeyValueMap_t & params() {
|
||||
return m_keysvalues;
|
||||
};
|
||||
|
||||
std::string path();
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
114
src/console.cpp
@@ -77,7 +77,7 @@ void EMSESPShell::display_banner() {
|
||||
|
||||
if (console_hostname_.empty()) {
|
||||
console_hostname_.resize(16, '\0');
|
||||
snprintf_P(&console_hostname_[0], console_hostname_.capacity() + 1, PSTR("ems-esp"));
|
||||
snprintf(&console_hostname_[0], console_hostname_.capacity() + 1, "ems-esp");
|
||||
}
|
||||
|
||||
// load the list of commands
|
||||
@@ -235,19 +235,25 @@ void EMSESPShell::add_console_commands() {
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||
char buffer[4];
|
||||
shell.printfln(F_(master_thermostat_fmt),
|
||||
settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str()
|
||||
settings.master_thermostat == 0 ? read_flash_string(F_(auto)).c_str()
|
||||
: Helpers::hextoa(buffer, settings.master_thermostat));
|
||||
shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str());
|
||||
});
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(read)},
|
||||
flash_string_vector{F_(deviceid_mandatory), F_(typeid_mandatory), F_(offset_optional)},
|
||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
||||
uint16_t type_id = Helpers::hextoint(arguments[1].c_str());
|
||||
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
||||
|
||||
if (!EMSESP::valid_device(device_id)) {
|
||||
shell.printfln(F("Invalid device ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t type_id = Helpers::hextoint(arguments[1].c_str());
|
||||
if (arguments.size() == 4) {
|
||||
uint16_t offset = Helpers::hextoint(arguments[2].c_str());
|
||||
uint8_t length = Helpers::hextoint(arguments.back().c_str());
|
||||
@@ -272,8 +278,7 @@ void EMSESPShell::add_console_commands() {
|
||||
settings.master_thermostat = value;
|
||||
EMSESP::actual_master_thermostat(value); // set the internal value too
|
||||
char buffer[5];
|
||||
shell.printfln(F_(master_thermostat_fmt),
|
||||
!value ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
|
||||
shell.printfln(F_(master_thermostat_fmt), !value ? read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
@@ -373,8 +378,6 @@ void EMSESPShell::add_console_commands() {
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
// validate that a command is present
|
||||
if (arguments.size() < 2) {
|
||||
@@ -383,37 +386,45 @@ void EMSESPShell::add_console_commands() {
|
||||
return;
|
||||
}
|
||||
|
||||
const char * cmd = arguments[1].c_str();
|
||||
|
||||
uint8_t cmd_return = CommandRet::OK;
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
int8_t id = -1;
|
||||
const char * cmd = Command::parse_command_string(arguments[1].c_str(), id);
|
||||
uint8_t return_code = CommandRet::OK;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
if (arguments.size() == 2) {
|
||||
// no value specified, just the cmd
|
||||
cmd_return = Command::call(device_type, cmd, nullptr, true, -1, json);
|
||||
return_code = Command::call(device_type, cmd, nullptr, true, id, json);
|
||||
} else if (arguments.size() == 3) {
|
||||
if (strncmp(cmd, "info", 4) == 0) {
|
||||
// info has a id but no value
|
||||
cmd_return = Command::call(device_type, cmd, nullptr, true, atoi(arguments.back().c_str()), json);
|
||||
return_code = Command::call(device_type, cmd, nullptr, true, atoi(arguments.back().c_str()), json);
|
||||
} else if (arguments[2] == "?") {
|
||||
return_code = Command::call(device_type, cmd, nullptr, true, id, json);
|
||||
} else {
|
||||
// has a value but no id so use -1
|
||||
cmd_return = Command::call(device_type, cmd, arguments.back().c_str(), true, -1, json);
|
||||
return_code = Command::call(device_type, cmd, arguments.back().c_str(), true, id, json);
|
||||
}
|
||||
} else {
|
||||
// use value, which could be an id or hc
|
||||
cmd_return = Command::call(device_type, cmd, arguments[2].c_str(), true, atoi(arguments[3].c_str()), json);
|
||||
if (arguments[2] == "?") {
|
||||
return_code = Command::call(device_type, cmd, nullptr, true, atoi(arguments[3].c_str()), json);
|
||||
} else {
|
||||
return_code = Command::call(device_type, cmd, arguments[2].c_str(), true, atoi(arguments[3].c_str()), json);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_return == CommandRet::OK && json.size()) {
|
||||
if (return_code == CommandRet::OK && json.size()) {
|
||||
serializeJsonPretty(doc, shell);
|
||||
shell.println();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd_return == CommandRet::NOT_FOUND) {
|
||||
if (return_code == CommandRet::NOT_FOUND) {
|
||||
shell.println(F("Unknown command"));
|
||||
shell.print(F("Available commands are: "));
|
||||
Command::show(shell, device_type, false); // non-verbose mode
|
||||
} else if (cmd_return != CommandRet::OK) {
|
||||
} else if (return_code != CommandRet::OK) {
|
||||
shell.println(F("Bad syntax"));
|
||||
}
|
||||
},
|
||||
@@ -434,7 +445,7 @@ void EMSESPShell::add_console_commands() {
|
||||
if (Command::device_has_commands(device_type)) {
|
||||
for (const auto & cf : Command::commands()) {
|
||||
if (cf.device_type_ == device_type) {
|
||||
command_list.emplace_back(uuid::read_flash_string(cf.cmd_));
|
||||
command_list.emplace_back(read_flash_string(cf.cmd_));
|
||||
}
|
||||
}
|
||||
return command_list;
|
||||
@@ -596,7 +607,7 @@ void Console::load_system_commands(unsigned int context) {
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(restart)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::system_.restart();
|
||||
EMSESP::system_.system_restart();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context,
|
||||
@@ -717,7 +728,7 @@ void Console::load_system_commands(unsigned int context) {
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (!settings.sensor[i].id.isEmpty()) {
|
||||
shell.print(settings.sensor[i].id);
|
||||
shell.print(" : ");
|
||||
@@ -751,32 +762,33 @@ void Console::load_system_commands(unsigned int context) {
|
||||
EMSESP::dallassensor_.update(arguments.front().c_str(), arguments[1].c_str(), offset);
|
||||
});
|
||||
|
||||
EMSESPShell::commands
|
||||
->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(board_profile)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
||||
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
||||
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, TLK110, LAN8720, CUSTOM)"));
|
||||
return;
|
||||
}
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.board_profile = board_profile.c_str();
|
||||
settings.led_gpio = data[0];
|
||||
settings.dallas_gpio = data[1];
|
||||
settings.rx_gpio = data[2];
|
||||
settings.tx_gpio = data[3];
|
||||
settings.pbutton_gpio = data[4];
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4]);
|
||||
EMSESP::system_.network_init(true);
|
||||
});
|
||||
EMSESPShell::commands->add_command(
|
||||
context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(board_profile)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
||||
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
||||
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, CUSTOM)"));
|
||||
return;
|
||||
}
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.board_profile = board_profile.c_str();
|
||||
settings.led_gpio = data[0];
|
||||
settings.dallas_gpio = data[1];
|
||||
settings.rx_gpio = data[2];
|
||||
settings.tx_gpio = data[3];
|
||||
settings.pbutton_gpio = data[4];
|
||||
settings.phy_type = data[5];
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
EMSESP::system_.network_init(true);
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(show), F_(users)},
|
||||
@@ -811,14 +823,14 @@ std::string EMSESPShell::prompt_suffix() {
|
||||
}
|
||||
|
||||
void EMSESPShell::end_of_transmission() {
|
||||
invoke_command(uuid::read_flash_string(F_(exit)));
|
||||
invoke_command(read_flash_string(F_(exit)));
|
||||
}
|
||||
|
||||
EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, bool local)
|
||||
: uuid::console::Shell(commands, ShellContext::MAIN, local ? (CommandFlags::USER | CommandFlags::LOCAL) : CommandFlags::USER)
|
||||
, uuid::console::StreamConsole(stream)
|
||||
, EMSESPShell()
|
||||
, name_(uuid::read_flash_string(F("Serial")))
|
||||
, name_(read_flash_string(F("Serial")))
|
||||
, pty_(SIZE_MAX)
|
||||
, addr_()
|
||||
, port_(0) {
|
||||
@@ -841,7 +853,7 @@ EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, const IPAddress & addr
|
||||
ptys_[pty_] = true;
|
||||
}
|
||||
|
||||
snprintf_P(text.data(), text.size(), PSTR("pty%u"), pty_);
|
||||
snprintf(text.data(), text.size(), "pty%u", pty_);
|
||||
name_ = text.data();
|
||||
#ifndef EMSESP_STANDALONE
|
||||
logger().info(F("Allocated console %s for connection from [%s]:%u"), name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);
|
||||
|
||||
@@ -41,15 +41,15 @@ void DallasSensor::start() {
|
||||
bus_.begin(dallas_gpio_);
|
||||
#endif
|
||||
// API calls
|
||||
Command::add_json(
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
F_(info),
|
||||
[&](const char * value, const int8_t id, JsonObject & json) { return command_info(value, id, json); },
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
|
||||
F_(info_cmd));
|
||||
Command::add_json(
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
F_(commands),
|
||||
[&](const char * value, const int8_t id, JsonObject & json) { return command_commands(value, id, json); },
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
|
||||
F_(commands_cmd));
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ void DallasSensor::loop() {
|
||||
if (state_ == State::IDLE) {
|
||||
if (time_now - last_activity_ >= READ_INTERVAL_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_DEBUG(F("Read sensor temperature"));
|
||||
LOG_DEBUG(F("[DEBUG] Read sensor temperature"));
|
||||
#endif
|
||||
if (bus_.reset() || parasite_) {
|
||||
YIELD;
|
||||
@@ -210,9 +210,6 @@ bool DallasSensor::temperature_convert_complete() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!bus_.reset()) {
|
||||
@@ -276,8 +273,6 @@ int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
const std::vector<DallasSensor::Sensor> DallasSensor::sensors() const {
|
||||
return sensors_;
|
||||
}
|
||||
@@ -299,13 +294,13 @@ uint64_t DallasSensor::Sensor::id() const {
|
||||
|
||||
std::string DallasSensor::Sensor::id_string() const {
|
||||
std::string str(20, '\0');
|
||||
snprintf_P(&str[0],
|
||||
str.capacity() + 1,
|
||||
PSTR("%02X-%04X-%04X-%04X"),
|
||||
(unsigned int)(id_ >> 48) & 0xFF,
|
||||
(unsigned int)(id_ >> 32) & 0xFFFF,
|
||||
(unsigned int)(id_ >> 16) & 0xFFFF,
|
||||
(unsigned int)(id_)&0xFFFF);
|
||||
snprintf(&str[0],
|
||||
str.capacity() + 1,
|
||||
"%02X-%04X-%04X-%04X",
|
||||
(unsigned int)(id_ >> 48) & 0xFF,
|
||||
(unsigned int)(id_ >> 32) & 0xFFFF,
|
||||
(unsigned int)(id_ >> 16) & 0xFFFF,
|
||||
(unsigned int)(id_)&0xFFFF);
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -313,7 +308,7 @@ std::string DallasSensor::Sensor::to_string(const bool name) const {
|
||||
std::string str = id_string();
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
if (settings.dallas_format == Dallas_Format::NAME || name) {
|
||||
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) {
|
||||
str = settings.sensor[i].name.c_str();
|
||||
}
|
||||
@@ -328,7 +323,7 @@ int16_t DallasSensor::Sensor::offset() const {
|
||||
std::string str = id_string();
|
||||
int16_t offset = 0; // default value
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) {
|
||||
offset = settings.sensor[i].offset;
|
||||
}
|
||||
@@ -347,7 +342,7 @@ void DallasSensor::delete_ha_config(uint8_t index, const char * name) {
|
||||
std::string topicname = name;
|
||||
std::replace(topicname.begin(), topicname.end(), '-', '_');
|
||||
|
||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/dallassensor_%s/config"), Mqtt::base().c_str(), topicname.c_str());
|
||||
snprintf(topic, sizeof(topic), "homeassistant/sensor/%s/dallassensor_%s/config", Mqtt::base().c_str(), topicname.c_str());
|
||||
Mqtt::publish(topic);
|
||||
registered_ha_[index] = false; // forces a recreate of the HA config topic
|
||||
}
|
||||
@@ -376,7 +371,7 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset)
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
// check for new name of stored id
|
||||
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (strcmp(id, settings.sensor[i].id.c_str()) == 0) {
|
||||
if (strlen(name) == 0 && offset == 0) { // delete entry if name and offset is empty
|
||||
LOG_INFO(F("Deleting entry for sensor %s"), id);
|
||||
@@ -397,7 +392,7 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset)
|
||||
}
|
||||
|
||||
// check for free place
|
||||
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (settings.sensor[i].id.isEmpty()) {
|
||||
settings.sensor[i].id = id;
|
||||
settings.sensor[i].name = (strlen(name) == 0) ? id : name;
|
||||
@@ -410,7 +405,7 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset)
|
||||
}
|
||||
|
||||
// check if there is a unused id and overwrite it
|
||||
for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
bool found = false;
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (strcmp(sensor.id_string().c_str(), settings.sensor[i].id.c_str()) == 0) {
|
||||
@@ -446,14 +441,14 @@ bool DallasSensor::updated_values() {
|
||||
}
|
||||
|
||||
// list commands
|
||||
bool DallasSensor::command_commands(const char * value, const int8_t id, JsonObject & json) {
|
||||
return Command::list(EMSdevice::DeviceType::DALLASSENSOR, json);
|
||||
bool DallasSensor::command_commands(const char * value, const int8_t id, JsonObject & output) {
|
||||
return Command::list(EMSdevice::DeviceType::DALLASSENSOR, output);
|
||||
}
|
||||
|
||||
// creates JSON doc from values
|
||||
// returns false if empty
|
||||
// e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":23.30},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":24.0}}
|
||||
bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject & json) {
|
||||
bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject & output) {
|
||||
if (sensors_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -461,23 +456,23 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
|
||||
uint8_t i = 1; // sensor count
|
||||
for (const auto & sensor : sensors_) {
|
||||
char sensorID[10]; // sensor{1-n}
|
||||
snprintf_P(sensorID, 10, PSTR("sensor%d"), i++);
|
||||
snprintf(sensorID, 10, "sensor%d", i++);
|
||||
if (id == -1) { // show number and id
|
||||
JsonObject dataSensor = json.createNestedObject(sensorID);
|
||||
JsonObject dataSensor = output.createNestedObject(sensorID);
|
||||
dataSensor["id"] = sensor.to_string();
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
} else { // show according to format
|
||||
if (dallas_format_ == Dallas_Format::NUMBER && Helpers::hasValue(sensor.temperature_c)) {
|
||||
json[sensorID] = (float)(sensor.temperature_c) / 10;
|
||||
output[sensorID] = (float)(sensor.temperature_c) / 10;
|
||||
} else if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
json[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
|
||||
output[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (json.size() > 0);
|
||||
return (output.size() > 0);
|
||||
}
|
||||
|
||||
// send all dallas sensor values as a JSON package to MQTT
|
||||
@@ -493,7 +488,7 @@ void DallasSensor::publish_values(const bool force) {
|
||||
|
||||
for (const auto & sensor : sensors_) {
|
||||
char sensorID[10]; // sensor{1-n}
|
||||
snprintf_P(sensorID, 10, PSTR("sensor%d"), sensor_no);
|
||||
snprintf(sensorID, 10, "sensor%d", sensor_no);
|
||||
if (dallas_format_ == Dallas_Format::NUMBER) {
|
||||
// e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03","temp":23.3},"sensor2":{"id":"28-233D-9497-0C03","temp":24.0}}
|
||||
JsonObject dataSensor = doc.createNestedObject(sensorID);
|
||||
@@ -513,28 +508,28 @@ void DallasSensor::publish_values(const bool force) {
|
||||
config["dev_cla"] = FJSON("temperature");
|
||||
|
||||
char stat_t[50];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/dallassensor_data"), Mqtt::base().c_str());
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
config["unit_of_meas"] = FJSON("°C");
|
||||
|
||||
char str[50];
|
||||
if (dallas_format_ != Dallas_Format::NUMBER) {
|
||||
snprintf_P(str, sizeof(str), PSTR("{{value_json['%s']}}"), sensor.to_string().c_str());
|
||||
snprintf(str, sizeof(str), "{{value_json['%s']}}", sensor.to_string().c_str());
|
||||
} else {
|
||||
snprintf_P(str, sizeof(str), PSTR("{{value_json.sensor%d.temp}}"), sensor_no);
|
||||
snprintf(str, sizeof(str), "{{value_json.sensor%d.temp}}", sensor_no);
|
||||
}
|
||||
config["val_tpl"] = str;
|
||||
|
||||
// name as sensor number not the long unique ID
|
||||
if (dallas_format_ != Dallas_Format::NUMBER) {
|
||||
snprintf_P(str, sizeof(str), PSTR("Dallas Sensor %s"), sensor.to_string().c_str());
|
||||
snprintf(str, sizeof(str), "Dallas Sensor %s", sensor.to_string().c_str());
|
||||
} else {
|
||||
snprintf_P(str, sizeof(str), PSTR("Dallas Sensor %d"), sensor_no);
|
||||
snprintf(str, sizeof(str), "Dallas Sensor %d", sensor_no);
|
||||
}
|
||||
config["name"] = str;
|
||||
|
||||
snprintf_P(str, sizeof(str), PSTR("dallasensor_%s"), sensor.to_string().c_str());
|
||||
snprintf(str, sizeof(str), "dallasensor_%s", sensor.to_string().c_str());
|
||||
config["uniq_id"] = str;
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
@@ -546,12 +541,12 @@ void DallasSensor::publish_values(const bool force) {
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (dallas_format_ == Dallas_Format::NUMBER) {
|
||||
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallassensor_%d/config"), Mqtt::base().c_str(), sensor_no);
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%d/config", Mqtt::base().c_str(), sensor_no);
|
||||
} else {
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string topicname = sensor.to_string();
|
||||
std::replace(topicname.begin(), topicname.end(), '-', '_');
|
||||
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallassensor_%s/config"), Mqtt::base().c_str(), topicname.c_str());
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::base().c_str(), topicname.c_str());
|
||||
}
|
||||
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
||||
|
||||
|
||||
@@ -130,8 +130,8 @@ class DallasSensor {
|
||||
int16_t get_temperature_c(const uint8_t addr[]);
|
||||
uint64_t get_id(const uint8_t addr[]);
|
||||
|
||||
bool command_info(const char * value, const int8_t id, JsonObject & json);
|
||||
bool command_commands(const char * value, const int8_t id, JsonObject & json);
|
||||
bool command_info(const char * value, const int8_t id, JsonObject & output);
|
||||
bool command_commands(const char * value, const int8_t id, JsonObject & output);
|
||||
|
||||
void delete_ha_config(uint8_t index, const char * name);
|
||||
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_TX_DELAY
|
||||
#define EMSESP_DEFAULT_TX_DELAY 0 // no delay
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_EMS_BUS_ID
|
||||
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
|
||||
#endif
|
||||
@@ -96,7 +92,7 @@
|
||||
#define EMSESP_DEFAULT_BOARD_PROFILE "S32" // Gateway S32
|
||||
#endif
|
||||
|
||||
// Default GPIO PIN definitions - based on Wemos/Nodemcu
|
||||
// Default GPIO PIN definitions
|
||||
|
||||
#ifndef EMSESP_DEFAULT_RX_GPIO
|
||||
#define EMSESP_DEFAULT_RX_GPIO 23 // D7
|
||||
@@ -118,6 +114,10 @@
|
||||
#define EMSESP_DEFAULT_PBUTTON_GPIO 0
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PHY_TYPE
|
||||
#define EMSESP_DEFAULT_PHY_TYPE 0 // No Ethernet, just Wifi
|
||||
#endif
|
||||
|
||||
// MQTT
|
||||
|
||||
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
|
||||
@@ -152,8 +152,8 @@
|
||||
#define EMSESP_DEFAULT_NESTED_FORMAT 1
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SUBSCRIBE_FORMAT
|
||||
#define EMSESP_DEFAULT_SUBSCRIBE_FORMAT 0
|
||||
#ifndef EMSESP_DEFAULT_SEND_RESPONSE
|
||||
#define EMSESP_DEFAULT_SEND_RESPONSE false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SOLAR_MAXFLOW
|
||||
@@ -164,4 +164,16 @@
|
||||
#define EMSESP_DEFAULT_SENSOR_NAME ""
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_WEBLOG_LEVEL
|
||||
#define EMSESP_DEFAULT_WEBLOG_LEVEL 6 // INFO
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_WEBLOG_BUFFER
|
||||
#define EMSESP_DEFAULT_WEBLOG_BUFFER 50
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_WEBLOG_COMPACT
|
||||
#define EMSESP_DEFAULT_WEBLOG_COMPACT true
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
{133, DeviceType::BOILER, F("Logano GB125/KB195i/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{167, DeviceType::BOILER, F("Cerapur Aero"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
|
||||
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco/Geo 5xx"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
|
||||
{173, DeviceType::BOILER, F("Geo 5xx"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
|
||||
{195, DeviceType::BOILER, F("Condens 5000i/Greenstar 8000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{208, DeviceType::BOILER, F("Logamax Plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
@@ -61,13 +62,15 @@
|
||||
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
|
||||
{224, DeviceType::CONTROLLER, F("9000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{240, DeviceType::CONTROLLER, F("Rego 3000"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{241, DeviceType::CONTROLLER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
|
||||
// Thermostat - not currently supporting write operations, like the Easy/100 types - 0x18
|
||||
{202, DeviceType::THERMOSTAT, F("Logamatic TC100/Moduline Easy"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
|
||||
{203, DeviceType::THERMOSTAT, F("EasyControl CT200"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
|
||||
|
||||
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19 / 0x38
|
||||
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19-0x1B for hc2-4 / 0x38
|
||||
{ 65, DeviceType::THERMOSTAT, F("RC10"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N},// 0x17
|
||||
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_N},// 0x10 - based on RC35
|
||||
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
|
||||
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
|
||||
@@ -77,14 +80,16 @@
|
||||
{ 90, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
|
||||
{ 94, DeviceType::THERMOSTAT, F("RFM20 Remote"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
|
||||
{151, DeviceType::THERMOSTAT, F("RC25"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
|
||||
{158, DeviceType::THERMOSTAT, F("RC300/RC310/Moduline 3000/1010H/CW400/Sense II"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
|
||||
{172, DeviceType::THERMOSTAT, F("Rego 2000/3000"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
{216, DeviceType::THERMOSTAT, F("CRF200S"), DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
|
||||
|
||||
// Thermostat - Sieger - 0x10 / 0x17
|
||||
{ 66, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
|
||||
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
||||
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC30_N}, // 0x10
|
||||
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||
|
||||
// Thermostat - Junkers - 0x10
|
||||
@@ -92,7 +97,7 @@
|
||||
{106, DeviceType::THERMOSTAT, F("FW200"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{107, DeviceType::THERMOSTAT, F("FR100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
|
||||
{108, DeviceType::THERMOSTAT, F("FR110"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
|
||||
{111, DeviceType::THERMOSTAT, F("FR10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{111, DeviceType::THERMOSTAT, F("FR10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
|
||||
{147, DeviceType::THERMOSTAT, F("FR50"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD},
|
||||
{191, DeviceType::THERMOSTAT, F("FR120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
|
||||
{192, DeviceType::THERMOSTAT, F("FW120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
@@ -110,6 +115,7 @@
|
||||
{159, DeviceType::MIXER, F("MM50"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{160, DeviceType::MIXER, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{161, DeviceType::MIXER, F("MM200"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{204, DeviceType::MIXER, F("MP100"), DeviceFlags::EMS_DEVICE_FLAG_MP},
|
||||
|
||||
// Heat Pumps - 0x38
|
||||
{200, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
@@ -120,10 +126,16 @@
|
||||
{205, DeviceType::CONNECT, F("Moduline Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{206, DeviceType::CONNECT, F("Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// wireless sensor base- 0x50
|
||||
{236, DeviceType::CONNECT, F("Wireless sensor base"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Switches - 0x11
|
||||
{ 71, DeviceType::SWITCH, F("WM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Gateways - 0x48
|
||||
{189, DeviceType::GATEWAY, F("KM200/MB LAN 2"), DeviceFlags::EMS_DEVICE_FLAG_NONE}
|
||||
{189, DeviceType::GATEWAY, F("KM200/MB LAN 2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// generic - 0x40 or other with no product-id and no version
|
||||
{0, DeviceType::GENERIC, F("unknown"), DeviceFlags::EMS_DEVICE_FLAG_NONE}
|
||||
|
||||
// clang-format on
|
||||
|
||||
@@ -26,8 +26,6 @@ uuid::log::Logger Boiler::logger_{F_(boiler), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
LOG_DEBUG(F("Adding new Boiler with device ID 0x%02X"), device_id);
|
||||
|
||||
// cascaded heatingsources, only some values per individual heatsource (hs)
|
||||
if (device_id != EMSdevice::EMS_DEVICE_ID_BOILER) {
|
||||
uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_BOILER_1; // heating source id, count from 0
|
||||
@@ -44,6 +42,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, FL_(curBurnPow), DeviceValueUOM::PERCENT);
|
||||
return;
|
||||
}
|
||||
|
||||
// register values for master boiler/cascade module
|
||||
reserve_telgram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation
|
||||
|
||||
@@ -51,12 +50,13 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
// common for all boilers
|
||||
register_telegram_type(0x10, F("UBAErrorMessage1"), false, MAKE_PF_CB(process_UBAErrorMessage));
|
||||
register_telegram_type(0x11, F("UBAErrorMessage2"), false, MAKE_PF_CB(process_UBAErrorMessage));
|
||||
register_telegram_type(0xC2, F("UBAErrorMessage3"), false, MAKE_PF_CB(process_UBAErrorMessage2));
|
||||
register_telegram_type(0x14, F("UBATotalUptime"), true, MAKE_PF_CB(process_UBATotalUptime));
|
||||
register_telegram_type(0x15, F("UBAMaintenanceData"), false, MAKE_PF_CB(process_UBAMaintenanceData));
|
||||
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, MAKE_PF_CB(process_UBAMaintenanceStatus));
|
||||
// EMS1.0 and maybe EMS+?
|
||||
register_telegram_type(0x18, F("UBAMonitorFast"), false, MAKE_PF_CB(process_UBAMonitorFast));
|
||||
register_telegram_type(0x19, F("UBAMonitorSlow"), true, MAKE_PF_CB(process_UBAMonitorSlow));
|
||||
register_telegram_type(0x19, F("UBAMonitorSlow"), false, MAKE_PF_CB(process_UBAMonitorSlow));
|
||||
register_telegram_type(0x1A, F("UBASetPoints"), false, MAKE_PF_CB(process_UBASetPoints));
|
||||
register_telegram_type(0x35, F("UBAFlags"), false, MAKE_PF_CB(process_UBAFlags));
|
||||
// only EMS 1.0
|
||||
@@ -68,21 +68,24 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_telegram_type(0x26, F("UBASettingsWW"), true, MAKE_PF_CB(process_UBASettingsWW));
|
||||
register_telegram_type(0x2A, F("MC110Status"), false, MAKE_PF_CB(process_MC110Status));
|
||||
}
|
||||
|
||||
// only EMS+
|
||||
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS && model() != EMSdevice::EMS_DEVICE_FLAG_HT3) {
|
||||
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, MAKE_PF_CB(process_UBAOutdoorTemp));
|
||||
register_telegram_type(0xE3, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus2));
|
||||
register_telegram_type(0xE3, F("UBAMonitorSlowPlus2"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus2));
|
||||
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
|
||||
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus));
|
||||
register_telegram_type(0xE6, F("UBAParametersPlus"), true, MAKE_PF_CB(process_UBAParametersPlus));
|
||||
register_telegram_type(0xE9, F("UBAMonitorWWPlus"), false, MAKE_PF_CB(process_UBAMonitorWWPlus));
|
||||
register_telegram_type(0xEA, F("UBAParameterWWPlus"), true, MAKE_PF_CB(process_UBAParameterWWPlus));
|
||||
}
|
||||
|
||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) {
|
||||
register_telegram_type(0x494, F("UBAEnergySupplied"), false, MAKE_PF_CB(process_UBAEnergySupplied));
|
||||
register_telegram_type(0x495, F("UBAInformation"), false, MAKE_PF_CB(process_UBAInformation));
|
||||
register_telegram_type(0x48D, F("HpPower"), false, MAKE_PF_CB(process_HpPower));
|
||||
register_telegram_type(0x48D, F("HpPower"), true, MAKE_PF_CB(process_HpPower));
|
||||
register_telegram_type(0x48F, F("HpOutdoor"), false, MAKE_PF_CB(process_HpOutdoor));
|
||||
register_telegram_type(0x48A, F("HpPool"), true, MAKE_PF_CB(process_HpPool));
|
||||
}
|
||||
|
||||
// MQTT commands for boiler topic
|
||||
@@ -96,13 +99,14 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
DeviceValueType::CMD,
|
||||
FL_(enum_bool),
|
||||
FL_(wwtapactivated),
|
||||
DeviceValueUOM::BOOLEAN,
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_tapwarmwater_activated));
|
||||
// reset is a command, so uses a dummy variable which is unused. It will not be shown in MQTT, Web or Console
|
||||
register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::LIST, MAKE_CF_CB(set_reset));
|
||||
|
||||
register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::BOOLEAN);
|
||||
// reset is a command, so uses a dummy variable which is unused. It will not be shown in MQTT, Web or Console
|
||||
register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset));
|
||||
|
||||
register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, nullptr, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp));
|
||||
register_device_value(TAG_BOILER_DATA, &selBurnPow_, DeviceValueType::UINT, nullptr, FL_(selBurnPow), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_burn_power));
|
||||
register_device_value(TAG_BOILER_DATA, &heatingPumpMod_, DeviceValueType::UINT, nullptr, FL_(heatingPumpMod), DeviceValueUOM::PERCENT);
|
||||
@@ -114,13 +118,13 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA, &sysPress_, DeviceValueType::UINT, FL_(div10), FL_(sysPress), DeviceValueUOM::BAR);
|
||||
register_device_value(TAG_BOILER_DATA, &boilTemp_, DeviceValueType::USHORT, FL_(div10), FL_(boilTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &exhaustTemp_, DeviceValueType::USHORT, FL_(div10), FL_(exhaustTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, FL_(burnGas), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, FL_(burnGas), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, FL_(div10), FL_(flameCurr), DeviceValueUOM::UA);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, FL_(heatingPump), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, FL_(fanWork), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, FL_(ignWork), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, FL_(heatingPump), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, FL_(fanWork), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, FL_(ignWork), DeviceValueUOM::NONE);
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, FL_(heatingActivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_heating_activated));
|
||||
TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, FL_(heatingActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_heating_activated));
|
||||
register_device_value(TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, nullptr, FL_(heatingTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heating_temp));
|
||||
register_device_value(TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, nullptr, FL_(pumpModMax), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_pump));
|
||||
register_device_value(TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, nullptr, FL_(pumpModMin), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_pump));
|
||||
@@ -133,46 +137,67 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA, &setFlowTemp_, DeviceValueType::UINT, nullptr, FL_(setFlowTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &setBurnPow_, DeviceValueType::UINT, nullptr, FL_(setBurnPow), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA, &curBurnPow_, DeviceValueType::UINT, nullptr, FL_(curBurnPow), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, FL_(burnStarts), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, FL_(burnStarts), DeviceValueUOM::TIMES);
|
||||
register_device_value(TAG_BOILER_DATA, &burnWorkMin_, DeviceValueType::TIME, nullptr, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &heatWorkMin_, DeviceValueType::TIME, nullptr, FL_(heatWorkMin), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &UBAuptime_, DeviceValueType::TIME, nullptr, FL_(UBAuptime), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, FL_(lastCode), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &serviceCode_, DeviceValueType::TEXT, nullptr, FL_(serviceCode), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &lastCode_, DeviceValueType::STRING, nullptr, FL_(lastCode), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &serviceCode_, DeviceValueType::STRING, nullptr, FL_(serviceCode), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &serviceCodeNumber_, DeviceValueType::USHORT, nullptr, FL_(serviceCodeNumber), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &maintenanceMessage_, DeviceValueType::TEXT, nullptr, FL_(maintenanceMessage), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &maintenanceMessage_, DeviceValueType::STRING, nullptr, FL_(maintenanceMessage), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA,
|
||||
&maintenanceType_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_off_time_date),
|
||||
FL_(maintenanceType),
|
||||
DeviceValueUOM::LIST,
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_maintenance));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA, &maintenanceTime_, DeviceValueType::USHORT, nullptr, FL_(maintenanceTime), DeviceValueUOM::HOURS, MAKE_CF_CB(set_maintenancetime));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::TEXT, nullptr, FL_(maintenanceDate), DeviceValueUOM::NONE, MAKE_CF_CB(set_maintenancedate));
|
||||
TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::STRING, nullptr, FL_(maintenanceDate), DeviceValueUOM::NONE, MAKE_CF_CB(set_maintenancedate));
|
||||
// heatpump info
|
||||
if (model() == EMS_DEVICE_FLAG_HEATPUMP) {
|
||||
register_device_value(TAG_BOILER_DATA, &upTimeControl_, DeviceValueType::TIME, FL_(div60), FL_(upTimeControl), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &upTimeCompHeating_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompHeating), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &upTimeCompCooling_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompCooling), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &upTimeCompWw_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompWw), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingStarts_, DeviceValueType::ULONG, nullptr, FL_(heatingStarts), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA, &coolingStarts_, DeviceValueType::ULONG, nullptr, FL_(coolingStarts), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA, &upTimeCompPool_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompPool), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &totalCompStarts_, DeviceValueType::ULONG, nullptr, FL_(totalcompStarts), DeviceValueUOM::TIMES);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingStarts_, DeviceValueType::ULONG, nullptr, FL_(heatingStarts), DeviceValueUOM::TIMES);
|
||||
register_device_value(TAG_BOILER_DATA, &coolingStarts_, DeviceValueType::ULONG, nullptr, FL_(coolingStarts), DeviceValueUOM::TIMES);
|
||||
register_device_value(TAG_BOILER_DATA, &wwStarts2_, DeviceValueType::ULONG, nullptr, FL_(wwStarts2), DeviceValueUOM::TIMES);
|
||||
register_device_value(TAG_BOILER_DATA, &poolStarts_, DeviceValueType::ULONG, nullptr, FL_(poolStarts), DeviceValueUOM::TIMES);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgConsTotal), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompTotal), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompHeating_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompHeating), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompWw_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompWw), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompCooling_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompCooling), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompPool_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompPool), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsTotal_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsTotal), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsHeating_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsHeating), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsWW_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsWW), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsPool_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsPool), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgSuppTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppTotal), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgSuppHeating_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppHeating), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgSuppWw_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppWw), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgSuppCooling_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppCooling), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgSuppPool_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppPool), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &hpPower_, DeviceValueType::UINT, FL_(div10), FL_(hpPower), DeviceValueUOM::KW);
|
||||
register_device_value(TAG_BOILER_DATA, &hpCompOn_, DeviceValueType::BOOL, nullptr, FL_(hpCompOn), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &hpActivity_, DeviceValueType::ENUM, FL_(enum_hpactivity), FL_(hpActivity), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &hpHeatingOn_, DeviceValueType::BOOL, nullptr, FL_(hpHeatingOn), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &hpCoolingOn_, DeviceValueType::BOOL, nullptr, FL_(hpCoolingOn), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &hpWwOn_, DeviceValueType::BOOL, nullptr, FL_(hpWwOn), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &hpPoolOn_, DeviceValueType::BOOL, nullptr, FL_(hpPoolOn), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &hpBrinePumpSpd_, DeviceValueType::UINT, nullptr, FL_(hpBrinePumpSpd), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA, &hpSwitchValve_, DeviceValueType::BOOL, nullptr, FL_(hpSwitchValve), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &hpCompSpd_, DeviceValueType::UINT, nullptr, FL_(hpCompSpd), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA, &hpCircSpd_, DeviceValueType::UINT, nullptr, FL_(hpCircSpd), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA, &hpBrineIn_, DeviceValueType::SHORT, FL_(div10), FL_(hpBrineIn), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &hpBrineOut_, DeviceValueType::SHORT, FL_(div10), FL_(hpBrineOut), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &hpSuctionGas_, DeviceValueType::SHORT, FL_(div10), FL_(hpSuctionGas), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &hpHotGas_, DeviceValueType::SHORT, FL_(div10), FL_(hpHotGas), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &hpTc0_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc0), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &hpTc1_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &hpTc3_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc3), DeviceValueUOM::DEGREES);
|
||||
@@ -184,78 +209,71 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA, &hpTl2_, DeviceValueType::SHORT, FL_(div10), FL_(hpTl2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &hpPl1_, DeviceValueType::SHORT, FL_(div10), FL_(hpPl1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &hpPh1_, DeviceValueType::SHORT, FL_(div10), FL_(hpPh1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &poolSetTemp_, DeviceValueType::UINT, FL_(div2), FL_(poolSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_pool_temp));
|
||||
}
|
||||
|
||||
// warm water - boiler_data_ww topic
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwSelTemp_, DeviceValueType::UINT, nullptr, FL_(wwSelTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_warmwater_temp));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwSetTemp_, DeviceValueType::UINT, nullptr, FL_(wwSetTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwType_, DeviceValueType::ENUM, FL_(enum_flow), FL_(wwType), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwSelTemp_, DeviceValueType::UINT, nullptr, FL_(wwSelTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_temp));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwSetTemp_, DeviceValueType::UINT, nullptr, FL_(wwSetTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwSelTempLow_, DeviceValueType::UINT, nullptr, FL_(wwSelTempLow), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_temp_low));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwSelTempOff_, DeviceValueType::UINT, nullptr, FL_(wwSelTempOff), DeviceValueUOM::DEGREES);
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wwComfort_, DeviceValueType::ENUM, FL_(enum_comfort), FL_(wwComfort), DeviceValueUOM::LIST, MAKE_CF_CB(set_warmwater_mode));
|
||||
TAG_DEVICE_DATA_WW, &wwSelTempSingle_, DeviceValueType::UINT, nullptr, FL_(wwSelTempSingle), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_temp_single));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwType_, DeviceValueType::ENUM, FL_(enum_flow), FL_(wwType), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwComfort_, DeviceValueType::ENUM, FL_(enum_comfort), FL_(wwComfort), DeviceValueUOM::NONE, MAKE_CF_CB(set_ww_mode));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wwFlowTempOffset_, DeviceValueType::UINT, nullptr, FL_(wwFlowTempOffset), DeviceValueUOM::NONE, MAKE_CF_CB(set_wWFlowTempOffset));
|
||||
TAG_DEVICE_DATA_WW, &wwFlowTempOffset_, DeviceValueType::UINT, nullptr, FL_(wwFlowTempOffset), DeviceValueUOM::NONE, MAKE_CF_CB(set_ww_flowTempOffset));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwMaxPower_, DeviceValueType::UINT, nullptr, FL_(wwMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_ww_maxpower));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wwMaxPower_, DeviceValueType::UINT, nullptr, FL_(wwMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_warmwater_maxpower));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wwCircPump_, DeviceValueType::BOOL, nullptr, FL_(wwCircPump), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_circulation_pump));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWChargeType_, DeviceValueType::ENUM, FL_(enum_charge), FL_(wwChargeType), DeviceValueUOM::LIST);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwHystOn_, DeviceValueType::INT, nullptr, FL_(wwHystOn), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_hyst_on));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwHystOff_, DeviceValueType::INT, nullptr, FL_(wwHystOff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_hyst_off));
|
||||
register_device_value(TAG_BOILER_DATA_WW,
|
||||
TAG_DEVICE_DATA_WW, &wwCircPump_, DeviceValueType::BOOL, nullptr, FL_(wwCircPump), DeviceValueUOM::NONE, MAKE_CF_CB(set_ww_circulation_pump));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwChargeType_, DeviceValueType::ENUM, FL_(enum_charge), FL_(wwChargeType), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwHystOn_, DeviceValueType::INT, nullptr, FL_(wwHystOn), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_hyst_on));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwHystOff_, DeviceValueType::INT, nullptr, FL_(wwHystOff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_hyst_off));
|
||||
register_device_value(TAG_DEVICE_DATA_WW,
|
||||
&wwDisinfectionTemp_,
|
||||
DeviceValueType::UINT,
|
||||
nullptr,
|
||||
FL_(wwDisinfectionTemp),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_disinfect_temp));
|
||||
register_device_value(TAG_BOILER_DATA_WW,
|
||||
&wwCircMode_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_freq),
|
||||
FL_(wwCircMode),
|
||||
DeviceValueUOM::LIST,
|
||||
MAKE_CF_CB(set_warmwater_circulation_mode));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwCirc_, DeviceValueType::BOOL, nullptr, FL_(wwCirc), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_circulation));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwCurTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwCurTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwCurFlow_, DeviceValueType::UINT, FL_(div10), FL_(wwCurFlow), DeviceValueUOM::LMIN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwStorageTemp1_, DeviceValueType::USHORT, FL_(div10), FL_(wwStorageTemp1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwStorageTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wwStorageTemp2), DeviceValueUOM::DEGREES);
|
||||
MAKE_CF_CB(set_ww_disinfect_temp));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wwActivated_, DeviceValueType::BOOL, nullptr, FL_(wwActivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_activated));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwOneTime_, DeviceValueType::BOOL, nullptr, FL_(wwOneTime), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_onetime));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwDisinfecting_, DeviceValueType::BOOL, nullptr, FL_(wwDisinfecting), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwCharging_, DeviceValueType::BOOL, nullptr, FL_(wwCharging), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwRecharging_, DeviceValueType::BOOL, nullptr, FL_(wwRecharging), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwTempOK_, DeviceValueType::BOOL, nullptr, FL_(wwTempOK), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwActive_, DeviceValueType::BOOL, nullptr, FL_(wwActive), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwHeat_, DeviceValueType::BOOL, nullptr, FL_(wwHeat), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwSetPumpPower_, DeviceValueType::UINT, nullptr, FL_(wwSetPumpPower), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwMixerTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wwMixerTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwTankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wwTankMiddleTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWStarts_, DeviceValueType::ULONG, nullptr, FL_(wwStarts), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwStarts2_, DeviceValueType::ULONG, nullptr, FL_(wwStarts2), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wwWorkM_, DeviceValueType::TIME, nullptr, FL_(wwWorkM), DeviceValueUOM::MINUTES);
|
||||
TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_freq), FL_(wwCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_ww_circulation_mode));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwCirc_, DeviceValueType::BOOL, nullptr, FL_(wwCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_ww_circulation));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwCurTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwCurTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwCurFlow_, DeviceValueType::UINT, FL_(div10), FL_(wwCurFlow), DeviceValueUOM::LMIN);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwStorageTemp1_, DeviceValueType::USHORT, FL_(div10), FL_(wwStorageTemp1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwStorageTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wwStorageTemp2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwActivated_, DeviceValueType::BOOL, nullptr, FL_(wwActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_ww_activated));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwOneTime_, DeviceValueType::BOOL, nullptr, FL_(wwOneTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_ww_onetime));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwDisinfect_, DeviceValueType::BOOL, nullptr, FL_(wwDisinfect), DeviceValueUOM::NONE, MAKE_CF_CB(set_ww_disinfect));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwCharging_, DeviceValueType::BOOL, nullptr, FL_(wwCharging), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwRecharging_, DeviceValueType::BOOL, nullptr, FL_(wwRecharging), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwTempOK_, DeviceValueType::BOOL, nullptr, FL_(wwTempOK), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwActive_, DeviceValueType::BOOL, nullptr, FL_(wwActive), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwHeat_, DeviceValueType::BOOL, nullptr, FL_(wwHeat), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwSetPumpPower_, DeviceValueType::UINT, nullptr, FL_(wwSetPumpPower), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwMixerTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wwMixerTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwTankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wwTankMiddleTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwStarts_, DeviceValueType::ULONG, nullptr, FL_(wwStarts), DeviceValueUOM::TIMES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwWorkM_, DeviceValueType::TIME, nullptr, FL_(wwWorkM), DeviceValueUOM::MINUTES);
|
||||
|
||||
// fetch some initial data
|
||||
EMSESP::send_read_request(0x10,
|
||||
device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0x11,
|
||||
device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0x15,
|
||||
device_id); // read maintenace data on start (only published on change)
|
||||
EMSESP::send_read_request(0x1C,
|
||||
device_id); // read maintenace status on start (only published on change)
|
||||
EMSESP::send_read_request(0x10, device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0x11, device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0xC2, device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0x15, device_id); // read maintenace data on start (only published on change)
|
||||
EMSESP::send_read_request(0x1C, device_id); // read maintenace status on start (only published on change)
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Boiler::publish_ha_config() {
|
||||
bool Boiler::publish_ha_device_config() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["uniq_id"] = F_(boiler);
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s"), Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/%s", Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
char name_s[40];
|
||||
@@ -272,7 +290,7 @@ bool Boiler::publish_ha_config() {
|
||||
ids.add("ems-esp-boiler");
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/boiler/config"), Mqtt::base().c_str());
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/boiler/config", Mqtt::base().c_str());
|
||||
Mqtt::publish_ha(topic,
|
||||
doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
@@ -335,7 +353,7 @@ void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(wwCircPump_, 6)); // 0xFF means on
|
||||
has_update(telegram->read_value(wwCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous
|
||||
has_update(telegram->read_value(wwDisinfectionTemp_, 8));
|
||||
has_update(telegram->read_bitvalue(wWChargeType_, 10, 0)); // 0 = charge pump, 0xff = 3-way valve
|
||||
has_update(telegram->read_bitvalue(wwChargeType_, 10, 0)); // 0 = charge pump, 0xff = 3-way valve
|
||||
|
||||
telegram->read_value(wwComfort_, 9);
|
||||
if (wwComfort_ == 0x00) {
|
||||
@@ -365,10 +383,9 @@ void Boiler::process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_bitvalue(wwCirc_, 7, 7));
|
||||
|
||||
// warm water storage sensors (if present)
|
||||
// wWStorageTemp2 is also used by some brands as the boiler temperature - see https://github.com/emsesp/EMS-ESP/issues/206
|
||||
has_update(telegram->read_value(wwStorageTemp1_, 9)); // 0x8300 if not available
|
||||
has_update(telegram->read_value(wwStorageTemp2_,
|
||||
11)); // 0x8000 if not available - this is boiler temp
|
||||
// wwStorageTemp2 is also used by some brands as the boiler temperature - see https://github.com/emsesp/EMS-ESP/issues/206
|
||||
has_update(telegram->read_value(wwStorageTemp1_, 9)); // 0x8300 if not available
|
||||
has_update(telegram->read_value(wwStorageTemp2_, 11)); // 0x8000 if not available - this is boiler temp
|
||||
|
||||
has_update(telegram->read_value(retTemp_, 13));
|
||||
has_update(telegram->read_value(flameCurr_, 15));
|
||||
@@ -437,10 +454,10 @@ void Boiler::process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(wwType_, 8));
|
||||
has_update(telegram->read_value(wwCurFlow_, 9));
|
||||
has_update(telegram->read_value(wwWorkM_, 10, 3)); // force to 3 bytes
|
||||
has_update(telegram->read_value(wWStarts_, 13, 3)); // force to 3 bytes
|
||||
has_update(telegram->read_value(wwStarts_, 13, 3)); // force to 3 bytes
|
||||
|
||||
has_update(telegram->read_bitvalue(wwOneTime_, 5, 1));
|
||||
has_update(telegram->read_bitvalue(wwDisinfecting_, 5, 2));
|
||||
has_update(telegram->read_bitvalue(wwDisinfect_, 5, 2));
|
||||
has_update(telegram->read_bitvalue(wwCharging_, 5, 3));
|
||||
has_update(telegram->read_bitvalue(wwRecharging_, 5, 4));
|
||||
has_update(telegram->read_bitvalue(wwTempOK_, 5, 5));
|
||||
@@ -464,8 +481,7 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram
|
||||
has_update(telegram->read_value(selBurnPow_, 9));
|
||||
has_update(telegram->read_value(curFlowTemp_, 7));
|
||||
has_update(telegram->read_value(flameCurr_, 19));
|
||||
has_update(telegram->read_value(retTemp_,
|
||||
17)); // can be 0 if no sensor, handled in export_values
|
||||
has_update(telegram->read_value(retTemp_, 17)); // can be 0 if no sensor, handled in export_values
|
||||
has_update(telegram->read_value(sysPress_, 21));
|
||||
|
||||
// read 3 char service code / installation status as appears on the display
|
||||
@@ -501,8 +517,7 @@ void Boiler::process_UBAMonitorSlow(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(outdoorTemp_, 0));
|
||||
has_update(telegram->read_value(boilTemp_, 2));
|
||||
has_update(telegram->read_value(exhaustTemp_, 4));
|
||||
has_update(telegram->read_value(switchTemp_,
|
||||
25)); // only if there is a mixer module present
|
||||
has_update(telegram->read_value(switchTemp_, 25)); // only if there is a mixer module present
|
||||
has_update(telegram->read_value(heatingPumpMod_, 9));
|
||||
has_update(telegram->read_value(burnStarts_, 10, 3)); // force to 3 bytes
|
||||
has_update(telegram->read_value(burnWorkMin_, 13, 3)); // force to 3 bytes
|
||||
@@ -561,12 +576,14 @@ void Boiler::process_UBAParametersPlus(std::shared_ptr<const Telegram> telegram)
|
||||
void Boiler::process_UBAParameterWWPlus(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(wwActivated_, 5)); // 0x01 means on
|
||||
has_update(telegram->read_value(wwCircPump_, 10)); // 0x01 means yes
|
||||
has_update(telegram->read_value(wwCircMode_,
|
||||
11)); // 1=1x3min... 6=6x3min, 7=continuous
|
||||
// has_update(telegram->read_value(wWDisinfectTemp_, 12)); // settings, status in E9
|
||||
// has_update(telegram->read_value(wWSelTemp_, 6)); // settings, status in E9
|
||||
has_update(telegram->read_value(wwCircMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous
|
||||
has_update(telegram->read_value(wwDisinfectionTemp_, 12));
|
||||
has_update(telegram->read_value(wwSelTemp_, 6));
|
||||
has_update(telegram->read_value(wwHystOn_, 7));
|
||||
has_update(telegram->read_value(wwHystOff_, 8));
|
||||
has_update(telegram->read_value(wwSelTempOff_, 0)); // confusing description in #96, hopefully this is right
|
||||
has_update(telegram->read_value(wwSelTempSingle_, 16));
|
||||
has_update(telegram->read_value(wwSelTempLow_, 18));
|
||||
}
|
||||
|
||||
// 0xE9 - WW monitor ems+
|
||||
@@ -577,49 +594,54 @@ void Boiler::process_UBAMonitorWWPlus(std::shared_ptr<const Telegram> telegram)
|
||||
has_update(telegram->read_value(wwCurTemp2_, 3));
|
||||
|
||||
has_update(telegram->read_value(wwWorkM_, 14, 3)); // force to 3 bytes
|
||||
has_update(telegram->read_value(wWStarts_, 17, 3)); // force to 3 bytes
|
||||
has_update(telegram->read_value(wwStarts_, 17, 3)); // force to 3 bytes
|
||||
|
||||
has_update(telegram->read_bitvalue(wwOneTime_, 12, 2));
|
||||
has_update(telegram->read_bitvalue(wwDisinfecting_, 12, 3));
|
||||
has_update(telegram->read_bitvalue(wwDisinfect_, 12, 3));
|
||||
has_update(telegram->read_bitvalue(wwCharging_, 12, 4));
|
||||
has_update(telegram->read_bitvalue(wwRecharging_, 13, 4));
|
||||
has_update(telegram->read_bitvalue(wwTempOK_, 13, 5));
|
||||
has_update(telegram->read_bitvalue(wwCirc_, 13, 2));
|
||||
|
||||
// has_update(telegram->read_value(wWActivated_, 20)); // Activated is in 0xEA, this is something other 0/100%
|
||||
has_update(telegram->read_value(wwSelTemp_, 10));
|
||||
has_update(telegram->read_value(wwDisinfectionTemp_, 9));
|
||||
// has_update(telegram->read_value(wwActivated_, 20)); // Activated is in 0xEA, this is something other 0/100%
|
||||
// has_update(telegram->read_value(wwSelTemp_, 10)); // see #96, this is not wwSelTemp (set in EA)
|
||||
// has_update(telegram->read_value(wwDisinfectionTemp_, 9));
|
||||
}
|
||||
|
||||
/*
|
||||
* UBAInformation - type 0x495
|
||||
* all values 32 bit
|
||||
* 08 0B FF 00 03 95 01 01 AB 83 00 27 78 EB 00 84 FA 39 FF FF FF 00 00 53 7D 8D 00 00 0F 04 1C
|
||||
* 08 00 FF 00 03 95 01 01 AB 83 00 27 78 EB 00 84 FA 39 FF FF FF 00 00 53 7D 8D 00 00 0F 04 63
|
||||
* 08 00 FF 18 03 95 00 00 05 84 00 00 07 22 FF FF FF FF 00 00 02 5C 00 00 03 C0 00 00 01 98 64
|
||||
* 08 00 FF 30 03 95 00 00 00 D4 FF FF FF FF 00 00 1C 70 FF FF FF FF 00 00 20 30 00 00 0E 06 FB
|
||||
* 08 00 FF 48 03 95 00 00 06 C0 00 00 07 66 FF FF FF FF 2E
|
||||
* 08 00 FF 00 03 95 00 0F 8E C2 00 08 39 C8 00 00 18 7A 00 07 3C 80 00 00 00 00 00 00 00 E5 F6 00
|
||||
* 08 00 FF 18 03 95 00 00 00 A1 00 00 00 00 00 00 00 44 00 00 00 00 00 00 00 0A 00 00 00 0A BD 00
|
||||
* 08 00 FF 30 03 95 00 00 00 00 00 00 00 00 00 00 02 10 00 00 00 00 00 00 02 1A 00 00 00 02 66 00
|
||||
* 08 00 FF 48 03 95 00 00 01 15 00 00 00 00 00 00 00 F9 29 00
|
||||
*
|
||||
*/
|
||||
void Boiler::process_UBAInformation(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(upTimeControl_, 0));
|
||||
has_update(telegram->read_value(upTimeCompHeating_, 8));
|
||||
has_update(telegram->read_value(upTimeCompCooling_, 16));
|
||||
has_update(telegram->read_value(upTimeCompWw_, 4));
|
||||
has_update(telegram->read_value(upTimeCompPool_, 12));
|
||||
|
||||
has_update(telegram->read_value(totalCompStarts_, 20));
|
||||
has_update(telegram->read_value(heatingStarts_, 28));
|
||||
has_update(telegram->read_value(coolingStarts_, 36));
|
||||
has_update(telegram->read_value(wwStarts2_, 24));
|
||||
has_update(telegram->read_value(poolStarts_, 32));
|
||||
|
||||
has_update(telegram->read_value(nrgConsTotal_, 64));
|
||||
|
||||
has_update(telegram->read_value(auxElecHeatNrgConsTotal_, 40));
|
||||
has_update(telegram->read_value(auxElecHeatNrgConsHeating_, 48));
|
||||
has_update(telegram->read_value(auxElecHeatNrgConsWW_, 44));
|
||||
has_update(telegram->read_value(auxElecHeatNrgConsPool_, 52));
|
||||
|
||||
has_update(telegram->read_value(nrgConsCompTotal_, 56));
|
||||
has_update(telegram->read_value(nrgConsCompHeating_, 68));
|
||||
has_update(telegram->read_value(nrgConsCompWw_, 72));
|
||||
has_update(telegram->read_value(nrgConsCompCooling_, 76));
|
||||
has_update(telegram->read_value(nrgConsCompPool_, 80));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -634,11 +656,48 @@ void Boiler::process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram)
|
||||
has_update(telegram->read_value(nrgSuppHeating_, 12));
|
||||
has_update(telegram->read_value(nrgSuppWw_, 8));
|
||||
has_update(telegram->read_value(nrgSuppCooling_, 16));
|
||||
has_update(telegram->read_value(nrgSuppPool_, 20));
|
||||
}
|
||||
|
||||
// Heatpump power - type 0x48D
|
||||
//08 00 FF 00 03 8D 03 00 10 30 10 60 00 04 00 00 00 17 00 00 00 3C 38 0E 64 00 00 0C 33 C7 00
|
||||
//XR1A050001 A05 Pump Heat circuit (1.0 ) 1 >> 1 & 0x01 ?
|
||||
//XR1A040001 A04 Pump Cold circuit (1.0 ) 1 & 0x1 ?
|
||||
|
||||
void Boiler::process_HpPower(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(hpPower_, 11));
|
||||
has_update(telegram->read_bitvalue(hpCompOn_, 3, 4));
|
||||
has_update(telegram->read_value(hpBrinePumpSpd_, 5));
|
||||
has_update(telegram->read_value(hpCompSpd_, 17));
|
||||
has_update(telegram->read_value(hpCircSpd_, 4));
|
||||
has_update(telegram->read_bitvalue(hpSwitchValve_, 0, 6));
|
||||
has_update(telegram->read_value(hpActivity_, 7));
|
||||
|
||||
hpHeatingOn_ = 0;
|
||||
hpCoolingOn_ = 0;
|
||||
hpWwOn_ = 0;
|
||||
hpPoolOn_ = 0;
|
||||
|
||||
switch (hpActivity_) {
|
||||
case 1: {
|
||||
hpHeatingOn_ = 0xFF;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
hpCoolingOn_ = 0xFF;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
hpWwOn_ = 0xFF;
|
||||
;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
hpPoolOn_ = 0xFF;
|
||||
;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Heatpump outdoor unit - type 0x48F
|
||||
@@ -654,8 +713,21 @@ void Boiler::process_HpOutdoor(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(hpTl2_, 12));
|
||||
has_update(telegram->read_value(hpPl1_, 26));
|
||||
has_update(telegram->read_value(hpPh1_, 28));
|
||||
has_update(telegram->read_value(hpBrineIn_, 8));
|
||||
has_update(telegram->read_value(hpBrineOut_, 10));
|
||||
has_update(telegram->read_value(hpSuctionGas_, 20));
|
||||
has_update(telegram->read_value(hpHotGas_, 0));
|
||||
}
|
||||
|
||||
// Heatpump pool unit - type 0x48A
|
||||
// 08 00 FF 00 03 8A 01 4C 01 0C 00 00 0A 00 1E 00 00 01 00 04 4A 00
|
||||
|
||||
void Boiler::process_HpPool(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(poolSetTemp_, 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 0x2A - MC110Status
|
||||
// e.g. 88 00 2A 00 00 00 00 00 00 00 00 00 D2 00 00 80 00 00 01 08 80 00 02 47 00
|
||||
// see https://github.com/emsesp/EMS-ESP/issues/397
|
||||
@@ -673,8 +745,7 @@ void Boiler::process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// UBASetPoint 0x1A
|
||||
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(setFlowTemp_,
|
||||
0)); // boiler set temp from thermostat
|
||||
has_update(telegram->read_value(setFlowTemp_, 0)); // boiler set temp from thermostat
|
||||
has_update(telegram->read_value(setBurnPow_, 1)); // max json power in %
|
||||
has_update(telegram->read_value(wwSetPumpPower_, 2)); // ww pump speed/power?
|
||||
}
|
||||
@@ -704,9 +775,12 @@ void Boiler::process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegr
|
||||
uint8_t message_code = maintenanceMessage_[2] - '0';
|
||||
has_update(telegram->read_value(message_code, 5));
|
||||
|
||||
// ignore if 0, which means all is ok
|
||||
if (Helpers::hasValue(message_code) && message_code > 0) {
|
||||
snprintf_P(maintenanceMessage_, sizeof(maintenanceMessage_), PSTR("H%02d"), message_code);
|
||||
if (message_code > 0) {
|
||||
snprintf(maintenanceMessage_, sizeof(maintenanceMessage_), "H%02d", message_code);
|
||||
} else {
|
||||
// No message. All Ok. But set a blank message so value is still in the MQTT payload to avoid HA giving warnings
|
||||
maintenanceMessage_[0] = ' ';
|
||||
maintenanceMessage_[1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -733,19 +807,63 @@ void Boiler::process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
uint32_t date = (year - 2000) * 535680UL + month * 44640UL + day * 1440UL + hour * 60 + min;
|
||||
// store only the newest code from telegrams 10 and 11
|
||||
if (date > lastCodeDate_) {
|
||||
snprintf_P(lastCode_, sizeof(lastCode_), PSTR("%s(%d) %02d.%02d.%d %02d:%02d"), code, codeNo, day, month, year, hour, min);
|
||||
snprintf(lastCode_, sizeof(lastCode_), "%s(%d) %02d.%02d.%d %02d:%02d", code, codeNo, day, month, year, hour, min);
|
||||
lastCodeDate_ = date;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0xC2
|
||||
void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram) {
|
||||
// not sure why this test is in , so removing
|
||||
// if (telegram->offset > 0 || telegram->message_length < 14) {
|
||||
// return;
|
||||
// }
|
||||
char code[4];
|
||||
uint16_t codeNo;
|
||||
char start_time[17];
|
||||
char end_time[17];
|
||||
|
||||
if (!(telegram->message_data[10] & 0x80)) { // no valid start date means no error?
|
||||
return;
|
||||
}
|
||||
|
||||
code[0] = telegram->message_data[5];
|
||||
code[1] = telegram->message_data[6];
|
||||
code[2] = telegram->message_data[7];
|
||||
code[3] = 0;
|
||||
telegram->read_value(codeNo, 8);
|
||||
|
||||
uint16_t start_year = (telegram->message_data[10] & 0x7F) + 2000;
|
||||
uint8_t start_month = telegram->message_data[11];
|
||||
uint8_t start_day = telegram->message_data[13];
|
||||
uint8_t start_hour = telegram->message_data[12];
|
||||
uint8_t start_min = telegram->message_data[14];
|
||||
snprintf(start_time, sizeof(start_time), "%d.%02d.%02d %02d:%02d", start_year, start_month, start_day, start_hour, start_min);
|
||||
|
||||
uint16_t end_year = (telegram->message_data[15] & 0x7F) + 2000;
|
||||
uint8_t end_month = telegram->message_data[16];
|
||||
uint8_t end_day = telegram->message_data[18];
|
||||
uint8_t end_hour = telegram->message_data[17];
|
||||
uint8_t end_min = telegram->message_data[19];
|
||||
|
||||
if (telegram->message_data[15] & 0x80) { // valid end date
|
||||
snprintf(end_time, sizeof(end_time), "%d.%02d.%02d %02d:%02d", end_year, end_month, end_day, end_hour, end_min);
|
||||
} else { // no valid end date means error still persists
|
||||
snprintf(end_time, sizeof(end_time), "%s", "none");
|
||||
}
|
||||
|
||||
snprintf(lastCode_, sizeof(lastCode_), "%s/%d start: %s, end: %s", code, codeNo, start_time, end_time);
|
||||
}
|
||||
|
||||
|
||||
// 0x15
|
||||
void Boiler::process_UBAMaintenanceData(std::shared_ptr<const Telegram> telegram) {
|
||||
if (telegram->offset > 0 || telegram->message_length < 5) {
|
||||
return;
|
||||
}
|
||||
// first byte: Maintenance messages (0 = none, 1 = by operating hours, 2 = by date)
|
||||
|
||||
// first byte: Maintenance messages (0 = none, 1 = by operating hours, 2 = by date)
|
||||
has_update(telegram->read_value(maintenanceType_, 0));
|
||||
|
||||
uint8_t time = (maintenanceTime_ == EMS_VALUE_USHORT_NOTSET) ? EMS_VALUE_UINT_NOTSET : maintenanceTime_ / 100;
|
||||
@@ -759,40 +877,67 @@ void Boiler::process_UBAMaintenanceData(std::shared_ptr<const Telegram> telegram
|
||||
uint8_t month = telegram->message_data[3];
|
||||
uint8_t year = telegram->message_data[4];
|
||||
if (day > 0 && month > 0) {
|
||||
snprintf_P(maintenanceDate_, sizeof(maintenanceDate_), PSTR("%02d.%02d.%04d"), day, month, year + 2000);
|
||||
snprintf(maintenanceDate_, sizeof(maintenanceDate_), "%02d.%02d.%04d", day, month, year + 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the warm water temperature 0x33/0x35 or 0xEA
|
||||
bool Boiler::set_warmwater_temp(const char * value, const int8_t id) {
|
||||
bool Boiler::set_ww_temp(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set boiler warm water temperature: Invalid value"));
|
||||
LOG_WARNING(F("Set boiler ww temperature: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler warm water temperature to %d C"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
LOG_INFO(F("Setting boiler ww temperature to %d C"), v);
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 6, v, EMS_TYPE_UBAParameterWWPlus);
|
||||
} else {
|
||||
// some boiler have it in 0x33, some in 0x35
|
||||
write_command(EMS_TYPE_UBAFlags, 3, v, 0x34); // for i9000, see #397
|
||||
write_command(EMS_TYPE_UBAFlags, 3, v, EMS_TYPE_UBAParameterWW); // for i9000, see #397
|
||||
write_command(EMS_TYPE_UBAParameterWW, 2, v, EMS_TYPE_UBAParameterWW); // read seltemp back
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the warm water disinfection temperature
|
||||
bool Boiler::set_disinfect_temp(const char * value, const int8_t id) {
|
||||
|
||||
// Set the lower warm water temperature 0xEA
|
||||
bool Boiler::set_ww_temp_low(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set boiler warm water disinfect temperature: Invalid value"));
|
||||
LOG_WARNING(F("Set boiler lower ww temperature: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler warm water disinfect temperature to %d C"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
LOG_INFO(F("Setting boiler lower ww temperature to %d C"), v);
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 18, v, EMS_TYPE_UBAParameterWWPlus);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the warm water single charge temperature 0xEA
|
||||
bool Boiler::set_ww_temp_single(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set single charge ww temperature: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting single charge ww temperature to %d C"), v);
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 16, v, EMS_TYPE_UBAParameterWWPlus);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the warm water disinfection temperature
|
||||
bool Boiler::set_ww_disinfect_temp(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set boiler ww disinfect temperature: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler ww disinfect temperature to %d C"), v);
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 12, v, EMS_TYPE_UBAParameterWWPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameterWW, 8, v, EMS_TYPE_UBAParameterWW);
|
||||
@@ -800,6 +945,7 @@ bool Boiler::set_disinfect_temp(const char * value, const int8_t id) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// flow temp
|
||||
bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
@@ -829,14 +975,14 @@ bool Boiler::set_burn_power(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
// Set the warm water flow temperature offset 0x33
|
||||
bool Boiler::set_wWFlowTempOffset(const char * value, const int8_t id) {
|
||||
bool Boiler::set_ww_flowTempOffset(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set boiler warm water flow temperature offset: Invalid value"));
|
||||
LOG_WARNING(F("Set boiler ww flow temperature offset: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler warm water flow temperature offset to %d C"), v);
|
||||
LOG_INFO(F("Setting boiler ww flow temperature offset to %d C"), v);
|
||||
write_command(EMS_TYPE_UBAParameterWW, 5, v, EMS_TYPE_UBAParameterWW);
|
||||
|
||||
return true;
|
||||
@@ -851,7 +997,7 @@ bool Boiler::set_heating_activated(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler heating %s"), v ? "on" : "off");
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 0, v ? 0x01 : 0, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 0, v ? 0xFF : 0, EMS_TYPE_UBAParameters);
|
||||
@@ -869,7 +1015,7 @@ bool Boiler::set_heating_temp(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler heating temperature to %d C"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 1, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 1, v, EMS_TYPE_UBAParameters);
|
||||
@@ -887,7 +1033,7 @@ bool Boiler::set_min_power(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler min power to %d %%"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 5, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 3, v, EMS_TYPE_UBAParameters);
|
||||
@@ -905,7 +1051,7 @@ bool Boiler::set_max_power(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler max power to %d %%"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 4, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 2, v, EMS_TYPE_UBAParameters);
|
||||
@@ -923,7 +1069,7 @@ bool Boiler::set_ww_hyst_on(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting ww on hysteresis on to %d C"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 7, v, EMS_TYPE_UBAParameterWWPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameterWW, 3, v, EMS_TYPE_UBAParameterWW);
|
||||
@@ -941,7 +1087,7 @@ bool Boiler::set_ww_hyst_off(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting ww off hysteresis off to %d C"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 8, v, EMS_TYPE_UBAParameterWWPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameterWW, 4, v, EMS_TYPE_UBAParameterWW);
|
||||
@@ -951,14 +1097,14 @@ bool Boiler::set_ww_hyst_off(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
// set warm water max power
|
||||
bool Boiler::set_warmwater_maxpower(const char * value, const int8_t id) {
|
||||
bool Boiler::set_ww_maxpower(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set warm water max power: Invalid value"));
|
||||
LOG_WARNING(F("Set ww max power: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting warm water max power to %d %%"), v);
|
||||
LOG_INFO(F("Setting ww max power to %d %%"), v);
|
||||
write_command(EMS_TYPE_UBASettingsWW, 7, v, EMS_TYPE_UBASettingsWW);
|
||||
|
||||
return true;
|
||||
@@ -973,7 +1119,7 @@ bool Boiler::set_min_pump(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting pump min to %d %%"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 14, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 10, v, EMS_TYPE_UBAParameters);
|
||||
@@ -991,7 +1137,7 @@ bool Boiler::set_max_pump(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting pump max to %d %%"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 13, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 9, v, EMS_TYPE_UBAParameters);
|
||||
@@ -1009,7 +1155,7 @@ bool Boiler::set_hyst_on(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler hysteresis on to %d C"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 9, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 5, v, EMS_TYPE_UBAParameters);
|
||||
@@ -1027,7 +1173,7 @@ bool Boiler::set_hyst_off(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler hysteresis off to %d C"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 8, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 4, v, EMS_TYPE_UBAParameters);
|
||||
@@ -1045,7 +1191,7 @@ bool Boiler::set_burn_period(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting burner min period to %d min"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 10, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameters, 6, v, EMS_TYPE_UBAParameters);
|
||||
@@ -1062,7 +1208,7 @@ bool Boiler::set_pump_delay(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameters)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParameters)) {
|
||||
LOG_INFO(F("Setting boiler pump delay to %d min"), v);
|
||||
write_command(EMS_TYPE_UBAParameters, 8, v, EMS_TYPE_UBAParameters);
|
||||
return true;
|
||||
@@ -1073,24 +1219,24 @@ bool Boiler::set_pump_delay(const char * value, const int8_t id) {
|
||||
|
||||
// note some boilers do not have this setting, than it's done by thermostat
|
||||
// on a RC35 it's by EMSESP::send_write_request(0x37, 0x10, 2, &set, 1, 0); (set is 1,2,3) 1=hot, 2=eco, 3=intelligent
|
||||
bool Boiler::set_warmwater_mode(const char * value, const int8_t id) {
|
||||
bool Boiler::set_ww_mode(const char * value, const int8_t id) {
|
||||
uint8_t set;
|
||||
if (!Helpers::value2enum(value, set, FL_(enum_comfort))) {
|
||||
LOG_WARNING(F("Set boiler warm water mode: Invalid value"));
|
||||
LOG_WARNING(F("Set boiler ww mode: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!get_toggle_fetch(EMS_TYPE_UBAParameterWW)) {
|
||||
if (!is_fetch(EMS_TYPE_UBAParameterWW)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (set == 0) {
|
||||
LOG_INFO(F("Setting boiler warm water to Hot"));
|
||||
LOG_INFO(F("Setting boiler ww to Hot"));
|
||||
} else if (set == 1) {
|
||||
LOG_INFO(F("Setting boiler warm water to Eco"));
|
||||
LOG_INFO(F("Setting boiler ww to Eco"));
|
||||
set = 0xD8;
|
||||
} else if (set == 2) {
|
||||
LOG_INFO(F("Setting boiler warm water to Intelligent"));
|
||||
LOG_INFO(F("Setting boiler ww to Intelligent"));
|
||||
set = 0xEC;
|
||||
} else {
|
||||
return false; // do nothing
|
||||
@@ -1101,19 +1247,19 @@ bool Boiler::set_warmwater_mode(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
// turn on/off warm water
|
||||
bool Boiler::set_warmwater_activated(const char * value, const int8_t id) {
|
||||
bool Boiler::set_ww_activated(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
LOG_WARNING(F("Set boiler warm water active: Invalid value"));
|
||||
LOG_WARNING(F("Set boiler ww active: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting boiler warm water active %s"), v ? "on" : "off");
|
||||
LOG_INFO(F("Setting boiler ww active %s"), v ? "on" : "off");
|
||||
|
||||
// https://github.com/emsesp/EMS-ESP/issues/268
|
||||
// 08 for HT3 seems to be wrong, see https://github.com/emsesp/EMS-ESP32/issues/89
|
||||
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 1, v ? 1 : 0, EMS_TYPE_UBAParameterWWPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameterWW, 1, v ? 0xFF : 0, EMS_TYPE_UBAParameterWW);
|
||||
@@ -1161,17 +1307,16 @@ bool Boiler::set_tapwarmwater_activated(const char * value, const int8_t id) {
|
||||
// Activate / De-activate One Time warm water 0x35
|
||||
// true = on, false = off
|
||||
// See also https://github.com/emsesp/EMS-ESP/issues/341#issuecomment-596245458 for Junkers
|
||||
bool Boiler::set_warmwater_onetime(const char * value, const int8_t id) {
|
||||
bool Boiler::set_ww_onetime(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
LOG_WARNING(F("Set warm water OneTime loading: Invalid value"));
|
||||
LOG_WARNING(F("Set ww OneTime loading: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting warm water OneTime loading %s"), v ? "on" : "off");
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x22 : 0x02),
|
||||
0xE9); // not sure if this is in flags
|
||||
LOG_INFO(F("Setting ww OneTime loading %s"), v ? "on" : "off");
|
||||
if (is_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x22 : 0x02), 0xE9); // not sure if this is in flags
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x22 : 0x02), 0x34);
|
||||
}
|
||||
@@ -1181,17 +1326,16 @@ bool Boiler::set_warmwater_onetime(const char * value, const int8_t id) {
|
||||
|
||||
// Activate / De-activate circulation of warm water 0x35
|
||||
// true = on, false = off
|
||||
bool Boiler::set_warmwater_circulation(const char * value, const int8_t id) {
|
||||
bool Boiler::set_ww_circulation(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
LOG_WARNING(F("Set warm water circulation: Invalid value"));
|
||||
LOG_WARNING(F("Set ww circulation: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting warm water circulation %s"), v ? "on" : "off");
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAFlags, 1, (v ? 0x22 : 0x02),
|
||||
0xE9); // not sure if this is in flags
|
||||
LOG_INFO(F("Setting ww circulation %s"), v ? "on" : "off");
|
||||
if (is_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAFlags, 1, (v ? 0x22 : 0x02), 0xE9); // not sure if this is in flags
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAFlags, 1, (v ? 0x22 : 0x02), 0x34);
|
||||
}
|
||||
@@ -1199,17 +1343,35 @@ bool Boiler::set_warmwater_circulation(const char * value, const int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// configuration of warm water circulation pump
|
||||
bool Boiler::set_warmwater_circulation_pump(const char * value, const int8_t id) {
|
||||
// starting warm water disinfect, set to off seems not working
|
||||
bool Boiler::set_ww_disinfect(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
LOG_WARNING(F("Set warm water circulation pump: Invalid value"));
|
||||
LOG_WARNING(F("Set ww disinfect: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting warm water circulation pump %s"), v ? "on" : "off");
|
||||
LOG_INFO(F("Setting ww disinfect %s"), v ? "on" : "off");
|
||||
if (is_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x44 : 0x04), 0xE9); // not sure if this is in flags
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x44 : 0x04), 0x34);
|
||||
}
|
||||
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// configuration of warm water circulation pump
|
||||
bool Boiler::set_ww_circulation_pump(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
LOG_WARNING(F("Set ww circulation pump: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting ww circulation pump %s"), v ? "on" : "off");
|
||||
|
||||
if (is_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 10, v ? 0x01 : 0x00, EMS_TYPE_UBAParameterWWPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameterWW, 6, v ? 0xFF : 0x00, EMS_TYPE_UBAParameterWW);
|
||||
@@ -1220,23 +1382,23 @@ bool Boiler::set_warmwater_circulation_pump(const char * value, const int8_t id)
|
||||
|
||||
// Set the mode of circulation, 1x3min, ... 6x3min, continuous
|
||||
// true = on, false = off
|
||||
bool Boiler::set_warmwater_circulation_mode(const char * value, const int8_t id) {
|
||||
bool Boiler::set_ww_circulation_mode(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set warm water circulation mode: Invalid value"));
|
||||
LOG_WARNING(F("Set ww circulation mode: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v < 7) {
|
||||
LOG_INFO(F("Setting warm water circulation mode %dx3min"), v);
|
||||
LOG_INFO(F("Setting ww circulation mode %dx3min"), v);
|
||||
} else if (v == 7) {
|
||||
LOG_INFO(F("Setting warm water circulation mode continuous"));
|
||||
LOG_INFO(F("Setting ww circulation mode continuous"));
|
||||
} else {
|
||||
LOG_WARNING(F("Set warm water circulation mode: Invalid value"));
|
||||
LOG_WARNING(F("Set ww circulation mode: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
if (is_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 11, v, EMS_TYPE_UBAParameterWWPlus);
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAParameterWW, 7, v, EMS_TYPE_UBAParameterWW);
|
||||
@@ -1271,7 +1433,7 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
|
||||
bool Boiler::set_maintenance(const char * value, const int8_t id) {
|
||||
std::string s(12, '\0');
|
||||
if (Helpers::value2string(value, s)) {
|
||||
if (s == Helpers::toLower(uuid::read_flash_string(F_(reset)))) {
|
||||
if (s == Helpers::toLower(read_flash_string(F_(reset)))) {
|
||||
LOG_INFO(F("Reset boiler maintenance message"));
|
||||
write_command(0x05, 0x08, 0xFF, 0x1C);
|
||||
return true;
|
||||
@@ -1349,4 +1511,20 @@ bool Boiler::set_maintenancedate(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the pool temperature 0x48A
|
||||
bool Boiler::set_pool_temp(const char * value, const int8_t id) {
|
||||
float v = 0;
|
||||
uint8_t v2 = 0;
|
||||
if (!Helpers::value2float(value, v)) {
|
||||
LOG_WARNING(F("Set pool water temperature: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
v2 = (int((v * 2) + 0.5) & 0xFF);
|
||||
|
||||
LOG_INFO(F("Setting pool temperature to %d C"), v2 / 2);
|
||||
write_command(0x48A, 1, v2, 0x48A);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -27,7 +27,7 @@ class Boiler : public EMSdevice {
|
||||
public:
|
||||
Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_config();
|
||||
virtual bool publish_ha_device_config();
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
@@ -57,10 +57,13 @@ class Boiler : public EMSdevice {
|
||||
// ww
|
||||
uint8_t wwSetTemp_; // Warm Water set temperature
|
||||
uint8_t wwSelTemp_; // Warm Water selected temperature
|
||||
uint8_t wwSelTempLow_; // Warm Water lower selected temperature
|
||||
uint8_t wwSelTempOff_; // Warm Water selected temperature for off position
|
||||
uint8_t wwSelTempSingle_; // Warm Water single charge temperature
|
||||
uint8_t wwType_; // 0-off, 1-flow, 2-flowbuffer, 3-buffer, 4-layered buffer
|
||||
uint8_t wwComfort_; // WW comfort mode
|
||||
uint8_t wwCircPump_; // Warm Water circulation pump available
|
||||
uint8_t wWChargeType_; // Warm Water charge type (pump or 3-way-valve)
|
||||
uint8_t wwChargeType_; // Warm Water charge type (pump or 3-way-valve)
|
||||
uint8_t wwDisinfectionTemp_; // Warm Water disinfection temperature to prevent infection
|
||||
uint8_t wwCircMode_; // Warm Water circulation pump mode
|
||||
uint8_t wwCirc_; // Circulation on/off
|
||||
@@ -71,7 +74,7 @@ class Boiler : public EMSdevice {
|
||||
uint16_t wwStorageTemp2_; // warm water storage temp 2
|
||||
uint8_t wwActivated_; // Warm Water activated
|
||||
uint8_t wwOneTime_; // Warm Water one time function on/off
|
||||
uint8_t wwDisinfecting_; // Warm Water disinfection on/off
|
||||
uint8_t wwDisinfect_; // Warm Water disinfection on/off
|
||||
uint8_t wwCharging_; // Warm Water charging on/off
|
||||
uint8_t wwRecharging_; // Warm Water recharge on/off
|
||||
uint8_t wwTempOK_; // Warm Water temperature ok on/off
|
||||
@@ -80,9 +83,9 @@ class Boiler : public EMSdevice {
|
||||
uint8_t wwSetPumpPower_; // ww pump speed/power?
|
||||
uint8_t wwFlowTempOffset_; // Boiler offset for ww heating
|
||||
uint8_t wwMaxPower_; // Warm Water maximum power
|
||||
uint32_t wWStarts_; // Warm Water # starts
|
||||
uint32_t wwStarts_; // Warm Water starts
|
||||
uint32_t wwStarts2_; // Warm water control starts
|
||||
uint32_t wwWorkM_; // Warm Water # minutes
|
||||
uint32_t wwWorkM_; // Warm Water minutes
|
||||
int8_t wwHystOn_;
|
||||
int8_t wwHystOff_;
|
||||
uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
|
||||
@@ -124,11 +127,11 @@ class Boiler : public EMSdevice {
|
||||
uint8_t setFlowTemp_; // boiler setpoint temp
|
||||
uint8_t curBurnPow_; // Burner current power %
|
||||
uint8_t setBurnPow_; // max output power in %
|
||||
uint32_t burnStarts_; // # burner restarts
|
||||
uint32_t burnStarts_; // burner restarts
|
||||
uint32_t burnWorkMin_; // Total burner operating time
|
||||
uint32_t heatWorkMin_; // Total heat operating time
|
||||
uint32_t UBAuptime_; // Total UBA working hours
|
||||
char lastCode_[30]; // last error code
|
||||
char lastCode_[60]; // last error code
|
||||
char serviceCode_[4]; // 3 character status/service code
|
||||
uint16_t serviceCodeNumber_; // error/service code
|
||||
|
||||
@@ -137,38 +140,62 @@ class Boiler : public EMSdevice {
|
||||
uint32_t upTimeCompHeating_; // Operating time compressor heating
|
||||
uint32_t upTimeCompCooling_; // Operating time compressor cooling
|
||||
uint32_t upTimeCompWw_; // Operating time compressor warm water
|
||||
uint32_t upTimeCompPool_; // Operating time compressor pool
|
||||
uint32_t totalCompStarts_; // Total Commpressor control starts
|
||||
uint32_t heatingStarts_; // Heating control starts
|
||||
uint32_t coolingStarts_; // Cooling control starts
|
||||
uint32_t poolStarts_; // Warm water control starts
|
||||
uint32_t nrgConsTotal_; // Energy consumption total
|
||||
uint32_t nrgConsCompTotal_; // Energy consumption compressor total
|
||||
uint32_t nrgConsCompHeating_; // Energy consumption compressor heating
|
||||
uint32_t nrgConsCompWw_; // Energy consumption compressor warm water
|
||||
uint32_t nrgConsCompCooling_; // Energy consumption compressor cooling
|
||||
uint32_t nrgConsCompPool_; // Energy consumption compressor pool
|
||||
uint32_t nrgSuppTotal_; // Energy supplied total
|
||||
uint32_t nrgSuppHeating_; // Energy supplied heating
|
||||
uint32_t nrgSuppWw_; // Energy supplied warm water
|
||||
uint32_t nrgSuppCooling_; // Energy supplied cooling
|
||||
uint32_t nrgSuppPool_; // Energy supplied pool
|
||||
uint32_t auxElecHeatNrgConsTotal_; // Auxiliary electrical heater energy consumption total
|
||||
uint32_t auxElecHeatNrgConsHeating_; // Auxiliary electrical heater energy consumption heating
|
||||
uint32_t auxElecHeatNrgConsWW_; // Auxiliary electrical heater energy consumption DHW
|
||||
uint32_t auxElecHeatNrgConsPool_; // Auxiliary electrical heater energy consumption Pool
|
||||
char maintenanceMessage_[4];
|
||||
char maintenanceDate_[12];
|
||||
uint8_t maintenanceType_;
|
||||
uint16_t maintenanceTime_;
|
||||
|
||||
// heatpump
|
||||
uint8_t hpPower_;
|
||||
int16_t hpTc0_;
|
||||
int16_t hpTc1_;
|
||||
int16_t hpTc3_;
|
||||
int16_t hpTr3_;
|
||||
int16_t hpTr4_;
|
||||
int16_t hpTr5_;
|
||||
int16_t hpTr6_;
|
||||
int16_t hpTr7_;
|
||||
int16_t hpTl2_;
|
||||
int16_t hpPl1_;
|
||||
int16_t hpPh1_;
|
||||
uint8_t hpPower_;
|
||||
uint8_t hpCompOn_;
|
||||
uint8_t hpBrinePumpSpd_;
|
||||
uint8_t hpCompSpd_;
|
||||
uint8_t hpCircSpd_;
|
||||
uint16_t hpBrineIn_;
|
||||
uint16_t hpBrineOut_;
|
||||
uint16_t hpSuctionGas_;
|
||||
uint16_t hpHotGas_;
|
||||
uint8_t hpSwitchValve_;
|
||||
uint8_t hpActivity_;
|
||||
uint8_t hpHeatingOn_;
|
||||
uint8_t hpCoolingOn_;
|
||||
uint8_t hpWwOn_;
|
||||
uint8_t hpPoolOn_;
|
||||
uint8_t hpHeatingOn;
|
||||
int16_t hpTc0_;
|
||||
int16_t hpTc1_;
|
||||
int16_t hpTc3_;
|
||||
int16_t hpTr3_;
|
||||
int16_t hpTr4_;
|
||||
int16_t hpTr5_;
|
||||
int16_t hpTr6_;
|
||||
int16_t hpTr7_;
|
||||
int16_t hpTl2_;
|
||||
int16_t hpPl1_;
|
||||
int16_t hpPh1_;
|
||||
|
||||
// Pool unit
|
||||
int8_t poolSetTemp_;
|
||||
|
||||
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -188,6 +215,7 @@ class Boiler : public EMSdevice {
|
||||
void process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAMaintenanceData(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAMonitorWWPlus(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAInformation(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -195,19 +223,23 @@ class Boiler : public EMSdevice {
|
||||
void process_UBASettingsWW(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HpPower(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HpOutdoor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HpPool(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
// commands - none of these use the additional id parameter
|
||||
bool set_warmwater_mode(const char * value, const int8_t id);
|
||||
bool set_warmwater_activated(const char * value, const int8_t id);
|
||||
bool set_ww_mode(const char * value, const int8_t id);
|
||||
bool set_ww_activated(const char * value, const int8_t id);
|
||||
bool set_tapwarmwater_activated(const char * value, const int8_t id);
|
||||
bool set_warmwater_onetime(const char * value, const int8_t id);
|
||||
bool set_warmwater_circulation(const char * value, const int8_t id);
|
||||
bool set_warmwater_circulation_pump(const char * value, const int8_t id);
|
||||
bool set_warmwater_circulation_mode(const char * value, const int8_t id);
|
||||
bool set_warmwater_temp(const char * value, const int8_t id);
|
||||
bool set_disinfect_temp(const char * value, const int8_t id);
|
||||
bool set_warmwater_maxpower(const char * value, const int8_t id);
|
||||
bool set_wWFlowTempOffset(const char * value, const int8_t id);
|
||||
bool set_ww_onetime(const char * value, const int8_t id);
|
||||
bool set_ww_disinfect(const char * value, const int8_t id);
|
||||
bool set_ww_circulation(const char * value, const int8_t id);
|
||||
bool set_ww_circulation_pump(const char * value, const int8_t id);
|
||||
bool set_ww_circulation_mode(const char * value, const int8_t id);
|
||||
bool set_ww_temp(const char * value, const int8_t id);
|
||||
bool set_ww_temp_low(const char * value, const int8_t id);
|
||||
bool set_ww_temp_single(const char * value, const int8_t id);
|
||||
bool set_ww_disinfect_temp(const char * value, const int8_t id);
|
||||
bool set_ww_maxpower(const char * value, const int8_t id);
|
||||
bool set_ww_flowTempOffset(const char * value, const int8_t id);
|
||||
bool set_flow_temp(const char * value, const int8_t id);
|
||||
bool set_burn_power(const char * value, const int8_t id);
|
||||
bool set_heating_activated(const char * value, const int8_t id);
|
||||
@@ -226,6 +258,7 @@ class Boiler : public EMSdevice {
|
||||
bool set_maintenancedate(const char * value, const int8_t id);
|
||||
bool set_ww_hyst_on(const char * value, const int8_t id);
|
||||
bool set_ww_hyst_off(const char * value, const int8_t id);
|
||||
bool set_pool_temp(const char * value, const int8_t id);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -29,7 +29,7 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Connect::publish_ha_config() {
|
||||
bool Connect::publish_ha_device_config() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class Connect : public EMSdevice {
|
||||
public:
|
||||
Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_config();
|
||||
virtual bool publish_ha_device_config();
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||