diff --git a/interface/package.json b/interface/package.json index 5a8542f33..08e1741f9 100644 --- a/interface/package.json +++ b/interface/package.json @@ -41,7 +41,7 @@ "typescript": "^5.8.2" }, "devDependencies": { - "@babel/core": "^7.26.9", + "@babel/core": "^7.26.10", "@eslint/js": "^9.22.0", "@preact/compat": "^18.3.1", "@preact/preset-vite": "^2.10.1", diff --git a/interface/src/app/main/CustomEntitiesDialog.tsx b/interface/src/app/main/CustomEntitiesDialog.tsx index a821c9fb8..0925452f3 100644 --- a/interface/src/app/main/CustomEntitiesDialog.tsx +++ b/interface/src/app/main/CustomEntitiesDialog.tsx @@ -61,7 +61,8 @@ const CustomEntitiesDialog = ({ setEditItem({ ...selectedItem, device_id: selectedItem.device_id.toString(16).toUpperCase(), - type_id: selectedItem.type_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 }); } }, [open, selectedItem]); @@ -82,6 +83,9 @@ 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') { + editItem.factor = parseInt(editItem.factor, 16); + } onSave(editItem); } catch (error) { setFieldErrors(error as ValidateFieldsError); @@ -315,7 +319,7 @@ const CustomEntitiesDialog = ({ exprToTokens(const std::string & expr) { } else if (strncmp(p, "abs", 3) == 0) { p += 2; tokens.emplace_back(Token::Type::Unary, "a", 5); + } else if (strncmp(p, "ln", 2) == 0) { + p += 1; + tokens.emplace_back(Token::Type::Unary, "l", 5); } else if (strncmp(p, "log", 3) == 0) { p += 2; - tokens.emplace_back(Token::Type::Unary, "l", 5); + tokens.emplace_back(Token::Type::Unary, "g", 5); } else if (strncmp(p, "exp", 3) == 0) { p += 2; tokens.emplace_back(Token::Type::Unary, "e", 5); @@ -93,6 +96,9 @@ std::deque exprToTokens(const std::string & expr) { } else if (strncmp(p, "pow", 3) == 0) { p += 2; tokens.emplace_back(Token::Type::Unary, "p", 5); + } else if (strncmp(p, "tohex", 5) == 0) { + p += 4; + tokens.emplace_back(Token::Type::Unary, "x", 5); } else if (strncmp(p, "hex", 3) == 0) { p += 2; tokens.emplace_back(Token::Type::Unary, "h", 5); @@ -102,7 +108,7 @@ std::deque exprToTokens(const std::string & expr) { ++p; } const auto s = std::string(b, p); - tokens.emplace_back(Token::Type::String, s, -2); + tokens.emplace_back(Token::Type::String, s, -3); --p; } else if (*p == '"') { ++p; @@ -132,7 +138,7 @@ std::deque exprToTokens(const std::string & expr) { ++p; } const auto s = std::string(b, p); - tokens.emplace_back(Token::Type::Number, s, -4); + tokens.emplace_back(Token::Type::Number, s, -2); --p; } else { Token::Type token = Token::Type::Operator; @@ -203,7 +209,7 @@ std::deque exprToTokens(const std::string & expr) { precedence = 1; token = Token::Type::Compare; } else { - precedence = 1; + precedence = 2; token = Token::Type::Unary; } break; @@ -359,9 +365,6 @@ std::string commands(std::string & expr, bool quotes = true) { if (e == std::string::npos) { e = expr.length(); } - while (e > 0 && expr[e - 1] == ' ') { // remove blanks from end - e--; - } char cmd[COMMAND_MAX_LENGTH]; size_t l = e - f; if (l >= sizeof(cmd) - 1) { @@ -420,6 +423,14 @@ std::string to_string(double d) { return s; } +// number to hex string +std::string to_hex(uint32_t i) { + char c[10]; + snprintf(c, 10, "%02X", i); + std::string s = c; + return s; +} + // RPN calculator std::string calculate(const std::string & expr) { std::string expr_new = expr; @@ -459,46 +470,57 @@ std::string calculate(const std::string & expr) { } const auto rhs = stack.back(); stack.pop_back(); + if (token.str[0] == '!') { + if (to_logic(rhs) < 0) { + } + if (to_logic(rhs) >= 0) { + stack.push_back(to_logic(rhs) == 0 ? "1" : "0"); + } else if (isnum(rhs)) { + stack.push_back(std::stod(rhs) == 0 ? "1" : "0"); + } else { + emsesp::EMSESP::logger().warning("missing operator"); + return ""; + } + break; + } + auto rhd = std::stod(rhs); switch (token.str[0]) { default: return ""; break; case 'm': // Special operator name for unary '-' - if (!isnum(rhs)) { - return ""; - } - stack.push_back(to_string(-1 * std::stod(rhs))); - break; - case '!': - if (to_logic(rhs) < 0) { - return ""; - } - stack.push_back(to_logic(rhs) == 0 ? "1" : "0"); + stack.push_back(to_string(-1 * rhd)); break; case 'i': - stack.push_back(to_string(std::stoi(rhs))); + stack.push_back(to_string(static_cast(rhd))); break; case 'r': - stack.push_back(to_string(std::round(std::stod(rhs)))); + stack.push_back(to_string(std::round(rhd))); break; case 'a': - stack.push_back(to_string(std::abs(std::stod(rhs)))); + stack.push_back(to_string(std::abs(rhd))); break; case 'e': - stack.push_back(to_string(std::exp(std::stod(rhs)))); + stack.push_back(to_string(std::exp(rhd))); break; case 'l': - stack.push_back(to_string(std::log(std::stod(rhs)))); + stack.push_back(to_string(std::log(rhd))); + break; + case 'g': + stack.push_back(to_string(std::log10(rhd))); break; case 's': - stack.push_back(to_string(std::sqrt(std::stod(rhs)))); + stack.push_back(to_string(std::sqrt(rhd))); break; case 'p': - stack.push_back(to_string(std::pow(std::stod(rhs), 2))); + stack.push_back(to_string(std::pow(rhd, 2))); break; case 'h': stack.push_back(to_string(std::stoi(rhs, 0, 16))); break; + case 'x': + stack.push_back(to_hex(static_cast(rhd))); + break; } } break; case Token::Type::Compare: { @@ -585,38 +607,40 @@ std::string calculate(const std::string & expr) { } break; case Token::Type::Operator: { // binary operators - if (stack.empty() || !isnum(stack.back())) { + if (stack.size() < 2) { return ""; } - const auto rhs = std::stod(stack.back()); + const auto rhs = stack.back(); stack.pop_back(); - if (stack.empty() || !isnum(stack.back())) { - return ""; + const auto lhs = stack.back(); + stack.pop_back(); + if (token.str[0] == '+' && (!isnum(rhs) || !isnum(lhs))) { + stack.push_back(lhs + rhs); + break; } - const auto lhs = std::stod(stack.back()); - stack.pop_back(); - + auto lhd = std::stod(lhs); + auto rhd = std::stod(rhs); switch (token.str[0]) { default: return ""; break; case '^': - stack.push_back(to_string(pow(lhs, rhs))); + stack.push_back(to_string(pow(lhd, rhd))); break; case '*': - stack.push_back(to_string(lhs * rhs)); + stack.push_back(to_string(lhd * rhd)); break; case '/': - stack.push_back(to_string(lhs / rhs)); + stack.push_back(to_string(lhd / rhd)); break; case '%': - stack.push_back(std::to_string(static_cast(lhs) % static_cast(rhs))); + stack.push_back(std::to_string(static_cast(lhd) % static_cast(rhd))); break; case '+': - stack.push_back(to_string(lhs + rhs)); + stack.push_back(to_string(lhd + rhd)); break; case '-': - stack.push_back(to_string(lhs - rhs)); + stack.push_back(to_string(lhd - rhd)); break; } } break; diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 985e21008..9844ff65e 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -105,6 +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; } else if (entityItem.value_type == DeviceValueType::BOOL) { entityItem.value = EMS_VALUE_DEFAULT_BOOL; entityItem.uom = 0;