diff --git a/interface/package.json b/interface/package.json index 08e1741f9..af81a5691 100644 --- a/interface/package.json +++ b/interface/package.json @@ -58,7 +58,7 @@ "rollup-plugin-visualizer": "^5.14.0", "terser": "^5.39.0", "typescript-eslint": "8.26.1", - "vite": "^6.2.1", + "vite": "^6.2.2", "vite-plugin-imagemin": "^0.6.1", "vite-tsconfig-paths": "^5.1.4" }, diff --git a/interface/src/app/main/CustomEntitiesDialog.tsx b/interface/src/app/main/CustomEntitiesDialog.tsx index 0925452f3..898ecc557 100644 --- a/interface/src/app/main/CustomEntitiesDialog.tsx +++ b/interface/src/app/main/CustomEntitiesDialog.tsx @@ -62,7 +62,10 @@ const CustomEntitiesDialog = ({ ...selectedItem, device_id: selectedItem.device_id.toString(16).toUpperCase(), type_id: selectedItem.type_id.toString(16).toUpperCase(), - factor: selectedItem.value_type === DeviceValueType.BOOL ? selectedItem.factor.toString(16).toUpperCase() : selectedItem.factor + factor: + selectedItem.value_type === DeviceValueType.BOOL + ? selectedItem.factor.toString(16).toUpperCase() + : selectedItem.factor }); } }, [open, selectedItem]); @@ -83,7 +86,10 @@ const CustomEntitiesDialog = ({ if (typeof editItem.type_id === 'string') { editItem.type_id = parseInt(editItem.type_id, 16); } - if (editItem.value_type === DeviceValueType.BOOL && typeof editItem.factor === 'string') { + if ( + editItem.value_type === DeviceValueType.BOOL && + typeof editItem.factor === 'string' + ) { editItem.factor = parseInt(editItem.factor, 16); } onSave(editItem); diff --git a/interface/yarn.lock b/interface/yarn.lock index 3e53e71b6..d40d0434e 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -51,30 +51,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.22.1": - version: 7.26.9 - resolution: "@babel/core@npm:7.26.9" - dependencies: - "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.26.2" - "@babel/generator": "npm:^7.26.9" - "@babel/helper-compilation-targets": "npm:^7.26.5" - "@babel/helper-module-transforms": "npm:^7.26.0" - "@babel/helpers": "npm:^7.26.9" - "@babel/parser": "npm:^7.26.9" - "@babel/template": "npm:^7.26.9" - "@babel/traverse": "npm:^7.26.9" - "@babel/types": "npm:^7.26.9" - convert-source-map: "npm:^2.0.0" - debug: "npm:^4.1.0" - gensync: "npm:^1.0.0-beta.2" - json5: "npm:^2.2.3" - semver: "npm:^6.3.1" - checksum: 10c0/ed7212ff42a9453765787019b7d191b167afcacd4bd8fec10b055344ef53fa0cc648c9a80159ae4ecf870016a6318731e087042dcb68d1a2a9d34eb290dc014b - languageName: node - linkType: hard - -"@babel/core@npm:^7.26.10": +"@babel/core@npm:^7.22.1, @babel/core@npm:^7.26.10": version: 7.26.10 resolution: "@babel/core@npm:7.26.10" dependencies: @@ -97,7 +74,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.26.10": +"@babel/generator@npm:^7.26.10, @babel/generator@npm:^7.26.5": version: 7.26.10 resolution: "@babel/generator@npm:7.26.10" dependencies: @@ -110,19 +87,6 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.26.5, @babel/generator@npm:^7.26.9": - version: 7.26.9 - resolution: "@babel/generator@npm:7.26.9" - dependencies: - "@babel/parser": "npm:^7.26.9" - "@babel/types": "npm:^7.26.9" - "@jridgewell/gen-mapping": "npm:^0.3.5" - "@jridgewell/trace-mapping": "npm:^0.3.25" - jsesc: "npm:^3.0.2" - checksum: 10c0/6b78872128205224a9a9761b9ea7543a9a7902a04b82fc2f6801ead4de8f59056bab3fd17b1f834ca7b049555fc4c79234b9a6230dd9531a06525306050becad - languageName: node - linkType: hard - "@babel/helper-annotate-as-pure@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-annotate-as-pure@npm:7.25.9" @@ -206,17 +170,7 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.26.9": - version: 7.26.9 - resolution: "@babel/helpers@npm:7.26.9" - dependencies: - "@babel/template": "npm:^7.26.9" - "@babel/types": "npm:^7.26.9" - checksum: 10c0/3d4dbc4a33fe4181ed810cac52318b578294745ceaec07e2f6ecccf6cda55d25e4bfcea8f085f333bf911c9e1fc13320248dd1d5315ab47ad82ce1077410df05 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.26.10": +"@babel/parser@npm:^7.26.10, @babel/parser@npm:^7.26.7, @babel/parser@npm:^7.26.9": version: 7.26.10 resolution: "@babel/parser@npm:7.26.10" dependencies: @@ -227,17 +181,6 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.26.7, @babel/parser@npm:^7.26.9": - version: 7.26.9 - resolution: "@babel/parser@npm:7.26.9" - dependencies: - "@babel/types": "npm:^7.26.9" - bin: - parser: ./bin/babel-parser.js - checksum: 10c0/4b9ef3c9a0d4c328e5e5544f50fe8932c36f8a2c851e7f14a85401487cd3da75cad72c2e1bcec1eac55599a6bbb2fdc091f274c4fcafa6bdd112d4915ff087fc - languageName: node - linkType: hard - "@babel/plugin-syntax-jsx@npm:^7.25.9": version: 7.25.9 resolution: "@babel/plugin-syntax-jsx@npm:7.25.9" @@ -275,12 +218,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": - version: 7.26.9 - resolution: "@babel/runtime@npm:7.26.9" +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.26.9, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": + version: 7.26.10 + resolution: "@babel/runtime@npm:7.26.10" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10c0/e8517131110a6ec3a7360881438b85060e49824e007f4a64b5dfa9192cf2bb5c01e84bfc109f02d822c7edb0db926928dd6b991e3ee460b483fb0fac43152d9b + checksum: 10c0/6dc6d88c7908f505c4f7770fb4677dfa61f68f659b943c2be1f2a99cb6680343462867abf2d49822adc435932919b36c77ac60125793e719ea8745f2073d3745 languageName: node linkType: hard @@ -295,22 +238,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.25.9, @babel/traverse@npm:^7.26.7, @babel/traverse@npm:^7.26.9": - version: 7.26.9 - resolution: "@babel/traverse@npm:7.26.9" - dependencies: - "@babel/code-frame": "npm:^7.26.2" - "@babel/generator": "npm:^7.26.9" - "@babel/parser": "npm:^7.26.9" - "@babel/template": "npm:^7.26.9" - "@babel/types": "npm:^7.26.9" - debug: "npm:^4.3.1" - globals: "npm:^11.1.0" - checksum: 10c0/51dd57fa39ea34d04816806bfead04c74f37301269d24c192d1406dc6e244fea99713b3b9c5f3e926d9ef6aa9cd5c062ad4f2fc1caa9cf843d5e864484ac955e - languageName: node - linkType: hard - -"@babel/traverse@npm:^7.26.10": +"@babel/traverse@npm:^7.25.9, @babel/traverse@npm:^7.26.10, @babel/traverse@npm:^7.26.7": version: 7.26.10 resolution: "@babel/traverse@npm:7.26.10" dependencies: @@ -325,17 +253,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.25.9, @babel/types@npm:^7.26.7, @babel/types@npm:^7.26.9": - version: 7.26.9 - resolution: "@babel/types@npm:7.26.9" - dependencies: - "@babel/helper-string-parser": "npm:^7.25.9" - "@babel/helper-validator-identifier": "npm:^7.25.9" - checksum: 10c0/999c56269ba00e5c57aa711fbe7ff071cd6990bafd1b978341ea7572cc78919986e2aa6ee51dacf4b6a7a6fa63ba4eb3f1a03cf55eee31b896a56d068b895964 - languageName: node - linkType: hard - -"@babel/types@npm:^7.26.10": +"@babel/types@npm:^7.25.9, @babel/types@npm:^7.26.10, @babel/types@npm:^7.26.7, @babel/types@npm:^7.26.9": version: 7.26.10 resolution: "@babel/types@npm:7.26.10" dependencies: @@ -674,13 +592,13 @@ __metadata: linkType: hard "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": - version: 4.4.1 - resolution: "@eslint-community/eslint-utils@npm:4.4.1" + version: 4.5.1 + resolution: "@eslint-community/eslint-utils@npm:4.5.1" dependencies: eslint-visitor-keys: "npm:^3.4.3" peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - checksum: 10c0/2aa0ac2fc50ff3f234408b10900ed4f1a0b19352f21346ad4cc3d83a1271481bdda11097baa45d484dd564c895e0762a27a8240be7a256b3ad47129e96528252 + checksum: 10c0/b520ae1b7bd04531a5c5da2021071815df4717a9f7d13720e3a5ddccf5c9c619532039830811fcbae1c2f1c9d133e63af2435ee69e0fc0fabbd6d928c6800fb2 languageName: node linkType: hard @@ -1000,14 +918,16 @@ __metadata: linkType: hard "@mui/types@npm:^7.2.21": - version: 7.2.23 - resolution: "@mui/types@npm:7.2.23" + version: 7.3.0 + resolution: "@mui/types@npm:7.3.0" + dependencies: + "@babel/runtime": "npm:^7.26.9" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/e7aebed6a79186f76b35354f094aed0c33291eaae4494fe471a6f4a4ebe6d6998071085c76b569d859723954a10eee7e751b39aa1fd85b0ce6d888922e05937d + checksum: 10c0/3700c3c9708ac85b9723269eb54a8faf62b0ff280b87667e29cf3cf4825708c3bcc7a4fe0bd32fe8c44d809586e02b64321f8d0b198a4250d01ed7b442312221 languageName: node linkType: hard @@ -1707,7 +1627,7 @@ __metadata: typesafe-i18n: "npm:^5.26.2" typescript: "npm:^5.8.2" typescript-eslint: "npm:8.26.1" - vite: "npm:^6.2.1" + vite: "npm:^6.2.2" vite-plugin-imagemin: "npm:^0.6.1" vite-tsconfig-paths: "npm:^5.1.4" languageName: unknown @@ -2121,9 +2041,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001688": - version: 1.0.30001703 - resolution: "caniuse-lite@npm:1.0.30001703" - checksum: 10c0/ed88e318da28e9e59c4ac3a2e3c42859558b7b713aebf03696a1f916e4ed4b70734dda82be04635e2b62ec355b8639bbed829b7b12ff528d7f9cc31a3a5bea91 + version: 1.0.30001704 + resolution: "caniuse-lite@npm:1.0.30001704" + checksum: 10c0/4efa0ece51ef58e7ce7e7c8cd7b50372bcb910581a47397be5c086c046c3cd436d123b734351fb20f638c322b339198edf89b5b632ff59bdd171c74ff7f4efcf languageName: node linkType: hard @@ -2704,9 +2624,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.73": - version: 1.5.114 - resolution: "electron-to-chromium@npm:1.5.114" - checksum: 10c0/cb86057d78f1aeb53ab6550dedacfd9496bcc6676bab7b48466c3958ba9ce0ed78c7213b1eab99ba38542cbaaa176eb7f8ea8b0274c0688b8ce3058291549430 + version: 1.5.119 + resolution: "electron-to-chromium@npm:1.5.119" + checksum: 10c0/f4ca0eb3af21d16d2c1fd480c5a43428ce4423ca887338d69bd3eadae818b6d6e799c62b1baac1bc4f76e4226f5fb15a6b0dc8965f601ac053e86508d3e8e41c languageName: node linkType: hard @@ -6920,15 +6840,16 @@ __metadata: linkType: hard "vite-prerender-plugin@npm:^0.5.3": - version: 0.5.6 - resolution: "vite-prerender-plugin@npm:0.5.6" + version: 0.5.7 + resolution: "vite-prerender-plugin@npm:0.5.7" dependencies: + kolorist: "npm:^1.8.0" magic-string: "npm:^0.30.6" node-html-parser: "npm:^6.1.12" simple-code-frame: "npm:^1.3.0" source-map: "npm:^0.7.4" stack-trace: "npm:^1.0.0-pre2" - checksum: 10c0/74e0d860b4f539804da23e85ea6f16b341cc882c8c1ccffc5467b4c726c399aea87cd1bf244a21b58c664e27ad8de5aac56c070292332ccaed93ddf1e29f17dc + checksum: 10c0/77005008315d39d5bb87f7462046cc08b074388f900905678ce9782378bf4c8ca6ea5c0321506403b8f22fe6edd55c49cc423903ce545fefd0d4f046f06823cb languageName: node linkType: hard @@ -6948,9 +6869,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:^6.2.1": - version: 6.2.1 - resolution: "vite@npm:6.2.1" +"vite@npm:^6.2.2": + version: 6.2.2 + resolution: "vite@npm:6.2.2" dependencies: esbuild: "npm:^0.25.0" fsevents: "npm:~2.3.3" @@ -6996,7 +6917,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/2c024376a840eae2ce9cfba98d62e9f1eae93caa8304875854dbc0740414aedcfbe157c2244567bd456cdb60a300312af02ae9b5c63c147d35cf4da3a0591312 + checksum: 10c0/52f5b1c10cfe5e3b6382c6de1811ebbf76df9b5a8bab3d65169446c6b54a5f1528f775b1548009a6d8aad11def20fba046bb3e9abb10c0c2c9ccd78118623bb8 languageName: node linkType: hard diff --git a/mock-api/package.json b/mock-api/package.json index 4a18cde8d..2602c51a5 100644 --- a/mock-api/package.json +++ b/mock-api/package.json @@ -9,7 +9,7 @@ "format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'" }, "dependencies": { - "@msgpack/msgpack": "^3.1.0", + "@msgpack/msgpack": "^3.1.1", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "formidable": "^3.5.2", "itty-router": "^5.0.18", diff --git a/mock-api/yarn.lock b/mock-api/yarn.lock index f899104d9..cfbc7eb43 100644 --- a/mock-api/yarn.lock +++ b/mock-api/yarn.lock @@ -16,16 +16,16 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.26.5, @babel/generator@npm:^7.26.9": - version: 7.26.9 - resolution: "@babel/generator@npm:7.26.9" +"@babel/generator@npm:^7.26.10, @babel/generator@npm:^7.26.5": + version: 7.26.10 + resolution: "@babel/generator@npm:7.26.10" dependencies: - "@babel/parser": "npm:^7.26.9" - "@babel/types": "npm:^7.26.9" + "@babel/parser": "npm:^7.26.10" + "@babel/types": "npm:^7.26.10" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^3.0.2" - checksum: 10c0/6b78872128205224a9a9761b9ea7543a9a7902a04b82fc2f6801ead4de8f59056bab3fd17b1f834ca7b049555fc4c79234b9a6230dd9531a06525306050becad + checksum: 10c0/88b3b3ea80592fc89349c4e1a145e1386e4042866d2507298adf452bf972f68d13bf699a845e6ab8c028bd52c2247013eb1221b86e1db5c9779faacba9c4b10e languageName: node linkType: hard @@ -43,14 +43,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.26.7, @babel/parser@npm:^7.26.9": - version: 7.26.9 - resolution: "@babel/parser@npm:7.26.9" +"@babel/parser@npm:^7.26.10, @babel/parser@npm:^7.26.7, @babel/parser@npm:^7.26.9": + version: 7.26.10 + resolution: "@babel/parser@npm:7.26.10" dependencies: - "@babel/types": "npm:^7.26.9" + "@babel/types": "npm:^7.26.10" bin: parser: ./bin/babel-parser.js - checksum: 10c0/4b9ef3c9a0d4c328e5e5544f50fe8932c36f8a2c851e7f14a85401487cd3da75cad72c2e1bcec1eac55599a6bbb2fdc091f274c4fcafa6bdd112d4915ff087fc + checksum: 10c0/c47f5c0f63cd12a663e9dc94a635f9efbb5059d98086a92286d7764357c66bceba18ccbe79333e01e9be3bfb8caba34b3aaebfd8e62c3d5921c8cf907267be75 languageName: node linkType: hard @@ -66,27 +66,27 @@ __metadata: linkType: hard "@babel/traverse@npm:^7.26.7": - version: 7.26.9 - resolution: "@babel/traverse@npm:7.26.9" + version: 7.26.10 + resolution: "@babel/traverse@npm:7.26.10" dependencies: "@babel/code-frame": "npm:^7.26.2" - "@babel/generator": "npm:^7.26.9" - "@babel/parser": "npm:^7.26.9" + "@babel/generator": "npm:^7.26.10" + "@babel/parser": "npm:^7.26.10" "@babel/template": "npm:^7.26.9" - "@babel/types": "npm:^7.26.9" + "@babel/types": "npm:^7.26.10" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10c0/51dd57fa39ea34d04816806bfead04c74f37301269d24c192d1406dc6e244fea99713b3b9c5f3e926d9ef6aa9cd5c062ad4f2fc1caa9cf843d5e864484ac955e + checksum: 10c0/4e86bb4e3c30a6162bb91df86329df79d96566c3e2d9ccba04f108c30473a3a4fd360d9990531493d90f6a12004f10f616bf9b9229ca30c816b708615e9de2ac languageName: node linkType: hard -"@babel/types@npm:^7.26.7, @babel/types@npm:^7.26.9": - version: 7.26.9 - resolution: "@babel/types@npm:7.26.9" +"@babel/types@npm:^7.26.10, @babel/types@npm:^7.26.7, @babel/types@npm:^7.26.9": + version: 7.26.10 + resolution: "@babel/types@npm:7.26.10" dependencies: "@babel/helper-string-parser": "npm:^7.25.9" "@babel/helper-validator-identifier": "npm:^7.25.9" - checksum: 10c0/999c56269ba00e5c57aa711fbe7ff071cd6990bafd1b978341ea7572cc78919986e2aa6ee51dacf4b6a7a6fa63ba4eb3f1a03cf55eee31b896a56d068b895964 + checksum: 10c0/7a7f83f568bfc3dfabfaf9ae3a97ab5c061726c0afa7dcd94226d4f84a81559da368ed79671e3a8039d16f12476cf110381a377ebdea07587925f69628200dac languageName: node linkType: hard @@ -132,10 +132,10 @@ __metadata: languageName: node linkType: hard -"@msgpack/msgpack@npm:^3.1.0": - version: 3.1.0 - resolution: "@msgpack/msgpack@npm:3.1.0" - checksum: 10c0/d31c299cc9f7620715eec77b12ec0cb4950943fd8b0c23275e37afe6ad30efe237e61333e8e340a0a433eca7a4595212fb905e70b4506ae5b866758e4291289b +"@msgpack/msgpack@npm:^3.1.1": + version: 3.1.1 + resolution: "@msgpack/msgpack@npm:3.1.1" + checksum: 10c0/f7048d2145ee39e4979b4e6bb9d9723db031ea7b4f00d0ec96258fbff40b8af027783f391ee294e5385a5a91849a77d934ad08eb11d08253484eee8f8f0866ff languageName: node linkType: hard @@ -260,7 +260,7 @@ __metadata: version: 0.0.0-use.local resolution: "mock-api@workspace:." dependencies: - "@msgpack/msgpack": "npm:^3.1.0" + "@msgpack/msgpack": "npm:^3.1.1" "@trivago/prettier-plugin-sort-imports": "npm:^5.2.2" formidable: "npm:^3.5.2" itty-router: "npm:^5.0.18" diff --git a/src/core/command.cpp b/src/core/command.cpp index 6f03cb74f..28c296ab4 100644 --- a/src/core/command.cpp +++ b/src/core/command.cpp @@ -184,7 +184,8 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec } // call the command based on the type - uint8_t return_code = CommandRet::ERROR; + uint8_t return_code = CommandRet::OK; + if (data.is()) { return_code = Command::call(device_type, command_p, data.as(), is_admin, id_n, output); } else if (data.is()) { @@ -312,11 +313,17 @@ bool Command::set_attribute(JsonObject output, const char * cmd, const char * at return true; } - // not found output.clear(); + // attribute isn't found + // it could be a value command, but the value doesn't exist? + if (strcmp(attribute, "value") == 0) { + LOG_DEBUG("%s has no value set", cmd); + return false; // fail + } + char error[100]; - snprintf(error, sizeof(error), "no %s in %s", attribute, cmd); + snprintf(error, sizeof(error), "no attribute '%s' in %s", attribute, cmd); output["message"] = error; return false; } @@ -356,7 +363,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha return CommandRet::OK; } } else if (device_type == EMSdevice::DeviceType::SYSTEM && strchr(cmd, '/')) { - // check service commands, if not found continue with commandsfunctions + // check service commands, if not found continue with command functions if (EMSESP::system_.command_service(cmd, value)) { return CommandRet::OK; } @@ -376,14 +383,23 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha flag = CommandFlag::CMD_FLAG_AHS; } - // see if there is a command registered and it's valid + // see if there is a command registered for this EMS device auto cf = find_command(device_type, device_id, cmd, flag); if (!cf) { // if we don't already have a message set, set it to invalid command if (output["message"]) { LOG_WARNING("Command failed: %s", output["message"].as()); + return CommandRet::ERROR; } else { - std::string err = "no " + std::string(cmd) + " in " + dname; + // not an error, no test if we're fetching a value, but the value is not set + auto attribute_s = Command::get_attribute(cmd); + if (attribute_s) { + if (strcmp(attribute_s, "value") == 0) { + return CommandRet::NO_VALUE; + } + } + + std::string err = "no entity '" + std::string(cmd) + "' in " + dname; output["message"] = err; LOG_WARNING("Command failed: %s", err.c_str()); } @@ -496,7 +512,7 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ui } } - return nullptr; // command not found + return nullptr; // command not found, could be an attribute? } void Command::erase_device_commands(const uint8_t device_type) { diff --git a/src/core/command.h b/src/core/command.h index 62517fb87..f85024b94 100644 --- a/src/core/command.h +++ b/src/core/command.h @@ -48,7 +48,8 @@ enum CommandRet : uint8_t { NOT_FOUND, // 2 ERROR, // 3 NOT_ALLOWED, // 4 - needs authentication - INVALID // 5 - invalid (tag) + INVALID, // 5 - invalid (tag) + NO_VALUE // 6 - no value }; using cmd_function_p = std::function; diff --git a/src/core/emsdevice.cpp b/src/core/emsdevice.cpp index ffba6f91e..3deebfd3c 100644 --- a/src/core/emsdevice.cpp +++ b/src/core/emsdevice.cpp @@ -1528,6 +1528,7 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t if (cmd_s == Helpers::toLower(dv.short_name) && (tag <= 0 || tag == dv.tag)) { get_value_json(output, dv); // if we're filtering on an attribute, go find it + // if we can't find it, maybe it exists but doesn't not have a value assigned yet return Command::set_attribute(output, cmd_s, attribute_s); } } diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index 45ee17a62..f4b11df0d 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -780,6 +780,7 @@ bool EMSESP::get_device_value_info(JsonObject root, const char * cmd, const int8 } } } + // if the EMS device was valid, but the cmd not found exit. it will be handled upstream. if (found_device) { return false; diff --git a/src/core/shuntingYard.hpp b/src/core/shuntingYard.hpp index f8eb47611..659978ba3 100644 --- a/src/core/shuntingYard.hpp +++ b/src/core/shuntingYard.hpp @@ -380,7 +380,13 @@ std::string commands(std::string & expr, bool quotes = true) { JsonObject output = doc_out.to(); JsonObject input = doc_in.to(); std::string cmd_s = "api/" + std::string(cmd); - emsesp::Command::process(cmd_s.c_str(), true, input, output); + + auto return_code = emsesp::Command::process(cmd_s.c_str(), true, input, output); + // check for no value (entity is valid but has no value set) + if (return_code != emsesp::CommandRet::OK && return_code != emsesp::CommandRet::NO_VALUE) { + return expr = ""; + } + std::string data = output["api_data"] | ""; if (!isnum(data) && quotes) { data.insert(data.begin(), '"'); diff --git a/src/test/test.cpp b/src/test/test.cpp index 7f1b2fce5..5430fcc39 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -324,20 +324,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const if (command == "general") { shell.printfln("Testing adding a boiler, thermostat, all sensors, scheduler and custom entities..."); - // setup fake data - // EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS - // add devices test("general"); - // EMSESP::webCustomEntityService.test(); // add custom entities - // EMSESP::temperaturesensor_.test(); // add temperature sensors - // EMSESP::webSchedulerService.test(); // add scheduler items + EMSESP::webCustomEntityService.test(); // custom entities + EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS + EMSESP::temperaturesensor_.test(); // add temperature sensors + EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions - // shell.invoke_command("show devices"); - // shell.invoke_command("show values"); - // shell.invoke_command("call system publish"); - // shell.invoke_command("show mqtt"); + shell.invoke_command("show values"); ok = true; } @@ -1089,6 +1084,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // request.url("/api/boiler/comfort/value"); // EMSESP::webAPIService.webAPIService(&request); + // this should fail but it doesn't + // request.url("/api/boiler/bad/value"); + // EMSESP::webAPIService.webAPIService(&request); + // POST COMMANDS request.method(HTTP_POST); diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index b30264ed6..89f23ad47 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -134,12 +134,13 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { } // send the json that came back from the command call - // sequence is FAIL, OK, NOT_FOUND, ERROR, NOT_ALLOWED, INVALID + // sequence matches CommandRet in command.h (FAIL, OK, NOT_FOUND, ERROR, NOT_ALLOWED, INVALID, NO_VALUE) // 400 (bad request) // 200 (OK) // 404 (not found) // 401 (unauthorized) - int ret_codes[6] = {400, 200, 404, 400, 401, 400}; + // 400 (invalid) + int ret_codes[7] = {400, 200, 404, 400, 401, 400, 404}; response->setCode(ret_codes[return_code]); response->setLength(); response->setContentType("application/json; charset=utf-8"); diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 9844ff65e..c6a346769 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -105,7 +105,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web if (entityItem.ram == 0 && entityItem.value_type == DeviceValueType::STRING) { entityItem.raw = new uint8_t[(size_t)entityItem.factor + 1]; entityItem.data = ""; - entityItem.uom = 0; + entityItem.uom = 0; } else if (entityItem.value_type == DeviceValueType::BOOL) { entityItem.value = EMS_VALUE_DEFAULT_BOOL; entityItem.uom = 0; diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 5459173e7..af6eed9f8 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -165,6 +165,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { } return true; } + for (const ScheduleItem & scheduleItem : *scheduleItems_) { if (Helpers::toLower(scheduleItem.name) == cmd) { get_value_json(output, scheduleItem); @@ -385,7 +386,6 @@ bool WebSchedulerService::command(const char * name, const std::string & command snprintf(command_str, sizeof(command_str), "/api/%s", cmd.c_str()); uint8_t return_code = Command::process(command_str, true, input, output); // admin set - if (return_code == CommandRet::OK) { #if defined(EMSESP_DEBUG) EMSESP::logger().debug("Schedule command '%s' with data '%s' was successful", cmd.c_str(), data.c_str()); @@ -596,10 +596,6 @@ void WebSchedulerService::test() { test_value = "(custom/seltemp)"; command("test5", test_cmd.c_str(), compute(test_value).c_str()); - // note: this will fail unless test("boiler") is loaded before hand - test_value = "(boiler/outdoortemp)"; - command("test6", test_cmd.c_str(), compute(test_value).c_str()); - test_value = "boiler/flowtempoffset"; command("test7", test_cmd.c_str(), compute(test_value).c_str()); @@ -613,10 +609,35 @@ void WebSchedulerService::test() { test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; command("test10", test_cmd.c_str(), compute(test_value).c_str()); - test_cmd = "{\"method\":\"POST\",\"url\":\"http://192.168.1.42:8123/api/services/script/test_notify2\", \"header\":{\"authorization\":\"Bearer " - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMmNlYWI5NDgzMmI0ODE2YWQ2NzU4MjkzZDE2YWMxZSIsImlhdCI6MTcyMTM5MTI0NCwiZXhwIjoyMDM2NzUxMjQ0fQ." - "S5sago1tEI6lNhrDCO0dM_WsVQHkD_laAjcks8tWAqo\"}}"; - command("test11", test_cmd.c_str(), ""); + // test case conversion + test_value = "(thermostat/hc1/modetype == \"comfort\")"; + command("test11a", test_cmd.c_str(), compute(test_value).c_str()); // should be 1 true + test_value = "(thermostat/hc1/modetype == \"Comfort\")"; + command("test11b", test_cmd.c_str(), compute(test_value).c_str()); // should be 1 true + test_value = "(thermostat/hc1/modetype == \"unknown\")"; + command("test11c", test_cmd.c_str(), compute(test_value).c_str()); // should be 0 false + + // can't find entity, should fail + test_value = "(boiler/storagetemp/value1)"; + command("test12", test_cmd.c_str(), compute(test_value).c_str()); + + // can't find attribute, should fail + test_value = "(boiler/storagetemp1/value1)"; + command("test13", test_cmd.c_str(), compute(test_value).c_str()); + + // check when entity has no value, should pass + test_value = "(boiler/storagetemp2/value)"; + command("test14", test_cmd.c_str(), compute(test_value).c_str()); + + // should pass + test_value = "(boiler/storagetemp1/value)"; + command("test15", test_cmd.c_str(), compute(test_value).c_str()); + + // test HTTP POST to call HA script + // test_cmd = "{\"method\":\"POST\",\"url\":\"http://192.168.1.42:8123/api/services/script/test_notify2\", \"header\":{\"authorization\":\"Bearer " + // "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMmNlYWI5NDgzMmI0ODE2YWQ2NzU4MjkzZDE2YWMxZSIsImlhdCI6MTcyMTM5MTI0NCwiZXhwIjoyMDM2NzUxMjQ0fQ." + // "S5sago1tEI6lNhrDCO0dM_WsVQHkD_laAjcks8tWAqo\"}}"; + // command("test99", test_cmd.c_str(), ""); } #endif diff --git a/test/test_api/test_api.h b/test/test_api/test_api.h index 2e8c2b813..ea404872d 100644 --- a/test/test_api/test_api.h +++ b/test/test_api/test_api.h @@ -294,82 +294,82 @@ void test_36() { } void test_37() { - auto expected_response = "[{\"message\":\"no bad/value in boiler\"}]"; + auto expected_response = "[{}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/bad/value")); } void test_38() { - auto expected_response = "[{\"message\":\"no valu in comfort\"}]"; + auto expected_response = "[{\"message\":\"no attribute 'valu' in comfort\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort/valu")); } void test_39() { - auto expected_response = "[{\"message\":\"no settings in system\"}]"; + auto expected_response = "[{\"message\":\"no entity 'settings' in system\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings/locale2")); } void test_40() { - auto expected_response = "[{\"message\":\"no settings2 in system\"}]"; + auto expected_response = "[{\"message\":\"no entity 'settings2' in system\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings2")); } void test_41() { - auto expected_response = "[{\"message\":\"no settings2 in system\"}]"; + auto expected_response = "[{\"message\":\"no entity 'settings2' in system\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings2/locale2")); } void test_42() { - auto expected_response = "[{\"message\":\"no test_scheduler2 in scheduler\"}]"; + auto expected_response = "[{\"message\":\"no entity 'test_scheduler2' in scheduler\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler2")); } void test_43() { - auto expected_response = "[{\"message\":\"no val in test_scheduler\"}]"; + auto expected_response = "[{\"message\":\"no attribute 'val' in test_scheduler\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler/val")); } void test_44() { - auto expected_response = "[{\"message\":\"no test_scheduler2 in scheduler\"}]"; + auto expected_response = "[{\"message\":\"no entity 'test_scheduler2' in scheduler\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler2/val2")); } void test_45() { - auto expected_response = "[{\"message\":\"no seltemp2 in custom\"}]"; + auto expected_response = "[{\"message\":\"no entity 'seltemp2' in custom\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/seltemp2")); } void test_46() { - auto expected_response = "[{\"message\":\"no val in seltemp\"}]"; + auto expected_response = "[{\"message\":\"no attribute 'val' in seltemp\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/seltemp/val")); } void test_47() { - auto expected_response = "[{\"message\":\"no test_sensor20 in temperaturesensor\"}]"; + auto expected_response = "[{\"message\":\"no entity 'test_sensor20' in temperaturesensor\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor20")); } void test_48() { - auto expected_response = "[{\"message\":\"no 0b_0c0d_0e0f_xxxx in temperaturesensor\"}]"; + auto expected_response = "[{\"message\":\"no entity '0b_0c0d_0e0f_xxxx' in temperaturesensor\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_XXXX")); } void test_49() { - auto expected_response = "[{\"message\":\"no bad in test_tempsensor2\"}]"; + auto expected_response = "[{\"message\":\"no attribute 'bad' in test_tempsensor2\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2/bad")); } void test_50() { - auto expected_response = "[{\"message\":\"no bad in test_analogsensor1\"}]"; + auto expected_response = "[{\"message\":\"no attribute 'bad' in test_analogsensor1\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1/bad")); } void test_51() { - auto expected_response = "[{\"message\":\"no test_analog10 in analogsensor\"}]"; + auto expected_response = "[{\"message\":\"no entity 'test_analog10' in analogsensor\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog10")); } void test_52() { - auto expected_response = "[{\"message\":\"no test_analog10 in analogsensor\"}]"; + auto expected_response = "[{\"message\":\"no entity 'test_analog10' in analogsensor\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog10/bad2")); } diff --git a/test/test_api/test_shuntingYard.hpp b/test/test_api/test_shuntingYard.hpp index d3e2b2c0e..3d719389c 100644 --- a/test/test_api/test_shuntingYard.hpp +++ b/test/test_api/test_shuntingYard.hpp @@ -18,7 +18,7 @@ void shuntingYard_test2() { } void shuntingYard_test3() { - std::string expected_result = "rssi is -23 dbm"; + std::string expected_result = "rssi is -23 dBm"; std::string test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } @@ -126,6 +126,12 @@ void shuntingYard_test20() { TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } +void shuntingYard_test21() { + std::string expected_result = ""; + std::string test_value = "boiler/storagetemp2 == \"\""; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + void run_shuntingYard_tests() { RUN_TEST(shuntingYard_test1); RUN_TEST(shuntingYard_test2); @@ -147,4 +153,5 @@ void run_shuntingYard_tests() { RUN_TEST(shuntingYard_test18); RUN_TEST(shuntingYard_test19); RUN_TEST(shuntingYard_test20); + RUN_TEST(shuntingYard_test21); }