mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-09 07:25:49 +00:00
Compare commits
102 Commits
dev
...
c40d828749
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c40d828749 | ||
|
|
84ad08887a | ||
|
|
37107d8500 | ||
|
|
dde6a8c5db | ||
|
|
e2750b8572 | ||
|
|
acd23925b5 | ||
|
|
b0db054e11 | ||
|
|
51cea8e757 | ||
|
|
bbb086ea41 | ||
|
|
539e6ed080 | ||
|
|
1d33a26318 | ||
|
|
86a20fc97a | ||
|
|
d6e00c4534 | ||
|
|
a813d38108 | ||
|
|
685a49c212 | ||
|
|
2c8eb534af | ||
|
|
5210fab4cb | ||
|
|
49787d27f1 | ||
|
|
dfe7b46461 | ||
|
|
f8257de0dd | ||
|
|
3b3ecc9f1d | ||
|
|
84105acf5d | ||
|
|
def5173692 | ||
|
|
6b31fef1af | ||
|
|
c9c059ca65 | ||
|
|
4d3b31e5a1 | ||
|
|
51d90095aa | ||
|
|
16c0370443 | ||
|
|
67bb38dcf4 | ||
|
|
049231a36e | ||
|
|
349d6b7375 | ||
|
|
b72b368d3c | ||
|
|
7f9fd44a02 | ||
|
|
a400c5974c | ||
|
|
afca995fe5 | ||
|
|
81504fedc5 | ||
|
|
3da3345683 | ||
|
|
c6c2889306 | ||
|
|
b60f0d260a | ||
|
|
cd750e4777 | ||
|
|
4e5d503b35 | ||
|
|
bd09e17e49 | ||
|
|
835eb743bb | ||
|
|
69a129d80e | ||
|
|
434bf483fd | ||
|
|
2b8e170b40 | ||
|
|
dc9b95f3e7 | ||
|
|
1616b0da0a | ||
|
|
91c457b22b | ||
|
|
70c60647c7 | ||
|
|
c0bea66d27 | ||
|
|
ed7cc078ed | ||
|
|
60b7d6d795 | ||
|
|
947f29cca0 | ||
|
|
d2a13ec0da | ||
|
|
cc39ba409e | ||
|
|
ac9db6256e | ||
|
|
096f628d97 | ||
|
|
22312812bb | ||
|
|
3584975acb | ||
|
|
30b9ca4e6c | ||
|
|
7c6ff01ebe | ||
|
|
a54edcaf5b | ||
|
|
e446954844 | ||
|
|
4a2d0d6787 | ||
|
|
9725314135 | ||
|
|
e610f0d57f | ||
|
|
8244af2940 | ||
|
|
40f371d23b | ||
|
|
817b791e59 | ||
|
|
25a7aac360 | ||
|
|
37115a174d | ||
|
|
1397f81fd0 | ||
|
|
56365cb403 | ||
|
|
dfd245ee7b | ||
|
|
9c81e4b34d | ||
|
|
67676df131 | ||
|
|
a73b129596 | ||
|
|
4600d886b5 | ||
|
|
0fe45a2405 | ||
|
|
db87213242 | ||
|
|
5c3c010d5a | ||
|
|
c804cedd7a | ||
|
|
aa30ca99bf | ||
|
|
c0ca9d1069 | ||
|
|
5e79e1d57f | ||
|
|
8c732f9f1e | ||
|
|
5e94c2f636 | ||
|
|
64e5d29996 | ||
|
|
b320d8ded2 | ||
|
|
0be1b20996 | ||
|
|
6c55460622 | ||
|
|
d627404dc2 | ||
|
|
f317123c26 | ||
|
|
e4df1887b0 | ||
|
|
34142c3e85 | ||
|
|
6e7f8bdf02 | ||
|
|
3dd9fcfb58 | ||
|
|
35e2954b8b | ||
|
|
59aa63db0f | ||
|
|
7a41a190f8 | ||
|
|
6741232450 |
6
.github/workflows/dev_release.yml
vendored
6
.github/workflows/dev_release.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
node-version: 24
|
node-version: 24
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Enable Corepack
|
- name: Enable Corepack
|
||||||
run: corepack enable pnpm
|
run: corepack enable pnpm
|
||||||
@@ -62,13 +62,13 @@ jobs:
|
|||||||
platformio run
|
platformio run
|
||||||
|
|
||||||
- name: Commit the generated files
|
- name: Commit the generated files
|
||||||
uses: stefanzweifel/git-auto-commit-action@v7
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
with:
|
with:
|
||||||
commit_message: "chore: update generated files for v${{steps.build_info.outputs.VERSION}}"
|
commit_message: "chore: update generated files for v${{steps.build_info.outputs.VERSION}}"
|
||||||
|
|
||||||
- name: Create GitHub Release
|
- name: Create GitHub Release
|
||||||
id: 'automatic_releases'
|
id: 'automatic_releases'
|
||||||
uses: emsesp/action-automatic-releases@v1.0.1
|
uses: emsesp/action-automatic-releases@v1.0.0
|
||||||
with:
|
with:
|
||||||
repo_token: '${{ secrets.GITHUB_TOKEN }}'
|
repo_token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
title: Development Build v${{steps.build_info.outputs.VERSION}}
|
title: Development Build v${{steps.build_info.outputs.VERSION}}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: GitHub Releases To Discord
|
- name: GitHub Releases To Discord
|
||||||
uses: SethCohen/github-releases-to-discord@v1.13.1
|
uses: SethCohen/github-releases-to-discord@v1.13.1
|
||||||
|
|||||||
2
.github/workflows/pr_check.yml
vendored
2
.github/workflows/pr_check.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Install python 3.13
|
- name: Install python 3.13
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
|
|||||||
2
.github/workflows/sonar_check.yml
vendored
2
.github/workflows/sonar_check.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
BUILD_WRAPPER_OUT_DIR: bw-output
|
BUILD_WRAPPER_OUT_DIR: bw-output
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install Build Wrapper
|
- name: Install Build Wrapper
|
||||||
|
|||||||
4
.github/workflows/stable_release.yml
vendored
4
.github/workflows/stable_release.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
node-version: 24
|
node-version: 24
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Enable Corepack
|
- name: Enable Corepack
|
||||||
run: corepack enable pnpm
|
run: corepack enable pnpm
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
platformio run
|
platformio run
|
||||||
|
|
||||||
- name: Create GitHub Release
|
- name: Create GitHub Release
|
||||||
uses: emsesp/action-automatic-releases@v1.0.1
|
uses: emsesp/action-automatic-releases@v1.0.0
|
||||||
with:
|
with:
|
||||||
repo_token: '${{ secrets.GITHUB_TOKEN }}'
|
repo_token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|||||||
4
.github/workflows/test_release.yml
vendored
4
.github/workflows/test_release.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
node-version: 24
|
node-version: 24
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Enable Corepack
|
- name: Enable Corepack
|
||||||
run: corepack enable pnpm
|
run: corepack enable pnpm
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create GitHub Release
|
- name: Create GitHub Release
|
||||||
id: 'automatic_releases'
|
id: 'automatic_releases'
|
||||||
uses: emsesp/action-automatic-releases@v1.0.1
|
uses: emsesp/action-automatic-releases@v1.0.0
|
||||||
with:
|
with:
|
||||||
repo_token: '${{ secrets.GITHUB_TOKEN }}'
|
repo_token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
title: Test Build v${{steps.build_info.outputs.VERSION}}
|
title: Test Build v${{steps.build_info.outputs.VERSION}}
|
||||||
|
|||||||
@@ -12,16 +12,14 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
|||||||
- prometheus metrics for temperature/analog/scheduler/custom [#2962](https://github.com/emsesp/EMS-ESP32/issues/2962)
|
- prometheus metrics for temperature/analog/scheduler/custom [#2962](https://github.com/emsesp/EMS-ESP32/issues/2962)
|
||||||
- boiler pumpkick [#2965](https://github.com/emsesp/EMS-ESP32/discussions/2965)
|
- boiler pumpkick [#2965](https://github.com/emsesp/EMS-ESP32/discussions/2965)
|
||||||
- heatpump reset [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
|
- heatpump reset [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
|
||||||
|
- e-mail notification using ReadyMail Client
|
||||||
- 2.nd freshwater module (dhw4, dhw5) [#2991](https://github.com/emsesp/EMS-ESP32/issues/2991)
|
- 2.nd freshwater module (dhw4, dhw5) [#2991](https://github.com/emsesp/EMS-ESP32/issues/2991)
|
||||||
- full system backup and restore
|
- full system backup and restore
|
||||||
- auto-logic to set ht3/ems+ tx-mode
|
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
|
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
|
||||||
- missing translations [#3015](https://github.com/emsesp/EMS-ESP32/issues/3015)
|
- missing translations [#3015](https://github.com/emsesp/EMS-ESP32/issues/3015)
|
||||||
- custom entities check fetch length
|
|
||||||
- modbus initialization [#3064](https://github.com/emsesp/EMS-ESP32/issues/3064)
|
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
@@ -33,8 +31,8 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
|||||||
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
|
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
|
||||||
- update version if it is 00.00 in first read [#2981](https://github.com/emsesp/EMS-ESP32/issues/2981)
|
- update version if it is 00.00 in first read [#2981](https://github.com/emsesp/EMS-ESP32/issues/2981)
|
||||||
- device class for % values [#2980](https://github.com/emsesp/EMS-ESP32/issues/2980)
|
- device class for % values [#2980](https://github.com/emsesp/EMS-ESP32/issues/2980)
|
||||||
|
- use tasmota core 2026.03.30
|
||||||
|
- secure mqtt uses ESP_SSLClient
|
||||||
- fetch telegrams: set length to fetch [#3017](https://github.com/emsesp/EMS-ESP32/issues/3017)
|
- fetch telegrams: set length to fetch [#3017](https://github.com/emsesp/EMS-ESP32/issues/3017)
|
||||||
- move http client from stack to heap
|
- move http client from stack to heap
|
||||||
- heap optimizations [#3021](https://github.com/emsesp/EMS-ESP32/discussions/3021)
|
- heap optimizations [#3021](https://github.com/emsesp/EMS-ESP32/discussions/3021)
|
||||||
- check and read 0x470 as summer2_typeids[0] only if received [#2686](https://github.com/emsesp/EMS-ESP32/issues/2686), [#3055](https://github.com/emsesp/EMS-ESP32/issues/3055)
|
|
||||||
- default bus-id: gateway1(0x49), tx-mode: auto
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
},
|
},
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
"-DTASMOTA_SDK",
|
"-DNO_TLS_SUPPORT",
|
||||||
"-DARDUINO_LOLIN_C3_MINI",
|
"-DARDUINO_LOLIN_C3_MINI",
|
||||||
"-DARDUINO_USB_MODE=1",
|
"-DARDUINO_USB_MODE=1",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
"-DBOARD_HAS_PSRAM",
|
"-DBOARD_HAS_PSRAM",
|
||||||
"-DTASMOTA_SDK",
|
"-DNO_TLS_SUPPORT",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
"-DARDUINO_USB_MODE=0"
|
"-DARDUINO_USB_MODE=0"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"arduino",
|
"arduino",
|
||||||
"espidf"
|
"espidf"
|
||||||
],
|
],
|
||||||
"name": "Espressif ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS",
|
"name": "Tasmota ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS",
|
||||||
"upload": {
|
"upload": {
|
||||||
"flash_size": "32MB",
|
"flash_size": "32MB",
|
||||||
"maximum_ram_size": 327680,
|
"maximum_ram_size": 327680,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"build": {
|
"build": {
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": "-DTASMOTA_SDK",
|
"extra_flags": "-DNO_TLS_SUPPORT",
|
||||||
"f_cpu": "240000000L",
|
"f_cpu": "240000000L",
|
||||||
"f_flash": "40000000L",
|
"f_flash": "40000000L",
|
||||||
"flash_mode": "dio",
|
"flash_mode": "dio",
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"arduino",
|
"arduino",
|
||||||
"espidf"
|
"espidf"
|
||||||
],
|
],
|
||||||
"name": "Espressif ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
|
"name": "Tasmota ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
|
||||||
"upload": {
|
"upload": {
|
||||||
"flash_size": "16MB",
|
"flash_size": "16MB",
|
||||||
"maximum_ram_size": 327680,
|
"maximum_ram_size": 327680,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"arduino",
|
"arduino",
|
||||||
"espidf"
|
"espidf"
|
||||||
],
|
],
|
||||||
"name": "Espressif ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS",
|
"name": "Tasmota ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS",
|
||||||
"upload": {
|
"upload": {
|
||||||
"flash_size": "16MB",
|
"flash_size": "16MB",
|
||||||
"maximum_ram_size": 327680,
|
"maximum_ram_size": 327680,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"build": {
|
"build": {
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": "-DTASMOTA_SDK",
|
"extra_flags": "-DNO_TLS_SUPPORT",
|
||||||
"f_cpu": "240000000L",
|
"f_cpu": "240000000L",
|
||||||
"f_flash": "40000000L",
|
"f_flash": "40000000L",
|
||||||
"flash_mode": "dio",
|
"flash_mode": "dio",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
|
"-DNO_TLS_SUPPORT",
|
||||||
"-DARDUINO_XIAO_ESP32C6",
|
"-DARDUINO_XIAO_ESP32C6",
|
||||||
"-DARDUINO_USB_MODE=1",
|
"-DARDUINO_USB_MODE=1",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||||
|
|||||||
@@ -112,10 +112,10 @@ telegram_type_id,name,is_fetched
|
|||||||
0x02A0,RC300Curves,
|
0x02A0,RC300Curves,
|
||||||
0x02A1,RC300Curves,
|
0x02A1,RC300Curves,
|
||||||
0x02A2,RC300Curves,
|
0x02A2,RC300Curves,
|
||||||
0x02A5,RC300Monitor,
|
0x02A5,RC300Monitor,fetched
|
||||||
0x02A6,RC300Monitor,
|
0x02A6,CRFMonitor,
|
||||||
0x02A7,RC300Monitor,
|
0x02A7,RC300Monitor,
|
||||||
0x02A8,RC300Monitor,
|
0x02A8,CRFMonitor,
|
||||||
0x02A9,RC300Monitor,
|
0x02A9,RC300Monitor,
|
||||||
0x02AA,RC300Monitor,
|
0x02AA,RC300Monitor,
|
||||||
0x02AB,RC300Monitor,
|
0x02AB,RC300Monitor,
|
||||||
@@ -171,7 +171,6 @@ telegram_type_id,name,is_fetched
|
|||||||
0x0468,HPSet,
|
0x0468,HPSet,
|
||||||
0x0469,HPSet,
|
0x0469,HPSet,
|
||||||
0x046A,HPSet,
|
0x046A,HPSet,
|
||||||
0x0470,RC300Summer2,fetched
|
|
||||||
0x0471,RC300Summer2,
|
0x0471,RC300Summer2,
|
||||||
0x0472,RC300Summer2,
|
0x0472,RC300Summer2,
|
||||||
0x0473,RC300Summer2,
|
0x0473,RC300Summer2,
|
||||||
|
|||||||
|
@@ -26,11 +26,11 @@
|
|||||||
"@alova/adapter-xhr": "2.3.1",
|
"@alova/adapter-xhr": "2.3.1",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@mui/icons-material": "^9.0.1",
|
"@mui/icons-material": "^9.0.0",
|
||||||
"@mui/material": "^9.0.1",
|
"@mui/material": "^9.0.0",
|
||||||
"@preact/compat": "^18.3.2",
|
"@preact/compat": "^18.3.2",
|
||||||
"@table-library/react-table-library": "4.1.15",
|
"@table-library/react-table-library": "4.1.15",
|
||||||
"alova": "3.5.1",
|
"alova": "^3.5.1",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"formidable": "^3.5.4",
|
"formidable": "^3.5.4",
|
||||||
@@ -38,11 +38,11 @@
|
|||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"preact": "^10.29.1",
|
"preact": "^10.29.1",
|
||||||
"react": "^19.2.6",
|
"react": "^19.2.5",
|
||||||
"react-dom": "^19.2.6",
|
"react-dom": "^19.2.5",
|
||||||
"react-icons": "^5.6.0",
|
"react-icons": "^5.6.0",
|
||||||
"react-router": "^7.15.0",
|
"react-router": "^7.14.1",
|
||||||
"react-toastify": "^11.1.0",
|
"react-toastify": "^11.0.5",
|
||||||
"typesafe-i18n": "^5.27.1",
|
"typesafe-i18n": "^5.27.1",
|
||||||
"typescript": "^6.0.3"
|
"typescript": "^6.0.3"
|
||||||
},
|
},
|
||||||
@@ -55,17 +55,16 @@
|
|||||||
"@types/node": "^25.6.0",
|
"@types/node": "^25.6.0",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"axe-core": "^4.11.4",
|
"axe-core": "^4.11.3",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"eslint": "^10.3.0",
|
"eslint": "^10.2.1",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.8.3",
|
||||||
"rollup-plugin-visualizer": "^7.0.1",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"terser": "^5.47.0",
|
"terser": "^5.46.1",
|
||||||
"typescript-eslint": "^8.59.2",
|
"typescript-eslint": "^8.58.2",
|
||||||
"vite": "^8.0.11",
|
"vite": "^8.0.8",
|
||||||
"vite-plugin-imagemin": "^0.6.1",
|
"vite-plugin-imagemin": "^0.6.1"
|
||||||
"vite-tsconfig-paths": "^6.1.1"
|
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.33.4"
|
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
||||||
}
|
}
|
||||||
|
|||||||
787
interface/pnpm-lock.yaml
generated
787
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ import { PROJECT_NAME } from 'env';
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import type { SignInRequest } from 'types';
|
import type { SignInRequest } from 'types';
|
||||||
import { onEnterCallback, updateValue } from 'utils';
|
import { onEnterCallback, updateValue } from 'utils';
|
||||||
import { SIGN_IN_REQUEST_VALIDATOR, ValidationError, validate } from 'validators';
|
import { SIGN_IN_REQUEST_VALIDATOR, validate } from 'validators';
|
||||||
|
|
||||||
const SignIn = memo(() => {
|
const SignIn = memo(() => {
|
||||||
const authenticationContext = useContext(AuthenticationContext);
|
const authenticationContext = useContext(AuthenticationContext);
|
||||||
@@ -74,7 +74,7 @@ const SignIn = memo(() => {
|
|||||||
await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest);
|
await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest);
|
||||||
await signIn();
|
await signIn();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
setProcessing(false);
|
setProcessing(false);
|
||||||
}
|
}
|
||||||
}, [signInRequest, signIn, LL]);
|
}, [signInRequest, signIn, LL]);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
|||||||
import { BlockFormControlLabel, ValidatedTextField } from 'components';
|
import { BlockFormControlLabel, ValidatedTextField } from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValue } from 'utils';
|
import { numberValue, updateValue } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
import { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types';
|
import { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types';
|
||||||
import type { EntityItem } from './types';
|
import type { EntityItem } from './types';
|
||||||
@@ -136,7 +136,7 @@ const CustomEntitiesDialog = ({
|
|||||||
}
|
}
|
||||||
onSave(processedItem);
|
onSave(processedItem);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}, [validator, editItem, onSave]);
|
}, [validator, editItem, onSave]);
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ const CustomEntitiesDialog = ({
|
|||||||
name="value"
|
name="value"
|
||||||
label={LL.DEFAULT(0) + ' ' + LL.VALUE(0)}
|
label={LL.DEFAULT(0) + ' ' + LL.VALUE(0)}
|
||||||
type="string"
|
type="string"
|
||||||
value={editItem.value}
|
value={editItem.value as string}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
fullWidth
|
fullWidth
|
||||||
@@ -260,7 +260,7 @@ const CustomEntitiesDialog = ({
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
type="string"
|
type="string"
|
||||||
value={editItem.device_id}
|
value={editItem.device_id as string}
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
@@ -280,7 +280,7 @@ const CustomEntitiesDialog = ({
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
type="string"
|
type="string"
|
||||||
value={editItem.type_id}
|
value={editItem.type_id as string}
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
@@ -381,7 +381,7 @@ const CustomEntitiesDialog = ({
|
|||||||
fieldErrors={fieldErrors || {}}
|
fieldErrors={fieldErrors || {}}
|
||||||
name="factor"
|
name="factor"
|
||||||
label={LL.BITMASK()}
|
label={LL.BITMASK()}
|
||||||
value={editItem.factor}
|
value={editItem.factor as string}
|
||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
|||||||
import { ValidatedTextField } from 'components';
|
import { ValidatedTextField } from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValue } from 'utils';
|
import { numberValue, updateValue } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
import { DeviceValueUOM, DeviceValueUOM_s } from './types';
|
import { DeviceValueUOM, DeviceValueUOM_s } from './types';
|
||||||
import type { DeviceValue } from './types';
|
import type { DeviceValue } from './types';
|
||||||
@@ -67,7 +67,7 @@ const DevicesDialog = ({
|
|||||||
await validate(validator, editItem);
|
await validate(validator, editItem);
|
||||||
onSave(editItem);
|
onSave(editItem);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}, [validator, editItem, onSave]);
|
}, [validator, editItem, onSave]);
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
|||||||
import { BlockFormControlLabel, ValidatedTextField } from 'components';
|
import { BlockFormControlLabel, ValidatedTextField } from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { updateValue } from 'utils';
|
import { updateValue } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
import { ScheduleFlag } from './types';
|
import { ScheduleFlag } from './types';
|
||||||
import type { ScheduleItem } from './types';
|
import type { ScheduleItem } from './types';
|
||||||
@@ -120,7 +120,7 @@ const SchedulerDialog = ({
|
|||||||
await validate(validator, itemToSave);
|
await validate(validator, itemToSave);
|
||||||
onSave(itemToSave);
|
onSave(itemToSave);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[validator, onSave]
|
[validator, onSave]
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
|||||||
import { ValidatedTextField } from 'components';
|
import { ValidatedTextField } from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValue } from 'utils';
|
import { numberValue, updateValue } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types';
|
import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types';
|
||||||
import type { AnalogSensor } from './types';
|
import type { AnalogSensor } from './types';
|
||||||
@@ -172,7 +172,7 @@ const SensorsAnalogDialog = ({
|
|||||||
await validate(validator, editItem);
|
await validate(validator, editItem);
|
||||||
onSave(editItem);
|
onSave(editItem);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}, [validator, editItem, onSave]);
|
}, [validator, editItem, onSave]);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
|||||||
import { ValidatedTextField } from 'components';
|
import { ValidatedTextField } from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValue } from 'utils';
|
import { numberValue, updateValue } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
import type { TemperatureSensor } from './types';
|
import type { TemperatureSensor } from './types';
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ const SensorsTemperatureDialog = ({
|
|||||||
await validate(validator, editItem);
|
await validate(validator, editItem);
|
||||||
onSave(editItem);
|
onSave(editItem);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}, [validator, editItem, onSave]);
|
}, [validator, editItem, onSave]);
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,16 @@ export interface Settings {
|
|||||||
modbus_port: number;
|
modbus_port: number;
|
||||||
modbus_max_clients: number;
|
modbus_max_clients: number;
|
||||||
modbus_timeout: number;
|
modbus_timeout: number;
|
||||||
|
email_enabled: boolean;
|
||||||
|
email_ssl?: boolean;
|
||||||
|
email_starttls?: boolean;
|
||||||
|
email_server: string;
|
||||||
|
email_port: number;
|
||||||
|
email_login: string;
|
||||||
|
email_pass: string;
|
||||||
|
email_sender: string;
|
||||||
|
email_recp: string;
|
||||||
|
email_subject: string;
|
||||||
developer_mode: boolean;
|
developer_mode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { useI18nContext } from 'i18n/i18n-react';
|
|||||||
import type { APSettingsType } from 'types';
|
import type { APSettingsType } from 'types';
|
||||||
import { APProvisionMode } from 'types';
|
import { APProvisionMode } from 'types';
|
||||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
import { ValidationError, createAPSettingsValidator, validate } from 'validators';
|
import { createAPSettingsValidator, validate } from 'validators';
|
||||||
|
|
||||||
export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
|
export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
|
||||||
provision_mode === APProvisionMode.AP_MODE_ALWAYS ||
|
provision_mode === APProvisionMode.AP_MODE_ALWAYS ||
|
||||||
@@ -86,7 +86,7 @@ const APSettings = () => {
|
|||||||
await validate(createAPSettingsValidator(data), data);
|
await validate(createAPSettingsValidator(data), data);
|
||||||
await saveData();
|
await saveData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}, [data, saveData]);
|
}, [data, saveData]);
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,13 @@ import {
|
|||||||
FormLoader,
|
FormLoader,
|
||||||
MessageBox,
|
MessageBox,
|
||||||
SectionContent,
|
SectionContent,
|
||||||
|
ValidatedPasswordField,
|
||||||
ValidatedTextField,
|
ValidatedTextField,
|
||||||
useLayoutTitle
|
useLayoutTitle
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
|
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
|
||||||
import { BOARD_PROFILES } from '../main/types';
|
import { BOARD_PROFILES } from '../main/types';
|
||||||
@@ -153,7 +154,7 @@ const ApplicationSettings = () => {
|
|||||||
setFieldErrors(undefined);
|
setFieldErrors(undefined);
|
||||||
await validate(createSettingsValidator(data), data);
|
await validate(createSettingsValidator(data), data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
} finally {
|
} finally {
|
||||||
await saveData();
|
await saveData();
|
||||||
}
|
}
|
||||||
@@ -351,6 +352,156 @@ const ApplicationSettings = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
<Typography color="secondary">eMail</Typography>
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.email_enabled}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
name="email_enabled"
|
||||||
|
disabled={!hardwareData.psram}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography color={!hardwareData.psram ? 'grey' : 'default'}>
|
||||||
|
Enable eMail notification
|
||||||
|
{!hardwareData.psram && (
|
||||||
|
<Typography variant="caption">
|
||||||
|
({LL.IS_REQUIRED('PSRAM')})
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{data.email_enabled && (
|
||||||
|
<>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={2}
|
||||||
|
direction="row"
|
||||||
|
justifyContent="flex-start"
|
||||||
|
alignItems="flex-start"
|
||||||
|
>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_server"
|
||||||
|
label="SMTP Server"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_server}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
sx={{ width: '12ch' }}
|
||||||
|
name="email_port"
|
||||||
|
variant="outlined"
|
||||||
|
label="Port"
|
||||||
|
value={numberValue(data.email_port)}
|
||||||
|
type="number"
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={4} mt={!data.email_ssl && !data.email_starttls ? 0 : 3}>
|
||||||
|
{!data.email_starttls && (
|
||||||
|
<BlockFormControlLabel
|
||||||
|
sx={{ width: '12ch' }}
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.email_ssl}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
name="email_ssl"
|
||||||
|
disabled={
|
||||||
|
data.email_starttls || data.email_ssl === undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="SSL/TLS"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!data.email_ssl && (
|
||||||
|
<BlockFormControlLabel
|
||||||
|
sx={{ width: '12ch' }}
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.email_starttls}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
name="email_starttls"
|
||||||
|
disabled={
|
||||||
|
data.email_ssl || data.email_starttls === undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="STARTTLS"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_login"
|
||||||
|
label="Login"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_login}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedPasswordField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_pass"
|
||||||
|
label="Password"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_pass}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_sender"
|
||||||
|
label="From"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_sender}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_recp"
|
||||||
|
label="To"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_recp}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors || {}}
|
||||||
|
name="email_subject"
|
||||||
|
label="Subject"
|
||||||
|
variant="outlined"
|
||||||
|
value={data.email_subject}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
|
||||||
{LL.SENSORS()}
|
{LL.SENSORS()}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -677,7 +828,6 @@ const ApplicationSettings = () => {
|
|||||||
<MenuItem value={2}>EMS+</MenuItem>
|
<MenuItem value={2}>EMS+</MenuItem>
|
||||||
<MenuItem value={3}>HT3</MenuItem>
|
<MenuItem value={3}>HT3</MenuItem>
|
||||||
<MenuItem value={4}>{LL.HARDWARE()}</MenuItem>
|
<MenuItem value={4}>{LL.HARDWARE()}</MenuItem>
|
||||||
<MenuItem value={5}>Auto</MenuItem>
|
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import type { MqttSettingsType } from 'types';
|
import type { MqttSettingsType } from 'types';
|
||||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
import { ValidationError, createMqttSettingsValidator, validate } from 'validators';
|
import { createMqttSettingsValidator, validate } from 'validators';
|
||||||
|
|
||||||
import { callAction } from '../../api/app';
|
import { callAction } from '../../api/app';
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ const MqttSettings = () => {
|
|||||||
await validate(createMqttSettingsValidator(data), data);
|
await validate(createMqttSettingsValidator(data), data);
|
||||||
await saveData();
|
await saveData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}, [data, saveData]);
|
}, [data, saveData]);
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import type { NTPSettingsType, Time } from 'types';
|
import type { NTPSettingsType, Time } from 'types';
|
||||||
import { formatLocalDateTime, updateValueDirty, useRest } from 'utils';
|
import { formatLocalDateTime, updateValueDirty, useRest } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
||||||
|
|
||||||
import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ';
|
import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ';
|
||||||
@@ -133,7 +133,7 @@ const NTPSettings = () => {
|
|||||||
await validate(NTP_SETTINGS_VALIDATOR, data);
|
await validate(NTP_SETTINGS_VALIDATOR, data);
|
||||||
await saveData();
|
await saveData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}, [data, saveData]);
|
}, [data, saveData]);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import {
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import type { NetworkSettingsType } from 'types';
|
import type { NetworkSettingsType } from 'types';
|
||||||
import { updateValueDirty, useRest } from 'utils';
|
import { updateValueDirty, useRest } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { createNetworkSettingsValidator } from 'validators/network';
|
import { createNetworkSettingsValidator } from 'validators/network';
|
||||||
|
|
||||||
import SystemMonitor from '../../status/SystemMonitor';
|
import SystemMonitor from '../../status/SystemMonitor';
|
||||||
@@ -116,7 +116,7 @@ const NetworkSettings = () => {
|
|||||||
await validate(createNetworkSettingsValidator(data), data);
|
await validate(createNetworkSettingsValidator(data), data);
|
||||||
await saveData();
|
await saveData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
deselectNetwork();
|
deselectNetwork();
|
||||||
}, [data, saveData, deselectNetwork]);
|
}, [data, saveData, deselectNetwork]);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { AuthenticatedContext } from 'contexts/authentication';
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import type { SecuritySettingsType } from 'types';
|
import type { SecuritySettingsType } from 'types';
|
||||||
import { updateValueDirty, useRest } from 'utils';
|
import { updateValueDirty, useRest } from 'utils';
|
||||||
import { SECURITY_SETTINGS_VALIDATOR, ValidationError, validate } from 'validators';
|
import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators';
|
||||||
|
|
||||||
const SecuritySettings = () => {
|
const SecuritySettings = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
@@ -58,7 +58,7 @@ const SecuritySettings = () => {
|
|||||||
await saveData();
|
await saveData();
|
||||||
await authenticatedContext.refresh();
|
await authenticatedContext.refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}, [data, saveData, authenticatedContext]);
|
}, [data, saveData, authenticatedContext]);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import type { UserType } from 'types';
|
import type { UserType } from 'types';
|
||||||
import { updateValue } from 'utils';
|
import { updateValue } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
interface UserFormProps {
|
interface UserFormProps {
|
||||||
creating: boolean;
|
creating: boolean;
|
||||||
@@ -69,7 +69,7 @@ const User: FC<UserFormProps> = ({
|
|||||||
await validate(validator, user);
|
await validate(validator, user);
|
||||||
onDoneEditing();
|
onDoneEditing();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldErrors((error as ValidationError).fieldErrors);
|
setFieldErrors(error as ValidateFieldsError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [user, validator, onDoneEditing]);
|
}, [user, validator, onDoneEditing]);
|
||||||
|
|||||||
@@ -315,22 +315,13 @@ const InstallDialog = memo(
|
|||||||
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
|
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{upgradeImportantMessageType === 1 && LL.UPGRADE_IMPORTANT_MESSAGES_1()}
|
||||||
{upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()}
|
{upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()}
|
||||||
{upgradeImportantMessageType === 1 && (
|
|
||||||
<>
|
|
||||||
{LL.UPGRADE_IMPORTANT_MESSAGES_1()}
|
|
||||||
<Typography sx={{ mt: 2 }}>
|
|
||||||
<Link to="/settings/downloadUpload" style={{ color: 'lightblue' }}>
|
|
||||||
{LL.DOWNLOAD_SYSTEM_BACKUP()}
|
|
||||||
</Link>
|
|
||||||
</Typography>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Typography sx={{ mt: 2 }}>
|
<Typography sx={{ mt: 2 }}>
|
||||||
<Link
|
<Link
|
||||||
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
|
||||||
style={{ color: 'lightblue' }}
|
style={{ color: 'lightblue' }}
|
||||||
>
|
>
|
||||||
{LL.ONLINE_HELP()}
|
{LL.ONLINE_HELP()}
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ const de: Translation = {
|
|||||||
COMPACT: 'Kompakte Darstellung',
|
COMPACT: 'Kompakte Darstellung',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
|
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Systemsicherung',
|
DOWNLOAD_SYSTEM_BACKUP: 'System Sicherung',
|
||||||
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch',
|
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch',
|
||||||
UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier',
|
UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier',
|
||||||
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
|
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
|
||||||
@@ -362,9 +362,9 @@ const de: Translation = {
|
|||||||
STORED_VERSIONS: 'Gespeicherte Versionen',
|
STORED_VERSIONS: 'Gespeicherte Versionen',
|
||||||
ONLINE_HELP: 'Online-Hilfe',
|
ONLINE_HELP: 'Online-Hilfe',
|
||||||
UPGRADE_IMPORTANT_MESSAGES: 'Wichtige Nachrichten aktualisieren',
|
UPGRADE_IMPORTANT_MESSAGES: 'Wichtige Nachrichten aktualisieren',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Für diese Aktualisierung ist ein Werksreset erforderlich. Stellen Sie sicher, dass Sie zuerst eine Systemsicherung herunterladen, bevor Sie fortfahren, und laden Sie diese Datei dann nach der Installation der neuen Version hoch.',
|
UPGRADE_IMPORTANT_MESSAGES_1: 'Diese Aktualisierung erfordert eine Werkseinstellung. Stellen Sie sicher, dass Sie zuerst eine Systemsicherung herunterladen, bevor Sie fortfahren, und laden Sie diese Datei dann nach der Installation der neuen Version hoch.',
|
||||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
|
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
|
||||||
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und Einstellungen erstellen. Alle Passwörter werden in dieser Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?'
|
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und -einstellungen erstellen. Alle Passwörter werden im Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,6 @@
|
|||||||
import type {
|
import type { InternalRuleItem, ValidateOption } from 'async-validator';
|
||||||
InternalRuleItem,
|
|
||||||
ValidateFieldsError,
|
|
||||||
ValidateOption
|
|
||||||
} from 'async-validator';
|
|
||||||
import type Schema from 'async-validator';
|
import type Schema from 'async-validator';
|
||||||
|
|
||||||
export class ValidationError extends Error {
|
|
||||||
readonly fieldErrors: ValidateFieldsError;
|
|
||||||
|
|
||||||
constructor(fieldErrors: ValidateFieldsError) {
|
|
||||||
super('Validation failed');
|
|
||||||
this.name = 'ValidationError';
|
|
||||||
this.fieldErrors = fieldErrors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const validate = <T extends object>(
|
export const validate = <T extends object>(
|
||||||
validator: Schema,
|
validator: Schema,
|
||||||
source: Partial<T>,
|
source: Partial<T>,
|
||||||
@@ -22,7 +8,7 @@ export const validate = <T extends object>(
|
|||||||
): Promise<T> =>
|
): Promise<T> =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
void validator.validate(source, options ?? {}, (errors, fieldErrors) => {
|
void validator.validate(source, options ?? {}, (errors, fieldErrors) => {
|
||||||
errors ? reject(new ValidationError(fieldErrors)) : resolve(source as T);
|
errors ? reject(fieldErrors as Error) : resolve(source as T);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ the LICENSE file.
|
|||||||
#define EMC_CLIENTID_LENGTH 23 + 1
|
#define EMC_CLIENTID_LENGTH 23 + 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef EMSESP_MQTT_STACKSIZE
|
||||||
|
#define EMC_TASK_STACK_SIZE EMSESP_MQTT_STACKSIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef EMC_TASK_STACK_SIZE
|
#ifndef EMC_TASK_STACK_SIZE
|
||||||
#define EMC_TASK_STACK_SIZE 5120
|
#define EMC_TASK_STACK_SIZE 5120
|
||||||
#endif
|
#endif
|
||||||
@@ -73,7 +77,3 @@ the LICENSE file.
|
|||||||
#define EMC_SIZE_POOL_ELEMENTS 128
|
#define EMC_SIZE_POOL_ELEMENTS 128
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef TASMOTA_SDK
|
|
||||||
#define EMC_CLIENT_SECURE
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -62,14 +62,19 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
|
|||||||
_xSemaphore = xSemaphoreCreateMutex();
|
_xSemaphore = xSemaphoreCreateMutex();
|
||||||
EMC_SEMAPHORE_GIVE(); // release before first use
|
EMC_SEMAPHORE_GIVE(); // release before first use
|
||||||
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
|
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
|
||||||
|
if (core > 1) {
|
||||||
|
xTaskCreate((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle);
|
||||||
|
} else {
|
||||||
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core);
|
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
(void) useInternalTask;
|
(void) useInternalTask;
|
||||||
(void) priority;
|
(void) priority;
|
||||||
(void) core;
|
(void) core;
|
||||||
#endif
|
#endif
|
||||||
_clientId = _generatedClientId;
|
_clientId = _generatedClientId;
|
||||||
|
_core = core;
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttClient::~MqttClient() {
|
MqttClient::~MqttClient() {
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ class MqttClient {
|
|||||||
const char* getClientId() const;
|
const char* getClientId() const;
|
||||||
size_t queueSize(); // No const because of mutex
|
size_t queueSize(); // No const because of mutex
|
||||||
void loop();
|
void loop();
|
||||||
|
uint32_t stack() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
return uxTaskGetStackHighWaterMark(_taskHandle);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
uint8_t core() {
|
||||||
|
return _core;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
|
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
|
||||||
@@ -98,6 +108,7 @@ class MqttClient {
|
|||||||
uint8_t _willQos;
|
uint8_t _willQos;
|
||||||
bool _willRetain;
|
bool _willRetain;
|
||||||
uint32_t _timeout;
|
uint32_t _timeout;
|
||||||
|
uint8_t _core;
|
||||||
|
|
||||||
// state is protected to allow state changes by the transport system, defined in child classes
|
// state is protected to allow state changes by the transport system, defined in child classes
|
||||||
// eg. to allow AsyncTCP
|
// eg. to allow AsyncTCP
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ the LICENSE file.
|
|||||||
|
|
||||||
#include "ClientPosix.h"
|
#include "ClientPosix.h"
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ the LICENSE file.
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -6,28 +6,31 @@ For a copy, see <https://opensource.org/licenses/MIT> or
|
|||||||
the LICENSE file.
|
the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
#ifndef NO_TLS_SUPPORT
|
||||||
|
|
||||||
#include "ClientSecureSync.h"
|
#include "ClientSecureSync.h"
|
||||||
#include <lwip/sockets.h> // socket options
|
#include <lwip/sockets.h>
|
||||||
|
#include "../Config.h"
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
|
|
||||||
ClientSecureSync::ClientSecureSync()
|
ClientSecureSync::ClientSecureSync()
|
||||||
: client() {
|
: client() {
|
||||||
// empty
|
client.setClient(&basic_client, true);
|
||||||
|
client.setBufferSizes(EMC_RX_BUFFER_SIZE, EMC_TX_BUFFER_SIZE);
|
||||||
|
client.setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientSecureSync::~ClientSecureSync() {
|
||||||
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
|
bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
|
||||||
bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool
|
bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool
|
||||||
if (ret) {
|
if (ret) {
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
|
||||||
client.setNoDelay(true);
|
|
||||||
#elif defined(ARDUINO_ARCH_ESP32)
|
|
||||||
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
||||||
int val = true;
|
int val = true;
|
||||||
client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -35,13 +38,9 @@ bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
|
|||||||
bool ClientSecureSync::connect(const char * host, uint16_t port) {
|
bool ClientSecureSync::connect(const char * host, uint16_t port) {
|
||||||
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
|
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
|
||||||
if (ret) {
|
if (ret) {
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
|
||||||
client.setNoDelay(true);
|
|
||||||
#elif defined(ARDUINO_ARCH_ESP32)
|
|
||||||
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
||||||
int val = true;
|
int val = true;
|
||||||
client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,11 @@ the LICENSE file.
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
#ifndef NO_TLS_SUPPORT
|
||||||
|
|
||||||
// Added for EMS-ESP
|
// #include "esp_tls.h"
|
||||||
#include "../Config.h"
|
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
|
||||||
#include <WiFiClientSecure.h> // includes IPAddress
|
|
||||||
#else
|
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
#endif
|
#include <ESP_SSLClient.h>
|
||||||
#include "Transport.h"
|
#include "Transport.h"
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
@@ -24,6 +20,7 @@ namespace espMqttClientInternals {
|
|||||||
class ClientSecureSync : public Transport {
|
class ClientSecureSync : public Transport {
|
||||||
public:
|
public:
|
||||||
ClientSecureSync();
|
ClientSecureSync();
|
||||||
|
~ClientSecureSync();
|
||||||
bool connect(IPAddress ip, uint16_t port) override;
|
bool connect(IPAddress ip, uint16_t port) override;
|
||||||
bool connect(const char * host, uint16_t port) override;
|
bool connect(const char * host, uint16_t port) override;
|
||||||
size_t write(const uint8_t * buf, size_t size) override;
|
size_t write(const uint8_t * buf, size_t size) override;
|
||||||
@@ -31,12 +28,9 @@ class ClientSecureSync : public Transport {
|
|||||||
void stop() override;
|
void stop() override;
|
||||||
bool connected() override;
|
bool connected() override;
|
||||||
bool disconnected() override;
|
bool disconnected() override;
|
||||||
// added for EMS-ESP
|
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
WiFiClient basic_client;
|
||||||
WiFiClientSecure client;
|
ESP_SSLClient client;
|
||||||
#else
|
|
||||||
WiFiClient client;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace espMqttClientInternals
|
} // namespace espMqttClientInternals
|
||||||
|
|||||||
@@ -8,50 +8,6 @@ the LICENSE file.
|
|||||||
|
|
||||||
#include "espMqttClient.h"
|
#include "espMqttClient.h"
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
|
||||||
espMqttClient::espMqttClient()
|
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
|
||||||
, _client() {
|
|
||||||
_transport = &_client;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure::espMqttClientSecure()
|
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
|
||||||
, _client() {
|
|
||||||
_transport = &_client;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
|
||||||
_client.client.setInsecure();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setFingerprint(const uint8_t fingerprint[20]) {
|
|
||||||
_client.client.setFingerprint(fingerprint);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setTrustAnchors(const X509List * ta) {
|
|
||||||
_client.client.setTrustAnchors(ta);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setClientRSACert(const X509List * cert, const PrivateKey * sk) {
|
|
||||||
_client.client.setClientRSACert(cert, sk);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setClientECCert(const X509List * cert, const PrivateKey * sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
|
|
||||||
_client.client.setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setCertStore(CertStoreBase * certStore) {
|
|
||||||
_client.client.setCertStore(certStore);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
|
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
|
||||||
: MqttClientSetup(useInternalTask)
|
: MqttClientSetup(useInternalTask)
|
||||||
@@ -78,51 +34,45 @@ espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core)
|
|||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setInsecure();
|
_client.client.setInsecure();
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) {
|
espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setCACert(rootCA);
|
_client.client.setCACert(rootCA);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) {
|
espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setCertificate(clientCa);
|
_client.client.setCertificate(clientCa);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) {
|
espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setPrivateKey(privateKey);
|
_client.client.setPrivateKey(privateKey);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) {
|
espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#ifndef NO_TLS_SUPPORT
|
||||||
_client.client.setPreSharedKey(pskIdent, psKey);
|
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
espMqttClient::espMqttClient()
|
espMqttClient::espMqttClient()
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
#elif defined(_WIN32) || defined(__APPLE__)
|
|
||||||
// Windows
|
|
||||||
espMqttClient::espMqttClient()
|
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) {
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ the LICENSE file.
|
|||||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||||
#include "Transport/ClientSync.h"
|
#include "Transport/ClientSync.h"
|
||||||
#include "Transport/ClientSecureSync.h"
|
#include "Transport/ClientSecureSync.h"
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__) || defined(__APPLE__)
|
||||||
#include "Transport/ClientPosix.h"
|
#include "Transport/ClientPosix.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -65,10 +65,16 @@ class espMqttClientSecure : public MqttClientSetup<espMqttClientSecure> {
|
|||||||
espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey);
|
espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
#ifndef NO_TLS_SUPPORT
|
||||||
espMqttClientInternals::ClientSecureSync _client;
|
espMqttClientInternals::ClientSecureSync _client;
|
||||||
|
#else
|
||||||
|
espMqttClientInternals::ClientSync _client;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
class espMqttClient : public MqttClientSetup<espMqttClient> {
|
class espMqttClient : public MqttClientSetup<espMqttClient> {
|
||||||
public:
|
public:
|
||||||
espMqttClient();
|
espMqttClient();
|
||||||
@@ -76,10 +82,4 @@ class espMqttClient : public MqttClientSetup<espMqttClient> {
|
|||||||
protected:
|
protected:
|
||||||
espMqttClientInternals::ClientPosix _client;
|
espMqttClientInternals::ClientPosix _client;
|
||||||
};
|
};
|
||||||
#elif defined(_WIN32) || defined(__APPLE__)
|
|
||||||
class espMqttClient : public MqttClientSetup<espMqttClient> {
|
|
||||||
public:
|
|
||||||
espMqttClient();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -22,11 +22,10 @@
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
#define EMS_TXMODE_OFF 0
|
#define EMS_TXMODE_OFF 0
|
||||||
#define EMS_TXMODE_EMS 1
|
#define EMS_TXMODE_DEFAULT 1
|
||||||
#define EMS_TXMODE_EMSPLUS 2
|
#define EMS_TXMODE_EMSPLUS 2
|
||||||
#define EMS_TXMODE_HT3 3
|
#define EMS_TXMODE_HT3 3
|
||||||
#define EMS_TXMODE_HW 4
|
#define EMS_TXMODE_HW 4
|
||||||
#define EMS_TXMODE_AUTO 5
|
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
|
|||||||
@@ -15,5 +15,5 @@
|
|||||||
"itty-router": "^5.0.23",
|
"itty-router": "^5.0.23",
|
||||||
"prettier": "^3.8.3"
|
"prettier": "^3.8.3"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820"
|
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ extra_configs =
|
|||||||
pio_local.ini
|
pio_local.ini
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
core_build_flags = -std=gnu++2a -O3 -flto=auto -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers
|
core_build_flags = -std=gnu++20 -O3 -flto=auto -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers
|
||||||
core_unbuild_flags = -std=gnu++11 -std=gnu++14 -std=gnu++17 -fno-lto
|
core_unbuild_flags = -std=gnu++11 -std=gnu++14 -std=gnu++17 -fno-lto
|
||||||
|
|
||||||
my_build_flags =
|
my_build_flags =
|
||||||
@@ -53,13 +53,21 @@ build_flags =
|
|||||||
unbuild_flags =
|
unbuild_flags =
|
||||||
${common.core_unbuild_flags}
|
${common.core_unbuild_flags}
|
||||||
|
|
||||||
|
; 4MB Flash variants
|
||||||
|
[espressif32_base_4M]
|
||||||
|
framework = arduino
|
||||||
|
board_build.partitions = partitions/esp32_partition_4M.csv
|
||||||
|
board_upload.flash_size = 4MB
|
||||||
|
board_build.app_partition_name = app0
|
||||||
|
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
|
||||||
|
|
||||||
; 16MB Flash variants
|
; 16MB Flash variants
|
||||||
[espressif32_base_16M]
|
[espressif32_base_16M]
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board_build.partitions = partitions/esp32_partition_16M.csv
|
board_build.partitions = partitions/esp32_partition_16M.csv
|
||||||
board_upload.flash_size = 16MB
|
board_upload.flash_size = 16MB
|
||||||
board_build.app_partition_name = app0
|
board_build.app_partition_name = app0
|
||||||
platform = espressif32@7.0.0 ; Arduino Core 2.0.17 / IDF 4.4.7
|
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
|
||||||
|
|
||||||
; 32MB Flash variants
|
; 32MB Flash variants
|
||||||
[espressif32_base_32M]
|
[espressif32_base_32M]
|
||||||
@@ -67,29 +75,7 @@ framework = arduino
|
|||||||
board_build.partitions = partitions/esp32_partition_32M.csv
|
board_build.partitions = partitions/esp32_partition_32M.csv
|
||||||
board_upload.flash_size = 32MB
|
board_upload.flash_size = 32MB
|
||||||
board_build.app_partition_name = app0
|
board_build.app_partition_name = app0
|
||||||
platform = espressif32@7.0.0 ; Arduino Core 2.0.17 / IDF 4.4.7
|
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
|
||||||
|
|
||||||
; use Tasmota's library for 4MB Flash variants.
|
|
||||||
; Removes libs (like mbedtsl, so no WiFi_secure.h) to increase available heap
|
|
||||||
[espressif32_base_T_4M]
|
|
||||||
framework = arduino
|
|
||||||
board_build.partitions = partitions/esp32_partition_4M.csv
|
|
||||||
board_upload.flash_size = 4MB
|
|
||||||
board_build.app_partition_name = app0
|
|
||||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.00/platform-espressif32.zip ; Arduino Core 2.0.18 with IPv6 support, based on IDF 4.4.8
|
|
||||||
; Tasmota Arduino Core 3.1.3.250302 based on IDF 5.3.2.250228
|
|
||||||
; platform = https://github.com/tasmota/platform-espressif32/releases/download/2025.03.30/platform-espressif32.zip
|
|
||||||
|
|
||||||
; use Tasmota's library for 16MB Flash variants.
|
|
||||||
; Removes libs (like mbedtsl, so no WiFi_secure.h) to increase available heap
|
|
||||||
[espressif32_base_T_16M]
|
|
||||||
framework = arduino
|
|
||||||
board_build.partitions = partitions/esp32_partition_16M.csv
|
|
||||||
board_upload.flash_size = 16MB
|
|
||||||
board_build.app_partition_name = app0
|
|
||||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.00/platform-espressif32.zip ; Arduino Core 2.0.18 with IPv6 support, based on IDF 4.4.8
|
|
||||||
; Tasmota Arduino Core 3.1.3.250302 based on IDF 5.3.2.250228
|
|
||||||
; platform = https://github.com/tasmota/platform-espressif32/releases/download/2025.03.30/platform-espressif32.zip
|
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
build_flags =
|
build_flags =
|
||||||
@@ -105,7 +91,9 @@ board_build.filesystem = littlefs
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson @ 7.4.3
|
bblanchon/ArduinoJson @ 7.4.3
|
||||||
ESP32Async/AsyncTCP @ 3.4.10
|
ESP32Async/AsyncTCP @ 3.4.10
|
||||||
ESP32Async/ESPAsyncWebServer @ 3.11.0
|
ESP32Async/ESPAsyncWebServer @ 3.10.3
|
||||||
|
https://github.com/mobizt/ReadyMail.git @ 0.3.8
|
||||||
|
https://github.com/mobizt/ESP_SSLClient.git @ 3.1.3
|
||||||
; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
||||||
|
|
||||||
; builds the web interface only, not the firmware
|
; builds the web interface only, not the firmware
|
||||||
@@ -118,18 +106,17 @@ build_src_filter = -<*>
|
|||||||
|
|
||||||
;
|
;
|
||||||
; Builds for different board types
|
; Builds for different board types
|
||||||
; We use Tasmota for boards without PSRAM as this framework has mbedtls removed to save memory.
|
|
||||||
; If you're building for a single target environment, we recommend creating a pio_local.ini (see example file)
|
; If you're building for a single target environment, we recommend creating a pio_local.ini (see example file)
|
||||||
;
|
;
|
||||||
|
|
||||||
[env:s_4M]
|
[env:s_4M]
|
||||||
; 4MB ESP32 - no SSL, no PSRAM - like a BBQKees older S32 and E32 models - uses Tasmota
|
; 4MB ESP32 - no SSL, no PSRAM - like a BBQKees older S32 and E32 models
|
||||||
extends = espressif32_base_T_4M
|
extends = espressif32_base_4M
|
||||||
board = s_4M
|
board = s_4M
|
||||||
|
|
||||||
[env:s_16M]
|
[env:s_16M]
|
||||||
; 16MB ESP32 - no PSRAM - like a BBQKees later S32 V2 models - uses Tasmota
|
; 16MB ESP32 - no PSRAM - like a BBQKees later S32 V2 models
|
||||||
extends = espressif32_base_T_16M
|
extends = espressif32_base_16M
|
||||||
board = s_16M
|
board = s_16M
|
||||||
|
|
||||||
[env:s_16M_P]
|
[env:s_16M_P]
|
||||||
@@ -148,24 +135,30 @@ extends = espressif32_base_32M
|
|||||||
board = s3_32M_P
|
board = s3_32M_P
|
||||||
|
|
||||||
[env:s2_4M_P]
|
[env:s2_4M_P]
|
||||||
; based on lolin_s2_mini 4MB with 2MB PSRAM - uses Tasmota
|
; based on lolin_s2_mini 4MB with 2MB PSRAM
|
||||||
extends = espressif32_base_T_4M
|
extends = espressif32_base_4M
|
||||||
board = s2_4M_P
|
board = s2_4M_P
|
||||||
|
|
||||||
[env:c3_mini_4M]
|
[env:c3_mini_4M]
|
||||||
; based on lolin_c3_mini 4MB, no PSRAM - uses Tasmota
|
; based on lolin_c3_mini 4MB, no PSRAM
|
||||||
extends = espressif32_base_T_4M
|
extends = espressif32_base_4M
|
||||||
board = c3_mini_4M
|
board = c3_mini_4M
|
||||||
|
|
||||||
; lolin C3 mini v1 needs special wifi initialization
|
; lolin C3 mini v1 needs special wifi initialization
|
||||||
; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||||
[env:c3_miniv1_4M]
|
[env:c3_miniv1_4M]
|
||||||
extends = espressif32_base_T_4M
|
extends = espressif32_base_4M
|
||||||
board = c3_mini_4M
|
board = c3_mini_4M
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
-DBOARD_C3_MINI_V1
|
-DBOARD_C3_MINI_V1
|
||||||
|
|
||||||
|
; XIAO ESP32C - 512KB SRAM & 4MB Flash - https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
|
||||||
|
[env:c6_xiao_4M]
|
||||||
|
extends = espressif32_base_4M
|
||||||
|
board_build.app_partition_name = app0
|
||||||
|
board = seeed_xiao_esp32c6
|
||||||
|
|
||||||
; foundation for building and testing natively, standalone without an ESP32
|
; foundation for building and testing natively, standalone without an ESP32
|
||||||
; use the `standalone` environment instead of `native` for testing
|
; use the `standalone` environment instead of `native` for testing
|
||||||
[env:native]
|
[env:native]
|
||||||
@@ -175,7 +168,8 @@ build_flags =
|
|||||||
build_src_flags =
|
build_src_flags =
|
||||||
-DEMSESP_STANDALONE -DEMSESP_TEST
|
-DEMSESP_STANDALONE -DEMSESP_TEST
|
||||||
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
|
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
|
||||||
-std=gnu++2a -Og -ggdb
|
-DNO_TLS_SUPPORT
|
||||||
|
-std=gnu++20 -Og -ggdb
|
||||||
-Wall -Wextra
|
-Wall -Wextra
|
||||||
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
|
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
|
||||||
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
|
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
|
||||||
@@ -184,6 +178,7 @@ build_src_flags =
|
|||||||
-I./lib/uuid-common/src
|
-I./lib/uuid-common/src
|
||||||
-I./lib/uuid-console/src
|
-I./lib/uuid-console/src
|
||||||
-I./lib/uuid-log/src
|
-I./lib/uuid-log/src
|
||||||
|
-I./lib/semver
|
||||||
-I./lib/PButton
|
-I./lib/PButton
|
||||||
-I./lib/espMqttClient/src
|
-I./lib/espMqttClient/src
|
||||||
-I./lib/espMqttClient/src/Transport
|
-I./lib/espMqttClient/src/Transport
|
||||||
@@ -213,8 +208,9 @@ build_src_flags =
|
|||||||
-DEMSESP_STANDALONE -DEMSESP_TEST
|
-DEMSESP_STANDALONE -DEMSESP_TEST
|
||||||
-DEMSESP_UNITY
|
-DEMSESP_UNITY
|
||||||
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
|
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
|
||||||
|
-DNO_TLS_SUPPORT
|
||||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||||
-std=gnu++2a -Og -ggdb
|
-std=gnu++20 -Og -ggdb
|
||||||
-Wall -Wextra
|
-Wall -Wextra
|
||||||
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
|
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
|
||||||
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
|
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
|
||||||
@@ -223,6 +219,7 @@ build_src_flags =
|
|||||||
-I./lib/uuid-common/src
|
-I./lib/uuid-common/src
|
||||||
-I./lib/uuid-console/src
|
-I./lib/uuid-console/src
|
||||||
-I./lib/uuid-log/src
|
-I./lib/uuid-log/src
|
||||||
|
-I./lib/semver
|
||||||
-I./lib/PButton
|
-I./lib/PButton
|
||||||
-I./lib/espMqttClient/src
|
-I./lib/espMqttClient/src
|
||||||
-I./lib/espMqttClient/src/Transport
|
-I./lib/espMqttClient/src/Transport
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ def buildWeb():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def build_webUI(*args, **kwargs):
|
def build-webUI(*args, **kwargs):
|
||||||
success = buildWeb()
|
success = buildWeb()
|
||||||
if not success:
|
if not success:
|
||||||
print("Web interface build failed!")
|
print("Web interface build failed!")
|
||||||
@@ -114,7 +114,7 @@ def build_webUI(*args, **kwargs):
|
|||||||
env.AddCustomTarget(
|
env.AddCustomTarget(
|
||||||
name="build",
|
name="build",
|
||||||
dependencies=None,
|
dependencies=None,
|
||||||
actions=[build_webUI],
|
actions=[build-webUI],
|
||||||
title="build web interface",
|
title="build web interface",
|
||||||
description="installs pnpm packages, updates libraries and builds web UI",
|
description="installs pnpm packages, updates libraries and builds web UI",
|
||||||
always_build=True
|
always_build=True
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityM
|
|||||||
, _reconfigureAp(false)
|
, _reconfigureAp(false)
|
||||||
, _connected(0) {
|
, _connected(0) {
|
||||||
addUpdateHandler([this] { reconfigureAP(); }, false);
|
addUpdateHandler([this] { reconfigureAP(); }, false);
|
||||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void APSettingsService::begin() {
|
void APSettingsService::begin() {
|
||||||
@@ -19,39 +18,27 @@ void APSettingsService::begin() {
|
|||||||
// reconfigureAP();
|
// reconfigureAP();
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait 10 sec on STA disconnect before starting AP
|
|
||||||
void APSettingsService::WiFiEvent(WiFiEvent_t event) {
|
|
||||||
const uint8_t was_connected = _connected;
|
|
||||||
switch (event) {
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
|
||||||
_connected &= ~1U;
|
|
||||||
break;
|
|
||||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
|
||||||
_connected &= ~2U;
|
|
||||||
break;
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
|
||||||
_connected |= 1U;
|
|
||||||
break;
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP6:
|
|
||||||
_connected |= 2U;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// wait 10 sec before starting AP
|
|
||||||
if (was_connected && !_connected) {
|
|
||||||
_lastManaged = uuid::get_uptime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void APSettingsService::reconfigureAP() {
|
void APSettingsService::reconfigureAP() {
|
||||||
_lastManaged = uuid::get_uptime() - MANAGE_NETWORK_DELAY;
|
_lastManaged = uuid::get_uptime() - MANAGE_NETWORK_DELAY;
|
||||||
_reconfigureAp = true;
|
_reconfigureAp = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APSettingsService::loop() {
|
void APSettingsService::loop() {
|
||||||
|
const uint8_t was_connected = _connected;
|
||||||
|
if (WiFi.isConnected()) {
|
||||||
|
_connected |= 1U;
|
||||||
|
} else {
|
||||||
|
_connected &= ~1U;
|
||||||
|
}
|
||||||
|
if (ETH.connected()) {
|
||||||
|
_connected |= 2U;
|
||||||
|
} else {
|
||||||
|
_connected &= ~2U;
|
||||||
|
}
|
||||||
|
// wait 10 sec before starting AP
|
||||||
|
if (was_connected && !_connected) {
|
||||||
|
_lastManaged = uuid::get_uptime();
|
||||||
|
}
|
||||||
const unsigned long currentMillis = uuid::get_uptime();
|
const unsigned long currentMillis = uuid::get_uptime();
|
||||||
if ((currentMillis - _lastManaged) >= MANAGE_NETWORK_DELAY) {
|
if ((currentMillis - _lastManaged) >= MANAGE_NETWORK_DELAY) {
|
||||||
_lastManaged = currentMillis;
|
_lastManaged = currentMillis;
|
||||||
@@ -76,11 +63,7 @@ void APSettingsService::manageAP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APSettingsService::startAP() {
|
void APSettingsService::startAP() {
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
WiFi.softAPenableIpV6(); // force IPV6, same as for WiFi - fixes https://github.com/emsesp/EMS-ESP32/issues/1922
|
|
||||||
#else
|
|
||||||
WiFi.softAPenableIPv6(); // force IPV6, same as for WiFi - fixes https://github.com/emsesp/EMS-ESP32/issues/1922
|
WiFi.softAPenableIPv6(); // force IPV6, same as for WiFi - fixes https://github.com/emsesp/EMS-ESP32/issues/1922
|
||||||
#endif
|
|
||||||
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
|
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
|
||||||
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients);
|
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients);
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ class APSettingsService : public StatefulService<APSettings> {
|
|||||||
void startAP();
|
void startAP();
|
||||||
void stopAP();
|
void stopAP();
|
||||||
void handleDNS();
|
void handleDNS();
|
||||||
void WiFiEvent(WiFiEvent_t event);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -70,12 +70,16 @@ void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument & jsonDocument) {
|
|||||||
*/
|
*/
|
||||||
String ArduinoJsonJWT::sign(String & payload) {
|
String ArduinoJsonJWT::sign(String & payload) {
|
||||||
std::array<unsigned char, 32> hmacResult{};
|
std::array<unsigned char, 32> hmacResult{};
|
||||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
{
|
||||||
reinterpret_cast<const unsigned char *>(_secret.c_str()),
|
mbedtls_md_context_t ctx;
|
||||||
_secret.length(),
|
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||||
reinterpret_cast<const unsigned char *>(payload.c_str()),
|
mbedtls_md_init(&ctx);
|
||||||
payload.length(),
|
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
|
||||||
hmacResult.data());
|
mbedtls_md_hmac_starts(&ctx, reinterpret_cast<const unsigned char *>(_secret.c_str()), _secret.length());
|
||||||
|
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char *>(payload.c_str()), payload.length());
|
||||||
|
mbedtls_md_hmac_finish(&ctx, hmacResult.data());
|
||||||
|
mbedtls_md_free(&ctx);
|
||||||
|
}
|
||||||
return encode(reinterpret_cast<const char *>(hmacResult.data()), hmacResult.size());
|
return encode(reinterpret_cast<const char *>(hmacResult.data()), hmacResult.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
static constexpr const char CACHE_CONTROL[] = "public,max-age=60";
|
static constexpr const char CACHE_CONTROL[] = "public,max-age=60";
|
||||||
|
|
||||||
// Single static-content handler serving all assets embedded in WWWData.h.
|
// Single static-content handler serving all assets embedded in WWWData.h
|
||||||
class StaticContentHandler : public AsyncWebHandler {
|
class StaticContentHandler : public AsyncWebHandler {
|
||||||
public:
|
public:
|
||||||
bool canHandle(AsyncWebServerRequest * request) const override {
|
bool canHandle(AsyncWebServerRequest * request) const override {
|
||||||
@@ -15,8 +15,7 @@ class StaticContentHandler : public AsyncWebHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleRequest(AsyncWebServerRequest * request) override {
|
void handleRequest(AsyncWebServerRequest * request) override {
|
||||||
// OPTIONS is handled generically - the server-level CORS headers are
|
// OPTIONS is handled generically - the server-level CORS headers are attached via DefaultHeaders in ESP32React::begin().
|
||||||
// attached via DefaultHeaders in ESP32React::begin().
|
|
||||||
if (request->method() == HTTP_OPTIONS) {
|
if (request->method() == HTTP_OPTIONS) {
|
||||||
request->send(200);
|
request->send(200);
|
||||||
return;
|
return;
|
||||||
@@ -57,8 +56,7 @@ class StaticContentHandler : public AsyncWebHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the /index.html asset, used as the SPA fallback for any GET
|
// Returns the /index.html asset, used as the SPA fallback for any GET
|
||||||
// that didn't match an embedded asset (React Router handles routing on
|
// that didn't match an embedded asset (React Router handles routing on the client side).
|
||||||
// the client side).
|
|
||||||
static const WWWAsset * index_asset() {
|
static const WWWAsset * index_asset() {
|
||||||
static const WWWAsset * cached = nullptr;
|
static const WWWAsset * cached = nullptr;
|
||||||
if (cached == nullptr) {
|
if (cached == nullptr) {
|
||||||
@@ -106,4 +104,5 @@ void ESP32React::loop() {
|
|||||||
_networkSettingsService.loop();
|
_networkSettingsService.loop();
|
||||||
_apSettingsService.loop();
|
_apSettingsService.loop();
|
||||||
_mqttSettingsService.loop();
|
_mqttSettingsService.loop();
|
||||||
|
_ntpSettingsService.loop();
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,6 @@ MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, Secur
|
|||||||
, _disconnectedAt(0)
|
, _disconnectedAt(0)
|
||||||
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
|
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
|
||||||
, _mqttClient(nullptr) {
|
, _mqttClient(nullptr) {
|
||||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
|
||||||
addUpdateHandler([this] { onConfigUpdated(); }, false);
|
addUpdateHandler([this] { onConfigUpdated(); }, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +28,7 @@ MqttSettingsService::~MqttSettingsService() {
|
|||||||
void MqttSettingsService::begin() {
|
void MqttSettingsService::begin() {
|
||||||
_fsPersistence.readFromFS();
|
_fsPersistence.readFromFS();
|
||||||
startClient();
|
startClient();
|
||||||
|
_reconfigureMqtt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::startClient() {
|
void MqttSettingsService::startClient() {
|
||||||
@@ -41,7 +41,7 @@ void MqttSettingsService::startClient() {
|
|||||||
delete _mqttClient;
|
delete _mqttClient;
|
||||||
_mqttClient = nullptr;
|
_mqttClient = nullptr;
|
||||||
}
|
}
|
||||||
#ifndef TASMOTA_SDK
|
#ifndef NO_TLS_SUPPORT
|
||||||
if (_state.enableTLS) {
|
if (_state.enableTLS) {
|
||||||
isSecure = true;
|
isSecure = true;
|
||||||
if (emsesp::EMSESP::system_.PSram() == 0) {
|
if (emsesp::EMSESP::system_.PSram() == 0) {
|
||||||
@@ -79,6 +79,10 @@ void MqttSettingsService::startClient() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::loop() {
|
void MqttSettingsService::loop() {
|
||||||
|
if (_state.enabled && _mqttClient && _mqttClient->connected() && !emsesp::EMSESP::system_.network_connected()) {
|
||||||
|
// emsesp::EMSESP::logger().info("Network connection dropped, stopping MQTT client");
|
||||||
|
_mqttClient->disconnect(true);
|
||||||
|
}
|
||||||
if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
|
if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
|
||||||
// reconfigure MQTT client
|
// reconfigure MQTT client
|
||||||
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
||||||
@@ -142,28 +146,6 @@ void MqttSettingsService::onConfigUpdated() {
|
|||||||
emsesp::EMSESP::mqtt_.start(); // reload EMS-ESP MQTT settings
|
emsesp::EMSESP::mqtt_.start(); // reload EMS-ESP MQTT settings
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::WiFiEvent(WiFiEvent_t event) {
|
|
||||||
switch (event) {
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP6:
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
|
||||||
if (_state.enabled && !_mqttClient->connected()) {
|
|
||||||
onConfigUpdated();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
|
||||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
|
||||||
if (_state.enabled) {
|
|
||||||
_mqttClient->disconnect(true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MqttSettingsService::configureMqtt() {
|
bool MqttSettingsService::configureMqtt() {
|
||||||
// disconnect if already connected
|
// disconnect if already connected
|
||||||
if (_mqttClient->connected()) {
|
if (_mqttClient->connected()) {
|
||||||
@@ -182,17 +164,13 @@ bool MqttSettingsService::configureMqtt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_reconfigureMqtt = false;
|
_reconfigureMqtt = false;
|
||||||
#ifndef TASMOTA_SDK
|
#ifndef NO_TLS_SUPPORT
|
||||||
if (_state.enableTLS) {
|
if (_state.enableTLS) {
|
||||||
if (_state.rootCA == "insecure") {
|
if (_state.rootCA == "insecure") {
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
emsesp::EMSESP::logger().debug("Start insecure MQTT");
|
emsesp::EMSESP::logger().debug("Start insecure MQTT");
|
||||||
#endif
|
|
||||||
static_cast<espMqttClientSecure *>(_mqttClient)->setInsecure();
|
static_cast<espMqttClientSecure *>(_mqttClient)->setInsecure();
|
||||||
} else {
|
} else {
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
emsesp::EMSESP::logger().debug("Start secure MQTT with rootCA");
|
emsesp::EMSESP::logger().debug("Start secure MQTT with rootCA");
|
||||||
#endif
|
|
||||||
String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n";
|
String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n";
|
||||||
static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(certificate.c_str());
|
static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(certificate.c_str());
|
||||||
}
|
}
|
||||||
@@ -223,7 +201,7 @@ bool MqttSettingsService::configureMqtt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettings::read(MqttSettings & settings, JsonObject root) {
|
void MqttSettings::read(MqttSettings & settings, JsonObject root) {
|
||||||
#ifndef TASMOTA_SDK
|
#ifndef NO_TLS_SUPPORT
|
||||||
root["enableTLS"] = settings.enableTLS;
|
root["enableTLS"] = settings.enableTLS;
|
||||||
root["rootCA"] = settings.rootCA;
|
root["rootCA"] = settings.rootCA;
|
||||||
#endif
|
#endif
|
||||||
@@ -262,7 +240,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
|||||||
MqttSettings newSettings;
|
MqttSettings newSettings;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
#ifndef TASMOTA_SDK
|
#ifndef NO_TLS_SUPPORT
|
||||||
newSettings.enableTLS = root["enableTLS"];
|
newSettings.enableTLS = root["enableTLS"];
|
||||||
newSettings.rootCA = root["rootCA"] | "";
|
newSettings.rootCA = root["rootCA"] | "";
|
||||||
#else
|
#else
|
||||||
@@ -393,7 +371,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
|||||||
emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat);
|
emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TASMOTA_SDK
|
#ifndef NO_TLS_SUPPORT
|
||||||
// strip down to certificate only
|
// strip down to certificate only
|
||||||
newSettings.rootCA.replace("\r", "");
|
newSettings.rootCA.replace("\r", "");
|
||||||
newSettings.rootCA.replace("\n", "");
|
newSettings.rootCA.replace("\n", "");
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
|
|||||||
// the MQTT client instance
|
// the MQTT client instance
|
||||||
MqttClient * _mqttClient;
|
MqttClient * _mqttClient;
|
||||||
|
|
||||||
void WiFiEvent(WiFiEvent_t event);
|
|
||||||
void onMqttConnect(bool sessionPresent);
|
void onMqttConnect(bool sessionPresent);
|
||||||
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
|
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -11,37 +11,18 @@ NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, Securit
|
|||||||
configureTime(request, json);
|
configureTime(request, json);
|
||||||
});
|
});
|
||||||
|
|
||||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
|
||||||
addUpdateHandler([this] { configureNTP(); }, false);
|
addUpdateHandler([this] { configureNTP(); }, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTPSettingsService::begin() {
|
void NTPSettingsService::begin() {
|
||||||
_fsPersistence.readFromFS();
|
_fsPersistence.readFromFS();
|
||||||
configureNTP();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles both WiFI and Ethernet
|
void NTPSettingsService::loop() {
|
||||||
void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
if (_connected != emsesp::EMSESP::system_.network_connected()) {
|
||||||
switch (event) {
|
_connected = emsesp::EMSESP::system_.network_connected();
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
|
||||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
|
||||||
if (_connected && emsesp::EMSESP::system_.ntp_connected()) {
|
|
||||||
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
|
|
||||||
_connected = false;
|
|
||||||
configureNTP();
|
configureNTP();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
|
||||||
// emsesp::EMSESP::logger().info("Got IP address, starting NTP synchronization");
|
|
||||||
_connected = true;
|
|
||||||
configureNTP();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
|
// https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
|
||||||
@@ -55,9 +36,11 @@ void NTPSettingsService::configureNTP() {
|
|||||||
} else {
|
} else {
|
||||||
setenv("TZ", _state.tzFormat.c_str(), 1);
|
setenv("TZ", _state.tzFormat.c_str(), 1);
|
||||||
tzset();
|
tzset();
|
||||||
|
if (esp_sntp_enabled()) {
|
||||||
esp_sntp_stop();
|
esp_sntp_stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant json) {
|
void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant json) {
|
||||||
if (json.is<JsonObject>()) {
|
if (json.is<JsonObject>()) {
|
||||||
@@ -68,7 +51,12 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari
|
|||||||
tm.tm_isdst = -1; // not set by strptime, tells mktime to determine daylightsaving
|
tm.tm_isdst = -1; // not set by strptime, tells mktime to determine daylightsaving
|
||||||
time_t time = mktime(&tm);
|
time_t time = mktime(&tm);
|
||||||
struct timeval now = {.tv_sec = time, .tv_usec = {}};
|
struct timeval now = {.tv_sec = time, .tv_usec = {}};
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
// settimeofday and adjtime() does not work, unknown how to set time
|
||||||
|
emsesp::EMSESP::logger().warning("manual clock setting not possible");
|
||||||
|
#else
|
||||||
settimeofday(&now, nullptr);
|
settimeofday(&now, nullptr);
|
||||||
|
#endif
|
||||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
|
|||||||
NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||||
|
|
||||||
void begin();
|
void begin();
|
||||||
|
void loop();
|
||||||
static void ntp_received(struct timeval * tv);
|
static void ntp_received(struct timeval * tv);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -51,7 +52,6 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
|
|||||||
FSPersistence<NTPSettings> _fsPersistence;
|
FSPersistence<NTPSettings> _fsPersistence;
|
||||||
volatile bool _connected;
|
volatile bool _connected;
|
||||||
|
|
||||||
void WiFiEvent(WiFiEvent_t event);
|
|
||||||
void configureNTP();
|
void configureNTP();
|
||||||
void configureTime(AsyncWebServerRequest * request, JsonVariant json);
|
void configureTime(AsyncWebServerRequest * request, JsonVariant json);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs,
|
|||||||
, _stopping(false) {
|
, _stopping(false) {
|
||||||
addUpdateHandler([this] { reconfigureWiFiConnection(); }, false);
|
addUpdateHandler([this] { reconfigureWiFiConnection(); }, false);
|
||||||
// Eth is also bound to the WifiGeneric event handler
|
// Eth is also bound to the WifiGeneric event handler
|
||||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event, info); });
|
// Network.onEvent([this](arduino_event_id_t event, arduino_event_info_t info) { WiFiEvent(event, info); });
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool formatBssid(const String & bssid, uint8_t (&mac)[6]) {
|
static bool formatBssid(const String & bssid, uint8_t (&mac)[6]) {
|
||||||
@@ -34,8 +34,8 @@ void NetworkSettingsService::begin() {
|
|||||||
WiFi.persistent(false);
|
WiFi.persistent(false);
|
||||||
WiFi.setAutoReconnect(false);
|
WiFi.setAutoReconnect(false);
|
||||||
|
|
||||||
WiFi.mode(WIFI_MODE_MAX);
|
WiFi.mode(WIFI_STA);
|
||||||
WiFi.mode(WIFI_MODE_NULL);
|
WiFi.mode(WIFI_OFF);
|
||||||
|
|
||||||
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
|
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
|
||||||
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
||||||
@@ -62,28 +62,124 @@ void NetworkSettingsService::loop() {
|
|||||||
_lastConnectionAttempt = currentMillis;
|
_lastConnectionAttempt = currentMillis;
|
||||||
manageSTA();
|
manageSTA();
|
||||||
}
|
}
|
||||||
|
static uint8_t connect = 0;
|
||||||
|
enum uint8_t {
|
||||||
|
CONNECT_IDLE = 0,
|
||||||
|
CONNECT_WAIT_ETH,
|
||||||
|
CONNECT_WAIT_IP4,
|
||||||
|
CONNECT_WAIT_ETH_IP4,
|
||||||
|
CONNECT_WAIT_IP6,
|
||||||
|
CONNECT_WAIT_ETH_IP6,
|
||||||
|
CONNECT_ETH_ACTIVE,
|
||||||
|
CONNECT_WIFI_ACTIVE
|
||||||
|
};
|
||||||
|
switch (connect) {
|
||||||
|
default:
|
||||||
|
connect = CONNECT_IDLE;
|
||||||
|
break;
|
||||||
|
case CONNECT_IDLE:
|
||||||
|
if (ETH.started() && _state.ssid.length() == 0) {
|
||||||
|
emsesp::EMSESP::logger().info("ETH started");
|
||||||
|
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
|
||||||
|
ETH.enableIPv6(true);
|
||||||
|
if (_state.staticIPConfig) {
|
||||||
|
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
|
||||||
|
}
|
||||||
|
connect = CONNECT_WAIT_ETH;
|
||||||
|
}
|
||||||
|
if (WiFi.isConnected()) {
|
||||||
|
emsesp::EMSESP::logger().info("Wifi connected");
|
||||||
|
if (_state.tx_power == 0) {
|
||||||
|
setWiFiPowerOnRSSI();
|
||||||
|
}
|
||||||
|
mDNS_start();
|
||||||
|
emsesp::EMSESP::system_.has_ipv6(true);
|
||||||
|
connect = CONNECT_WAIT_IP4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONNECT_WAIT_ETH:
|
||||||
|
if (ETH.connected()) {
|
||||||
|
emsesp::EMSESP::logger().info("ETH connected");
|
||||||
|
emsesp::EMSESP::system_.ethernet_connected(true);
|
||||||
|
mDNS_start();
|
||||||
|
emsesp::EMSESP::system_.has_ipv6(true);
|
||||||
|
connect = CONNECT_WAIT_ETH_IP4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONNECT_WAIT_ETH_IP4:
|
||||||
|
if (ETH.hasIP()) {
|
||||||
|
emsesp::EMSESP::logger().info("ETH IPv4: %s", ETH.localIP().toString().c_str());
|
||||||
|
connect = CONNECT_WAIT_ETH_IP6;
|
||||||
|
}
|
||||||
|
if (!ETH.connected()) {
|
||||||
|
connect = CONNECT_ETH_ACTIVE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONNECT_WAIT_ETH_IP6:
|
||||||
|
if (ETH.hasLinkLocalIPv6() && ETH.hasGlobalIPv6()) {
|
||||||
|
emsesp::EMSESP::system_.has_ipv6(true);
|
||||||
|
connect = CONNECT_ETH_ACTIVE;
|
||||||
|
}
|
||||||
|
if (!ETH.connected()) {
|
||||||
|
connect = CONNECT_ETH_ACTIVE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONNECT_ETH_ACTIVE:
|
||||||
|
if (!ETH.connected()) {
|
||||||
|
emsesp::EMSESP::logger().info("ETH disconnected");
|
||||||
|
emsesp::EMSESP::system_.ethernet_connected(false);
|
||||||
|
emsesp::EMSESP::system_.has_ipv6(false);
|
||||||
|
connect = CONNECT_IDLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONNECT_WAIT_IP4:
|
||||||
|
if (!WiFi.localIP().toString().isEmpty()) {
|
||||||
|
emsesp::EMSESP::logger().info("Wifi IPv4: %s", WiFi.localIP().toString().c_str());
|
||||||
|
connect = CONNECT_WAIT_IP6;
|
||||||
|
}
|
||||||
|
if (!WiFi.isConnected()) {
|
||||||
|
connect = CONNECT_ETH_ACTIVE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONNECT_WAIT_IP6:
|
||||||
|
if (WiFi.linkLocalIPv6().toString() != "::" && WiFi.globalIPv6().toString() != "::") {
|
||||||
|
emsesp::EMSESP::logger().info("Wifi IPv6: %s, %s", WiFi.linkLocalIPv6().toString().c_str(), WiFi.globalIPv6().toString().c_str());
|
||||||
|
emsesp::EMSESP::system_.has_ipv6(true);
|
||||||
|
connect = CONNECT_WIFI_ACTIVE;
|
||||||
|
}
|
||||||
|
if (!WiFi.isConnected()) {
|
||||||
|
connect = CONNECT_WIFI_ACTIVE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONNECT_WIFI_ACTIVE:
|
||||||
|
if (!WiFi.isConnected()) {
|
||||||
|
emsesp::EMSESP::logger().info("WiFi disconnected");
|
||||||
|
if (_stopping) {
|
||||||
|
_lastConnectionAttempt = 0;
|
||||||
|
_stopping = false;
|
||||||
|
}
|
||||||
|
emsesp::EMSESP::system_.has_ipv6(false);
|
||||||
|
connect = CONNECT_IDLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSettingsService::manageSTA() {
|
void NetworkSettingsService::manageSTA() {
|
||||||
// Abort if already connected, or if we have no SSID
|
// Abort if already connected, or if we have no SSID
|
||||||
if (WiFi.isConnected() || _state.ssid.length() == 0) {
|
if (WiFi.isConnected() || _state.ssid.length() == 0) {
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
if (_state.ssid.length() == 0) {
|
|
||||||
ETH.enableIPv6(true);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect or reconnect as required
|
// Connect or reconnect as required
|
||||||
if ((WiFi.getMode() & WIFI_STA) == 0) {
|
if ((WiFi.getMode() & WIFI_STA) == 0) {
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
WiFi.setHostname(_state.hostname.c_str()); // updates shared default_hostname buffer
|
||||||
|
WiFi.enableSTA(true); // creates the STA netif
|
||||||
|
WiFi.STA.setHostname(_state.hostname.c_str()); // pushes to esp_netif_set_hostname
|
||||||
WiFi.enableIPv6(true);
|
WiFi.enableIPv6(true);
|
||||||
#endif
|
|
||||||
if (_state.staticIPConfig) {
|
if (_state.staticIPConfig) {
|
||||||
WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); // configure for static IP
|
WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); // configure for static IP
|
||||||
}
|
}
|
||||||
WiFi.setHostname(_state.hostname.c_str()); // set hostname
|
|
||||||
|
|
||||||
// www.esp32.com/viewtopic.php?t=12055
|
// www.esp32.com/viewtopic.php?t=12055
|
||||||
if (_state.bandwidth20) {
|
if (_state.bandwidth20) {
|
||||||
@@ -293,7 +389,7 @@ const char * NetworkSettingsService::disconnectReason(uint8_t code) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handles both WiFI and Ethernet
|
// handles both WiFI and Ethernet
|
||||||
void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
void NetworkSettingsService::WiFiEvent(arduino_event_id_t event, arduino_event_info_t info) {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
@@ -305,7 +401,7 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||||
connectcount_++; // count the number of WiFi reconnects
|
connectcount_ = connectcount_ + 1; // count the number of WiFi reconnects
|
||||||
emsesp::EMSESP::logger().warning("WiFi disconnected (#%d). Reason: %s (%d)",
|
emsesp::EMSESP::logger().warning("WiFi disconnected (#%d). Reason: %s (%d)",
|
||||||
connectcount_,
|
connectcount_,
|
||||||
disconnectReason(info.wifi_sta_disconnected.reason),
|
disconnectReason(info.wifi_sta_disconnected.reason),
|
||||||
@@ -325,11 +421,11 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ARDUINO_EVENT_ETH_START:
|
case ARDUINO_EVENT_ETH_START:
|
||||||
// configure for static IP
|
// apply hostname FIRST so DHCP DISCOVER carries the correct name
|
||||||
|
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
|
||||||
if (_state.staticIPConfig) {
|
if (_state.staticIPConfig) {
|
||||||
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
|
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
|
||||||
}
|
}
|
||||||
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||||
@@ -360,25 +456,15 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
|||||||
if (_state.tx_power == 0) {
|
if (_state.tx_power == 0) {
|
||||||
setWiFiPowerOnRSSI();
|
setWiFiPowerOnRSSI();
|
||||||
}
|
}
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
WiFi.enableIpV6(); // force ipv6
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARDUINO_EVENT_ETH_CONNECTED:
|
case ARDUINO_EVENT_ETH_CONNECTED:
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
ETH.enableIpV6(); // force ipv6
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// IPv6 specific - WiFi/Eth
|
// IPv6 specific - WiFi/Eth
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP6: {
|
case ARDUINO_EVENT_ETH_GOT_IP6: {
|
||||||
#if !TASMOTA_SDK && ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
auto ip6 = IPv6Address((uint8_t *)info.got_ip6.ip6_info.ip.addr).toString();
|
|
||||||
#else
|
|
||||||
auto ip6 = IPAddress(IPv6, (uint8_t *)info.got_ip6.ip6_info.ip.addr, 0).toString();
|
auto ip6 = IPAddress(IPv6, (uint8_t *)info.got_ip6.ip6_info.ip.addr, 0).toString();
|
||||||
#endif
|
|
||||||
const char * link = event == ARDUINO_EVENT_ETH_GOT_IP6 ? "Eth" : "WiFi";
|
const char * link = event == ARDUINO_EVENT_ETH_GOT_IP6 ? "Eth" : "WiFi";
|
||||||
if (ip6.startsWith("fe80")) {
|
if (ip6.startsWith("fe80")) {
|
||||||
emsesp::EMSESP::logger().info("IPv6 (%s) local: %s", link, ip6.c_str());
|
emsesp::EMSESP::logger().info("IPv6 (%s) local: %s", link, ip6.c_str());
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class NetworkSettingsService : public StatefulService<NetworkSettings> {
|
|||||||
|
|
||||||
volatile uint16_t connectcount_ = 0; // number of wifi reconnects
|
volatile uint16_t connectcount_ = 0; // number of wifi reconnects
|
||||||
|
|
||||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
void WiFiEvent(arduino_event_id_t event, arduino_event_info_t info);
|
||||||
void mDNS_start() const;
|
void mDNS_start() const;
|
||||||
const char * disconnectReason(uint8_t code);
|
const char * disconnectReason(uint8_t code);
|
||||||
void reconfigureWiFiConnection();
|
void reconfigureWiFiConnection();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <emsesp.h>
|
#include <emsesp.h>
|
||||||
|
|
||||||
#ifdef TASMOTA_SDK
|
#ifdef NO_TLS_SUPPORT
|
||||||
#include "lwip/dns.h"
|
#include "lwip/dns.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -32,21 +32,12 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
|||||||
if (ethernet_connected) {
|
if (ethernet_connected) {
|
||||||
// Ethernet
|
// Ethernet
|
||||||
root["local_ip"] = ETH.localIP().toString();
|
root["local_ip"] = ETH.localIP().toString();
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
root["local_ipv6"] = ETH.localIPv6().toString();
|
|
||||||
#else
|
|
||||||
root["local_ipv6"] = ETH.linkLocalIPv6().toString();
|
root["local_ipv6"] = ETH.linkLocalIPv6().toString();
|
||||||
#endif
|
|
||||||
root["mac_address"] = ETH.macAddress();
|
root["mac_address"] = ETH.macAddress();
|
||||||
root["subnet_mask"] = ETH.subnetMask().toString();
|
root["subnet_mask"] = ETH.subnetMask().toString();
|
||||||
root["gateway_ip"] = ETH.gatewayIP().toString();
|
root["gateway_ip"] = ETH.gatewayIP().toString();
|
||||||
#ifdef TASMOTA_SDK
|
|
||||||
IPAddress dnsIP1 = IPAddress(dns_getserver(0));
|
|
||||||
IPAddress dnsIP2 = IPAddress(dns_getserver(1));
|
|
||||||
#else
|
|
||||||
IPAddress dnsIP1 = ETH.dnsIP(0);
|
IPAddress dnsIP1 = ETH.dnsIP(0);
|
||||||
IPAddress dnsIP2 = ETH.dnsIP(1);
|
IPAddress dnsIP2 = ETH.dnsIP(1);
|
||||||
#endif
|
|
||||||
if (IPUtils::isSet(dnsIP1)) {
|
if (IPUtils::isSet(dnsIP1)) {
|
||||||
root["dns_ip_1"] = dnsIP1.toString();
|
root["dns_ip_1"] = dnsIP1.toString();
|
||||||
}
|
}
|
||||||
@@ -55,11 +46,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
|||||||
}
|
}
|
||||||
} else if (wifi_status == WL_CONNECTED) {
|
} else if (wifi_status == WL_CONNECTED) {
|
||||||
root["local_ip"] = WiFi.localIP().toString();
|
root["local_ip"] = WiFi.localIP().toString();
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
root["local_ipv6"] = WiFi.localIPv6().toString();
|
|
||||||
#else
|
|
||||||
root["local_ipv6"] = WiFi.linkLocalIPv6().toString();
|
root["local_ipv6"] = WiFi.linkLocalIPv6().toString();
|
||||||
#endif
|
|
||||||
root["mac_address"] = WiFi.macAddress();
|
root["mac_address"] = WiFi.macAddress();
|
||||||
root["rssi"] = WiFi.RSSI();
|
root["rssi"] = WiFi.RSSI();
|
||||||
root["ssid"] = WiFi.SSID();
|
root["ssid"] = WiFi.SSID();
|
||||||
@@ -71,14 +58,8 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
|||||||
if (WiFi.gatewayIP() != INADDR_NONE) {
|
if (WiFi.gatewayIP() != INADDR_NONE) {
|
||||||
root["gateway_ip"] = WiFi.gatewayIP().toString();
|
root["gateway_ip"] = WiFi.gatewayIP().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TASMOTA_SDK
|
|
||||||
IPAddress dnsIP1 = IPAddress(dns_getserver(0));
|
|
||||||
IPAddress dnsIP2 = IPAddress(dns_getserver(1));
|
|
||||||
#else
|
|
||||||
IPAddress dnsIP1 = WiFi.dnsIP(0);
|
IPAddress dnsIP1 = WiFi.dnsIP(0);
|
||||||
IPAddress dnsIP2 = WiFi.dnsIP(1);
|
IPAddress dnsIP2 = WiFi.dnsIP(1);
|
||||||
#endif
|
|
||||||
if (dnsIP1 != INADDR_NONE) {
|
if (dnsIP1 != INADDR_NONE) {
|
||||||
root["dns_ip_1"] = dnsIP1.toString();
|
root["dns_ip_1"] = dnsIP1.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,9 +69,7 @@ class EMSESP_Version {
|
|||||||
return a.major_ < b.major_;
|
return a.major_ < b.major_;
|
||||||
if (a.minor_ != b.minor_)
|
if (a.minor_ != b.minor_)
|
||||||
return a.minor_ < b.minor_;
|
return a.minor_ < b.minor_;
|
||||||
if (a.patch_ != b.patch_)
|
|
||||||
return a.patch_ < b.patch_;
|
return a.patch_ < b.patch_;
|
||||||
return a.prerelease_ < b.prerelease_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator>(const EMSESP_Version & a, const EMSESP_Version & b) {
|
friend bool operator>(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
@@ -79,7 +77,7 @@ class EMSESP_Version {
|
|||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator==(const EMSESP_Version & a, const EMSESP_Version & b) {
|
friend bool operator==(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
return a.major_ == b.major_ && a.minor_ == b.minor_ && a.patch_ == b.patch_ && a.prerelease_ == b.prerelease_;
|
return a.major_ == b.major_ && a.minor_ == b.minor_ && a.patch_ == b.patch_;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator!=(const EMSESP_Version & a, const EMSESP_Version & b) {
|
friend bool operator!=(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
|
|||||||
@@ -347,23 +347,13 @@ void AnalogSensor::reload(bool get_nvs) {
|
|||||||
sensor.polltime_ = sensor.value() != 0 ? uuid::get_uptime() + (sensor.factor() * 1000) : 0;
|
sensor.polltime_ = sensor.value() != 0 ? uuid::get_uptime() + (sensor.factor() * 1000) : 0;
|
||||||
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
|
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
|
||||||
LOG_DEBUG("PWM output on GPIO %02d", sensor.gpio());
|
LOG_DEBUG("PWM output on GPIO %02d", sensor.gpio());
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
ledcAttach(sensor.gpio(), sensor.factor(), 13);
|
ledcAttach(sensor.gpio(), sensor.factor(), 13);
|
||||||
#else
|
|
||||||
uint8_t channel = sensor.type() - AnalogType::PWM_0;
|
|
||||||
ledcSetup(channel, sensor.factor(), 13);
|
|
||||||
ledcAttachPin(sensor.gpio(), channel);
|
|
||||||
#endif
|
|
||||||
if (sensor.offset() > 100) {
|
if (sensor.offset() > 100) {
|
||||||
sensor.set_offset(100);
|
sensor.set_offset(100);
|
||||||
} else if (sensor.offset() < 0) {
|
} else if (sensor.offset() < 0) {
|
||||||
sensor.set_offset(0);
|
sensor.set_offset(0);
|
||||||
}
|
}
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
|
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
|
||||||
#else
|
|
||||||
ledcWrite(channel, (uint32_t)(sensor.offset() * 8191 / 100));
|
|
||||||
#endif
|
|
||||||
sensor.set_value(sensor.offset());
|
sensor.set_value(sensor.offset());
|
||||||
sensor.set_uom(DeviceValueUOM::PERCENT);
|
sensor.set_uom(DeviceValueUOM::PERCENT);
|
||||||
publish_sensor(sensor);
|
publish_sensor(sensor);
|
||||||
@@ -1041,12 +1031,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
|||||||
}
|
}
|
||||||
sensor.set_offset(val);
|
sensor.set_offset(val);
|
||||||
sensor.set_value(val);
|
sensor.set_value(val);
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
|
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
|
||||||
#else
|
|
||||||
uint8_t channel = sensor.type() - AnalogType::PWM_0;
|
|
||||||
ledcWrite(channel, (uint32_t)(val * 8191 / 100));
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef EMSESP_DEFAULT_TX_MODE
|
#ifndef EMSESP_DEFAULT_TX_MODE
|
||||||
#define EMSESP_DEFAULT_TX_MODE 5 // Auto
|
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef EMSESP_DEFAULT_EMS_BUS_ID
|
#ifndef EMSESP_DEFAULT_EMS_BUS_ID
|
||||||
#define EMSESP_DEFAULT_EMS_BUS_ID 0x49 // gateway 2
|
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef EMSESP_DEFAULT_SYSLOG_ENABLED
|
#ifndef EMSESP_DEFAULT_SYSLOG_ENABLED
|
||||||
@@ -285,6 +285,8 @@ enum {
|
|||||||
#define EMSESP_PLATFORM "ESP32S3"
|
#define EMSESP_PLATFORM "ESP32S3"
|
||||||
#elif CONFIG_IDF_TARGET_ESP32 || EMSESP_STANDALONE
|
#elif CONFIG_IDF_TARGET_ESP32 || EMSESP_STANDALONE
|
||||||
#define EMSESP_PLATFORM "ESP32"
|
#define EMSESP_PLATFORM "ESP32"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||||
|
#define EMSESP_PLATFORM "ESP32C6"
|
||||||
#else
|
#else
|
||||||
#error Target CONFIG_IDF_TARGET is not supported
|
#error Target CONFIG_IDF_TARGET is not supported
|
||||||
#endif
|
#endif
|
||||||
@@ -293,12 +295,9 @@ enum {
|
|||||||
#ifndef STRINGIZE
|
#ifndef STRINGIZE
|
||||||
#define STRINGIZE(s) #s
|
#define STRINGIZE(s) #s
|
||||||
#endif
|
#endif
|
||||||
#ifdef TASMOTA_SDK
|
|
||||||
#define ARDUINO_VERSION_STR(major, minor, patch) "Tasmota Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
#define ARDUINO_VERSION_STR(major, minor, patch) "Tasmota Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
||||||
#else
|
|
||||||
#define ARDUINO_VERSION_STR(major, minor, patch) "ESP32 Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
|
||||||
#endif
|
|
||||||
#define ARDUINO_VERSION ARDUINO_VERSION_STR(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
|
#define ARDUINO_VERSION ARDUINO_VERSION_STR(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -387,10 +387,10 @@ void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get status of automatic fetch for a telegramID
|
// get status of automatic fetch for a telegramID
|
||||||
bool EMSdevice::is_fetch(uint16_t telegram_id, uint8_t len) const {
|
bool EMSdevice::is_fetch(uint16_t telegram_id) const {
|
||||||
for (const auto & tf : telegram_functions_) {
|
for (const auto & tf : telegram_functions_) {
|
||||||
if (tf.telegram_type_id_ == telegram_id) {
|
if (tf.telegram_type_id_ == telegram_id) {
|
||||||
return tf.fetch_ && tf.length_ >= len;
|
return tf.fetch_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ class EMSdevice {
|
|||||||
const char * telegram_type_name(std::shared_ptr<const Telegram> telegram);
|
const char * telegram_type_name(std::shared_ptr<const Telegram> telegram);
|
||||||
void fetch_values();
|
void fetch_values();
|
||||||
void toggle_fetch(uint16_t telegram_id, bool toggle);
|
void toggle_fetch(uint16_t telegram_id, bool toggle);
|
||||||
bool is_fetch(uint16_t telegram_id, uint8_t len = 0) const;
|
bool is_fetch(uint16_t telegram_id) const;
|
||||||
bool is_received(uint16_t telegram_id) const;
|
bool is_received(uint16_t telegram_id) const;
|
||||||
bool has_telegram_id(uint16_t id) const;
|
bool has_telegram_id(uint16_t id) const;
|
||||||
void ha_config_clear();
|
void ha_config_clear();
|
||||||
@@ -448,14 +448,6 @@ class EMSdevice {
|
|||||||
static constexpr uint8_t EMS_DEVICE_ID_DHW2 = 0x29; // MM100 module as water station
|
static constexpr uint8_t EMS_DEVICE_ID_DHW2 = 0x29; // MM100 module as water station
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_DHW8 = 0x2F; // last DHW module id?
|
static constexpr uint8_t EMS_DEVICE_ID_DHW8 = 0x2F; // last DHW module id?
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_IPM_DHW = 0x41; // IPM module as water station
|
static constexpr uint8_t EMS_DEVICE_ID_IPM_DHW = 0x41; // IPM module as water station
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_GATEWAY1 = 0x48; // KM200, MX300, MX400
|
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_GATEWAY2 = 0x49;
|
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_GATEWAY3 = 0x4A;
|
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_GATEWAY4 = 0x4B;
|
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_GATEWAY5 = 0x4C;
|
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_GATEWAY6 = 0x4D;
|
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_GATEWAY7 = 0x4E;
|
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_GATEWAY8 = 0x4F;
|
|
||||||
|
|
||||||
// generic type IDs
|
// generic type IDs
|
||||||
static constexpr uint16_t EMS_TYPE_NAME = 0x01; // device config for ems devices, name ascii on offset 27ff for ems+
|
static constexpr uint16_t EMS_TYPE_NAME = 0x01; // device config for ems devices, name ascii on offset 27ff for ems+
|
||||||
|
|||||||
@@ -380,12 +380,16 @@ std::string commands(std::string & expr, bool quotes) {
|
|||||||
if (return_code != CommandRet::OK && return_code != CommandRet::NO_VALUE) {
|
if (return_code != CommandRet::OK && return_code != CommandRet::NO_VALUE) {
|
||||||
return expr = "";
|
return expr = "";
|
||||||
}
|
}
|
||||||
|
std::string data;
|
||||||
std::string data = output["api_data"] | "";
|
if (output["api_data"].is<std::string>()) {
|
||||||
|
data = output["api_data"].as<std::string>();
|
||||||
if (!isnum(data) && quotes) {
|
if (!isnum(data) && quotes) {
|
||||||
data.insert(data.begin(), '"');
|
data.insert(data.begin(), '"');
|
||||||
data.insert(data.end(), '"');
|
data.insert(data.end(), '"');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
serializeJson(output, data);
|
||||||
|
}
|
||||||
expr.replace(f, l, data);
|
expr.replace(f, l, data);
|
||||||
e = f + data.length();
|
e = f + data.length();
|
||||||
expr_new = Helpers::toLower(expr);
|
expr_new = Helpers::toLower(expr);
|
||||||
@@ -700,7 +704,6 @@ std::string compute(const std::string & expr) {
|
|||||||
std::string cmd = expr_new.substr(f, e - f).c_str();
|
std::string cmd = expr_new.substr(f, e - f).c_str();
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
if (DeserializationError::Ok == deserializeJson(doc, cmd)) {
|
if (DeserializationError::Ok == deserializeJson(doc, cmd)) {
|
||||||
HTTPClient * http = new HTTPClient;
|
|
||||||
std::string url, header_s, value_s, method_s, key_s, keys_s;
|
std::string url, header_s, value_s, method_s, key_s, keys_s;
|
||||||
// search keys lower case
|
// search keys lower case
|
||||||
for (JsonPair p : doc.as<JsonObject>()) {
|
for (JsonPair p : doc.as<JsonObject>()) {
|
||||||
@@ -720,33 +723,126 @@ std::string compute(const std::string & expr) {
|
|||||||
keys_s = p.key().c_str();
|
keys_s = p.key().c_str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (http->begin(url.c_str())) {
|
bool content_set = false;
|
||||||
|
std::string value = doc[value_s] | "";
|
||||||
|
std::string method = doc[method_s] | "GET";
|
||||||
|
if (value.length()) {
|
||||||
|
method = "POST";
|
||||||
|
}
|
||||||
|
std::string result;
|
||||||
int httpResult = 0;
|
int httpResult = 0;
|
||||||
|
#ifndef NO_TLS_SUPPORT
|
||||||
|
if (Helpers::toLower(url.c_str()).starts_with("https://")) {
|
||||||
|
WiFiClient * basic_client = new WiFiClient;
|
||||||
|
ESP_SSLClient * ssl_client = new ESP_SSLClient;
|
||||||
|
ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert);
|
||||||
|
ssl_client->setBufferSizes(1024, 1024);
|
||||||
|
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
|
||||||
|
url.replace(0, 8, "");
|
||||||
|
std::string host = url;
|
||||||
|
auto index = url.find_first_of('/');
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
host = url.substr(0, index);
|
||||||
|
url.replace(0, index, "");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
index = host.find_first_of('@');
|
||||||
|
std::string auth;
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
auth = base64::encode(host.substr(0, index));
|
||||||
|
host.replace(0, index, "");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
ssl_client->setClient(basic_client);
|
||||||
|
if (ssl_client->connect(host.c_str(), 443)) {
|
||||||
|
if (value.length() || Helpers::toLower(method) == "post") {
|
||||||
|
ssl_client->print("POST ");
|
||||||
|
ssl_client->print(url.c_str());
|
||||||
|
ssl_client->println(" HTTP/1.1");
|
||||||
|
ssl_client->print("Host: ");
|
||||||
|
ssl_client->println(host.c_str());
|
||||||
|
for (JsonPair p : doc[header_s].as<JsonObject>()) {
|
||||||
|
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
|
||||||
|
ssl_client->print(p.key().c_str());
|
||||||
|
ssl_client->print(": ");
|
||||||
|
ssl_client->println(p.value().as<std::string>().c_str());
|
||||||
|
}
|
||||||
|
if (!content_set) {
|
||||||
|
ssl_client->print("Content-Type: ");
|
||||||
|
if (value.starts_with('{')) {
|
||||||
|
ssl_client->println(asyncsrv::T_application_json);
|
||||||
|
} else {
|
||||||
|
ssl_client->println(asyncsrv::T_text_plain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ssl_client->print("Content-Length: ");
|
||||||
|
ssl_client->println(value.length());
|
||||||
|
ssl_client->println("Connection: close");
|
||||||
|
ssl_client->print("\r\n");
|
||||||
|
ssl_client->print(value.c_str());
|
||||||
|
} else {
|
||||||
|
ssl_client->print("GET ");
|
||||||
|
ssl_client->print(url.c_str());
|
||||||
|
ssl_client->println(" HTTP/1.1");
|
||||||
|
ssl_client->print("Host: ");
|
||||||
|
ssl_client->println(host.c_str());
|
||||||
|
for (JsonPair p : doc[header_s].as<JsonObject>()) {
|
||||||
|
ssl_client->print(p.key().c_str());
|
||||||
|
ssl_client->print(": ");
|
||||||
|
ssl_client->println(p.value().as<std::string>().c_str());
|
||||||
|
}
|
||||||
|
ssl_client->println("Connection: close");
|
||||||
|
}
|
||||||
|
auto ms = millis();
|
||||||
|
while (!ssl_client->available() && millis() - ms < 3000) {
|
||||||
|
delay(0);
|
||||||
|
}
|
||||||
|
while (ssl_client->available()) {
|
||||||
|
result += (char)ssl_client->read();
|
||||||
|
}
|
||||||
|
ssl_client->stop();
|
||||||
|
index = result.find_first_of(' ');
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
httpResult = stoi(result.substr(index + 1, 3));
|
||||||
|
}
|
||||||
|
index = result.find("\r\n\r\n");
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
result.replace(0, index + 4, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete ssl_client;
|
||||||
|
delete basic_client;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
if (Helpers::toLower(url.c_str()).starts_with("http://")) {
|
||||||
|
HTTPClient * http = new HTTPClient;
|
||||||
|
if (http->begin(url.c_str())) {
|
||||||
for (JsonPair p : doc[header_s].as<JsonObject>()) {
|
for (JsonPair p : doc[header_s].as<JsonObject>()) {
|
||||||
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
|
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
|
||||||
|
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
|
||||||
}
|
}
|
||||||
std::string value = doc[value_s] | "";
|
|
||||||
std::string method = doc[method_s] | "get";
|
|
||||||
|
|
||||||
// if there is data, force a POST
|
|
||||||
if (value.length() || Helpers::toLower(method) == "post") {
|
if (value.length() || Helpers::toLower(method) == "post") {
|
||||||
if (value.find_first_of('{') != std::string::npos) {
|
if (!content_set) {
|
||||||
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
|
http->addHeader("Content-Type", value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain);
|
||||||
}
|
}
|
||||||
httpResult = http->POST(value.c_str());
|
httpResult = http->POST(value.c_str());
|
||||||
} else {
|
} else {
|
||||||
httpResult = http->GET(); // normal GET
|
httpResult = http->GET(); // normal GET
|
||||||
}
|
}
|
||||||
|
|
||||||
if (httpResult > 0) {
|
if (httpResult > 0) {
|
||||||
std::string result = http->getString().c_str();
|
result = http->getString().c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http->end();
|
||||||
|
delete http;
|
||||||
|
}
|
||||||
|
if (httpResult == 200) {
|
||||||
std::string key = doc[key_s] | "";
|
std::string key = doc[key_s] | "";
|
||||||
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
|
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
|
||||||
if (doc[keys_s].is<JsonArray>()) {
|
if (doc[keys_s].is<JsonArray>()) {
|
||||||
keys_doc.set(doc[keys_s].as<JsonArray>());
|
keys_doc.set(doc[keys_s].as<JsonArray>());
|
||||||
}
|
}
|
||||||
JsonArray keys = keys_doc.as<JsonArray>();
|
JsonArray keys = keys_doc.as<JsonArray>();
|
||||||
|
|
||||||
if (key.length() || !keys.isNull()) {
|
if (key.length() || !keys.isNull()) {
|
||||||
doc.clear();
|
doc.clear();
|
||||||
if (DeserializationError::Ok == deserializeJson(doc, result)) {
|
if (DeserializationError::Ok == deserializeJson(doc, result)) {
|
||||||
@@ -767,10 +863,9 @@ std::string compute(const std::string & expr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expr_new.replace(f, e - f, result.c_str());
|
expr_new.replace(f, e - f, result);
|
||||||
}
|
} else if (httpResult != 0) {
|
||||||
http->end();
|
EMSESP::logger().warning("URL command failed with https code: %d, response: %s", httpResult, result.c_str());
|
||||||
delete http;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f = expr_new.find_first_of('{', e);
|
f = expr_new.find_first_of('{', e);
|
||||||
|
|||||||
@@ -37,6 +37,15 @@
|
|||||||
#include "../test/test.h"
|
#include "../test/test.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef NO_TLS_SUPPORT
|
||||||
|
#define ENABLE_SMTP
|
||||||
|
#define USE_ESP_SSLCLIENT
|
||||||
|
#define READYCLIENT_SSL_CLIENT ESP_SSLClient
|
||||||
|
#define READYCLIENT_TYPE_1 // TYPE 1 when using ESP_SSLClient
|
||||||
|
#include <ESP_SSLClient.h>
|
||||||
|
#include <ReadyMail.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
// Languages supported. Note: the order is important
|
// Languages supported. Note: the order is important
|
||||||
@@ -105,6 +114,115 @@ bool System::command_send(const char * value, const int8_t id) {
|
|||||||
return EMSESP::txservice_.send_raw(value); // ignore id
|
return EMSESP::txservice_.send_raw(value); // ignore id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool System::command_sendmail(const char * value, const int8_t id) {
|
||||||
|
bool enabled = false;
|
||||||
|
bool ssl, starttls;
|
||||||
|
uint16_t port;
|
||||||
|
String server, login, pass, sender, recp, subject;
|
||||||
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
|
enabled = settings.email_enabled;
|
||||||
|
ssl = settings.email_ssl;
|
||||||
|
starttls = settings.email_starttls;
|
||||||
|
server = settings.email_server;
|
||||||
|
port = settings.email_port;
|
||||||
|
login = settings.email_login;
|
||||||
|
pass = settings.email_pass;
|
||||||
|
sender = settings.email_sender;
|
||||||
|
recp = settings.email_recp;
|
||||||
|
subject = settings.email_subject;
|
||||||
|
});
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_DEBUG("Command sendmail port %d%s called with '%s'", port, ssl ? " (SSL)" : starttls ? " (STARTTLS)" : "", value);
|
||||||
|
// LOG_DEBUG("Command sendmail port %d called with '%s'", port, value);
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
#ifndef NO_TLS_SUPPORT
|
||||||
|
WiFiClient * basic_client = new WiFiClient;
|
||||||
|
ESP_SSLClient * ssl_client = new ESP_SSLClient;
|
||||||
|
ReadyClient * r_client = new ReadyClient(*ssl_client);
|
||||||
|
SMTPClient * smtp = new SMTPClient(*r_client);
|
||||||
|
|
||||||
|
ssl_client->setClient(basic_client);
|
||||||
|
ssl_client->setInsecure();
|
||||||
|
ssl_client->setBufferSizes(1024, 1024);
|
||||||
|
r_client->addPort(port, starttls ? readymail_protocol_tls : ssl ? readymail_protocol_ssl : readymail_protocol_plain_text);
|
||||||
|
|
||||||
|
// smtp->connect(server, port, sendmailCallback);
|
||||||
|
smtp->connect(server, port);
|
||||||
|
if (!smtp->isConnected()) {
|
||||||
|
LOG_ERROR("Sendmail connection error");
|
||||||
|
delete smtp;
|
||||||
|
delete r_client;
|
||||||
|
delete ssl_client;
|
||||||
|
delete basic_client;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LOG_INFO("authenticate %s:%s", login.c_str(), pass.c_str());
|
||||||
|
smtp->authenticate(login, pass, readymail_auth_password);
|
||||||
|
if (!smtp->isAuthenticated()) {
|
||||||
|
LOG_ERROR("Sendmail authenticate error");
|
||||||
|
delete smtp;
|
||||||
|
delete r_client;
|
||||||
|
delete ssl_client;
|
||||||
|
delete basic_client;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JsonDocument doc;
|
||||||
|
String body = value;
|
||||||
|
if (body.length()) {
|
||||||
|
auto error = deserializeJson(doc, (const char *)value);
|
||||||
|
if (!error && doc.as<JsonObject>().size() >= 0) {
|
||||||
|
subject = doc["subject"] | subject;
|
||||||
|
recp = doc["to"] | recp;
|
||||||
|
sender = doc["from"] | sender;
|
||||||
|
body = doc["body"] | body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SMTPMessage & msg = smtp->getMessage();
|
||||||
|
msg.headers.add(rfc822_subject, subject);
|
||||||
|
msg.headers.add(rfc822_from, sender);
|
||||||
|
msg.headers.add(rfc822_to, recp);
|
||||||
|
|
||||||
|
// Use addCustom to add custom header e.g. Importance and Priority.
|
||||||
|
// msg.headers.addCustom("Importance", PRIORITY);
|
||||||
|
// msg.headers.addCustom("X-MSMail-Priority", PRIORITY);
|
||||||
|
// msg.headers.addCustom("X-Priority", PRIORITY_NUM);
|
||||||
|
EMSESP::webSchedulerService.computed_value.clear();
|
||||||
|
EMSESP::webSchedulerService.raw_value = body.c_str();
|
||||||
|
for (uint16_t wait = 0; wait < 2000 && !EMSESP::webSchedulerService.raw_value.empty(); wait++) {
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
if (!EMSESP::webSchedulerService.computed_value.empty()) {
|
||||||
|
body = EMSESP::webSchedulerService.computed_value.c_str();
|
||||||
|
EMSESP::webSchedulerService.computed_value.clear();
|
||||||
|
EMSESP::webSchedulerService.computed_value.shrink_to_fit(); // free allocated memory
|
||||||
|
}
|
||||||
|
msg.text.body(body);
|
||||||
|
|
||||||
|
// bodyText.replace("\r\n", "<br>\r\n");
|
||||||
|
// msg.html.body("<html><body><div style=\"color:#cc0066;\">" + bodyText + "</div></body></html>");
|
||||||
|
// msg.html.transferEncoding("base64");
|
||||||
|
|
||||||
|
// With embedFile function, the html message will send as attachment.
|
||||||
|
// if (EMBED_MESSAGE)
|
||||||
|
// msg.html.embedFile(true, "msg.html", embed_message_type_attachment);
|
||||||
|
|
||||||
|
msg.timestamp = time(nullptr);
|
||||||
|
|
||||||
|
success = smtp->send(msg);
|
||||||
|
|
||||||
|
delete smtp;
|
||||||
|
delete r_client;
|
||||||
|
delete ssl_client;
|
||||||
|
delete basic_client;
|
||||||
|
#endif
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
// return string of languages and count
|
// return string of languages and count
|
||||||
std::string System::languages_string() {
|
std::string System::languages_string() {
|
||||||
std::string languages_string = std::to_string(NUM_LANGUAGES) + " languages (";
|
std::string languages_string = std::to_string(NUM_LANGUAGES) + " languages (";
|
||||||
@@ -237,7 +355,8 @@ bool System::command_message(const char * value, const int8_t id, JsonObject out
|
|||||||
LOG_INFO("Message: %s", EMSESP::webSchedulerService.computed_value.c_str()); // send to log
|
LOG_INFO("Message: %s", EMSESP::webSchedulerService.computed_value.c_str()); // send to log
|
||||||
Mqtt::queue_publish(F_(message), EMSESP::webSchedulerService.computed_value); // send to MQTT if enabled
|
Mqtt::queue_publish(F_(message), EMSESP::webSchedulerService.computed_value); // send to MQTT if enabled
|
||||||
output["api_data"] = EMSESP::webSchedulerService.computed_value; // send to API
|
output["api_data"] = EMSESP::webSchedulerService.computed_value; // send to API
|
||||||
|
EMSESP::webSchedulerService.computed_value.clear();
|
||||||
|
EMSESP::webSchedulerService.computed_value.shrink_to_fit();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +580,6 @@ void System::system_restart(const char * partitionname) {
|
|||||||
|
|
||||||
Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1
|
Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1
|
||||||
EMSuart::stop(); // stop UART so there is no interference
|
EMSuart::stop(); // stop UART so there is no interference
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
delay(1000); // wait 1 second
|
delay(1000); // wait 1 second
|
||||||
ESP.restart(); // ka-boom! - this is the only place where the ESP32 restart is called
|
ESP.restart(); // ka-boom! - this is the only place where the ESP32 restart is called
|
||||||
@@ -526,29 +644,6 @@ void System::syslog_init() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// start or reconfigure modbus
|
|
||||||
void System::modbus_init() {
|
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
|
||||||
if (settings.modbus_enabled) {
|
|
||||||
if (EMSESP::modbus_ == nullptr) {
|
|
||||||
EMSESP::modbus_ = new Modbus;
|
|
||||||
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
|
|
||||||
} else if (settings.modbus_port != modbus_port_ || settings.modbus_max_clients != modbus_max_clients_ || settings.modbus_timeout != modbus_timeout_) {
|
|
||||||
EMSESP::modbus_->stop();
|
|
||||||
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
|
|
||||||
}
|
|
||||||
} else if (EMSESP::modbus_ != nullptr) {
|
|
||||||
EMSESP::modbus_->stop();
|
|
||||||
delete EMSESP::modbus_;
|
|
||||||
EMSESP::modbus_ = nullptr;
|
|
||||||
}
|
|
||||||
modbus_enabled_ = settings.modbus_enabled;
|
|
||||||
modbus_port_ = settings.modbus_port;
|
|
||||||
modbus_max_clients_ = settings.modbus_max_clients;
|
|
||||||
modbus_timeout_ = settings.modbus_timeout;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// read specific major system settings to store locally for faster access
|
// read specific major system settings to store locally for faster access
|
||||||
void System::store_settings(WebSettings & settings) {
|
void System::store_settings(WebSettings & settings) {
|
||||||
version_ = settings.version;
|
version_ = settings.version;
|
||||||
@@ -586,6 +681,24 @@ void System::store_settings(WebSettings & settings) {
|
|||||||
|
|
||||||
locale_ = settings.locale;
|
locale_ = settings.locale;
|
||||||
developer_mode_ = settings.developer_mode;
|
developer_mode_ = settings.developer_mode;
|
||||||
|
// start services
|
||||||
|
if (settings.modbus_enabled) {
|
||||||
|
if (EMSESP::modbus_ == nullptr) {
|
||||||
|
EMSESP::modbus_ = new Modbus;
|
||||||
|
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
|
||||||
|
} else if (settings.modbus_port != modbus_port_ || settings.modbus_max_clients != modbus_max_clients_ || settings.modbus_timeout != modbus_timeout_) {
|
||||||
|
EMSESP::modbus_->stop();
|
||||||
|
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
|
||||||
|
}
|
||||||
|
} else if (EMSESP::modbus_ != nullptr) {
|
||||||
|
EMSESP::modbus_->stop();
|
||||||
|
delete EMSESP::modbus_;
|
||||||
|
EMSESP::modbus_ = nullptr;
|
||||||
|
}
|
||||||
|
modbus_enabled_ = settings.modbus_enabled;
|
||||||
|
modbus_port_ = settings.modbus_port;
|
||||||
|
modbus_max_clients_ = settings.modbus_max_clients;
|
||||||
|
modbus_timeout_ = settings.modbus_timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts up core services
|
// Starts up core services
|
||||||
@@ -609,20 +722,11 @@ void System::start() {
|
|||||||
appfree_ = esp_ota_get_running_partition()->size / 1024 - appused_;
|
appfree_ = esp_ota_get_running_partition()->size / 1024 - appused_;
|
||||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
temp_sensor_config_t temp_sensor = TSENS_CONFIG_DEFAULT();
|
|
||||||
temp_sensor_get_config(&temp_sensor);
|
|
||||||
temp_sensor.dac_offset = TSENS_DAC_DEFAULT; // DEFAULT: range:-10℃ ~ 80℃, error < 1℃.
|
|
||||||
temp_sensor_set_config(temp_sensor);
|
|
||||||
temp_sensor_start();
|
|
||||||
temp_sensor_read_celsius(&temperature_);
|
|
||||||
#else
|
|
||||||
temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
|
temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
|
||||||
temperature_sensor_install(&temp_sensor_config, &temperature_handle_);
|
temperature_sensor_install(&temp_sensor_config, &temperature_handle_);
|
||||||
temperature_sensor_enable(temperature_handle_);
|
temperature_sensor_enable(temperature_handle_);
|
||||||
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||||
@@ -635,7 +739,6 @@ void System::start() {
|
|||||||
network_init(); // network
|
network_init(); // network
|
||||||
uart_init(); // start UART
|
uart_init(); // start UART
|
||||||
syslog_init(); // start syslog
|
syslog_init(); // start syslog
|
||||||
modbus_init(); // start modbus
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// button single click
|
// button single click
|
||||||
@@ -844,16 +947,9 @@ void System::send_info_mqtt() {
|
|||||||
doc["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
doc["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
||||||
doc["IPv4 nameserver"] = uuid::printable_to_string(WiFi.dnsIP());
|
doc["IPv4 nameserver"] = uuid::printable_to_string(WiFi.dnsIP());
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.localIPv6().toString() != "::") {
|
|
||||||
doc["IPv6 address"] = uuid::printable_to_string(WiFi.localIPv6());
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
||||||
doc["IPv6 address"] = uuid::printable_to_string(WiFi.linkLocalIPv6());
|
doc["IPv6 address"] = uuid::printable_to_string(WiFi.linkLocalIPv6());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Mqtt::queue_publish_retain(F_(info), doc.as<JsonObject>()); // topic called "info" and it's Retained
|
Mqtt::queue_publish_retain(F_(info), doc.as<JsonObject>()); // topic called "info" and it's Retained
|
||||||
@@ -962,12 +1058,11 @@ void System::network_init() {
|
|||||||
delay(500);
|
delay(500);
|
||||||
digitalWrite(eth_power_, HIGH);
|
digitalWrite(eth_power_, HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
eth_present_ = ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
|
|
||||||
#else
|
|
||||||
eth_present_ = ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode);
|
eth_present_ = ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode);
|
||||||
#endif
|
if (eth_present_) {
|
||||||
|
// Push hostname to the ETH netif immediately after it's created
|
||||||
|
ETH.setHostname(hostname_.c_str());
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -979,13 +1074,9 @@ void System::system_check() {
|
|||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
temp_sensor_read_celsius(&temperature_);
|
|
||||||
#else
|
|
||||||
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef EMSESP_PINGTEST
|
#ifdef EMSESP_PINGTEST
|
||||||
static uint64_t ping_count = 0;
|
static uint64_t ping_count = 0;
|
||||||
@@ -1037,6 +1128,7 @@ void System::commands_init() {
|
|||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(read), System::command_read, FL_(read_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(read), System::command_read, FL_(read_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, FL_(send_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, FL_(send_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, FL_(fetch_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, FL_(fetch_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(sendmail), System::command_sendmail, FL_(sendmail_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, FL_(restart_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, FL_(restart_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(format), System::command_format, FL_(format_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(format), System::command_format, FL_(format_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(txpause), System::command_txpause, FL_(txpause_cmd), CommandFlag::ADMIN_ONLY);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(txpause), System::command_txpause, FL_(txpause_cmd), CommandFlag::ADMIN_ONLY);
|
||||||
@@ -1285,16 +1377,9 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
||||||
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
||||||
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.localIPv6().toString() != "::") {
|
|
||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.localIPv6()).c_str());
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.linkLocalIPv6()).c_str());
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.linkLocalIPv6()).c_str());
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_CONNECT_FAILED:
|
case WL_CONNECT_FAILED:
|
||||||
@@ -1325,15 +1410,9 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
||||||
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
|
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
|
||||||
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(ETH.dnsIP()).c_str());
|
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(ETH.dnsIP()).c_str());
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.localIPv6().toString() != "::") {
|
|
||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.localIPv6()).c_str());
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (ETH.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.linkLocalIPv6().toString() != "::") {
|
if (ETH.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.linkLocalIPv6().toString() != "::") {
|
||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
shell.println();
|
shell.println();
|
||||||
|
|
||||||
@@ -1355,10 +1434,10 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shell.println();
|
shell.println();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// see if there is a restore of an older settings file that needs to be applied
|
// see if there is a restore of an older settings file that needs to be applied
|
||||||
// note there can be only one file at a time
|
// note there can be only one file at a time
|
||||||
bool System::check_restore() {
|
bool System::check_restore() {
|
||||||
@@ -1658,8 +1737,8 @@ bool System::check_upgrade() {
|
|||||||
return false; // no reboot required
|
return false; // no reboot required
|
||||||
}
|
}
|
||||||
|
|
||||||
// map each config filename to its human-readable section key
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// map each config filename to its human-readable section key
|
||||||
static const std::pair<const char *, const char *> SECTION_MAP[] = {
|
static const std::pair<const char *, const char *> SECTION_MAP[] = {
|
||||||
{NETWORK_SETTINGS_FILE, "Network"},
|
{NETWORK_SETTINGS_FILE, "Network"},
|
||||||
{AP_SETTINGS_FILE, "AP"},
|
{AP_SETTINGS_FILE, "AP"},
|
||||||
@@ -1774,27 +1853,16 @@ void System::exportSystemBackup(JsonObject output) {
|
|||||||
|
|
||||||
const char * nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs1") ? "nvs1" : "nvs"; // nvs1 is on 16MBs
|
const char * nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs1") ? "nvs1" : "nvs"; // nvs1 is on 16MBs
|
||||||
nvs_iterator_t it = nullptr;
|
nvs_iterator_t it = nullptr;
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
it = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY);
|
|
||||||
if (it == nullptr) {
|
|
||||||
#else
|
|
||||||
esp_err_t err = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY, &it);
|
esp_err_t err = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY, &it);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
#endif
|
|
||||||
LOG_ERROR("Failed to find NVS entry for %s", nvs_part);
|
LOG_ERROR("Failed to find NVS entry for %s", nvs_part);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonArray entries = node["nvs"].to<JsonArray>();
|
JsonArray entries = node["nvs"].to<JsonArray>();
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
while (it != nullptr) {
|
|
||||||
nvs_entry_info_t info;
|
|
||||||
nvs_entry_info(it, &info);
|
|
||||||
#else
|
|
||||||
while (err == ESP_OK) {
|
while (err == ESP_OK) {
|
||||||
nvs_entry_info_t info;
|
nvs_entry_info_t info;
|
||||||
nvs_entry_info(it, &info);
|
nvs_entry_info(it, &info);
|
||||||
#endif
|
|
||||||
JsonObject entry = entries.add<JsonObject>();
|
JsonObject entry = entries.add<JsonObject>();
|
||||||
entry["type"] = info.type;
|
entry["type"] = info.type;
|
||||||
entry["key"] = info.key;
|
entry["key"] = info.key;
|
||||||
@@ -1829,14 +1897,8 @@ void System::exportSystemBackup(JsonObject output) {
|
|||||||
entry["value"] = EMSESP::nvs_.getString(info.key);
|
entry["value"] = EMSESP::nvs_.getString(info.key);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
it = nvs_entry_next(it);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
err = nvs_entry_next(&it);
|
err = nvs_entry_next(&it);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (it != nullptr) {
|
if (it != nullptr) {
|
||||||
nvs_release_iterator(it);
|
nvs_release_iterator(it);
|
||||||
@@ -3334,6 +3396,10 @@ void System::set_valid_system_gpios() {
|
|||||||
} else {
|
} else {
|
||||||
valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 20, 24, 28-31");
|
valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 20, 24, 28-31");
|
||||||
}
|
}
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||||
|
// https://docs.espressif.com/projects/esp-idf/en/v5.5.3/esp32c6/api-reference/peripherals/gpio.html
|
||||||
|
// 24-30 used for flash, 12-13 USB, 16-17 uart0
|
||||||
|
valid_system_gpios_ = string_range_to_vector("0-30", "12-13, 16-17, 24-30");
|
||||||
#elif defined(EMSESP_STANDALONE)
|
#elif defined(EMSESP_STANDALONE)
|
||||||
valid_system_gpios_ = string_range_to_vector("0-39");
|
valid_system_gpios_ = string_range_to_vector("0-39");
|
||||||
#endif
|
#endif
|
||||||
@@ -3383,24 +3449,6 @@ void System::remove_gpio(uint8_t pin, bool also_system) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove a gpio that has 0 for disable
|
|
||||||
void System::remove_optional_gpio(uint8_t pin) {
|
|
||||||
if (pin) {
|
|
||||||
remove_gpio(pin, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set unused gpios to default state input high-Z
|
|
||||||
void System::reset_unused_gpios() {
|
|
||||||
for (const auto & pin : valid_system_gpios_) {
|
|
||||||
auto it = std::find_if(used_gpios_.begin(), used_gpios_.end(), [pin](const GpioUsage & usage) { return usage.pin == pin; });
|
|
||||||
if (it == used_gpios_.end()) {
|
|
||||||
LOG_DEBUG("reset pin %d", pin);
|
|
||||||
pinMode(pin, INPUT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return a list of GPIO's available for use
|
// return a list of GPIO's available for use
|
||||||
std::vector<uint8_t> System::available_gpios() {
|
std::vector<uint8_t> System::available_gpios() {
|
||||||
std::vector<uint8_t> gpios;
|
std::vector<uint8_t> gpios;
|
||||||
|
|||||||
@@ -37,11 +37,7 @@
|
|||||||
#include <uuid/log.h>
|
#include <uuid/log.h>
|
||||||
#include <PButton.h>
|
#include <PButton.h>
|
||||||
|
|
||||||
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
|
||||||
#define EMSESP_RGB_WRITE neopixelWrite
|
|
||||||
#else
|
|
||||||
#define EMSESP_RGB_WRITE rgbLedWrite
|
#define EMSESP_RGB_WRITE rgbLedWrite
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
// there is no official API available on the original ESP32
|
// there is no official API available on the original ESP32
|
||||||
@@ -49,12 +45,8 @@ extern "C" {
|
|||||||
uint8_t temprature_sens_read();
|
uint8_t temprature_sens_read();
|
||||||
}
|
}
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
#include "driver/temp_sensor.h"
|
|
||||||
#else
|
|
||||||
#include "driver/temperature_sensor.h"
|
#include "driver/temperature_sensor.h"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
using uuid::console::Shell;
|
using uuid::console::Shell;
|
||||||
|
|
||||||
@@ -103,6 +95,7 @@ class System {
|
|||||||
static bool command_info(const char * value, const int8_t id, JsonObject output);
|
static bool command_info(const char * value, const int8_t id, JsonObject output);
|
||||||
static bool command_response(const char * value, const int8_t id, JsonObject output);
|
static bool command_response(const char * value, const int8_t id, JsonObject output);
|
||||||
static bool command_service(const char * cmd, const char * value);
|
static bool command_service(const char * cmd, const char * value);
|
||||||
|
static bool command_sendmail(const char * value, const int8_t id);
|
||||||
static bool command_txpause(const char * value, const int8_t id);
|
static bool command_txpause(const char * value, const int8_t id);
|
||||||
|
|
||||||
static bool get_value_info(JsonObject root, const char * cmd);
|
static bool get_value_info(JsonObject root, const char * cmd);
|
||||||
@@ -122,7 +115,6 @@ class System {
|
|||||||
void show_mem(const char * note);
|
void show_mem(const char * note);
|
||||||
void store_settings(class WebSettings & settings);
|
void store_settings(class WebSettings & settings);
|
||||||
void syslog_init();
|
void syslog_init();
|
||||||
void modbus_init();
|
|
||||||
bool check_upgrade();
|
bool check_upgrade();
|
||||||
bool check_restore();
|
bool check_restore();
|
||||||
void heartbeat_json(JsonObject output);
|
void heartbeat_json(JsonObject output);
|
||||||
@@ -377,8 +369,6 @@ class System {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void remove_gpio(uint8_t pin, bool also_system = false); // remove a gpio from both valid (optional) and used lists
|
static void remove_gpio(uint8_t pin, bool also_system = false); // remove a gpio from both valid (optional) and used lists
|
||||||
static void remove_optional_gpio(uint8_t pin);
|
|
||||||
static void reset_unused_gpios();
|
|
||||||
|
|
||||||
// Partition info map: partition name -> {version, size, install_date}
|
// Partition info map: partition name -> {version, size, install_date}
|
||||||
std::map<std::string, PartitionInfo, std::less<>, AllocatorPSRAM<std::pair<const std::string, PartitionInfo>>> partition_info_;
|
std::map<std::string, PartitionInfo, std::less<>, AllocatorPSRAM<std::pair<const std::string, PartitionInfo>>> partition_info_;
|
||||||
@@ -505,9 +495,7 @@ class System {
|
|||||||
uint32_t appfree_;
|
uint32_t appfree_;
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
temperature_sensor_handle_t temperature_handle_ = NULL;
|
temperature_sensor_handle_t temperature_handle_ = NULL;
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
float temperature_ = 0;
|
float temperature_ = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ uint8_t EMSbus::ems_mask_ = EMS_MASK_UNSET; // unset so its triggered
|
|||||||
uint8_t EMSbus::ems_bus_id_ = EMSESP_DEFAULT_EMS_BUS_ID;
|
uint8_t EMSbus::ems_bus_id_ = EMSESP_DEFAULT_EMS_BUS_ID;
|
||||||
uint8_t EMSbus::tx_mode_ = EMSESP_DEFAULT_TX_MODE;
|
uint8_t EMSbus::tx_mode_ = EMSESP_DEFAULT_TX_MODE;
|
||||||
uint8_t EMSbus::tx_state_ = Telegram::Operation::NONE;
|
uint8_t EMSbus::tx_state_ = Telegram::Operation::NONE;
|
||||||
bool EMSbus::isEMS2_ = false;
|
|
||||||
|
|
||||||
uuid::log::Logger EMSbus::logger_{F_(telegram), uuid::log::Facility::CONSOLE};
|
uuid::log::Logger EMSbus::logger_{F_(telegram), uuid::log::Facility::CONSOLE};
|
||||||
|
|
||||||
@@ -207,9 +206,7 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
|||||||
message_data = data + 6;
|
message_data = data + 6;
|
||||||
message_length = length - 7;
|
message_length = length - 7;
|
||||||
}
|
}
|
||||||
if (type_id > 0x0FF && message_length > 1) { // used for auto tx_mode
|
|
||||||
set_ems2();
|
|
||||||
}
|
|
||||||
// if we're watching and "raw" print out actual telegram as bytes to the console
|
// if we're watching and "raw" print out actual telegram as bytes to the console
|
||||||
// including the CRC at the end
|
// including the CRC at the end
|
||||||
if (EMSESP::watch() == EMSESP::Watch::WATCH_RAW) {
|
if (EMSESP::watch() == EMSESP::Watch::WATCH_RAW) {
|
||||||
|
|||||||
@@ -163,19 +163,12 @@ class EMSbus {
|
|||||||
static constexpr uint8_t EMS_MASK_HT3 = 0x80; // EMS bus type Junkers/HT3
|
static constexpr uint8_t EMS_MASK_HT3 = 0x80; // EMS bus type Junkers/HT3
|
||||||
static constexpr uint8_t EMS_MASK_BUDERUS = 0xFF; // EMS bus type Buderus
|
static constexpr uint8_t EMS_MASK_BUDERUS = 0xFF; // EMS bus type Buderus
|
||||||
static constexpr uint8_t EMS_TX_ERROR_LIMIT = 10; // % limit of failed Tx read/write attempts before showing a warning
|
static constexpr uint8_t EMS_TX_ERROR_LIMIT = 10; // % limit of failed Tx read/write attempts before showing a warning
|
||||||
|
static constexpr uint8_t EMS_POLL_MATCH_LIMIT = 3; // consecutive poll matches needed before declaring bus connected
|
||||||
|
|
||||||
static bool is_ht3() {
|
static bool is_ht3() {
|
||||||
return (ems_mask_ == EMS_MASK_HT3);
|
return (ems_mask_ == EMS_MASK_HT3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_ems2() {
|
|
||||||
return isEMS2_;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_ems2() {
|
|
||||||
isEMS2_ = true;;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t ems_mask() {
|
static uint8_t ems_mask() {
|
||||||
return ems_mask_;
|
return ems_mask_;
|
||||||
}
|
}
|
||||||
@@ -218,7 +211,6 @@ class EMSbus {
|
|||||||
if (!last_bus_activity_) {
|
if (!last_bus_activity_) {
|
||||||
bus_uptime_start_ = timestamp;
|
bus_uptime_start_ = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_bus_activity_ = timestamp;
|
last_bus_activity_ = timestamp;
|
||||||
bus_connected_ = true;
|
bus_connected_ = true;
|
||||||
}
|
}
|
||||||
@@ -250,7 +242,6 @@ class EMSbus {
|
|||||||
static uint8_t ems_bus_id_; // the bus id, which configurable and stored in settings
|
static uint8_t ems_bus_id_; // the bus id, which configurable and stored in settings
|
||||||
static uint8_t tx_mode_; // local copy of the tx mode
|
static uint8_t tx_mode_; // local copy of the tx mode
|
||||||
static uint8_t tx_state_; // state of the Tx line (NONE or waiting on a TX_READ or TX_WRITE)
|
static uint8_t tx_state_; // state of the Tx line (NONE or waiting on a TX_READ or TX_WRITE)
|
||||||
static bool isEMS2_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RxService : public EMSbus {
|
class RxService : public EMSbus {
|
||||||
|
|||||||
@@ -21,6 +21,12 @@
|
|||||||
#include "temperaturesensor.h"
|
#include "temperaturesensor.h"
|
||||||
#include "emsesp.h"
|
#include "emsesp.h"
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
|
#define YIELD
|
||||||
|
#else
|
||||||
|
#define YIELD yield()
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::Facility::DAEMON};
|
uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::Facility::DAEMON};
|
||||||
@@ -75,6 +81,7 @@ void TemperatureSensor::loop() {
|
|||||||
LOG_DEBUG("Read sensor temperature");
|
LOG_DEBUG("Read sensor temperature");
|
||||||
#endif
|
#endif
|
||||||
if (bus_.reset() || parasite_) {
|
if (bus_.reset() || parasite_) {
|
||||||
|
YIELD;
|
||||||
bus_.skip();
|
bus_.skip();
|
||||||
bus_.write(CMD_CONVERT_TEMP, parasite_ ? 1 : 0);
|
bus_.write(CMD_CONVERT_TEMP, parasite_ ? 1 : 0);
|
||||||
state_ = State::READING;
|
state_ = State::READING;
|
||||||
@@ -253,16 +260,19 @@ int16_t TemperatureSensor::get_temperature_c(const uint8_t addr[]) {
|
|||||||
LOG_ERROR("Bus reset failed before reading scratchpad from %s", Sensor(addr).id());
|
LOG_ERROR("Bus reset failed before reading scratchpad from %s", Sensor(addr).id());
|
||||||
return EMS_VALUE_INT16_NOTSET;
|
return EMS_VALUE_INT16_NOTSET;
|
||||||
}
|
}
|
||||||
|
YIELD;
|
||||||
|
|
||||||
uint8_t scratchpad[SCRATCHPAD_LEN] = {0};
|
uint8_t scratchpad[SCRATCHPAD_LEN] = {0};
|
||||||
bus_.select(addr);
|
bus_.select(addr);
|
||||||
bus_.write(CMD_READ_SCRATCHPAD);
|
bus_.write(CMD_READ_SCRATCHPAD);
|
||||||
bus_.read_bytes(scratchpad, SCRATCHPAD_LEN);
|
bus_.read_bytes(scratchpad, SCRATCHPAD_LEN);
|
||||||
|
YIELD;
|
||||||
|
|
||||||
if (!bus_.reset()) {
|
if (!bus_.reset()) {
|
||||||
LOG_ERROR("Bus reset failed after reading scratchpad from %s", Sensor(addr).id());
|
LOG_ERROR("Bus reset failed after reading scratchpad from %s", Sensor(addr).id());
|
||||||
return EMS_VALUE_INT16_NOTSET;
|
return EMS_VALUE_INT16_NOTSET;
|
||||||
}
|
}
|
||||||
|
YIELD;
|
||||||
|
|
||||||
if (bus_.crc8(scratchpad, SCRATCHPAD_LEN - 1) != scratchpad[SCRATCHPAD_LEN - 1]) {
|
if (bus_.crc8(scratchpad, SCRATCHPAD_LEN - 1) != scratchpad[SCRATCHPAD_LEN - 1]) {
|
||||||
LOG_WARNING("Invalid scratchpad CRC: %02X%02X%02X%02X%02X%02X%02X%02X%02X from sensor %s",
|
LOG_WARNING("Invalid scratchpad CRC: %02X%02X%02X%02X%02X%02X%02X%02X%02X from sensor %s",
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
|||||||
for (uint8_t i = 0; i < monitor_size; i++) {
|
for (uint8_t i = 0; i < monitor_size; i++) {
|
||||||
register_telegram_type(monitor_typeids[i], "RC300Monitor", false, MAKE_PF_CB(process_RC300Monitor), 33);
|
register_telegram_type(monitor_typeids[i], "RC300Monitor", false, MAKE_PF_CB(process_RC300Monitor), 33);
|
||||||
register_telegram_type(set_typeids[i], "RC300Set", false, MAKE_PF_CB(process_RC300Set), 29);
|
register_telegram_type(set_typeids[i], "RC300Set", false, MAKE_PF_CB(process_RC300Set), 29);
|
||||||
register_telegram_type(summer_typeids[i], "RC300Summer", false, MAKE_PF_CB(process_RC300Summer), 14);
|
register_telegram_type(summer_typeids[i], "RC300Summer", false, MAKE_PF_CB(process_RC300Summer), 13);
|
||||||
register_telegram_type(curve_typeids[i], "RC300Curves", false, MAKE_PF_CB(process_RC300Curve), 9);
|
register_telegram_type(curve_typeids[i], "RC300Curves", false, MAKE_PF_CB(process_RC300Curve), 9);
|
||||||
register_telegram_type(summer2_typeids[i], "RC300Summer2", false, MAKE_PF_CB(process_RC300Summer2), 8);
|
register_telegram_type(summer2_typeids[i], "RC300Summer2", false, MAKE_PF_CB(process_RC300Summer2), 8);
|
||||||
}
|
}
|
||||||
@@ -202,12 +202,11 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
|||||||
if (model == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
if (model == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
||||||
register_telegram_type(0x43F, "CRHolidays", true, MAKE_PF_CB(process_RC300Holiday), 6);
|
register_telegram_type(0x43F, "CRHolidays", true, MAKE_PF_CB(process_RC300Holiday), 6);
|
||||||
} else {
|
} else {
|
||||||
register_telegram_type(0x269, "RC300Holiday", true, MAKE_PF_CB(process_RC300Holiday), 18);
|
register_telegram_type(0x269, "RC300Holiday", true, MAKE_PF_CB(process_RC300Holiday), 6);
|
||||||
}
|
}
|
||||||
register_telegram_type(0x16E, "Absent", true, MAKE_PF_CB(process_Absent), 1);
|
register_telegram_type(0x16E, "Absent", true, MAKE_PF_CB(process_Absent), 1);
|
||||||
register_telegram_type(0xBF, "ErrorMessage", false, MAKE_PF_CB(process_ErrorMessageBF));
|
register_telegram_type(0xBF, "ErrorMessage", false, MAKE_PF_CB(process_ErrorMessageBF));
|
||||||
register_telegram_type(0xC0, "RCErrorMessage", false, MAKE_PF_CB(process_RCErrorMessage2));
|
register_telegram_type(0xC0, "RCErrorMessage", false, MAKE_PF_CB(process_RCErrorMessage2));
|
||||||
register_telegram_type(0x470, "RC300Summer2", true, MAKE_PF_CB(process_RC300Summer2), 8);
|
|
||||||
EMSESP::send_read_request(0xC0, device_id, 0, 20); // read last errorcode on start (only published on errors)
|
EMSESP::send_read_request(0xC0, device_id, 0, 20); // read last errorcode on start (only published on errors)
|
||||||
|
|
||||||
// JUNKERS/HT3
|
// JUNKERS/HT3
|
||||||
@@ -1269,14 +1268,8 @@ void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
|
|||||||
void Thermostat::process_RC300Summer2(std::shared_ptr<const Telegram> telegram) {
|
void Thermostat::process_RC300Summer2(std::shared_ptr<const Telegram> telegram) {
|
||||||
auto hc = heating_circuit(telegram);
|
auto hc = heating_circuit(telegram);
|
||||||
if (hc == nullptr) {
|
if (hc == nullptr) {
|
||||||
// telegram 0x470 see https://github.com/emsesp/EMS-ESP32/issues/2686
|
|
||||||
if (telegram->type_id == 0x470 && telegram->message_length > 2) {
|
|
||||||
hc = heating_circuit(1);
|
|
||||||
summer2_typeids[0] = 0x470;
|
|
||||||
} else {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (hc->statusbyte & 1) {
|
if (hc->statusbyte & 1) {
|
||||||
has_update(telegram, hc->summersetmode, 0);
|
has_update(telegram, hc->summersetmode, 0);
|
||||||
has_update(hc->hpoperatingmode, EMS_VALUE_UINT8_NOTSET);
|
has_update(hc->hpoperatingmode, EMS_VALUE_UINT8_NOTSET);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.8.2-dev.22"
|
#define EMSESP_APP_VERSION "3.9.0-dev.0"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
|
|||||||
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
|
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
|
||||||
|
|
||||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
|
|
||||||
// Thermostat RCPLUSStatusMessage_HC1(0x01A5)
|
// Thermostat RCPLUSStatusMessage_HC1(0x01A5)
|
||||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
||||||
@@ -89,7 +89,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
|
|||||||
|
|
||||||
add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170
|
add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170
|
||||||
|
|
||||||
// [emsesp] boiler(0x08) -W-> Me(0x49), UBAMonitorFastPlus(0xE4), data: 00 01 35 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 80 00 (offset 6)
|
// [emsesp] boiler(0x08) -W-> Me(0x0B), UBAMonitorFastPlus(0xE4), data: 00 01 35 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 80 00 (offset 6)
|
||||||
uart_telegram({0x08, 0x00, 0xE4, 0x00, //
|
uart_telegram({0x08, 0x00, 0xE4, 0x00, //
|
||||||
00, 01, 0x35, 00, 00, 00, 00, 00, 00, 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x80, 00});
|
00, 01, 0x35, 00, 00, 00, 00, 00, 00, 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x80, 00});
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
|
|||||||
uart_telegram({0x08, 0x90, 0x33, 0x00, 0x23, 0x24});
|
uart_telegram({0x08, 0x90, 0x33, 0x00, 0x23, 0x24});
|
||||||
|
|
||||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
|
|
||||||
// Thermostat 0x2A5 for HC1
|
// Thermostat 0x2A5 for HC1
|
||||||
uart_telegram({0x10, 00, 0xFF, 00, 01, 0xA5, 0x80, 00, 01, 0x30, 0x28, 00, 0x30, 0x28, 01, 0x54,
|
uart_telegram({0x10, 00, 0xFF, 00, 01, 0xA5, 0x80, 00, 01, 0x30, 0x28, 00, 0x30, 0x28, 01, 0x54,
|
||||||
@@ -144,7 +144,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
|
|||||||
uart_telegram({0x08, 0x90, 0x33, 0x00, 0x23, 0x24});
|
uart_telegram({0x08, 0x90, 0x33, 0x00, 0x23, 0x24});
|
||||||
|
|
||||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
|
|
||||||
// Thermostat 0x2A5 for HC1
|
// Thermostat 0x2A5 for HC1
|
||||||
uart_telegram({0x10, 00, 0xFF, 00, 01, 0xA5, 0x80, 00, 01, 0x30, 0x28, 00, 0x30, 0x28, 01, 0x54,
|
uart_telegram({0x10, 00, 0xFF, 00, 01, 0xA5, 0x80, 00, 01, 0x30, 0x28, 00, 0x30, 0x28, 01, 0x54,
|
||||||
@@ -160,7 +160,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
|
|||||||
EMSESP::logger().notice("Adding a Gateway...");
|
EMSESP::logger().notice("Adding a Gateway...");
|
||||||
|
|
||||||
// add 0x48 KM200, via a version command
|
// add 0x48 KM200, via a version command
|
||||||
rx_telegram({0x48, 0x49, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00});
|
rx_telegram({0x48, 0x0B, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00});
|
||||||
|
|
||||||
// Boiler(0x08) -> All(0x00), UBADevices(0x07), data: 09 01 00 00 00 00 00 00 01 00 00 00 00
|
// Boiler(0x08) -> All(0x00), UBADevices(0x07), data: 09 01 00 00 00 00 00 00 01 00 00 00 00
|
||||||
// check: make sure 0x48 is not detected again !
|
// check: make sure 0x48 is not detected again !
|
||||||
@@ -203,14 +203,14 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
|
|||||||
add_device(0x08, 123); // Nefit Trendline
|
add_device(0x08, 123); // Nefit Trendline
|
||||||
|
|
||||||
// UBAuptime
|
// UBAuptime
|
||||||
uart_telegram({0x08, 0x49, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||||
|
|
||||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||||
|
|
||||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -234,10 +234,10 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
|
|||||||
add_device(0x30, 163); // SM100
|
add_device(0x30, 163); // SM100
|
||||||
|
|
||||||
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
||||||
uart_telegram({0xB0, 0x49, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
uart_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||||
|
|
||||||
uart_telegram({0xB0, 0x49, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
uart_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
||||||
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||||
|
|
||||||
uart_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
uart_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
||||||
@@ -580,7 +580,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
add_device(0x09, 206); // Nefit Excellent HR30 Controller
|
add_device(0x09, 206); // Nefit Excellent HR30 Controller
|
||||||
|
|
||||||
// UBAuptime
|
// UBAuptime
|
||||||
uart_telegram({0x08, 0x49, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,10 +588,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
EMSESP::logger().notice("Testing 620...");
|
EMSESP::logger().notice("Testing 620...");
|
||||||
|
|
||||||
// Version Controller
|
// Version Controller
|
||||||
uart_telegram({0x09, 0x49, 0x02, 0x00, 0x5F, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
uart_telegram({0x09, 0x0B, 0x02, 0x00, 0x5F, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||||
|
|
||||||
// Version Boiler
|
// Version Boiler
|
||||||
uart_telegram({0x08, 0x49, 0x02, 0x00, 0x5F, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
uart_telegram({0x08, 0x0B, 0x02, 0x00, 0x5F, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,7 +607,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
|
|
||||||
// simulate getting version information back from an unknown device
|
// simulate getting version information back from an unknown device
|
||||||
// note there is no brand (byte 9)
|
// note there is no brand (byte 9)
|
||||||
rx_telegram({0x09, 0x49, 0x02, 0x00, 0x59, 0x09, 0x0a});
|
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x59, 0x09, 0x0a});
|
||||||
|
|
||||||
shell.invoke_command("show devices");
|
shell.invoke_command("show devices");
|
||||||
shell.invoke_command("call system report");
|
shell.invoke_command("call system report");
|
||||||
@@ -618,7 +618,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
shell.printfln("Testing unknown2...");
|
shell.printfln("Testing unknown2...");
|
||||||
|
|
||||||
// simulate getting version information back from an unknown device
|
// simulate getting version information back from an unknown device
|
||||||
rx_telegram({0x09, 0x49, 0x02, 0x00, 0x5A, 0x01, 0x02}); // productID is 90 which doesn't exist
|
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x5A, 0x01, 0x02}); // productID is 90 which doesn't exist
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,9 +815,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
// test("thermostat");
|
// test("thermostat");
|
||||||
|
|
||||||
// 0xC2
|
// 0xC2
|
||||||
// [emsesp] Boiler(0x08) -> Me(0x49), UBAErrorMessage3(0xC2), data: 08 AC 00 10 31 48 30 31 15 80 95 0B 0E 10 38 00 7F FF FF FF 08 AC 00 10 09 41 30
|
// [emsesp] Boiler(0x08) -> Me(0x0B), UBAErrorMessage3(0xC2), data: 08 AC 00 10 31 48 30 31 15 80 95 0B 0E 10 38 00 7F FF FF FF 08 AC 00 10 09 41 30
|
||||||
uart_telegram(
|
uart_telegram(
|
||||||
{0x08, 0x49, 0xC2, 0, 0x08, 0xAC, 00, 0x10, 0x31, 0x48, 0x30, 0x31, 0x15, 0x80, 0x95, 0x0B, 0x0E, 0x10, 0x38, 00, 0x7F, 0xFF, 0xFF, 0xFF});
|
{0x08, 0x0B, 0xC2, 0, 0x08, 0xAC, 00, 0x10, 0x31, 0x48, 0x30, 0x31, 0x15, 0x80, 0x95, 0x0B, 0x0E, 0x10, 0x38, 00, 0x7F, 0xFF, 0xFF, 0xFF});
|
||||||
|
|
||||||
// shell.invoke_command("show values");
|
// shell.invoke_command("show values");
|
||||||
|
|
||||||
@@ -1042,7 +1042,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
// Boiler -> Me, UBAParameterWW(0x33)
|
// Boiler -> Me, UBAParameterWW(0x33)
|
||||||
// wwseltemp = goes from 52 degrees (0x34) to void (0xFF)
|
// wwseltemp = goes from 52 degrees (0x34) to void (0xFF)
|
||||||
// it should delete the HA config topic homeassistant/sensor/ems-esp/boiler_wwseltemp/config
|
// it should delete the HA config topic homeassistant/sensor/ems-esp/boiler_wwseltemp/config
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0xFF, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0xFF, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
|
|
||||||
shell.invoke_command("call boiler wwseltemp");
|
shell.invoke_command("call boiler wwseltemp");
|
||||||
shell.invoke_command("call system publish");
|
shell.invoke_command("call system publish");
|
||||||
@@ -1795,7 +1795,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
add_device(0x18, 202); // Bosch TC100 - https://github.com/emsesp/EMS-ESP/issues/474
|
add_device(0x18, 202); // Bosch TC100 - https://github.com/emsesp/EMS-ESP/issues/474
|
||||||
|
|
||||||
// 0x0A
|
// 0x0A
|
||||||
uart_telegram({0x98, 0x49, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
uart_telegram({0x98, 0x0B, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
@@ -1829,10 +1829,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
|
|
||||||
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
||||||
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
||||||
rx_telegram({0xB0, 0x49, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||||
|
|
||||||
rx_telegram({0xB0, 0x49, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
||||||
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||||
|
|
||||||
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
||||||
@@ -1936,7 +1936,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
if (command == "rx2") {
|
if (command == "rx2") {
|
||||||
shell.printfln("Testing Rx2...");
|
shell.printfln("Testing Rx2...");
|
||||||
for (uint8_t i = 0; i < 30; i++) {
|
for (uint8_t i = 0; i < 30; i++) {
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1953,23 +1953,23 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
uart_telegram({0x08, 0x97, 0x33, 0x00, 0x23, 0x24});
|
uart_telegram({0x08, 0x97, 0x33, 0x00, 0x23, 0x24});
|
||||||
|
|
||||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
|
|
||||||
// Thermostat -> Me, RC20StatusMessage(0x91), telegram: 17 0B 91 05 44 45 46 47 (#data=4)
|
// Thermostat -> Me, RC20StatusMessage(0x91), telegram: 17 0B 91 05 44 45 46 47 (#data=4)
|
||||||
uart_telegram({0x17, 0x49, 0x91, 0x05, 0x44, 0x45, 0x46, 0x47});
|
uart_telegram({0x17, 0x0B, 0x91, 0x05, 0x44, 0x45, 0x46, 0x47});
|
||||||
|
|
||||||
// bad CRC - corrupt telegram - CRC should be 0x8E
|
// bad CRC - corrupt telegram - CRC should be 0x8E
|
||||||
uint8_t t5[] = {0x17, 0x49, 0x91, 0x05, 0x44, 0x45, 0x46, 0x47, 0x99};
|
uint8_t t5[] = {0x17, 0x0B, 0x91, 0x05, 0x44, 0x45, 0x46, 0x47, 0x99};
|
||||||
EMSESP::rxservice_.add(t5, sizeof(t5));
|
EMSESP::rxservice_.add(t5, sizeof(t5));
|
||||||
|
|
||||||
// simulating a Tx record
|
// simulating a Tx record
|
||||||
uart_telegram({0x49, 0x88, 0x07, 0x00, 0x20});
|
uart_telegram({0x0B, 0x88, 0x07, 0x00, 0x20});
|
||||||
|
|
||||||
// Version Boiler
|
// Version Boiler
|
||||||
uart_telegram({0x08, 0x49, 0x02, 0x00, 0x7B, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04});
|
uart_telegram({0x08, 0x0B, 0x02, 0x00, 0x7B, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04});
|
||||||
|
|
||||||
// Version Thermostat, device_id 0x11
|
// Version Thermostat, device_id 0x11
|
||||||
uart_telegram({0x11, 0x49, 0x02, 0x00, 0x4D, 0x03, 0x03});
|
uart_telegram({0x11, 0x0B, 0x02, 0x00, 0x4D, 0x03, 0x03});
|
||||||
|
|
||||||
// Thermostat -> all, telegram: 10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00
|
// Thermostat -> all, telegram: 10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00
|
||||||
// 0x1A5 test ems+
|
// 0x1A5 test ems+
|
||||||
@@ -1987,7 +1987,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
uart_telegram({0x17, 0x08, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00});
|
uart_telegram({0x17, 0x08, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||||
|
|
||||||
// Thermostat -> Me, RC20Set(0xA8), telegram: 17 0B A8 00 01 00 FF F6 01 06 00 01 0D 01 00 FF FF 01 02 02 02 00 00 05 1F 05 1F 02 0E 00 FF (#data=27)
|
// Thermostat -> Me, RC20Set(0xA8), telegram: 17 0B A8 00 01 00 FF F6 01 06 00 01 0D 01 00 FF FF 01 02 02 02 00 00 05 1F 05 1F 02 0E 00 FF (#data=27)
|
||||||
uart_telegram({0x17, 0x49, 0xA8, 0x00, 0x01, 0x00, 0xFF, 0xF6, 0x01, 0x06, 0x00, 0x01, 0x0D, 0x01, 0x00, 0xFF,
|
uart_telegram({0x17, 0x0B, 0xA8, 0x00, 0x01, 0x00, 0xFF, 0xF6, 0x01, 0x06, 0x00, 0x01, 0x0D, 0x01, 0x00, 0xFF,
|
||||||
0xFF, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x05, 0x1F, 0x05, 0x1F, 0x02, 0x0E, 0x00, 0xFF});
|
0xFF, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x05, 0x1F, 0x05, 0x1F, 0x02, 0x0E, 0x00, 0xFF});
|
||||||
|
|
||||||
// Boiler(0x08) -> All(0x00), UBAMonitorWW(0x34), data: 36 01 A5 80 00 21 00 00 01 00 01 3E 8D 03 77 91 00 80 00
|
// Boiler(0x08) -> All(0x00), UBAMonitorWW(0x34), data: 36 01 A5 80 00 21 00 00 01 00 01 3E 8D 03 77 91 00 80 00
|
||||||
@@ -2021,7 +2021,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
EMSESP::send_write_request(0x91, 0x17, 0x00, t18, sizeof(t18), 0x00);
|
EMSESP::send_write_request(0x91, 0x17, 0x00, t18, sizeof(t18), 0x00);
|
||||||
|
|
||||||
// TX - send EMS+
|
// TX - send EMS+
|
||||||
const uint8_t t13[] = {0x90, 0x49, 0xFF, 00, 01, 0xBA, 00, 0x2E, 0x2A, 0x26, 0x1E, 0x03,
|
const uint8_t t13[] = {0x90, 0x0B, 0xFF, 00, 01, 0xBA, 00, 0x2E, 0x2A, 0x26, 0x1E, 0x03,
|
||||||
00, 0xFF, 0xFF, 05, 0x2A, 01, 0xE1, 0x20, 0x01, 0x0F, 05, 0x2A};
|
00, 0xFF, 0xFF, 05, 0x2A, 01, 0xE1, 0x20, 0x01, 0x0F, 05, 0x2A};
|
||||||
EMSESP::txservice_.add(Telegram::Operation::TX_RAW, t13, sizeof(t13), 0);
|
EMSESP::txservice_.add(Telegram::Operation::TX_RAW, t13, sizeof(t13), 0);
|
||||||
|
|
||||||
@@ -2145,7 +2145,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
|
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
|
||||||
|
|
||||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
|
|
||||||
// add a thermostat
|
// add a thermostat
|
||||||
add_device(0x18, 157); // Bosch CR100
|
add_device(0x18, 157); // Bosch CR100
|
||||||
@@ -2487,7 +2487,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
if (command == "rx3") {
|
if (command == "rx3") {
|
||||||
shell.printfln("Testing rx3...");
|
shell.printfln("Testing rx3...");
|
||||||
|
|
||||||
uart_telegram({0x21, 0x49, 0xFF, 0x00});
|
uart_telegram({0x21, 0x0B, 0xFF, 0x00});
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2495,7 +2495,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
|||||||
if (command == "tx2") {
|
if (command == "tx2") {
|
||||||
shell.printfln("Testing tx2...");
|
shell.printfln("Testing tx2...");
|
||||||
|
|
||||||
uint8_t t[] = {0x49, 0x88, 0x18, 0x00, 0x20, 0xD4}; // including CRC
|
uint8_t t[] = {0x0B, 0x88, 0x18, 0x00, 0x20, 0xD4}; // including CRC
|
||||||
EMSuart::transmit(t, sizeof(t));
|
EMSuart::transmit(t, sizeof(t));
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ uint32_t inverse_mask = 0;
|
|||||||
// receive task, wait for break and call incoming_telegram
|
// receive task, wait for break and call incoming_telegram
|
||||||
void EMSuart::uart_event_task(void * pvParameters) {
|
void EMSuart::uart_event_task(void * pvParameters) {
|
||||||
uart_event_t event;
|
uart_event_t event;
|
||||||
uint8_t telegram[UART_FIFO_LEN + 1]; // same size as in driver_install
|
uint8_t telegram[UART_HW_FIFO_LEN(EMSUART_NUM) + 1]; // same size as in driver_install
|
||||||
uint8_t length = 0;
|
uint8_t length = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -66,18 +66,23 @@ void EMSuart::uart_event_task(void * pvParameters) {
|
|||||||
// initialize UART driver
|
// initialize UART driver
|
||||||
void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t tx_gpio) {
|
void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t tx_gpio) {
|
||||||
if (tx_mode_ == EMS_TXMODE_INIT) {
|
if (tx_mode_ == EMS_TXMODE_INIT) {
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C6
|
||||||
|
uart_config_t uart_config = {.baud_rate = EMSUART_BAUD,
|
||||||
|
.data_bits = UART_DATA_8_BITS,
|
||||||
|
.parity = UART_PARITY_DISABLE,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||||
|
.rx_flow_ctrl_thresh = 0};
|
||||||
|
#else
|
||||||
uart_config_t uart_config = {.baud_rate = EMSUART_BAUD,
|
uart_config_t uart_config = {.baud_rate = EMSUART_BAUD,
|
||||||
.data_bits = UART_DATA_8_BITS,
|
.data_bits = UART_DATA_8_BITS,
|
||||||
.parity = UART_PARITY_DISABLE,
|
.parity = UART_PARITY_DISABLE,
|
||||||
.stop_bits = UART_STOP_BITS_1,
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||||
.rx_flow_ctrl_thresh = 0,
|
.rx_flow_ctrl_thresh = 0,
|
||||||
.source_clk = UART_SCLK_APB
|
.source_clk = UART_SCLK_APB,
|
||||||
#if ESP_ARDUINO_VERSION_MAJOR >= 3
|
.flags = {0}};
|
||||||
,
|
|
||||||
.flags = {0}
|
|
||||||
#endif
|
#endif
|
||||||
};
|
|
||||||
#if defined(EMSUART_RX_INVERT)
|
#if defined(EMSUART_RX_INVERT)
|
||||||
inverse_mask |= UART_SIGNAL_RXD_INV;
|
inverse_mask |= UART_SIGNAL_RXD_INV;
|
||||||
#endif
|
#endif
|
||||||
@@ -87,7 +92,12 @@ void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t
|
|||||||
uart_param_config(EMSUART_NUM, &uart_config);
|
uart_param_config(EMSUART_NUM, &uart_config);
|
||||||
uart_set_pin(EMSUART_NUM, tx_gpio, rx_gpio, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
uart_set_pin(EMSUART_NUM, tx_gpio, rx_gpio, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||||
uart_set_line_inverse(EMSUART_NUM, inverse_mask);
|
uart_set_line_inverse(EMSUART_NUM, inverse_mask);
|
||||||
uart_driver_install(EMSUART_NUM, UART_FIFO_LEN + 1, 0, UART_FIFO_LEN + 3, &uart_queue, 0); // buffer must be > fifo, queue can hold data+break+overflow message
|
uart_driver_install(EMSUART_NUM,
|
||||||
|
UART_HW_FIFO_LEN(EMSUART_NUM) + 1,
|
||||||
|
0,
|
||||||
|
UART_HW_FIFO_LEN(EMSUART_NUM) + 3,
|
||||||
|
&uart_queue,
|
||||||
|
0); // buffer must be > fifo, queue can hold data+break+overflow message
|
||||||
uart_set_rx_full_threshold(EMSUART_NUM, 1);
|
uart_set_rx_full_threshold(EMSUART_NUM, 1);
|
||||||
uart_set_rx_timeout(EMSUART_NUM, 0); // disable
|
uart_set_rx_timeout(EMSUART_NUM, 0); // disable
|
||||||
|
|
||||||
@@ -149,10 +159,8 @@ uint8_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
|||||||
return EMS_TX_STATUS_OK;
|
return EMS_TX_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tx_mode = tx_mode_ != EMS_TXMODE_AUTO ? tx_mode_ : EMSbus::is_ht3() ? EMS_TXMODE_HT3 : EMSbus::is_ems2() ? EMS_TXMODE_EMSPLUS : EMS_TXMODE_EMS;
|
|
||||||
|
|
||||||
// TXMODE is EMS+ with long delay
|
// TXMODE is EMS+ with long delay
|
||||||
if (tx_mode == EMS_TXMODE_EMSPLUS) {
|
if (tx_mode_ == EMS_TXMODE_EMSPLUS) {
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
uart_write_bytes(EMSUART_NUM, &buf[i], 1);
|
uart_write_bytes(EMSUART_NUM, &buf[i], 1);
|
||||||
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
|
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
|
||||||
@@ -162,7 +170,7 @@ uint8_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TXMODE is HT3 with 7 bittimes delay
|
// TXMODE is HT3 with 7 bittimes delay
|
||||||
if (tx_mode == EMS_TXMODE_HT3) {
|
if (tx_mode_ == EMS_TXMODE_HT3) {
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
uart_write_bytes(EMSUART_NUM, &buf[i], 1);
|
uart_write_bytes(EMSUART_NUM, &buf[i], 1);
|
||||||
delayMicroseconds(EMSUART_TX_WAIT_HT3);
|
delayMicroseconds(EMSUART_TX_WAIT_HT3);
|
||||||
|
|||||||
@@ -42,11 +42,10 @@
|
|||||||
|
|
||||||
#define EMS_TXMODE_INIT 0xFF
|
#define EMS_TXMODE_INIT 0xFF
|
||||||
#define EMS_TXMODE_OFF 0
|
#define EMS_TXMODE_OFF 0
|
||||||
#define EMS_TXMODE_EMS 1
|
#define EMS_TXMODE_DEFAULT 1
|
||||||
#define EMS_TXMODE_EMSPLUS 2
|
#define EMS_TXMODE_EMSPLUS 2
|
||||||
#define EMS_TXMODE_HT3 3
|
#define EMS_TXMODE_HT3 3
|
||||||
#define EMS_TXMODE_HW 4
|
#define EMS_TXMODE_HW 4
|
||||||
#define EMS_TXMODE_AUTO 5
|
|
||||||
|
|
||||||
// LEGACY
|
// LEGACY
|
||||||
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud
|
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud
|
||||||
|
|||||||
@@ -166,9 +166,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
|||||||
bool WebCustomEntityService::command_setvalue(const char * value, const int8_t id, const char * name) {
|
bool WebCustomEntityService::command_setvalue(const char * value, const int8_t id, const char * name) {
|
||||||
// don't write if there is no value, to prevent setting an empty value by mistake when parsing attributes
|
// don't write if there is no value, to prevent setting an empty value by mistake when parsing attributes
|
||||||
if (!strlen(value)) {
|
if (!strlen(value)) {
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
EMSESP::logger().debug("can't set empty value!");
|
EMSESP::logger().debug("can't set empty value!");
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,7 +686,7 @@ void WebCustomEntityService::fetch() {
|
|||||||
uint8_t stop = (entity.offset + len[entity.value_type]) % fetchblock;
|
uint8_t stop = (entity.offset + len[entity.value_type]) % fetchblock;
|
||||||
bool is_fetched = start < fetchblock && stop < fetchblock; // make sure the complete value is a a fetched block
|
bool is_fetched = start < fetchblock && stop < fetchblock; // make sure the complete value is a a fetched block
|
||||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
if (emsdevice->is_device_id(entity.device_id) && emsdevice->is_fetch(entity.type_id, entity.offset + len[entity.value_type])
|
if (emsdevice->is_device_id(entity.device_id) && emsdevice->is_fetch(entity.type_id)
|
||||||
&& (is_fetched || entity.value_type == DeviceValueType::STRING)) {
|
&& (is_fetched || entity.value_type == DeviceValueType::STRING)) {
|
||||||
needFetch = false;
|
needFetch = false;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject root)
|
|||||||
JsonArray sensorsJson = root["ts"].to<JsonArray>();
|
JsonArray sensorsJson = root["ts"].to<JsonArray>();
|
||||||
for (const SensorCustomization & sensor : customizations.sensorCustomizations) {
|
for (const SensorCustomization & sensor : customizations.sensorCustomizations) {
|
||||||
JsonObject sensorJson = sensorsJson.add<JsonObject>();
|
JsonObject sensorJson = sensorsJson.add<JsonObject>();
|
||||||
sensorJson["id"] = (const char *)sensor.id; // ID of chip
|
sensorJson["id"] = (const char *)sensor.id; // ID of dallas temperature sensor chip
|
||||||
sensorJson["name"] = (const char *)sensor.name; // n
|
sensorJson["name"] = (const char *)sensor.name; // n
|
||||||
sensorJson["offset"] = sensor.offset; // o
|
sensorJson["offset"] = sensor.offset; // o
|
||||||
sensorJson["is_system"] = sensor.is_system; // s for core_voltage, supply_voltage
|
sensorJson["is_system"] = sensor.is_system; // s for gateway_temperature
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analog Sensor customization
|
// Analog Sensor customization
|
||||||
|
|||||||
@@ -352,7 +352,6 @@ bool WebSchedulerService::command(const char * name, const std::string & command
|
|||||||
// parse json
|
// parse json
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
if (deserializeJson(doc, cmd) == DeserializationError::Ok) {
|
if (deserializeJson(doc, cmd) == DeserializationError::Ok) {
|
||||||
HTTPClient * http = new HTTPClient;
|
|
||||||
std::string url = doc["url"] | "";
|
std::string url = doc["url"] | "";
|
||||||
// for a GET with parameters replace commands with values
|
// for a GET with parameters replace commands with values
|
||||||
// don't search the complete url, it may contain a devicename in path
|
// don't search the complete url, it may contain a devicename in path
|
||||||
@@ -363,40 +362,139 @@ bool WebSchedulerService::command(const char * name, const std::string & command
|
|||||||
commands(s, false);
|
commands(s, false);
|
||||||
url.replace(q + 1, l, s);
|
url.replace(q + 1, l, s);
|
||||||
}
|
}
|
||||||
if (http->begin(url.c_str())) {
|
std::string value = doc["value"] | data; // extract value if its in the command, or take the data
|
||||||
// add any given headers
|
|
||||||
for (JsonPair p : doc["header"].as<JsonObject>()) {
|
|
||||||
http->addHeader(p.key().c_str(), p.value().as<String>().c_str());
|
|
||||||
}
|
|
||||||
std::string value = doc["value"] | data.c_str(); // extract value if its in the command, or take the data
|
|
||||||
std::string method = doc["method"] | "GET"; // default GET
|
std::string method = doc["method"] | "GET"; // default GET
|
||||||
|
|
||||||
commands(value, false);
|
commands(value, false);
|
||||||
// if there is data, force a POST
|
if (value.length()) {
|
||||||
|
method = "POST";
|
||||||
|
}
|
||||||
|
std::string result;
|
||||||
int httpResult = 0;
|
int httpResult = 0;
|
||||||
if (value.length() || method == "post") { // we have all lowercase
|
#ifndef NO_TLS_SUPPORT
|
||||||
if (value.find_first_of('{') != std::string::npos) {
|
if (Helpers::toLower(url.c_str()).starts_with("https://")) {
|
||||||
|
WiFiClient * basic_client = new WiFiClient;
|
||||||
|
ESP_SSLClient * ssl_client = new ESP_SSLClient;
|
||||||
|
ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert);
|
||||||
|
ssl_client->setBufferSizes(1024, 1024);
|
||||||
|
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
|
||||||
|
url.replace(0, 8, "");
|
||||||
|
std::string host = url;
|
||||||
|
auto index = url.find_first_of('/');
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
host = url.substr(0, index);
|
||||||
|
url.replace(0, index, "");
|
||||||
|
}
|
||||||
|
// EMSESP::logger().debug("Host: %s, URL: %s", host.c_str(), url.c_str());
|
||||||
|
ssl_client->setClient(basic_client);
|
||||||
|
if (ssl_client->connect(host.c_str(), 443)) {
|
||||||
|
if (value.length() || Helpers::toLower(method) == "post") {
|
||||||
|
// EMSESP::logger().debug("POST %s HTTP/1.1", url.c_str());
|
||||||
|
ssl_client->print("POST ");
|
||||||
|
ssl_client->print(url.c_str());
|
||||||
|
ssl_client->println(" HTTP/1.1");
|
||||||
|
ssl_client->print("Host: ");
|
||||||
|
ssl_client->println(host.c_str());
|
||||||
|
bool content_set = false;
|
||||||
|
for (JsonPair p : doc["header"].as<JsonObject>()) {
|
||||||
|
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
|
||||||
|
ssl_client->print(p.key().c_str());
|
||||||
|
ssl_client->print(": ");
|
||||||
|
ssl_client->println(p.value().as<std::string>().c_str());
|
||||||
|
}
|
||||||
|
if (!content_set) {
|
||||||
|
ssl_client->print("Content-Type: ");
|
||||||
|
if (value.starts_with('{')) {
|
||||||
|
ssl_client->println(asyncsrv::T_application_json);
|
||||||
|
} else {
|
||||||
|
ssl_client->println(asyncsrv::T_text_plain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ssl_client->print("Content-Length: ");
|
||||||
|
ssl_client->println(value.length());
|
||||||
|
ssl_client->println("Connection: close");
|
||||||
|
ssl_client->print("\r\n");
|
||||||
|
ssl_client->print(value.c_str());
|
||||||
|
} else {
|
||||||
|
// EMSESP::logger().debug("GET %s HTTP/1.1", url.c_str());
|
||||||
|
ssl_client->print("GET ");
|
||||||
|
ssl_client->print(url.c_str());
|
||||||
|
ssl_client->println(" HTTP/1.1");
|
||||||
|
ssl_client->print("Host: ");
|
||||||
|
ssl_client->println(host.c_str());
|
||||||
|
for (JsonPair p : doc["header"].as<JsonObject>()) {
|
||||||
|
ssl_client->print(p.key().c_str());
|
||||||
|
ssl_client->print(": ");
|
||||||
|
ssl_client->println(p.value().as<std::string>().c_str());
|
||||||
|
}
|
||||||
|
ssl_client->println("Connection: close");
|
||||||
|
}
|
||||||
|
auto ms = millis();
|
||||||
|
while (ssl_client->connected() && !ssl_client->available() && millis() - ms < 3000) {
|
||||||
|
delay(0);
|
||||||
|
}
|
||||||
|
while (ssl_client->available()) {
|
||||||
|
result += (char)ssl_client->read();
|
||||||
|
}
|
||||||
|
ssl_client->stop();
|
||||||
|
// EMSESP::logger().debug("HTTPS response: %s", result.c_str());
|
||||||
|
index = result.find_first_of(' ');
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
httpResult = stoi(result.substr(index + 1, 3));
|
||||||
|
// EMSESP::logger().debug("HTTPS code: %i", httpResult);
|
||||||
|
}
|
||||||
|
index = result.find("\r\n\r\n");
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
result.replace(0, index + 4, "");
|
||||||
|
// EMSESP::logger().debug("HTTPS response: %s", result.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EMSESP::logger().warning("HTTPS connection failed");
|
||||||
|
}
|
||||||
|
delete ssl_client;
|
||||||
|
delete basic_client;
|
||||||
|
// check HTTP return code
|
||||||
|
if (httpResult != 200) {
|
||||||
|
EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
if (Helpers::toLower(url.c_str()).starts_with("http://")) {
|
||||||
|
HTTPClient * http = new HTTPClient;
|
||||||
|
if (http->begin(url.c_str())) {
|
||||||
|
bool content_set = false;
|
||||||
|
for (JsonPair p : doc["header"].as<JsonObject>()) {
|
||||||
|
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
|
||||||
|
content_set |= p.key() == "content-type";
|
||||||
|
}
|
||||||
|
// if there is data, force a POST
|
||||||
|
if (Helpers::toLower(method) == "post") { // we have all lowercase
|
||||||
|
if (!content_set) {
|
||||||
|
// http->addHeader("Content-Type", value.find_first_of('{') != std::string::npos ? "application/json" : "text/plain");
|
||||||
|
if (value.starts_with('{')) {
|
||||||
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
|
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
|
||||||
|
} else {
|
||||||
|
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_text_plain, false); // auto-set to JSON
|
||||||
|
}
|
||||||
}
|
}
|
||||||
httpResult = http->POST(value.c_str());
|
httpResult = http->POST(value.c_str());
|
||||||
} else {
|
} else {
|
||||||
httpResult = http->GET(); // normal GET
|
httpResult = http->GET(); // normal GET
|
||||||
|
if (httpResult > 0) {
|
||||||
|
result = http->getString().c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http->end();
|
http->end();
|
||||||
delete http;
|
delete http;
|
||||||
|
|
||||||
// check HTTP return code
|
// check HTTP return code
|
||||||
if (httpResult != 200) {
|
if (httpResult != 200) {
|
||||||
char error[100];
|
EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult);
|
||||||
snprintf(error, sizeof(error), "Schedule %s: URL command failed with http code %d", name, httpResult);
|
|
||||||
EMSESP::logger().warning(error);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
char msg[100];
|
EMSESP::logger().debug("Schedule %s: URL '%s' command successful with http code %d", name, url.c_str(), httpResult);
|
||||||
snprintf(msg, sizeof(msg), "Schedule %s: URL command successful with http code %d", name, httpResult);
|
|
||||||
EMSESP::logger().debug(msg);
|
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -465,9 +563,7 @@ void WebSchedulerService::condition() {
|
|||||||
} else if (match.length() == 1 && match[0] == '0' && scheduleItem.retry_cnt == 1) {
|
} else if (match.length() == 1 && match[0] == '0' && scheduleItem.retry_cnt == 1) {
|
||||||
scheduleItem.retry_cnt = 0xFF;
|
scheduleItem.retry_cnt = 0xFF;
|
||||||
} else if (match.length() != 1) { // the match is not boolean
|
} else if (match.length() != 1) { // the match is not boolean
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
EMSESP::logger().debug("condition result: %s", match.c_str());
|
EMSESP::logger().debug("condition result: %s", match.c_str());
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
uint16_t WebSettings::flags_ = 0;
|
uint8_t WebSettings::flags_ = 0;
|
||||||
|
|
||||||
WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||||
: _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
|
: _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
|
||||||
@@ -83,6 +83,20 @@ void WebSettings::read(WebSettings & settings, JsonObject root) {
|
|||||||
root["modbus_max_clients"] = settings.modbus_max_clients;
|
root["modbus_max_clients"] = settings.modbus_max_clients;
|
||||||
root["modbus_timeout"] = settings.modbus_timeout;
|
root["modbus_timeout"] = settings.modbus_timeout;
|
||||||
root["developer_mode"] = settings.developer_mode;
|
root["developer_mode"] = settings.developer_mode;
|
||||||
|
#ifndef NO_TLS_SUPPORT
|
||||||
|
root["email_enabled"] = settings.email_enabled;
|
||||||
|
#else
|
||||||
|
root["email_enabled"] = false;
|
||||||
|
#endif
|
||||||
|
root["email_ssl"] = settings.email_ssl;
|
||||||
|
root["email_starttls"] = settings.email_starttls;
|
||||||
|
root["email_server"] = settings.email_server;
|
||||||
|
root["email_port"] = settings.email_port;
|
||||||
|
root["email_login"] = settings.email_login;
|
||||||
|
root["email_pass"] = settings.email_pass;
|
||||||
|
root["email_sender"] = settings.email_sender;
|
||||||
|
root["email_recp"] = settings.email_recp;
|
||||||
|
root["email_subject"] = settings.email_subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
// call on initialization and also when settings are updated/saved via web or console
|
// call on initialization and also when settings are updated/saved via web or console
|
||||||
@@ -114,11 +128,11 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
reset_flags();
|
reset_flags();
|
||||||
|
|
||||||
// before loading new board profile free old gpios from used list to allow remapping
|
// before loading new board profile free old gpios from used list to allow remapping
|
||||||
EMSESP::system_.remove_optional_gpio(original_settings.led_gpio);
|
EMSESP::system_.remove_gpio(original_settings.led_gpio);
|
||||||
EMSESP::system_.remove_optional_gpio(original_settings.dallas_gpio);
|
EMSESP::system_.remove_gpio(original_settings.dallas_gpio);
|
||||||
EMSESP::system_.remove_gpio(original_settings.pbutton_gpio);
|
EMSESP::system_.remove_gpio(original_settings.pbutton_gpio);
|
||||||
EMSESP::system_.remove_optional_gpio(original_settings.rx_gpio);
|
EMSESP::system_.remove_gpio(original_settings.rx_gpio);
|
||||||
EMSESP::system_.remove_optional_gpio(original_settings.tx_gpio);
|
EMSESP::system_.remove_gpio(original_settings.tx_gpio);
|
||||||
|
|
||||||
// see if the user has changed the board profile
|
// see if the user has changed the board profile
|
||||||
// this will set: led_gpio, dallas_gpio, rx_gpio, tx_gpio, pbutton_gpio, phy_type, eth_power, eth_phy_addr, eth_clock_mode, led_type
|
// this will set: led_gpio, dallas_gpio, rx_gpio, tx_gpio, pbutton_gpio, phy_type, eth_power, eth_phy_addr, eth_clock_mode, led_type
|
||||||
@@ -243,13 +257,9 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
|
|
||||||
// Modbus settings
|
// Modbus settings
|
||||||
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
|
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
|
||||||
check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::MODBUS);
|
|
||||||
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
|
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
|
||||||
check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::MODBUS);
|
|
||||||
settings.modbus_max_clients = root["modbus_max_clients"] | EMSESP_DEFAULT_MODBUS_MAX_CLIENTS;
|
settings.modbus_max_clients = root["modbus_max_clients"] | EMSESP_DEFAULT_MODBUS_MAX_CLIENTS;
|
||||||
check_flag(original_settings.modbus_max_clients, settings.modbus_max_clients, ChangeFlags::MODBUS);
|
|
||||||
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
|
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
|
||||||
check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::MODBUS);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// these may need mqtt restart to rebuild HA discovery topics
|
// these may need mqtt restart to rebuild HA discovery topics
|
||||||
@@ -300,6 +310,20 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
||||||
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
||||||
|
|
||||||
|
settings.email_enabled = root["email_enabled"] | FACTORY_EMAIL_ENABLE;
|
||||||
|
settings.email_ssl = root["email_ssl"] | FACTORY_EMAIL_SSL;
|
||||||
|
settings.email_starttls = root["email_starttls"] | FACTORY_EMAIL_STARTTLS;
|
||||||
|
settings.email_server = root["email_server"] | FACTORY_EMAIL_SERVER;
|
||||||
|
settings.email_port = root["email_port"] | FACTORY_EMAIL_PORT;
|
||||||
|
settings.email_login = root["email_login"] | FACTORY_EMAIL_LOGIN;
|
||||||
|
settings.email_pass = root["email_pass"] | FACTORY_EMAIL_PASSWORD;
|
||||||
|
settings.email_sender = root["email_sender"] | FACTORY_EMAIL_FROM;
|
||||||
|
settings.email_recp = root["email_recp"] | FACTORY_EMAIL_TO;
|
||||||
|
settings.email_subject = root["email_subject"] | FACTORY_EMAIL_SUBJECT;
|
||||||
|
|
||||||
|
if (settings.email_ssl && settings.email_starttls) {
|
||||||
|
settings.email_ssl = false;
|
||||||
|
}
|
||||||
// if no psram limit weblog buffer to 25 messages
|
// if no psram limit weblog buffer to 25 messages
|
||||||
if (EMSESP::system_.PSram() > 0) {
|
if (EMSESP::system_.PSram() > 0) {
|
||||||
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
|
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
|
||||||
@@ -372,11 +396,7 @@ void WebSettingsService::onUpdate() {
|
|||||||
Mqtt::reset_mqtt(); // reload MQTT, init HA etc
|
Mqtt::reset_mqtt(); // reload MQTT, init HA etc
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::MODBUS)) {
|
|
||||||
EMSESP::system_.modbus_init();
|
|
||||||
}
|
|
||||||
WebSettings::reset_flags();
|
WebSettings::reset_flags();
|
||||||
EMSESP::system_.reset_unused_gpios();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSettingsService::begin() {
|
void WebSettingsService::begin() {
|
||||||
@@ -462,23 +482,14 @@ void WebSettings::set_board_profile(WebSettings & settings) {
|
|||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
// check for no PSRAM, could be a E32 or S32?
|
// check for no PSRAM, could be a E32 or S32?
|
||||||
if (!ESP.getPsramSize()) {
|
if (!ESP.getPsramSize()) {
|
||||||
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
|
||||||
if (ETH.begin(1, 16, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_IN)) {
|
|
||||||
#else
|
|
||||||
if (ETH.begin(ETH_PHY_LAN8720, 1, 23, 18, 16, ETH_CLOCK_GPIO0_IN)) {
|
if (ETH.begin(ETH_PHY_LAN8720, 1, 23, 18, 16, ETH_CLOCK_GPIO0_IN)) {
|
||||||
#endif
|
|
||||||
settings.board_profile = "E32"; // Ethernet without PSRAM
|
settings.board_profile = "E32"; // Ethernet without PSRAM
|
||||||
} else {
|
} else {
|
||||||
settings.board_profile = "S32"; // ESP32 standard WiFi without PSRAM
|
settings.board_profile = "S32"; // ESP32 standard WiFi without PSRAM
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// check for boards with PSRAM, could be a E32V2 otherwise default back to the S32
|
// check for boards with PSRAM, could be a E32V2 otherwise default back to the S32
|
||||||
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
|
||||||
if (ETH.begin(0, 15, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_OUT)) {
|
|
||||||
#else
|
|
||||||
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
|
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
|
||||||
#endif
|
|
||||||
|
|
||||||
if (analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
if (analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
||||||
settings.board_profile = "E32V2_2"; // Ethernet, PSRAM, internal sensors
|
settings.board_profile = "E32V2_2"; // Ethernet, PSRAM, internal sensors
|
||||||
} else {
|
} else {
|
||||||
@@ -527,7 +538,7 @@ void WebSettings::set_board_profile(WebSettings & settings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns true if the value was changed
|
// returns true if the value was changed
|
||||||
bool WebSettings::check_flag(int prev_v, int new_v, uint16_t flag) {
|
bool WebSettings::check_flag(int prev_v, int new_v, uint8_t flag) {
|
||||||
if (prev_v != new_v) {
|
if (prev_v != new_v) {
|
||||||
add_flags(flag);
|
add_flags(flag);
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
@@ -538,11 +549,11 @@ bool WebSettings::check_flag(int prev_v, int new_v, uint16_t flag) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSettings::add_flags(uint16_t flags) {
|
void WebSettings::add_flags(uint8_t flags) {
|
||||||
flags_ |= flags;
|
flags_ |= flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSettings::has_flags(uint16_t flags) {
|
bool WebSettings::has_flags(uint8_t flags) {
|
||||||
return (flags_ & flags) == flags;
|
return (flags_ & flags) == flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,7 +561,7 @@ void WebSettings::reset_flags() {
|
|||||||
flags_ = ChangeFlags::NONE;
|
flags_ = ChangeFlags::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t WebSettings::get_flags() {
|
uint8_t WebSettings::get_flags() {
|
||||||
return flags_;
|
return flags_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,36 @@
|
|||||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/settings"
|
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/settings"
|
||||||
#define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile"
|
#define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile"
|
||||||
|
|
||||||
|
#ifndef FACTORY_EMAIL_ENABLE
|
||||||
|
#define FACTORY_EMAIL_ENABLE false
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_SSL
|
||||||
|
#define FACTORY_EMAIL_SSL false
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_STARTTLS
|
||||||
|
#define FACTORY_EMAIL_STARTTLS true
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_PORT
|
||||||
|
#define FACTORY_EMAIL_PORT 587
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_SERVER
|
||||||
|
#define FACTORY_EMAIL_SERVER "smtp.example.net"
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_LOGIN
|
||||||
|
#define FACTORY_EMAIL_LOGIN ""
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_PASSWORD
|
||||||
|
#define FACTORY_EMAIL_PASSWORD ""
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_FROM
|
||||||
|
#define FACTORY_EMAIL_FROM "ems-esp@example.net"
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_TO
|
||||||
|
#define FACTORY_EMAIL_TO ""
|
||||||
|
#endif
|
||||||
|
#ifndef FACTORY_EMAIL_SUBJECT
|
||||||
|
#define FACTORY_EMAIL_SUBJECT "ems-esp notification"
|
||||||
|
#endif
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
class WebSettings {
|
class WebSettings {
|
||||||
@@ -77,6 +107,16 @@ class WebSettings {
|
|||||||
uint16_t modbus_port;
|
uint16_t modbus_port;
|
||||||
uint8_t modbus_max_clients;
|
uint8_t modbus_max_clients;
|
||||||
uint32_t modbus_timeout;
|
uint32_t modbus_timeout;
|
||||||
|
bool email_enabled;
|
||||||
|
bool email_ssl;
|
||||||
|
bool email_starttls;
|
||||||
|
String email_server;
|
||||||
|
uint16_t email_port;
|
||||||
|
String email_login;
|
||||||
|
String email_pass;
|
||||||
|
String email_sender;
|
||||||
|
String email_recp;
|
||||||
|
String email_subject;
|
||||||
|
|
||||||
uint8_t phy_type;
|
uint8_t phy_type;
|
||||||
int8_t eth_power; // -1 means disabled
|
int8_t eth_power; // -1 means disabled
|
||||||
@@ -88,7 +128,7 @@ class WebSettings {
|
|||||||
static void read(WebSettings & settings, JsonObject root);
|
static void read(WebSettings & settings, JsonObject root);
|
||||||
static StateUpdateResult update(JsonObject root, WebSettings & settings);
|
static StateUpdateResult update(JsonObject root, WebSettings & settings);
|
||||||
|
|
||||||
enum ChangeFlags : uint16_t {
|
enum ChangeFlags : uint8_t {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
UART = (1 << 0), // 1 - uart
|
UART = (1 << 0), // 1 - uart
|
||||||
SYSLOG = (1 << 1), // 2 - syslog
|
SYSLOG = (1 << 1), // 2 - syslog
|
||||||
@@ -98,20 +138,19 @@ class WebSettings {
|
|||||||
LED = (1 << 5), // 32 - led
|
LED = (1 << 5), // 32 - led
|
||||||
BUTTON = (1 << 6), // 64 - button
|
BUTTON = (1 << 6), // 64 - button
|
||||||
MQTT = (1 << 7), // 128 - mqtt
|
MQTT = (1 << 7), // 128 - mqtt
|
||||||
MODBUS = (1 << 8), // 256 - modbus
|
RESTART = 0xFF // 255 - restart request (all changes)
|
||||||
RESTART = 0xFFFF // restart request (all changes)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool check_flag(int prev_v, int new_v, uint16_t flag);
|
static bool check_flag(int prev_v, int new_v, uint8_t flag);
|
||||||
static void add_flags(uint16_t flags);
|
static void add_flags(uint8_t flags);
|
||||||
static bool has_flags(uint16_t flags);
|
static bool has_flags(uint8_t flags);
|
||||||
static void reset_flags();
|
static void reset_flags();
|
||||||
static uint16_t get_flags();
|
static uint8_t get_flags();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void set_board_profile(WebSettings & settings);
|
static void set_board_profile(WebSettings & settings);
|
||||||
|
|
||||||
static uint16_t flags_;
|
static uint8_t flags_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WebSettingsService : public StatefulService<WebSettings> {
|
class WebSettingsService : public StatefulService<WebSettings> {
|
||||||
|
|||||||
@@ -296,6 +296,10 @@ uint8_t WebStatusService::upgradeImportantMessages(std::string & version) {
|
|||||||
|
|
||||||
version::EMSESP_Version current_version(current_version_s); // get current version
|
version::EMSESP_Version current_version(current_version_s); // get current version
|
||||||
|
|
||||||
|
if (latest_version > current_version && current_version.minor() < latest_version.minor()) {
|
||||||
|
return 0; // if it's just a minor version upgrade return 0
|
||||||
|
}
|
||||||
|
|
||||||
if ((current_version.major() <= 3 && current_version.minor() <= 8) && (latest_version.major() == 3 && latest_version.minor() == 9)) {
|
if ((current_version.major() <= 3 && current_version.minor() <= 8) && (latest_version.major() == 3 && latest_version.minor() == 9)) {
|
||||||
return 1; // if moving from below 3.8.x to 3.9.x return 1
|
return 1; // if moving from below 3.8.x to 3.9.x return 1
|
||||||
}
|
}
|
||||||
@@ -304,10 +308,6 @@ uint8_t WebStatusService::upgradeImportantMessages(std::string & version) {
|
|||||||
return 2; // if it's a major version upgrade return 2
|
return 2; // if it's a major version upgrade return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
if (latest_version > current_version && current_version.minor() < latest_version.minor()) {
|
|
||||||
return 0; // if it's just a minor version upgrade return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; // if it's not a valid version upgrade return 0
|
return 0; // if it's not a valid version upgrade return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ void uart_telegram(const char * rx_data) {
|
|||||||
|
|
||||||
// add an EMS device and register it
|
// add an EMS device and register it
|
||||||
void add_device(uint8_t device_id, uint8_t product_id) {
|
void add_device(uint8_t device_id, uint8_t product_id) {
|
||||||
uart_telegram({device_id, 0x49, EMSdevice::EMS_TYPE_VERSION, 0, product_id, 1, 0});
|
uart_telegram({device_id, 0x0B, EMSdevice::EMS_TYPE_VERSION, 0, product_id, 1, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
// add our EMS test devices
|
// add our EMS test devices
|
||||||
@@ -143,14 +143,14 @@ void add_devices() {
|
|||||||
add_device(0x08, 123); // Nefit Trendline
|
add_device(0x08, 123); // Nefit Trendline
|
||||||
|
|
||||||
// UBAuptime
|
// UBAuptime
|
||||||
uart_telegram({0x08, 0x49, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||||
|
|
||||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||||
|
|
||||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 49 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||||
uart_telegram({0x08, 0x49, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||||
|
|
||||||
//
|
//
|
||||||
// thermostat
|
// thermostat
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ void test_23() {
|
|||||||
"\"temperatureSensorReads\":0,\"temperatureSensorFails\":0},\"analog\":{\"enabled\":true,\"analogSensors\":5,\"analogSensorReads\":0,"
|
"\"temperatureSensorReads\":0,\"temperatureSensorFails\":0},\"analog\":{\"enabled\":true,\"analogSensors\":5,\"analogSensorReads\":0,"
|
||||||
"\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\","
|
"\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\","
|
||||||
"\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0,"
|
"\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0,"
|
||||||
"\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":5,\"emsBusID\":73,"
|
"\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":1,\"emsBusID\":11,"
|
||||||
"\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
|
"\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
|
||||||
"false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
|
"false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
|
||||||
"\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
|
"\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
|
||||||
@@ -259,7 +259,7 @@ void test_24() {
|
|||||||
"\"temperatureSensorReads\":0,\"temperatureSensorFails\":0},\"analog\":{\"enabled\":true,\"analogSensors\":5,\"analogSensorReads\":0,"
|
"\"temperatureSensorReads\":0,\"temperatureSensorFails\":0},\"analog\":{\"enabled\":true,\"analogSensors\":5,\"analogSensorReads\":0,"
|
||||||
"\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\","
|
"\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\","
|
||||||
"\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0,"
|
"\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0,"
|
||||||
"\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":5,\"emsBusID\":73,"
|
"\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":1,\"emsBusID\":11,"
|
||||||
"\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
|
"\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
|
||||||
"false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
|
"false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
|
||||||
"\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
|
"\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
|
||||||
@@ -330,8 +330,8 @@ void test_25() {
|
|||||||
"gauge\\nemsesp_bus_buswritesfailed 0\\n# HELP emsesp_bus_busrxlinequality busRxLineQuality\\n# TYPE emsesp_bus_busrxlinequality "
|
"gauge\\nemsesp_bus_buswritesfailed 0\\n# HELP emsesp_bus_busrxlinequality busRxLineQuality\\n# TYPE emsesp_bus_busrxlinequality "
|
||||||
"gauge\\nemsesp_bus_busrxlinequality 100\\n# HELP emsesp_bus_bustxlinequality busTxLineQuality\\n# TYPE emsesp_bus_bustxlinequality "
|
"gauge\\nemsesp_bus_busrxlinequality 100\\n# HELP emsesp_bus_bustxlinequality busTxLineQuality\\n# TYPE emsesp_bus_bustxlinequality "
|
||||||
"gauge\\nemsesp_bus_bustxlinequality 100\\n# HELP emsesp_bus_info info\\n# TYPE emsesp_bus_info gauge\\nemsesp_bus_info{busstatus=\\\"connected\\\", "
|
"gauge\\nemsesp_bus_bustxlinequality 100\\n# HELP emsesp_bus_info info\\n# TYPE emsesp_bus_info gauge\\nemsesp_bus_info{busstatus=\\\"connected\\\", "
|
||||||
"busprotocol=\\\"Buderus\\\"} 1\\n# HELP emsesp_settings_txmode txMode\\n# TYPE emsesp_settings_txmode gauge\\nemsesp_settings_txmode 5\\n# HELP "
|
"busprotocol=\\\"Buderus\\\"} 1\\n# HELP emsesp_settings_txmode txMode\\n# TYPE emsesp_settings_txmode gauge\\nemsesp_settings_txmode 1\\n# HELP "
|
||||||
"emsesp_settings_emsbusid emsBusID\\n# TYPE emsesp_settings_emsbusid gauge\\nemsesp_settings_emsbusid 73\\n# HELP emsesp_settings_showertimer "
|
"emsesp_settings_emsbusid emsBusID\\n# TYPE emsesp_settings_emsbusid gauge\\nemsesp_settings_emsbusid 11\\n# HELP emsesp_settings_showertimer "
|
||||||
"showerTimer\\n# TYPE emsesp_settings_showertimer gauge\\nemsesp_settings_showertimer 0\\n# HELP emsesp_settings_showerminduration "
|
"showerTimer\\n# TYPE emsesp_settings_showertimer gauge\\nemsesp_settings_showertimer 0\\n# HELP emsesp_settings_showerminduration "
|
||||||
"showerMinDuration\\n# TYPE emsesp_settings_showerminduration gauge\\nemsesp_settings_showerminduration 180\\n# HELP emsesp_settings_showeralert "
|
"showerMinDuration\\n# TYPE emsesp_settings_showerminduration gauge\\nemsesp_settings_showerminduration 180\\n# HELP emsesp_settings_showeralert "
|
||||||
"showerAlert\\n# TYPE emsesp_settings_showeralert gauge\\nemsesp_settings_showeralert 0\\n# HELP emsesp_settings_hideled hideLed\\n# TYPE "
|
"showerAlert\\n# TYPE emsesp_settings_showeralert gauge\\nemsesp_settings_showeralert 0\\n# HELP emsesp_settings_hideled hideLed\\n# TYPE "
|
||||||
|
|||||||
Reference in New Issue
Block a user