mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-04-29 12:05:12 +00:00
Compare commits
123 Commits
latest
...
0e08334132
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e08334132 | ||
|
|
3d51acf9e7 | ||
|
|
fd6ea5ed7e | ||
|
|
d18e5b1f14 | ||
|
|
20327d817d | ||
|
|
051c332426 | ||
|
|
a09258325e | ||
|
|
61dca0cbda | ||
|
|
2bff299193 | ||
|
|
1329b13db3 | ||
|
|
9dd894f0fe | ||
|
|
30d1ae5642 | ||
|
|
79aceef382 | ||
|
|
a28e52210a | ||
|
|
08eb294213 | ||
|
|
888baed81a | ||
|
|
4de3955db2 | ||
|
|
25f08c7624 | ||
|
|
1e8013100c | ||
|
|
62c8f55568 | ||
|
|
c40d828749 | ||
|
|
84ad08887a | ||
|
|
ed0a678020 | ||
|
|
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 |
@@ -12,6 +12,7 @@ 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)
|
||||
- boiler pumpkick [#2965](https://github.com/emsesp/EMS-ESP32/discussions/2965)
|
||||
- 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)
|
||||
- full system backup and restore
|
||||
|
||||
@@ -31,6 +32,8 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
||||
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
|
||||
- update version if it is 00.00 in first read [#2981](https://github.com/emsesp/EMS-ESP32/issues/2981)
|
||||
- device class for % values [#2980](https://github.com/emsesp/EMS-ESP32/issues/2980)
|
||||
- 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)
|
||||
- move http client from stack to heap
|
||||
- heap optimizations [#3021](https://github.com/emsesp/EMS-ESP32/discussions/3021)
|
||||
|
||||
2
Makefile
2
Makefile
@@ -47,7 +47,7 @@ MAKEFLAGS += -j$(JOBS) -l$(shell echo $$(($(JOBS) * 2)))
|
||||
#----------------------------------------------------------------------
|
||||
TARGET := emsesp
|
||||
BUILD := build
|
||||
SOURCES := src/core src/devices src/web src/test lib_standalone lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton
|
||||
SOURCES := src/core src/devices src/web src/test lib_standalone lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton
|
||||
INCLUDES := src/core src/devices src/web src/test lib_standalone lib/* lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src
|
||||
LIBRARIES :=
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DTASMOTA_SDK",
|
||||
"-DNO_TLS_SUPPORT",
|
||||
"-DARDUINO_LOLIN_C3_MINI",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DTASMOTA_SDK",
|
||||
"-DNO_TLS_SUPPORT",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0"
|
||||
],
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"arduino",
|
||||
"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": {
|
||||
"flash_size": "32MB",
|
||||
"maximum_ram_size": 327680,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"build": {
|
||||
"core": "esp32",
|
||||
"extra_flags": "-DTASMOTA_SDK",
|
||||
"extra_flags": "-DNO_TLS_SUPPORT",
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
@@ -19,7 +19,7 @@
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "Espressif ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
|
||||
"name": "Tasmota ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"arduino",
|
||||
"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": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"build": {
|
||||
"core": "esp32",
|
||||
"extra_flags": "-DTASMOTA_SDK",
|
||||
"extra_flags": "-DNO_TLS_SUPPORT",
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"build": {
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DNO_TLS_SUPPORT",
|
||||
"-DARDUINO_XIAO_ESP32C6",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"@mui/material": "^9.0.0",
|
||||
"@preact/compat": "^18.3.2",
|
||||
"@table-library/react-table-library": "4.1.15",
|
||||
"alova": "3.5.1",
|
||||
"alova": "^3.5.1",
|
||||
"async-validator": "^4.2.5",
|
||||
"etag": "^1.8.1",
|
||||
"formidable": "^3.5.4",
|
||||
@@ -64,8 +64,7 @@
|
||||
"terser": "^5.46.1",
|
||||
"typescript-eslint": "^8.59.0",
|
||||
"vite": "^8.0.9",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-tsconfig-paths": "^6.1.1"
|
||||
"vite-plugin-imagemin": "^0.6.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820"
|
||||
}
|
||||
|
||||
39
interface/pnpm-lock.yaml
generated
39
interface/pnpm-lock.yaml
generated
@@ -30,7 +30,7 @@ importers:
|
||||
specifier: 4.1.15
|
||||
version: 4.1.15(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
alova:
|
||||
specifier: 3.5.1
|
||||
specifier: ^3.5.1
|
||||
version: 3.5.1
|
||||
async-validator:
|
||||
specifier: ^4.2.5
|
||||
@@ -126,9 +126,6 @@ importers:
|
||||
vite-plugin-imagemin:
|
||||
specifier: ^0.6.1
|
||||
version: 0.6.1(vite@8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1))
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^6.1.1
|
||||
version: 6.1.1(typescript@6.0.3)(vite@8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1))
|
||||
|
||||
packages:
|
||||
|
||||
@@ -1986,9 +1983,6 @@ packages:
|
||||
resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
globrex@0.1.2:
|
||||
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3176,16 +3170,6 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4'
|
||||
|
||||
tsconfck@3.1.6:
|
||||
resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==}
|
||||
engines: {node: ^18 || >=20}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
@@ -3274,11 +3258,6 @@ packages:
|
||||
peerDependencies:
|
||||
vite: 5.x || 6.x || 7.x || 8.x
|
||||
|
||||
vite-tsconfig-paths@6.1.1:
|
||||
resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==}
|
||||
peerDependencies:
|
||||
vite: '*'
|
||||
|
||||
vite@8.0.9:
|
||||
resolution: {integrity: sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -5265,8 +5244,6 @@ snapshots:
|
||||
merge2: 1.4.1
|
||||
slash: 3.0.0
|
||||
|
||||
globrex@0.1.2: {}
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
got@7.1.0:
|
||||
@@ -6400,10 +6377,6 @@ snapshots:
|
||||
dependencies:
|
||||
typescript: 6.0.3
|
||||
|
||||
tsconfck@3.1.6(typescript@6.0.3):
|
||||
optionalDependencies:
|
||||
typescript: 6.0.3
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
@@ -6516,16 +6489,6 @@ snapshots:
|
||||
stack-trace: 1.0.0-pre2
|
||||
vite: 8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1)
|
||||
|
||||
vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1)):
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.6(typescript@6.0.3)
|
||||
vite: 8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite@8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1):
|
||||
dependencies:
|
||||
lightningcss: 1.32.0
|
||||
|
||||
@@ -43,6 +43,16 @@ export interface Settings {
|
||||
modbus_port: number;
|
||||
modbus_max_clients: 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
FormLoader,
|
||||
MessageBox,
|
||||
SectionContent,
|
||||
ValidatedPasswordField,
|
||||
ValidatedTextField,
|
||||
useLayoutTitle
|
||||
} from 'components';
|
||||
@@ -351,6 +352,156 @@ const ApplicationSettings = () => {
|
||||
</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">
|
||||
{LL.SENSORS()}
|
||||
</Typography>
|
||||
|
||||
@@ -315,22 +315,13 @@ const InstallDialog = memo(
|
||||
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
{upgradeImportantMessageType === 1 && LL.UPGRADE_IMPORTANT_MESSAGES_1()}
|
||||
{upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()}
|
||||
{upgradeImportantMessageType === 1 && (
|
||||
<>
|
||||
{LL.UPGRADE_IMPORTANT_MESSAGES_1()}
|
||||
<Typography sx={{ mt: 2 }}>
|
||||
<Link to="/settings/downloadUpload" style={{ color: 'lightblue' }}>
|
||||
{LL.DOWNLOAD_SYSTEM_BACKUP()}
|
||||
</Link>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
<Typography sx={{ mt: 2 }}>
|
||||
<Link
|
||||
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
|
||||
style={{ color: 'lightblue' }}
|
||||
>
|
||||
{LL.ONLINE_HELP()}
|
||||
|
||||
@@ -49,6 +49,10 @@ the LICENSE file.
|
||||
#define EMC_CLIENTID_LENGTH 23 + 1
|
||||
#endif
|
||||
|
||||
#ifdef EMSESP_MQTT_STACKSIZE
|
||||
#define EMC_TASK_STACK_SIZE EMSESP_MQTT_STACKSIZE
|
||||
#endif
|
||||
|
||||
#ifndef EMC_TASK_STACK_SIZE
|
||||
#define EMC_TASK_STACK_SIZE 5120
|
||||
#endif
|
||||
@@ -66,14 +70,10 @@ the LICENSE file.
|
||||
#endif
|
||||
|
||||
#if EMC_USE_MEMPOOL
|
||||
#ifndef EMC_NUM_POOL_ELEMENTS
|
||||
#define EMC_NUM_POOL_ELEMENTS 32
|
||||
#endif
|
||||
#ifndef EMC_SIZE_POOL_ELEMENTS
|
||||
#define EMC_SIZE_POOL_ELEMENTS 128
|
||||
#endif
|
||||
#ifndef EMC_NUM_POOL_ELEMENTS
|
||||
#define EMC_NUM_POOL_ELEMENTS 32
|
||||
#endif
|
||||
#ifndef EMC_SIZE_POOL_ELEMENTS
|
||||
#define EMC_SIZE_POOL_ELEMENTS 128
|
||||
#endif
|
||||
|
||||
#ifndef TASMOTA_SDK
|
||||
#define EMC_CLIENT_SECURE
|
||||
#endif
|
||||
|
||||
@@ -62,7 +62,11 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
|
||||
_xSemaphore = xSemaphoreCreateMutex();
|
||||
EMC_SEMAPHORE_GIVE(); // release before first use
|
||||
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core);
|
||||
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);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void) useInternalTask;
|
||||
@@ -70,6 +74,7 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
|
||||
(void) core;
|
||||
#endif
|
||||
_clientId = _generatedClientId;
|
||||
_core = core;
|
||||
}
|
||||
|
||||
MqttClient::~MqttClient() {
|
||||
|
||||
@@ -69,7 +69,17 @@ class MqttClient {
|
||||
const char* getClientId() const;
|
||||
size_t queueSize(); // No const because of mutex
|
||||
void loop();
|
||||
|
||||
uint32_t stack() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
return uxTaskGetStackHighWaterMark(_taskHandle);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
uint8_t core() {
|
||||
return _core;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
|
||||
espMqttClientTypes::UseInternalTask _useInternalTask;
|
||||
@@ -98,6 +108,7 @@ class MqttClient {
|
||||
uint8_t _willQos;
|
||||
bool _willRetain;
|
||||
uint32_t _timeout;
|
||||
uint8_t _core;
|
||||
|
||||
// state is protected to allow state changes by the transport system, defined in child classes
|
||||
// eg. to allow AsyncTCP
|
||||
|
||||
@@ -8,7 +8,7 @@ the LICENSE file.
|
||||
|
||||
#include "ClientPosix.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
|
||||
namespace espMqttClientInternals {
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -6,66 +6,65 @@ For a copy, see <https://opensource.org/licenses/MIT> or
|
||||
the LICENSE file.
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
|
||||
#include "ClientSecureSync.h"
|
||||
#include <lwip/sockets.h> // socket options
|
||||
#include <lwip/sockets.h>
|
||||
#include "../Config.h"
|
||||
|
||||
namespace espMqttClientInternals {
|
||||
|
||||
ClientSecureSync::ClientSecureSync()
|
||||
: client() {
|
||||
// empty
|
||||
: client() {
|
||||
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 ret = client.connect(ip, port); // implicit conversion of return code int --> bool
|
||||
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
|
||||
int val = true;
|
||||
client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool
|
||||
if (ret) {
|
||||
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
||||
int val = true;
|
||||
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ClientSecureSync::connect(const char* host, uint16_t port) {
|
||||
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
|
||||
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
|
||||
int val = true;
|
||||
client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
bool ClientSecureSync::connect(const char * host, uint16_t port) {
|
||||
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
|
||||
if (ret) {
|
||||
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
|
||||
int val = true;
|
||||
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t ClientSecureSync::write(const uint8_t* buf, size_t size) {
|
||||
return client.write(buf, size);
|
||||
size_t ClientSecureSync::write(const uint8_t * buf, size_t size) {
|
||||
return client.write(buf, size);
|
||||
}
|
||||
|
||||
int ClientSecureSync::read(uint8_t* buf, size_t size) {
|
||||
return client.read(buf, size);
|
||||
int ClientSecureSync::read(uint8_t * buf, size_t size) {
|
||||
return client.read(buf, size);
|
||||
}
|
||||
|
||||
void ClientSecureSync::stop() {
|
||||
client.stop();
|
||||
client.stop();
|
||||
}
|
||||
|
||||
bool ClientSecureSync::connected() {
|
||||
return client.connected();
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
bool ClientSecureSync::disconnected() {
|
||||
return !client.connected();
|
||||
return !client.connected();
|
||||
}
|
||||
|
||||
} // namespace espMqttClientInternals
|
||||
} // namespace espMqttClientInternals
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -8,15 +8,11 @@ the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
|
||||
// Added for EMS-ESP
|
||||
#include "../Config.h"
|
||||
#if defined(EMC_CLIENT_SECURE)
|
||||
#include <WiFiClientSecure.h> // includes IPAddress
|
||||
#else
|
||||
// #include "esp_tls.h"
|
||||
#include <WiFiClient.h>
|
||||
#endif
|
||||
#include <ESP_SSLClient.h>
|
||||
#include "Transport.h"
|
||||
|
||||
namespace espMqttClientInternals {
|
||||
@@ -24,6 +20,7 @@ namespace espMqttClientInternals {
|
||||
class ClientSecureSync : public Transport {
|
||||
public:
|
||||
ClientSecureSync();
|
||||
~ClientSecureSync();
|
||||
bool connect(IPAddress ip, uint16_t port) override;
|
||||
bool connect(const char * host, uint16_t port) override;
|
||||
size_t write(const uint8_t * buf, size_t size) override;
|
||||
@@ -31,14 +28,11 @@ class ClientSecureSync : public Transport {
|
||||
void stop() override;
|
||||
bool connected() override;
|
||||
bool disconnected() override;
|
||||
// added for EMS-ESP
|
||||
#if defined(EMC_CLIENT_SECURE)
|
||||
WiFiClientSecure client;
|
||||
#else
|
||||
WiFiClient client;
|
||||
#endif
|
||||
|
||||
WiFiClient basic_client;
|
||||
ESP_SSLClient client;
|
||||
};
|
||||
|
||||
} // namespace espMqttClientInternals
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -8,50 +8,6 @@ the LICENSE file.
|
||||
|
||||
#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)
|
||||
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
|
||||
: MqttClientSetup(useInternalTask)
|
||||
@@ -78,51 +34,45 @@ espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core)
|
||||
}
|
||||
|
||||
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
||||
#if defined(EMC_CLIENT_SECURE)
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
_client.client.setInsecure();
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) {
|
||||
#if defined(EMC_CLIENT_SECURE)
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
_client.client.setCACert(rootCA);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) {
|
||||
#if defined(EMC_CLIENT_SECURE)
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
_client.client.setCertificate(clientCa);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) {
|
||||
#if defined(EMC_CLIENT_SECURE)
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
_client.client.setPrivateKey(privateKey);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) {
|
||||
#if defined(EMC_CLIENT_SECURE)
|
||||
_client.client.setPreSharedKey(pskIdent, psKey);
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
espMqttClient::espMqttClient()
|
||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
||||
, _client() {
|
||||
_transport = &_client;
|
||||
}
|
||||
#elif defined(_WIN32) || defined(__APPLE__)
|
||||
// Windows
|
||||
espMqttClient::espMqttClient()
|
||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) {
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -14,7 +14,7 @@ the LICENSE file.
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
#include "Transport/ClientSync.h"
|
||||
#include "Transport/ClientSecureSync.h"
|
||||
#elif defined(__linux__)
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
#include "Transport/ClientPosix.h"
|
||||
#endif
|
||||
|
||||
@@ -65,10 +65,16 @@ class espMqttClientSecure : public MqttClientSetup<espMqttClientSecure> {
|
||||
espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey);
|
||||
|
||||
protected:
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
espMqttClientInternals::ClientSecureSync _client;
|
||||
#else
|
||||
espMqttClientInternals::ClientSync _client;
|
||||
#endif
|
||||
};
|
||||
|
||||
#elif defined(__linux__)
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
class espMqttClient : public MqttClientSetup<espMqttClient> {
|
||||
public:
|
||||
espMqttClient();
|
||||
@@ -76,10 +82,4 @@ class espMqttClient : public MqttClientSetup<espMqttClient> {
|
||||
protected:
|
||||
espMqttClientInternals::ClientPosix _client;
|
||||
};
|
||||
#elif defined(_WIN32) || defined(__APPLE__)
|
||||
class espMqttClient : public MqttClientSetup<espMqttClient> {
|
||||
public:
|
||||
espMqttClient();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,7 +21,7 @@ extra_configs =
|
||||
pio_local.ini
|
||||
|
||||
[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
|
||||
|
||||
my_build_flags =
|
||||
@@ -53,13 +53,21 @@ build_flags =
|
||||
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
|
||||
[espressif32_base_16M]
|
||||
framework = arduino
|
||||
board_build.partitions = partitions/esp32_partition_16M.csv
|
||||
board_upload.flash_size = 16MB
|
||||
board_build.app_partition_name = app0
|
||||
platform = espressif32@6.13.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
|
||||
[espressif32_base_32M]
|
||||
@@ -67,29 +75,7 @@ framework = arduino
|
||||
board_build.partitions = partitions/esp32_partition_32M.csv
|
||||
board_upload.flash_size = 32MB
|
||||
board_build.app_partition_name = app0
|
||||
platform = espressif32@6.13.0 ; Arduino Core 2.0.17 / IDF 4.4.7
|
||||
|
||||
; 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
|
||||
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
|
||||
|
||||
[env]
|
||||
build_flags =
|
||||
@@ -106,6 +92,8 @@ lib_deps =
|
||||
bblanchon/ArduinoJson @ 7.4.3
|
||||
ESP32Async/AsyncTCP @ 3.4.10
|
||||
ESP32Async/ESPAsyncWebServer @ 3.10.3
|
||||
https://github.com/mobizt/ReadyMail.git @ 0.4.0
|
||||
https://github.com/mobizt/ESP_SSLClient.git @ 3.1.3
|
||||
; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
||||
|
||||
; builds the web interface only, not the firmware
|
||||
@@ -118,18 +106,17 @@ build_src_filter = -<*>
|
||||
|
||||
;
|
||||
; 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)
|
||||
;
|
||||
|
||||
[env:s_4M]
|
||||
; 4MB ESP32 - no SSL, no PSRAM - like a BBQKees older S32 and E32 models - uses Tasmota
|
||||
extends = espressif32_base_T_4M
|
||||
; 4MB ESP32 - no SSL, no PSRAM - like a BBQKees older S32 and E32 models
|
||||
extends = espressif32_base_4M
|
||||
board = s_4M
|
||||
|
||||
[env:s_16M]
|
||||
; 16MB ESP32 - no PSRAM - like a BBQKees later S32 V2 models - uses Tasmota
|
||||
extends = espressif32_base_T_16M
|
||||
; 16MB ESP32 - no PSRAM - like a BBQKees later S32 V2 models
|
||||
extends = espressif32_base_16M
|
||||
board = s_16M
|
||||
|
||||
[env:s_16M_P]
|
||||
@@ -148,24 +135,30 @@ extends = espressif32_base_32M
|
||||
board = s3_32M_P
|
||||
|
||||
[env:s2_4M_P]
|
||||
; based on lolin_s2_mini 4MB with 2MB PSRAM - uses Tasmota
|
||||
extends = espressif32_base_T_4M
|
||||
; based on lolin_s2_mini 4MB with 2MB PSRAM
|
||||
extends = espressif32_base_4M
|
||||
board = s2_4M_P
|
||||
|
||||
[env:c3_mini_4M]
|
||||
; based on lolin_c3_mini 4MB, no PSRAM - uses Tasmota
|
||||
extends = espressif32_base_T_4M
|
||||
; based on lolin_c3_mini 4MB, no PSRAM
|
||||
extends = espressif32_base_4M
|
||||
board = c3_mini_4M
|
||||
|
||||
; lolin C3 mini v1 needs special wifi initialization
|
||||
; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||
[env:c3_miniv1_4M]
|
||||
extends = espressif32_base_T_4M
|
||||
extends = espressif32_base_4M
|
||||
board = c3_mini_4M
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-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
|
||||
; use the `standalone` environment instead of `native` for testing
|
||||
[env:native]
|
||||
@@ -175,7 +168,8 @@ build_flags =
|
||||
build_src_flags =
|
||||
-DEMSESP_STANDALONE -DEMSESP_TEST
|
||||
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
|
||||
-std=gnu++2a -Og -ggdb
|
||||
-DNO_TLS_SUPPORT
|
||||
-std=gnu++20 -Og -ggdb
|
||||
-Wall -Wextra
|
||||
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
|
||||
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
|
||||
@@ -213,8 +207,9 @@ build_src_flags =
|
||||
-DEMSESP_STANDALONE -DEMSESP_TEST
|
||||
-DEMSESP_UNITY
|
||||
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
|
||||
-DNO_TLS_SUPPORT
|
||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
-std=gnu++2a -Og -ggdb
|
||||
-std=gnu++20 -Og -ggdb
|
||||
-Wall -Wextra
|
||||
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
|
||||
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
|
||||
|
||||
@@ -10,7 +10,6 @@ APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityM
|
||||
, _reconfigureAp(false)
|
||||
, _connected(0) {
|
||||
addUpdateHandler([this] { reconfigureAP(); }, false);
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
||||
}
|
||||
|
||||
void APSettingsService::begin() {
|
||||
@@ -19,39 +18,27 @@ void APSettingsService::begin() {
|
||||
// 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() {
|
||||
_lastManaged = uuid::get_uptime() - MANAGE_NETWORK_DELAY;
|
||||
_reconfigureAp = true;
|
||||
}
|
||||
|
||||
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();
|
||||
if ((currentMillis - _lastManaged) >= MANAGE_NETWORK_DELAY) {
|
||||
_lastManaged = currentMillis;
|
||||
@@ -76,11 +63,7 @@ void APSettingsService::manageAP() {
|
||||
}
|
||||
|
||||
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
|
||||
#endif
|
||||
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
|
||||
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);
|
||||
|
||||
@@ -104,7 +104,6 @@ class APSettingsService : public StatefulService<APSettings> {
|
||||
void startAP();
|
||||
void stopAP();
|
||||
void handleDNS();
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
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 {
|
||||
public:
|
||||
bool canHandle(AsyncWebServerRequest * request) const override {
|
||||
@@ -15,8 +15,7 @@ class StaticContentHandler : public AsyncWebHandler {
|
||||
}
|
||||
|
||||
void handleRequest(AsyncWebServerRequest * request) override {
|
||||
// OPTIONS is handled generically - the server-level CORS headers are
|
||||
// attached via DefaultHeaders in ESP32React::begin().
|
||||
// OPTIONS is handled generically - the server-level CORS headers are attached via DefaultHeaders in ESP32React::begin().
|
||||
if (request->method() == HTTP_OPTIONS) {
|
||||
request->send(200);
|
||||
return;
|
||||
@@ -57,8 +56,7 @@ class StaticContentHandler : public AsyncWebHandler {
|
||||
}
|
||||
|
||||
// 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
|
||||
// the client side).
|
||||
// that didn't match an embedded asset (React Router handles routing on the client side).
|
||||
static const WWWAsset * index_asset() {
|
||||
static const WWWAsset * cached = nullptr;
|
||||
if (cached == nullptr) {
|
||||
@@ -106,4 +104,5 @@ void ESP32React::loop() {
|
||||
_networkSettingsService.loop();
|
||||
_apSettingsService.loop();
|
||||
_mqttSettingsService.loop();
|
||||
_ntpSettingsService.loop();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, Secur
|
||||
, _disconnectedAt(0)
|
||||
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
|
||||
, _mqttClient(nullptr) {
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
||||
addUpdateHandler([this] { onConfigUpdated(); }, false);
|
||||
}
|
||||
|
||||
@@ -29,6 +28,7 @@ MqttSettingsService::~MqttSettingsService() {
|
||||
void MqttSettingsService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
startClient();
|
||||
_reconfigureMqtt = true;
|
||||
}
|
||||
|
||||
void MqttSettingsService::startClient() {
|
||||
@@ -41,7 +41,7 @@ void MqttSettingsService::startClient() {
|
||||
delete _mqttClient;
|
||||
_mqttClient = nullptr;
|
||||
}
|
||||
#ifndef TASMOTA_SDK
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
if (_state.enableTLS) {
|
||||
isSecure = true;
|
||||
if (emsesp::EMSESP::system_.PSram() == 0) {
|
||||
@@ -79,6 +79,10 @@ void MqttSettingsService::startClient() {
|
||||
}
|
||||
|
||||
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)) {
|
||||
// reconfigure MQTT client
|
||||
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
||||
@@ -142,28 +146,6 @@ void MqttSettingsService::onConfigUpdated() {
|
||||
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() {
|
||||
// disconnect if already connected
|
||||
if (_mqttClient->connected()) {
|
||||
@@ -182,7 +164,7 @@ bool MqttSettingsService::configureMqtt() {
|
||||
}
|
||||
|
||||
_reconfigureMqtt = false;
|
||||
#ifndef TASMOTA_SDK
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
if (_state.enableTLS) {
|
||||
if (_state.rootCA == "insecure") {
|
||||
emsesp::EMSESP::logger().debug("Start insecure MQTT");
|
||||
@@ -219,7 +201,7 @@ bool MqttSettingsService::configureMqtt() {
|
||||
}
|
||||
|
||||
void MqttSettings::read(MqttSettings & settings, JsonObject root) {
|
||||
#ifndef TASMOTA_SDK
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
root["enableTLS"] = settings.enableTLS;
|
||||
root["rootCA"] = settings.rootCA;
|
||||
#endif
|
||||
@@ -258,7 +240,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
MqttSettings newSettings;
|
||||
bool changed = false;
|
||||
|
||||
#ifndef TASMOTA_SDK
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
newSettings.enableTLS = root["enableTLS"];
|
||||
newSettings.rootCA = root["rootCA"] | "";
|
||||
#else
|
||||
@@ -389,7 +371,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat);
|
||||
}
|
||||
|
||||
#ifndef TASMOTA_SDK
|
||||
#ifndef NO_TLS_SUPPORT
|
||||
// strip down to certificate only
|
||||
newSettings.rootCA.replace("\r", "");
|
||||
newSettings.rootCA.replace("\n", "");
|
||||
|
||||
@@ -134,7 +134,6 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
|
||||
// the MQTT client instance
|
||||
MqttClient * _mqttClient;
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
|
||||
void
|
||||
|
||||
@@ -11,36 +11,17 @@ NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, Securit
|
||||
configureTime(request, json);
|
||||
});
|
||||
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
||||
addUpdateHandler([this] { configureNTP(); }, false);
|
||||
}
|
||||
|
||||
void NTPSettingsService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
configureNTP();
|
||||
}
|
||||
|
||||
// handles both WiFI and Ethernet
|
||||
void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
switch (event) {
|
||||
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();
|
||||
}
|
||||
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;
|
||||
void NTPSettingsService::loop() {
|
||||
if (_connected != emsesp::EMSESP::system_.network_connected()) {
|
||||
_connected = emsesp::EMSESP::system_.network_connected();
|
||||
configureNTP();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +36,9 @@ void NTPSettingsService::configureNTP() {
|
||||
} else {
|
||||
setenv("TZ", _state.tzFormat.c_str(), 1);
|
||||
tzset();
|
||||
esp_sntp_stop();
|
||||
if (esp_sntp_enabled()) {
|
||||
esp_sntp_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +51,12 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari
|
||||
tm.tm_isdst = -1; // not set by strptime, tells mktime to determine daylightsaving
|
||||
time_t time = mktime(&tm);
|
||||
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);
|
||||
#endif
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
return;
|
||||
|
||||
@@ -44,6 +44,7 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
|
||||
NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
static void ntp_received(struct timeval * tv);
|
||||
|
||||
private:
|
||||
@@ -51,7 +52,6 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
|
||||
FSPersistence<NTPSettings> _fsPersistence;
|
||||
volatile bool _connected;
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
void configureNTP();
|
||||
void configureTime(AsyncWebServerRequest * request, JsonVariant json);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs,
|
||||
, _stopping(false) {
|
||||
addUpdateHandler([this] { reconfigureWiFiConnection(); }, false);
|
||||
// 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]) {
|
||||
@@ -34,8 +34,8 @@ void NetworkSettingsService::begin() {
|
||||
WiFi.persistent(false);
|
||||
WiFi.setAutoReconnect(false);
|
||||
|
||||
WiFi.mode(WIFI_MODE_MAX);
|
||||
WiFi.mode(WIFI_MODE_NULL);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.mode(WIFI_OFF);
|
||||
|
||||
// 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
|
||||
@@ -62,28 +62,124 @@ void NetworkSettingsService::loop() {
|
||||
_lastConnectionAttempt = currentMillis;
|
||||
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() {
|
||||
// Abort if already connected, or if we have no SSID
|
||||
if (WiFi.isConnected() || _state.ssid.length() == 0) {
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
if (_state.ssid.length() == 0) {
|
||||
ETH.enableIPv6(true);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect or reconnect as required
|
||||
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);
|
||||
#endif
|
||||
if (_state.staticIPConfig) {
|
||||
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
|
||||
if (_state.bandwidth20) {
|
||||
@@ -293,7 +389,7 @@ const char * NetworkSettingsService::disconnectReason(uint8_t code) {
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
switch (event) {
|
||||
@@ -305,7 +401,7 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||
break;
|
||||
|
||||
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)",
|
||||
connectcount_,
|
||||
disconnectReason(info.wifi_sta_disconnected.reason),
|
||||
@@ -325,11 +421,11 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||
break;
|
||||
|
||||
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) {
|
||||
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
|
||||
}
|
||||
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
@@ -360,25 +456,15 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||
if (_state.tx_power == 0) {
|
||||
setWiFiPowerOnRSSI();
|
||||
}
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
WiFi.enableIpV6(); // force ipv6
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_ETH_CONNECTED:
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
ETH.enableIpV6(); // force ipv6
|
||||
#endif
|
||||
break;
|
||||
|
||||
// IPv6 specific - WiFi/Eth
|
||||
case ARDUINO_EVENT_WIFI_STA_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();
|
||||
#endif
|
||||
auto ip6 = IPAddress(IPv6, (uint8_t *)info.got_ip6.ip6_info.ip.addr, 0).toString();
|
||||
const char * link = event == ARDUINO_EVENT_ETH_GOT_IP6 ? "Eth" : "WiFi";
|
||||
if (ip6.startsWith("fe80")) {
|
||||
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
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void WiFiEvent(arduino_event_id_t event, arduino_event_info_t info);
|
||||
void mDNS_start() const;
|
||||
const char * disconnectReason(uint8_t code);
|
||||
void reconfigureWiFiConnection();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <emsesp.h>
|
||||
|
||||
#ifdef TASMOTA_SDK
|
||||
#ifdef NO_TLS_SUPPORT
|
||||
#include "lwip/dns.h"
|
||||
#endif
|
||||
|
||||
@@ -31,22 +31,13 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
// for both connections show ethernet
|
||||
if (ethernet_connected) {
|
||||
// Ethernet
|
||||
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();
|
||||
#endif
|
||||
root["local_ip"] = ETH.localIP().toString();
|
||||
root["local_ipv6"] = ETH.linkLocalIPv6().toString();
|
||||
root["mac_address"] = ETH.macAddress();
|
||||
root["subnet_mask"] = ETH.subnetMask().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 dnsIP2 = ETH.dnsIP(1);
|
||||
#endif
|
||||
IPAddress dnsIP1 = ETH.dnsIP(0);
|
||||
IPAddress dnsIP2 = ETH.dnsIP(1);
|
||||
if (IPUtils::isSet(dnsIP1)) {
|
||||
root["dns_ip_1"] = dnsIP1.toString();
|
||||
}
|
||||
@@ -54,12 +45,8 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
root["dns_ip_2"] = dnsIP2.toString();
|
||||
}
|
||||
} else if (wifi_status == WL_CONNECTED) {
|
||||
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();
|
||||
#endif
|
||||
root["local_ip"] = WiFi.localIP().toString();
|
||||
root["local_ipv6"] = WiFi.linkLocalIPv6().toString();
|
||||
root["mac_address"] = WiFi.macAddress();
|
||||
root["rssi"] = WiFi.RSSI();
|
||||
root["ssid"] = WiFi.SSID();
|
||||
@@ -71,14 +58,8 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
if (WiFi.gatewayIP() != INADDR_NONE) {
|
||||
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 dnsIP2 = WiFi.dnsIP(1);
|
||||
#endif
|
||||
if (dnsIP1 != INADDR_NONE) {
|
||||
root["dns_ip_1"] = dnsIP1.toString();
|
||||
}
|
||||
|
||||
@@ -347,23 +347,13 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
sensor.polltime_ = sensor.value() != 0 ? uuid::get_uptime() + (sensor.factor() * 1000) : 0;
|
||||
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
|
||||
LOG_DEBUG("PWM output on GPIO %02d", sensor.gpio());
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
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) {
|
||||
sensor.set_offset(100);
|
||||
} else if (sensor.offset() < 0) {
|
||||
sensor.set_offset(0);
|
||||
}
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
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_uom(DeviceValueUOM::PERCENT);
|
||||
publish_sensor(sensor);
|
||||
@@ -1041,12 +1031,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
||||
}
|
||||
sensor.set_offset(val);
|
||||
sensor.set_value(val);
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -285,6 +285,8 @@ enum {
|
||||
#define EMSESP_PLATFORM "ESP32S3"
|
||||
#elif CONFIG_IDF_TARGET_ESP32 || EMSESP_STANDALONE
|
||||
#define EMSESP_PLATFORM "ESP32"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#define EMSESP_PLATFORM "ESP32C6"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
@@ -293,12 +295,9 @@ enum {
|
||||
#ifndef STRINGIZE
|
||||
#define STRINGIZE(s) #s
|
||||
#endif
|
||||
#ifdef TASMOTA_SDK
|
||||
|
||||
#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)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1727,6 +1727,8 @@ void EMSESP::start() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (factory_settings) {
|
||||
LOG_WARNING("No settings found on filesystem. Using factory settings.");
|
||||
// make sure OTAdata is updated with core3 format
|
||||
esp_ota_set_boot_partition(esp_ota_get_running_partition());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -380,11 +380,15 @@ std::string commands(std::string & expr, bool quotes) {
|
||||
if (return_code != CommandRet::OK && return_code != CommandRet::NO_VALUE) {
|
||||
return expr = "";
|
||||
}
|
||||
|
||||
std::string data = output["api_data"] | "";
|
||||
if (!isnum(data) && quotes) {
|
||||
data.insert(data.begin(), '"');
|
||||
data.insert(data.end(), '"');
|
||||
std::string data;
|
||||
if (output["api_data"].is<std::string>()) {
|
||||
data = output["api_data"].as<std::string>();
|
||||
if (!isnum(data) && quotes) {
|
||||
data.insert(data.begin(), '"');
|
||||
data.insert(data.end(), '"');
|
||||
}
|
||||
} else {
|
||||
serializeJson(output, data);
|
||||
}
|
||||
expr.replace(f, l, data);
|
||||
e = f + data.length();
|
||||
@@ -700,8 +704,7 @@ std::string compute(const std::string & expr) {
|
||||
std::string cmd = expr_new.substr(f, e - f).c_str();
|
||||
JsonDocument doc;
|
||||
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
|
||||
for (JsonPair p : doc.as<JsonObject>()) {
|
||||
if (Helpers::toLower(p.key().c_str()) == "url") {
|
||||
@@ -720,58 +723,150 @@ std::string compute(const std::string & expr) {
|
||||
keys_s = p.key().c_str();
|
||||
}
|
||||
}
|
||||
if (http->begin(url.c_str())) {
|
||||
int httpResult = 0;
|
||||
for (JsonPair p : doc[header_s].as<JsonObject>()) {
|
||||
http->addHeader(p.key().c_str(), p.value().as<std::string>().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;
|
||||
#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, "");
|
||||
}
|
||||
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.find_first_of('{') != std::string::npos) {
|
||||
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
|
||||
}
|
||||
httpResult = http->POST(value.c_str());
|
||||
} else {
|
||||
httpResult = http->GET(); // normal GET
|
||||
/*
|
||||
index = host.find_first_of('@');
|
||||
std::string auth;
|
||||
if (index != std::string::npos) {
|
||||
auth = base64::encode(host.substr(0, index));
|
||||
host.replace(0, index, "");
|
||||
}
|
||||
|
||||
if (httpResult > 0) {
|
||||
std::string result = http->getString().c_str();
|
||||
std::string key = doc[key_s] | "";
|
||||
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
|
||||
if (doc[keys_s].is<JsonArray>()) {
|
||||
keys_doc.set(doc[keys_s].as<JsonArray>());
|
||||
}
|
||||
JsonArray keys = keys_doc.as<JsonArray>();
|
||||
|
||||
if (key.length() || !keys.isNull()) {
|
||||
doc.clear();
|
||||
if (DeserializationError::Ok == deserializeJson(doc, result)) {
|
||||
if (key.length()) {
|
||||
result = doc[key.c_str()].as<std::string>();
|
||||
*/
|
||||
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 {
|
||||
JsonVariant json = doc.as<JsonVariant>();
|
||||
for (JsonVariant keys_key : keys) {
|
||||
if (keys_key.is<std::string>() && json.is<JsonObject>()) {
|
||||
json = json[keys_key.as<std::string>()].as<JsonVariant>();
|
||||
} else if (keys_key.is<int>() && json.is<JsonArray>()) {
|
||||
json = json[keys_key.as<int>()].as<JsonVariant>();
|
||||
} else {
|
||||
break; // type mismatch
|
||||
}
|
||||
}
|
||||
result = json.as<std::string>();
|
||||
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>()) {
|
||||
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
|
||||
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
|
||||
}
|
||||
if (value.length() || Helpers::toLower(method) == "post") {
|
||||
if (!content_set) {
|
||||
http->addHeader("Content-Type", value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain);
|
||||
}
|
||||
httpResult = http->POST(value.c_str());
|
||||
} else {
|
||||
httpResult = http->GET(); // normal GET
|
||||
}
|
||||
if (httpResult > 0) {
|
||||
result = http->getString().c_str();
|
||||
}
|
||||
expr_new.replace(f, e - f, result.c_str());
|
||||
}
|
||||
http->end();
|
||||
delete http;
|
||||
}
|
||||
if (httpResult == 200) {
|
||||
std::string key = doc[key_s] | "";
|
||||
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
|
||||
if (doc[keys_s].is<JsonArray>()) {
|
||||
keys_doc.set(doc[keys_s].as<JsonArray>());
|
||||
}
|
||||
JsonArray keys = keys_doc.as<JsonArray>();
|
||||
if (key.length() || !keys.isNull()) {
|
||||
doc.clear();
|
||||
if (DeserializationError::Ok == deserializeJson(doc, result)) {
|
||||
if (key.length()) {
|
||||
result = doc[key.c_str()].as<std::string>();
|
||||
} else {
|
||||
JsonVariant json = doc.as<JsonVariant>();
|
||||
for (JsonVariant keys_key : keys) {
|
||||
if (keys_key.is<std::string>() && json.is<JsonObject>()) {
|
||||
json = json[keys_key.as<std::string>()].as<JsonVariant>();
|
||||
} else if (keys_key.is<int>() && json.is<JsonArray>()) {
|
||||
json = json[keys_key.as<int>()].as<JsonVariant>();
|
||||
} else {
|
||||
break; // type mismatch
|
||||
}
|
||||
}
|
||||
result = json.as<std::string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_new.replace(f, e - f, result);
|
||||
} else if (httpResult != 0) {
|
||||
EMSESP::logger().warning("URL command failed with https code: %d, response: %s", httpResult, result.c_str());
|
||||
}
|
||||
}
|
||||
f = expr_new.find_first_of('{', e);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,15 @@
|
||||
#include "../test/test.h"
|
||||
#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 {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
std::string System::languages_string() {
|
||||
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
|
||||
Mqtt::queue_publish(F_(message), EMSESP::webSchedulerService.computed_value); // send to MQTT if enabled
|
||||
output["api_data"] = EMSESP::webSchedulerService.computed_value; // send to API
|
||||
|
||||
EMSESP::webSchedulerService.computed_value.clear();
|
||||
EMSESP::webSchedulerService.computed_value.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -461,7 +580,6 @@ void System::system_restart(const char * partitionname) {
|
||||
|
||||
Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1
|
||||
EMSuart::stop(); // stop UART so there is no interference
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
delay(1000); // wait 1 second
|
||||
ESP.restart(); // ka-boom! - this is the only place where the ESP32 restart is called
|
||||
@@ -563,7 +681,6 @@ void System::store_settings(WebSettings & settings) {
|
||||
|
||||
locale_ = settings.locale;
|
||||
developer_mode_ = settings.developer_mode;
|
||||
|
||||
// start services
|
||||
if (settings.modbus_enabled) {
|
||||
if (EMSESP::modbus_ == nullptr) {
|
||||
@@ -605,20 +722,11 @@ void System::start() {
|
||||
appfree_ = esp_ota_get_running_partition()->size / 1024 - appused_;
|
||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
#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_install(&temp_sensor_config, &temperature_handle_);
|
||||
temperature_sensor_enable(temperature_handle_);
|
||||
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
@@ -839,16 +947,9 @@ void System::send_info_mqtt() {
|
||||
doc["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
||||
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() != "::") {
|
||||
doc["IPv6 address"] = uuid::printable_to_string(WiFi.linkLocalIPv6());
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
Mqtt::queue_publish_retain(F_(info), doc.as<JsonObject>()); // topic called "info" and it's Retained
|
||||
@@ -957,12 +1058,11 @@ void System::network_init() {
|
||||
delay(500);
|
||||
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);
|
||||
#endif
|
||||
if (eth_present_) {
|
||||
// Push hostname to the ETH netif immediately after it's created
|
||||
ETH.setHostname(hostname_.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -974,13 +1074,9 @@ void System::system_check() {
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#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_);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EMSESP_PINGTEST
|
||||
static uint64_t ping_count = 0;
|
||||
@@ -1032,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_(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_(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_(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);
|
||||
@@ -1280,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 gateway: %s", uuid::printable_to_string(WiFi.gatewayIP()).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() != "::") {
|
||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.linkLocalIPv6()).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
|
||||
case WL_CONNECT_FAILED:
|
||||
@@ -1320,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 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).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() != "::") {
|
||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
shell.println();
|
||||
|
||||
@@ -1350,10 +1434,10 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
shell.println();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
bool System::check_restore() {
|
||||
@@ -1653,8 +1737,8 @@ bool System::check_upgrade() {
|
||||
return false; // no reboot required
|
||||
}
|
||||
|
||||
// map each config filename to its human-readable section key
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// map each config filename to its human-readable section key
|
||||
static const std::pair<const char *, const char *> SECTION_MAP[] = {
|
||||
{NETWORK_SETTINGS_FILE, "Network"},
|
||||
{AP_SETTINGS_FILE, "AP"},
|
||||
@@ -1713,7 +1797,7 @@ void System::exportSystemBackup(JsonObject output) {
|
||||
output["version"] = EMSESP_APP_VERSION; // add the version to the output
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// add date/time if NTP enabled and active
|
||||
// add date/time if NTP enabled and active
|
||||
if ((esp_sntp_enabled()) && (EMSESP::system_.ntp_connected())) {
|
||||
time_t now = time(nullptr);
|
||||
if (now > 1500000000L) {
|
||||
@@ -1769,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
|
||||
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) {
|
||||
#endif
|
||||
LOG_ERROR("Failed to find NVS entry for %s", nvs_part);
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
nvs_entry_info_t info;
|
||||
nvs_entry_info(it, &info);
|
||||
#endif
|
||||
JsonObject entry = entries.add<JsonObject>();
|
||||
entry["type"] = info.type;
|
||||
entry["key"] = info.key;
|
||||
@@ -1824,14 +1897,8 @@ void System::exportSystemBackup(JsonObject output) {
|
||||
entry["value"] = EMSESP::nvs_.getString(info.key);
|
||||
break;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
it = nvs_entry_next(it);
|
||||
}
|
||||
#else
|
||||
err = nvs_entry_next(&it);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (it != nullptr) {
|
||||
nvs_release_iterator(it);
|
||||
@@ -2633,12 +2700,12 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["maxWebLogBuffer"] = settings.weblog_buffer;
|
||||
|
||||
/*
|
||||
#if defined(EMSESP_UNITY)
|
||||
node["webLogBuffer"] = 0;
|
||||
#else
|
||||
node["webLogBuffer"] = EMSESP::webLogService.num_log_messages();
|
||||
#endif
|
||||
*/
|
||||
#if defined(EMSESP_UNITY)
|
||||
node["webLogBuffer"] = 0;
|
||||
#else
|
||||
node["webLogBuffer"] = EMSESP::webLogService.num_log_messages();
|
||||
#endif
|
||||
*/
|
||||
node["modbusEnabled"] = settings.modbus_enabled;
|
||||
node["forceHeatingOff"] = settings.boiler_heatingoff;
|
||||
node["developerMode"] = settings.developer_mode;
|
||||
@@ -3266,7 +3333,7 @@ void System::set_valid_system_gpios() {
|
||||
valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19");
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO26 - GPIO32 = SPI flash and PSRAM
|
||||
// GPIO45 - GPIO46 = strapping pins
|
||||
@@ -3279,7 +3346,7 @@ void System::set_valid_system_gpios() {
|
||||
valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25");
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO3, GPIO45 - GPIO46 = strapping pins
|
||||
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
|
||||
@@ -3298,7 +3365,7 @@ void System::set_valid_system_gpios() {
|
||||
}
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11)
|
||||
// GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist
|
||||
@@ -3329,6 +3396,10 @@ void System::set_valid_system_gpios() {
|
||||
} else {
|
||||
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)
|
||||
valid_system_gpios_ = string_range_to_vector("0-39");
|
||||
#endif
|
||||
|
||||
@@ -37,11 +37,7 @@
|
||||
#include <uuid/log.h>
|
||||
#include <PButton.h>
|
||||
|
||||
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
||||
#define EMSESP_RGB_WRITE neopixelWrite
|
||||
#else
|
||||
#define EMSESP_RGB_WRITE rgbLedWrite
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// there is no official API available on the original ESP32
|
||||
@@ -49,12 +45,8 @@ extern "C" {
|
||||
uint8_t temprature_sens_read();
|
||||
}
|
||||
#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"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
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_response(const char * value, const int8_t id, JsonObject output);
|
||||
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 get_value_info(JsonObject root, const char * cmd);
|
||||
@@ -502,9 +495,7 @@ class System {
|
||||
uint32_t appfree_;
|
||||
|
||||
#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;
|
||||
#endif
|
||||
#endif
|
||||
float temperature_ = 0;
|
||||
};
|
||||
|
||||
@@ -159,10 +159,11 @@ class EMSbus {
|
||||
public:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static constexpr uint8_t EMS_MASK_UNSET = 0xFF; // EMS bus type (budrus/junkers) hasn't been detected yet
|
||||
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_TX_ERROR_LIMIT = 10; // % limit of failed Tx read/write attempts before showing a warning
|
||||
static constexpr uint8_t EMS_MASK_UNSET = 0xFF; // EMS bus type (budrus/junkers) hasn't been detected yet
|
||||
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_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() {
|
||||
return (ems_mask_ == EMS_MASK_HT3);
|
||||
@@ -210,7 +211,6 @@ class EMSbus {
|
||||
if (!last_bus_activity_) {
|
||||
bus_uptime_start_ = timestamp;
|
||||
}
|
||||
|
||||
last_bus_activity_ = timestamp;
|
||||
bus_connected_ = true;
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
|
||||
DeviceValueUOM::DEGREES);
|
||||
// Roomthermostats
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
register_telegram_type(0x0BDD + i, "Room", false, MAKE_PF_CB(process_roomThermostat)); // broadcasted
|
||||
register_telegram_type(0x0B3D + i, "Roomname", false, MAKE_PF_CB(process_roomThermostatName), 100); // fetch for active circuits
|
||||
register_telegram_type(0x0BDD + i, "Room", false, MAKE_PF_CB(process_roomThermostat)); // broadcasted
|
||||
register_telegram_type(0x0B3D + i, "Roomname", false, MAKE_PF_CB(process_roomThermostatName), 100); // fetch for active circuits
|
||||
register_telegram_type(0x0BB5 + i, "Roomsettings", false, MAKE_PF_CB(process_roomThermostatSettings), 8); // fetch for active circuits
|
||||
register_telegram_type(0x1230 + i, "Roomparams", false, MAKE_PF_CB(process_roomThermostatParam)); // fetch for active circuits
|
||||
register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData)); // broadcasted
|
||||
register_telegram_type(0x1230 + i, "Roomparams", false, MAKE_PF_CB(process_roomThermostatParam)); // fetch for active circuits
|
||||
register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData)); // broadcasted
|
||||
}
|
||||
register_telegram_type(0xDB65, "Roomschedule", true, MAKE_PF_CB(process_roomSchedule));
|
||||
// 0x2040, broadcast 36 bytes:
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.8.2-dev.18"
|
||||
#define EMSESP_APP_VERSION "3.9.0-dev.0"
|
||||
|
||||
@@ -39,7 +39,7 @@ uint32_t inverse_mask = 0;
|
||||
// receive task, wait for break and call incoming_telegram
|
||||
void EMSuart::uart_event_task(void * pvParameters) {
|
||||
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;
|
||||
|
||||
while (1) {
|
||||
@@ -66,18 +66,23 @@ void EMSuart::uart_event_task(void * pvParameters) {
|
||||
// initialize UART driver
|
||||
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 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,
|
||||
.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,
|
||||
.source_clk = UART_SCLK_APB
|
||||
#if ESP_ARDUINO_VERSION_MAJOR >= 3
|
||||
,
|
||||
.flags = {0}
|
||||
.source_clk = UART_SCLK_APB,
|
||||
.flags = {0}};
|
||||
#endif
|
||||
};
|
||||
#if defined(EMSUART_RX_INVERT)
|
||||
inverse_mask |= UART_SIGNAL_RXD_INV;
|
||||
#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_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_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_timeout(EMSUART_NUM, 0); // disable
|
||||
|
||||
|
||||
@@ -54,10 +54,10 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject root)
|
||||
JsonArray sensorsJson = root["ts"].to<JsonArray>();
|
||||
for (const SensorCustomization & sensor : customizations.sensorCustomizations) {
|
||||
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["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
|
||||
|
||||
@@ -352,7 +352,6 @@ bool WebSchedulerService::command(const char * name, const std::string & command
|
||||
// parse json
|
||||
JsonDocument doc;
|
||||
if (deserializeJson(doc, cmd) == DeserializationError::Ok) {
|
||||
HTTPClient * http = new HTTPClient;
|
||||
std::string url = doc["url"] | "";
|
||||
// for a GET with parameters replace commands with values
|
||||
// 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);
|
||||
url.replace(q + 1, l, s);
|
||||
}
|
||||
if (http->begin(url.c_str())) {
|
||||
// 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; // extract value if its in the command, or take the data
|
||||
std::string method = doc["method"] | "GET"; // default GET
|
||||
commands(value, false);
|
||||
if (value.length()) {
|
||||
method = "POST";
|
||||
}
|
||||
std::string result;
|
||||
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, "");
|
||||
}
|
||||
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
|
||||
|
||||
commands(value, false);
|
||||
// if there is data, force a POST
|
||||
int httpResult = 0;
|
||||
if (value.length() || method == "post") { // we have all lowercase
|
||||
if (value.find_first_of('{') != std::string::npos) {
|
||||
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
|
||||
// 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());
|
||||
}
|
||||
httpResult = http->POST(value.c_str());
|
||||
} else {
|
||||
httpResult = http->GET(); // normal GET
|
||||
EMSESP::logger().warning("HTTPS connection failed");
|
||||
}
|
||||
|
||||
http->end();
|
||||
delete http;
|
||||
|
||||
delete ssl_client;
|
||||
delete basic_client;
|
||||
// check HTTP return code
|
||||
if (httpResult != 200) {
|
||||
char error[100];
|
||||
snprintf(error, sizeof(error), "Schedule %s: URL command failed with http code %d", name, httpResult);
|
||||
EMSESP::logger().warning(error);
|
||||
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
|
||||
} else {
|
||||
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_text_plain, false); // auto-set to JSON
|
||||
}
|
||||
}
|
||||
httpResult = http->POST(value.c_str());
|
||||
} else {
|
||||
httpResult = http->GET(); // normal GET
|
||||
if (httpResult > 0) {
|
||||
result = http->getString().c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
http->end();
|
||||
delete http;
|
||||
// check HTTP return code
|
||||
if (httpResult != 200) {
|
||||
EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult);
|
||||
return false;
|
||||
}
|
||||
#if defined(EMSESP_DEBUG)
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg), "Schedule %s: URL command successful with http code %d", name, httpResult);
|
||||
EMSESP::logger().debug(msg);
|
||||
EMSESP::logger().debug("Schedule %s: URL '%s' command successful with http code %d", name, url.c_str(), httpResult);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,20 @@ void WebSettings::read(WebSettings & settings, JsonObject root) {
|
||||
root["modbus_max_clients"] = settings.modbus_max_clients;
|
||||
root["modbus_timeout"] = settings.modbus_timeout;
|
||||
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
|
||||
@@ -242,14 +256,10 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
||||
check_flag(original_settings.low_clock, settings.low_clock, ChangeFlags::RESTART);
|
||||
|
||||
// Modbus settings
|
||||
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
|
||||
check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::RESTART);
|
||||
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
|
||||
check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::RESTART);
|
||||
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
|
||||
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
|
||||
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::RESTART);
|
||||
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
|
||||
check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::RESTART);
|
||||
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
|
||||
|
||||
//
|
||||
// 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_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 (EMSESP::system_.PSram() > 0) {
|
||||
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
|
||||
@@ -458,23 +482,14 @@ void WebSettings::set_board_profile(WebSettings & settings) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// check for no PSRAM, could be a E32 or S32?
|
||||
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)) {
|
||||
#endif
|
||||
settings.board_profile = "E32"; // Ethernet without PSRAM
|
||||
} else {
|
||||
settings.board_profile = "S32"; // ESP32 standard WiFi without PSRAM
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
// check for boards with PSRAM, could be a E32V2 otherwise default back to the S32
|
||||
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
|
||||
#endif
|
||||
|
||||
if (analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
||||
settings.board_profile = "E32V2_2"; // Ethernet, PSRAM, internal sensors
|
||||
} else {
|
||||
|
||||
@@ -26,6 +26,36 @@
|
||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/settings"
|
||||
#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 {
|
||||
|
||||
class WebSettings {
|
||||
@@ -77,6 +107,16 @@ class WebSettings {
|
||||
uint16_t modbus_port;
|
||||
uint8_t modbus_max_clients;
|
||||
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;
|
||||
int8_t eth_power; // -1 means disabled
|
||||
|
||||
Reference in New Issue
Block a user