This commit is contained in:
MichaelDvP
2026-01-08 12:24:46 +01:00
7 changed files with 6438 additions and 8085 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,7 @@
"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,7 +51,7 @@
"@eslint/js": "^9.39.2",
"@preact/compat": "^18.3.1",
"@preact/preset-vite": "^2.10.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.1",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.0.3",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
@@ -63,7 +63,7 @@
"rollup-plugin-visualizer": "^6.0.5",
"terser": "^5.44.1",
"typescript-eslint": "^8.52.0",
"vite": "^7.3.0",
"vite": "^7.3.1",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^6.0.3"
},

View File

@@ -63,8 +63,8 @@ importers:
specifier: ^5.5.0
version: 5.5.0(react@19.2.3)
react-router:
specifier: ^7.11.0
version: 7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
specifier: ^7.12.0
version: 7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react-toastify:
specifier: ^11.0.5
version: 11.0.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -83,10 +83,10 @@ importers:
version: 9.39.2
'@preact/preset-vite':
specifier: ^2.10.2
version: 2.10.2(@babel/core@7.28.5)(preact@10.28.2)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))
version: 2.10.2(@babel/core@7.28.5)(preact@10.28.2)(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1))
'@trivago/prettier-plugin-sort-imports':
specifier: ^6.0.1
version: 6.0.1(prettier@3.7.4)
specifier: ^6.0.2
version: 6.0.2(prettier@3.7.4)
'@types/node':
specifier: ^25.0.3
version: 25.0.3
@@ -121,14 +121,14 @@ importers:
specifier: ^8.52.0
version: 8.52.0(eslint@9.39.2)(typescript@5.9.3)
vite:
specifier: ^7.3.0
version: 7.3.0(@types/node@25.0.3)(terser@5.44.1)
specifier: ^7.3.1
version: 7.3.1(@types/node@25.0.3)(terser@5.44.1)
vite-plugin-imagemin:
specifier: ^0.6.1
version: 0.6.1(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))
version: 0.6.1(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1))
vite-tsconfig-paths:
specifier: ^6.0.3
version: 6.0.3(typescript@5.9.3)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))
version: 6.0.3(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1))
packages:
@@ -815,8 +815,8 @@ packages:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@trivago/prettier-plugin-sort-imports@6.0.1':
resolution: {integrity: sha512-6B13DCWDfAfh4AEJ43gRgeCSAQmlKG5LHqHzHc0lbUwgBy0rX7o41US+46Fd4XiXBx+JDGEz3NBadCbUls0dUQ==}
'@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
@@ -1042,8 +1042,8 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
baseline-browser-mapping@2.9.11:
resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==}
baseline-browser-mapping@2.9.12:
resolution: {integrity: sha512-Mij6Lij93pTAIsSYy5cyBQ975Qh9uLEc5rwGTpomiZeXZL9yIS6uORJakb3ScHgfs0serMMfIbXzokPMuEiRyw==}
hasBin: true
bin-build@3.0.0:
@@ -2540,8 +2540,8 @@ packages:
react-is@19.2.3:
resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==}
react-router@7.11.0:
resolution: {integrity: sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==}
react-router@7.12.0:
resolution: {integrity: sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: '>=18'
@@ -3011,8 +3011,8 @@ packages:
vite:
optional: true
vite@7.3.0:
resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==}
vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -3610,18 +3610,18 @@ snapshots:
dependencies:
preact: 10.28.2
'@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.2)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))':
'@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.2)(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1))':
dependencies:
'@babel/core': 7.28.5
'@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5)
'@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5)
'@prefresh/vite': 2.4.11(preact@10.28.2)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))
'@prefresh/vite': 2.4.11(preact@10.28.2)(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1))
'@rollup/pluginutils': 4.2.1
babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5)
debug: 4.4.3
picocolors: 1.1.1
vite: 7.3.0(@types/node@25.0.3)(terser@5.44.1)
vite-prerender-plugin: 0.5.12(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))
vite: 7.3.1(@types/node@25.0.3)(terser@5.44.1)
vite-prerender-plugin: 0.5.12(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1))
transitivePeerDependencies:
- preact
- supports-color
@@ -3634,7 +3634,7 @@ snapshots:
'@prefresh/utils@1.2.1': {}
'@prefresh/vite@2.4.11(preact@10.28.2)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))':
'@prefresh/vite@2.4.11(preact@10.28.2)(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1))':
dependencies:
'@babel/core': 7.28.5
'@prefresh/babel-plugin': 0.5.2
@@ -3642,7 +3642,7 @@ snapshots:
'@prefresh/utils': 1.2.1
'@rollup/pluginutils': 4.2.1
preact: 10.28.2
vite: 7.3.0(@types/node@25.0.3)(terser@5.44.1)
vite: 7.3.1(@types/node@25.0.3)(terser@5.44.1)
transitivePeerDependencies:
- supports-color
@@ -3737,7 +3737,7 @@ snapshots:
react-virtualized-auto-sizer: 1.0.26(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react-window: 1.8.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@trivago/prettier-plugin-sort-imports@6.0.1(prettier@3.7.4)':
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.7.4)':
dependencies:
'@babel/generator': 7.28.5
'@babel/parser': 7.28.5
@@ -3982,7 +3982,7 @@ snapshots:
base64-js@1.5.1: {}
baseline-browser-mapping@2.9.11: {}
baseline-browser-mapping@2.9.12: {}
bin-build@3.0.0:
dependencies:
@@ -4039,7 +4039,7 @@ snapshots:
browserslist@4.28.1:
dependencies:
baseline-browser-mapping: 2.9.11
baseline-browser-mapping: 2.9.12
caniuse-lite: 1.0.30001762
electron-to-chromium: 1.5.267
node-releases: 2.0.27
@@ -5549,7 +5549,7 @@ snapshots:
react-is@19.2.3: {}
react-router@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
react-router@7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
cookie: 1.1.1
react: 19.2.3
@@ -5990,7 +5990,7 @@ snapshots:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
vite-plugin-imagemin@0.6.1(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)):
vite-plugin-imagemin@0.6.1(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1)):
dependencies:
'@types/imagemin': 7.0.1
'@types/imagemin-gifsicle': 7.0.4
@@ -6015,11 +6015,11 @@ snapshots:
imagemin-webp: 6.1.0
jpegtran-bin: 6.0.1
pathe: 0.2.0
vite: 7.3.0(@types/node@25.0.3)(terser@5.44.1)
vite: 7.3.1(@types/node@25.0.3)(terser@5.44.1)
transitivePeerDependencies:
- supports-color
vite-prerender-plugin@0.5.12(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)):
vite-prerender-plugin@0.5.12(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1)):
dependencies:
kolorist: 1.8.0
magic-string: 0.30.21
@@ -6027,20 +6027,20 @@ snapshots:
simple-code-frame: 1.3.0
source-map: 0.7.6
stack-trace: 1.0.0-pre2
vite: 7.3.0(@types/node@25.0.3)(terser@5.44.1)
vite: 7.3.1(@types/node@25.0.3)(terser@5.44.1)
vite-tsconfig-paths@6.0.3(typescript@5.9.3)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)):
vite-tsconfig-paths@6.0.3(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.3)(terser@5.44.1)):
dependencies:
debug: 4.4.3
globrex: 0.1.2
tsconfck: 3.1.6(typescript@5.9.3)
optionalDependencies:
vite: 7.3.0(@types/node@25.0.3)(terser@5.44.1)
vite: 7.3.1(@types/node@25.0.3)(terser@5.44.1)
transitivePeerDependencies:
- supports-color
- typescript
vite@7.3.0(@types/node@25.0.3)(terser@5.44.1):
vite@7.3.1(@types/node@25.0.3)(terser@5.44.1):
dependencies:
esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3)

