mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-09-13 21:54:07 +00:00
@@ -14,9 +14,13 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
|||||||
|
|
||||||
- signed value for solarInfuence [#3077](https://github.com/emsesp/EMS-ESP32/issues/3077)
|
- signed value for solarInfuence [#3077](https://github.com/emsesp/EMS-ESP32/issues/3077)
|
||||||
- TLS support with 4MB boards without PSRAM
|
- TLS support with 4MB boards without PSRAM
|
||||||
|
- Prevent system crash during WiFi network discovery on ESP32-S3 hardware [#3128](https://github.com/emsesp/EMS-ESP32/pull/3128)
|
||||||
|
- check arithmetric operations on strings [#3127](https://github.com/emsesp/EMS-ESP32/discussions/3127)
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
- various memory optimizations [#3083](https://github.com/emsesp/EMS-ESP32/issues/3083)
|
- various memory optimizations [#3083](https://github.com/emsesp/EMS-ESP32/issues/3083)
|
||||||
- Scheduler name is now mandatory
|
- Scheduler name is now mandatory
|
||||||
- network fallback to AP only after start [#3090](https://github.com/emsesp/EMS-ESP32/issues/3090)
|
- network fallback to AP only after start [#3090](https://github.com/emsesp/EMS-ESP32/issues/3090)
|
||||||
|
- replaced Web async-validator with custom validator and toast with native snackbar to reduce bundle size
|
||||||
|
- Dewtemperature for Easycontrol calculated by ems-esp [3135](https://github.com/emsesp/EMS-ESP32/issues/3135)
|
||||||
|
|||||||
@@ -27,13 +27,13 @@
|
|||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@mui/icons-material": "^9.1.1",
|
"@mui/icons-material": "^9.1.1",
|
||||||
"@mui/material": "^9.1.1",
|
"@mui/material": "^9.1.2",
|
||||||
"@table-library/react-table-library": "4.1.15",
|
"@table-library/react-table-library": "4.1.15",
|
||||||
"alova": "^3.5.1",
|
"alova": "^3.5.1",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"preact": "^10.29.2",
|
"preact": "^10.29.3",
|
||||||
"react": "^19.2.7",
|
"react": "^19.2.7",
|
||||||
"react-dom": "^19.2.7",
|
"react-dom": "^19.2.7",
|
||||||
"react-icons": "^5.6.0",
|
"react-icons": "^5.6.0",
|
||||||
@@ -45,18 +45,18 @@
|
|||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@preact/preset-vite": "^2.10.5",
|
"@preact/preset-vite": "^2.10.5",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^26.0.0",
|
"@types/node": "^26.0.1",
|
||||||
"@types/react": "^19.2.17",
|
"@types/react": "^19.2.17",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"concurrently": "^10.0.3",
|
"concurrently": "^10.0.3",
|
||||||
"eslint": "^10.5.0",
|
"eslint": "^10.6.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"prettier": "^3.8.4",
|
"prettier": "^3.9.0",
|
||||||
"rollup-plugin-visualizer": "^7.0.1",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"terser": "^5.48.0",
|
"terser": "^5.48.0",
|
||||||
"typescript-eslint": "^8.61.1",
|
"typescript-eslint": "^8.62.0",
|
||||||
"vite": "^8.0.16",
|
"vite": "^8.1.0",
|
||||||
"vite-plugin-imagemin": "^0.6.1"
|
"vite-plugin-imagemin": "^0.6.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@11.8.0+sha512.c1f5e7c4cb241c8f174b743851d82f42b802324afc8b0f116b96adb15aa06664948dde36960a3ba1079ba5b4b29dd0140135b94b5b5f5263592249d68e555f26"
|
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+262
-262
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,8 @@ allowBuilds:
|
|||||||
gifsicle: false
|
gifsicle: false
|
||||||
jpegtran-bin: false
|
jpegtran-bin: false
|
||||||
mozjpeg: false
|
mozjpeg: false
|
||||||
optipng-bin: false
|
optipng-bin: true
|
||||||
pngquant-bin: false
|
pngquant-bin: true
|
||||||
minimumReleaseAgeExclude:
|
minimumReleaseAgeExclude:
|
||||||
- '@types/node@25.9.2'
|
- '@types/node@25.9.2'
|
||||||
- '@types/react@19.2.17'
|
- '@types/react@19.2.17'
|
||||||
|
|||||||
@@ -13,11 +13,7 @@ import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
|
|||||||
import type { SvgIconProps } from '@mui/material';
|
import type { SvgIconProps } from '@mui/material';
|
||||||
|
|
||||||
export type OptionType =
|
export type OptionType =
|
||||||
| 'deleted'
|
'deleted' | 'readonly' | 'web_exclude' | 'api_mqtt_exclude' | 'favorite';
|
||||||
| 'readonly'
|
|
||||||
| 'web_exclude'
|
|
||||||
| 'api_mqtt_exclude'
|
|
||||||
| 'favorite';
|
|
||||||
|
|
||||||
type IconPair = [
|
type IconPair = [
|
||||||
React.ComponentType<SvgIconProps>,
|
React.ComponentType<SvgIconProps>,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"formidable": "^3.5.4",
|
"formidable": "^3.5.4",
|
||||||
"itty-router": "^5.0.24",
|
"itty-router": "^5.0.24",
|
||||||
"prettier": "^3.8.4"
|
"prettier": "^3.9.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@11.8.0+sha512.c1f5e7c4cb241c8f174b743851d82f42b802324afc8b0f116b96adb15aa06664948dde36960a3ba1079ba5b4b29dd0140135b94b5b5f5263592249d68e555f26"
|
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+8
-8
@@ -13,7 +13,7 @@ importers:
|
|||||||
version: 3.1.3
|
version: 3.1.3
|
||||||
'@trivago/prettier-plugin-sort-imports':
|
'@trivago/prettier-plugin-sort-imports':
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2(prettier@3.8.4)
|
version: 6.0.2(prettier@3.9.0)
|
||||||
formidable:
|
formidable:
|
||||||
specifier: ^3.5.4
|
specifier: ^3.5.4
|
||||||
version: 3.5.4
|
version: 3.5.4
|
||||||
@@ -21,8 +21,8 @@ importers:
|
|||||||
specifier: ^5.0.24
|
specifier: ^5.0.24
|
||||||
version: 5.0.24
|
version: 5.0.24
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.8.4
|
specifier: ^3.9.0
|
||||||
version: 3.8.4
|
version: 3.9.0
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -167,8 +167,8 @@ packages:
|
|||||||
picocolors@1.1.1:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
prettier@3.8.4:
|
prettier@3.9.0:
|
||||||
resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==}
|
resolution: {integrity: sha512-LjIqSIC5VYLzs9WedVmJ2ljNAGnU+DteIClbahu4L/DBeWjZ6iT/k1lAYyu9JUh+1xINxWadaPw/Pl63y/agAw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.8.0
|
'@noble/hashes': 1.8.0
|
||||||
|
|
||||||
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.4)':
|
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/generator': 7.29.7
|
'@babel/generator': 7.29.7
|
||||||
'@babel/parser': 7.29.7
|
'@babel/parser': 7.29.7
|
||||||
@@ -256,7 +256,7 @@ snapshots:
|
|||||||
lodash-es: 4.18.1
|
lodash-es: 4.18.1
|
||||||
minimatch: 9.0.9
|
minimatch: 9.0.9
|
||||||
parse-imports-exports: 0.2.4
|
parse-imports-exports: 0.2.4
|
||||||
prettier: 3.8.4
|
prettier: 3.9.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -311,6 +311,6 @@ snapshots:
|
|||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
prettier@3.8.4: {}
|
prettier@3.9.0: {}
|
||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|||||||
+47
-20
@@ -146,9 +146,9 @@ let LATEST_DEV_VERSION = '3.9.0-dev.1';
|
|||||||
|
|
||||||
// scenarios for testing versioning
|
// scenarios for testing versioning
|
||||||
// let version_test = 0; // on latest stable, or switch to dev
|
// let version_test = 0; // on latest stable, or switch to dev
|
||||||
// let version_test = 1; // on latest dev, or switch back to stable
|
let version_test = 1; // on latest dev, or switch back to stable
|
||||||
// let version_test = 2; // upgrade an older stable to latest stable or switch to latest dev
|
// let version_test = 2; // upgrade an older stable to latest stable or switch to latest dev
|
||||||
let version_test = 3; // upgrade dev to latest, or switch to stable
|
// let version_test = 3; // upgrade dev to latest, or switch to stable
|
||||||
// let version_test = 4; // downgrade to an older dev, or switch back to stable
|
// let version_test = 4; // downgrade to an older dev, or switch back to stable
|
||||||
|
|
||||||
switch (version_test as number) {
|
switch (version_test as number) {
|
||||||
@@ -318,10 +318,10 @@ function updateMask(entity: any, de: any, dd: any) {
|
|||||||
const old_custom_name = dd.nodes[dd_objIndex].cn;
|
const old_custom_name = dd.nodes[dd_objIndex].cn;
|
||||||
console.log(
|
console.log(
|
||||||
'comparing names, old (' +
|
'comparing names, old (' +
|
||||||
old_custom_name +
|
old_custom_name +
|
||||||
') with new (' +
|
') with new (' +
|
||||||
new_custom_name +
|
new_custom_name +
|
||||||
')'
|
')'
|
||||||
);
|
);
|
||||||
if (old_custom_name !== new_custom_name) {
|
if (old_custom_name !== new_custom_name) {
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -438,9 +438,9 @@ function upgradeImportantMessages(version: string) {
|
|||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'upgradeImportantMessageType: version=' +
|
'upgradeImportantMessageType: version=' +
|
||||||
version +
|
version +
|
||||||
' type=' +
|
' type=' +
|
||||||
upgradeImportantMessageType_n
|
upgradeImportantMessageType_n
|
||||||
);
|
);
|
||||||
return { upgradeImportantMessageType: upgradeImportantMessageType_n };
|
return { upgradeImportantMessageType: upgradeImportantMessageType_n };
|
||||||
}
|
}
|
||||||
@@ -487,17 +487,17 @@ function get_versions() {
|
|||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'getVersions: current=' +
|
'getVersions: current=' +
|
||||||
THIS_VERSION +
|
THIS_VERSION +
|
||||||
' stable=' +
|
' stable=' +
|
||||||
LATEST_STABLE_VERSION +
|
LATEST_STABLE_VERSION +
|
||||||
' (upgradeable=' +
|
' (upgradeable=' +
|
||||||
(STABLE_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') +
|
(STABLE_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') +
|
||||||
') dev=' +
|
') dev=' +
|
||||||
LATEST_DEV_VERSION +
|
LATEST_DEV_VERSION +
|
||||||
' (upgradeable=' +
|
' (upgradeable=' +
|
||||||
(DEV_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') +
|
(DEV_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') +
|
||||||
')' +
|
')' +
|
||||||
(MOCK_OFFLINE ? ' [offline]' : '')
|
(MOCK_OFFLINE ? ' [offline]' : '')
|
||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -4147,6 +4147,21 @@ let emsesp_customentities = {
|
|||||||
value_type: 0,
|
value_type: 0,
|
||||||
writeable: true,
|
writeable: true,
|
||||||
value: 21
|
value: 21
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
ram: 1,
|
||||||
|
device_id: 0,
|
||||||
|
type_id: 0,
|
||||||
|
offset: 0,
|
||||||
|
factor: 1,
|
||||||
|
name: 'message',
|
||||||
|
uom: 0,
|
||||||
|
value_type: 9,
|
||||||
|
writeable: true,
|
||||||
|
hide: false,
|
||||||
|
value:
|
||||||
|
'{"stable":{"version":"3.8.2","date":"2026-05-12"},"dev":{"version":"3.8.3-dev.5","date":"2026-05-28T09:55:29Z"}}'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -4189,6 +4204,18 @@ let emsesp_commands = {
|
|||||||
cmd: 'system/message',
|
cmd: 'system/message',
|
||||||
value: '"hello world"',
|
value: '"hello world"',
|
||||||
name: 'send_message'
|
name: 'send_message'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
cmd: 'custom/message',
|
||||||
|
value: '{"url":"http://emsesp.org/versions.json"}',
|
||||||
|
name: 'get_versions'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
cmd: 'system/sendmail',
|
||||||
|
value: '"test email with version " + custom/message',
|
||||||
|
name: 'sendmail'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "WiFiScanner.h"
|
#include "WiFiScanner.h"
|
||||||
|
|
||||||
#include "../core/psram_async_json_response.h"
|
|
||||||
|
|
||||||
WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
|
WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||||
securityManager->addEndpoint(server, SCAN_NETWORKS_SERVICE_PATH, AuthenticationPredicates::IS_ADMIN, [this](AsyncWebServerRequest * request) {
|
securityManager->addEndpoint(server, SCAN_NETWORKS_SERVICE_PATH, AuthenticationPredicates::IS_ADMIN, [this](AsyncWebServerRequest * request) {
|
||||||
scanNetworks(request);
|
scanNetworks(request);
|
||||||
@@ -17,14 +15,14 @@ void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
|
|||||||
|
|
||||||
if (WiFi.scanComplete() != -1) {
|
if (WiFi.scanComplete() != -1) {
|
||||||
WiFi.scanDelete();
|
WiFi.scanDelete();
|
||||||
WiFi.scanNetworks(true);
|
WiFi.scanNetworks(true, false, true, 250);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
||||||
const int numNetworks = WiFi.scanComplete();
|
const int numNetworks = WiFi.scanComplete();
|
||||||
if (numNetworks > -1) {
|
if (numNetworks > -1) {
|
||||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
auto * response = new AsyncJsonResponse(false);
|
||||||
JsonObject root = response->getRoot();
|
JsonObject root = response->getRoot();
|
||||||
JsonArray networks = root["networks"].to<JsonArray>();
|
JsonArray networks = root["networks"].to<JsonArray>();
|
||||||
for (uint8_t i = 0; i < numNetworks; i++) {
|
for (uint8_t i = 0; i < numNetworks; i++) {
|
||||||
@@ -40,6 +38,10 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
|||||||
} else if (numNetworks == -1) {
|
} else if (numNetworks == -1) {
|
||||||
request->send(202); // special code to indicate scan in progress
|
request->send(202); // special code to indicate scan in progress
|
||||||
} else {
|
} else {
|
||||||
scanNetworks(request);
|
auto * response = new AsyncJsonResponse(false);
|
||||||
|
JsonObject root = response->getRoot();
|
||||||
|
root["networks"].to<JsonArray>();
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,12 +207,12 @@ class AnalogSensor {
|
|||||||
uint32_t sensorreads_ = 0;
|
uint32_t sensorreads_ = 0;
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
static void IRAM_ATTR freqIrq0();
|
static void IRAM_ATTR freqIrq0();
|
||||||
static void IRAM_ATTR freqIrq1();
|
static void IRAM_ATTR freqIrq1();
|
||||||
static void IRAM_ATTR freqIrq2();
|
static void IRAM_ATTR freqIrq2();
|
||||||
static volatile unsigned long edge[3]; // written from freqIrqN() ISRs, read from the main measure() loop (partly outside the critical section)
|
static volatile unsigned long edge[3]; // written from freqIrqN() ISRs, read from the main measure() loop (partly outside the critical section)
|
||||||
static volatile unsigned long edgecnt[3]; // written from freqIrqN() ISRs, read from the main measure() loop (partly outside the critical section)
|
static volatile unsigned long edgecnt[3]; // written from freqIrqN() ISRs, read from the main measure() loop (partly outside the critical section)
|
||||||
unsigned long lastedge[3] = {0, 0, 0};
|
unsigned long lastedge[3] = {0, 0, 0};
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -381,8 +381,8 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
|||||||
// for the Commands device, calling a named command executes it (using its stored value)
|
// for the Commands device, calling a named command executes it (using its stored value)
|
||||||
// rather than returning its definition, so skip the value-info lookup and fall through
|
// rather than returning its definition, so skip the value-info lookup and fall through
|
||||||
// to the registered command function. The list keywords above are still handled.
|
// to the registered command function. The list keywords above are still handled.
|
||||||
bool is_named_command = (device_type == EMSdevice::DeviceType::COMMAND) && strcmp(cmd, F_(info)) && strcmp(cmd, F_(values))
|
bool is_named_command = (device_type == EMSdevice::DeviceType::COMMAND) && strcmp(cmd, F_(info)) && strcmp(cmd, F_(values)) && strcmp(cmd, F_(entities))
|
||||||
&& strcmp(cmd, F_(entities)) && strcmp(cmd, F_(metrics));
|
&& strcmp(cmd, F_(metrics));
|
||||||
if (!is_named_command && EMSESP::get_device_value_info(output, cmd, id, device_type)) { // entity = cmd
|
if (!is_named_command && EMSESP::get_device_value_info(output, cmd, id, device_type)) { // entity = cmd
|
||||||
LOG_DEBUG("Fetched device entity/attributes for %s/%s (id=%d)", dname, cmd, id);
|
LOG_DEBUG("Fetched device entity/attributes for %s/%s (id=%d)", dname, cmd, id);
|
||||||
return CommandRet::OK;
|
return CommandRet::OK;
|
||||||
|
|||||||
+6
-3
@@ -59,7 +59,7 @@ using cmd_function_p = std::function<bool(const char * data, const int8_t id, Js
|
|||||||
class Command {
|
class Command {
|
||||||
public:
|
public:
|
||||||
struct CmdFunction {
|
struct CmdFunction {
|
||||||
uint8_t device_type_; // DeviceType::
|
uint8_t device_type_; // DeviceType::
|
||||||
uint8_t device_id_;
|
uint8_t device_id_;
|
||||||
uint8_t flags_; // mqtt flags for command subscriptions
|
uint8_t flags_; // mqtt flags for command subscriptions
|
||||||
bool has_json_output_; // true if the command writes JSON output; such commands bypass the readonly check
|
bool has_json_output_; // true if the command writes JSON output; such commands bypass the readonly check
|
||||||
@@ -116,8 +116,11 @@ class Command {
|
|||||||
static void
|
static void
|
||||||
add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||||
// command that writes a JSON object as its output; bypasses the readonly check
|
// command that writes a JSON object as its output; bypasses the readonly check
|
||||||
static void
|
static void add_json(const uint8_t device_type,
|
||||||
add_json(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
const char * cmd,
|
||||||
|
const cmd_function_p cb,
|
||||||
|
const char * const * description,
|
||||||
|
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||||
|
|
||||||
static void reserve(size_t num) {
|
static void reserve(size_t num) {
|
||||||
cmdfunctions_.reserve(num);
|
cmdfunctions_.reserve(num);
|
||||||
|
|||||||
@@ -351,10 +351,10 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, uint32_t & dv_set_max) {
|
|||||||
|
|
||||||
// extract custom min from custom_fullname
|
// extract custom min from custom_fullname
|
||||||
bool DeviceValue::get_custom_min(int16_t & val) {
|
bool DeviceValue::get_custom_min(int16_t & val) {
|
||||||
const auto & cf = custom_fullname();
|
const auto & cf = custom_fullname();
|
||||||
auto min_pos = cf.find('>');
|
auto min_pos = cf.find('>');
|
||||||
bool has_min = (min_pos != std::string::npos);
|
bool has_min = (min_pos != std::string::npos);
|
||||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||||
if (has_min) {
|
if (has_min) {
|
||||||
int32_t v = Helpers::atoint(cf.substr(min_pos + 1).c_str());
|
int32_t v = Helpers::atoint(cf.substr(min_pos + 1).c_str());
|
||||||
if (fahrenheit) {
|
if (fahrenheit) {
|
||||||
@@ -370,10 +370,10 @@ bool DeviceValue::get_custom_min(int16_t & val) {
|
|||||||
|
|
||||||
// extract custom max from custom_fullname
|
// extract custom max from custom_fullname
|
||||||
bool DeviceValue::get_custom_max(uint32_t & val) {
|
bool DeviceValue::get_custom_max(uint32_t & val) {
|
||||||
const auto & cf = custom_fullname();
|
const auto & cf = custom_fullname();
|
||||||
auto max_pos = cf.find('<');
|
auto max_pos = cf.find('<');
|
||||||
bool has_max = (max_pos != std::string::npos);
|
bool has_max = (max_pos != std::string::npos);
|
||||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||||
if (has_max) {
|
if (has_max) {
|
||||||
int32_t v = Helpers::atoint(cf.substr(max_pos + 1).c_str());
|
int32_t v = Helpers::atoint(cf.substr(max_pos + 1).c_str());
|
||||||
if (fahrenheit) {
|
if (fahrenheit) {
|
||||||
|
|||||||
+10
-10
@@ -1884,16 +1884,16 @@ void EMSESP::loop() {
|
|||||||
if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD
|
if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD
|
||||||
&& EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
&& EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||||
// loop through the services
|
// loop through the services
|
||||||
webStatusService.loop(); // periodic refresh of cached versions.json
|
webStatusService.loop(); // periodic refresh of cached versions.json
|
||||||
rxservice_.loop(); // process any incoming Rx telegrams
|
rxservice_.loop(); // process any incoming Rx telegrams
|
||||||
shower_.loop(); // check for shower on/off
|
shower_.loop(); // check for shower on/off
|
||||||
temperaturesensor_.loop(); // read sensor temperatures
|
temperaturesensor_.loop(); // read sensor temperatures
|
||||||
analogsensor_.loop(); // read analog sensor values
|
analogsensor_.loop(); // read analog sensor values
|
||||||
publish_all_loop(); // with HA messages in parts to avoid flooding the MQTT queue
|
publish_all_loop(); // with HA messages in parts to avoid flooding the MQTT queue
|
||||||
mqtt_.loop(); // sends out anything in the MQTT queue
|
mqtt_.loop(); // sends out anything in the MQTT queue
|
||||||
webModulesService.loop(); // loop through the external library modules
|
webModulesService.loop(); // loop through the external library modules
|
||||||
webSchedulerService.loop(); // scheduler timing logic; command execution is offloaded to WebCommandService's worker task
|
webSchedulerService.loop(); // scheduler timing logic; command execution is offloaded to WebCommandService's worker task
|
||||||
scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||||
compact_entities_if_stable(); // reclaim over-reserved entity vector capacity once device discovery settles
|
compact_entities_if_stable(); // reclaim over-reserved entity vector capacity once device discovery settles
|
||||||
}
|
}
|
||||||
// check for GPIO Errors - this is called once when booting
|
// check for GPIO Errors - this is called once when booting
|
||||||
|
|||||||
+32
-28
@@ -31,8 +31,8 @@ uint8_t Roomctrl::type_[HCS] = {RemoteType::NONE, RemoteType::NONE, Rem
|
|||||||
uint32_t Roomctrl::timeout_ = 0;
|
uint32_t Roomctrl::timeout_ = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the temperature,
|
* set the temperature,
|
||||||
*/
|
*/
|
||||||
void Roomctrl::set_timeout(uint8_t t) {
|
void Roomctrl::set_timeout(uint8_t t) {
|
||||||
timeout_ = t * 3600000; // ms
|
timeout_ = t * 3600000; // ms
|
||||||
}
|
}
|
||||||
@@ -86,8 +86,8 @@ uint8_t Roomctrl::get_hc(uint8_t addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if remote control is active send the temperature every 15 seconds
|
* if remote control is active send the temperature every 15 seconds
|
||||||
*/
|
*/
|
||||||
void Roomctrl::send(uint8_t addr) {
|
void Roomctrl::send(uint8_t addr) {
|
||||||
if ((addr & 0x80) || EMSESP::system_.readonly_mode()) {
|
if ((addr & 0x80) || EMSESP::system_.readonly_mode()) {
|
||||||
return;
|
return;
|
||||||
@@ -143,8 +143,8 @@ void Roomctrl::send(uint8_t addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check if there is a message for the remote room controller
|
* check if there is a message for the remote room controller
|
||||||
*/
|
*/
|
||||||
void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) {
|
void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) {
|
||||||
if (EMSESP::system_.readonly_mode()) {
|
if (EMSESP::system_.readonly_mode()) {
|
||||||
return;
|
return;
|
||||||
@@ -190,8 +190,8 @@ void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* send version info
|
* send version info
|
||||||
*/
|
*/
|
||||||
void Roomctrl::version(uint8_t addr, uint8_t dst, uint8_t hc) {
|
void Roomctrl::version(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||||
uint8_t data[20];
|
uint8_t data[20];
|
||||||
data[0] = addr | EMSbus::ems_mask();
|
data[0] = addr | EMSbus::ems_mask();
|
||||||
@@ -265,8 +265,8 @@ void Roomctrl::version(uint8_t addr, uint8_t dst, uint8_t hc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* unknown message id, we reply with empty message
|
* unknown message id, we reply with empty message
|
||||||
*/
|
*/
|
||||||
void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset) {
|
void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset) {
|
||||||
uint8_t data[10];
|
uint8_t data[10];
|
||||||
data[0] = addr | EMSbus::ems_mask();
|
data[0] = addr | EMSbus::ems_mask();
|
||||||
@@ -290,8 +290,8 @@ void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typeh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* send the room temperature in message 0xAF
|
* send the room temperature in message 0xAF
|
||||||
*/
|
*/
|
||||||
void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) {
|
void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||||
uint8_t data[14];
|
uint8_t data[14];
|
||||||
data[0] = addr | EMSbus::ems_mask();
|
data[0] = addr | EMSbus::ems_mask();
|
||||||
@@ -377,25 +377,29 @@ void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) {
|
|||||||
|
|
||||||
// send telegram 0x047B only for RC100H
|
// send telegram 0x047B only for RC100H
|
||||||
void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) {
|
void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||||
|
int16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]);
|
||||||
|
int8_t dew8 = EMS_VALUE_INT8_NOTSET;
|
||||||
|
if (dew != EMS_VALUE_INT16_NOTSET) {
|
||||||
|
dew8 = static_cast<int8_t>((dew >= 0 ? dew + 5 : dew - 5) / 10);
|
||||||
|
}
|
||||||
uint8_t data[11];
|
uint8_t data[11];
|
||||||
data[0] = addr | EMSbus::ems_mask();
|
data[0] = addr | EMSbus::ems_mask();
|
||||||
data[1] = dst & 0x7F;
|
data[1] = dst & 0x7F;
|
||||||
uint16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]);
|
data[2] = 0xFF;
|
||||||
data[2] = 0xFF;
|
data[3] = 0;
|
||||||
data[3] = 0;
|
data[4] = 3;
|
||||||
data[4] = 3;
|
data[5] = 0x7B + hc;
|
||||||
data[5] = 0x7B + hc;
|
data[6] = static_cast<uint8_t>(dew8);
|
||||||
data[6] = dew == EMS_VALUE_INT16_NOTSET ? EMS_VALUE_INT8_NOTSET : (uint8_t)((dew + 5) / 10);
|
data[7] = remotehum_[hc];
|
||||||
data[7] = remotehum_[hc];
|
data[8] = static_cast<uint8_t>(static_cast<uint16_t>(dew) >> 8);
|
||||||
data[8] = (uint8_t)(dew << 8);
|
data[9] = static_cast<uint8_t>(static_cast<uint16_t>(dew) & 0xFF);
|
||||||
data[9] = (uint8_t)(dew & 0xFF);
|
data[10] = EMSbus::calculate_crc(data, 10); // append CRC
|
||||||
data[10] = EMSbus::calculate_crc(data, 10); // append CRC
|
|
||||||
EMSuart::transmit(data, 11);
|
EMSuart::transmit(data, 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* send a nack if someone want to write to us.
|
* send a nack if someone want to write to us.
|
||||||
*/
|
*/
|
||||||
void Roomctrl::nack_write() {
|
void Roomctrl::nack_write() {
|
||||||
uint8_t data[1];
|
uint8_t data[1];
|
||||||
data[0] = TxService::TX_WRITE_FAIL;
|
data[0] = TxService::TX_WRITE_FAIL;
|
||||||
@@ -403,8 +407,8 @@ void Roomctrl::nack_write() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* send a ack if someone want to write to us.
|
* send a ack if someone want to write to us.
|
||||||
*/
|
*/
|
||||||
void Roomctrl::ack_write() {
|
void Roomctrl::ack_write() {
|
||||||
uint8_t data[1];
|
uint8_t data[1];
|
||||||
data[0] = TxService::TX_WRITE_SUCCESS;
|
data[0] = TxService::TX_WRITE_SUCCESS;
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ class Roomctrl {
|
|||||||
static bool is_remote(const uint8_t hc) {
|
static bool is_remote(const uint8_t hc) {
|
||||||
return (hc < 4 && remotetemp_[hc] != EMS_VALUE_INT16_NOTSET);
|
return (hc < 4 && remotetemp_[hc] != EMS_VALUE_INT16_NOTSET);
|
||||||
}
|
}
|
||||||
static void set_timeout(uint8_t t);
|
static void set_timeout(uint8_t t);
|
||||||
|
static int16_t calc_dew(int16_t temp, uint8_t hum);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint32_t SEND_INTERVAL = 15000; // 15 sec
|
static constexpr uint32_t SEND_INTERVAL = 15000; // 15 sec
|
||||||
@@ -50,7 +51,6 @@ class Roomctrl {
|
|||||||
static void nack_write();
|
static void nack_write();
|
||||||
static void ack_write();
|
static void ack_write();
|
||||||
static void replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc);
|
static void replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc);
|
||||||
static int16_t calc_dew(int16_t temp, uint8_t hum);
|
|
||||||
|
|
||||||
static bool switch_off_[HCS];
|
static bool switch_off_[HCS];
|
||||||
static uint32_t send_time_[HCS];
|
static uint32_t send_time_[HCS];
|
||||||
|
|||||||
@@ -816,16 +816,16 @@ void Thermostat::process_RemoteTemp(const std::shared_ptr<const Telegram> & tele
|
|||||||
// e.g. "38 10 FF 00 03 7B 08 24 00 4B"
|
// e.g. "38 10 FF 00 03 7B 08 24 00 4B"
|
||||||
void Thermostat::process_RemoteHumidity(const std::shared_ptr<const Telegram> & telegram) {
|
void Thermostat::process_RemoteHumidity(const std::shared_ptr<const Telegram> & telegram) {
|
||||||
// has_update(telegram, dewtemperature_, 0); // this is int8
|
// has_update(telegram, dewtemperature_, 0); // this is int8
|
||||||
has_update(telegram, humidity_, 1);
|
|
||||||
has_update(telegram, dewtemperature_, 2); // this is int16
|
|
||||||
// some thermostats use short telegram with int8 dewpoint, https://github.com/emsesp/EMS-ESP32/issues/1491
|
// some thermostats use short telegram with int8 dewpoint, https://github.com/emsesp/EMS-ESP32/issues/1491
|
||||||
if (telegram->offset == 0 && telegram->message_length < 4) {
|
// but it's not a dewpoint in offset 0, removed, see https://github.com/emsesp/EMS-ESP32/issues/3135
|
||||||
int8_t dew = dewtemperature_ / 10;
|
has_update(telegram, humidity_, 1);
|
||||||
telegram->read_value(dew, 0);
|
// has_update(telegram, dewtemperature_, 2); // this is int16
|
||||||
if (dew != EMS_VALUE_INT8_NOTSET && dewtemperature_ != dew * 10) {
|
int16_t dew = EMS_VALUE_INT16_NOTSET;
|
||||||
dewtemperature_ = dew * 10;
|
if (telegram->read_value(dew, 2)) {
|
||||||
has_update(dewtemperature_);
|
has_update(dewtemperature_, dew);
|
||||||
}
|
} else if (telegram->offset == 0 && telegram->message_length < 4) {
|
||||||
|
has_update(dewtemperature_, Roomctrl::calc_dew(tempsensor1_, humidity_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.9.0-dev.14"
|
#define EMSESP_APP_VERSION "3.9.0-dev.15"
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ class WebCommandService : public StatefulService<WebCommands> {
|
|||||||
bool queueCommand(const char * name, const char * value = nullptr);
|
bool queueCommand(const char * name, const char * value = nullptr);
|
||||||
bool dispatchCommand(const char * name, const char * value = nullptr);
|
bool dispatchCommand(const char * name, const char * value = nullptr);
|
||||||
|
|
||||||
static bool isUrlCommand(const std::string & command); // true if the command definition is a HTTP/URL command
|
static bool isUrlCommand(const std::string & command); // true if the command definition is a HTTP/URL command
|
||||||
static bool valueContainsUrl(const std::string & value); // true if a value embeds a {"url":...} compute() will fetch
|
static bool valueContainsUrl(const std::string & value); // true if a value embeds a {"url":...} compute() will fetch
|
||||||
|
|
||||||
const CommandItem * find(const char * name);
|
const CommandItem * find(const char * name);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user