16 Commits

Author SHA1 Message Date
Proddy
64058b0f61 Merge pull request #2792 from proddy/dev
small updates
2025-12-04 20:32:21 +01:00
proddy
d7b5c81b0e package update 2025-12-04 20:29:20 +01:00
proddy
02e8dba971 fix max size to 32MB 2025-12-03 22:16:39 +01:00
proddy
59878fb190 remove vscode settings and package.json 2025-12-03 22:11:44 +01:00
proddy
9ff0f83af9 remove obsolete double auto-commit 2025-12-03 22:06:06 +01:00
proddy
e6f825371e fix make for Windows 2025-12-03 22:05:45 +01:00
proddy
45f3f23033 package update 2025-12-03 21:53:33 +01:00
proddy
ffd27db208 show elapsed time 2025-12-03 21:53:26 +01:00
proddy
a452d6131b DiffTemp ranged to 4-15 K - fix #2783 2025-12-03 21:53:16 +01:00
Proddy
03ef981765 Merge pull request #2788 from proddy/dev
fix mem issue
2025-12-02 19:42:05 +01:00
proddy
9ca9f25fd3 rollback modbus hash map - fix #2752 2025-12-02 19:41:17 +01:00
proddy
41122dddb2 dev-34 2025-12-02 19:40:45 +01:00
proddy
1e0c94d007 package update 2025-12-02 19:40:38 +01:00
proddy
3e42a7fb4c package update 2025-12-01 19:56:59 +01:00
proddy
a8fcc1fb44 show mem 2025-12-01 19:56:53 +01:00
proddy
e43416019d package update 2025-12-01 17:40:53 +01:00
14 changed files with 310 additions and 347 deletions

View File

@@ -64,29 +64,7 @@ jobs:
- name: Commit the generated files
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: update generated files"
- name: Configure Git
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
- name: Check for changes and commit
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "Changes detected, committing..."
git add .
git commit -m "Auto-commit build artifacts and configuration updates
- Updated build configurations
- Generated build artifacts
- Version: ${{steps.build_info.outputs.VERSION}}"
echo "Pushing changes to repository..."
git push origin dev
else
echo "No changes to commit"
fi
commit_message: "chore: update generated files for v${{steps.build_info.outputs.VERSION}}"
- name: Create GitHub Release
id: 'automatic_releases'

2
.gitignore vendored
View File