View File

@@ -10,7 +10,7 @@
},
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"@trivago/prettier-plugin-sort-imports": "^6.0.1",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"formidable": "^3.5.4",
"itty-router": "^5.0.22",
"prettier": "^3.7.4"

View File

@@ -12,8 +12,8 @@ importers:
specifier: ^3.1.3
version: 3.1.3
'@trivago/prettier-plugin-sort-imports':
specifier: ^6.0.1
version: 6.0.1(prettier@3.7.4)
specifier: ^6.0.2
version: 6.0.2(prettier@3.7.4)
formidable:
specifier: ^3.5.4
version: 3.5.4
@@ -87,8 +87,8 @@ packages:
'@paralleldrive/cuid2@2.3.1':
resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
'@trivago/prettier-plugin-sort-imports@6.0.1':
resolution: {integrity: sha512-6B13DCWDfAfh4AEJ43gRgeCSAQmlKG5LHqHzHc0lbUwgBy0rX7o41US+46Fd4XiXBx+JDGEz3NBadCbUls0dUQ==}
'@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
@@ -246,7 +246,7 @@ snapshots:
dependencies:
'@noble/hashes': 1.8.0
'@trivago/prettier-plugin-sort-imports@6.0.1(prettier@3.7.4)':
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.7.4)':
dependencies:
'@babel/generator': 7.28.5
'@babel/parser': 7.28.5

View File

@@ -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(",");