diff --git a/components/nspanel_ha_blueprint/text.h b/components/nspanel_ha_blueprint/text.h index 75a8902..24e802f 100644 --- a/components/nspanel_ha_blueprint/text.h +++ b/components/nspanel_ha_blueprint/text.h @@ -1,6 +1,8 @@ // text.h #pragma once +#include +#include #include #include @@ -34,15 +36,65 @@ namespace nspanel_ha_blueprint { * Note: The destination array is always null-terminated, even if the source string * is truncated to fit into the array. */ - template + template void copyStringToCharArray(char (&dest)[N], const std::string& src) { - static_assert(N > 0, "Destination array size must be greater than 0."); + // Ensure we do not exceed the buffer size, leaving space for the null terminator + size_t length = std::min(src.size(), N - 1); - // Copy up to N - 1 characters to ensure there's room for the null terminator - std::strncpy(dest, src.c_str(), N - 1); + // Copy the string data into the destination buffer + std::strncpy(dest, src.c_str(), length); - // Manually null-terminate the destination array - dest[N - 1] = '\0'; + // Explicitly null-terminate the destination buffer + dest[length] = '\0'; + } + + // Helper function to determine if a character is part of a number + bool isNumberChar(char c) { + return std::isdigit(c) || c == '.' || c == '-' || c == ','; + } + + /** + * Adjusts the decimal separator in a numeric string to a specified character. + * This function identifies and modifies the decimal separator of a number within a string, + * ensuring that only the first occurrence is replaced if it is a valid number followed by + * any non-numeric characters (e.g., units of measurement). If the input string does not + * start with a valid number, it is returned unchanged. + * + * @param input The string potentially containing a numeric value followed by text. + * @param decimalSeparator The character to use as the decimal separator. + * @return A string with the decimal separator adjusted if the input is a valid number, + * otherwise the original input string. + */ + std::string adjustDecimalSeparator(const std::string& input, char decimalSeparator) { + // Immediately return the original string if the desired decimal separator is "." + if (decimalSeparator == '.') { + return input; + } + + // Find the end of the numeric part of the string + size_t numericEnd = 0; + for (; numericEnd < input.size() && isNumberChar(input[numericEnd]); ++numericEnd); + + // Extract the numeric part and the suffix (if any) + std::string numericPart = input.substr(0, numericEnd); + std::string suffix = input.substr(numericEnd); + + // Attempt to convert the numeric part to a double + char* end; + double val = strtod(numericPart.c_str(), &end); + + // Check if conversion was successful (end points to a null terminator if so) + if (end != numericPart.c_str() && *end == '\0') { + // Find and replace only the first occurrence of '.' with the specified decimalSeparator + size_t decimalPointPos = numericPart.find('.'); + if (decimalPointPos != std::string::npos) { + numericPart[decimalPointPos] = decimalSeparator; + } + return numericPart + suffix; + } else { + // If the input is not a number, return it as is + return input; + } } } // namespace nspanel_ha_blueprint diff --git a/docs/api.md b/docs/api.md index 2703b0b..97646a3 100644 --- a/docs/api.md +++ b/docs/api.md @@ -316,6 +316,7 @@ It configures ESPHome with settings that affect overall functionality and user i - `screensaver_time` (bool): Enables or disables the screensaver time display. - `screensaver_time_font` (int): Specifies the font id for the screensaver time display. - `screensaver_time_color` (int[]): Specifies the RGB color array for the screensaver time display. +- `decimal_separator` (string): The char to be used as decimal separator. **Home Assistant Example:** ```yaml @@ -328,6 +329,7 @@ data: screensaver_time: true screensaver_time_font: 11 screensaver_time_color: [165, 42, 42] # Reddish-brown + decimal_separator: "," ``` > [!NOTE] > Replace `` with the specific name of your panel configured in Home Assistant. diff --git a/esphome/nspanel_esphome_core.yaml b/esphome/nspanel_esphome_core.yaml index eea8102..c23a7fc 100644 --- a/esphome/nspanel_esphome_core.yaml +++ b/esphome/nspanel_esphome_core.yaml @@ -307,19 +307,20 @@ api: screensaver_time: bool # Toggles the screensaver time display. screensaver_time_font: int # Specifies the font id for the screensaver time display. screensaver_time_color: int[] # RGB color for the screensaver time display, e.g., [165, 42, 42] for reddish-brown. + decimal_separator: string # The char to be used as decimal separator. then: - script.execute: id: global_settings - blueprint_version: !lambda "return blueprint_version;" - ent_value_xcen: !lambda "return ent_value_xcen;" - mui_please_confirm: !lambda "return mui_please_confirm;" - mui_unavailable: !lambda "return mui_unavailable;" - screensaver_time: !lambda "return screensaver_time;" - screensaver_time_font: !lambda "return screensaver_time_font;" - screensaver_time_color: !lambda "return screensaver_time_color;" + blueprint_version: !lambda return blueprint_version; + ent_value_xcen: !lambda return ent_value_xcen; + mui_please_confirm: !lambda return mui_please_confirm; + mui_unavailable: !lambda return mui_unavailable; + screensaver_time: !lambda return screensaver_time; + screensaver_time_font: !lambda return screensaver_time_font; + screensaver_time_color: !lambda return screensaver_time_color; + decimal_separator: !lambda return decimal_separator; - script.wait: global_settings - - lambda: |- - blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 5)); + - lambda: blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 5)); # Configures NSPanel hardware (buttons, relays, etc.) settings - service: init_hardware @@ -795,7 +796,7 @@ api: icon_color[2]))); if (!(name.empty())) disp1->set_component_text_printf((id + "_label").c_str(), "%s", name.c_str()); - if (!(value.empty())) disp1->set_component_text_printf(id.c_str(), "%s", value.c_str()); + if (!(value.empty())) disp1->set_component_text_printf(id.c_str(), "%s", adjustDecimalSeparator(value, id(mui_decimal_separator)).c_str()); if (value_color.size() == 3) disp1->set_component_font_color(id.c_str(), esphome::display::ColorUtil::color_to_565(esphome::Color(value_color[0], value_color[1], @@ -844,6 +845,11 @@ display: ##### START - GLOBALS CONFIGURATION ##### globals: + - id: mui_decimal_separator + type: char + restore_value: true + initial_value: '.' + ###### Buttons settings ###### # Bit # Settings # # 0 # Left Bt - Enabled # @@ -1164,8 +1170,8 @@ button: ##### START - NUMBER CONFIGURATION ##### number: ##### SCREEN BRIGHTNESS ##### - - name: Display Brightness - id: display_brightness + - id: display_brightness + name: Display Brightness platform: template entity_category: config unit_of_measurement: '%' @@ -1180,18 +1186,17 @@ number: - lambda: |- disp1->send_command_printf("brightness=%i", int(x)); disp1->send_command_printf("settings.brightslider.val=%i", int(x)); - if (current_page->state != "screensaver") - { - disp1->set_backlight_brightness(x/100); - current_brightness->update(); - timer_dim->execute(current_page->state.c_str(), int(timeout_dim->state)); - timer_sleep->execute(current_page->state.c_str(), int(timeout_sleep->state)); - if (current_page->state == "settings") disp1->set_component_text_printf("bright_text", "%i%%", int(x)); - } + if (current_page->state != "screensaver") { + disp1->set_backlight_brightness(x/100); + current_brightness->update(); + timer_dim->execute(current_page->state.c_str(), int(timeout_dim->state)); + timer_sleep->execute(current_page->state.c_str(), int(timeout_sleep->state)); + if (current_page->state == "settings") disp1->set_component_text_printf("bright_text", "%i%%", int(x)); + } ##### SCREEN BRIGHTNESS DIMMED DOWN ##### - - name: Display Brightness Dimdown - id: display_dim_brightness + - id: display_dim_brightness + name: Display Brightness Dimdown platform: template entity_category: config unit_of_measurement: '%' @@ -1206,16 +1211,15 @@ number: - lambda: |- disp1->send_command_printf("brightness_dim=%i", int(x)); disp1->send_command_printf("settings.dimslider.val=%i", int(x)); - if (current_page->state != "screensaver" and (current_brightness->state <= display_dim_brightness->state)) - { - set_brightness->execute(x); - timer_sleep->execute(current_page->state.c_str(), int(timeout_sleep->state)); - if (current_page->state == "settings") disp1->set_component_text_printf("dim_text", "%i%%", int(x)); - } + if (current_page->state != "screensaver" and current_brightness->state <= x) { + set_brightness->execute(x); + timer_sleep->execute(current_page->state.c_str(), int(timeout_sleep->state)); + if (current_page->state == "settings") disp1->set_component_text_printf("dim_text", "%i%%", int(x)); + } ##### SCREEN BRIGHTNESS SLEEP ##### - - name: Display Brightness Sleep - id: display_sleep_brightness + - id: display_sleep_brightness + name: Display Brightness Sleep platform: template entity_category: config unit_of_measurement: '%' @@ -1232,9 +1236,9 @@ number: page_screensaver->execute(); ##### Temperature Correction ##### - - name: Temperature Correction + - id: temperature_correction + name: Temperature Correction platform: template - id: temperature_correction entity_category: config unit_of_measurement: °C min_value: -10 @@ -1860,6 +1864,7 @@ script: screensaver_time: bool screensaver_time_font: int screensaver_time_color: int32_t[] + decimal_separator: string then: - lambda: |- if (id(is_uploading_tft)) global_settings->stop(); @@ -1882,6 +1887,9 @@ script: // Entities pages alignment id(page_entity_value_horizontal_alignment) = ent_value_xcen; + // Decimal separator + if (not decimal_separator.empty()) id(mui_decimal_separator) = decimal_separator[0]; + if (current_page->state != "boot") { // Update current page page_changed->execute(current_page->state.c_str()); diff --git a/hmi/dev/nspanel_eu_code/utilities2.txt b/hmi/dev/nspanel_eu_code/utilities2.txt new file mode 100644 index 0000000..351e7bc --- /dev/null +++ b/hmi/dev/nspanel_eu_code/utilities2.txt @@ -0,0 +1,440 @@ +Page utilities2 + Attributes + ID : 0 + Scope : local + Dragging : 0 + Send Component ID : on press and release + Locked : no + Swide up page ID : disabled + Swide down page ID : disabled + Swide left page ID : disabled + Swide right page ID: disabled + + Events + Preinitialize Event + if(api==0) + { + page home + } + vis unavailable,0 + + Postinitialize Event + sendme + +Text title + Attributes + ID : 1 + Scope : local + Dragging : 0 + Send Component ID : on press and release + Associated Keyboard: none + Text : Power Dashboard + Max. Text Size : 100 + +Text title_icon + Attributes + ID : 2 + Scope : local + Dragging : 0 + Send Component ID : on press and release + Associated Keyboard: none + Text :  + Max. Text Size : 10 + +Text unavailable + Attributes + ID : 5 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : + Max. Text Size : 1 + +Text t1 + Attributes + ID : 6 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 賈 + Max. Text Size : 4 + +Text t2 + Attributes + ID : 7 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text :  + Max. Text Size : 4 + +Text t3 + Attributes + ID : 8 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text :  + Max. Text Size : 4 + +Text t4 + Attributes + ID : 9 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : Green + Max. Text Size : 10 + +Text t5 + Attributes + ID : 10 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : Fossil + Max. Text Size : 10 + +Text t7 + Attributes + ID : 13 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 全 + Max. Text Size : 4 + +Text t8 + Attributes + ID : 14 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : Solar + Max. Text Size : 10 + +Text t9 + Attributes + ID : 16 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 98% + Max. Text Size : 10 + +Text t10 + Attributes + ID : 17 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 2% + Max. Text Size : 10 + +Text t11 + Attributes + ID : 18 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 豈 + Max. Text Size : 4 + +Text t12 + Attributes + ID : 19 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : Battery + Max. Text Size : 10 + +Text t13 + Attributes + ID : 21 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 暑 + Max. Text Size : 4 + +Text t14 + Attributes + ID : 22 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : Heating + Max. Text Size : 10 + +Text t15 + Attributes + ID : 24 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 1.7 kW + Max. Text Size : 10 + +Text t16 + Attributes + ID : 25 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 21.7°C + Max. Text Size : 10 + +Text t17 + Attributes + ID : 26 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 1.1 kW + Max. Text Size : 10 + +Text t18 + Attributes + ID : 27 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : -2.1 kW + Max. Text Size : 10 + +Text t19 + Attributes + ID : 28 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 0.8 kW + Max. Text Size : 10 + +Text t20 + Attributes + ID : 29 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 慎 + Max. Text Size : 4 + +Text t21 + Attributes + ID : 30 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : Wind + Max. Text Size : 10 + +Text t22 + Attributes + ID : 32 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 1.1 kW + Max. Text Size : 10 + +Text t23 + Attributes + ID : 33 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : î—° + Max. Text Size : 4 + +Text t24 + Attributes + ID : 34 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : Car + Max. Text Size : 10 + +Text t25 + Attributes + ID : 36 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 1.1 kW + Max. Text Size : 10 + +Text t0 + Attributes + ID : 37 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : î—° + Max. Text Size : 4 + +Text t6 + Attributes + ID : 38 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : Car + Max. Text Size : 10 + +Text t26 + Attributes + ID : 40 + Scope : local + Dragging : 0 + Send Component ID : disabled + Associated Keyboard: none + Text : 1.1 kW + Max. Text Size : 10 + +Slider h1 + Attributes + ID : 11 + Scope : local + Dragging : 0 + Send Component ID: disabled + Position : 50 + Upper range limit: 100 + Lower range limit: 0 + +Slider h2 + Attributes + ID : 12 + Scope : local + Dragging : 0 + Send Component ID: disabled + Position : 50 + Upper range limit: 100 + Lower range limit: 0 + +Slider h3 + Attributes + ID : 15 + Scope : local + Dragging : 0 + Send Component ID: disabled + Position : 50 + Upper range limit: 100 + Lower range limit: 0 + +Slider h4 + Attributes + ID : 20 + Scope : local + Dragging : 0 + Send Component ID: disabled + Position : 50 + Upper range limit: 100 + Lower range limit: 0 + +Slider h5 + Attributes + ID : 23 + Scope : local + Dragging : 0 + Send Component ID: disabled + Position : 50 + Upper range limit: 100 + Lower range limit: 0 + +Slider h6 + Attributes + ID : 31 + Scope : local + Dragging : 0 + Send Component ID: disabled + Position : 50 + Upper range limit: 100 + Lower range limit: 0 + +Slider h7 + Attributes + ID : 35 + Scope : local + Dragging : 0 + Send Component ID: disabled + Position : 50 + Upper range limit: 100 + Lower range limit: 0 + +Slider h0 + Attributes + ID : 39 + Scope : local + Dragging : 0 + Send Component ID: disabled + Position : 50 + Upper range limit: 100 + Lower range limit: 0 + +Button button_back + Attributes + ID : 3 + Scope : local + Dragging : 0 + Send Component ID: on press and release + State : unpressed + Text : î…˜ + Max. Text Size : 3 + + Events + Touch Press Event + page back_page_id + +Timer wakeup_timer + Attributes + ID : 4 + Scope : local + Period (ms): 100 + Enabled : yes + + Events + Timer Event + if(dim + *SYSTEM settings* + + *Choose the decimal separator for displaying numbers.* + + *For custom separators not listed, please type your preferred character directly.* + default: '.' + selector: + select: + multiple: false + custom_value: true + options: + - label: '. (Dot)' + value: '.' + - label: ', (Comma)' + value: ',' + ##### TFT Folder ##### tft_path: name: Nextion TFT File Folder (Optional) @@ -7251,6 +7270,7 @@ action: screensaver_time: '{{ screensaver_display_time if screensaver_display_time is boolean else false }}' screensaver_time_font: '{{ int(screensaver_display_time_font_size) }}' screensaver_time_color: '{{ screensaver_display_time_font_color if screensaver_display_time_font_color is sequence else [64, 64, 64] }}' + decimal_separator: !input decimal_separator continue_on_error: true ##### Update Date & Time before showing the Home page #####