@@ -2,7 +2,6 @@
.vscode/c_cpp_properties.json
.vscode/extensions.json
.vscode/launch.json
.vscode/settings.json
# c++ compiling
.clang_complete
@@ -73,7 +72,6 @@ logs/*
sdkconfig.*
sdkconfig_tasmota_esp32
pnpm-lock.yaml
package.json
.cache/
interface/.tsbuildinfo
test/test_api/package-lock.json

View File

@@ -21,13 +21,14 @@ endif
# Optimize parallel build configuration
UNAME_S := $(shell uname -s)
JOBS ?= 1
ifeq ($(UNAME_S),Linux)
EXTRA_CPPFLAGS = -D LINUX
JOBS ?= $(shell nproc)
JOBS := $(shell nproc)
endif
ifeq ($(UNAME_S),Darwin)
EXTRA_CPPFLAGS = -D OSX -Wno-tautological-constant-out-of-range-compare
JOBS ?= $(shell sysctl -n hw.ncpu)
JOBS := $(shell sysctl -n hw.ncpu)
endif
# Set optimal parallel build settings

View File

@@ -25,7 +25,7 @@
"upload": {
"flash_size": "32MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"maximum_size": 33554432,
"require_upload_port": true,
"speed": 460800
},

View File

@@ -26,8 +26,8 @@
"@alova/adapter-xhr": "2.3.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.5",
"@mui/material": "^7.3.5",
"@mui/icons-material": "^7.3.6",
"@mui/material": "^7.3.6",
"@preact/compat": "^18.3.1",
"@table-library/react-table-library": "4.1.15",
"alova": "3.4.0",
@@ -37,11 +37,11 @@
"jwt-decode": "^4.0.0",
"magic-string": "^0.30.21",
"mime-types": "^3.0.2",
"preact": "^10.27.2",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"preact": "^10.28.0",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"react-icons": "^5.5.0",
"react-router": "^7.9.6",
"react-router": "^7.10.1",
"react-toastify": "^11.0.5",
"typesafe-i18n": "^5.26.2",
"typescript": "^5.9.3"
@@ -59,11 +59,11 @@
"concurrently": "^9.2.1",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.7.3",
"prettier": "^3.7.4",
"rollup-plugin-visualizer": "^6.0.5",
"terser": "^5.44.1",
"typescript-eslint": "^8.48.0",
"vite": "^7.2.4",
"typescript-eslint": "^8.48.1",
"vite": "^7.2.6",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4"
},

476
interface/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
"formidable": "^3.5.4",
"itty-router": "^5.0.22",
"prettier": "^3.7.3"
"prettier": "^3.7.4"
},
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a"
}

View File

@@ -13,7 +13,7 @@ importers:
version: 3.1.2
'@trivago/prettier-plugin-sort-imports':
specifier: ^6.0.0
version: 6.0.0(prettier@3.7.3)
version: 6.0.0(prettier@3.7.4)
formidable:
specifier: ^3.5.4
version: 3.5.4
@@ -21,8 +21,8 @@ importers:
specifier: ^5.0.22
version: 5.0.22
prettier:
specifier: ^3.7.3
version: 3.7.3
specifier: ^3.7.4
version: 3.7.4
packages:
@@ -167,8 +167,8 @@ packages:
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
prettier@3.7.3:
resolution: {integrity: sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==}
prettier@3.7.4:
resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==}
engines: {node: '>=14'}
hasBin: true
@@ -246,7 +246,7 @@ snapshots:
dependencies:
'@noble/hashes': 1.8.0
'@trivago/prettier-plugin-sort-imports@6.0.0(prettier@3.7.3)':
'@trivago/prettier-plugin-sort-imports@6.0.0(prettier@3.7.4)':
dependencies:
'@babel/generator': 7.28.5
'@babel/parser': 7.28.5
@@ -256,7 +256,7 @@ snapshots:
lodash-es: 4.17.21
minimatch: 9.0.5
parse-imports-exports: 0.2.4
prettier: 3.7.3
prettier: 3.7.4
transitivePeerDependencies:
- supports-color
@@ -311,6 +311,6 @@ snapshots:
picocolors@1.1.1: {}
prettier@3.7.3: {}
prettier@3.7.4: {}
wrappy@1.0.2: {}

View File

@@ -632,9 +632,6 @@ void EMSdevice::add_device_value(int8_t tag, // to b
devicevalues_.emplace_back(
device_type_, tag, value_p, type, options, options_single, numeric_operator, short_name, fullname, custom_fullname, uom, has_cmd, min, max, state);
// add to index for fast lookup by (tag, short_name)
devicevalue_index_[{static_cast<uint8_t>(tag), short_name}] = devicevalues_.size() - 1;
// add a new command if it has a function attached
if (has_cmd) {
uint8_t flags = CommandFlag::ADMIN_ONLY; // executing commands require admin privileges
@@ -2212,13 +2209,14 @@ std::string EMSdevice::name() {
// copy a raw value (i.e. without applying the numeric_operator) to the output buffer.
// returns true on success.
int EMSdevice::get_modbus_value(uint8_t tag, const std::string & shortname, std::vector<uint16_t> & result) {
// find device value by shortname using hash map index
auto index_it = devicevalue_index_.find({tag, shortname});
if (index_it == devicevalue_index_.end()) {
// find device value by shortname
// TODO replace linear search which is inefficient
const auto & it = std::find_if(devicevalues_.begin(), devicevalues_.end(), [&](const DeviceValue & x) { return x.tag == tag && x.short_name == shortname; });
if (it == devicevalues_.end() && (it->short_name != shortname || it->tag != tag)) {
return -1;
}
auto & dv = devicevalues_[index_it->second];
auto & dv = *it;
// check if it exists, there is a value for the entity. Set the flag to ACTIVE
// not that this will override any previously removed states
@@ -2299,13 +2297,13 @@ int EMSdevice::get_modbus_value(uint8_t tag, const std::string & shortname, std:
int EMSdevice::modbus_value_to_json(uint8_t tag, const std::string & shortname, const std::vector<uint8_t> & modbus_data, JsonObject jsonValue) {
// LOG_DEBUG("modbus_value_to_json(%d,%s,[%d bytes])\n", tag, shortname.c_str(), modbus_data.size());
// find device value by shortname using hash map index
auto index_it = devicevalue_index_.find({tag, shortname});
if (index_it == devicevalue_index_.end()) {
// find device value by shortname
const auto & it = std::find_if(devicevalues_.begin(), devicevalues_.end(), [&](const DeviceValue & x) { return x.tag == tag && x.short_name == shortname; });
if (it == devicevalues_.end() && (it->short_name != shortname || it->tag != tag)) {
return -1;
}
auto & dv = devicevalues_[index_it->second];
auto & dv = *it;
// handle Booleans
if (dv.type == DeviceValueType::BOOL) {

View File

@@ -556,26 +556,6 @@ class EMSdevice {
#endif
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
std::vector<DeviceValue> devicevalues_; // all the device values
// added for modbus
// Hash map for O(1) lookup of device values by (tag, short_name) key
struct DeviceValueKey {
uint8_t tag;
std::string short_name;
bool operator==(const DeviceValueKey & other) const {
return tag == other.tag && short_name == other.short_name;
}
};
struct DeviceValueKeyHash {
std::size_t operator()(const DeviceValueKey & key) const {
// Combine hash of tag and short_name
return std::hash<uint8_t>()(key.tag) ^ (std::hash<std::string>()(key.short_name) << 1);
}
};
std::unordered_map<DeviceValueKey, size_t, DeviceValueKeyHash> devicevalue_index_; // index: key -> devicevalues_ position
};
} // namespace emsesp

View File

@@ -964,17 +964,17 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
48,
63);
register_device_value(
DeviceValueTAG::TAG_DHW1, &wwComfDiffTemp_, DeviceValueType::UINT8, FL_(wwComfDiffTemp), DeviceValueUOM::K, MAKE_CF_CB(set_wwComfDiffTemp), 4, 12);
DeviceValueTAG::TAG_DHW1, &wwComfDiffTemp_, DeviceValueType::UINT8, FL_(wwComfDiffTemp), DeviceValueUOM::K, MAKE_CF_CB(set_wwComfDiffTemp), 4, 15);
register_device_value(
DeviceValueTAG::TAG_DHW1, &wwEcoDiffTemp_, DeviceValueType::UINT8, FL_(wwEcoDiffTemp), DeviceValueUOM::K, MAKE_CF_CB(set_wwEcoDiffTemp), 4, 12);
DeviceValueTAG::TAG_DHW1, &wwEcoDiffTemp_, DeviceValueType::UINT8, FL_(wwEcoDiffTemp), DeviceValueUOM::K, MAKE_CF_CB(set_wwEcoDiffTemp), 4, 15);
register_device_value(DeviceValueTAG::TAG_DHW1,
&wwEcoPlusDiffTemp_,
DeviceValueType::UINT8,
FL_(wwEcoPlusDiffTemp),
DeviceValueUOM::K,
MAKE_CF_CB(set_wwEcoPlusDiffTemp),
6,
12);
4,
15);
register_device_value(DeviceValueTAG::TAG_DHW1,
&wwComfStopTemp_,
DeviceValueType::UINT8,

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.3-dev.33"
#define EMSESP_APP_VERSION "3.7.3-dev.34"

View File

@@ -3,48 +3,50 @@
const axios = require('axios');
async function testAPI(ip = "ems-esp.local", apiPath = "system", loopCount = 1, delayMs = 1000) {
const baseUrl = `http://${ip}/api`;
const baseUrl = `http://${ip}`;
const url = `${baseUrl}/${apiPath}`;
const results = [];
const testStartTime = Date.now();
for (let i = 0; i < loopCount; i++) {
let logMessage = '';
if (loopCount > 1) {
logMessage = `--- Request ${i + 1} of ${loopCount} ---`;
const totalElapsed = ((Date.now() - testStartTime) / 1000).toFixed(1);
logMessage = `[${totalElapsed}s] Request: ${i + 1}/${loopCount},`;
}
try {
const startTime = Date.now();
const response = await axios.get(url, {
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
});
// console.log('Status:', response.status);
// console.log('Data:', JSON.stringify(response.data, null, 2));
// Extract and print freeMem
const freeMem = response.data?.freeMem || response.data?.system?.freeMem;
if (freeMem !== undefined) {
logMessage += (logMessage ? ' ' : '') + `System Free Memory: ${freeMem}`;
} else {
logMessage += (logMessage ? ' ' : '') + 'freeMem not found in response';
}
console.log(logMessage);
// Delay before next request (except for the last one)
if (i < loopCount - 1) {
await new Promise(resolve => setTimeout(resolve, delayMs));
}
logMessage += (logMessage ? ' ' : '') + `URL: ${url}, Status: ${response.status}`;
} catch (error) {
console.error('Error:', error.message);
if (error.response) {
console.error('Response status:', error.response.status);
console.error('Response data:', error.response.data);
}
// if (error.response) {
// console.error('Response status:', error.response.status);
// console.error('Response data:', error.response.data);
// }
throw error;
}
// if successful make another request to the /api/system/info endpoint to fetch the freeMem
const response = await axios.get(`${baseUrl}/api/system/info`);
const freeMem = response.data?.freeMem || response.data?.system?.freeMem;
if (freeMem !== undefined) {
logMessage += `, freeMem: ${freeMem}`;
} else {
logMessage += 'freeMem not found in response';
}
console.log(logMessage);
// Delay before next request (except for the last one)
if (i < loopCount - 1) {
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
return loopCount === 1 ? results[0] : results;
@@ -52,10 +54,11 @@ async function testAPI(ip = "ems-esp.local", apiPath = "system", loopCount = 1,
// Run the test
// Examples:
// testAPI("192.168.1.65", "system") - single call
// testAPI("192.168.1.65", "system", 5) - 5 calls with 1000ms delay
// testAPI("192.168.1.65", "system", 10, 2000) - 10 calls with 2000ms delay
testAPI("192.168.1.65", "system", 20000, 5)
// testAPI("192.168.1.65", "api/system") - single call
// testAPI("192.168.1.65", "api/system", 5) - 5 calls with 1000ms delay
// testAPI("192.168.1.65", "api/system", 10, 2000) - 10 calls with 2000ms delay
// testAPI("192.168.1.65", "status", 20000, 5)
testAPI("192.168.1.65", "api/custom/test_custom", 1000, 5)
.then(() => {
console.log('Test completed successfully');
process.exit(0);

View File

@@ -0,0 +1,5 @@
{
"dependencies": {
"axios": "^1.13.2"
}
}