mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-06-15 04:16:27 +03:00
Compare commits
21 Commits
ea5be9508e
...
core3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
437b8447a2 | ||
|
|
4042d47736 | ||
|
|
0bc5cc0fe8 | ||
|
|
fa265e8ea4 | ||
|
|
30cc720dcf | ||
|
|
cdc5011eb6 | ||
|
|
fe8cf2620d | ||
|
|
e818a5a943 | ||
|
|
6dde0ffc79 | ||
|
|
a790cf594e | ||
|
|
c260914dac | ||
|
|
9558e1d5ab | ||
|
|
7f98139deb | ||
|
|
ea8e0c1819 | ||
|
|
be0737f783 | ||
|
|
6cedc0adc1 | ||
|
|
06a81db88a | ||
|
|
5ee08443f9 | ||
|
|
515eb2a16b | ||
|
|
497d7cf7f3 | ||
|
|
9cb2455af2 |
@@ -18,6 +18,5 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
||||
## Changed
|
||||
|
||||
- various memory optimizations [#3083](https://github.com/emsesp/EMS-ESP32/issues/3083)
|
||||
- Scheduler name is mandatory
|
||||
- Scheduler with type "Immediate", click Execute does not run the command [#3092](https://github.com/emsesp/EMS-ESP32/issues/3092)
|
||||
- Scheduler name is now mandatory
|
||||
- network fallback to AP only after start [#3090](https://github.com/emsesp/EMS-ESP32/issues/3090)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "systembackup",
|
||||
"version": "3.9.0",
|
||||
"date": "2026-06-14T10:59:16",
|
||||
"systembackup": [
|
||||
{
|
||||
"type": "settings",
|
||||
@@ -18,7 +19,7 @@
|
||||
"tx_power": 0
|
||||
},
|
||||
"AP": {
|
||||
"provision_mode": 1,
|
||||
"provision_mode": 2,
|
||||
"ssid": "ems-esp",
|
||||
"password": "ems-esp-neo",
|
||||
"channel": 1,
|
||||
@@ -31,13 +32,13 @@
|
||||
"MQTT": {
|
||||
"enableTLS": false,
|
||||
"rootCA": "",
|
||||
"enabled": false,
|
||||
"host": "",
|
||||
"enabled": true,
|
||||
"host": "192.168.1.2",
|
||||
"port": 1883,
|
||||
"base": "ems-esp",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"client_id": "esp32-b8ffc9ec",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"client_id": "esp32",
|
||||
"keep_alive": 60,
|
||||
"clean_session": false,
|
||||
"entity_format": 1,
|
||||
@@ -51,17 +52,17 @@
|
||||
"publish_time_heartbeat": 60,
|
||||
"mqtt_qos": 0,
|
||||
"mqtt_retain": false,
|
||||
"ha_enabled": false,
|
||||
"ha_enabled": true,
|
||||
"nested_format": 1,
|
||||
"discovery_prefix": "homeassistant",
|
||||
"discovery_type": 0,
|
||||
"ha_number_mode": 0,
|
||||
"ha_number_mode": 1,
|
||||
"publish_single": false,
|
||||
"publish_single2cmd": false,
|
||||
"send_response": false
|
||||
},
|
||||
"NTP": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"server": "time.google.com",
|
||||
"tz_label": "Europe/Amsterdam",
|
||||
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||
@@ -85,8 +86,9 @@
|
||||
"version": "3.9.0",
|
||||
"board_profile": "E32V2_2",
|
||||
"platform": "ESP32",
|
||||
"system_name": "prod",
|
||||
"locale": "en",
|
||||
"tx_mode": 5,
|
||||
"tx_mode": 1,
|
||||
"ems_bus_id": 73,
|
||||
"syslog_enabled": false,
|
||||
"syslog_level": 3,
|
||||
@@ -97,7 +99,7 @@
|
||||
"boiler_heatingoff": false,
|
||||
"remote_timeout": 24,
|
||||
"remote_timeout_en": false,
|
||||
"shower_timer": false,
|
||||
"shower_timer": true,
|
||||
"shower_alert": false,
|
||||
"shower_alert_coldshot": 10,
|
||||
"shower_alert_trigger": 7,
|
||||
@@ -107,7 +109,7 @@
|
||||
"dallas_gpio": 14,
|
||||
"dallas_parasite": false,
|
||||
"led_gpio": 32,
|
||||
"hide_led": false,
|
||||
"hide_led": true,
|
||||
"led_type": 1,
|
||||
"low_clock": false,
|
||||
"telnet_enabled": true,
|
||||
@@ -131,22 +133,68 @@
|
||||
"modbus_port": 502,
|
||||
"modbus_max_clients": 10,
|
||||
"modbus_timeout": 300,
|
||||
"developer_mode": false,
|
||||
"email_enabled": false,
|
||||
"developer_mode": true,
|
||||
"email_enabled": true,
|
||||
"email_security": 2,
|
||||
"email_server": "smtp.example.net",
|
||||
"email_server": "smtp.gmail.com",
|
||||
"email_port": 587,
|
||||
"email_login": "",
|
||||
"email_pass": "",
|
||||
"email_sender": "ems-esp@example.net",
|
||||
"email_recp": "",
|
||||
"email_login": "test@emsesp.org",
|
||||
"email_pass": "password",
|
||||
"email_sender": "test@emsesp.org",
|
||||
"email_recp": "test@emsesp.org",
|
||||
"email_subject": "ems-esp notification"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "commands",
|
||||
"Commands": {
|
||||
"commands": [
|
||||
{
|
||||
"id": 1,
|
||||
"cmd": "{\"url\":\"https://api.pushover.net/1/messages.json\"}",
|
||||
"value": "{\"user\":\"userkey\", \"token\":\"tokenkey\", \"message\":\"custom/message2\"}",
|
||||
"name": "push_over"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"cmd": "system/message",
|
||||
"value": "hello",
|
||||
"name": "sendmessage"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"cmd": "system/message",
|
||||
"value": "{\"url\":\"http://emsesp.org/versions.json\"}",
|
||||
"name": "sendversions"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"cmd": "custom/message",
|
||||
"value": "{\"url\":\"http://emsesp.org/versions.json\"}",
|
||||
"name": "getversions"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"cmd": "system/sendmail",
|
||||
"value": "\"test email with \" + custom/message2",
|
||||
"name": "sendmail"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "schedule",
|
||||
"Schedule": {
|
||||
"schedule": []
|
||||
"schedule": [
|
||||
{
|
||||
"id": 1,
|
||||
"flags": 128,
|
||||
"active": false,
|
||||
"time": "",
|
||||
"cmd_name": "sendmail",
|
||||
"name": "sendmailtest"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -187,7 +235,36 @@
|
||||
{
|
||||
"type": "entities",
|
||||
"Entities": {
|
||||
"entities": []
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"ram": 1,
|
||||
"device_id": 0,
|
||||
"type_id": 0,
|
||||
"offset": 0,
|
||||
"factor": 1,
|
||||
"name": "message",
|
||||
"uom": 0,
|
||||
"value_type": 9,
|
||||
"writeable": true,
|
||||
"hide": false,
|
||||
"value": "{\"stable\":{\"version\":\"3.8.2\",\"date\":\"2026-05-12\"},\"dev\":{\"version\":\"3.8.3-dev.5\",\"date\":\"2026-05-28T09:55:29Z\"}}"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"ram": 1,
|
||||
"device_id": 0,
|
||||
"type_id": 0,
|
||||
"offset": 0,
|
||||
"factor": 1,
|
||||
"name": "message2",
|
||||
"uom": 0,
|
||||
"value_type": 9,
|
||||
"writeable": true,
|
||||
"hide": false,
|
||||
"value": "testing"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -199,10 +276,65 @@
|
||||
{
|
||||
"type": "nvs",
|
||||
"nvs": [
|
||||
{
|
||||
"type": 1,
|
||||
"key": "nompower",
|
||||
"value": 30
|
||||
},
|
||||
{
|
||||
"type": 33,
|
||||
"key": "boot",
|
||||
"value": "3.9.0"
|
||||
},
|
||||
{
|
||||
"type": 17,
|
||||
"key": "led",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"type": 33,
|
||||
"key": "intTemp",
|
||||
"value": "28_1767_7B13_2502"
|
||||
},
|
||||
{
|
||||
"type": 4,
|
||||
"key": "d_boot",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"type": 33,
|
||||
"key": "app1",
|
||||
"value": "3.9.0"
|
||||
},
|
||||
{
|
||||
"type": 33,
|
||||
"key": "app0",
|
||||
"value": "3.9.0"
|
||||
},
|
||||
{
|
||||
"type": 66,
|
||||
"key": "nrgheat",
|
||||
"value": 49355.29793
|
||||
},
|
||||
{
|
||||
"type": 4,
|
||||
"key": "d_app1",
|
||||
"value": 1781336521
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"key": "fresh_firmware",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"type": 4,
|
||||
"key": "d_app0",
|
||||
"value": 1781363944
|
||||
},
|
||||
{
|
||||
"type": 66,
|
||||
"key": "nrgww",
|
||||
"value": 41852.79107
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"concurrently": "^10.0.3",
|
||||
"eslint": "^10.4.1",
|
||||
"eslint": "^10.5.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.8.4",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
|
||||
98
interface/pnpm-lock.yaml
generated
98
interface/pnpm-lock.yaml
generated
@@ -68,7 +68,7 @@ importers:
|
||||
devDependencies:
|
||||
'@eslint/js':
|
||||
specifier: ^10.0.1
|
||||
version: 10.0.1(eslint@10.4.1)
|
||||
version: 10.0.1(eslint@10.5.0)
|
||||
'@preact/preset-vite':
|
||||
specifier: ^2.10.5
|
||||
version: 2.10.5(@babel/core@7.29.7)(preact@10.29.2)(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0))
|
||||
@@ -88,11 +88,11 @@ importers:
|
||||
specifier: ^10.0.3
|
||||
version: 10.0.3
|
||||
eslint:
|
||||
specifier: ^10.4.1
|
||||
version: 10.4.1
|
||||
specifier: ^10.5.0
|
||||
version: 10.5.0
|
||||
eslint-config-prettier:
|
||||
specifier: ^10.1.8
|
||||
version: 10.1.8(eslint@10.4.1)
|
||||
version: 10.1.8(eslint@10.5.0)
|
||||
prettier:
|
||||
specifier: ^3.8.4
|
||||
version: 3.8.4
|
||||
@@ -104,7 +104,7 @@ importers:
|
||||
version: 5.48.0
|
||||
typescript-eslint:
|
||||
specifier: ^8.61.0
|
||||
version: 8.61.0(eslint@10.4.1)(typescript@6.0.3)
|
||||
version: 8.61.0(eslint@10.5.0)(typescript@6.0.3)
|
||||
vite:
|
||||
specifier: ^8.0.16
|
||||
version: 8.0.16(@types/node@25.9.3)(terser@5.48.0)
|
||||
@@ -780,8 +780,8 @@ packages:
|
||||
peerDependencies:
|
||||
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
|
||||
acorn@8.16.0:
|
||||
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
||||
acorn@8.17.0:
|
||||
resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -853,8 +853,8 @@ packages:
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
baseline-browser-mapping@2.10.35:
|
||||
resolution: {integrity: sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==}
|
||||
baseline-browser-mapping@2.10.37:
|
||||
resolution: {integrity: sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1185,8 +1185,8 @@ packages:
|
||||
duplexer3@0.1.5:
|
||||
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
|
||||
|
||||
electron-to-chromium@1.5.371:
|
||||
resolution: {integrity: sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==}
|
||||
electron-to-chromium@1.5.372:
|
||||
resolution: {integrity: sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==}
|
||||
|
||||
emoji-regex@10.6.0:
|
||||
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
||||
@@ -1371,8 +1371,8 @@ packages:
|
||||
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||
|
||||
eslint@10.4.1:
|
||||
resolution: {integrity: sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==}
|
||||
eslint@10.5.0:
|
||||
resolution: {integrity: sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -3240,9 +3240,9 @@ snapshots:
|
||||
'@esbuild/linux-loong64@0.14.54':
|
||||
optional: true
|
||||
|
||||
'@eslint-community/eslint-utils@4.9.1(eslint@10.4.1)':
|
||||
'@eslint-community/eslint-utils@4.9.1(eslint@10.5.0)':
|
||||
dependencies:
|
||||
eslint: 10.4.1
|
||||
eslint: 10.5.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.2': {}
|
||||
@@ -3263,9 +3263,9 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.15
|
||||
|
||||
'@eslint/js@10.0.1(eslint@10.4.1)':
|
||||
'@eslint/js@10.0.1(eslint@10.5.0)':
|
||||
optionalDependencies:
|
||||
eslint: 10.4.1
|
||||
eslint: 10.5.0
|
||||
|
||||
'@eslint/object-schema@3.0.5': {}
|
||||
|
||||
@@ -3631,15 +3631,15 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 25.9.3
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1)(typescript@6.0.3))(eslint@10.4.1)(typescript@6.0.3)':
|
||||
'@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
'@typescript-eslint/parser': 8.61.0(eslint@10.4.1)(typescript@6.0.3)
|
||||
'@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/scope-manager': 8.61.0
|
||||
'@typescript-eslint/type-utils': 8.61.0(eslint@10.4.1)(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.61.0(eslint@10.4.1)(typescript@6.0.3)
|
||||
'@typescript-eslint/type-utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/visitor-keys': 8.61.0
|
||||
eslint: 10.4.1
|
||||
eslint: 10.5.0
|
||||
ignore: 7.0.5
|
||||
natural-compare: 1.4.0
|
||||
ts-api-utils: 2.5.0(typescript@6.0.3)
|
||||
@@ -3647,14 +3647,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.61.0(eslint@10.4.1)(typescript@6.0.3)':
|
||||
'@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.61.0
|
||||
'@typescript-eslint/types': 8.61.0
|
||||
'@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
|
||||
'@typescript-eslint/visitor-keys': 8.61.0
|
||||
debug: 4.4.3
|
||||
eslint: 10.4.1
|
||||
eslint: 10.5.0
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -3677,13 +3677,13 @@ snapshots:
|
||||
dependencies:
|
||||
typescript: 6.0.3
|
||||
|
||||
'@typescript-eslint/type-utils@8.61.0(eslint@10.4.1)(typescript@6.0.3)':
|
||||
'@typescript-eslint/type-utils@8.61.0(eslint@10.5.0)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.61.0
|
||||
'@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.61.0(eslint@10.4.1)(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3)
|
||||
debug: 4.4.3
|
||||
eslint: 10.4.1
|
||||
eslint: 10.5.0
|
||||
ts-api-utils: 2.5.0(typescript@6.0.3)
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
@@ -3706,13 +3706,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.61.0(eslint@10.4.1)(typescript@6.0.3)':
|
||||
'@typescript-eslint/utils@8.61.0(eslint@10.5.0)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1)
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0)
|
||||
'@typescript-eslint/scope-manager': 8.61.0
|
||||
'@typescript-eslint/types': 8.61.0
|
||||
'@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
|
||||
eslint: 10.4.1
|
||||
eslint: 10.5.0
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -3722,11 +3722,11 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.61.0
|
||||
eslint-visitor-keys: 5.0.1
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.16.0):
|
||||
acorn-jsx@5.3.2(acorn@8.17.0):
|
||||
dependencies:
|
||||
acorn: 8.16.0
|
||||
acorn: 8.17.0
|
||||
|
||||
acorn@8.16.0: {}
|
||||
acorn@8.17.0: {}
|
||||
|
||||
ajv@6.15.0:
|
||||
dependencies:
|
||||
@@ -3784,7 +3784,7 @@ snapshots:
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
baseline-browser-mapping@2.10.35: {}
|
||||
baseline-browser-mapping@2.10.37: {}
|
||||
|
||||
bin-build@3.0.0:
|
||||
dependencies:
|
||||
@@ -3845,9 +3845,9 @@ snapshots:
|
||||
|
||||
browserslist@4.28.2:
|
||||
dependencies:
|
||||
baseline-browser-mapping: 2.10.35
|
||||
baseline-browser-mapping: 2.10.37
|
||||
caniuse-lite: 1.0.30001799
|
||||
electron-to-chromium: 1.5.371
|
||||
electron-to-chromium: 1.5.372
|
||||
node-releases: 2.0.47
|
||||
update-browserslist-db: 1.2.3(browserslist@4.28.2)
|
||||
|
||||
@@ -4202,7 +4202,7 @@ snapshots:
|
||||
|
||||
duplexer3@0.1.5: {}
|
||||
|
||||
electron-to-chromium@1.5.371: {}
|
||||
electron-to-chromium@1.5.372: {}
|
||||
|
||||
emoji-regex@10.6.0: {}
|
||||
|
||||
@@ -4316,9 +4316,9 @@ snapshots:
|
||||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
eslint-config-prettier@10.1.8(eslint@10.4.1):
|
||||
eslint-config-prettier@10.1.8(eslint@10.5.0):
|
||||
dependencies:
|
||||
eslint: 10.4.1
|
||||
eslint: 10.5.0
|
||||
|
||||
eslint-scope@9.1.2:
|
||||
dependencies:
|
||||
@@ -4331,9 +4331,9 @@ snapshots:
|
||||
|
||||
eslint-visitor-keys@5.0.1: {}
|
||||
|
||||
eslint@10.4.1:
|
||||
eslint@10.5.0:
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1)
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0)
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
'@eslint/config-array': 0.23.5
|
||||
'@eslint/config-helpers': 0.6.0
|
||||
@@ -4368,8 +4368,8 @@ snapshots:
|
||||
|
||||
espree@11.2.0:
|
||||
dependencies:
|
||||
acorn: 8.16.0
|
||||
acorn-jsx: 5.3.2(acorn@8.16.0)
|
||||
acorn: 8.17.0
|
||||
acorn-jsx: 5.3.2(acorn@8.17.0)
|
||||
eslint-visitor-keys: 5.0.1
|
||||
|
||||
esquery@1.7.0:
|
||||
@@ -5696,7 +5696,7 @@ snapshots:
|
||||
terser@5.48.0:
|
||||
dependencies:
|
||||
'@jridgewell/source-map': 0.3.11
|
||||
acorn: 8.16.0
|
||||
acorn: 8.17.0
|
||||
commander: 2.20.3
|
||||
source-map-support: 0.5.21
|
||||
|
||||
@@ -5753,13 +5753,13 @@ snapshots:
|
||||
dependencies:
|
||||
typescript: 6.0.3
|
||||
|
||||
typescript-eslint@8.61.0(eslint@10.4.1)(typescript@6.0.3):
|
||||
typescript-eslint@8.61.0(eslint@10.5.0)(typescript@6.0.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1)(typescript@6.0.3))(eslint@10.4.1)(typescript@6.0.3)
|
||||
'@typescript-eslint/parser': 8.61.0(eslint@10.4.1)(typescript@6.0.3)
|
||||
'@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.61.0(eslint@10.4.1)(typescript@6.0.3)
|
||||
eslint: 10.4.1
|
||||
'@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3)
|
||||
eslint: 10.5.0
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -4,9 +4,11 @@ import { toast } from 'react-toastify';
|
||||
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Box, Button, Typography } from '@mui/material';
|
||||
import { Box, Button, IconButton, Typography } from '@mui/material';
|
||||
|
||||
import { callAction } from '@/api/app';
|
||||
import {
|
||||
Body,
|
||||
Cell,
|
||||
@@ -46,7 +48,7 @@ const DEFAULT_COMMAND_ITEM: Omit<CommandItem, 'id'> = {
|
||||
|
||||
const commandsTheme = {
|
||||
Table: `
|
||||
--data-table-library_grid-template-columns: repeat(1, minmax(100px, 1fr)) repeat(1, minmax(100px, 1fr)) 160px;
|
||||
--data-table-library_grid-template-columns: repeat(1, minmax(100px, 1fr)) repeat(1, minmax(100px, 1fr)) 160px 32px;
|
||||
`,
|
||||
BaseRow: `
|
||||
font-size: 14px;
|
||||
@@ -81,7 +83,7 @@ const commandsTheme = {
|
||||
`
|
||||
};
|
||||
|
||||
const CommandsPage = () => {
|
||||
const Commands = () => {
|
||||
const { LL } = useI18nContext();
|
||||
const [numChanges, setNumChanges] = useState<number>(0);
|
||||
const blocker = useBlocker(numChanges !== 0);
|
||||
@@ -141,6 +143,21 @@ const CommandsPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const { send: executeCommand } = useRequest(
|
||||
(id: string) => callAction({ action: 'executeCommand', param: id }),
|
||||
{ immediate: false }
|
||||
)
|
||||
.onSuccess(() => {
|
||||
toast.success(LL.EXECUTE_COMMAND_SENT());
|
||||
})
|
||||
.onError((error) => {
|
||||
toast.error(String(error.error?.message || 'An error occurred'));
|
||||
});
|
||||
|
||||
const execute = async (name: string) => {
|
||||
await executeCommand(name);
|
||||
};
|
||||
|
||||
const editItem = (ci: CommandItem) => {
|
||||
setCreating(false);
|
||||
setSelectedItem(ci);
|
||||
@@ -205,6 +222,7 @@ const CommandsPage = () => {
|
||||
<HeaderCell stiff>{LL.COMMAND(0)}</HeaderCell>
|
||||
<HeaderCell stiff>{LL.VALUE(0)}</HeaderCell>
|
||||
<HeaderCell stiff>{LL.NAME(0)}</HeaderCell>
|
||||
<HeaderCell></HeaderCell>
|
||||
</HeaderRow>
|
||||
</Header>
|
||||
<Body>
|
||||
@@ -213,6 +231,16 @@ const CommandsPage = () => {
|
||||
<Cell>{ci.cmd}</Cell>
|
||||
<Cell>{ci.value}</Cell>
|
||||
<Cell>{ci.name}</Cell>
|
||||
<Cell>
|
||||
{numChanges === 0 && (
|
||||
<IconButton
|
||||
onClick={() => execute(ci.name)}
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<PlayArrowIcon color="primary" sx={{ fontSize: 16 }} />
|
||||
</IconButton>
|
||||
)}
|
||||
</Cell>
|
||||
</Row>
|
||||
))}
|
||||
</Body>
|
||||
@@ -281,4 +309,4 @@ const CommandsPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default CommandsPage;
|
||||
export default Commands;
|
||||
|
||||
@@ -82,7 +82,7 @@ const CommandsDialog = ({
|
||||
} catch (error) {
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
} finally {
|
||||
setHasChanges(false);
|
||||
// setHasChanges(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -336,6 +336,7 @@ const Dashboard = memo(() => {
|
||||
size="small"
|
||||
aria-label={LL.RUN_COMMAND()}
|
||||
onClick={() => editDashboardValue(di)}
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<PlayArrowIcon
|
||||
color="primary"
|
||||
@@ -349,6 +350,7 @@ const Dashboard = memo(() => {
|
||||
LL.CHANGE_VALUE() + ' ' + LL.VALUE(0)
|
||||
}
|
||||
onClick={() => editDashboardValue(di)}
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<EditIcon
|
||||
color="primary"
|
||||
|
||||
@@ -777,6 +777,7 @@ const Devices = memo(() => {
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => showDeviceValue(dv)}
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
{dv.v === '' ? (
|
||||
<PlayArrowIcon color="primary" sx={{ fontSize: 16 }} />
|
||||
|
||||
@@ -123,6 +123,11 @@ class Command {
|
||||
cmdfunctions_.reserve(num);
|
||||
}
|
||||
|
||||
// release any reserved-but-unused capacity once commands have settled
|
||||
static void compact() {
|
||||
cmdfunctions_.shrink_to_fit();
|
||||
}
|
||||
|
||||
static void show_all(uuid::console::Shell & shell);
|
||||
static Command::CmdFunction * find_command(const uint8_t device_type, const uint8_t device_id, const char * cmd, const uint8_t flag);
|
||||
static std::string tagged_cmd(const std::string & cmd, const uint8_t flag);
|
||||
|
||||
@@ -559,6 +559,7 @@ void EMSdevice::show_mqtt_handlers(uuid::console::Shell & shell) const {
|
||||
// register a callback function for a specific telegram type
|
||||
void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, const process_function_p f, uint8_t length) {
|
||||
telegram_functions_.emplace_back(telegram_type_id, telegram_type_name, fetch, false, length, f);
|
||||
EMSESP::mark_entities_changed();
|
||||
}
|
||||
|
||||
// add to device value library, also know now as a "device entity"
|
||||
@@ -675,6 +676,7 @@ void EMSdevice::add_device_value(int8_t tag, // to b
|
||||
// add the device entity
|
||||
devicevalues_.emplace_back(
|
||||
device_type_, tag, value_p, type, options, options_single, numeric_operator, short_name, fullname, custom_fullname, uom, has_cmd, min, max, state);
|
||||
EMSESP::mark_entities_changed();
|
||||
|
||||
// add a new command if it has a function attached
|
||||
if (has_cmd) {
|
||||
@@ -1281,9 +1283,9 @@ void EMSdevice::setCustomizationEntity(const std::string & entity_id) {
|
||||
|
||||
// set the custom name if it has one, or clear it
|
||||
if (has_custom_name) {
|
||||
dv.custom_fullname = entity_id.substr(custom_name_pos + 1);
|
||||
dv.set_custom_fullname(entity_id.substr(custom_name_pos + 1));
|
||||
} else {
|
||||
dv.custom_fullname = "";
|
||||
dv.set_custom_fullname("");
|
||||
}
|
||||
|
||||
auto min = dv.min;
|
||||
@@ -1322,11 +1324,11 @@ void EMSdevice::getCustomizationEntities(std::vector<std::string> & entity_ids)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_set && (mask || !dv.custom_fullname.empty())) {
|
||||
if (dv.custom_fullname.empty()) {
|
||||
if (!is_set && (mask || dv.has_custom_fullname())) {
|
||||
if (!dv.has_custom_fullname()) {
|
||||
entity_ids.push_back(Helpers::hextoa(mask, false) + entity_name);
|
||||
} else {
|
||||
entity_ids.push_back(Helpers::hextoa(mask, false) + entity_name + "|" + dv.custom_fullname);
|
||||
entity_ids.push_back(Helpers::hextoa(mask, false) + entity_name + "|" + dv.custom_fullname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,6 +550,12 @@ class EMSdevice {
|
||||
telegram_functions_.reserve(elements);
|
||||
}
|
||||
|
||||
// release any reserved-but-unused capacity once the entity/telegram set has settled
|
||||
void compact() {
|
||||
devicevalues_.shrink_to_fit();
|
||||
telegram_functions_.shrink_to_fit();
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
struct TelegramFunctionDump {
|
||||
uint16_t type_id_;
|
||||
|
||||
@@ -53,8 +53,7 @@ DeviceValue::DeviceValue(uint8_t device_type,
|
||||
, uom(uom)
|
||||
, has_cmd(has_cmd)
|
||||
, min(min)
|
||||
, max(max)
|
||||
, custom_fullname(custom_fullname) {
|
||||
, max(max) {
|
||||
// calculate #options in options list
|
||||
if (options_single) {
|
||||
options_size = 1;
|
||||
@@ -62,7 +61,12 @@ DeviceValue::DeviceValue(uint8_t device_type,
|
||||
options_size = Helpers::count_items(options);
|
||||
}
|
||||
|
||||
// set the min/max
|
||||
// store the custom name on the heap, but only if one was actually provided
|
||||
if (!custom_fullname.empty()) {
|
||||
custom_fullname_ = std::make_unique<std::string>(custom_fullname);
|
||||
}
|
||||
|
||||
// set the min/max (reads back the custom name set above)
|
||||
set_custom_minmax();
|
||||
|
||||
/*
|
||||
@@ -347,11 +351,12 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, uint32_t & dv_set_max) {
|
||||
|
||||
// extract custom min from custom_fullname
|
||||
bool DeviceValue::get_custom_min(int16_t & val) {
|
||||
auto min_pos = custom_fullname.find('>');
|
||||
bool has_min = (min_pos != std::string::npos);
|
||||
const auto & cf = custom_fullname();
|
||||
auto min_pos = cf.find('>');
|
||||
bool has_min = (min_pos != std::string::npos);
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
if (has_min) {
|
||||
int32_t v = Helpers::atoint(custom_fullname.substr(min_pos + 1).c_str());
|
||||
int32_t v = Helpers::atoint(cf.substr(min_pos + 1).c_str());
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
@@ -365,11 +370,12 @@ bool DeviceValue::get_custom_min(int16_t & val) {
|
||||
|
||||
// extract custom max from custom_fullname
|
||||
bool DeviceValue::get_custom_max(uint32_t & val) {
|
||||
auto max_pos = custom_fullname.find('<');
|
||||
bool has_max = (max_pos != std::string::npos);
|
||||
const auto & cf = custom_fullname();
|
||||
auto max_pos = cf.find('<');
|
||||
bool has_max = (max_pos != std::string::npos);
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
if (has_max) {
|
||||
int32_t v = Helpers::atoint(custom_fullname.substr(max_pos + 1).c_str());
|
||||
int32_t v = Helpers::atoint(cf.substr(max_pos + 1).c_str());
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
@@ -387,14 +393,32 @@ void DeviceValue::set_custom_minmax() {
|
||||
get_custom_max(max);
|
||||
}
|
||||
|
||||
std::string DeviceValue::get_custom_fullname() const {
|
||||
auto min_pos = custom_fullname.find('>');
|
||||
auto max_pos = custom_fullname.find('<');
|
||||
auto minmax_pos = min_pos < max_pos ? min_pos : max_pos;
|
||||
if (minmax_pos != std::string::npos) {
|
||||
return custom_fullname.substr(0, minmax_pos);
|
||||
// raw stored custom name (empty string if none was set)
|
||||
const std::string & DeviceValue::custom_fullname() const {
|
||||
static const std::string empty_string;
|
||||
return custom_fullname_ ? *custom_fullname_ : empty_string;
|
||||
}
|
||||
|
||||
// set or clear the custom name, only allocating heap when there's actually a name
|
||||
void DeviceValue::set_custom_fullname(const std::string & name) {
|
||||
if (name.empty()) {
|
||||
custom_fullname_.reset();
|
||||
} else if (custom_fullname_) {
|
||||
*custom_fullname_ = name;
|
||||
} else {
|
||||
custom_fullname_ = std::make_unique<std::string>(name);
|
||||
}
|
||||
return custom_fullname;
|
||||
}
|
||||
|
||||
std::string DeviceValue::get_custom_fullname() const {
|
||||
const auto & cf = custom_fullname();
|
||||
auto min_pos = cf.find('>');
|
||||
auto max_pos = cf.find('<');
|
||||
auto minmax_pos = min_pos < max_pos ? min_pos : max_pos;
|
||||
if (minmax_pos != std::string::npos) {
|
||||
return cf.substr(0, minmax_pos);
|
||||
}
|
||||
return cf;
|
||||
}
|
||||
|
||||
// returns the translated fullname or the custom fullname (if provided)
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "helpers.h" // for conversions
|
||||
#include "default_settings.h" // for enum types
|
||||
|
||||
@@ -188,8 +190,6 @@ class DeviceValue {
|
||||
// wider numeric range fields
|
||||
int16_t min; // min range
|
||||
uint32_t max; // max range
|
||||
// largest member last (cold path: only read during customization save/load and web display)
|
||||
std::string custom_fullname; // optional, from customization
|
||||
|
||||
DeviceValue(uint8_t device_type, // EMSdevice::DeviceType
|
||||
int8_t tag, // DeviceValueTAG::*
|
||||
@@ -219,6 +219,13 @@ class DeviceValue {
|
||||
std::string get_fullname() const;
|
||||
static std::string get_name(const std::string & entity);
|
||||
|
||||
// raw stored custom name (including any >min<max suffix), empty if none. Stored on heap only when set.
|
||||
const std::string & custom_fullname() const;
|
||||
void set_custom_fullname(const std::string & name);
|
||||
bool has_custom_fullname() const {
|
||||
return (bool)custom_fullname_;
|
||||
}
|
||||
|
||||
// dv state flags
|
||||
void add_state(uint8_t s) {
|
||||
state |= s;
|
||||
@@ -237,6 +244,11 @@ class DeviceValue {
|
||||
static const char * const * DeviceValueTAG_s[];
|
||||
static const char * const DeviceValueTAG_mqtt[];
|
||||
static uint8_t NUM_TAGS; // # tags
|
||||
|
||||
private:
|
||||
// optional custom name from customization. Allocated on heap only when actually set,
|
||||
// so unnamed entities (the vast majority) don't pay for an inline std::string.
|
||||
std::unique_ptr<std::string> custom_fullname_;
|
||||
};
|
||||
|
||||
}; // namespace emsesp
|
||||
|
||||
@@ -48,6 +48,9 @@ uint16_t EMSESP::wait_validate_ = 0;
|
||||
bool EMSESP::wait_km_ = false;
|
||||
uint32_t EMSESP::last_fetch_ = 0;
|
||||
|
||||
uint32_t EMSESP::last_entity_change_ = 0;
|
||||
bool EMSESP::entity_compaction_pending_ = false;
|
||||
|
||||
AsyncWebServer webServer(80);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
@@ -176,6 +179,37 @@ void EMSESP::clear_all_devices() {
|
||||
// emsdevices.clear(); // remove entries, but doesn't delete actual devices
|
||||
}
|
||||
|
||||
// called from EMSdevice/Command whenever an entity or telegram handler is registered.
|
||||
// Devices reserve their value/telegram vectors generously (to avoid realloc storms while
|
||||
// heating circuits etc. are discovered incrementally), so once registration settles we
|
||||
// reclaim the unused capacity - see compact_entities_if_stable().
|
||||
void EMSESP::mark_entities_changed() {
|
||||
last_entity_change_ = uuid::get_uptime();
|
||||
entity_compaction_pending_ = true;
|
||||
}
|
||||
|
||||
// once the entity/telegram set has been stable for ENTITY_COMPACT_DELAY, shrink the
|
||||
// per-device and command vectors to their actual size. Re-arms automatically if a new
|
||||
// device/circuit appears later (which just costs a single realloc).
|
||||
void EMSESP::compact_entities_if_stable() {
|
||||
if (!entity_compaction_pending_) {
|
||||
return; // nothing to do (cheap early-out on the hot path)
|
||||
}
|
||||
if ((uuid::get_uptime() - last_entity_change_) < ENTITY_COMPACT_DELAY) {
|
||||
return; // still settling
|
||||
}
|
||||
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
emsdevice->compact();
|
||||
}
|
||||
}
|
||||
Command::compact();
|
||||
|
||||
entity_compaction_pending_ = false;
|
||||
LOG_DEBUG("Reclaimed unused entity vector capacity");
|
||||
}
|
||||
|
||||
// return total number of devices excluding the Controller
|
||||
uint8_t EMSESP::count_devices() {
|
||||
if (emsdevices.empty()) {
|
||||
@@ -1860,6 +1894,7 @@ void EMSESP::loop() {
|
||||
webModulesService.loop(); // loop through the external library modules
|
||||
webSchedulerService.loop(); // scheduler timing logic; command execution is offloaded to WebCommandService's worker task
|
||||
scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||
compact_entities_if_stable(); // reclaim over-reserved entity vector capacity once device discovery settles
|
||||
}
|
||||
// check for GPIO Errors - this is called once when booting
|
||||
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) {
|
||||
|
||||
@@ -239,6 +239,10 @@ class EMSESP {
|
||||
static void scan_devices();
|
||||
static void clear_all_devices();
|
||||
|
||||
// called whenever a device entity or telegram handler is registered, so we can
|
||||
// later reclaim the (deliberately generous) reserved vector capacity once stable
|
||||
static void mark_entities_changed();
|
||||
|
||||
static std::vector<std::unique_ptr<EMSdevice>, AllocatorPSRAM<std::unique_ptr<EMSdevice>>> emsdevices;
|
||||
// services
|
||||
static Mqtt mqtt_;
|
||||
@@ -275,6 +279,9 @@ class EMSESP {
|
||||
static void publish_response(const std::shared_ptr<const Telegram> & telegram);
|
||||
static void publish_all_loop();
|
||||
|
||||
// one-time compaction of per-device/command vectors once registration has been stable
|
||||
static void compact_entities_if_stable();
|
||||
|
||||
void shell_prompt();
|
||||
void start_serial_console();
|
||||
|
||||
@@ -303,6 +310,11 @@ class EMSESP {
|
||||
static bool wait_km_;
|
||||
static uint32_t last_fetch_;
|
||||
|
||||
// entity/telegram registration tracking, used to trigger a one-time vector compaction
|
||||
static constexpr uint32_t ENTITY_COMPACT_DELAY = 60000; // ms of stability before compacting
|
||||
static uint32_t last_entity_change_; // uptime (ms) of last registration
|
||||
static bool entity_compaction_pending_; // true while a compaction is owed
|
||||
|
||||
// UUID stuff
|
||||
static constexpr auto & serial_console_ = Serial;
|
||||
static constexpr unsigned long SERIAL_CONSOLE_BAUD_RATE = 115200;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <map>
|
||||
|
||||
#include "firmwareVersion.h"
|
||||
#include "shuntingYard.h" // for compute() used by the message and sendmail commands
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
#include "../test/test.h"
|
||||
@@ -194,15 +195,11 @@ bool System::command_sendmail(const char * value, const int8_t id) {
|
||||
// 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
|
||||
// run the body through the Shunting Yard calculator (entity substitution, expressions, optional {url} fetch)
|
||||
// keep the original body if the calculator returns nothing
|
||||
std::string computed_body = compute(body.c_str());
|
||||
if (!computed_body.empty()) {
|
||||
body = computed_body.c_str();
|
||||
}
|
||||
msg.text.body(body);
|
||||
|
||||
@@ -344,22 +341,16 @@ bool System::command_message(const char * value, const int8_t id, JsonObject out
|
||||
return false; // must have a string value
|
||||
}
|
||||
|
||||
EMSESP::webSchedulerService.computed_value.clear();
|
||||
EMSESP::webSchedulerService.raw_value = value;
|
||||
for (uint16_t wait = 0; wait < 2000 && !EMSESP::webSchedulerService.raw_value.empty(); wait++) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
if (EMSESP::webSchedulerService.computed_value.empty()) {
|
||||
// process the message via the Shunting Yard calculator (entity substitution, expressions, optional {url} fetch)
|
||||
std::string computed_value = compute(value);
|
||||
if (computed_value.empty()) {
|
||||
LOG_WARNING("Message result is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
LOG_INFO("Message: %s", computed_value.c_str()); // send to log
|
||||
Mqtt::queue_publish(F_(message), computed_value); // send to MQTT if enabled
|
||||
output["api_data"] = computed_value; // send to API
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1285,6 +1276,9 @@ bool System::check_restore() {
|
||||
// continue processing the rest of the sections
|
||||
saveSettings(EMSESP_SETTINGS_FILE, section);
|
||||
}
|
||||
if (section_type == "commands") {
|
||||
saveSettings(EMSESP_COMMANDS_FILE, section);
|
||||
}
|
||||
if (section_type == "schedule") {
|
||||
saveSettings(EMSESP_SCHEDULER_FILE, section);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.9.0-dev.12"
|
||||
#define EMSESP_APP_VERSION "3.9.0-dev.13"
|
||||
|
||||
@@ -141,14 +141,12 @@ bool WebCommandService::dispatchCommand(const char * name, const char * value) {
|
||||
if (isUrlCommand(ci->cmd.c_str())) {
|
||||
return queueCommand(name, value);
|
||||
}
|
||||
// system/message defers evaluation of its value (via the scheduler's raw_value),
|
||||
// so executing it never blocks - keep it synchronous even if the value has a {url}
|
||||
if (Helpers::toLower(ci->cmd.c_str()) != "system/message") {
|
||||
// the effective value is the override if given, else the command's stored default
|
||||
const std::string effective_value = value ? value : std::string(ci->value.c_str());
|
||||
if (valueContainsUrl(effective_value)) {
|
||||
return queueCommand(name, value);
|
||||
}
|
||||
// internal command whose value embeds a {url} fetch (e.g. system/message) - the value is
|
||||
// resolved by compute() at execution time and would block, so offload it to the worker task
|
||||
// the effective value is the override if given, else the command's stored default
|
||||
const std::string effective_value = value ? value : std::string(ci->value.c_str());
|
||||
if (valueContainsUrl(effective_value)) {
|
||||
return queueCommand(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,8 +238,8 @@ bool WebCommandService::executeCommand(const char * name, const std::string & co
|
||||
// run the value through the shunting-yard calculator so expressions like "custom/heatcnt + 1"
|
||||
// are resolved (entity references replaced by their values, then computed). Plain values pass
|
||||
// through unchanged. Applies to both URL and internal commands, like the old scheduler code
|
||||
// which computed the value before executing. system/message evaluates its own argument later
|
||||
// (deferred via the scheduler's raw_value), so pre-computing it would run it twice - pass raw.
|
||||
// which computed the value before executing. system/message runs the shunting-yard on its own
|
||||
// argument, so pre-computing it here would run it twice - pass it through raw.
|
||||
std::string computed_data = data;
|
||||
if (!data.empty() && cmd != "system/message") {
|
||||
computed_data = compute(data);
|
||||
|
||||
@@ -491,8 +491,8 @@ void WebCustomizationService::load_test_data() {
|
||||
// find the device value and set the mask and custom name to match the above fake data
|
||||
for (auto & dv : emsdevice->devicevalues_) {
|
||||
if (strcmp(dv.short_name, "heatingactive") == 0) {
|
||||
dv.state = DeviceValueState::DV_FAVORITE; // set as favorite
|
||||
dv.custom_fullname = "is my heating on?";
|
||||
dv.state = DeviceValueState::DV_FAVORITE; // set as favorite
|
||||
dv.set_custom_fullname("is my heating on?");
|
||||
} else if (strcmp(dv.short_name, "tapwateractive") == 0) {
|
||||
dv.state = DeviceValueState::DV_FAVORITE; // set as favorite
|
||||
} else if (strcmp(dv.short_name, "selflowtemp") == 0) {
|
||||
|
||||
@@ -367,11 +367,6 @@ void WebSchedulerService::loop() {
|
||||
static uint32_t last_uptime_min = 0;
|
||||
static uint32_t last_uptime_sec = 0;
|
||||
|
||||
if (!raw_value.empty()) {
|
||||
computed_value = compute(raw_value);
|
||||
raw_value.clear();
|
||||
}
|
||||
|
||||
if (scheduleItems_->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,9 +75,6 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
|
||||
|
||||
std::string get_metrics_prometheus();
|
||||
|
||||
std::string raw_value;
|
||||
std::string computed_value;
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
void load_test_data();
|
||||
#endif
|
||||
|
||||
@@ -306,7 +306,7 @@ uint8_t WebStatusService::upgradeImportantMessages(std::string & version) {
|
||||
return 0; // no upgrade (same version or downgrade)
|
||||
}
|
||||
|
||||
if (current_version < FirmwareVersion("3.9.0") && latest_version.major() == 3 && latest_version.minor() == 9) {
|
||||
if (current_version < FirmwareVersion("3.9.0-dev.0") && latest_version.major() == 3 && latest_version.minor() == 9) {
|
||||
return 1; // upgrading to 3.9.x from anything older - new partition layout warning
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user