mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-09 07:25:49 +00:00
Compare commits
67 Commits
45690f5418
...
v3.8.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28135c225b | ||
|
|
eaa277fef0 | ||
|
|
e418b7d8e7 | ||
|
|
a4e3be6a69 | ||
|
|
a1bc5bb055 | ||
|
|
f444ca31e0 | ||
|
|
1b70b55989 | ||
|
|
1487f30c43 | ||
|
|
e00eb8e64f | ||
|
|
f41bb3671c | ||
|
|
22c75e6df3 | ||
|
|
5ab10b7aa6 | ||
|
|
ee5fd4d0eb | ||
|
|
46f35bc67c | ||
|
|
ec85a7ec24 | ||
|
|
02f2389587 | ||
|
|
7f140021aa | ||
|
|
6796962c1e | ||
|
|
df6de21cf4 | ||
|
|
df9f75a5c9 | ||
|
|
7bd8710eb6 | ||
|
|
32f2c6d341 | ||
|
|
86919c1684 | ||
|
|
86e29515e7 | ||
|
|
46eb4185d7 | ||
|
|
8da6761a48 | ||
|
|
9233f0dfcc | ||
|
|
292f743b14 | ||
|
|
dd6dfffd57 | ||
|
|
ec705a5307 | ||
|
|
f45f071710 | ||
|
|
f3858546de | ||
|
|
d0ac0b7804 | ||
|
|
d8284ec09f | ||
|
|
6e982acde8 | ||
|
|
8c94ce99b2 | ||
|
|
fc057d18c9 | ||
|
|
18e9b99413 | ||
|
|
a47e0e8266 | ||
|
|
f412ddc716 | ||
|
|
29110e96e5 | ||
|
|
b65866217a | ||
|
|
611e3b1243 | ||
|
|
2ca0a0c634 | ||
|
|
7eb1f061b7 | ||
|
|
50459a23fe | ||
|
|
5bf53c3389 | ||
|
|
4b7aa95be3 | ||
|
|
70943f5758 | ||
|
|
3bc280b817 | ||
|
|
62b15a5319 | ||
|
|
8dd18802d6 | ||
|
|
57a516a83a | ||
|
|
a57fdaa4b3 | ||
|
|
4841e42286 | ||
|
|
8c2d2b06ed | ||
|
|
38c8b1b7f0 | ||
|
|
6fb5933a02 | ||
|
|
c0944433be | ||
|
|
478e6362c9 | ||
|
|
4d6354db78 | ||
|
|
beab0f0c77 | ||
|
|
c17749bd22 | ||
|
|
2bad769c5c | ||
|
|
8ad89ca64b | ||
|
|
9244d8daec | ||
|
|
02d01334b2 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -22,7 +22,7 @@ _Make sure your have performed every step and checked the applicable boxes befor
|
||||
- [ ] Searched the issue in [issues](https://github.com/emsesp/EMS-ESP32/issues)
|
||||
- [ ] Searched the issue in [discussions](https://github.com/emsesp/EMS-ESP32/discussions)
|
||||
- [ ] Searched the issue in the [docs](https://emsesp.org/Troubleshooting/)
|
||||
- [ ] Searched the issue in the [chat](https://discord.gg/GP9DPSgeJq)
|
||||
- [ ] Searched the issue in the [chat](https://discord.gg/3J3GgnzpyT)
|
||||
- [ ] Provide the System information in the area below, taken from `http://<IP>/api/system`
|
||||
|
||||
```json
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -7,5 +7,5 @@ contact_links:
|
||||
url: https://github.com/emsesp/EMS-ESP32/discussions
|
||||
about: EMS-ESP usage Questions, Feature Requests and Projects.
|
||||
- name: EMS-ESP Users Chat
|
||||
url: https://discord.gg/GP9DPSgeJq
|
||||
url: https://discord.gg/3J3GgnzpyT
|
||||
about: Chat for feedback, questions and troubleshooting.
|
||||
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -5,36 +5,7 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [3.8.1] 11 January 2026
|
||||
|
||||
## Added
|
||||
|
||||
- update time saved in nvs
|
||||
- heatpump entities [#2883](https://github.com/emsesp/EMS-ESP32/issues/2883)
|
||||
- HA input number format (mode) selectable box/slider (slider for max range 100) [#2900](https://github.com/emsesp/EMS-ESP32/discussions/2900)
|
||||
|
||||
## Fixed
|
||||
|
||||
- fix EMS bus disconnected errors on some systems [#2881](https://github.com/emsesp/EMS-ESP32/issues/2881)
|
||||
- selflowtemp fix [#2876](https://github.com/emsesp/EMS-ESP32/issues/2876)
|
||||
- updated valid GPIOs for ESP32S2, ESP32S3 and ESP32 that caused custom systems to block gpios [#2887](https://github.com/emsesp/EMS-ESP32/issues/2887)
|
||||
- Junkers wwcharge offset [#2860](https://github.com/emsesp/EMS-ESP32/issues/2860)
|
||||
- fixed minflowtemp [#2890](https://github.com/emsesp/EMS-ESP32/issues/2890)
|
||||
- don't add HA uom/classes for bool values [#2885](https://github.com/emsesp/EMS-ESP32/issues/2885)
|
||||
- fixed missing progress bar on web firmware uploads
|
||||
|
||||
## Changed
|
||||
|
||||
- snapshot gpios stored in temporary ram
|
||||
- GPIOs stored along with the name and reported in log if conflicting
|
||||
- free GPIOs depend on board profile [#2901](https://github.com/emsesp/EMS-ESP32/issues/2901)
|
||||
- prefer PSram for mqtt queue [#2889](https://github.com/emsesp/EMS-ESP32/issues/2889)
|
||||
- day schedule defult to all days, no day selected is not allowed
|
||||
- board profile `CUSTOM` can only be selected in developer mode
|
||||
- mqtt sends round values without decimals (`28` instead of `28.0`)
|
||||
|
||||
|
||||
## [3.8.0] 31 December 2025
|
||||
## [3.8.0]
|
||||
|
||||
## Added
|
||||
|
||||
@@ -55,8 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- FW200 display options [#2610](https://github.com/emsesp/EMS-ESP32/discussions/2610)
|
||||
- CR11 mode settings OFF/MANUAL depends on selTemp [#2437](https://github.com/emsesp/EMS-ESP32/issues/2437)
|
||||
- implemented eFuse settings for BBQKees boards to store model type and ESP chipset
|
||||
- analogsensors for pulse output [#2624](https://github.com/emsesp/EMS-ESP32/discussions/2624)
|
||||
- analogsensors frequency input [#2631](https://github.com/emsesp/EMS-ESP32/discussions/2631)
|
||||
- Analogsensors for pulse output [#2624](https://github.com/emsesp/EMS-ESP32/discussions/2624)
|
||||
- Analogsensors frequency input [#2631](https://github.com/emsesp/EMS-ESP32/discussions/2631)
|
||||
- SRC plus thermostats [#2636](https://github.com/emsesp/EMS-ESP32/issues/2636)
|
||||
- Greenstar 2000 [#2645](https://github.com/emsesp/EMS-ESP32/issues/2645)
|
||||
- RC3xx `dhw modetype` [#2659](https://github.com/emsesp/EMS-ESP32/discussions/2659)
|
||||
@@ -119,6 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- added Home Assistant device details to MQTT Discovery for all devices
|
||||
- device_class and state_class changes for HA MQTT Discovery [#2825](https://github.com/emsesp/EMS-ESP32/issues/2825)
|
||||
|
||||
|
||||
## [3.7.2] 22 March 2025
|
||||
|
||||
## Added
|
||||
|
||||
@@ -1,27 +1,3 @@
|
||||
# Changelog
|
||||
|
||||
For more details go to [emsesp.org](https://emsesp.org/).
|
||||
|
||||
## [3.8.2]
|
||||
|
||||
## Added
|
||||
|
||||
- comfortpoint for BC400 [#2935](https://github.com/emsesp/EMS-ESP32/issues/2935)
|
||||
- customize device brand [#2784](https://github.com/emsesp/EMS-ESP32/issues/2784)
|
||||
- set model for ems-esp devices temperature, analog, etc. [#2958](https://github.com/emsesp/EMS-ESP32/discussions/2958)
|
||||
- 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)
|
||||
|
||||
## Fixed
|
||||
|
||||
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
|
||||
|
||||
## Changed
|
||||
|
||||
- weblogbuffer up to 1000 messages with PSRAM, mentioned in [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
|
||||
- validate custom entity writes, [#2931](https://github.com/emsesp/EMS-ESP32/issues/2931)
|
||||
- remove wrong burnMinPower [#2918](https://github.com/emsesp/EMS-ESP32/issues/2918)
|
||||
- store scheduler active state to nvs [#2946](https://github.com/emsesp/EMS-ESP32/discussions/2946)
|
||||
- translated modes `heat` and `eco` for HA-climate mode-str-tpl
|
||||
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
|
||||
|
||||
2
Makefile
2
Makefile
@@ -67,7 +67,7 @@ DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSO
|
||||
DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMC_RX_BUFFER_SIZE=1500
|
||||
DEFINES += $(ARGS)
|
||||
|
||||
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
|
||||
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Sources & Files
|
||||
|
||||
22
README.md
22
README.md
@@ -18,7 +18,7 @@
|
||||
<a href="https://emsesp.org">
|
||||
<img src="https://img.shields.io/badge/Documentation-0077b5?style=for-the-badge&logo=googledocs&logoColor=white" alt="Guides" />
|
||||
</a>
|
||||
<a href="https://discord.gg/GP9DPSgeJq">
|
||||
<a href="https://discord.gg/3J3GgnzpyT">
|
||||
<img src="https://img.shields.io/badge/Discord-7289da?style=for-the-badge&logo=discord&logoColor=white" alt="Discord" />
|
||||
</a>
|
||||
<a href="https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md">
|
||||
@@ -32,8 +32,7 @@
|
||||
[](https://sonarcloud.io/summary/new_code?id=emsesp_EMS-ESP32)
|
||||
[](https://app.codacy.com/gh/emsesp/EMS-ESP32/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
||||
[](https://github.com/emsesp/EMS-ESP32/releases)
|
||||
[](https://discord.gg/GP9DPSgeJq)
|
||||
[](https://deepwiki.com/emsesp/EMS-ESP32)
|
||||
[](https://discord.gg/3J3GgnzpyT)
|
||||
|
||||
[](https://github.com/emsesp/EMS-ESP32/stargazers)
|
||||
[](https://github.com/emsesp/EMS-ESP32/network)
|
||||
@@ -41,8 +40,7 @@
|
||||
|
||||
**EMS-ESP** is an open-source firmware for the Espressif ESP32 microcontroller to communicate with **EMS** (Energy Management System) compatible equipment from manufacturers such as Bosch, Buderus, Nefit, Junkers, Worcester, Sieger, elm.leblanc and iVT.
|
||||
|
||||
It requires a small circuit to interface with the EMS bus which can be purchased from <https://bbqkees-electronics.nl>. These gateways are tested thoroughly and certified to work with EMS-ESP.
|
||||
|
||||
It requires a small circuit to interface with the EMS bus which can be purchased from <https://bbqkees-electronics.nl> or custom built.
|
||||
|
||||
## 📦 **Key Features**
|
||||
|
||||
@@ -66,27 +64,29 @@ Head over to the [Installation Guide](https://emsesp.org/Installing) section of
|
||||
|
||||
## 📋 **Documentation**
|
||||
|
||||
Visit [emsesp.org](https://emsesp.org) for more details on how to setup and configure EMS-ESP. You'll also find more a collection of example configuarations, Frequently Asked Questions and Troubleshooting tips.
|
||||
Visit [emsesp.org](https://emsesp.org) for more details on how to install and configure EMS-ESP. There is also a collection of Frequently Asked Questions and Troubleshooting tips with example customizations from the community.
|
||||
|
||||
## 💬 **Getting Support**
|
||||
|
||||
To chat with the community reach out on our [Discord Server](https://discord.gg/GP9DPSgeJq).
|
||||
To chat with the community reach out on our [Discord Server](https://discord.gg/3J3GgnzpyT).
|
||||
|
||||
If you find an issue or have a request, see the [Getting Support](https://emsesp.org/Support/) section of the documentation. Note if you are using a non-BBQKees EMS gateway, you may need to contact the manufacturer for support.
|
||||
If you find an issue or have a request, see [how to request support](https://emsesp.org/Support/) on how to submit a bug report or feature request.
|
||||
|
||||
## 🎥 **Live Demo**
|
||||
|
||||
To see a live demo go to [demo.emsesp.org](https://demo.emsesp.org). Pick a language and use any username and password to log in. Note whast you're seeing is static example data so not all features are operational.
|
||||
For a live demo go to [demo.emsesp.org](https://demo.emsesp.org). Pick a language from the sign on page and log in with any username or password. Note not all features are operational as it's based on static data.
|
||||
|
||||
## 💖 **Contributors**
|
||||
|
||||
EMS-ESP is a project originally created by [proddy](https://github.com/proddy) and maintained by the ems-esp community.
|
||||
EMS-ESP is a project created by [proddy](https://github.com/proddy) and owned and maintained by both [proddy](https://github.com/proddy) and [MichaelDvP](https://github.com/MichaelDvP) with support from [BBQKees Electronics](https://bbqkees-electronics.nl).
|
||||
|
||||
If you like **EMS-ESP**, please give it a ✨ on GitHub, or even better fork it and contribute. You can also offer a small donation. This is an open-source project maintained by volunteers, and your support is greatly appreciated.
|
||||
|
||||
## 📦 **Building**
|
||||
|
||||
See the [Building the firmware](https://emsesp.org/Building) guide in the documentation for instructions on how to build EMS-ESP from this source code.
|
||||
To build the web interface only, run `platformio run -e build_webUI`. This will install the necessary dependencies and build the web interface and also create the embedded code used need to build the firmware. You can run the web interface locally by going to the `interface` directory and running `pnpm standalone`.
|
||||
|
||||
To build the firmware, run `platformio run`. This will build the firmware for all ESP32 modules and place the binaries in the `build/firmware` folder. If you want to configure the build for a single platform create a local `pio_local.ni` file in the root directory (see example in `pio_local.ini_example`).
|
||||
|
||||
## 📢 **Libraries used**
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report any security vulnerabilities using the [Contact Form](https://emsesp.org/About/#-contact).
|
||||
@@ -37,8 +37,8 @@
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"use_1200bps_touch": false,
|
||||
"wait_for_upload_port": false,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
}
|
||||
],
|
||||
"dictionaries": ["project-words"],
|
||||
"caseSensitive": false,
|
||||
"ignorePaths": [
|
||||
"node_modules",
|
||||
"compile_commands.json",
|
||||
@@ -36,8 +35,6 @@
|
||||
"managed_components/**",
|
||||
"pnpm-*.yaml",
|
||||
"vite.config.ts",
|
||||
"lib/esp32-psram/**",
|
||||
"test/test_api/test_api.h",
|
||||
"lib_standalone/**"
|
||||
"lib/esp32-psram/**"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ telegram_type_id,name,is_fetched
|
||||
0x19,UBAMonitorSlow,
|
||||
0x1A,UBASetPoints,
|
||||
0x1C,UBAMaintenanceStatus,
|
||||
0x1E,HydrTemp,
|
||||
0x1E,WM10TempMessage,
|
||||
0x23,JunkersSetMixer,fetched
|
||||
0x27,UBASettingsWW,fetched
|
||||
0x28,WeatherComp,fetched
|
||||
@@ -72,12 +72,11 @@ telegram_type_id,name,is_fetched
|
||||
0xE6,UBAParametersPlus,fetched
|
||||
0xE9,UBAMonitorWWPlus,
|
||||
0xEA,UBAParameterWWPlus,fetched
|
||||
0xEB,PumpKick,fetched
|
||||
0x0101,ISM1Set,fetched
|
||||
0x0103,ISM1StatusMessage,fetched
|
||||
0x0104,ISM2StatusMessage,
|
||||
0x010C,IPMStatusMessage,
|
||||
0x011E,IPMTempMessage,
|
||||
0x011E,JunkersDisp,fetched
|
||||
0x012E,HPEnergy1,
|
||||
0x013B,HPEnergy2,
|
||||
0x0165,JunkersSet,
|
||||
@@ -113,9 +112,9 @@ telegram_type_id,name,is_fetched
|
||||
0x02A1,RC300Curves,
|
||||
0x02A2,RC300Curves,
|
||||
0x02A5,RC300Monitor,fetched
|
||||
0x02A6,CRFMonitor,
|
||||
0x02A6,RC300Monitor,
|
||||
0x02A7,RC300Monitor,
|
||||
0x02A8,CRFMonitor,
|
||||
0x02A8,RC300Monitor,
|
||||
0x02A9,RC300Monitor,
|
||||
0x02AA,RC300Monitor,
|
||||
0x02AB,RC300Monitor,
|
||||
@@ -198,7 +197,7 @@ telegram_type_id,name,is_fetched
|
||||
0x04A2,HpInput,fetched
|
||||
0x04A5,HPFan,fetched
|
||||
0x04A7,HPPowerLimit,fetched
|
||||
0x04AA,HPPower,
|
||||
0x04AA,HPPower2,fetched
|
||||
0x04AE,HPEnergy,fetched
|
||||
0x04AF,HPMeters,fetched
|
||||
0x055C,VentilationSet,fetched
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"adapter": "react",
|
||||
"baseLocale": "pl",
|
||||
"$schema": "https://unpkg.com/typesafe-i18n@5.27.1/schema/typesafe-i18n.json"
|
||||
"$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json"
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build-hosted": "typesafe-i18n --no-watch && vite build --mode hosted",
|
||||
"build-hosted": "typesafe-i18n && vite build --mode hosted",
|
||||
"mock-rest": "bun --watch ../mock-api/restServer.ts",
|
||||
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"pnpm:mock-rest\" \"vite preview\"",
|
||||
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite dev\"",
|
||||
@@ -26,46 +26,46 @@
|
||||
"@alova/adapter-xhr": "2.3.1",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.3.9",
|
||||
"@mui/material": "^7.3.9",
|
||||
"@preact/compat": "^18.3.2",
|
||||
"@mui/icons-material": "^7.3.6",
|
||||
"@mui/material": "^7.3.6",
|
||||
"@preact/compat": "^18.3.1",
|
||||
"@table-library/react-table-library": "4.1.15",
|
||||
"alova": "3.5.1",
|
||||
"alova": "3.4.1",
|
||||
"async-validator": "^4.2.5",
|
||||
"etag": "^1.8.1",
|
||||
"formidable": "^3.5.4",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"magic-string": "^0.30.21",
|
||||
"mime-types": "^3.0.2",
|
||||
"preact": "^10.29.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-icons": "^5.6.0",
|
||||
"react-router": "^7.13.1",
|
||||
"preact": "^10.28.1",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router": "^7.11.0",
|
||||
"react-toastify": "^11.0.5",
|
||||
"typesafe-i18n": "^5.27.1",
|
||||
"typesafe-i18n": "^5.26.2",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.29.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@preact/compat": "^18.3.2",
|
||||
"@preact/preset-vite": "^2.10.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@babel/core": "^7.28.5",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@preact/compat": "^18.3.1",
|
||||
"@preact/preset-vite": "^2.10.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
|
||||
"@types/node": "^25.0.3",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"axe-core": "^4.11.1",
|
||||
"axe-core": "^4.11.0",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^10.0.3",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.8.1",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
"terser": "^5.46.0",
|
||||
"typescript-eslint": "^8.57.0",
|
||||
"vite": "^7.3.1",
|
||||
"prettier": "^3.7.4",
|
||||
"rollup-plugin-visualizer": "^6.0.5",
|
||||
"terser": "^5.44.1",
|
||||
"typescript-eslint": "^8.51.0",
|
||||
"vite": "^7.3.0",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-tsconfig-paths": "^6.1.1"
|
||||
"vite-tsconfig-paths": "^6.0.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.32.1"
|
||||
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
|
||||
}
|
||||
|
||||
6600
interface/pnpm-lock.yaml
generated
6600
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -310,15 +310,13 @@ const CustomEntities = () => {
|
||||
/>
|
||||
)}
|
||||
</Cell>
|
||||
<Cell>{ei.ram > 0 ? '' : showHex(ei.device_id as number, 2)}</Cell>
|
||||
<Cell>{ei.ram > 0 ? '' : showHex(ei.type_id as number, 3)}</Cell>
|
||||
<Cell>{ei.ram > 0 ? '' : ei.offset}</Cell>
|
||||
<Cell>
|
||||
{ei.ram === 1
|
||||
? 'RAM'
|
||||
: ei.ram === 2
|
||||
? 'NVS'
|
||||
: DeviceValueTypeNames[ei.value_type]}
|
||||
{ei.ram === 1 ? '' : showHex(ei.device_id as number, 2)}
|
||||
</Cell>
|
||||
<Cell>{ei.ram === 1 ? '' : showHex(ei.type_id as number, 3)}</Cell>
|
||||
<Cell>{ei.ram === 1 ? '' : ei.offset}</Cell>
|
||||
<Cell>
|
||||
{ei.ram === 1 ? 'RAM' : DeviceValueTypeNames[ei.value_type]}
|
||||
</Cell>
|
||||
<Cell>{formatValue(ei.value, ei.uom)}</Cell>
|
||||
</Row>
|
||||
|
||||
@@ -205,10 +205,9 @@ const CustomEntitiesDialog = ({
|
||||
>
|
||||
<MenuItem value={0}>EMS-{LL.VALUE(1)}</MenuItem>
|
||||
<MenuItem value={1}>RAM-{LL.VALUE(1)}</MenuItem>
|
||||
<MenuItem value={2}>NVS-{LL.VALUE(1)}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
{editItem.ram > 0 && (
|
||||
{editItem.ram === 1 && (
|
||||
<>
|
||||
<Grid>
|
||||
<TextField
|
||||
|
||||
@@ -111,14 +111,13 @@ const Customizations = () => {
|
||||
const [selectedDeviceTypeNameURL, setSelectedDeviceTypeNameURL] =
|
||||
useState<string>(''); // needed for API URL
|
||||
const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
|
||||
const [selectedDeviceBrand, setSelectedDeviceBrand] = useState<string>('');
|
||||
|
||||
const { send: sendResetCustomizations } = useRequest(resetCustomizations(), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const { send: sendDeviceName } = useRequest(
|
||||
(data: { id: number; name: string; brand: string }) => writeDeviceName(data),
|
||||
(data: { id: number; name: string }) => writeDeviceName(data),
|
||||
{
|
||||
immediate: false
|
||||
}
|
||||
@@ -268,7 +267,6 @@ const Customizations = () => {
|
||||
if (device) {
|
||||
setSelectedDeviceTypeNameURL(device.url || '');
|
||||
setSelectedDeviceName(device.n);
|
||||
setSelectedDeviceBrand(device.b);
|
||||
}
|
||||
setNumChanges(0);
|
||||
setRestartNeeded(false);
|
||||
@@ -444,11 +442,7 @@ const Customizations = () => {
|
||||
}, [devices, deviceEntities, selectedDevice, sendCustomizationEntities, LL]);
|
||||
|
||||
const renameDevice = useCallback(async () => {
|
||||
await sendDeviceName({
|
||||
id: selectedDevice,
|
||||
name: selectedDeviceName,
|
||||
brand: selectedDeviceBrand
|
||||
})
|
||||
await sendDeviceName({ id: selectedDevice, name: selectedDeviceName })
|
||||
.then(() => {
|
||||
toast.success(LL.UPDATED_OF(LL.NAME(1)));
|
||||
})
|
||||
@@ -459,14 +453,7 @@ const Customizations = () => {
|
||||
setRename(false);
|
||||
await fetchCoreData();
|
||||
});
|
||||
}, [
|
||||
selectedDevice,
|
||||
selectedDeviceName,
|
||||
selectedDeviceBrand,
|
||||
sendDeviceName,
|
||||
LL,
|
||||
fetchCoreData
|
||||
]);
|
||||
}, [selectedDevice, selectedDeviceName, sendDeviceName, LL, fetchCoreData]);
|
||||
|
||||
const renderDeviceList = () => (
|
||||
<>
|
||||
@@ -475,26 +462,15 @@ const Customizations = () => {
|
||||
</Box>
|
||||
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
|
||||
{rename ? (
|
||||
<>
|
||||
<TextField
|
||||
name="device"
|
||||
label={LL.EMS_DEVICE()}
|
||||
style={{ minWidth: '48%' }}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={selectedDeviceName}
|
||||
onChange={(e) => setSelectedDeviceName(e.target.value)}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
name="brand"
|
||||
label={LL.BRAND()}
|
||||
style={{ minWidth: '48%' }}
|
||||
variant="outlined"
|
||||
value={selectedDeviceBrand}
|
||||
onChange={(e) => setSelectedDeviceBrand(e.target.value)}
|
||||
margin="normal"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<TextField
|
||||
name="device"
|
||||
@@ -812,12 +788,10 @@ const Customizations = () => {
|
||||
</>
|
||||
);
|
||||
|
||||
return restarting ? (
|
||||
<SystemMonitor />
|
||||
) : (
|
||||
return (
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{renderContent()}
|
||||
{restarting ? <SystemMonitor /> : renderContent()}
|
||||
{selectedDeviceEntity && (
|
||||
<SettingsCustomizationsDialog
|
||||
open={dialogOpen}
|
||||
|
||||
@@ -120,7 +120,7 @@ const HelpComponent = () => {
|
||||
label: () => LL.HELP_INFORMATION_1()
|
||||
},
|
||||
{
|
||||
href: 'https://discord.gg/GP9DPSgeJq',
|
||||
href: 'https://discord.gg/3J3GgnzpyT',
|
||||
icon: <CommentIcon />,
|
||||
label: () => LL.HELP_INFORMATION_2()
|
||||
},
|
||||
|
||||
@@ -41,7 +41,6 @@ const MIN_ID = -100;
|
||||
const MAX_ID = 100;
|
||||
const ICON_SIZE = 16;
|
||||
const SCHEDULE_FLAG_THRESHOLD = 127;
|
||||
const FLAG_ALL_DAYS = 127;
|
||||
const REFERENCE_YEAR = 2017;
|
||||
const REFERENCE_MONTH = '01';
|
||||
const LOG_2 = Math.log(2);
|
||||
@@ -52,7 +51,7 @@ const WEEK_DAYS = [1, 2, 3, 4, 5, 6, 7] as const;
|
||||
const DEFAULT_SCHEDULE_ITEM: Omit<ScheduleItem, 'id' | 'o_id'> = {
|
||||
active: false,
|
||||
deleted: false,
|
||||
flags: FLAG_ALL_DAYS,
|
||||
flags: ScheduleFlag.SCHEDULE_DAY,
|
||||
time: '',
|
||||
cmd: '',
|
||||
value: '',
|
||||
|
||||
@@ -33,8 +33,7 @@ import type { ScheduleItem } from './types';
|
||||
|
||||
// Constants
|
||||
const FLAG_MASK_127 = 127;
|
||||
const SCHEDULE_TYPE_THRESHOLD = 127;
|
||||
const FLAG_ALL_DAYS = 127;
|
||||
const SCHEDULE_TYPE_THRESHOLD = 128;
|
||||
const DEFAULT_TIME = '00:00';
|
||||
const TYPOGRAPHY_FONT_SIZE = 10;
|
||||
|
||||
@@ -105,7 +104,7 @@ const SchedulerDialog = ({
|
||||
// 130 is on condition
|
||||
// 132 is immediate
|
||||
setScheduleType(
|
||||
selectedItem.flags <= SCHEDULE_TYPE_THRESHOLD
|
||||
selectedItem.flags < SCHEDULE_TYPE_THRESHOLD
|
||||
? ScheduleFlag.SCHEDULE_DAY
|
||||
: selectedItem.flags
|
||||
);
|
||||
@@ -182,7 +181,7 @@ const SchedulerDialog = ({
|
||||
setScheduleType(flag);
|
||||
// wipe the time field when changing the schedule type
|
||||
// set the flags based on type
|
||||
const newFlags = flag === ScheduleFlag.SCHEDULE_DAY ? FLAG_ALL_DAYS : flag;
|
||||
const newFlags = flag === ScheduleFlag.SCHEDULE_DAY ? 0 : flag;
|
||||
setEditItem((prev) => ({ ...prev, time: '', flags: newFlags }));
|
||||
}
|
||||
},
|
||||
@@ -191,8 +190,7 @@ const SchedulerDialog = ({
|
||||
|
||||
const handleDOWChange = useCallback(
|
||||
(_event: React.SyntheticEvent<HTMLElement>, flags: string[]) => {
|
||||
const newFlags =
|
||||
getFlagDOWnumber(flags) === 0 ? FLAG_ALL_DAYS : getFlagDOWnumber(flags);
|
||||
const newFlags = getFlagDOWnumber(flags);
|
||||
setEditItem((prev) => ({ ...prev, flags: newFlags }));
|
||||
},
|
||||
[getFlagDOWnumber]
|
||||
|
||||
@@ -196,7 +196,7 @@ export enum DeviceValueUOM {
|
||||
MBAR,
|
||||
LH,
|
||||
CTKWH,
|
||||
HERTZ
|
||||
HZ
|
||||
}
|
||||
|
||||
export const DeviceValueUOM_s = [
|
||||
|
||||
@@ -489,25 +489,17 @@ const ApplicationSettings = () => {
|
||||
name="board_profile"
|
||||
label={LL.BOARD_PROFILE()}
|
||||
value={data.board_profile}
|
||||
disabled={processingBoard}
|
||||
disabled={processingBoard || hardwareData.model.startsWith('BBQKees')}
|
||||
variant="outlined"
|
||||
onChange={changeBoardProfile}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
{hardwareData.model.startsWith('BBQKees') ? (
|
||||
<MenuItem key={hardwareData.board} value={hardwareData.board}>
|
||||
{BOARD_PROFILES[hardwareData.board as BoardProfileKey]}
|
||||
</MenuItem>
|
||||
) : (
|
||||
boardProfileItems
|
||||
)}
|
||||
{(data.board_profile === 'CUSTOM' || data.developer_mode) && <Divider />}
|
||||
{(data.board_profile === 'CUSTOM' || data.developer_mode) && (
|
||||
{boardProfileItems}
|
||||
<Divider />
|
||||
<MenuItem key={'CUSTOM'} value={'CUSTOM'}>
|
||||
{LL.CUSTOM()}…
|
||||
</MenuItem>
|
||||
)}
|
||||
</TextField>
|
||||
{data.board_profile === 'CUSTOM' && (
|
||||
<>
|
||||
@@ -904,12 +896,10 @@ const ApplicationSettings = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return restarting ? (
|
||||
<SystemMonitor />
|
||||
) : (
|
||||
return (
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
{restarting ? <SystemMonitor /> : content()}
|
||||
</SectionContent>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -345,7 +345,7 @@ const MqttSettings = () => {
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
{/* <Grid container spacing={2} rowSpacing={0}> */}
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
@@ -411,25 +411,9 @@ const MqttSettings = () => {
|
||||
</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid>
|
||||
{data.discovery_type === 0 && (
|
||||
<TextField
|
||||
name="ha_number_mode"
|
||||
label={LL.MQTT_INPUT_NUMBER_FORMAT()}
|
||||
value={data.ha_number_mode}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
sx={{ width: '20ch' }}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>Box</MenuItem>
|
||||
<MenuItem value={1}>Slider</MenuItem>
|
||||
</TextField>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
||||
{LL.MQTT_PUBLISH_INTERVALS()} (0=auto)
|
||||
</Typography>
|
||||
|
||||
@@ -182,7 +182,7 @@ const Settings = () => {
|
||||
restarting
|
||||
]);
|
||||
|
||||
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
|
||||
return <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -397,12 +397,10 @@ const NetworkSettings = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return restarting ? (
|
||||
<SystemMonitor />
|
||||
) : (
|
||||
return (
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{content()}
|
||||
{restarting ? <SystemMonitor /> : content()}
|
||||
</SectionContent>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -443,7 +443,7 @@ const SystemStatus = () => {
|
||||
renderRestartDialog
|
||||
]);
|
||||
|
||||
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
|
||||
return <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
|
||||
};
|
||||
|
||||
export default SystemStatus;
|
||||
|
||||
@@ -38,6 +38,8 @@ import type { LogEntry, LogSettings } from 'types';
|
||||
import { LogLevel } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
|
||||
const MAX_LOG_ENTRIES = 1000; // Limit log entries to prevent memory issues
|
||||
|
||||
const TextColors: Record<LogLevel, string> = {
|
||||
[LogLevel.ERROR]: '#ff0000', // red
|
||||
[LogLevel.WARNING]: '#ff0000', // red
|
||||
@@ -198,6 +200,10 @@ const SystemLog = () => {
|
||||
}
|
||||
}
|
||||
const newLog = [...log, logentry];
|
||||
// Limit log entries to prevent memory issues - only slice when necessary
|
||||
if (newLog.length > MAX_LOG_ENTRIES) {
|
||||
return newLog.slice(-MAX_LOG_ENTRIES);
|
||||
}
|
||||
return newLog;
|
||||
});
|
||||
}, []);
|
||||
@@ -302,8 +308,6 @@ const SystemLog = () => {
|
||||
<MenuItem value={50}>50</MenuItem>
|
||||
<MenuItem value={75}>75</MenuItem>
|
||||
<MenuItem value={100}>100</MenuItem>
|
||||
<MenuItem value={500}>500</MenuItem>
|
||||
<MenuItem value={1000}>1000</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
@@ -103,8 +103,8 @@ const SystemMonitor = () => {
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
// backdropFilter: 'blur(8px)'
|
||||
justifyContent: 'center',
|
||||
backdropFilter: 'blur(8px)'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
|
||||
@@ -868,7 +868,7 @@ const Version = () => {
|
||||
installPartitionFirmware
|
||||
]);
|
||||
|
||||
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
|
||||
return <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
|
||||
};
|
||||
|
||||
export default memo(Version);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type FC, type PropsWithChildren, memo } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
import type { BoxProps } from '@mui/material';
|
||||
|
||||
const ButtonRow: FC<PropsWithChildren<BoxProps>> = memo(({ children, ...rest }) => (
|
||||
const ButtonRow = memo<BoxProps>(({ children, ...rest }) => (
|
||||
<Box
|
||||
sx={{
|
||||
'& button, & a, & .MuiCard-root': {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type FC, type PropsWithChildren, memo, useMemo } from 'react';
|
||||
import { type FC, memo, useMemo } from 'react';
|
||||
|
||||
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
|
||||
import ErrorIcon from '@mui/icons-material/Error';
|
||||
@@ -12,7 +12,6 @@ type MessageBoxLevel = 'warning' | 'success' | 'info' | 'error';
|
||||
export interface MessageBoxProps extends BoxProps {
|
||||
level: MessageBoxLevel;
|
||||
message?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const LEVEL_ICONS: Record<MessageBoxLevel, React.ComponentType<SvgIconProps>> = {
|
||||
@@ -29,7 +28,7 @@ const LEVEL_PALETTE_PATHS: Record<MessageBoxLevel, string> = {
|
||||
error: 'error.dark'
|
||||
};
|
||||
|
||||
const MessageBox: FC<PropsWithChildren<MessageBoxProps>> = ({
|
||||
const MessageBox: FC<MessageBoxProps> = ({
|
||||
level,
|
||||
message,
|
||||
sx,
|
||||
|
||||
@@ -39,8 +39,9 @@ const LayoutMenuItemComponent = ({
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: selected ? '3px' : '0px',
|
||||
width: selected ? '4px' : '0px',
|
||||
backgroundColor: '#90caf9',
|
||||
borderRadius: '0 2px 2px 0',
|
||||
transition: 'width 0.05s cubic-bezier(0.55, 0.085, 0.68, 0.53)'
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -219,7 +219,6 @@ const cz: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Povolit MQTT Discovery',
|
||||
MQTT_PUBLISH_TEXT_4: 'Prefix pro Discovery témata',
|
||||
MQTT_PUBLISH_TEXT_5: 'Typ Discovery',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Formát čísla',
|
||||
MQTT_PUBLISH_INTERVALS: 'Intervaly publikování',
|
||||
MQTT_INT_BOILER: 'Kotle a tepelná čerpadla',
|
||||
MQTT_INT_THERMOSTATS: 'Termostaty',
|
||||
|
||||
@@ -219,7 +219,6 @@ const de: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Aktiviere `MQTT Discovery`',
|
||||
MQTT_PUBLISH_TEXT_4: 'Prefix für die `Discovery`-Topics',
|
||||
MQTT_PUBLISH_TEXT_5: 'Discovery Typ',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Zahlenformat',
|
||||
MQTT_PUBLISH_INTERVALS: 'Veröffentlichungs-Intervalle',
|
||||
MQTT_INT_BOILER: 'Boiler und Wärmepumpen',
|
||||
MQTT_INT_THERMOSTATS: 'Thermostate',
|
||||
|
||||
@@ -219,7 +219,6 @@ const en: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Enable MQTT Discovery',
|
||||
MQTT_PUBLISH_TEXT_4: 'Prefix for the Discovery topics',
|
||||
MQTT_PUBLISH_TEXT_5: 'Discovery type',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Input Number Fomat',
|
||||
MQTT_PUBLISH_INTERVALS: 'Publish Intervals',
|
||||
MQTT_INT_BOILER: 'Boilers and Heat Pumps',
|
||||
MQTT_INT_THERMOSTATS: 'Thermostats',
|
||||
|
||||
@@ -219,7 +219,6 @@ const fr: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Activer la découverte MQTT',
|
||||
MQTT_PUBLISH_TEXT_4: 'Préfixe pour les topics découverte',
|
||||
MQTT_PUBLISH_TEXT_5: 'Type de découverte',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Format des nombres',
|
||||
MQTT_PUBLISH_INTERVALS: 'Intervalles de publication',
|
||||
MQTT_INT_BOILER: 'Chaudières et pompes à chaleur',
|
||||
MQTT_INT_THERMOSTATS: 'Thermostats',
|
||||
|
||||
@@ -219,7 +219,6 @@ const it: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Abilita rilevamento MQTT (Home Assistant, Domoticz)',
|
||||
MQTT_PUBLISH_TEXT_4: 'Prefisso per gli argomenti di scoperta',
|
||||
MQTT_PUBLISH_TEXT_5: 'Discovery type',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Formato numero di input',
|
||||
MQTT_PUBLISH_INTERVALS: 'Pubblica intervalli',
|
||||
MQTT_INT_BOILER: 'Caldaie e Pompe di Calore',
|
||||
MQTT_INT_THERMOSTATS: 'Termostati',
|
||||
|
||||
@@ -27,7 +27,7 @@ const nl: Translation = {
|
||||
REFRESH: 'Ververs',
|
||||
EXPORT: 'Export',
|
||||
FAVORITES: "Favorieten",
|
||||
DEVICE_DETAILS: 'Apparaat Gegevens',
|
||||
DEVICE_DETAILS: 'Device Gegevens',
|
||||
ID_OF: '{0} ID',
|
||||
DEVICE: 'Apparaat',
|
||||
PRODUCT: 'Product',
|
||||
@@ -65,7 +65,7 @@ const nl: Translation = {
|
||||
TEMP_SENSOR: 'Temperatuur sensor',
|
||||
TEMP_SENSORS: 'Temperatuur Sensoren',
|
||||
WRITE_CMD_SENT: 'Schrijf commando gestuurd',
|
||||
EMS_BUS_WARNING: 'EMS bus niet gevonden. Als deze waarschuwing blijft staan na een paar seconden loop dan de instellingen na en in het bijzonder het apparaat type profiel.',
|
||||
EMS_BUS_WARNING: 'EMS bus niet gevonden. Als deze waarschuwing blijft staan na een paar seconden dan loop de instellingen na en in het bijzonder het apparaat type profiel na.',
|
||||
EMS_BUS_SCANNING: 'Scannen naar EMS apparaten...',
|
||||
CONNECTED: 'Verbonden',
|
||||
TX_ISSUES: 'Tx bus probleem. Probeer een andere Tx verzendmodus',
|
||||
@@ -75,7 +75,7 @@ const nl: Translation = {
|
||||
EMS_DEVICE: 'EMS Apparaat',
|
||||
SUCCESS: 'SUCCESS',
|
||||
FAIL: 'MISLUKT',
|
||||
QUALITY: 'KWALITEIT',
|
||||
QUALITY: 'QUALITEIT',
|
||||
SCAN: 'Scan',
|
||||
STATUS_NAMES: [
|
||||
'EMS Telegrammen ontvangen (Rx)',
|
||||
@@ -120,7 +120,7 @@ const nl: Translation = {
|
||||
ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)',
|
||||
ENABLE_SHOWER_ALERT: 'Activeer Douchemelding',
|
||||
TRIGGER_TIME: 'Trigger tijd',
|
||||
COLD_SHOT_DURATION: 'Lengte koud water puls',
|
||||
COLD_SHOT_DURATION: 'Tijd Shot koud water',
|
||||
FORMATTING_OPTIONS: 'Formatteringsopties',
|
||||
BOOLEAN_FORMAT_DASHBOARD: 'Boolean formaat web',
|
||||
BOOLEAN_FORMAT_API: 'Boolean formaat API/MQTT',
|
||||
@@ -143,7 +143,7 @@ const nl: Translation = {
|
||||
CUSTOMIZATIONS_FULL: 'Te veel entiteiten geselecteerd. Sla op in delen aub',
|
||||
CUSTOMIZATIONS_SAVED: 'Custom aanpassingen opgeslagen',
|
||||
CUSTOMIZATIONS_HELP_1: 'Selecteer een apparaat en pas de entiteiten aan door middel van de opties',
|
||||
CUSTOMIZATIONS_HELP_2: 'Markeer als favoriet',
|
||||
CUSTOMIZATIONS_HELP_2: 'Markeer as favoriet',
|
||||
CUSTOMIZATIONS_HELP_3: 'Zet schrijfacties uit',
|
||||
CUSTOMIZATIONS_HELP_4: 'Uitsluiten van MQTT en API',
|
||||
CUSTOMIZATIONS_HELP_5: 'verbergen voor apparaten',
|
||||
@@ -191,7 +191,7 @@ const nl: Translation = {
|
||||
UPLOAD_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier',
|
||||
ERROR: 'Onverwachte fout, probeer opnieuw',
|
||||
TIME_SET: 'Tijd ingesteld',
|
||||
MANAGE_USERS: 'Gebruikersbeheer',
|
||||
MANAGE_USERS: 'Beheer Gebruikers',
|
||||
IS_ADMIN: 'is Admin',
|
||||
USER_WARNING: 'U dient tenminste 1 admin gebruiker te configureren',
|
||||
ADD: 'Toevoegen',
|
||||
@@ -200,7 +200,7 @@ const nl: Translation = {
|
||||
GENERATING_TOKEN: 'Token aan het genereren',
|
||||
USER: 'Gebruiker',
|
||||
MODIFY: 'Aanpassen',
|
||||
SU_TEXT: 'Het su (super user) wachtwoord wordt gebruikt om authorisatie tokens te ondertekenen en ook om admin privileges te activeren in de console.',
|
||||
SU_TEXT: 'Het su (super user) wachtwoord wordt gebruikt om authorisatie tokens te signeren en ook om admin privileges te activeren in de console.',
|
||||
NOT_ENABLED: 'Niet geactiveerd',
|
||||
ERRORS_OF: '{0} Foutmeldingen',
|
||||
DISCONNECT_REASON: 'Verbinding verbroken vanwege',
|
||||
@@ -219,7 +219,6 @@ const nl: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Activeer MQTT Discovery',
|
||||
MQTT_PUBLISH_TEXT_4: 'Prefix voor de Discovery topics',
|
||||
MQTT_PUBLISH_TEXT_5: 'Discovery type',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Getalformaat',
|
||||
MQTT_PUBLISH_INTERVALS: 'Publicatie intervallen',
|
||||
MQTT_INT_BOILER: 'CV ketels en warmtepompen',
|
||||
MQTT_INT_THERMOSTATS: 'Thermostaten',
|
||||
|
||||
@@ -219,7 +219,6 @@ const no: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Aktiver MQTT Discovery',
|
||||
MQTT_PUBLISH_TEXT_4: 'Prefiks for Discovery topics',
|
||||
MQTT_PUBLISH_TEXT_5: 'Discovery type',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Nummerformat',
|
||||
MQTT_PUBLISH_INTERVALS: 'Publiseringsintervall',
|
||||
MQTT_INT_BOILER: 'Fyr/Varmepumpe',
|
||||
MQTT_INT_THERMOSTATS: 'Termostat',
|
||||
|
||||
@@ -219,7 +219,6 @@ const pl: BaseTranslation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Włącz opcję "MQTT discovery"',
|
||||
MQTT_PUBLISH_TEXT_4: 'Prefiks dla "MQTT discovery"',
|
||||
MQTT_PUBLISH_TEXT_5: 'Typ "MQTT discovery"',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Format liczby',
|
||||
MQTT_PUBLISH_INTERVALS: 'Interwały publikowania',
|
||||
MQTT_INT_BOILER: 'Kotły i pompy ciepła',
|
||||
MQTT_INT_THERMOSTATS: 'Termostaty',
|
||||
|
||||
@@ -219,7 +219,6 @@ const sk: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Povolenie zisťovania MQTT',
|
||||
MQTT_PUBLISH_TEXT_4: 'Predpona tém Discovery',
|
||||
MQTT_PUBLISH_TEXT_5: 'Typ zistenia',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Formát čísla',
|
||||
MQTT_PUBLISH_INTERVALS: 'Intervaly zverejňovania',
|
||||
MQTT_INT_BOILER: 'Kotly a tepelné čerpadlá',
|
||||
MQTT_INT_THERMOSTATS: 'Termostaty',
|
||||
|
||||
@@ -219,7 +219,6 @@ const sv: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'Aktivera MQTT Discovery',
|
||||
MQTT_PUBLISH_TEXT_4: 'Prefix för Discovery topics',
|
||||
MQTT_PUBLISH_TEXT_5: 'Discoverytyp',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Nummerformat',
|
||||
MQTT_PUBLISH_INTERVALS: 'Publiceringsintervall',
|
||||
MQTT_INT_BOILER: 'Värmepump/panna',
|
||||
MQTT_INT_THERMOSTATS: 'Termostater',
|
||||
|
||||
@@ -219,7 +219,6 @@ const tr: Translation = {
|
||||
MQTT_PUBLISH_TEXT_3: 'MQTT keşfi etkinleştir (Home Assistant, Domoticz)',
|
||||
MQTT_PUBLISH_TEXT_4: 'Keşif konuları için ön ek',
|
||||
MQTT_PUBLISH_TEXT_5: 'Domoticz Format',
|
||||
MQTT_INPUT_NUMBER_FORMAT: 'Sayı Biçimi',
|
||||
MQTT_PUBLISH_INTERVALS: 'Yayınlama aralıkları',
|
||||
MQTT_INT_BOILER: 'Kazanlar ve Isı Pompaları',
|
||||
MQTT_INT_THERMOSTATS: 'Termostatlar',
|
||||
|
||||
@@ -49,5 +49,4 @@ export interface MqttSettingsType {
|
||||
publish_single2cmd: boolean;
|
||||
discovery_prefix: string;
|
||||
discovery_type: number;
|
||||
ha_number_mode: number;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ export interface SystemStatus {
|
||||
free_psram?: number;
|
||||
free_caps: number;
|
||||
model: string;
|
||||
board: string;
|
||||
has_loader: boolean;
|
||||
has_partition: boolean;
|
||||
partitions: {
|
||||
|
||||
@@ -136,16 +136,9 @@ const createManualChunks = (detailed = false) => {
|
||||
return 'vendor';
|
||||
}
|
||||
if (detailed) {
|
||||
// Group circularly dependent modules together to avoid circular chunk warnings
|
||||
// components, app, and utils are tightly coupled, so combine them
|
||||
if (
|
||||
id.includes('components/') ||
|
||||
id.includes('app/') ||
|
||||
id.includes('utils/')
|
||||
) {
|
||||
return 'app';
|
||||
}
|
||||
// Keep api separate as it's typically more independent
|
||||
if (id.includes('components/')) return 'components';
|
||||
if (id.includes('app/')) return 'app';
|
||||
if (id.includes('utils/')) return 'utils';
|
||||
if (id.includes('api/')) return 'api';
|
||||
}
|
||||
return undefined;
|
||||
|
||||
@@ -52,7 +52,7 @@ ModbusClientTCPasync::~ModbusClientTCPasync() {
|
||||
}
|
||||
}
|
||||
// force close client
|
||||
MTA_client.close();
|
||||
MTA_client.close(true);
|
||||
}
|
||||
|
||||
// optionally manually connect to modbus server. Otherwise connection will be made upon first request
|
||||
@@ -79,7 +79,7 @@ void ModbusClientTCPasync::connect(IPAddress host, uint16_t port) {
|
||||
// manually disconnect from modbus server. Connection will also auto close after idle time
|
||||
void ModbusClientTCPasync::disconnect(bool force) {
|
||||
LOG_D("disconnecting\n");
|
||||
MTA_client.close();
|
||||
MTA_client.close(force);
|
||||
}
|
||||
|
||||
// Set timeout value
|
||||
|
||||
@@ -253,7 +253,7 @@ void ModbusServerTCPasync::onClientConnect(AsyncClient* client) {
|
||||
LOG_D("nr clients: %u\n", clients.size());
|
||||
} else {
|
||||
LOG_D("max number of clients reached, closing new\n");
|
||||
client->close();
|
||||
client->close(true);
|
||||
delete client;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,14 +321,7 @@ bool Packet::_allocate(size_t remainingLength, bool check) {
|
||||
#if EMC_USE_MEMPOOL
|
||||
_data = reinterpret_cast<uint8_t*>(_memPool.malloc(_size));
|
||||
#else
|
||||
#ifdef EMSESP_STANDALONE
|
||||
_data = reinterpret_cast<uint8_t*>(malloc(_size));
|
||||
#else
|
||||
_data = reinterpret_cast<uint8_t*>(ps_malloc(_size));
|
||||
if (!_data) {
|
||||
_data = reinterpret_cast<uint8_t*>(malloc(_size));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
if (!_data) {
|
||||
_size = 0;
|
||||
|
||||
@@ -42,7 +42,7 @@ int ClientAsync::read(uint8_t* buf, size_t size) {
|
||||
}
|
||||
|
||||
void ClientAsync::stop() {
|
||||
client.close();
|
||||
client.close(false);
|
||||
}
|
||||
|
||||
bool ClientAsync::connected() {
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <esp32-psram.h>
|
||||
#include <uuid/common.h>
|
||||
#include <uuid/log.h>
|
||||
|
||||
@@ -1648,13 +1647,13 @@ class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Hand
|
||||
|
||||
Stream & stream_; /*!< Stream used for the input/output of this shell. @since 3.0.0 */
|
||||
std::shared_ptr<Commands> commands_; /*!< Commands available for execution in this shell. @since 0.1.0 */
|
||||
std::deque<unsigned int, AllocatorPSRAM<unsigned int>> context_; /*!< Context stack for this shell. Affects which commands are available. Should never be empty. @since 0.1.0 */
|
||||
std::deque<unsigned int> context_; /*!< Context stack for this shell. Affects which commands are available. Should never be empty. @since 0.1.0 */
|
||||
unsigned int flags_ = 0; /*!< Current flags for this shell. Affects which commands are available. @since 0.1.0 */
|
||||
#if UUID_CONSOLE_THREAD_SAFE
|
||||
mutable std::mutex mutex_; /*!< Mutex for queued log messages. @since 1.0.0 */
|
||||
#endif
|
||||
unsigned long log_message_id_ = 0; /*!< The next identifier to use for queued log messages. @since 0.1.0 */
|
||||
std::list<QueuedLogMessage, AllocatorPSRAM<QueuedLogMessage>> log_messages_; /*!< Queued log messages, in the order they were received. @since 0.1.0 */
|
||||
std::list<QueuedLogMessage> log_messages_; /*!< Queued log messages, in the order they were received. @since 0.1.0 */
|
||||
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum command line length in bytes. @since 0.6.0 */
|
||||
std::string line_buffer_; /*!< Command line buffer. Limited to maximum_command_line_length() bytes. @since 0.1.0 */
|
||||
size_t maximum_command_line_length_ = MAX_COMMAND_LINE_LENGTH; /*!< Maximum command line length in bytes. @since 0.6.0 */
|
||||
@@ -1787,7 +1786,7 @@ class CommandLine {
|
||||
* @return A reference to the parameters.
|
||||
* @since 0.6.0
|
||||
*/
|
||||
inline std::vector<std::string, AllocatorPSRAM<std::string>> & operator*() {
|
||||
inline std::vector<std::string> & operator*() {
|
||||
return parameters_;
|
||||
}
|
||||
/**
|
||||
@@ -1796,7 +1795,7 @@ class CommandLine {
|
||||
* @return A reference to the parameters.
|
||||
* @since 0.6.0
|
||||
*/
|
||||
inline const std::vector<std::string, AllocatorPSRAM<std::string>> & operator*() const {
|
||||
inline const std::vector<std::string> & operator*() const {
|
||||
return parameters_;
|
||||
}
|
||||
/**
|
||||
@@ -1805,7 +1804,7 @@ class CommandLine {
|
||||
* @return A pointer to the parameters.
|
||||
* @since 0.4.0
|
||||
*/
|
||||
inline std::vector<std::string, AllocatorPSRAM<std::string>> * operator->() {
|
||||
inline std::vector<std::string> * operator->() {
|
||||
return ¶meters_;
|
||||
}
|
||||
/**
|
||||
@@ -1814,7 +1813,7 @@ class CommandLine {
|
||||
* @return A pointer to the parameters.
|
||||
* @since 0.4.0
|
||||
*/
|
||||
inline const std::vector<std::string, AllocatorPSRAM<std::string>> * operator->() const {
|
||||
inline const std::vector<std::string> * operator->() const {
|
||||
return ¶meters_;
|
||||
}
|
||||
|
||||
@@ -1844,7 +1843,7 @@ class CommandLine {
|
||||
bool trailing_space = false; /*!< Command line has a trailing space. @since 0.4.0 */
|
||||
|
||||
private:
|
||||
std::vector<std::string, AllocatorPSRAM<std::string>> parameters_; /*!< Separate command line parameters. @since 0.4.0 */
|
||||
std::vector<std::string> parameters_; /*!< Separate command line parameters. @since 0.4.0 */
|
||||
size_t escape_parameters_ = std::numeric_limits<size_t>::max(); /*!< Number of initial arguments to escape in output. @since 0.5.0 */
|
||||
};
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ void Logger::vlog(Level level, const char * format, va_list ap) const {
|
||||
}
|
||||
|
||||
void Logger::vlog(Level level, Facility facility, const char * format, va_list ap) const {
|
||||
std::vector<char, AllocatorPSRAM<char>> text(MAX_LOG_LENGTH + 1);
|
||||
std::vector<char> text(MAX_LOG_LENGTH + 1);
|
||||
|
||||
if (vsnprintf(text.data(), text.size(), format, ap) <= 0) {
|
||||
return;
|
||||
@@ -241,7 +241,7 @@ void Logger::vlog(Level level, Facility facility, const char * format, va_list a
|
||||
dispatch(level, facility, text);
|
||||
}
|
||||
|
||||
void Logger::dispatch(Level level, Facility facility, std::vector<char, AllocatorPSRAM<char>> & text) const {
|
||||
void Logger::dispatch(Level level, Facility facility, std::vector<char> & text) const {
|
||||
std::shared_ptr<Message> message = std::make_shared<Message>(get_uptime_ms(), level, facility, name_, text.data());
|
||||
text.resize(0);
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <esp32-psram.h>
|
||||
#include <uuid/common.h>
|
||||
|
||||
#ifndef UUID_COMMON_THREAD_SAFE
|
||||
@@ -646,7 +645,7 @@ class Logger {
|
||||
* @param[in] text Log message text.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void dispatch(Level level, Facility facility, std::vector<char, AllocatorPSRAM<char>> & text) const;
|
||||
void dispatch(Level level, Facility facility, std::vector<char> & text) const;
|
||||
|
||||
static std::atomic<Level> global_level_; /*!< Minimum global log level across all handlers. @since 3.0.0 */
|
||||
#if UUID_LOG_THREAD_SAFE
|
||||
@@ -724,7 +723,7 @@ class PrintHandler : public uuid::log::Handler {
|
||||
mutable std::mutex mutex_; /*!< Mutex for configuration, state and queued log messages. @since 2.3.0 */
|
||||
#endif
|
||||
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum number of log messages to buffer before they are output. @since 2.2.0 */
|
||||
std::list<std::shared_ptr<Message>, AllocatorPSRAM<std::shared_ptr<Message>>> log_messages_; /*!< Queued log messages, in the order they were received. @since 2.2.0 */
|
||||
std::list<std::shared_ptr<Message>> log_messages_; /*!< Queued log messages, in the order they were received. @since 2.2.0 */
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <esp32-psram.h>
|
||||
#include <uuid/log.h>
|
||||
|
||||
#ifndef UUID_LOG_THREAD_SAFE
|
||||
@@ -322,7 +321,7 @@ class SyslogService : public uuid::log::Handler {
|
||||
#endif
|
||||
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum number of log messages to buffer before they are output. @since 1.0.0 */
|
||||
unsigned long log_message_id_ = 0; /*!< The next identifier to use for queued log messages. @since 1.0.0 */
|
||||
std::list<QueuedLogMessage, AllocatorPSRAM<QueuedLogMessage>> log_messages_; /*!< Queued log messages, in the order they were received. @since 1.0.0 */
|
||||
std::list<QueuedLogMessage> log_messages_; /*!< Queued log messages, in the order they were received. @since 1.0.0 */
|
||||
uint64_t mark_interval_ = 0; /*!< Mark interval in milliseconds. @since 2.0.0 */
|
||||
uint64_t last_message_ = 0; /*!< Last message/mark time. @since 2.0.0 */
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ size_t TelnetStream::write(uint8_t data) {
|
||||
}
|
||||
|
||||
size_t TelnetStream::write(const uint8_t * buffer, size_t size) {
|
||||
std::vector<unsigned char, AllocatorPSRAM<unsigned char>> data;
|
||||
std::vector<unsigned char> data;
|
||||
data.reserve(size);
|
||||
|
||||
while (size-- > 0) {
|
||||
@@ -310,7 +310,7 @@ size_t TelnetStream::raw_write(unsigned char data) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t TelnetStream::raw_write(const std::vector<unsigned char, AllocatorPSRAM<unsigned char>> & data) {
|
||||
size_t TelnetStream::raw_write(const std::vector<unsigned char> & data) {
|
||||
return raw_write(reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <esp32-psram.h>
|
||||
#include <uuid/console.h>
|
||||
|
||||
namespace uuid {
|
||||
@@ -204,7 +203,7 @@ class TelnetStream : public ::Stream {
|
||||
* @return The number of bytes that were output.
|
||||
* @since 0.1.0
|
||||
*/
|
||||
size_t raw_write(const std::vector<unsigned char, AllocatorPSRAM<unsigned char>> & data);
|
||||
size_t raw_write(const std::vector<unsigned char> & data);
|
||||
/**
|
||||
* Write an array of bytes directly to the output stream.
|
||||
*
|
||||
@@ -223,7 +222,7 @@ class TelnetStream : public ::Stream {
|
||||
unsigned char previous_in_ = 0; /*!< Previous character that was received. Used to detect CR NUL. @since 0.1.0 */
|
||||
unsigned char previous_out_ = 0; /*!< Previous character that was sent. Used to insert NUL after CR without LF. @since 0.1.0 */
|
||||
int peek_ = -1; /*!< Previously read data cached by peek(). @since 0.1.0 */
|
||||
std::vector<char, AllocatorPSRAM<char>> output_buffer_; /*!< Buffer data to be output until a read function is called. @since 0.1.0 */
|
||||
std::vector<char> output_buffer_; /*!< Buffer data to be output until a read function is called. @since 0.1.0 */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -426,7 +425,7 @@ class TelnetService {
|
||||
|
||||
WiFiServer server_; /*!< TCP server. @since 0.1.0 */
|
||||
size_t maximum_connections_ = MAX_CONNECTIONS; /*!< Maximum number of concurrent open connections. @since 0.1.0 */
|
||||
std::list<Connection, AllocatorPSRAM<Connection>> connections_; /*!< Open connections. @since 0.1.0 */
|
||||
std::list<Connection> connections_; /*!< Open connections. @since 0.1.0 */
|
||||
shell_factory_function shell_factory_; /*!< Function to create a shell. @since 0.1.0 */
|
||||
unsigned long initial_idle_timeout_ = DEFAULT_IDLE_TIMEOUT; /*!< Initial idle timeout (in seconds). @since 0.1.0 */
|
||||
unsigned long write_timeout_ = DEFAULT_WRITE_TIMEOUT; /*!< Write timeout (in milliseconds). @since 0.1.0 */
|
||||
|
||||
@@ -76,10 +76,6 @@ void ledcWrite(uint8_t chan, uint32_t duty);
|
||||
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
|
||||
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
|
||||
|
||||
inline int rtc_get_reset_reason(int cpu_no) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PROGMEM
|
||||
#define PGM_P const char *
|
||||
|
||||
|
||||
@@ -26,157 +26,5 @@ class AsyncServer {
|
||||
uint16_t _port;
|
||||
};
|
||||
|
||||
namespace asyncsrv {
|
||||
|
||||
static constexpr const char empty[] = "";
|
||||
|
||||
static constexpr const char T__opaque[] = "\", opaque=\"";
|
||||
static constexpr const char T_100_CONTINUE[] = "100-continue";
|
||||
static constexpr const char T_13[] = "13";
|
||||
static constexpr const char T_ACCEPT[] = "Accept";
|
||||
static constexpr const char T_Accept_Ranges[] = "Accept-Ranges";
|
||||
static constexpr const char T_attachment[] = "attachment; filename=\"";
|
||||
static constexpr const char T_AUTH[] = "Authorization";
|
||||
static constexpr const char T_auth_nonce[] = "\", qop=\"auth\", nonce=\"";
|
||||
static constexpr const char T_BASIC[] = "Basic";
|
||||
static constexpr const char T_BASIC_REALM[] = "Basic realm=\"";
|
||||
static constexpr const char T_BEARER[] = "Bearer";
|
||||
static constexpr const char T_BODY[] = "body";
|
||||
static constexpr const char T_Cache_Control[] = "Cache-Control";
|
||||
static constexpr const char T_chunked[] = "chunked";
|
||||
static constexpr const char T_close[] = "close";
|
||||
static constexpr const char T_cnonce[] = "cnonce";
|
||||
static constexpr const char T_Connection[] = "Connection";
|
||||
static constexpr const char T_Content_Disposition[] = "Content-Disposition";
|
||||
static constexpr const char T_Content_Encoding[] = "Content-Encoding";
|
||||
static constexpr const char T_Content_Length[] = "Content-Length";
|
||||
static constexpr const char T_Content_Type[] = "Content-Type";
|
||||
static constexpr const char T_Content_Location[] = "Content-Location";
|
||||
static constexpr const char T_Cookie[] = "Cookie";
|
||||
static constexpr const char T_CORS_ACAC[] = "Access-Control-Allow-Credentials";
|
||||
static constexpr const char T_CORS_ACAH[] = "Access-Control-Allow-Headers";
|
||||
static constexpr const char T_CORS_ACAM[] = "Access-Control-Allow-Methods";
|
||||
static constexpr const char T_CORS_ACAO[] = "Access-Control-Allow-Origin";
|
||||
static constexpr const char T_CORS_ACMA[] = "Access-Control-Max-Age";
|
||||
static constexpr const char T_CORS_O[] = "Origin";
|
||||
static constexpr const char T_data_[] = "data: ";
|
||||
static constexpr const char T_Date[] = "Date";
|
||||
static constexpr const char T_DIGEST[] = "Digest";
|
||||
static constexpr const char T_DIGEST_[] = "Digest ";
|
||||
static constexpr const char T_ETag[] = "ETag";
|
||||
static constexpr const char T_event_[] = "event: ";
|
||||
static constexpr const char T_EXPECT[] = "Expect";
|
||||
static constexpr const char T_FALSE[] = "false";
|
||||
static constexpr const char T_filename[] = "filename";
|
||||
static constexpr const char T_gzip[] = "gzip";
|
||||
static constexpr const char T_Host[] = "host";
|
||||
static constexpr const char T_HTTP_1_0[] = "HTTP/1.0";
|
||||
static constexpr const char T_HTTP_100_CONT[] = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||
static constexpr const char T_id__[] = "id: ";
|
||||
static constexpr const char T_IMS[] = "If-Modified-Since";
|
||||
static constexpr const char T_INM[] = "If-None-Match";
|
||||
static constexpr const char T_inline[] = "inline";
|
||||
static constexpr const char T_keep_alive[] = "keep-alive";
|
||||
static constexpr const char T_Last_Event_ID[] = "Last-Event-ID";
|
||||
static constexpr const char T_Last_Modified[] = "Last-Modified";
|
||||
static constexpr const char T_LOCATION[] = "Location";
|
||||
static constexpr const char T_LOGIN_REQ[] = "Login Required";
|
||||
static constexpr const char T_MULTIPART_[] = "multipart/";
|
||||
static constexpr const char T_name[] = "name";
|
||||
static constexpr const char T_nc[] = "nc";
|
||||
static constexpr const char T_no_cache[] = "no-cache";
|
||||
static constexpr const char T_nonce[] = "nonce";
|
||||
static constexpr const char T_none[] = "none";
|
||||
static constexpr const char T_opaque[] = "opaque";
|
||||
static constexpr const char T_qop[] = "qop";
|
||||
static constexpr const char T_realm[] = "realm";
|
||||
static constexpr const char T_realm__[] = "realm=\"";
|
||||
static constexpr const char T_response[] = "response";
|
||||
static constexpr const char T_retry_[] = "retry: ";
|
||||
static constexpr const char T_retry_after[] = "Retry-After";
|
||||
static constexpr const char T_nn[] = "\n\n";
|
||||
static constexpr const char T_rn[] = "\r\n";
|
||||
static constexpr const char T_rnrn[] = "\r\n\r\n";
|
||||
static constexpr const char T_Server[] = "Server";
|
||||
static constexpr const char T_Transfer_Encoding[] = "Transfer-Encoding";
|
||||
static constexpr const char T_TRUE[] = "true";
|
||||
static constexpr const char T_UPGRADE[] = "Upgrade";
|
||||
static constexpr const char T_uri[] = "uri";
|
||||
static constexpr const char T_username[] = "username";
|
||||
static constexpr const char T_WS[] = "websocket";
|
||||
static constexpr const char T_WWW_AUTH[] = "WWW-Authenticate";
|
||||
|
||||
// HTTP Methods
|
||||
static constexpr const char T_ANY[] = "ANY";
|
||||
static constexpr const char T_GET[] = "GET";
|
||||
static constexpr const char T_POST[] = "POST";
|
||||
static constexpr const char T_PUT[] = "PUT";
|
||||
static constexpr const char T_DELETE[] = "DELETE";
|
||||
static constexpr const char T_PATCH[] = "PATCH";
|
||||
static constexpr const char T_HEAD[] = "HEAD";
|
||||
static constexpr const char T_OPTIONS[] = "OPTIONS";
|
||||
static constexpr const char T_UNKNOWN[] = "UNKNOWN";
|
||||
|
||||
// Req content types
|
||||
static constexpr const char T_RCT_NOT_USED[] = "RCT_NOT_USED";
|
||||
static constexpr const char T_RCT_DEFAULT[] = "RCT_DEFAULT";
|
||||
static constexpr const char T_RCT_HTTP[] = "RCT_HTTP";
|
||||
static constexpr const char T_RCT_WS[] = "RCT_WS";
|
||||
static constexpr const char T_RCT_EVENT[] = "RCT_EVENT";
|
||||
static constexpr const char T_ERROR[] = "ERROR";
|
||||
|
||||
// extensions & MIME-Types
|
||||
static constexpr const char T__avif[] = ".avif"; // AVIF: Highly compressed images. Compatible with all modern browsers.
|
||||
static constexpr const char T__csv[] = ".csv"; // CSV: Data logging and configuration
|
||||
static constexpr const char T__css[] = ".css"; // CSS: Styling for web interfaces
|
||||
static constexpr const char T__gif[] = ".gif"; // GIF: Simple animations. Legacy support
|
||||
static constexpr const char T__gz[] = ".gz"; // GZ: compressed files
|
||||
static constexpr const char T__htm[] = ".htm"; // HTM: Web interface files
|
||||
static constexpr const char T__html[] = ".html"; // HTML: Web interface files
|
||||
static constexpr const char T__ico[] = ".ico"; // ICO: Favicons, system icons. Legacy support
|
||||
static constexpr const char T__jpg[] = ".jpg"; // JPEG/JPG: Photos. Legacy support
|
||||
static constexpr const char T__js[] = ".js"; // JavaScript: Interactive functionality
|
||||
static constexpr const char T__json[] = ".json"; // JSON: Data exchange format
|
||||
static constexpr const char T__mp4[] = ".mp4"; // MP4: Proprietary format. Worse compression than WEBM.
|
||||
static constexpr const char T__mjs[] = ".mjs"; // MJS: JavaScript module format
|
||||
static constexpr const char T__opus[] = ".opus"; // OPUS: High compression audio format
|
||||
static constexpr const char T__pdf[] = ".pdf"; // PDF: Universal document format
|
||||
static constexpr const char T__png[] = ".png"; // PNG: Icons, logos, transparency. Legacy support
|
||||
static constexpr const char T__svg[] = ".svg"; // SVG: Vector graphics, icons (scalable, tiny file sizes)
|
||||
static constexpr const char T__ttf[] = ".ttf"; // TTF: Font file. Legacy support
|
||||
static constexpr const char T__txt[] = ".txt"; // TXT: Plain text files
|
||||
static constexpr const char T__webm[] = ".webm"; // WebM: Video. Open source, optimized for web. Compatible with all modern browsers.
|
||||
static constexpr const char T__webp[] = ".webp"; // WebP: Highly compressed images. Compatible with all modern browsers.
|
||||
static constexpr const char T__woff[] = ".woff"; // WOFF: Font file. Legacy support
|
||||
static constexpr const char T__woff2[] = ".woff2"; // WOFF2: Better compression. Compatible with all modern browsers.
|
||||
static constexpr const char T__xml[] = ".xml"; // XML: Configuration and data files
|
||||
static constexpr const char T_application_javascript[] = "application/javascript"; // Obsolete type for JavaScript
|
||||
static constexpr const char T_application_json[] = "application/json";
|
||||
static constexpr const char T_application_msgpack[] = "application/msgpack";
|
||||
static constexpr const char T_application_octet_stream[] = "application/octet-stream";
|
||||
static constexpr const char T_application_pdf[] = "application/pdf";
|
||||
static constexpr const char T_app_xform_urlencoded[] = "application/x-www-form-urlencoded";
|
||||
static constexpr const char T_audio_opus[] = "audio/opus";
|
||||
static constexpr const char T_font_ttf[] = "font/ttf";
|
||||
static constexpr const char T_font_woff[] = "font/woff";
|
||||
static constexpr const char T_font_woff2[] = "font/woff2";
|
||||
static constexpr const char T_image_avif[] = "image/avif";
|
||||
static constexpr const char T_image_gif[] = "image/gif";
|
||||
static constexpr const char T_image_jpeg[] = "image/jpeg";
|
||||
static constexpr const char T_image_png[] = "image/png";
|
||||
static constexpr const char T_image_svg_xml[] = "image/svg+xml";
|
||||
static constexpr const char T_image_webp[] = "image/webp";
|
||||
static constexpr const char T_image_x_icon[] = "image/x-icon";
|
||||
static constexpr const char T_text_css[] = "text/css";
|
||||
static constexpr const char T_text_csv[] = "text/csv";
|
||||
static constexpr const char T_text_event_stream[] = "text/event-stream";
|
||||
static constexpr const char T_text_html[] = "text/html";
|
||||
static constexpr const char T_text_javascript[] = "text/javascript";
|
||||
static constexpr const char T_text_plain[] = "text/plain";
|
||||
static constexpr const char T_text_xml[] = "text/xml";
|
||||
static constexpr const char T_video_mp4[] = "video/mp4";
|
||||
static constexpr const char T_video_webm[] = "video/webm";
|
||||
} // namespace asyncsrv
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,7 +36,6 @@ class DummySettings {
|
||||
uint8_t nested_format = 1; // 1=nested 2=single
|
||||
String discovery_prefix = "homeassistant";
|
||||
uint8_t discovery_type = 0; // HA
|
||||
uint8_t ha_number_mode = 0; // box
|
||||
bool ha_enabled = true;
|
||||
String base = "ems-esp";
|
||||
bool publish_single = false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -67,9 +67,6 @@ class Preferences {
|
||||
size_t putString(const char * key, String value) {
|
||||
return 0;
|
||||
}
|
||||
bool isKey(const char * key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// unused....
|
||||
|
||||
@@ -87,6 +84,7 @@ class Preferences {
|
||||
// size_t putBool(const char * key, bool value);
|
||||
|
||||
// size_t putBytes(const char * key, const void * value, size_t len);
|
||||
// bool isKey(const char * key);
|
||||
// PreferenceType getType(const char * key);
|
||||
// int8_t getChar(const char * key, int8_t defaultValue = 0);
|
||||
// int16_t getShort(const char * key, int16_t defaultValue = 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
Refer to the [EMS-ESP documentation](https://emsesp.org/Building#webui-development) on how to run the mock Web server.
|
||||
When developing and testing the web interface, it's handy not to bother with re-flashing an ESP32 each time. The idea is to mimic the ESP using a mock/stub server that responds to the REST (HTTP POST & GET) and WebSocket calls.
|
||||
|
||||
# prerequisites
|
||||
|
||||
- Install the latest LTS of NodeJS
|
||||
- install corepack `npm install -g corepack@latest` and `orepack enable`
|
||||
- Install pnpm (`corepack use pnpm@latest-10` on linux or `winget install -e --id pnpm.pnpm` on windows). See <https://pnpm.io/installation>
|
||||
- Install bun (<https://bun.sh/docs/installation>)
|
||||
- type `pnpm install` from this `mock-api` folder to build
|
||||
|
||||
# To run
|
||||
|
||||
- `pnpm standalone` from the main `interface` folder and then navigate to <http://localhost:3000>
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@msgpack/msgpack": "^3.1.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
|
||||
"formidable": "^3.5.4",
|
||||
"itty-router": "^5.0.22",
|
||||
"prettier": "^3.8.1"
|
||||
"prettier": "^3.7.4"
|
||||
},
|
||||
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264"
|
||||
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
|
||||
}
|
||||
|
||||
316
mock-api/pnpm-lock.yaml
generated
316
mock-api/pnpm-lock.yaml
generated
@@ -1,316 +0,0 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@msgpack/msgpack':
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3
|
||||
'@trivago/prettier-plugin-sort-imports':
|
||||
specifier: ^6.0.2
|
||||
version: 6.0.2(prettier@3.8.1)
|
||||
formidable:
|
||||
specifier: ^3.5.4
|
||||
version: 3.5.4
|
||||
itty-router:
|
||||
specifier: ^5.0.22
|
||||
version: 5.0.22
|
||||
prettier:
|
||||
specifier: ^3.8.1
|
||||
version: 3.8.1
|
||||
|
||||
packages:
|
||||
|
||||
'@babel/code-frame@7.29.0':
|
||||
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/generator@7.29.0':
|
||||
resolution: {integrity: sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-globals@7.28.0':
|
||||
resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-string-parser@7.27.1':
|
||||
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5':
|
||||
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/parser@7.29.0':
|
||||
resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/template@7.28.6':
|
||||
resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/traverse@7.29.0':
|
||||
resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/types@7.29.0':
|
||||
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2':
|
||||
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@msgpack/msgpack@3.1.3':
|
||||
resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@noble/hashes@1.8.0':
|
||||
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
|
||||
engines: {node: ^14.21.3 || >=16}
|
||||
|
||||
'@paralleldrive/cuid2@2.3.1':
|
||||
resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
|
||||
|
||||
'@trivago/prettier-plugin-sort-imports@6.0.2':
|
||||
resolution: {integrity: sha512-3DgfkukFyC/sE/VuYjaUUWoFfuVjPK55vOFDsxD56XXynFMCZDYFogH2l/hDfOsQAm1myoU/1xByJ3tWqtulXA==}
|
||||
engines: {node: '>= 20'}
|
||||
peerDependencies:
|
||||
'@vue/compiler-sfc': 3.x
|
||||
prettier: 2.x - 3.x
|
||||
prettier-plugin-ember-template-tag: '>= 2.0.0'
|
||||
prettier-plugin-svelte: 3.x
|
||||
svelte: 4.x || 5.x
|
||||
peerDependenciesMeta:
|
||||
'@vue/compiler-sfc':
|
||||
optional: true
|
||||
prettier-plugin-ember-template-tag:
|
||||
optional: true
|
||||
prettier-plugin-svelte:
|
||||
optional: true
|
||||
svelte:
|
||||
optional: true
|
||||
|
||||
asap@2.0.6:
|
||||
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
brace-expansion@2.0.2:
|
||||
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
dezalgo@1.0.4:
|
||||
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
|
||||
|
||||
formidable@3.5.4:
|
||||
resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
itty-router@5.0.22:
|
||||
resolution: {integrity: sha512-9hmdGErWdYDOurGYxSbqLhy4EFReIwk71hMZTJ5b+zfa2zjMNV1ftFno2b8VjAQvX615gNB8Qxbl9JMRqHnIVA==}
|
||||
|
||||
javascript-natural-sort@0.7.1:
|
||||
resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
jsesc@3.1.0:
|
||||
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
lodash-es@4.17.23:
|
||||
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
|
||||
|
||||
minimatch@9.0.5:
|
||||
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
parse-imports-exports@0.2.4:
|
||||
resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==}
|
||||
|
||||
parse-statements@1.0.11:
|
||||
resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
prettier@3.8.1:
|
||||
resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/code-frame@7.29.0':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
js-tokens: 4.0.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
'@babel/generator@7.29.0':
|
||||
dependencies:
|
||||
'@babel/parser': 7.29.0
|
||||
'@babel/types': 7.29.0
|
||||
'@jridgewell/gen-mapping': 0.3.13
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
jsesc: 3.1.0
|
||||
|
||||
'@babel/helper-globals@7.28.0': {}
|
||||
|
||||
'@babel/helper-string-parser@7.27.1': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5': {}
|
||||
|
||||
'@babel/parser@7.29.0':
|
||||
dependencies:
|
||||
'@babel/types': 7.29.0
|
||||
|
||||
'@babel/template@7.28.6':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.29.0
|
||||
'@babel/parser': 7.29.0
|
||||
'@babel/types': 7.29.0
|
||||
|
||||
'@babel/traverse@7.29.0':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.29.0
|
||||
'@babel/generator': 7.29.0
|
||||
'@babel/helper-globals': 7.28.0
|
||||
'@babel/parser': 7.29.0
|
||||
'@babel/template': 7.28.6
|
||||
'@babel/types': 7.29.0
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/types@7.29.0':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2': {}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@msgpack/msgpack@3.1.3': {}
|
||||
|
||||
'@noble/hashes@1.8.0': {}
|
||||
|
||||
'@paralleldrive/cuid2@2.3.1':
|
||||
dependencies:
|
||||
'@noble/hashes': 1.8.0
|
||||
|
||||
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.1)':
|
||||
dependencies:
|
||||
'@babel/generator': 7.29.0
|
||||
'@babel/parser': 7.29.0
|
||||
'@babel/traverse': 7.29.0
|
||||
'@babel/types': 7.29.0
|
||||
javascript-natural-sort: 0.7.1
|
||||
lodash-es: 4.17.23
|
||||
minimatch: 9.0.5
|
||||
parse-imports-exports: 0.2.4
|
||||
prettier: 3.8.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
asap@2.0.6: {}
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
brace-expansion@2.0.2:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
||||
debug@4.4.3:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
dezalgo@1.0.4:
|
||||
dependencies:
|
||||
asap: 2.0.6
|
||||
wrappy: 1.0.2
|
||||
|
||||
formidable@3.5.4:
|
||||
dependencies:
|
||||
'@paralleldrive/cuid2': 2.3.1
|
||||
dezalgo: 1.0.4
|
||||
once: 1.4.0
|
||||
|
||||
itty-router@5.0.22: {}
|
||||
|
||||
javascript-natural-sort@0.7.1: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
lodash-es@4.17.23: {}
|
||||
|
||||
minimatch@9.0.5:
|
||||
dependencies:
|
||||
brace-expansion: 2.0.2
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
parse-imports-exports@0.2.4:
|
||||
dependencies:
|
||||
parse-statements: 1.0.11
|
||||
|
||||
parse-statements@1.0.11: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
prettier@3.8.1: {}
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
@@ -130,9 +130,7 @@ let system_status = {
|
||||
// partitions: [],
|
||||
developer_mode: true,
|
||||
model: '',
|
||||
board: '',
|
||||
// model: 'BBQKees Electronics EMS Gateway E32 V2 (E32 V2.0 P3/2024011)',
|
||||
// board: 'E32V2',
|
||||
// status: 0,
|
||||
status: 3
|
||||
};
|
||||
@@ -586,7 +584,6 @@ let mqtt_settings = {
|
||||
keep_alive: 60,
|
||||
clean_session: true,
|
||||
entity_format: 1,
|
||||
ha_number_mode: 0,
|
||||
publish_time_boiler: 10,
|
||||
publish_time_thermostat: 10,
|
||||
publish_time_solar: 10,
|
||||
|
||||
@@ -105,8 +105,8 @@ build_type = release
|
||||
board_build.filesystem = littlefs
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson @ 7.4.2
|
||||
ESP32Async/AsyncTCP @ 3.4.10
|
||||
ESP32Async/ESPAsyncWebServer @ 3.10.1
|
||||
ESP32Async/AsyncTCP @ 3.4.9
|
||||
ESP32Async/ESPAsyncWebServer @ 3.9.3
|
||||
https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ build_src_flags =
|
||||
-DEMSESP_STANDALONE -DEMSESP_TEST
|
||||
-DEMSESP_UNITY
|
||||
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
|
||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
-std=gnu++17 -Og -ggdb
|
||||
-Wall -Wextra
|
||||
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
|
||||
@@ -284,7 +284,7 @@ build_flags =
|
||||
[env:standalone]
|
||||
extends = env:native
|
||||
build_flags =
|
||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
|
||||
; Modbus
|
||||
; Creates the file modbus_entity_parameters.hpp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -94,9 +94,8 @@ def print_device_entities(device_name, device_entities):
|
||||
type_suffix = split_type[1] if len(split_type) > 1 else ""
|
||||
|
||||
# Optimize type_rest extraction and int range detection
|
||||
type_rest_str = ""
|
||||
type_rest_str = type_suffix
|
||||
if "int" in type_base and "(" in type_suffix:
|
||||
print(type_base)
|
||||
try:
|
||||
# Extract inner part of parentheses
|
||||
range_inner = type_suffix[type_suffix.index("(")+1:type_suffix.index(")")]
|
||||
@@ -108,6 +107,8 @@ def print_device_entities(device_name, device_entities):
|
||||
min_value, max_value = range_inner.split("/")
|
||||
if min_value is not None and max_value is not None:
|
||||
type_rest_str = f"(>={min_value.strip()}<={max_value.strip()})"
|
||||
else:
|
||||
type_rest_str = ""
|
||||
except Exception:
|
||||
# fallback to original
|
||||
pass
|
||||
@@ -134,9 +135,6 @@ def print_device_type_devices(device_type, devices):
|
||||
|
||||
def print_header():
|
||||
"""Print the markdown document header."""
|
||||
print("---")
|
||||
print("id: Modbus-Entity-Registers")
|
||||
print("---")
|
||||
print("# Modbus Entity/Register Mapping")
|
||||
print()
|
||||
print(":::warning")
|
||||
|
||||
@@ -21,24 +21,24 @@ void APSettingsService::begin() {
|
||||
|
||||
// wait 10 sec on STA disconnect before starting AP
|
||||
void APSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
const uint8_t was_connected = _connected;
|
||||
uint8_t was_connected = _connected;
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
_connected &= ~1U;
|
||||
_connected &= ~1;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
_connected &= ~2U;
|
||||
_connected &= ~2;
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
||||
_connected |= 1U;
|
||||
_connected |= 1;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
case ARDUINO_EVENT_ETH_GOT_IP6:
|
||||
_connected |= 2U;
|
||||
_connected |= 2;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
// wait 10 sec before starting AP
|
||||
if (was_connected && !_connected) {
|
||||
@@ -52,19 +52,18 @@ void APSettingsService::reconfigureAP() {
|
||||
}
|
||||
|
||||
void APSettingsService::loop() {
|
||||
const unsigned long currentMillis = uuid::get_uptime();
|
||||
if ((currentMillis - _lastManaged) >= MANAGE_NETWORK_DELAY) {
|
||||
unsigned long currentMillis = uuid::get_uptime();
|
||||
unsigned long manageElapsed = static_cast<uint32_t>(currentMillis - _lastManaged);
|
||||
if (manageElapsed >= MANAGE_NETWORK_DELAY) {
|
||||
_lastManaged = currentMillis;
|
||||
manageAP();
|
||||
}
|
||||
|
||||
if (_dnsServer) {
|
||||
handleDNS();
|
||||
}
|
||||
}
|
||||
|
||||
void APSettingsService::manageAP() {
|
||||
const WiFiMode_t currentWiFiMode = WiFi.getMode();
|
||||
WiFiMode_t currentWiFiMode = WiFi.getMode();
|
||||
if (_state.provisionMode == AP_MODE_ALWAYS || (_state.provisionMode == AP_MODE_DISCONNECTED && !_connected)) {
|
||||
if (_reconfigureAp || currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) {
|
||||
startAP();
|
||||
@@ -88,10 +87,8 @@ void APSettingsService::startAP() {
|
||||
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||
#endif
|
||||
if (!_dnsServer) {
|
||||
const IPAddress apIp = WiFi.softAPIP();
|
||||
char ipStr[16];
|
||||
snprintf(ipStr, sizeof(ipStr), "%u.%u.%u.%u", apIp[0], apIp[1], apIp[2], apIp[3]);
|
||||
emsesp::EMSESP::logger().info("Starting Access Point with captive portal on %s", ipStr);
|
||||
IPAddress apIp = WiFi.softAPIP();
|
||||
emsesp::EMSESP::logger().info("Starting Access Point with captive portal on %s", apIp.toString().c_str());
|
||||
_dnsServer = new DNSServer;
|
||||
_dnsServer->start(DNS_PORT, "*", apIp);
|
||||
}
|
||||
@@ -114,8 +111,8 @@ void APSettingsService::handleDNS() {
|
||||
}
|
||||
|
||||
APNetworkStatus APSettingsService::getAPNetworkStatus() {
|
||||
const WiFiMode_t currentWiFiMode = WiFi.getMode();
|
||||
const bool apActive = (currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA);
|
||||
WiFiMode_t currentWiFiMode = WiFi.getMode();
|
||||
bool apActive = currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA;
|
||||
|
||||
if (apActive && _state.provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) {
|
||||
return APNetworkStatus::LINGERING;
|
||||
@@ -138,7 +135,7 @@ void APSettings::read(const APSettings & settings, JsonObject root) {
|
||||
}
|
||||
|
||||
StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
|
||||
APSettings newSettings{};
|
||||
APSettings newSettings = {};
|
||||
newSettings.provisionMode = static_cast<uint8_t>(root["provision_mode"] | FACTORY_AP_PROVISION_MODE);
|
||||
|
||||
switch (settings.provisionMode) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef APSettingsService_h
|
||||
#define APSettingsService_h
|
||||
#ifndef APSettingsConfig_h
|
||||
#define APSettingsConfig_h
|
||||
|
||||
#include "HttpEndpoint.h"
|
||||
#include "FSPersistence.h"
|
||||
@@ -58,21 +58,21 @@ enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING };
|
||||
|
||||
class APSettings {
|
||||
public:
|
||||
uint8_t provisionMode = FACTORY_AP_PROVISION_MODE; // 0 = on, 2 = off
|
||||
String ssid = FACTORY_AP_SSID;
|
||||
String password = FACTORY_AP_PASSWORD;
|
||||
uint8_t channel = FACTORY_AP_CHANNEL;
|
||||
bool ssidHidden = FACTORY_AP_SSID_HIDDEN;
|
||||
uint8_t maxClients = FACTORY_AP_MAX_CLIENTS;
|
||||
uint8_t provisionMode; // 0 = on, 2 = off
|
||||
String ssid;
|
||||
String password;
|
||||
uint8_t channel;
|
||||
bool ssidHidden;
|
||||
uint8_t maxClients;
|
||||
|
||||
IPAddress localIP;
|
||||
IPAddress gatewayIP;
|
||||
IPAddress subnetMask;
|
||||
|
||||
bool operator==(const APSettings & settings) const {
|
||||
return provisionMode == settings.provisionMode && channel == settings.channel && ssidHidden == settings.ssidHidden
|
||||
&& maxClients == settings.maxClients && localIP == settings.localIP && gatewayIP == settings.gatewayIP
|
||||
&& subnetMask == settings.subnetMask && ssid == settings.ssid && password == settings.password;
|
||||
return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && channel == settings.channel
|
||||
&& ssidHidden == settings.ssidHidden && maxClients == settings.maxClients && localIP == settings.localIP && gatewayIP == settings.gatewayIP
|
||||
&& subnetMask == settings.subnetMask;
|
||||
}
|
||||
|
||||
static void read(const APSettings & settings, JsonObject root);
|
||||
@@ -96,8 +96,8 @@ class APSettingsService : public StatefulService<APSettings> {
|
||||
|
||||
// for the management delay loop
|
||||
volatile unsigned long _lastManaged;
|
||||
volatile bool _reconfigureAp;
|
||||
volatile uint8_t _connected;
|
||||
volatile boolean _reconfigureAp;
|
||||
uint8_t _connected;
|
||||
|
||||
void reconfigureAP();
|
||||
void manageAP();
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include "WWWData.h" // include auto-generated static web resources
|
||||
|
||||
static constexpr const char CACHE_CONTROL[] = "public,max-age=60";
|
||||
|
||||
ESP32React::ESP32React(AsyncWebServer * server, FS * fs)
|
||||
: _securitySettingsService(server, fs)
|
||||
, _networkSettingsService(server, fs, &_securitySettingsService)
|
||||
@@ -24,18 +22,21 @@ ESP32React::ESP32React(AsyncWebServer * server, FS * fs)
|
||||
ArRequestHandlerFunction indexHtmlHandler = nullptr;
|
||||
|
||||
WWWData::registerRoutes([server, &indexHtmlHandler](const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash) {
|
||||
String etag = "\"" + hash + "\""; // RFC9110: ETag must be enclosed in double quotes
|
||||
ArRequestHandlerFunction requestHandler = [contentType, content, len, hash](AsyncWebServerRequest * request) {
|
||||
AsyncWebServerResponse * response;
|
||||
|
||||
ArRequestHandlerFunction requestHandler = [contentType, content, len, etag](AsyncWebServerRequest * request) {
|
||||
if (request->header(asyncsrv::T_INM) == etag) {
|
||||
request->send(304);
|
||||
return;
|
||||
// Check if the client already has the same version and respond with a 304 (Not modified)
|
||||
if (request->header("If-None-Match").equals(hash)) {
|
||||
response = request->beginResponse(304);
|
||||
} else {
|
||||
response = request->beginResponse(200, contentType, content, len);
|
||||
response->addHeader("Content-Encoding", "gzip"); // not br for brotlin only works over HTTPS
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(200, contentType, content, len);
|
||||
response->addHeader(asyncsrv::T_Content_Encoding, asyncsrv::T_gzip, false);
|
||||
response->addHeader(asyncsrv::T_ETag, etag, false);
|
||||
response->addHeader(asyncsrv::T_Cache_Control, CACHE_CONTROL, false);
|
||||
// always send these headers - see https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
|
||||
response->addHeader("ETag", hash);
|
||||
response->addHeader("Cache-Control", "no-cache"); // Requires revalidation before using cached content (ETags enable 304 responses)
|
||||
|
||||
request->send(response);
|
||||
};
|
||||
|
||||
@@ -68,11 +69,11 @@ void ESP32React::begin() {
|
||||
_networkSettingsService.read([&](NetworkSettings & networkSettings) {
|
||||
DefaultHeaders & defaultHeaders = DefaultHeaders::Instance();
|
||||
if (networkSettings.enableCORS) {
|
||||
defaultHeaders.addHeader(asyncsrv::T_CORS_ACAO, networkSettings.CORSOrigin);
|
||||
defaultHeaders.addHeader(asyncsrv::T_CORS_ACAH, "Accept, Content-Type, Authorization");
|
||||
defaultHeaders.addHeader(asyncsrv::T_CORS_ACAC, "true");
|
||||
defaultHeaders.addHeader("Access-Control-Allow-Origin", networkSettings.CORSOrigin);
|
||||
defaultHeaders.addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
|
||||
defaultHeaders.addHeader("Access-Control-Allow-Credentials", "true");
|
||||
}
|
||||
defaultHeaders.addHeader(asyncsrv::T_Server, networkSettings.hostname);
|
||||
defaultHeaders.addHeader("Server", networkSettings.hostname);
|
||||
});
|
||||
_apSettingsService.begin();
|
||||
_ntpSettingsService.begin();
|
||||
|
||||
@@ -70,10 +70,6 @@ class FSPersistence {
|
||||
|
||||
// failed to open file, return false
|
||||
if (!settingsFile || !jsonObject.size()) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Serial.printf("Failed to write file %s", _filePath);
|
||||
Serial.println();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,23 +10,20 @@
|
||||
class JsonUtils {
|
||||
public:
|
||||
static void readIP(JsonObject root, const String & key, IPAddress & ip, const String & def) {
|
||||
IPAddress defaultIp{};
|
||||
IPAddress defaultIp = {};
|
||||
if (!defaultIp.fromString(def)) {
|
||||
defaultIp = INADDR_NONE;
|
||||
}
|
||||
readIP(root, key, ip, defaultIp);
|
||||
}
|
||||
static void readIP(JsonObject root, const String & key, IPAddress & ip, const IPAddress & defaultIp = INADDR_NONE) {
|
||||
const JsonVariant value = root[key];
|
||||
if (!value.is<String>() || !ip.fromString(value.as<const char *>())) {
|
||||
if (!root[key].is<String>() || !ip.fromString(root[key].as<String>())) {
|
||||
ip = defaultIp;
|
||||
}
|
||||
}
|
||||
static void writeIP(JsonObject root, const String & key, const IPAddress & ip) {
|
||||
if (IPUtils::isSet(ip)) {
|
||||
char ipStr[16];
|
||||
snprintf(ipStr, sizeof(ipStr), "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
|
||||
root[key] = ipStr;
|
||||
root[key] = ip.toString();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -248,14 +248,13 @@ void MqttSettings::read(MqttSettings & settings, JsonObject root) {
|
||||
root["nested_format"] = settings.nested_format;
|
||||
root["discovery_prefix"] = settings.discovery_prefix;
|
||||
root["discovery_type"] = settings.discovery_type;
|
||||
root["ha_number_mode"] = settings.ha_number_mode;
|
||||
root["publish_single"] = settings.publish_single;
|
||||
root["publish_single2cmd"] = settings.publish_single2cmd;
|
||||
root["send_response"] = settings.send_response;
|
||||
}
|
||||
|
||||
StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings) {
|
||||
MqttSettings newSettings;
|
||||
MqttSettings newSettings = {};
|
||||
bool changed = false;
|
||||
|
||||
#ifndef TASMOTA_SDK
|
||||
@@ -289,7 +288,6 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
newSettings.nested_format = static_cast<uint8_t>(root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT);
|
||||
newSettings.discovery_prefix = root["discovery_prefix"] | EMSESP_DEFAULT_DISCOVERY_PREFIX;
|
||||
newSettings.discovery_type = static_cast<uint8_t>(root["discovery_type"] | EMSESP_DEFAULT_DISCOVERY_TYPE);
|
||||
newSettings.ha_number_mode = static_cast<uint8_t>(root["ha_number_mode"] | EMSESP_DEFAULT_HA_NUMBER_MODE);
|
||||
newSettings.publish_single = root["publish_single"] | EMSESP_DEFAULT_PUBLISH_SINGLE;
|
||||
newSettings.publish_single2cmd = root["publish_single2cmd"] | EMSESP_DEFAULT_PUBLISH_SINGLE2CMD;
|
||||
newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE;
|
||||
@@ -316,10 +314,6 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (newSettings.ha_number_mode != settings.ha_number_mode) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (newSettings.entity_format != settings.entity_format) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <espMqttClient.h>
|
||||
|
||||
#include <uuid/common.h>
|
||||
#include <default_settings.h>
|
||||
|
||||
#define MQTT_RECONNECTION_DELAY 2000 // 2 seconds
|
||||
|
||||
@@ -66,38 +65,37 @@
|
||||
|
||||
class MqttSettings {
|
||||
public:
|
||||
bool enabled = FACTORY_MQTT_ENABLED;
|
||||
String host = FACTORY_MQTT_HOST;
|
||||
uint16_t port = FACTORY_MQTT_PORT;
|
||||
String rootCA = "";
|
||||
bool enableTLS = false;
|
||||
String username = FACTORY_MQTT_USERNAME;
|
||||
String password = FACTORY_MQTT_PASSWORD;
|
||||
bool enabled;
|
||||
String host;
|
||||
uint16_t port;
|
||||
String rootCA;
|
||||
bool enableTLS;
|
||||
String username;
|
||||
String password;
|
||||
String clientId;
|
||||
uint16_t keepAlive = FACTORY_MQTT_KEEP_ALIVE;
|
||||
bool cleanSession = FACTORY_MQTT_CLEAN_SESSION;
|
||||
uint16_t keepAlive;
|
||||
bool cleanSession;
|
||||
|
||||
// EMS-ESP specific
|
||||
String base = FACTORY_MQTT_BASE;
|
||||
uint16_t publish_time_boiler = EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
uint16_t publish_time_thermostat = EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
uint16_t publish_time_solar = EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
uint16_t publish_time_mixer = EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
uint16_t publish_time_water = EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
uint16_t publish_time_other = EMSESP_DEFAULT_PUBLISH_TIME_OTHER;
|
||||
uint16_t publish_time_sensor = EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
uint16_t publish_time_heartbeat = EMSESP_DEFAULT_PUBLISH_HEARTBEAT;
|
||||
uint8_t mqtt_qos = EMSESP_DEFAULT_MQTT_QOS;
|
||||
bool mqtt_retain = EMSESP_DEFAULT_MQTT_RETAIN;
|
||||
bool ha_enabled = EMSESP_DEFAULT_HA_ENABLED;
|
||||
uint8_t nested_format = EMSESP_DEFAULT_NESTED_FORMAT;
|
||||
String discovery_prefix = EMSESP_DEFAULT_DISCOVERY_PREFIX;
|
||||
uint8_t discovery_type = EMSESP_DEFAULT_DISCOVERY_TYPE;
|
||||
uint8_t ha_number_mode = EMSESP_DEFAULT_HA_NUMBER_MODE;
|
||||
bool publish_single = EMSESP_DEFAULT_PUBLISH_SINGLE;
|
||||
bool publish_single2cmd = EMSESP_DEFAULT_PUBLISH_SINGLE2CMD;
|
||||
bool send_response = EMSESP_DEFAULT_SEND_RESPONSE;
|
||||
uint8_t entity_format = EMSESP_DEFAULT_ENTITY_FORMAT;
|
||||
String base;
|
||||
uint16_t publish_time_boiler;
|
||||
uint16_t publish_time_thermostat;
|
||||
uint16_t publish_time_solar;
|
||||
uint16_t publish_time_mixer;
|
||||
uint16_t publish_time_water;
|
||||
uint16_t publish_time_other;
|
||||
uint16_t publish_time_sensor;
|
||||
uint16_t publish_time_heartbeat;
|
||||
uint8_t mqtt_qos;
|
||||
bool mqtt_retain;
|
||||
bool ha_enabled;
|
||||
uint8_t nested_format;
|
||||
String discovery_prefix;
|
||||
uint8_t discovery_type;
|
||||
bool publish_single;
|
||||
bool publish_single2cmd;
|
||||
bool send_response;
|
||||
uint8_t entity_format;
|
||||
|
||||
static void read(MqttSettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, MqttSettings & settings);
|
||||
@@ -125,11 +123,11 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
|
||||
FSPersistence<MqttSettings> _fsPersistence;
|
||||
|
||||
// variable to help manage connection
|
||||
volatile bool _reconfigureMqtt;
|
||||
volatile unsigned long _disconnectedAt;
|
||||
bool _reconfigureMqtt;
|
||||
unsigned long _disconnectedAt;
|
||||
|
||||
// connection status
|
||||
volatile espMqttClientTypes::DisconnectReason _disconnectReason;
|
||||
espMqttClientTypes::DisconnectReason _disconnectReason;
|
||||
|
||||
// the MQTT client instance
|
||||
MqttClient * _mqttClient;
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
|
||||
class NTPSettings {
|
||||
public:
|
||||
bool enabled = FACTORY_NTP_ENABLED;
|
||||
String tzLabel = FACTORY_NTP_TIME_ZONE_LABEL;
|
||||
String tzFormat = FACTORY_NTP_TIME_ZONE_FORMAT;
|
||||
String server = FACTORY_NTP_SERVER;
|
||||
bool enabled;
|
||||
String tzLabel;
|
||||
String tzFormat;
|
||||
String server;
|
||||
|
||||
static void read(NTPSettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, NTPSettings & settings);
|
||||
@@ -49,7 +49,7 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
|
||||
private:
|
||||
HttpEndpoint<NTPSettings> _httpEndpoint;
|
||||
FSPersistence<NTPSettings> _fsPersistence;
|
||||
volatile bool _connected;
|
||||
bool _connected;
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
void configureNTP();
|
||||
|
||||
@@ -64,17 +64,17 @@
|
||||
class NetworkSettings {
|
||||
public:
|
||||
// core wifi configuration
|
||||
String ssid = FACTORY_WIFI_SSID;
|
||||
String bssid = "";
|
||||
String password = FACTORY_WIFI_PASSWORD;
|
||||
String hostname = FACTORY_WIFI_HOSTNAME;
|
||||
bool staticIPConfig = false;
|
||||
bool bandwidth20 = false;
|
||||
uint8_t tx_power = 0;
|
||||
bool nosleep = true;
|
||||
bool enableMDNS = true;
|
||||
bool enableCORS = false;
|
||||
String CORSOrigin = "*";
|
||||
String ssid;
|
||||
String bssid;
|
||||
String password;
|
||||
String hostname;
|
||||
bool staticIPConfig;
|
||||
bool bandwidth20;
|
||||
uint8_t tx_power;
|
||||
bool nosleep;
|
||||
bool enableMDNS;
|
||||
bool enableCORS;
|
||||
String CORSOrigin;
|
||||
|
||||
// optional configuration for static IP address
|
||||
IPAddress localIP;
|
||||
@@ -102,10 +102,10 @@ class NetworkSettingsService : public StatefulService<NetworkSettings> {
|
||||
HttpEndpoint<NetworkSettings> _httpEndpoint;
|
||||
FSPersistence<NetworkSettings> _fsPersistence;
|
||||
|
||||
volatile unsigned long _lastConnectionAttempt;
|
||||
volatile bool _stopping;
|
||||
unsigned long _lastConnectionAttempt;
|
||||
bool _stopping;
|
||||
|
||||
volatile uint16_t connectcount_ = 0; // number of wifi reconnects
|
||||
uint16_t connectcount_ = 0; // number of wifi reconnects
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void mDNS_start() const;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <list>
|
||||
|
||||
#define ACCESS_TOKEN_PARAMATER "access_token"
|
||||
|
||||
#define AUTHORIZATION_HEADER "Authorization"
|
||||
#define AUTHORIZATION_HEADER_PREFIX "Bearer "
|
||||
#define AUTHORIZATION_HEADER_PREFIX_LEN 7
|
||||
|
||||
@@ -172,7 +172,7 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
|
||||
// check for invalid extension and immediately kill the connection, which will throw an error
|
||||
// that is caught by the web code. Unfortunately the http error code is not sent to the client on fast network connections
|
||||
if (code == 406) {
|
||||
request->client()->close();
|
||||
request->client()->close(true);
|
||||
_is_firmware = false;
|
||||
Update.abort();
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ void IRAM_ATTR AnalogSensor::freqIrq2() {
|
||||
#endif
|
||||
|
||||
void AnalogSensor::start(const bool factory_settings) {
|
||||
if (factory_settings && EMSESP::system_.board_profile() == "E32V2_2") {
|
||||
// if (factory_settings && EMSESP::nvs_.getString("boot").equals("E32V2_2")) {
|
||||
if (factory_settings && analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
auto newSensor = AnalogCustomization();
|
||||
strcpy(newSensor.name, "core_voltage");
|
||||
@@ -67,22 +68,12 @@ void AnalogSensor::start(const bool factory_settings) {
|
||||
newSensor.type = AnalogType::ADC;
|
||||
newSensor.is_system = true;
|
||||
settings.analogCustomizations.push_back(newSensor);
|
||||
EMSESP::system_.add_gpio(newSensor.gpio, newSensor.name);
|
||||
|
||||
strcpy(newSensor.name, "supply_voltage");
|
||||
newSensor.gpio = 36;
|
||||
newSensor.factor = 0.017; // Divider 24k - 1,5k
|
||||
newSensor.is_system = true;
|
||||
settings.analogCustomizations.push_back(newSensor);
|
||||
EMSESP::system_.add_gpio(newSensor.gpio, newSensor.name);
|
||||
|
||||
strcpy(newSensor.name, "led");
|
||||
newSensor.gpio = 2;
|
||||
newSensor.type = AnalogType::DIGITAL_OUT;
|
||||
newSensor.uom = DeviceValueUOM::NONE; // unchanged after restart
|
||||
newSensor.factor = 1; // active high
|
||||
settings.analogCustomizations.push_back(newSensor);
|
||||
EMSESP::system_.add_gpio(newSensor.gpio, newSensor.name);
|
||||
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
});
|
||||
@@ -585,7 +576,7 @@ bool AnalogSensor::update(uint8_t gpio, const char * org_name, double offset, do
|
||||
newSensor.is_system = is_system;
|
||||
settings.analogCustomizations.push_back(newSensor);
|
||||
// check the gpio again and add to used list
|
||||
if (EMSESP::system_.add_gpio(gpio, name)) {
|
||||
if (EMSESP::system_.add_gpio(gpio, "Analog Sensor")) {
|
||||
LOG_DEBUG("Adding customization for analog sensor GPIO %02d", gpio);
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
} else {
|
||||
@@ -852,15 +843,6 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(metrics))) {
|
||||
std::string metrics = get_metrics_prometheus();
|
||||
if (!metrics.empty()) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// this is for a specific sensor, return the value
|
||||
const char * attribute_s = Command::get_attribute(cmd);
|
||||
|
||||
@@ -875,35 +857,6 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
// generate Prometheus metrics format from analog values
|
||||
std::string AnalogSensor::get_metrics_prometheus() {
|
||||
std::string result;
|
||||
result.reserve(sensors_.size() * 140);
|
||||
char val[10];
|
||||
for (auto & sensor : sensors_) {
|
||||
result += (std::string) "# HELP emsesp_" + sensor.name() + " " + sensor.name();
|
||||
if (sensor.type() != AnalogType::DIGITAL_OUT && sensor.type() != AnalogType::DIGITAL_IN) {
|
||||
result += (std::string) ", " + EMSdevice::uom_to_string(sensor.uom());
|
||||
} else {
|
||||
result += (std::string) ", boolean";
|
||||
}
|
||||
result += (std::string) ", readable, visible";
|
||||
if (sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::RGB || sensor.type() == AnalogType::PULSE
|
||||
|| (sensor.type() >= AnalogType::DIGITAL_OUT && sensor.type() <= AnalogType::PWM_2)
|
||||
|| (sensor.type() >= AnalogType::CNT_0 && sensor.type() <= AnalogType::CNT_2)) {
|
||||
result += (std::string) ", writable";
|
||||
}
|
||||
result += (std::string) "\n# TYPE emsesp_" + sensor.name() + " gauge\n";
|
||||
result += (std::string) "emsesp_" + sensor.name() + " ";
|
||||
if (sensor.type() != AnalogType::DIGITAL_OUT && sensor.type() != AnalogType::DIGITAL_IN) {
|
||||
result += (std::string) Helpers::render_value(val, sensor.value(), 2) + "\n";
|
||||
} else {
|
||||
result += (std::string) (sensor.value() == 0 ? "0\n" : "1\n");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// note we don't add the device and state classes here, as we do in the custom entity service
|
||||
void AnalogSensor::get_value_json(JsonObject output, const Sensor & sensor) {
|
||||
output["name"] = (const char *)sensor.name();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -177,7 +177,6 @@ class AnalogSensor {
|
||||
bool update(uint8_t gpio, const char * name, double offset, double factor, uint8_t uom, int8_t type, bool deleted, bool is_system);
|
||||
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
|
||||
void store_counters();
|
||||
std::string get_metrics_prometheus();
|
||||
static std::vector<uint8_t> exclude_types() {
|
||||
return exclude_types_;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef EMSESP_COMMAND_H_
|
||||
#define EMSESP_COMMAND_H_
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "console.h"
|
||||
#include <esp32-psram.h>
|
||||
@@ -153,7 +153,7 @@ class Command {
|
||||
|
||||
class SUrlParser {
|
||||
private:
|
||||
std::map<std::string, std::string> m_keysvalues;
|
||||
std::unordered_map<std::string, std::string> m_keysvalues;
|
||||
std::vector<std::string> m_folders;
|
||||
|
||||
public:
|
||||
@@ -166,7 +166,7 @@ class SUrlParser {
|
||||
return m_folders;
|
||||
};
|
||||
|
||||
std::map<std::string, std::string> & params() {
|
||||
std::unordered_map<std::string, std::string> & params() {
|
||||
return m_keysvalues;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -25,6 +25,10 @@
|
||||
#define EMSESP_DEFAULT_LOCALE EMSESP_LOCALE_EN // English
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_VERSION
|
||||
#define EMSESP_DEFAULT_VERSION ""
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_TX_MODE
|
||||
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
|
||||
#endif
|
||||
@@ -210,10 +214,6 @@
|
||||
#define EMSESP_DEFAULT_DISCOVERY_TYPE 0 // HA
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_HA_NUMBER_MODE
|
||||
#define EMSESP_DEFAULT_HA_NUMBER_MODE 0 // BOX
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_SINGLE
|
||||
#define EMSESP_DEFAULT_PUBLISH_SINGLE false
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -72,21 +72,6 @@ const char * EMSdevice::tag_to_mqtt(int8_t tag) {
|
||||
return (DeviceValue::DeviceValueTAG_mqtt[tag > DeviceValue::NUM_TAGS ? 0 : tag]);
|
||||
}
|
||||
|
||||
uint8_t EMSdevice::tag_to_flag(const uint8_t tag) {
|
||||
if (tag >= DeviceValueTAG::TAG_HC1 && tag <= DeviceValueTAG::TAG_HC8) {
|
||||
return CommandFlag::CMD_FLAG_HC;
|
||||
} else if (tag >= DeviceValueTAG::TAG_DHW1 && tag <= DeviceValueTAG::TAG_DHW10) {
|
||||
return CommandFlag::CMD_FLAG_DHW;
|
||||
} else if (tag >= DeviceValueTAG::TAG_HS1 && tag <= DeviceValueTAG::TAG_HS16) {
|
||||
return CommandFlag::CMD_FLAG_HS;
|
||||
} else if (tag >= DeviceValueTAG::TAG_AHS1 && tag <= DeviceValueTAG::TAG_AHS1) {
|
||||
return CommandFlag::CMD_FLAG_AHS;
|
||||
} else if (tag >= DeviceValueTAG::TAG_SRC1 && tag <= DeviceValueTAG::TAG_SRC16) {
|
||||
return CommandFlag::CMD_FLAG_SRC;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert UOM to a char string - translating only for hours/minutes/seconds
|
||||
const char * EMSdevice::uom_to_string(uint8_t uom) {
|
||||
switch (uom) {
|
||||
@@ -104,10 +89,7 @@ const char * EMSdevice::uom_to_string(uint8_t uom) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string EMSdevice::brand_to_char() {
|
||||
if (!custom_brand().empty()) {
|
||||
return custom_brand();
|
||||
}
|
||||
const char * EMSdevice::brand_to_char() {
|
||||
switch (brand_) {
|
||||
case EMSdevice::Brand::BOSCH:
|
||||
return F_(bosch);
|
||||
@@ -331,15 +313,15 @@ uint8_t EMSdevice::decode_brand(uint8_t value) {
|
||||
std::string EMSdevice::to_string() {
|
||||
// for devices that haven't been lookup yet, don't show all details
|
||||
if (product_id_ == 0) {
|
||||
return name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ")";
|
||||
return std::string(name()) + " (DeviceID:" + Helpers::hextoa(device_id_) + ")";
|
||||
}
|
||||
|
||||
if (brand_ == Brand::NO_BRAND && custom_brand().empty()) {
|
||||
return name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_ + ")";
|
||||
if (brand_ == Brand::NO_BRAND) {
|
||||
return std::string(name()) + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_ + ")";
|
||||
}
|
||||
|
||||
return brand_to_char() + " " + name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_
|
||||
+ ")";
|
||||
return std::string(brand_to_char()) + " " + name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_)
|
||||
+ ", Version:" + version_ + ")";
|
||||
}
|
||||
|
||||
// returns string of EMS device version and productID
|
||||
@@ -350,7 +332,7 @@ std::string EMSdevice::to_string_version() {
|
||||
// returns out brand + device name
|
||||
// translated
|
||||
std::string EMSdevice::to_string_short() {
|
||||
if (brand_ == Brand::NO_BRAND && custom_brand().empty()) {
|
||||
if (brand_ == Brand::NO_BRAND) {
|
||||
return std::string(device_type_2_device_name_translated()) + ": " + name();
|
||||
}
|
||||
|
||||
@@ -565,56 +547,40 @@ void EMSdevice::add_device_value(int8_t tag, // to b
|
||||
int16_t min, // min allowed value
|
||||
uint32_t max // max allowed value
|
||||
) {
|
||||
// initialize the device value depending on it's type
|
||||
|
||||
if (type == DeviceValueType::STRING) {
|
||||
*(char *)(value_p) = {'\0'}; // this is important for string functions like strlen() to work later
|
||||
} else if (type == DeviceValueType::INT8) {
|
||||
*(int8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_INT8_DUMMY : EMS_VALUE_DEFAULT_INT8;
|
||||
} else if (type == DeviceValueType::UINT8) {
|
||||
*(uint8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_UINT8_DUMMY : EMS_VALUE_DEFAULT_UINT8;
|
||||
} else if (type == DeviceValueType::INT16) {
|
||||
*(int16_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_INT16_DUMMY : EMS_VALUE_DEFAULT_INT16;
|
||||
} else if (type == DeviceValueType::UINT16) {
|
||||
*(uint16_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_UINT16_DUMMY : EMS_VALUE_DEFAULT_UINT16;
|
||||
} else if ((type == DeviceValueType::UINT24) || (type == DeviceValueType::TIME) || (type == DeviceValueType::UINT32)) {
|
||||
*(uint32_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_UINT24_DUMMY : EMS_VALUE_DEFAULT_UINT24;
|
||||
} else if (type == DeviceValueType::BOOL) {
|
||||
*(int8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_BOOL_DUMMY : EMS_VALUE_DEFAULT_BOOL; // bool is uint8_t, but other initial value
|
||||
} else if (type == DeviceValueType::ENUM) {
|
||||
*(uint8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_ENUM_DUMMY : EMS_VALUE_DEFAULT_ENUM; // enums behave as uint8_t
|
||||
} else if (type == DeviceValueType::CMD) {
|
||||
if (uom == DeviceValueUOM::NONE) {
|
||||
*(uint8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_ENUM_DUMMY : EMS_VALUE_DEFAULT_ENUM; // enums behave as uint8_t
|
||||
} else if (uom == DeviceValueUOM::DEGREES) {
|
||||
*(int16_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_INT16_DUMMY : EMS_VALUE_DEFAULT_INT16;
|
||||
} else if (uom == DeviceValueUOM::PERCENT) {
|
||||
*(uint8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_UINT8_DUMMY : EMS_VALUE_DEFAULT_UINT8;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t state = DeviceValueState::DV_DEFAULT; // determine state
|
||||
auto custom_fullname = std::string(""); // custom fullname
|
||||
auto short_name = name[0]; // entity name
|
||||
bool has_cmd = (f != nullptr); // is it a command?
|
||||
bool ignore = false; // ignore this entity?
|
||||
|
||||
// check test mode, which populates all device values with some dummy date
|
||||
bool test_active = System::test_set_all_active();
|
||||
|
||||
// if writable and in test mode, set to max value directly; otherwise set to default/dummy
|
||||
bool use_max = test_active && has_cmd;
|
||||
|
||||
switch (type) {
|
||||
case DeviceValueType::STRING:
|
||||
*(char *)(value_p) = {'\0'}; // this is important for string functions like strlen() to work later
|
||||
break;
|
||||
case DeviceValueType::INT8:
|
||||
*(int8_t *)(value_p) = use_max ? static_cast<int8_t>(max) : (test_active ? EMS_VALUE_DEFAULT_INT8_DUMMY : EMS_VALUE_DEFAULT_INT8);
|
||||
break;
|
||||
case DeviceValueType::UINT8:
|
||||
*(uint8_t *)(value_p) = use_max ? static_cast<uint8_t>(max) : (test_active ? EMS_VALUE_DEFAULT_UINT8_DUMMY : EMS_VALUE_DEFAULT_UINT8);
|
||||
break;
|
||||
case DeviceValueType::INT16:
|
||||
*(int16_t *)(value_p) = use_max ? static_cast<int16_t>(max) : (test_active ? EMS_VALUE_DEFAULT_INT16_DUMMY : EMS_VALUE_DEFAULT_INT16);
|
||||
break;
|
||||
case DeviceValueType::UINT16:
|
||||
*(uint16_t *)(value_p) = use_max ? static_cast<uint16_t>(max) : (test_active ? EMS_VALUE_DEFAULT_UINT16_DUMMY : EMS_VALUE_DEFAULT_UINT16);
|
||||
break;
|
||||
case DeviceValueType::UINT24:
|
||||
case DeviceValueType::TIME:
|
||||
case DeviceValueType::UINT32:
|
||||
*(uint32_t *)(value_p) = use_max ? static_cast<uint32_t>(max) : (test_active ? EMS_VALUE_DEFAULT_UINT24_DUMMY : EMS_VALUE_DEFAULT_UINT24);
|
||||
break;
|
||||
case DeviceValueType::BOOL:
|
||||
*(int8_t *)(value_p) = use_max ? static_cast<int8_t>(max ? 1 : 0) : (test_active ? EMS_VALUE_DEFAULT_BOOL_DUMMY : EMS_VALUE_DEFAULT_BOOL);
|
||||
break;
|
||||
case DeviceValueType::ENUM:
|
||||
*(uint8_t *)(value_p) = use_max ? static_cast<uint8_t>(max) : (test_active ? EMS_VALUE_DEFAULT_ENUM_DUMMY : EMS_VALUE_DEFAULT_ENUM);
|
||||
break;
|
||||
case DeviceValueType::CMD:
|
||||
if (uom == DeviceValueUOM::NONE) {
|
||||
*(uint8_t *)(value_p) = use_max ? static_cast<uint8_t>(max) : (test_active ? EMS_VALUE_DEFAULT_ENUM_DUMMY : EMS_VALUE_DEFAULT_ENUM);
|
||||
} else if (uom == DeviceValueUOM::DEGREES) {
|
||||
*(int16_t *)(value_p) = use_max ? static_cast<int16_t>(max) : (test_active ? EMS_VALUE_DEFAULT_INT16_DUMMY : EMS_VALUE_DEFAULT_INT16);
|
||||
} else if (uom == DeviceValueUOM::PERCENT) {
|
||||
*(uint8_t *)(value_p) = use_max ? static_cast<uint8_t>(max) : (test_active ? EMS_VALUE_DEFAULT_UINT8_DUMMY : EMS_VALUE_DEFAULT_UINT8);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// get fullname, getting translation if it exists
|
||||
const char * const * fullname;
|
||||
if (Helpers::count_items(name) == 1) {
|
||||
@@ -668,21 +634,25 @@ void EMSdevice::add_device_value(int8_t tag, // to b
|
||||
|
||||
// add a new command if it has a function attached
|
||||
if (has_cmd) {
|
||||
uint8_t flags = CommandFlag::ADMIN_ONLY | tag_to_flag(tag); // executing commands require admin privileges
|
||||
uint8_t flags = CommandFlag::ADMIN_ONLY; // executing commands require admin privileges
|
||||
|
||||
if (tag >= DeviceValueTAG::TAG_HC1 && tag <= DeviceValueTAG::TAG_HC8) {
|
||||
flags |= CommandFlag::CMD_FLAG_HC;
|
||||
} else if (tag >= DeviceValueTAG::TAG_DHW1 && tag <= DeviceValueTAG::TAG_DHW10) {
|
||||
flags |= CommandFlag::CMD_FLAG_DHW;
|
||||
} else if (tag >= DeviceValueTAG::TAG_HS1 && tag <= DeviceValueTAG::TAG_HS16) {
|
||||
flags |= CommandFlag::CMD_FLAG_HS;
|
||||
} else if (tag >= DeviceValueTAG::TAG_AHS1 && tag <= DeviceValueTAG::TAG_AHS1) {
|
||||
flags |= CommandFlag::CMD_FLAG_AHS;
|
||||
} else if (tag >= DeviceValueTAG::TAG_SRC1 && tag <= DeviceValueTAG::TAG_SRC16) {
|
||||
flags |= CommandFlag::CMD_FLAG_SRC;
|
||||
}
|
||||
|
||||
// add the command to our library
|
||||
Command::add(device_type_, device_id_, short_name, f, fullname, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void EMSdevice::erase_device_values() {
|
||||
for (auto & dv : devicevalues_) {
|
||||
if (dv.has_cmd) {
|
||||
Command::erase_command(device_type_, dv.short_name, tag_to_flag(dv.tag));
|
||||
}
|
||||
}
|
||||
devicevalues_.clear();
|
||||
}
|
||||
|
||||
// single list of options
|
||||
void EMSdevice::register_device_value(int8_t tag,
|
||||
void * value_p,
|
||||
@@ -912,7 +882,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
// looks up the UOM for a given key from the device value table
|
||||
std::string EMSdevice::get_value_uom(const std::string & shortname) const {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
if ((!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE)) && !strcmp(dv.short_name, shortname.c_str())) {
|
||||
if ((!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE)) && (dv.short_name == shortname)) {
|
||||
// ignore TIME since "minutes" is already added to the string value
|
||||
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
|
||||
break;
|
||||
@@ -1282,7 +1252,7 @@ void EMSdevice::setCustomizationEntity(const std::string & entity_id) {
|
||||
// set the min / max
|
||||
dv.set_custom_minmax();
|
||||
|
||||
if (Mqtt::ha_enabled() && dv.tag <= DeviceValueTAG::TAG_HC8 && !strcmp(dv.short_name, FL_(selRoomTemp)[0]) && (min != dv.min || max != dv.max)) {
|
||||
if (Mqtt::ha_enabled() && dv.short_name == FL_(seltemp)[0] && (min != dv.min || max != dv.max)) {
|
||||
set_climate_minmax(dv.tag, dv.min, dv.max);
|
||||
}
|
||||
|
||||
@@ -1325,21 +1295,11 @@ void EMSdevice::getCustomizationEntities(std::vector<std::string> & entity_ids)
|
||||
// dumps all entity values in native English
|
||||
// the code is intended to run only once standalone, outside the ESP32 so not optimized for memory efficiency
|
||||
// pipe symbols (|) are escaped so they can be converted to Markdown in the Wiki
|
||||
// format is: device name,device type,product id,shortname,fullname,type [options...] [(min/max)],uom,writeable,discovery entityid v3.4, discovery entityid
|
||||
// format is: device name,device type,product id,shortname,fullname,type [options...] \\| (min/max),uom,writeable,discovery entityid v3.4, discovery entityid
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
void EMSdevice::dump_devicevalue_info() {
|
||||
for (auto & dv : devicevalues_) {
|
||||
if (dv.fullname != nullptr) {
|
||||
// for debugging, finding max values
|
||||
/*
|
||||
if (dv.has_cmd) {
|
||||
if (dv.max - dv.min > 400) {
|
||||
Serial.printf("shortname:%s, min:%d, max:%d\n", dv.short_name, dv.min, dv.max);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
*/
|
||||
|
||||
Serial.print('\"');
|
||||
Serial.print(default_name());
|
||||
Serial.print('\"');
|
||||
@@ -1363,7 +1323,7 @@ void EMSdevice::dump_devicevalue_info() {
|
||||
Serial.print(dv.fullname[0]);
|
||||
Serial.print(',');
|
||||
|
||||
// type [options...] [(min/max)]
|
||||
// per type
|
||||
switch (dv.type) {
|
||||
case DeviceValueType::ENUM:
|
||||
case DeviceValueType::CMD:
|
||||
@@ -1423,12 +1383,14 @@ void EMSdevice::dump_devicevalue_info() {
|
||||
break;
|
||||
}
|
||||
|
||||
// min/max range, if writeable
|
||||
if (dv.has_cmd) {
|
||||
// min/max range
|
||||
int16_t dv_set_min;
|
||||
uint32_t dv_set_max;
|
||||
if (dv.get_min_max(dv_set_min, dv_set_max)) {
|
||||
Serial.print(" (>=");
|
||||
Serial.print(dv.min);
|
||||
Serial.print(dv_set_min);
|
||||
Serial.print("<=");
|
||||
Serial.print(dv.max);
|
||||
Serial.print(dv_set_max);
|
||||
Serial.print(")");
|
||||
}
|
||||
Serial.print(",");
|
||||
@@ -1735,7 +1697,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
// generate Prometheus metrics format from device values
|
||||
std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
std::string result;
|
||||
std::map<std::string, bool> seen_metrics;
|
||||
std::unordered_map<std::string, bool> seen_metrics;
|
||||
|
||||
// Helper function to check if a device value type is supported for Prometheus metrics
|
||||
auto is_supported_type = [](uint8_t type) -> bool {
|
||||
@@ -2122,6 +2084,7 @@ bool EMSdevice::generate_values(JsonObject output, const int8_t tag_filter, cons
|
||||
void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
bool create_device_config = !ha_config_done(); // do we need to create the main Discovery device config with this entity?
|
||||
uint16_t count = 0;
|
||||
const char * const ** mode_options = nullptr;
|
||||
|
||||
// check the state of each of the device values
|
||||
// create the discovery topic if if hasn't already been created, not a command (like reset) and is active and visible
|
||||
@@ -2134,15 +2097,17 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
bool needs_update = !has_config_created || (haclimate_value == 1 ? has_climate_no_rt : !has_climate_no_rt);
|
||||
|
||||
if (needs_update) {
|
||||
const char * const ** mode_options = nullptr;
|
||||
for (const auto & d : devicevalues_) {
|
||||
// make sure mode in same circuit is DeviceValueType::ENUM
|
||||
if ((d.tag == dv.tag) && (d.type == DeviceValueType::ENUM) && !strcmp(d.short_name, FL_(mode)[0]) && (d.options_size > 0)) {
|
||||
// if it's a thermostat go fetch the list of modes
|
||||
if (device_type() == EMSdevice::DeviceType::THERMOSTAT) {
|
||||
for (auto & dv : devicevalues_) {
|
||||
// make sure it's a type DeviceValueType::ENUM
|
||||
if ((dv.type == DeviceValueType::ENUM) && !strcmp(dv.short_name, FL_(mode)[0])) {
|
||||
// get options
|
||||
mode_options = d.options;
|
||||
mode_options = dv.options;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_room_temp = (haclimate_value == 1);
|
||||
if (Mqtt::publish_ha_climate_config(dv, has_room_temp, mode_options, false)) {
|
||||
@@ -2160,33 +2125,24 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && dv.has_state(DeviceValueState::DV_ACTIVE)
|
||||
&& !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) {
|
||||
// create_device_config is only done once for the EMS device. It can added to any entity, so we take the first
|
||||
if (Mqtt::publish_ha_sensor_config_dv(dv, name().c_str(), brand_to_char().c_str(), to_string_version().c_str(), false, create_device_config)) {
|
||||
if (Mqtt::publish_ha_sensor_config_dv(dv, name().c_str(), brand_to_char(), to_string_version().c_str(), false, create_device_config)) {
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
create_device_config = false; // only create the main config once
|
||||
count++;
|
||||
}
|
||||
|
||||
// SRC thermostats mapped to connect/src1/... always contains mode, selRoomTemp, currtemp
|
||||
// SRC thermostats mapped to connect/src1/...
|
||||
if (dv.tag >= DeviceValueTAG::TAG_SRC1 && dv.tag <= DeviceValueTAG::TAG_SRC16 && !strcmp(dv.short_name, FL_(selRoomTemp)[0])) {
|
||||
// add modes and icon if we have one
|
||||
const char * icon = nullptr;
|
||||
const char * const ** mode_options = nullptr;
|
||||
for (const auto & d : devicevalues_) {
|
||||
if ((d.tag != dv.tag) || (d.type != DeviceValueType::ENUM)) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(d.short_name, FL_(mode)[0]) && (d.options_size > 0)) {
|
||||
mode_options = d.options;
|
||||
}
|
||||
if (!strcmp(d.short_name, FL_(icon)[0])) {
|
||||
uint8_t val = *(uint8_t *)(d.value_p);
|
||||
if (val != 0 && val < d.options_size) {
|
||||
icon = d.options[val][0];
|
||||
for (auto & d : devicevalues_) {
|
||||
if (d.tag == dv.tag && !strcmp(d.short_name, FL_(icon)[0]) && *(uint8_t *)(d.value_p != 0)) {
|
||||
icon = d.options[*(uint8_t *)(d.value_p)][0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Mqtt::publish_ha_climate_config(dv, true, mode_options, false, icon);
|
||||
count++;
|
||||
// add all modes - auto, heat, off, cool
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/2636
|
||||
Mqtt::publish_ha_climate_config(dv, true, nullptr, false, icon);
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "emsdevicevalue.h"
|
||||
|
||||
#include <esp32-psram.h>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -55,7 +55,6 @@ class EMSdevice {
|
||||
static const char * tag_to_mqtt(int8_t tag);
|
||||
static uint8_t decode_brand(uint8_t value);
|
||||
static bool export_values(uint8_t device_type, JsonObject output, const int8_t id, const uint8_t output_target);
|
||||
static uint8_t tag_to_flag(const uint8_t tag);
|
||||
|
||||
// non static functions
|
||||
|
||||
@@ -63,7 +62,7 @@ class EMSdevice {
|
||||
const char * device_type_2_device_name_translated(); // returns translated device type name
|
||||
bool has_tags(const int8_t tag) const;
|
||||
bool has_cmd(const char * cmd, const int8_t id) const;
|
||||
std::string brand_to_char();
|
||||
const char * brand_to_char();
|
||||
std::string to_string();
|
||||
std::string to_string_short();
|
||||
std::string to_string_version();
|
||||
@@ -125,14 +124,6 @@ class EMSdevice {
|
||||
return custom_name_;
|
||||
}
|
||||
|
||||
// set custom brand
|
||||
void custom_brand(std::string const & custom_brand) {
|
||||
custom_brand_ = custom_brand;
|
||||
}
|
||||
|
||||
std::string custom_brand() const {
|
||||
return custom_brand_;
|
||||
}
|
||||
// set device model
|
||||
void model(std::string const & model) {
|
||||
model_ = model;
|
||||
@@ -291,8 +282,6 @@ class EMSdevice {
|
||||
int16_t min,
|
||||
uint32_t max);
|
||||
|
||||
void erase_device_values();
|
||||
|
||||
void
|
||||
register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom, const cmd_function_p f);
|
||||
|
||||
@@ -537,7 +526,6 @@ class EMSdevice {
|
||||
char version_[6];
|
||||
const char * default_name_; // the fixed name the EMS model taken from the device library
|
||||
std::string custom_name_ = ""; // custom name
|
||||
std::string custom_brand_ = ""; // custom brand
|
||||
std::string model_ = ""; // model, taken from the 0x01 telegram. see process_deviceName()
|
||||
uint8_t flags_ = 0;
|
||||
uint8_t brand_ = Brand::NO_BRAND;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -103,7 +103,7 @@ DeviceValue::DeviceValue(uint8_t device_type,
|
||||
}
|
||||
|
||||
// mapping of UOM, to match order in DeviceValueUOM enum emsdevicevalue.h
|
||||
// also maps to DeviceValueUOM in interface/src/app/main/types.ts for the Web UI
|
||||
// also maps to DeviceValueUOM in interface/src/project/types.ts for the Web UI
|
||||
// must be an int of 4 bytes, 32bit aligned
|
||||
const char * DeviceValue::DeviceValueUOM_s[] = {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -76,7 +76,7 @@ class DeviceValue {
|
||||
MBAR, // 24 - mbar - atmospheric pressure
|
||||
LH, // 25 - l/h - volume flow rate
|
||||
CTKWH, // 26 - ct/kWh - monetary
|
||||
HERTZ, // 27 - Hz - frequency
|
||||
HZ, // 27 - Hz - frequency
|
||||
CONNECTIVITY // 28 - used in HA - connectivity
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include "esp_ota_ops.h"
|
||||
#include "rom/rtc.h"
|
||||
#endif
|
||||
|
||||
static_assert(uuid::thread_safe, "uuid-common must be thread-safe");
|
||||
@@ -1310,7 +1309,6 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
if (product_id == 0 || (*it)->product_id() != 0) { // update only with valid product_id
|
||||
return true;
|
||||
}
|
||||
(*it)->erase_device_values();
|
||||
emsdevices.erase(it); // erase the old device without product_id and re detect
|
||||
break;
|
||||
}
|
||||
@@ -1451,7 +1449,6 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
if ((e.device_id == device_id) && (e.product_id == product_id)) {
|
||||
LOG_DEBUG("Have customizations for %s with deviceID 0x%02X productID %d", e.custom_name.c_str(), device_id, product_id);
|
||||
emsdevices.back()->custom_name(e.custom_name);
|
||||
emsdevices.back()->custom_brand(e.custom_brand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1724,31 +1721,15 @@ void EMSESP::start() {
|
||||
LOG_INFO("EMS-ESP version %s", EMSESP_APP_VERSION);
|
||||
#endif
|
||||
|
||||
LOG_DEBUG("System is running in Debug mode");
|
||||
|
||||
// check if the firmware is fresh, i.e. a new install or a new version has been uploaded
|
||||
// check if the firmware is fresh
|
||||
// this is set in UploadFileService::uploadComplete()
|
||||
// and reset in System::set_partition_install_date()
|
||||
if (EMSESP::nvs_.getBool(EMSESP_NVS_BOOT_NEW_FIRMWARE)) {
|
||||
LOG_DEBUG("Firmware is a new install");
|
||||
} else {
|
||||
// check if the firmware has been uploaded via Serial/USB
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
if (rtc_get_reset_reason(0) == 11) { // ESP_RST_USB (Reset by USB peripheral, on CPU 0 only
|
||||
#else
|
||||
if ((rtc_get_reset_reason(0) == 14 || rtc_get_reset_reason(1) == 14)) { // APP CPU reset by PRO CPU, can be either CPUs
|
||||
#endif
|
||||
LOG_DEBUG("Firmware is a new install, uploaded via Serial/USB");
|
||||
EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true); // set flag so it's picked up later to set the install date
|
||||
}
|
||||
if (!EMSESP::nvs_.getBool(EMSESP_NVS_BOOT_NEW_FIRMWARE)) {
|
||||
LOG_DEBUG("Firmware is fresh");
|
||||
}
|
||||
|
||||
// S2 are C3 are both single core
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
LOG_INFO("Last system reset reason Core0: %s", system_.reset_reason(0).c_str());
|
||||
#else
|
||||
LOG_DEBUG("System is running in Debug mode");
|
||||
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
||||
#endif
|
||||
|
||||
// see if we're restoring a settings file
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -1762,7 +1743,7 @@ void EMSESP::start() {
|
||||
nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs
|
||||
}
|
||||
|
||||
LOG_DEBUG("Fuse device information: %s", system_.getBBQKeesGatewayDetails().isEmpty() ? "not set" : system_.getBBQKeesGatewayDetails().c_str());
|
||||
LOG_DEBUG("NVS device information: %s", system_.getBBQKeesGatewayDetails().isEmpty() ? "not set" : system_.getBBQKeesGatewayDetails().c_str());
|
||||
|
||||
webSettingsService.begin(); // load EMS-ESP Application settings
|
||||
|
||||
@@ -1856,7 +1837,7 @@ void EMSESP::loop() {
|
||||
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) {
|
||||
static bool only_once = false;
|
||||
if (!only_once) {
|
||||
LOG_ERROR("Invalid GPIOs used. Please check your settings and the system log");
|
||||
LOG_ERROR("Invalid GPIOs used. Please check your settings and log");
|
||||
only_once = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,15 +19,14 @@
|
||||
#ifndef EMSESP_EMSFACTORY_H_
|
||||
#define EMSESP_EMSFACTORY_H_
|
||||
|
||||
#include <memory> // for unique_ptr
|
||||
#include <map>
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
// Forward declaration
|
||||
namespace emsesp {
|
||||
class EMSdevice;
|
||||
}
|
||||
#include "emsdevice.h"
|
||||
|
||||
// Macro for class registration
|
||||
// Anonymous namespace is used to make the definitions here private to the current
|
||||
// compilation unit (current file). It is equivalent to the old C static keyword.
|
||||
#define REGISTER_FACTORY(derivedClass, device_type) \
|
||||
namespace { \
|
||||
auto registry_##derivedClass = ConcreteEMSFactory<derivedClass>(device_type); \
|
||||
@@ -35,29 +34,30 @@ class EMSdevice;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSdevice; // forward declaration, for gcc linking
|
||||
|
||||
class EMSFactory {
|
||||
public:
|
||||
virtual ~EMSFactory() = default;
|
||||
|
||||
using FactoryMap = std::map<uint8_t, EMSFactory *>;
|
||||
|
||||
// Register factory object of derived class using the device_type as the unique identifier
|
||||
static inline auto registerFactory(const uint8_t device_type, EMSFactory * factory) -> void {
|
||||
getRegister()[device_type] = factory;
|
||||
// Register factory object of derived class
|
||||
// using the device_type as the unique identifier
|
||||
static auto registerFactory(const uint8_t device_type, EMSFactory * factory) -> void {
|
||||
auto & reg = EMSFactory::getRegister();
|
||||
reg[device_type] = factory;
|
||||
}
|
||||
|
||||
using FactoryMap = std::map<uint8_t, EMSFactory *>;
|
||||
|
||||
// returns all registered classes (really only for debugging)
|
||||
static inline auto device_handlers() -> const FactoryMap & {
|
||||
return getRegister();
|
||||
static auto device_handlers() -> FactoryMap {
|
||||
return EMSFactory::getRegister();
|
||||
}
|
||||
|
||||
// Construct derived class returning an unique ptr
|
||||
static auto add(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * default_name, uint8_t flags, uint8_t brand)
|
||||
-> std::unique_ptr<EMSdevice> {
|
||||
if (auto * ptr = makeRaw(device_type, device_id, product_id, version, default_name, flags, brand)) {
|
||||
return std::unique_ptr<EMSdevice>(ptr);
|
||||
}
|
||||
return nullptr;
|
||||
return std::unique_ptr<EMSdevice>(EMSFactory::makeRaw(device_type, device_id, product_id, version, default_name, flags, brand));
|
||||
}
|
||||
|
||||
virtual auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand) const
|
||||
@@ -65,7 +65,7 @@ class EMSFactory {
|
||||
|
||||
private:
|
||||
// Force global variable to be initialized, thus it avoids the "initialization order fiasco"
|
||||
static inline auto getRegister() -> FactoryMap & {
|
||||
static auto getRegister() -> FactoryMap & {
|
||||
static FactoryMap classRegister{};
|
||||
return classRegister;
|
||||
}
|
||||
@@ -74,9 +74,11 @@ class EMSFactory {
|
||||
// find which EMS device it is and use that class
|
||||
static auto makeRaw(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
|
||||
-> EMSdevice * {
|
||||
const auto & reg = getRegister();
|
||||
const auto it = reg.find(device_type);
|
||||
return (it != reg.end()) ? it->second->construct(device_type, device_id, product_id, version, name, flags, brand) : nullptr;
|
||||
auto it = EMSFactory::getRegister().find(device_type);
|
||||
if (it != EMSFactory::getRegister().end()) {
|
||||
return it->second->construct(device_type, device_id, product_id, version, name, flags, brand);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,7 +86,7 @@ template <typename DerivedClass>
|
||||
class ConcreteEMSFactory : EMSFactory {
|
||||
public:
|
||||
// Register this global object on the EMSFactory register
|
||||
explicit ConcreteEMSFactory(const uint8_t device_type) {
|
||||
ConcreteEMSFactory(const uint8_t device_type) {
|
||||
EMSFactory::registerFactory(device_type, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -207,18 +207,6 @@ char * Helpers::render_boolean(char * result, const bool value, const bool dashb
|
||||
return result;
|
||||
}
|
||||
|
||||
char * Helpers::remove_trailing_zero(char * result) {
|
||||
if (strchr(result, '.')) {
|
||||
while (result[strlen(result) - 1] == '0') {
|
||||
result[strlen(result) - 1] = '\0';
|
||||
}
|
||||
if (result[strlen(result) - 1] == '.') {
|
||||
result[strlen(result) - 1] = '\0';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// convert unsigned int (single byte) to text value and returns it
|
||||
// format: 255(0xFF)=boolean, 0=no formatting, otherwise divide by format
|
||||
char * Helpers::render_value(char * result, uint8_t value, int8_t format, const uint8_t fahrenheit) {
|
||||
@@ -252,11 +240,14 @@ char * Helpers::render_value(char * result, uint8_t value, int8_t format, const
|
||||
strlcpy(result, itoa(new_value >> 1, s2, 10), 5);
|
||||
strlcat(result, ".", 5);
|
||||
strlcat(result, ((new_value & 0x01) ? "5" : "0"), 7);
|
||||
return result;
|
||||
} else if (format == 4) {
|
||||
strlcpy(result, itoa(new_value >> 2, s2, 10), 5);
|
||||
strlcat(result, ".", 5);
|
||||
new_value = (new_value & 0x03) * 25;
|
||||
strlcat(result, itoa(new_value, s2, 10), 7);
|
||||
return result;
|
||||
|
||||
} else if (format > 0) {
|
||||
strlcpy(result, itoa(new_value / format, s2, 10), 5);
|
||||
strlcat(result, ".", 5);
|
||||
@@ -265,7 +256,7 @@ char * Helpers::render_value(char * result, uint8_t value, int8_t format, const
|
||||
strlcpy(result, itoa(new_value * format * -1, s2, 10), 5);
|
||||
}
|
||||
|
||||
return remove_trailing_zero(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// float: convert float to char
|
||||
@@ -306,7 +297,7 @@ char * Helpers::render_value(char * result, const double value, const int8_t for
|
||||
}
|
||||
itoa(decimal, result, 10);
|
||||
|
||||
return remove_trailing_zero(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// int32: convert signed 32bit to text string and returns string
|
||||
@@ -344,7 +335,7 @@ char * Helpers::render_value(char * result, const int32_t value, const int8_t fo
|
||||
strlcat(result, itoa(new_value * format * -1, s, 10), sizeof(s));
|
||||
}
|
||||
|
||||
return remove_trailing_zero(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// int16: convert short (two bytes) to text string and prints it
|
||||
@@ -406,7 +397,7 @@ char * Helpers::render_value(char * result, const uint32_t value, const int8_t f
|
||||
}
|
||||
#endif
|
||||
|
||||
return remove_trailing_zero(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// convert special Latin1 characters to UTF8
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user