mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-09-14 06:04:06 +00:00
Merge branch 'dev'
This commit is contained in:
+3
-4
@@ -5,7 +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.0]
|
||||
## [3.8.0] 31 December 2025
|
||||
|
||||
## Added
|
||||
|
||||
@@ -26,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)
|
||||
@@ -90,7 +90,6 @@ 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,3 +1,31 @@
|
||||
# Changelog
|
||||
|
||||
For more details go to [emsesp.org](https://emsesp.org/).
|
||||
|
||||
## [3.8.1]
|
||||
|
||||
## 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`)
|
||||
|
||||
@@ -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_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
|
||||
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Sources & Files
|
||||
|
||||
@@ -78,15 +78,13 @@ For a live demo go to [demo.emsesp.org](https://demo.emsesp.org). Pick a languag
|
||||
|
||||
## 💖 **Contributors**
|
||||
|
||||
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).
|
||||
EMS-ESP is a project originally created by [proddy](https://github.com/proddy) and maintained by the ems-esp community.
|
||||
|
||||
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**
|
||||
|
||||
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`).
|
||||
See the [Building Guide](https://emsesp.org/Building) section of the documentation for instructions on how to build EMS-ESP.
|
||||
|
||||
## 📢 **Libraries used**
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report any security vulnerabilities using the [Contact Form](https://emsesp.org/About/#-contact).
|
||||
+2
-2
@@ -37,8 +37,8 @@
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"use_1200bps_touch": false,
|
||||
"wait_for_upload_port": false,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
|
||||
+2
-1
@@ -35,6 +35,7 @@
|
||||
"managed_components/**",
|
||||
"pnpm-*.yaml",
|
||||
"vite.config.ts",
|
||||
"lib/esp32-psram/**"
|
||||
"lib/esp32-psram/**",
|
||||
"test/test_api/test_api.h"
|
||||
]
|
||||
}
|
||||
+6636
-4838
File diff suppressed because it is too large
Load Diff
+3957
-3948
File diff suppressed because it is too large
Load Diff
@@ -111,10 +111,10 @@ telegram_type_id,name,is_fetched
|
||||
0x02A0,RC300Curves,
|
||||
0x02A1,RC300Curves,
|
||||
0x02A2,RC300Curves,
|
||||
0x02A5,RC300Monitor,fetched
|
||||
0x02A5,RC300Monitor,
|
||||
0x02A6,RC300Monitor,
|
||||
0x02A7,RC300Monitor,
|
||||
0x02A8,RC300Monitor,
|
||||
0x02A8,CRFMonitor,
|
||||
0x02A9,RC300Monitor,
|
||||
0x02AA,RC300Monitor,
|
||||
0x02AB,RC300Monitor,
|
||||
|
||||
|
+13
-13
@@ -12,7 +12,7 @@
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build-hosted": "typesafe-i18n && vite build --mode hosted",
|
||||
"build-hosted": "typesafe-i18n --no-watch && 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,8 +26,8 @@
|
||||
"@alova/adapter-xhr": "2.3.1",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.3.6",
|
||||
"@mui/material": "^7.3.6",
|
||||
"@mui/icons-material": "^7.3.7",
|
||||
"@mui/material": "^7.3.7",
|
||||
"@preact/compat": "^18.3.1",
|
||||
"@table-library/react-table-library": "4.1.15",
|
||||
"alova": "3.4.1",
|
||||
@@ -37,11 +37,11 @@
|
||||
"jwt-decode": "^4.0.0",
|
||||
"magic-string": "^0.30.21",
|
||||
"mime-types": "^3.0.2",
|
||||
"preact": "^10.28.1",
|
||||
"preact": "^10.28.2",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router": "^7.11.0",
|
||||
"react-router": "^7.12.0",
|
||||
"react-toastify": "^11.0.5",
|
||||
"typesafe-i18n": "^5.26.2",
|
||||
"typescript": "^5.9.3"
|
||||
@@ -51,21 +51,21 @@
|
||||
"@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",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||
"@types/node": "^25.0.6",
|
||||
"@types/react": "^19.2.8",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"axe-core": "^4.11.0",
|
||||
"axe-core": "^4.11.1",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.7.4",
|
||||
"rollup-plugin-visualizer": "^6.0.5",
|
||||
"terser": "^5.44.1",
|
||||
"typescript-eslint": "^8.51.0",
|
||||
"vite": "^7.3.0",
|
||||
"typescript-eslint": "^8.52.0",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-tsconfig-paths": "^6.0.3"
|
||||
"vite-tsconfig-paths": "^6.0.4"
|
||||
},
|
||||
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
|
||||
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
|
||||
}
|
||||
|
||||
Generated
+6111
File diff suppressed because it is too large
Load Diff
@@ -788,10 +788,12 @@ const Customizations = () => {
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
return restarting ? (
|
||||
<SystemMonitor />
|
||||
) : (
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{restarting ? <SystemMonitor /> : renderContent()}
|
||||
{renderContent()}
|
||||
{selectedDeviceEntity && (
|
||||
<SettingsCustomizationsDialog
|
||||
open={dialogOpen}
|
||||
|
||||
@@ -41,6 +41,7 @@ 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);
|
||||
@@ -51,7 +52,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: ScheduleFlag.SCHEDULE_DAY,
|
||||
flags: FLAG_ALL_DAYS,
|
||||
time: '',
|
||||
cmd: '',
|
||||
value: '',
|
||||
|
||||
@@ -33,7 +33,8 @@ import type { ScheduleItem } from './types';
|
||||
|
||||
// Constants
|
||||
const FLAG_MASK_127 = 127;
|
||||
const SCHEDULE_TYPE_THRESHOLD = 128;
|
||||
const SCHEDULE_TYPE_THRESHOLD = 127;
|
||||
const FLAG_ALL_DAYS = 127;
|
||||
const DEFAULT_TIME = '00:00';
|
||||
const TYPOGRAPHY_FONT_SIZE = 10;
|
||||
|
||||
@@ -104,7 +105,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
|
||||
);
|
||||
@@ -181,7 +182,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 ? 0 : flag;
|
||||
const newFlags = flag === ScheduleFlag.SCHEDULE_DAY ? FLAG_ALL_DAYS : flag;
|
||||
setEditItem((prev) => ({ ...prev, time: '', flags: newFlags }));
|
||||
}
|
||||
},
|
||||
@@ -190,7 +191,8 @@ const SchedulerDialog = ({
|
||||
|
||||
const handleDOWChange = useCallback(
|
||||
(_event: React.SyntheticEvent<HTMLElement>, flags: string[]) => {
|
||||
const newFlags = getFlagDOWnumber(flags);
|
||||
const newFlags =
|
||||
getFlagDOWnumber(flags) === 0 ? FLAG_ALL_DAYS : getFlagDOWnumber(flags);
|
||||
setEditItem((prev) => ({ ...prev, flags: newFlags }));
|
||||
},
|
||||
[getFlagDOWnumber]
|
||||
|
||||
@@ -196,7 +196,7 @@ export enum DeviceValueUOM {
|
||||
MBAR,
|
||||
LH,
|
||||
CTKWH,
|
||||
HZ
|
||||
HERTZ
|
||||
}
|
||||
|
||||
export const DeviceValueUOM_s = [
|
||||
|
||||
@@ -489,17 +489,25 @@ const ApplicationSettings = () => {
|
||||
name="board_profile"
|
||||
label={LL.BOARD_PROFILE()}
|
||||
value={data.board_profile}
|
||||
disabled={processingBoard || hardwareData.model.startsWith('BBQKees')}
|
||||
disabled={processingBoard}
|
||||
variant="outlined"
|
||||
onChange={changeBoardProfile}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
{boardProfileItems}
|
||||
<Divider />
|
||||
<MenuItem key={'CUSTOM'} value={'CUSTOM'}>
|
||||
{LL.CUSTOM()}…
|
||||
</MenuItem>
|
||||
{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) && (
|
||||
<MenuItem key={'CUSTOM'} value={'CUSTOM'}>
|
||||
{LL.CUSTOM()}…
|
||||
</MenuItem>
|
||||
)}
|
||||
</TextField>
|
||||
{data.board_profile === 'CUSTOM' && (
|
||||
<>
|
||||
@@ -896,10 +904,12 @@ const ApplicationSettings = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
return restarting ? (
|
||||
<SystemMonitor />
|
||||
) : (
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{restarting ? <SystemMonitor /> : content()}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -345,75 +345,91 @@ const MqttSettings = () => {
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
name="ha_enabled"
|
||||
checked={data.ha_enabled}
|
||||
onChange={updateFormValue}
|
||||
disabled={data.publish_single}
|
||||
/>
|
||||
}
|
||||
label={LL.MQTT_PUBLISH_TEXT_3()}
|
||||
/>
|
||||
</Grid>
|
||||
{data.ha_enabled && (
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="discovery_type"
|
||||
label={LL.MQTT_PUBLISH_TEXT_5()}
|
||||
value={data.discovery_type}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>Home Assistant</MenuItem>
|
||||
<MenuItem value={1}>Domoticz</MenuItem>
|
||||
<MenuItem value={2}>Domoticz (latest)</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="discovery_prefix"
|
||||
label={LL.MQTT_PUBLISH_TEXT_4()}
|
||||
variant="outlined"
|
||||
value={data.discovery_prefix}
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="entity_format"
|
||||
label={LL.MQTT_ENTITY_FORMAT()}
|
||||
value={data.entity_format}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>{LL.MQTT_ENTITY_FORMAT_0()}</MenuItem>
|
||||
<MenuItem value={3}>
|
||||
{LL.MQTT_ENTITY_FORMAT_1()} (v3.5)
|
||||
</MenuItem>
|
||||
<MenuItem value={4}>
|
||||
{LL.MQTT_ENTITY_FORMAT_2()} (v3.5)
|
||||
</MenuItem>
|
||||
<MenuItem value={1}>
|
||||
{LL.MQTT_ENTITY_FORMAT_1()} (latest)
|
||||
</MenuItem>
|
||||
<MenuItem value={2}>
|
||||
{LL.MQTT_ENTITY_FORMAT_2()} (latest)
|
||||
</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
{/* <Grid container spacing={2} rowSpacing={0}> */}
|
||||
<Grid>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
name="ha_enabled"
|
||||
checked={data.ha_enabled}
|
||||
onChange={updateFormValue}
|
||||
disabled={data.publish_single}
|
||||
/>
|
||||
}
|
||||
label={LL.MQTT_PUBLISH_TEXT_3()}
|
||||
/>
|
||||
</Grid>
|
||||
{data.ha_enabled && (
|
||||
<Grid container spacing={2} rowSpacing={0}>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="discovery_type"
|
||||
label={LL.MQTT_PUBLISH_TEXT_5()}
|
||||
value={data.discovery_type}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>Home Assistant</MenuItem>
|
||||
<MenuItem value={1}>Domoticz</MenuItem>
|
||||
<MenuItem value={2}>Domoticz (latest)</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="discovery_prefix"
|
||||
label={LL.MQTT_PUBLISH_TEXT_4()}
|
||||
variant="outlined"
|
||||
value={data.discovery_prefix}
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextField
|
||||
name="entity_format"
|
||||
label={LL.MQTT_ENTITY_FORMAT()}
|
||||
value={data.entity_format}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>{LL.MQTT_ENTITY_FORMAT_0()}</MenuItem>
|
||||
<MenuItem value={3}>
|
||||
{LL.MQTT_ENTITY_FORMAT_1()} (v3.5)
|
||||
</MenuItem>
|
||||
<MenuItem value={4}>
|
||||
{LL.MQTT_ENTITY_FORMAT_2()} (v3.5)
|
||||
</MenuItem>
|
||||
<MenuItem value={1}>
|
||||
{LL.MQTT_ENTITY_FORMAT_1()} (latest)
|
||||
</MenuItem>
|
||||
<MenuItem value={2}>
|
||||
{LL.MQTT_ENTITY_FORMAT_2()} (latest)
|
||||
</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>
|
||||
)}
|
||||
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
||||
{LL.MQTT_PUBLISH_INTERVALS()} (0=auto)
|
||||
</Typography>
|
||||
|
||||
@@ -182,7 +182,7 @@ const Settings = () => {
|
||||
restarting
|
||||
]);
|
||||
|
||||
return <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
|
||||
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -397,10 +397,12 @@ const NetworkSettings = () => {
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
return restarting ? (
|
||||
<SystemMonitor />
|
||||
) : (
|
||||
<SectionContent>
|
||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
||||
{restarting ? <SystemMonitor /> : content()}
|
||||
{content()}
|
||||
</SectionContent>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -443,7 +443,7 @@ const SystemStatus = () => {
|
||||
renderRestartDialog
|
||||
]);
|
||||
|
||||
return <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
|
||||
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
|
||||
};
|
||||
|
||||
export default SystemStatus;
|
||||
|
||||
@@ -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 <SectionContent>{restarting ? <SystemMonitor /> : content}</SectionContent>;
|
||||
return restarting ? <SystemMonitor /> : <SectionContent>{content}</SectionContent>;
|
||||
};
|
||||
|
||||
export default memo(Version);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { memo } from 'react';
|
||||
import { type FC, type PropsWithChildren, memo } from 'react';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
import type { BoxProps } from '@mui/material';
|
||||
|
||||
const ButtonRow = memo<BoxProps>(({ children, ...rest }) => (
|
||||
const ButtonRow: FC<PropsWithChildren<BoxProps>> = memo(({ children, ...rest }) => (
|
||||
<Box
|
||||
sx={{
|
||||
'& button, & a, & .MuiCard-root': {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type FC, memo, useMemo } from 'react';
|
||||
import { type FC, type PropsWithChildren, memo, useMemo } from 'react';
|
||||
|
||||
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
|
||||
import ErrorIcon from '@mui/icons-material/Error';
|
||||
@@ -12,6 +12,7 @@ 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>> = {
|
||||
@@ -28,7 +29,7 @@ const LEVEL_PALETTE_PATHS: Record<MessageBoxLevel, string> = {
|
||||
error: 'error.dark'
|
||||
};
|
||||
|
||||
const MessageBox: FC<MessageBoxProps> = ({
|
||||
const MessageBox: FC<PropsWithChildren<MessageBoxProps>> = ({
|
||||
level,
|
||||
message,
|
||||
sx,
|
||||
|
||||
@@ -39,9 +39,8 @@ const LayoutMenuItemComponent = ({
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: selected ? '4px' : '0px',
|
||||
width: selected ? '3px' : '0px',
|
||||
backgroundColor: '#90caf9',
|
||||
borderRadius: '0 2px 2px 0',
|
||||
transition: 'width 0.05s cubic-bezier(0.55, 0.085, 0.68, 0.53)'
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -219,6 +219,7 @@ 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,6 +219,7 @@ 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,6 +219,7 @@ 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,6 +219,7 @@ 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,6 +219,7 @@ 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',
|
||||
|
||||
@@ -219,6 +219,7 @@ 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,6 +219,7 @@ 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,6 +219,7 @@ 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,6 +219,7 @@ 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,6 +219,7 @@ 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,6 +219,7 @@ 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,4 +49,5 @@ export interface MqttSettingsType {
|
||||
publish_single2cmd: boolean;
|
||||
discovery_prefix: string;
|
||||
discovery_type: number;
|
||||
ha_number_mode: number;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ export interface SystemStatus {
|
||||
free_psram?: number;
|
||||
free_caps: number;
|
||||
model: string;
|
||||
board: string;
|
||||
has_loader: boolean;
|
||||
has_partition: boolean;
|
||||
partitions: {
|
||||
|
||||
@@ -136,9 +136,16 @@ const createManualChunks = (detailed = false) => {
|
||||
return 'vendor';
|
||||
}
|
||||
if (detailed) {
|
||||
if (id.includes('components/')) return 'components';
|
||||
if (id.includes('app/')) return 'app';
|
||||
if (id.includes('utils/')) return 'utils';
|
||||
// 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('api/')) return 'api';
|
||||
}
|
||||
return undefined;
|
||||
|
||||
@@ -52,7 +52,7 @@ ModbusClientTCPasync::~ModbusClientTCPasync() {
|
||||
}
|
||||
}
|
||||
// force close client
|
||||
MTA_client.close(true);
|
||||
MTA_client.close();
|
||||
}
|
||||
|
||||
// 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(force);
|
||||
MTA_client.close();
|
||||
}
|
||||
|
||||
// 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(true);
|
||||
client->close();
|
||||
delete client;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +321,14 @@ bool Packet::_allocate(size_t remainingLength, bool check) {
|
||||
#if EMC_USE_MEMPOOL
|
||||
_data = reinterpret_cast<uint8_t*>(_memPool.malloc(_size));
|
||||
#else
|
||||
_data = reinterpret_cast<uint8_t*>(malloc(_size));
|
||||
#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(false);
|
||||
client.close();
|
||||
}
|
||||
|
||||
bool ClientAsync::connected() {
|
||||
|
||||
@@ -76,6 +76,10 @@ 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 *
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
-13
@@ -1,13 +1 @@
|
||||
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>
|
||||
Refer to the [EMS-ESP documentation](https://emsesp.org/Building#webui-development) on how to run the mock Web server.
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@msgpack/msgpack": "^3.1.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||
"formidable": "^3.5.4",
|
||||
"itty-router": "^5.0.22",
|
||||
"prettier": "^3.7.4"
|
||||
},
|
||||
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
|
||||
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
|
||||
}
|
||||
|
||||
Generated
+316
@@ -0,0 +1,316 @@
|
||||
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.7.4)
|
||||
formidable:
|
||||
specifier: ^3.5.4
|
||||
version: 3.5.4
|
||||
itty-router:
|
||||
specifier: ^5.0.22
|
||||
version: 5.0.22
|
||||
prettier:
|
||||
specifier: ^3.7.4
|
||||
version: 3.7.4
|
||||
|
||||
packages:
|
||||
|
||||
'@babel/code-frame@7.27.1':
|
||||
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/generator@7.28.5':
|
||||
resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==}
|
||||
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.28.5':
|
||||
resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/template@7.27.2':
|
||||
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/traverse@7.28.5':
|
||||
resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
||||
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.22:
|
||||
resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==}
|
||||
|
||||
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.7.4:
|
||||
resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/code-frame@7.27.1':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
js-tokens: 4.0.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
'@babel/generator@7.28.5':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/types': 7.28.5
|
||||
'@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.28.5':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.5
|
||||
|
||||
'@babel/template@7.27.2':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/types': 7.28.5
|
||||
|
||||
'@babel/traverse@7.28.5':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
'@babel/generator': 7.28.5
|
||||
'@babel/helper-globals': 7.28.0
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/types': 7.28.5
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
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.7.4)':
|
||||
dependencies:
|
||||
'@babel/generator': 7.28.5
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/traverse': 7.28.5
|
||||
'@babel/types': 7.28.5
|
||||
javascript-natural-sort: 0.7.1
|
||||
lodash-es: 4.17.22
|
||||
minimatch: 9.0.5
|
||||
parse-imports-exports: 0.2.4
|
||||
prettier: 3.7.4
|
||||
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.22: {}
|
||||
|
||||
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.7.4: {}
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
@@ -130,7 +130,9 @@ 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
|
||||
};
|
||||
@@ -584,6 +586,7 @@ 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,
|
||||
|
||||
+4
-4
@@ -105,8 +105,8 @@ build_type = release
|
||||
board_build.filesystem = littlefs
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson @ 7.4.2
|
||||
ESP32Async/AsyncTCP @ 3.4.9
|
||||
ESP32Async/ESPAsyncWebServer @ 3.9.3
|
||||
ESP32Async/AsyncTCP @ 3.4.10
|
||||
ESP32Async/ESPAsyncWebServer @ 3.9.4
|
||||
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_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -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_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
|
||||
; Modbus
|
||||
; Creates the file modbus_entity_parameters.hpp
|
||||
|
||||
@@ -1446,3 +1446,6 @@ vchip
|
||||
SPIIO
|
||||
SPIDQS
|
||||
txpause
|
||||
relais
|
||||
pinout
|
||||
TXEN
|
||||
@@ -94,8 +94,9 @@ 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_suffix
|
||||
type_rest_str = ""
|
||||
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(")")]
|
||||
@@ -106,14 +107,12 @@ def print_device_entities(device_name, device_entities):
|
||||
elif "/" in range_inner:
|
||||
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 = ""
|
||||
type_rest_str = f" (>={min_value.strip()}<={max_value.strip()})"
|
||||
except Exception:
|
||||
# fallback to original
|
||||
pass
|
||||
|
||||
print(f"| {entity['shortname']} | {entity['fullname']} | {type_base} {type_rest_str} | "
|
||||
print(f"| {entity['shortname']} | {entity['fullname']} | {type_base}{type_rest_str} | "
|
||||
f"{entity['uom']} | {entity['writeable']} | {tag_type} | {entity['modbus offset']} | "
|
||||
f"{entity['modbus count']} | {entity['modbus scale factor']} |")
|
||||
print()
|
||||
@@ -135,6 +134,9 @@ 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")
|
||||
|
||||
@@ -58,12 +58,12 @@ enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING };
|
||||
|
||||
class APSettings {
|
||||
public:
|
||||
uint8_t provisionMode; // 0 = on, 2 = off
|
||||
String ssid;
|
||||
String password;
|
||||
uint8_t channel;
|
||||
bool ssidHidden;
|
||||
uint8_t maxClients;
|
||||
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;
|
||||
|
||||
IPAddress localIP;
|
||||
IPAddress gatewayIP;
|
||||
|
||||
@@ -70,6 +70,10 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -248,6 +248,7 @@ 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;
|
||||
@@ -288,6 +289,7 @@ 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;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <espMqttClient.h>
|
||||
|
||||
#include <uuid/common.h>
|
||||
#include <default_settings.h>
|
||||
|
||||
#define MQTT_RECONNECTION_DELAY 2000 // 2 seconds
|
||||
|
||||
@@ -65,37 +66,38 @@
|
||||
|
||||
class MqttSettings {
|
||||
public:
|
||||
bool enabled;
|
||||
String host;
|
||||
uint16_t port;
|
||||
String rootCA;
|
||||
bool enableTLS;
|
||||
String username;
|
||||
String password;
|
||||
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;
|
||||
String clientId;
|
||||
uint16_t keepAlive;
|
||||
bool cleanSession;
|
||||
uint16_t keepAlive = FACTORY_MQTT_KEEP_ALIVE;
|
||||
bool cleanSession = FACTORY_MQTT_CLEAN_SESSION;
|
||||
|
||||
// EMS-ESP specific
|
||||
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;
|
||||
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;
|
||||
|
||||
static void read(MqttSettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, MqttSettings & settings);
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
|
||||
class NTPSettings {
|
||||
public:
|
||||
bool enabled;
|
||||
String tzLabel;
|
||||
String tzFormat;
|
||||
String server;
|
||||
bool enabled = FACTORY_NTP_ENABLED;
|
||||
String tzLabel = FACTORY_NTP_TIME_ZONE_LABEL;
|
||||
String tzFormat = FACTORY_NTP_TIME_ZONE_FORMAT;
|
||||
String server = FACTORY_NTP_SERVER;
|
||||
|
||||
static void read(NTPSettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, NTPSettings & settings);
|
||||
|
||||
@@ -64,17 +64,17 @@
|
||||
class NetworkSettings {
|
||||
public:
|
||||
// core wifi configuration
|
||||
String ssid;
|
||||
String bssid;
|
||||
String password;
|
||||
String hostname;
|
||||
bool staticIPConfig;
|
||||
bool bandwidth20;
|
||||
uint8_t tx_power;
|
||||
bool nosleep;
|
||||
bool enableMDNS;
|
||||
bool enableCORS;
|
||||
String CORSOrigin;
|
||||
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 = "*";
|
||||
|
||||
// optional configuration for static IP address
|
||||
IPAddress localIP;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#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(true);
|
||||
request->client()->close();
|
||||
_is_firmware = false;
|
||||
Update.abort();
|
||||
}
|
||||
|
||||
@@ -56,8 +56,7 @@ void IRAM_ATTR AnalogSensor::freqIrq2() {
|
||||
#endif
|
||||
|
||||
void AnalogSensor::start(const bool factory_settings) {
|
||||
// if (factory_settings && EMSESP::nvs_.getString("boot").equals("E32V2_2")) {
|
||||
if (factory_settings && analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
|
||||
if (factory_settings && EMSESP::system_.board_profile() == "E32V2_2") {
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
auto newSensor = AnalogCustomization();
|
||||
strcpy(newSensor.name, "core_voltage");
|
||||
@@ -68,12 +67,22 @@ 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
|
||||
});
|
||||
@@ -576,7 +585,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, "Analog Sensor")) {
|
||||
if (EMSESP::system_.add_gpio(gpio, name)) {
|
||||
LOG_DEBUG("Adding customization for analog sensor GPIO %02d", gpio);
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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,10 +25,6 @@
|
||||
#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
|
||||
@@ -214,6 +210,10 @@
|
||||
#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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
|
||||
+61
-37
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -547,40 +547,56 @@ 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) {
|
||||
@@ -1295,11 +1311,21 @@ 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('\"');
|
||||
@@ -1323,7 +1349,7 @@ void EMSdevice::dump_devicevalue_info() {
|
||||
Serial.print(dv.fullname[0]);
|
||||
Serial.print(',');
|
||||
|
||||
// per type
|
||||
// type [options...] [(min/max)]
|
||||
switch (dv.type) {
|
||||
case DeviceValueType::ENUM:
|
||||
case DeviceValueType::CMD:
|
||||
@@ -1383,14 +1409,12 @@ void EMSdevice::dump_devicevalue_info() {
|
||||
break;
|
||||
}
|
||||
|
||||
// min/max range
|
||||
int16_t dv_set_min;
|
||||
uint32_t dv_set_max;
|
||||
if (dv.get_min_max(dv_set_min, dv_set_max)) {
|
||||
// min/max range, if writeable
|
||||
if (dv.has_cmd) {
|
||||
Serial.print(" (>=");
|
||||
Serial.print(dv_set_min);
|
||||
Serial.print(dv.min);
|
||||
Serial.print("<=");
|
||||
Serial.print(dv_set_max);
|
||||
Serial.print(dv.max);
|
||||
Serial.print(")");
|
||||
}
|
||||
Serial.print(",");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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/project/types.ts for the Web UI
|
||||
// also maps to DeviceValueUOM in interface/src/app/main/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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
HZ, // 27 - Hz - frequency
|
||||
HERTZ, // 27 - Hz - frequency
|
||||
CONNECTIVITY // 28 - used in HA - connectivity
|
||||
};
|
||||
|
||||
|
||||
+23
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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,6 +20,7 @@
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include "esp_ota_ops.h"
|
||||
#include "rom/rtc.h"
|
||||
#endif
|
||||
|
||||
static_assert(uuid::thread_safe, "uuid-common must be thread-safe");
|
||||
@@ -1721,15 +1722,31 @@ void EMSESP::start() {
|
||||
LOG_INFO("EMS-ESP version %s", EMSESP_APP_VERSION);
|
||||
#endif
|
||||
|
||||
// check if the firmware is fresh
|
||||
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
|
||||
// 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 fresh");
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("System is running in Debug mode");
|
||||
// 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_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
|
||||
@@ -1837,7 +1854,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 log");
|
||||
LOG_ERROR("Invalid GPIOs used. Please check your settings and the system log");
|
||||
only_once = true;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
|
||||
+17
-8
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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,6 +207,18 @@ 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) {
|
||||
@@ -240,14 +252,11 @@ 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);
|
||||
@@ -256,7 +265,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 result;
|
||||
return remove_trailing_zero(result);
|
||||
}
|
||||
|
||||
// float: convert float to char
|
||||
@@ -297,7 +306,7 @@ char * Helpers::render_value(char * result, const double value, const int8_t for
|
||||
}
|
||||
itoa(decimal, result, 10);
|
||||
|
||||
return ret;
|
||||
return remove_trailing_zero(ret);
|
||||
}
|
||||
|
||||
// int32: convert signed 32bit to text string and returns string
|
||||
@@ -335,7 +344,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 result;
|
||||
return remove_trailing_zero(result);
|
||||
}
|
||||
|
||||
// int16: convert short (two bytes) to text string and prints it
|
||||
@@ -397,7 +406,7 @@ char * Helpers::render_value(char * result, const uint32_t value, const int8_t f
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
return remove_trailing_zero(result);
|
||||
}
|
||||
|
||||
// convert special Latin1 characters to UTF8
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -88,6 +88,8 @@ class Helpers {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
static char * ultostr(char * ptr, uint32_t value, const uint8_t base);
|
||||
#endif
|
||||
private:
|
||||
static char * remove_trailing_zero(char * result);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -245,6 +245,7 @@ MAKE_NOTRANSLATION(tag_device_data, "")
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||
// Translating hours/minute/seconds are done in emsdevice.cpp (uom_to_string())
|
||||
// to keep to some form of consistency, all UOMs follow Home Assistant naming conventions - https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes
|
||||
MAKE_WORD_CUSTOM(uom_blank, " ")
|
||||
MAKE_WORD_CUSTOM(uom_percent, "%")
|
||||
MAKE_WORD_CUSTOM(uom_degrees, "°C")
|
||||
@@ -381,6 +382,7 @@ MAKE_ENUM(enum_lowNoiseMode, FL_(off), FL_(reduced_output), FL_(switchoff), FL_(
|
||||
|
||||
// heat pump
|
||||
MAKE_ENUM(enum_hpactivity, FL_(off), FL_(heating), FL_(cooling), FL_(hot_water), FL_(pool), FL_(pool_heating), FL_(defrost), FL_(compressor_alarm)) // BV name COMPRESSOR_E21_STATUS
|
||||
MAKE_ENUM(enum_hpactivity1, FL_(off), FL_(heating), FL_(hot_water), FL_(defrost), FL_(compressor_alarm))
|
||||
MAKE_ENUM(enum_silentMode, FL_(off), FL_(auto), FL_(on))
|
||||
MAKE_ENUM(enum_4way, FL_(cool_defrost), FL_(heat_ww))
|
||||
MAKE_ENUM(enum_auxHeaterSource, FL_(not_installed), FL_(el_heater), FL_(exclusive), FL_(parallel), FL_(hybrid))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -504,30 +504,33 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpPh1), 13, 1), // hpph1
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatingPumpMod), 14, 1), // heatingpumpmod
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpCompSpd), 15, 1), // hpcompspd
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hybridStrategy), 16, 1), // hybridstrategy
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseMode), 17, 1), // lownoisemode
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseStart), 18, 1), // lownoisestart
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseStop), 19, 1), // lownoisestop
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceGas), 20, 1), // energypricegas
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceEl), 21, 1), // energypriceel
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPricePV), 22, 1), // energyfeedpv
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(switchOverTemp), 23, 1), // switchovertemp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(airPurgeMode), 24, 1), // airpurgemode
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatPumpOutput), 25, 1), // heatpumpoutput
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(coolingCircuit), 26, 1), // coolingcircuit
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(compStartMod), 27, 1), // compstartmod
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatDrainPan), 28, 1), // heatdrainpan
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatCable), 29, 1), // heatcable
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgTotal), 30, 2), // nrgtotal
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat), 32, 2), // nrgheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterTotal), 34, 2), // metertotal
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterComp), 36, 2), // metercomp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterEHeat), 38, 2), // metereheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterHeat), 40, 2), // meterheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatingStarts), 42, 2), // heatingstarts
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(fuelHeat), 44, 2), // fuelheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(elHeat), 46, 2), // elheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(elGenHeat), 48, 2), // elgenheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpActivity), 16, 1), // hpactivity
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpPower), 17, 1), // hppower
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpCurrPower), 18, 1), // hpcurrpower
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hybridStrategy), 19, 1), // hybridstrategy
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseMode), 20, 1), // lownoisemode
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseStart), 21, 1), // lownoisestart
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseStop), 22, 1), // lownoisestop
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceGas), 23, 1), // energypricegas
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceEl), 24, 1), // energypriceel
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPricePV), 25, 1), // energyfeedpv
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(switchOverTemp), 26, 1), // switchovertemp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(airPurgeMode), 27, 1), // airpurgemode
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatPumpOutput), 28, 1), // heatpumpoutput
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(coolingCircuit), 29, 1), // coolingcircuit
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(compStartMod), 30, 1), // compstartmod
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatDrainPan), 31, 1), // heatdrainpan
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatCable), 32, 1), // heatcable
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgTotal), 33, 2), // nrgtotal
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat), 35, 2), // nrgheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterTotal), 37, 2), // metertotal
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterComp), 39, 2), // metercomp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterEHeat), 41, 2), // metereheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterHeat), 43, 2), // meterheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatingStarts), 45, 2), // heatingstarts
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(fuelHeat), 47, 2), // fuelheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(elHeat), 49, 2), // elheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(elGenHeat), 51, 2), // elgenheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(hybridDHW), 0, 1), // hybriddhw
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(nrgWw), 1, 2), // nrg
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(meterWw), 3, 2), // meter
|
||||
|
||||
+41
-31
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -43,6 +43,7 @@ bool Mqtt::ha_enabled_;
|
||||
uint8_t Mqtt::nested_format_;
|
||||
std::string Mqtt::discovery_prefix_;
|
||||
uint8_t Mqtt::discovery_type_;
|
||||
uint8_t Mqtt::ha_number_mode_;
|
||||
bool Mqtt::send_response_;
|
||||
bool Mqtt::publish_single_;
|
||||
bool Mqtt::publish_single2cmd_;
|
||||
@@ -343,6 +344,7 @@ void Mqtt::load_settings() {
|
||||
discovery_prefix_ = mqttSettings.discovery_prefix.c_str();
|
||||
entity_format_ = mqttSettings.entity_format;
|
||||
discovery_type_ = mqttSettings.discovery_type;
|
||||
ha_number_mode_ = mqttSettings.ha_number_mode;
|
||||
|
||||
// convert to milliseconds
|
||||
publish_time_boiler_ = mqttSettings.publish_time_boiler * 1000;
|
||||
@@ -562,8 +564,8 @@ void Mqtt::ha_status() {
|
||||
// Note we don't use camelCase as it would change the HA entity_id and impact historic data
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!EMSESP::system_.ethernet_connected() || WiFi.isConnected()) {
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT8, "WiFi RSSI", "rssi", DeviceValueUOM::DBM);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT8, "WiFi strength", "wifistrength", DeviceValueUOM::PERCENT);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT8, "RSSI", "rssi", DeviceValueUOM::DBM);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT8, "Signal", "wifistrength", DeviceValueUOM::PERCENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1024,8 +1026,12 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
snprintf(sample_val, sizeof(sample_val), "'%s'", Helpers::translated_word(options[0]));
|
||||
}
|
||||
} else if (type != DeviceValueType::STRING && type != DeviceValueType::BOOL) {
|
||||
// For numeric's add the range
|
||||
doc["mode"] = "box"; // auto, slider or box
|
||||
// For numeric's add the range and mode
|
||||
if (ha_number_mode_ == 1 && (dv_set_max - dv_set_min) <= 100) {
|
||||
doc["mode"] = "slider";
|
||||
} else {
|
||||
doc["mode"] = "box"; // auto, slider or box
|
||||
}
|
||||
if (num_op > 0) {
|
||||
doc["step"] = 1.0 / num_op;
|
||||
} else if (num_op < 0) {
|
||||
@@ -1132,6 +1138,9 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||
doc["ent_cat"] = "diagnostic"; // instead of 'config'
|
||||
}
|
||||
if (type == DeviceValueType::BOOL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For display_only, we don't use the aliases, also we don't display the icon
|
||||
const char * dc_ha = display_only ? "device_class" : "dev_cla"; // device class, dev_cla
|
||||
@@ -1146,24 +1155,24 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
|
||||
// set uom, unless boolean - as HA is fussy with the naming and is case sensitive
|
||||
// map too HA uom specific codes from https://github.com/home-assistant/core/blob/dev/homeassistant/const.py
|
||||
if (type != DeviceValueType::BOOL) {
|
||||
if (uom == DeviceValueUOM::HOURS) {
|
||||
doc[uom_ha] = "h";
|
||||
} else if (uom == DeviceValueUOM::MINUTES) {
|
||||
doc[uom_ha] = "min";
|
||||
} else if (uom == DeviceValueUOM::SECONDS) {
|
||||
doc[uom_ha] = "s";
|
||||
} else if (uom == DeviceValueUOM::KB) {
|
||||
doc[uom_ha] = "kB";
|
||||
} else if (uom == DeviceValueUOM::LMIN) {
|
||||
doc[uom_ha] = "L/min";
|
||||
} else if (uom == DeviceValueUOM::LH) {
|
||||
doc[uom_ha] = "L/h";
|
||||
} else if (uom != DeviceValueUOM::NONE) {
|
||||
doc[uom_ha] = EMSdevice::uom_to_string(uom); // use default
|
||||
} else if (discovery_type() != discoveryType::HOMEASSISTANT) {
|
||||
doc[uom_ha] = " "; // Domoticz uses " " for a no-uom
|
||||
}
|
||||
if (uom == DeviceValueUOM::HOURS) {
|
||||
doc[uom_ha] = "h";
|
||||
} else if (uom == DeviceValueUOM::MINUTES) {
|
||||
doc[uom_ha] = "min";
|
||||
} else if (uom == DeviceValueUOM::SECONDS) {
|
||||
doc[uom_ha] = "s";
|
||||
} else if (uom == DeviceValueUOM::KB) {
|
||||
doc[uom_ha] = "kB";
|
||||
} else if (uom == DeviceValueUOM::LMIN) {
|
||||
doc[uom_ha] = "L/min";
|
||||
} else if (uom == DeviceValueUOM::LH) {
|
||||
doc[uom_ha] = "L/h";
|
||||
} else if (uom == DeviceValueUOM::L) {
|
||||
doc[uom_ha] = "L";
|
||||
} else if (uom != DeviceValueUOM::NONE) {
|
||||
doc[uom_ha] = EMSdevice::uom_to_string(uom); // use default
|
||||
} else if (discovery_type() != discoveryType::HOMEASSISTANT) {
|
||||
doc[uom_ha] = " "; // Domoticz uses " " for a no-uom
|
||||
}
|
||||
|
||||
// set state and device class - always
|
||||
@@ -1261,7 +1270,7 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
doc[sc_ha] = sc_ha_total_increasing;
|
||||
doc[dc_ha] = "monetary";
|
||||
break;
|
||||
case DeviceValueUOM::HZ:
|
||||
case DeviceValueUOM::HERTZ:
|
||||
doc[sc_ha] = sc_ha_measurement;
|
||||
doc[dc_ha] = "frequency";
|
||||
break;
|
||||
@@ -1280,9 +1289,8 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
// DeviceValueUOM::NONE:
|
||||
// DeviceValueUOM::KMIN:
|
||||
// for device entities which have numerical values, with no UOM
|
||||
if ((type != DeviceValueType::STRING)
|
||||
&& (type == DeviceValueType::INT8 || type == DeviceValueType::UINT8 || type == DeviceValueType::INT16 || type == DeviceValueType::UINT16
|
||||
|| type == DeviceValueType::UINT24 || type == DeviceValueType::UINT32)) {
|
||||
if (type == DeviceValueType::INT8 || type == DeviceValueType::UINT8 || type == DeviceValueType::INT16 || type == DeviceValueType::UINT16
|
||||
|| type == DeviceValueType::UINT24 || type == DeviceValueType::UINT32) {
|
||||
if (display_only) {
|
||||
doc[ic_ha] = F_(iconnum); // set icon
|
||||
}
|
||||
@@ -1418,11 +1426,13 @@ bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_room
|
||||
doc["mode_cmd_t"] = mode_cmd_s;
|
||||
|
||||
// add hvac_action - https://github.com/emsesp/EMS-ESP32/discussions/2562
|
||||
doc["act_t"] = "~/boiler_data";
|
||||
doc["act_tpl"] = "{% if value_json.hpactivity=='cooling'%}cooling{%elif value_json.heatingactive=='on'%}heating{%else%}idle{%endif%}";
|
||||
doc["act_t"] = "~/boiler_data";
|
||||
char on_string[12];
|
||||
doc["act_tpl"] = "{% if value_json.hpactivity=='cooling'%}cooling{%elif value_json.heatingactive=='" + std::string(Helpers::render_boolean(on_string, true))
|
||||
+ "'%}heating{%else%}idle{%endif%}"; // uses boolean translation for on/true
|
||||
|
||||
// map EMS modes to HA climate modes
|
||||
// EMS modes: auto, manual, heat, off, night, day, nofrost, eco, comfort, cool)
|
||||
// EMS modes: auto, manual, heat, off, night, day, nofrost, eco, comfort, cool
|
||||
// HA supports: auto, off, cool, heat, dry, fan_only
|
||||
bool found_auto = false;
|
||||
bool found_heat = false;
|
||||
@@ -1628,4 +1638,4 @@ void Mqtt::add_value_bool(JsonObject doc, const char * name, bool value) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -341,6 +341,7 @@ class Mqtt {
|
||||
static uint8_t entity_format_;
|
||||
static std::string discovery_prefix_;
|
||||
static uint8_t discovery_type_;
|
||||
static uint8_t ha_number_mode_;
|
||||
static bool publish_single_;
|
||||
static bool publish_single2cmd_;
|
||||
static bool send_response_;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
|
||||
+259
-136
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -105,10 +105,8 @@ uint32_t System::led_flash_duration_ = 0;
|
||||
bool System::led_flash_timer_ = false;
|
||||
|
||||
// GPIOs
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::valid_system_gpios_;
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::used_gpios_;
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::snapshot_used_gpios_;
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::snapshot_valid_system_gpios_;
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::valid_system_gpios_;
|
||||
std::vector<System::GpioUsage, AllocatorPSRAM<System::GpioUsage>> System::used_gpios_;
|
||||
|
||||
// find the index of the language
|
||||
// 0 = EN, 1 = DE, etc...
|
||||
@@ -318,13 +316,15 @@ void System::get_partition_info() {
|
||||
|
||||
auto current_partition = (const char *)esp_ota_get_running_partition()->label;
|
||||
|
||||
// update the current version and partition name in NVS if not already set (saves on flash wearing)
|
||||
if (EMSESP::nvs_.isKey(current_partition)) {
|
||||
if (EMSESP::nvs_.getString(current_partition) != EMSESP_APP_VERSION) {
|
||||
EMSESP::nvs_.putString(current_partition, EMSESP_APP_VERSION);
|
||||
}
|
||||
} else {
|
||||
EMSESP::nvs_.putString(current_partition, EMSESP_APP_VERSION); // create new entry
|
||||
// update the current version and partition name in NVS if not already set
|
||||
if (EMSESP::nvs_.getString(current_partition) != EMSESP_APP_VERSION || emsesp::EMSESP::nvs_.getBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true)) {
|
||||
EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, false);
|
||||
EMSESP::nvs_.putString(current_partition, EMSESP_APP_VERSION);
|
||||
char c[20];
|
||||
snprintf(c, sizeof(c), "d_%s", current_partition);
|
||||
auto t = time(nullptr);
|
||||
// write timestamp always with new version, if clock is not set, this will be updated with ntp
|
||||
EMSESP::nvs_.putULong(c, t);
|
||||
}
|
||||
|
||||
// Loop through all available partitions and update map with the version info pulled from NVS
|
||||
@@ -347,16 +347,17 @@ void System::get_partition_info() {
|
||||
// get the version from the NVS store, and add to map
|
||||
if (is_valid) {
|
||||
PartitionInfo p_info;
|
||||
p_info.version = "";
|
||||
p_info.install_date = ""; // this will be added later when NTP is connected
|
||||
// if there is an entry for this partition in NVS, get it's version from NVS
|
||||
p_info.version = EMSESP::nvs_.getString(part->label, "").c_str();
|
||||
char c[20];
|
||||
snprintf(c, sizeof(c), "d_%s", (const char *)part->label);
|
||||
time_t d = EMSESP::nvs_.getULong(c, 0);
|
||||
char time_string[25];
|
||||
strftime(time_string, sizeof(time_string), "%FT%T", localtime(&d));
|
||||
p_info.install_date = d > 1500000000L ? time_string : "";
|
||||
|
||||
p_info.size = part->size / 1024; // set size in KB
|
||||
|
||||
// if there is an entry for this partition in NVS, get it's version from NVS
|
||||
if (EMSESP::nvs_.isKey(part->label)) {
|
||||
p_info.version = EMSESP::nvs_.getString(part->label).c_str();
|
||||
}
|
||||
|
||||
partition_info_[part->label] = p_info;
|
||||
}
|
||||
|
||||
@@ -368,36 +369,21 @@ void System::get_partition_info() {
|
||||
|
||||
// set NTP install time/date for the current partition
|
||||
// assumes NTP is connected and working
|
||||
void System::set_partition_install_date(bool override) {
|
||||
void System::set_partition_install_date() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
auto current_partition = (const char *)esp_ota_get_running_partition()->label;
|
||||
if (current_partition == nullptr) {
|
||||
return; // fail-safe
|
||||
}
|
||||
|
||||
// skip if it already has an install date, unless override is true
|
||||
if (!partition_info_[current_partition].install_date.empty() && !override) {
|
||||
return;
|
||||
char c[20];
|
||||
snprintf(c, sizeof(c), "d_%s", current_partition);
|
||||
time_t d = EMSESP::nvs_.getULong(c, 0);
|
||||
if (d < 1500000000L) {
|
||||
LOG_INFO("Firmware is fresh, setting the new install date in partition %s", current_partition);
|
||||
auto t = time(nullptr) - uuid::get_uptime_sec();
|
||||
EMSESP::nvs_.putULong(c, t);
|
||||
}
|
||||
|
||||
// EMSESP_NVS_BOOT_NEW_FIRMWARE is set in UploadFileService::uploadComplete()
|
||||
auto is_fresh_firmware = EMSESP::nvs_.getBool(EMSESP_NVS_BOOT_NEW_FIRMWARE);
|
||||
// always reset flag after setting the install date
|
||||
EMSESP::nvs_.putBool(EMSESP_NVS_BOOT_NEW_FIRMWARE, false);
|
||||
|
||||
// check if the firmware is a new upload
|
||||
if (is_fresh_firmware) {
|
||||
LOG_DEBUG("Firmware is fresh, setting the new install date in partition %s", current_partition);
|
||||
} else {
|
||||
return; // skip, not new
|
||||
}
|
||||
|
||||
// set current date/time from NTP
|
||||
char time_string[25];
|
||||
time_t now = time(nullptr);
|
||||
strftime(time_string, sizeof(time_string), "%FT%T", localtime(&now));
|
||||
partition_info_[current_partition].install_date = time_string;
|
||||
LOG_DEBUG("Adding NTP install date %s to partition %s", time_string, current_partition);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1228,16 +1214,16 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
// GPIOs
|
||||
shell.println(" GPIOs:");
|
||||
shell.printf(" in use:");
|
||||
for (const auto & gpio : used_gpios_) {
|
||||
shell.printf(" %d", gpio);
|
||||
for (const auto & usage : used_gpios_) {
|
||||
shell.printf(" %d(%s)", usage.pin, usage.source.c_str());
|
||||
}
|
||||
shell.printfln(" (total %d)", used_gpios_.size());
|
||||
shell.printfln(" [total %d]", used_gpios_.size());
|
||||
auto available = available_gpios();
|
||||
shell.printf(" available:");
|
||||
for (const auto & gpio : available) {
|
||||
shell.printf(" %d", gpio);
|
||||
}
|
||||
shell.printfln(" (total %d)", available.size());
|
||||
shell.printfln(" [total %d]", available.size());
|
||||
// List all partitions and their version info
|
||||
shell.println(" Partitions:");
|
||||
for (const auto & partition : partition_info_) {
|
||||
@@ -1422,7 +1408,7 @@ bool System::check_upgrade() {
|
||||
// see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022
|
||||
missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5));
|
||||
if (missing_version) {
|
||||
LOG_WARNING("No version information found");
|
||||
LOG_WARNING("No version information found. Assuming version 3.5.0");
|
||||
settingsVersion = "3.5.0"; // this was the last stable version without version info
|
||||
}
|
||||
|
||||
@@ -1434,7 +1420,7 @@ bool System::check_upgrade() {
|
||||
bool save_version = true;
|
||||
bool reboot_required = false;
|
||||
|
||||
LOG_DEBUG("Checking for version upgrades (settings file is v%d.%d.%d%s)",
|
||||
LOG_DEBUG("Checking for version upgrades from v%d.%d.%d%s",
|
||||
settings_version.major(),
|
||||
settings_version.minor(),
|
||||
settings_version.patch(),
|
||||
@@ -1444,7 +1430,7 @@ bool System::check_upgrade() {
|
||||
if (this_version > settings_version) {
|
||||
// we need to do an upgrade
|
||||
if (missing_version) {
|
||||
LOG_NOTICE("Upgrading to version %d.%d.%d%s", this_version.major(), this_version.minor(), this_version.patch(), this_version_type);
|
||||
LOG_NOTICE("Upgrading to version %d.%d.%d%s", this_version.major(), this_version.minor(), this_version.patch(), this_version_type.c_str());
|
||||
} else {
|
||||
LOG_NOTICE("Upgrading from version %d.%d.%d%s to %d.%d.%d%s",
|
||||
settings_version.major(),
|
||||
@@ -1511,7 +1497,15 @@ bool System::check_upgrade() {
|
||||
});
|
||||
} else if (this_version < settings_version) {
|
||||
// downgrading
|
||||
LOG_NOTICE("Downgrading to version %d.%d.%d%s", this_version.major(), this_version.minor(), this_version.patch(), this_version_type.c_str());
|
||||
LOG_NOTICE("Downgrading from version %d.%d.%d%s to version %d.%d.%d%s",
|
||||
settings_version.major(),
|
||||
settings_version.minor(),
|
||||
settings_version.patch(),
|
||||
settings_version_type.c_str(),
|
||||
this_version.major(),
|
||||
this_version.minor(),
|
||||
this_version.patch(),
|
||||
this_version_type.c_str());
|
||||
} else {
|
||||
save_version = false; // same version, do nothing
|
||||
}
|
||||
@@ -1749,9 +1743,10 @@ void System::get_value_json(JsonObject output, const std::string & circuit, cons
|
||||
if (circuit.length()) {
|
||||
output["circuit"] = circuit;
|
||||
}
|
||||
output["readable"] = true;
|
||||
output["writeable"] = (name == "showerTimer" || name == "showerAlert" || name == "enabled" || name == "hideLed" || name == "analogEnabled");
|
||||
output["visible"] = true;
|
||||
output["readable"] = true;
|
||||
output["writeable"] =
|
||||
(name == "txpause" || name == "showerTimer" || name == "showerAlert" || name == "enabled" || name == "hideLed" || name == "analogEnabled");
|
||||
output["visible"] = true;
|
||||
if (val.is<bool>()) {
|
||||
output["value"] = val.as<bool>();
|
||||
output["type"] = "boolean";
|
||||
@@ -1842,7 +1837,6 @@ std::string System::get_metrics_prometheus() {
|
||||
|
||||
for (JsonPair p : obj) {
|
||||
std::string key = p.key().c_str();
|
||||
std::string path = prefix.empty() ? key : prefix + "." + key;
|
||||
std::string metric_name = prefix.empty() ? key : prefix + "_" + key;
|
||||
|
||||
if (should_ignore(prefix, key)) {
|
||||
@@ -1981,6 +1975,7 @@ std::string System::get_metrics_prometheus() {
|
||||
}
|
||||
|
||||
result += info_metric;
|
||||
// TODO fix, as local_info_labels is always empty here
|
||||
if (!local_info_labels.empty()) {
|
||||
result += "{";
|
||||
bool first = true;
|
||||
@@ -2042,8 +2037,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
#else
|
||||
node["version"] = EMSESP_APP_VERSION;
|
||||
#endif
|
||||
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
node["uptimeSec"] = uuid::get_uptime_sec();
|
||||
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
node["uptimeSec"] = uuid::get_uptime_sec();
|
||||
node["resetReason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
node["platform"] = EMSESP_PLATFORM;
|
||||
node["cpuType"] = ESP.getChipModel();
|
||||
@@ -2056,10 +2052,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["freeApp"] = EMSESP::system_.appFree(); // kilobytes
|
||||
node["partition"] = (const char *)esp_ota_get_running_partition()->label; // active partition
|
||||
node["flash_chip_size"] = ESP.getFlashChipSize() / 1024; // kilobytes
|
||||
#endif
|
||||
node["resetReason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
node["psram"] = (EMSESP::system_.PSram() > 0); // make boolean
|
||||
node["psram"] = (EMSESP::system_.PSram() > 0); // make boolean
|
||||
if (EMSESP::system_.PSram()) {
|
||||
node["psramSize"] = EMSESP::system_.PSram();
|
||||
node["freePsram"] = ESP.getFreePsram() / 1024;
|
||||
@@ -2068,8 +2061,27 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||
node["temperature"] = EMSESP::system_.temperature();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
node["txpause"] = EMSbus::tx_mode() == EMS_TXMODE_OFF;
|
||||
|
||||
// GPIO information
|
||||
std::string gpios_in_use_str;
|
||||
for (const auto & usage : EMSESP::system_.used_gpios_) {
|
||||
if (!gpios_in_use_str.empty()) {
|
||||
gpios_in_use_str += ", ";
|
||||
}
|
||||
gpios_in_use_str += Helpers::itoa(usage.pin);
|
||||
}
|
||||
node["gpios_in_use"] = gpios_in_use_str;
|
||||
|
||||
std::string gpios_available_str;
|
||||
for (const auto & gpio : EMSESP::system_.available_gpios()) {
|
||||
if (!gpios_available_str.empty()) {
|
||||
gpios_available_str += ", ";
|
||||
}
|
||||
gpios_available_str += Helpers::itoa(gpio);
|
||||
}
|
||||
node["gpios_available"] = gpios_available_str;
|
||||
|
||||
// Network Status
|
||||
node = output["network"].to<JsonObject>();
|
||||
@@ -2387,35 +2399,62 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
|
||||
if (board_profile == "default") {
|
||||
return false; // unknown, return false
|
||||
} else if (board_profile == "S32") {
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // BBQKees Gateway S32
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // BBQKees Gateway S32
|
||||
valid_system_gpios_ = {0, 2, 5, 18, 23};
|
||||
} else if (board_profile == "E32") {
|
||||
data = {2, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720, 16, 1, 0, 0}; // BBQKees Gateway E32
|
||||
data = {2, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720, 16, 1, 0, 0}; // BBQKees Gateway E32
|
||||
valid_system_gpios_ = {0, 2, 4, 5, 16, 17, 33};
|
||||
} else if (board_profile == "E32V2") {
|
||||
data = {2, 14, 4, 5, 34, PHY_type::PHY_TYPE_LAN8720, 15, 0, 1, 0}; // BBQKees Gateway E32 V2
|
||||
data = {2, 14, 4, 5, 34, PHY_type::PHY_TYPE_LAN8720, 15, 0, 1, 0}; // BBQKees Gateway E32 V2
|
||||
valid_system_gpios_ = {0, 2, 4, 5, 14, 15, 34};
|
||||
} else if (board_profile == "E32V2_2") {
|
||||
data = {32, 14, 4, 5, 34, PHY_type::PHY_TYPE_LAN8720, 15, 0, 1, 1}; // BBQKees Gateway E32 V2.2, rgb led
|
||||
data = {32, 14, 4, 5, 34, PHY_type::PHY_TYPE_LAN8720, 15, 0, 1, 1}; // BBQKees Gateway E32 V2.2, rgb led
|
||||
valid_system_gpios_ = {0, 2, 4, 5, 14, 15, 32, 34, 36, 39}; // system analogs 36, 39, led 2
|
||||
} else if (board_profile == "MH-ET") {
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // MH-ET Live D1 Mini
|
||||
// allow only pins that are marked as `can always be used`
|
||||
valid_system_gpios_ = {0, 2, 5, 18, 23, 12, 13, 14, 15, 16, 17, 26, 27, 33};
|
||||
// can always be used: 12, 13 ,14, 15, 16, 17, 26, 27, 33
|
||||
// can be used if no other function 2, 4, 5, 9, 10, 18, 19, 21, 22, 23, 25, 34, 35, 36, 39
|
||||
} else if (board_profile == "NODEMCU") {
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // NodeMCU 32S
|
||||
// https://blog.berrybase.de/esp32-node-mcu-module-anfaenger-guide/
|
||||
// all available pins, exclude uart0
|
||||
valid_system_gpios_ = {0, 2, 5, 18, 23, 4, 12, 13, 14, 15, 16, 17, 21, 22, 25, 26, 27, 32, 33, 34, 35, 36, 39};
|
||||
} else if (board_profile == "LOLIN") {
|
||||
data = {2, 18, 17, 16, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // Lolin D32
|
||||
// https://www.wemos.cc/en/latest/d32/d32.html
|
||||
valid_system_gpios_ = {2, 18, 17, 16, 0, 4, 5, 12, 13, 14, 15, 21, 22, 25, 26, 27, 32, 33, 34, 35, 36, 39};
|
||||
} else if (board_profile == "OLIMEX") {
|
||||
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720, -1, 0, 0, 0}; // Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Temperature sensor)
|
||||
// https://github.com/OLIMEX/ESP32-EVB/blob/master/HARDWARE/REV-K1/ESP32-EVB_Rev_K1.pdf
|
||||
// uart0 = 1, 3; CAN = 5, 35; relais = 32, 33; ir = 12(tx), 39(rx); SD-card = 2, 14, 15, button = 34
|
||||
// relais and ir can be configured as analog sensor
|
||||
valid_system_gpios_ = {4, 34, 36, 12, 13, 21, 22, 25, 26, 27, 32, 33, 39};
|
||||
} else if (board_profile == "OLIMEXPOE") {
|
||||
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720, 12, 0, 3, 0}; // Olimex ESP32-POE
|
||||
// https://github.com/OLIMEX/ESP32-POE/blob/master/HARDWARE/ESP32-PoE-hardware-revision-L1/ESP32-PoE_Rev_L1.pdf
|
||||
// uart0 = 1, 3; SD-card = 2, 14, 15; button = 34;
|
||||
valid_system_gpios_ = {4, 34, 36, 12, 13, 21, 22, 25, 26, 27, 32, 33, 39};
|
||||
} else if (board_profile == "C3MINI") {
|
||||
#if defined(BOARD_C3_MINI_V1)
|
||||
data = {7, 1, 4, 5, 9, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // Lolin C3 Mini V1
|
||||
#else
|
||||
data = {7, 1, 4, 5, 9, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 1}; // Lolin C3 Mini with RGB Led
|
||||
#endif
|
||||
// https://www.wemos.cc/en/latest/c3/c3_mini.html
|
||||
valid_system_gpios_ = {0, 1, 3, 4, 5, 6, 7, 9, 10, 20, 21};
|
||||
} else if (board_profile == "S2MINI") {
|
||||
data = {15, 7, 11, 12, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // Lolin S2 Mini
|
||||
// https://www.wemos.cc/en/latest/s2/s2_mini.html
|
||||
set_valid_system_gpios();
|
||||
} else if (board_profile == "S3MINI") {
|
||||
data = {17, 18, 8, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // Liligo S3
|
||||
// https://lilygo.cc/products/t7-s3
|
||||
set_valid_system_gpios();
|
||||
} else if (board_profile == "S32S3") {
|
||||
data = {2, 18, 5, 17, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // BBQKees Gateway S3
|
||||
data = {2, 18, 5, 17, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // BBQKees Gateway S3
|
||||
valid_system_gpios_ = {0, 2, 5, 17, 18};
|
||||
} else {
|
||||
return false; // unknown, return false
|
||||
}
|
||||
@@ -2458,15 +2497,15 @@ bool System::command_txpause(const char * value, const int8_t id) {
|
||||
|
||||
// format command - factory reset, removing all config files
|
||||
bool System::command_format(const char * value, const int8_t id) {
|
||||
#if !defined(EMSESP_STANDALONE) && !defined(EMSESP_DEBUG)
|
||||
// don't really format the filesystem in debug or standalone mode
|
||||
#if !defined(EMSESP_STANDALONE) && !defined(EMSESP_TEST)
|
||||
// don't really format the filesystem in test or standalone mode
|
||||
if (LittleFS.format()) {
|
||||
LOG_INFO("Filesystem formatted successfully. All config files removed.");
|
||||
} else {
|
||||
LOG_ERROR("Format failed");
|
||||
}
|
||||
#else
|
||||
LOG_INFO("Format command not available in standalone or debug mode");
|
||||
LOG_ERROR("Format command not available in standalone or test mode");
|
||||
#endif
|
||||
|
||||
// restart will be handled by the main loop
|
||||
@@ -2540,11 +2579,7 @@ void System::ntp_connected(bool b) {
|
||||
if (b != ntp_connected_) {
|
||||
if (b) {
|
||||
LOG_INFO("NTP connected");
|
||||
#ifdef EMSESP_DEBUG // reset the install date on startup if in debug mode
|
||||
set_partition_install_date(true);
|
||||
#else
|
||||
set_partition_install_date(false);
|
||||
#endif
|
||||
set_partition_install_date();
|
||||
} else {
|
||||
LOG_WARNING("NTP disconnected"); // if turned off report it
|
||||
}
|
||||
@@ -2567,21 +2602,6 @@ bool System::ntp_connected() {
|
||||
// see if its a BBQKees Gateway by checking the nvs values
|
||||
String System::getBBQKeesGatewayDetails(uint8_t detail) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
/*
|
||||
if (!EMSESP::nvs_.isKey("mfg")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// mfg can be either "BBQKees" or "BBQKees Electronics"
|
||||
auto mfg = EMSESP::nvs_.getString("mfg");
|
||||
if (mfg) {
|
||||
if (!mfg.startsWith("BBQKees")) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return "BBQKees Gateway Model " + EMSESP::nvs_.getString("model") + " v" + EMSESP::nvs_.getString("hwrevision") + "/" + EMSESP::nvs_.getString("batch");
|
||||
*/
|
||||
union {
|
||||
struct {
|
||||
uint32_t no : 4;
|
||||
@@ -2594,14 +2614,17 @@ String System::getBBQKeesGatewayDetails(uint8_t detail) {
|
||||
};
|
||||
uint32_t reg;
|
||||
} gw;
|
||||
|
||||
for (uint8_t reg = 0; reg < 8; reg++) {
|
||||
gw.reg = esp_efuse_read_reg(EFUSE_BLK3, reg);
|
||||
if (reg == 7 || esp_efuse_read_reg(EFUSE_BLK3, reg + 1) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
const char * mfg[] = {"unknown", "BBQKees Electronics", "", "", "", "", "", ""};
|
||||
const char * model[] = {"unknown", "S3", "E32V2", "E32V2.2", "S32", "E32", "", "", ""};
|
||||
const char * board[] = {"CUSTOM", "S32S3", "E32V2", "E32V2_2", "S32", "E32", "", "", ""};
|
||||
|
||||
switch (detail) {
|
||||
case FUSE_VALUE::MFG:
|
||||
return gw.mfg < 2 ? String(mfg[gw.mfg]) : "unknown";
|
||||
@@ -2619,9 +2642,11 @@ String System::getBBQKeesGatewayDetails(uint8_t detail) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!gw.reg || gw.mfg > 1 || gw.model > 5) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return String(mfg[gw.mfg]) + " " + String(model[gw.model]) + " rev." + String(gw.rev_major) + "." + String(gw.rev_minor) + "/" + String(2000 + gw.year)
|
||||
+ (gw.month < 10 ? "0" : "") + String(gw.month) + String(gw.no);
|
||||
#else
|
||||
@@ -2698,6 +2723,9 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
// we're about to start the upload, set the status so the Web System Monitor spots it
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING);
|
||||
|
||||
// set a callback so we can monitor progress in the WebUI
|
||||
Update.onProgress([](size_t progress, size_t total) { EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING + (progress * 100 / total)); });
|
||||
|
||||
// get tcp stream and send it to Updater
|
||||
WiFiClient * stream = http.getStreamPtr();
|
||||
if (Update.writeStream(*stream) != firmware_size) {
|
||||
@@ -2786,10 +2814,15 @@ bool System::command_read(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
// set the system status code - SYSTEM_STATUS in system.h
|
||||
// this is also used in the SystemMonitor.tsx WebUI to show the progress of the firmware upload, start at 100
|
||||
void System::systemStatus(uint8_t status_code) {
|
||||
if (systemStatus_ != status_code) {
|
||||
systemStatus_ = status_code;
|
||||
LOG_DEBUG("Setting System status code %d", status_code);
|
||||
#ifdef EMSESP_DEBUG
|
||||
if (status_code < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||
LOG_DEBUG("Setting System status code %d", status_code);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2797,8 +2830,11 @@ uint8_t System::systemStatus() {
|
||||
return systemStatus_;
|
||||
}
|
||||
|
||||
// takes a string range like "6-11, 1, 23, 24-48" which has optional ranges and single values and converts to a vector of ints
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::string_range_to_vector(const std::string & range) {
|
||||
// takes two arguments:
|
||||
// the first is the full range of pins to consider
|
||||
// the second is a string range of GPIOs to exclude, like "6-11, 1, 23, 24-48"
|
||||
// returns a vector array of GPIOs that are valid for use
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::string_range_to_vector(const std::string & range, const std::string & exclude) {
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> gpios;
|
||||
std::string::size_type pos = 0;
|
||||
std::string::size_type prev = 0;
|
||||
@@ -2830,41 +2866,138 @@ std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::string_range_to_vector(con
|
||||
// handle the last part
|
||||
process_part(range.substr(prev));
|
||||
|
||||
// if exclude list is provided, parse it and remove excluded GPIOs
|
||||
if (!exclude.empty()) {
|
||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> exclude_gpios;
|
||||
pos = 0;
|
||||
prev = 0;
|
||||
|
||||
auto process_exclude = [&exclude_gpios](std::string part) {
|
||||
// trim whitespace
|
||||
part.erase(0, part.find_first_not_of(" \t"));
|
||||
part.erase(part.find_last_not_of(" \t") + 1);
|
||||
|
||||
// check if it's a range (contains '-')
|
||||
std::string::size_type dash_pos = part.find('-');
|
||||
if (dash_pos != std::string::npos) {
|
||||
// it's a range like "6-11"
|
||||
int start = std::stoi(part.substr(0, dash_pos));
|
||||
int end = std::stoi(part.substr(dash_pos + 1));
|
||||
for (int i = start; i <= end; i++) {
|
||||
exclude_gpios.push_back(static_cast<uint8_t>(i));
|
||||
}
|
||||
} else {
|
||||
exclude_gpios.push_back(static_cast<uint8_t>(std::stoi(part)));
|
||||
}
|
||||
};
|
||||
|
||||
while ((pos = exclude.find(',', prev)) != std::string::npos) {
|
||||
process_exclude(exclude.substr(prev, pos - prev));
|
||||
prev = pos + 1;
|
||||
}
|
||||
|
||||
// handle the last part
|
||||
process_exclude(exclude.substr(prev));
|
||||
|
||||
// remove excluded GPIOs from the main list
|
||||
gpios.erase(std::remove_if(gpios.begin(),
|
||||
gpios.end(),
|
||||
[&exclude_gpios](uint8_t gpio) { return std::find(exclude_gpios.begin(), exclude_gpios.end(), gpio) != exclude_gpios.end(); }),
|
||||
gpios.end());
|
||||
}
|
||||
|
||||
return gpios;
|
||||
}
|
||||
|
||||
// initialize a list of valid GPIOs based on the ESP32 board
|
||||
// note: we always allow 0, which is used to indicate Dallas or LED is disabled
|
||||
// string_to_vector() take two strings, the first is the range of GPIOs to use, the second is a list of GPIOs to exclude
|
||||
// notes:
|
||||
// we always allow 0 (which is usually a strapping pin), because it's used to indicate whether EMS-ESP Dallas or the LED is disabled
|
||||
// we allow UART0, 1 and 2 as they are configurable
|
||||
// strapping pins are disabled as they can affect boot behaviour
|
||||
// we accept GPIOs that are fixed on BBQKees boards
|
||||
//
|
||||
void System::set_valid_system_gpios() {
|
||||
valid_system_gpios_.clear(); // reset system list
|
||||
used_gpios_.clear(); // reset used list
|
||||
|
||||
// get free gpios based on board/platform type
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
// https://www.wemos.cc/en/latest/c3/c3_mini.html
|
||||
valid_system_gpios_ = string_range_to_vector("0-10"); // UART0=20,21
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32c3/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO2, GPIO8 - GPIO9 = strapping pins
|
||||
// GPIO12 - GPIO17 = used for SPI flash and PSRAM
|
||||
// GPIO18 - GPIO19 = USB-JTAG
|
||||
//
|
||||
// notes on what is allowed:
|
||||
// GPIO09 = button on BOARD_C3_MINI_V1
|
||||
// GPIO20 - GPIO21 = UART0, , no chip connected because native USB
|
||||
valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19");
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
// 43 and 44 are UART0 pins
|
||||
// 38 and 39 are strapping pins, input only
|
||||
valid_system_gpios_ = string_range_to_vector("0-14, 19, 20, 21, 33-37, 45, 46");
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO26 - GPIO32 = SPI flash and PSRAM
|
||||
// GPIO45 - GPIO46 = strapping pins
|
||||
// GPIO39 - GPIO42 = USB-JTAG
|
||||
// GPIO22 - GPIO25 = don't exist
|
||||
// GPIO19 - GPIO20 = USB
|
||||
//
|
||||
// notes on what is allowed:
|
||||
// GPIO43, GPIO44 = UART0, no chip connected because native USB
|
||||
valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25");
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
// 43 and 44 are UART0 pins
|
||||
// 33-37 for Octal SPI (SPIIO4 through SPIIO7 and SPIDQS)
|
||||
// 38 and 39 are input only
|
||||
// 45 and 36 are strapping pins, input only
|
||||
valid_system_gpios_ = string_range_to_vector("0-14, 17, 18, 21, 33-39, 45, 46");
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO3, GPIO45 - GPIO46 = strapping pins
|
||||
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
|
||||
// GPIO33 - GPIO37 = Octal flash/PSRAM
|
||||
// GPIO19 - GPIO20 = USB-JTAG
|
||||
// GPIO22 - GPIO25 = don't exist
|
||||
//
|
||||
// notes on what is allowed:
|
||||
// GPIO11 - GPIO19 = ADC analog input only pins
|
||||
// GPIO47 - GPIO48 = valid on a Wemos S3
|
||||
// GPIO8 = used by Liligo S3 board profile for Rx
|
||||
valid_system_gpios_ = string_range_to_vector("0-48", "3, 45-46, 26-32, 33-37, 19-20, 22-25");
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32
|
||||
// 1 and 3 are UART0 pins, but used for some eth-boards (BBQKees-E32, OlimexPOE)
|
||||
// 32-39 is ADC1, input only
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11)
|
||||
// GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist
|
||||
// GPIO01, GPIO03 = UART0, normal connected to UART/USB chip
|
||||
//
|
||||
// notes on known boards:
|
||||
// boards have valid gpios depending on pinout and internal used gpios, see: `load_board_profile()`
|
||||
//
|
||||
// notes on BBQKees boards:
|
||||
// *** We block all free GPIOS in load_board_profile() ***
|
||||
// GPIO2, GPIO4, GPIO5, GPIO14 = used on BBQKees boards for either LED, Dallas or Rx
|
||||
// GPIO12, GPIO13, GPIO35 = BBQKees E32V2_2 internal system pins
|
||||
// GPIO33 = BBQKees E32V_2 unused internal NTC system sensor
|
||||
// GPIO36 = used on BBQKees boards for supply_voltage (E32V2.2)
|
||||
// GPIO39 = used on BBQKees boards for core_voltage (E32V2.2)
|
||||
//
|
||||
// notes on what is allowed with special functions:
|
||||
// GPIO01, GPIO03 = UART0, not always connected to uart/usb chip
|
||||
// GPIO12 - GPIO15 = JTAG, normally not used
|
||||
// GPIO00, GPIO05, GPIO12, GPIO15 = strapping pins, can be used with care
|
||||
// GPIO34, GPIO35, GPIO37 = input only
|
||||
// GPIO23 and GPIO18 are used by Ethernet, excluded later by eth config
|
||||
// GPIO00, GPIO02, GPIO04, GPIO12 - GPIO15, GPIO25 - GPIO27 = ADC2 (10 ch), used by WiFI-driver
|
||||
// GPIO32 - GPIO39 = ADC1 (8 ch), can always be used
|
||||
if (ESP.getPsramSize() > 0) {
|
||||
// if psram is enabled remove pins 16 and 17 from the list
|
||||
valid_system_gpios_ = string_range_to_vector("0-5, 12-15, 18-19, 23, 25-27, 32-39");
|
||||
// remove SPI0/1 PSRAM pins GPIO16 (CS) and GPIO17 (CLK) from the list
|
||||
valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 16, 17, 20, 24, 28-31");
|
||||
} else {
|
||||
valid_system_gpios_ = string_range_to_vector("0-5, 12-19, 23, 25-27, 32-39");
|
||||
valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 20, 24, 28-31");
|
||||
}
|
||||
#elif defined(EMSESP_STANDALONE)
|
||||
valid_system_gpios_ = string_range_to_vector("0-5, 12-19, 23, 25-27, 32-39");
|
||||
valid_system_gpios_ = string_range_to_vector("0-39");
|
||||
#endif
|
||||
valid_system_gpios_.shrink_to_fit();
|
||||
}
|
||||
|
||||
// check if a pin is valid ESP32 pin and if not already used, add to the used gpio list
|
||||
@@ -2873,8 +3006,9 @@ bool System::add_gpio(uint8_t pin, const char * source_name) {
|
||||
// check if this is a valid user GPIO
|
||||
if (std::find(valid_system_gpios_.begin(), valid_system_gpios_.end(), pin) != valid_system_gpios_.end()) {
|
||||
// It's valid now check if it's already in the used list
|
||||
if (std::find(used_gpios_.begin(), used_gpios_.end(), pin) != used_gpios_.end()) {
|
||||
LOG_WARNING("GPIO %d for %s is already in use", pin, source_name);
|
||||
auto it = std::find_if(used_gpios_.begin(), used_gpios_.end(), [pin](const GpioUsage & usage) { return usage.pin == pin; });
|
||||
if (it != used_gpios_.end()) {
|
||||
LOG_WARNING("GPIO %d for %s is already in use by %s", pin, source_name, it->source.c_str());
|
||||
return false; // Pin is already used
|
||||
}
|
||||
} else {
|
||||
@@ -2887,24 +3021,24 @@ bool System::add_gpio(uint8_t pin, const char * source_name) {
|
||||
remove_gpio(pin);
|
||||
|
||||
LOG_DEBUG("Adding GPIO %d for %s to used gpio list", pin, source_name);
|
||||
used_gpios_.push_back(pin); // add to used list
|
||||
used_gpios_.push_back({pin, source_name}); // add to used list
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// remove a gpio from both valid and used lists
|
||||
void System::remove_gpio(uint8_t pin, bool also_system) {
|
||||
auto it = std::find(used_gpios_.begin(), used_gpios_.end(), pin);
|
||||
auto it = std::find_if(used_gpios_.begin(), used_gpios_.end(), [pin](const GpioUsage & usage) { return usage.pin == pin; });
|
||||
if (it != used_gpios_.end()) {
|
||||
LOG_DEBUG("GPIO %d removed from used gpio list", pin);
|
||||
used_gpios_.erase(it);
|
||||
}
|
||||
|
||||
if (also_system) {
|
||||
it = std::find(valid_system_gpios_.begin(), valid_system_gpios_.end(), pin);
|
||||
if (it != valid_system_gpios_.end()) {
|
||||
auto it_sys = std::find(valid_system_gpios_.begin(), valid_system_gpios_.end(), pin);
|
||||
if (it_sys != valid_system_gpios_.end()) {
|
||||
LOG_DEBUG("GPIO %d removed from valid gpio list", pin);
|
||||
valid_system_gpios_.erase(it);
|
||||
valid_system_gpios_.erase(it_sys);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2913,7 +3047,7 @@ void System::remove_gpio(uint8_t pin, bool also_system) {
|
||||
std::vector<uint8_t> System::available_gpios() {
|
||||
std::vector<uint8_t> gpios;
|
||||
for (const auto & gpio : valid_system_gpios_) {
|
||||
if (std::find(used_gpios_.begin(), used_gpios_.end(), gpio) == used_gpios_.end()) {
|
||||
if (std::find_if(used_gpios_.begin(), used_gpios_.end(), [gpio](const GpioUsage & usage) { return usage.pin == gpio; }) == used_gpios_.end()) {
|
||||
gpios.push_back(gpio); // didn't find it in used_gpios_, so it's available
|
||||
}
|
||||
}
|
||||
@@ -2921,37 +3055,26 @@ std::vector<uint8_t> System::available_gpios() {
|
||||
}
|
||||
|
||||
// make a snapshot of the current GPIOs
|
||||
void System::make_snapshot_gpios() {
|
||||
snapshot_used_gpios_.clear();
|
||||
for (const auto & gpio : used_gpios_) {
|
||||
snapshot_used_gpios_.push_back(gpio);
|
||||
void System::make_snapshot_gpios(std::vector<int8_t> & u_gpios, std::vector<int8_t> & s_gpios) {
|
||||
for (const auto & usage : used_gpios_) {
|
||||
u_gpios.push_back(usage.pin);
|
||||
}
|
||||
|
||||
snapshot_valid_system_gpios_.clear();
|
||||
for (const auto & gpio : valid_system_gpios_) {
|
||||
snapshot_valid_system_gpios_.push_back(gpio);
|
||||
s_gpios.push_back(gpio);
|
||||
}
|
||||
}
|
||||
|
||||
// restore the GPIOs from the snapshot
|
||||
void System::restore_snapshot_gpios() {
|
||||
void System::restore_snapshot_gpios(std::vector<int8_t> & u_gpios, std::vector<int8_t> & s_gpios) {
|
||||
used_gpios_.clear();
|
||||
for (const auto & gpio : snapshot_used_gpios_) {
|
||||
used_gpios_.push_back(gpio);
|
||||
for (const auto & gpio : u_gpios) {
|
||||
used_gpios_.push_back({static_cast<uint8_t>(gpio), "restored"});
|
||||
}
|
||||
|
||||
valid_system_gpios_.clear();
|
||||
for (const auto & gpio : snapshot_valid_system_gpios_) {
|
||||
for (const auto & gpio : s_gpios) {
|
||||
valid_system_gpios_.push_back(gpio);
|
||||
}
|
||||
|
||||
// clear the snapshot
|
||||
clear_snapshot_gpios();
|
||||
}
|
||||
|
||||
void System::clear_snapshot_gpios() {
|
||||
snapshot_used_gpios_.clear();
|
||||
snapshot_valid_system_gpios_.clear();
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -159,13 +159,11 @@ class System {
|
||||
static void extractSettings(const char * filename, const char * section, JsonObject output);
|
||||
static bool saveSettings(const char * filename, const char * section, JsonObject input);
|
||||
|
||||
// GPIOs
|
||||
static bool add_gpio(uint8_t pin, const char * source_name);
|
||||
static std::vector<uint8_t> available_gpios();
|
||||
static bool load_board_profile(std::vector<int8_t> & data, const std::string & board_profile);
|
||||
static void make_snapshot_gpios();
|
||||
static void restore_snapshot_gpios();
|
||||
static void clear_snapshot_gpios();
|
||||
static void make_snapshot_gpios(std::vector<int8_t> & u_gpios, std::vector<int8_t> & s_gpios);
|
||||
static void restore_snapshot_gpios(std::vector<int8_t> & u_gpios, std::vector<int8_t> & s_gpios);
|
||||
|
||||
static bool readCommand(const char * data);
|
||||
|
||||
@@ -392,7 +390,7 @@ class System {
|
||||
|
||||
uint8_t systemStatus_; // uses SYSTEM_STATUS enum
|
||||
|
||||
void set_partition_install_date(bool override = false);
|
||||
void set_partition_install_date();
|
||||
|
||||
// button
|
||||
static PButton myPButton_; // PButton instance
|
||||
@@ -435,13 +433,15 @@ class System {
|
||||
void led_monitor();
|
||||
void system_check();
|
||||
|
||||
static std::vector<uint8_t, AllocatorPSRAM<uint8_t>> string_range_to_vector(const std::string & range);
|
||||
static std::vector<uint8_t, AllocatorPSRAM<uint8_t>> string_range_to_vector(const std::string & range, const std::string & exclude = "");
|
||||
|
||||
// GPIOs
|
||||
static std::vector<uint8_t, AllocatorPSRAM<uint8_t>> valid_system_gpios_; // list of valid GPIOs for the ESP32 board that can be used
|
||||
static std::vector<uint8_t, AllocatorPSRAM<uint8_t>> used_gpios_; // list of GPIOs used by the application
|
||||
static std::vector<uint8_t, AllocatorPSRAM<uint8_t>> snapshot_used_gpios_; // snapshot of the used GPIOs
|
||||
static std::vector<uint8_t, AllocatorPSRAM<uint8_t>> snapshot_valid_system_gpios_; // snapshot of the valid GPIOs
|
||||
struct GpioUsage {
|
||||
uint8_t pin;
|
||||
std::string source;
|
||||
};
|
||||
static std::vector<uint8_t, AllocatorPSRAM<uint8_t>> valid_system_gpios_; // list of valid GPIOs for the ESP32 board that can be used
|
||||
static std::vector<GpioUsage, AllocatorPSRAM<GpioUsage>> used_gpios_; // list of GPIOs used by the application
|
||||
|
||||
int8_t wifi_quality(int8_t dBm);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -221,14 +221,14 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
LOG_TRACE("Rx: %s", Helpers::data_to_hex(data, length).c_str());
|
||||
}
|
||||
|
||||
LOG_DEBUG("New Rx telegram, message length %d", message_length);
|
||||
|
||||
// if we don't have a type_id exit,
|
||||
// do not exit on empty message, it is checked for toggle fetch
|
||||
// if we don't have a type_id exit
|
||||
// do not exit on empty message, it is checked later for toggle fetch
|
||||
if (type_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("New Rx telegram, message length %d", message_length);
|
||||
|
||||
// create the telegram
|
||||
auto telegram = std::make_shared<Telegram>(operation, src, dest, type_id, offset, message_data, message_length);
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
@@ -33,8 +33,7 @@ uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::F
|
||||
|
||||
// start the 1-wire
|
||||
void TemperatureSensor::start(const bool factory_settings) {
|
||||
// set_internal_ = factory_settings && EMSESP::nvs_.getString("boot").equals("E32V2_2") && EMSESP::nvs_.getString("hwrevision").equals("3.0");
|
||||
set_internal_ = factory_settings && analogReadMilliVolts(39) > 700; // core voltage > 2.6V
|
||||
set_internal_ = factory_settings && EMSESP::system_.board_profile() == "E32V2_2";
|
||||
reload();
|
||||
|
||||
if (!dallas_gpio_) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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 - proddy, MichaelDvP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* 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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user