From 01005b586376424cba5d5c530a8fbf9453fd0b77 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 22 Feb 2024 23:19:34 +0100 Subject: [PATCH] Memory optimization & external components Helps with #1686 - This is deprecating Relay local fallback switches (replaced by globals) to save memory. - Using reboot timeout from ESPHome instead of custom engine - Do not change page is already there. - Use of external components to streamline some repetitive code - API always transfer colors in RGB array to keep consistency --- ReleaseNotes.md | 11 +- docs/api.md | 17 +- docs/customization.md | 29 +- .../nspanel_ha_blueprint/__init__.py | 18 + .../nspanel_ha_blueprint/page_names.h | 67 ++ .../components/nspanel_ha_blueprint/relays.h | 44 ++ .../components/nspanel_ha_blueprint/text.h | 48 ++ .../nspanel_ha_blueprint/versioning.h | 25 + esphome/nspanel_esphome_addon_upload_tft.yaml | 7 +- esphome/nspanel_esphome_advanced.yaml | 17 + esphome/nspanel_esphome_core.yaml | 649 ++++++------------ hmi/nspanel_eu.HMI | Bin 15035221 -> 15035221 bytes hmi/nspanel_us_land.HMI | Bin 14681699 -> 14741225 bytes hmi/nspanel_us_land.tft | Bin 7351804 -> 7471676 bytes hmi/nspanel_us_land_code/utilities.txt | 430 ++++++++++++ nspanel_blueprint.yaml | 18 +- prebuilt/nspanel_esphome_prebuilt.yaml | 31 +- 17 files changed, 939 insertions(+), 472 deletions(-) create mode 100644 esphome/components/nspanel_ha_blueprint/__init__.py create mode 100644 esphome/components/nspanel_ha_blueprint/page_names.h create mode 100644 esphome/components/nspanel_ha_blueprint/relays.h create mode 100644 esphome/components/nspanel_ha_blueprint/text.h create mode 100644 esphome/components/nspanel_ha_blueprint/versioning.h create mode 100644 hmi/nspanel_us_land_code/utilities.txt diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 8bc3d9b..d89f318 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -63,17 +63,22 @@ please look at "[Customizations - Remove non-essential components](docs/customiz 4. **15s hardware button press no longer restarts the device** as the benefits of this were too small compared to leaving the button available for other uses. If you still want the 15s restart behaviour, you can look at "[Customizations - Restart with 15s button press](docs/customization.md#restart-with-15s-button-press) and, in addition to that, the [reset pin in the bottom of your panel](docs/pics/eu_reset_button.png) can be used. +5. **Relay Fallback Switches Removed**: These switches have been deprecated and hidden for some time and are now fully removed from the code. +If you have used any customizations to expose these switches, please refer to the updated guide in "[Customizations - Expose Relay Fallback Switch](docs/customization.md#expose-relay-fallback-switch)", +or you can use a call to the service `esphome.xxxxx_init_relays` to set it accordingly without the additional memory consumption related to creating additional switches. ## Overview of noteworthy changes -1. New default framework +1. Transition to ESP-IDF as Default Framework 2. Performance improvements 3. New API documentation 4. Selectable font size for screensaver time display ## Details of noteworthy changes -### 1. New default framework -Some text +### 1. Transition to ESP-IDF as Default Framework +As previously announced, we have now transitioned to ESP-IDF as our default framework starting with this release. This change aligns with our commitment to provide a robust and future-ready platform, offering enhanced performance, direct hardware access, and access to the latest developments from Espressif. For those who have prepared for this transition, we look forward to the new possibilities this brings to your projects. For users wishing to continue with the Arduino framework, instructions have been provided on explicitly setting your framework preference. We are here to assist through our community support channels for any questions or assistance needed. +For the ones previously using the default framework (without any explicity framework especification on your panel's yaml), remember you have to flash your device using a serial cable this time, otherwise you can face some issues. +More information about this transition can be found in our discussion here: https://github.com/Blackymas/NSPanel_HA_Blueprint/discussions/1756 ### 2. Performance improvements Some text diff --git a/docs/api.md b/docs/api.md index 73dbab4..be3023e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -426,11 +426,11 @@ It tailors ESPHome's relay operations for specific use cases, including local co **Parameters:** - `relay1_local_control` (bool): Enables or disables local control for Relay 1. - `relay1_icon` (string): Icon codepoint from [HASwitchPlate Material Design Icons](https://htmlpreview.github.io/?https://github.com/jobr99/Generate-HASP-Fonts/blob/master/cheatsheet.html) for Relay 1. -- `relay1_icon_color` (int): Sets the [RGB565 color number](https://rgbcolorpicker.com/565) for Relay 1's icon. +- `relay1_icon_color` (int[]): The RGB color array for Relay 1's icon. - `relay1_fallback` (bool): Determines the fallback state for Relay 1 in case of communication loss. - `relay2_local_control` (bool): Enables or disables local control for Relay 2. - `relay2_icon` (string): Icon codepoint from [HASwitchPlate Material Design Icons](https://htmlpreview.github.io/?https://github.com/jobr99/Generate-HASP-Fonts/blob/master/cheatsheet.html) for Relay 2. -- `relay2_icon_color` (int): Sets the [RGB565 color number](https://rgbcolorpicker.com/565) for Relay 2's icon. +- `relay2_icon_color` (int[]): The RGB color array for Relay 2's icon. - `relay2_fallback` (bool): Determines the fallback state for Relay 2 in case of communication loss. **Home Assistant Example:** @@ -438,23 +438,18 @@ It tailors ESPHome's relay operations for specific use cases, including local co service: esphome._init_relays data: relay1_local_control: true - relay1_icon: "\uE3A5" # Example for mdi:numeric-1-box-outline - relay1_icon_color: 63488 # Red in 16-bit color (0xF800) + relay1_icon: "\uE3A5" # Example for mdi:numeric-1-box-outline + relay1_icon_color: [248, 0, 0] # Red relay1_fallback: false relay2_local_control: true - relay2_icon: "\uE3A8" # Example for mdi:numeric-2-box-outline - relay2_icon_color: 2016 # Green in 16-bit color (0x07E0) + relay2_icon: "\uE3A8" # Example for mdi:numeric-2-box-outline + relay2_icon_color: [0, 252, 0] # Green relay2_fallback: true ``` - > [!NOTE] > Replace `` with the specific name of your panel configured in Home Assistant. > This service initializes relay settings based on the provided parameters, customizing relay functionality and presentation as defined in the blueprint. -> [!IMPORTANT] -> Colors here are in RGB565 numeric (decimal) format. You can use a [RGB565 color picker](https://rgbcolorpicker.com/565) to convert from RGB. - - ### Notification Clear Service: `notification_clear` Removes any displayed notification from the screen, allowing the display to return to its normal state or view. diff --git a/docs/customization.md b/docs/customization.md index 9f85f56..d99e854 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -663,10 +663,35 @@ Subsequent activations will trigger `light.toggle` from the blueprint, as this f ```yaml # Expose relay local control switch to Home Assistant switch: - - id: !extend relay1_local + - name: Relay 1 Local + platform: template + id: relay1_local + entity_category: config internal: false - - id: !extend relay2_local + lambda: |- + return (id(relay_settings) & nspanel_ha_blueprint::RelaySettings::Relay1_Local); + turn_on_action: + - lambda: nspanel_ha_blueprint::update_relay_setting(id(relay_settings), true, RelaySettings::Relay1_Local); + on_turn_on: + - logger.log: "Relay 1 Local turned On!" + turn_off_action: + - lambda: nspanel_ha_blueprint::update_relay_setting(id(relay_settings), false, RelaySettings::Relay1_Local); + on_turn_off: + - logger.log: "Relay 1 Local turned Off!" + - name: Relay 2 Local + platform: template + id: relay2_local + entity_category: config internal: false + lambda: return (id(relay_settings) & nspanel_ha_blueprint::RelaySettings::Relay2_Local); + turn_on_action: + - lambda: nspanel_ha_blueprint::update_relay_setting(id(relay_settings), true, RelaySettings::Relay2_Local); + on_turn_on: + - logger.log: "Relay 2 Local turned On!" + turn_off_action: + - lambda: nspanel_ha_blueprint::update_relay_setting(id(relay_settings), false, RelaySettings::Relay2_Local); + on_turn_off: + - logger.log: "Relay 2 Local turned Off!" ``` ### Relay Interlocking diff --git a/esphome/components/nspanel_ha_blueprint/__init__.py b/esphome/components/nspanel_ha_blueprint/__init__.py new file mode 100644 index 0000000..4753134 --- /dev/null +++ b/esphome/components/nspanel_ha_blueprint/__init__.py @@ -0,0 +1,18 @@ +# __init__.py +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.core import coroutine_with_priority + +CODEOWNERS = ["@edwardtfn"] + +nspanel_ha_blueprint_ns = cg.esphome_ns.namespace('nspanel_ha_blueprint') + +CONFIG_SCHEMA = cv.All( + cv.Schema({}), +) + +@coroutine_with_priority(1.0) + +async def to_code(config): + cg.add_define("USE_NSPANEL_HA_BLUEPRINT") + cg.add_global(nspanel_ha_blueprint_ns.using) diff --git a/esphome/components/nspanel_ha_blueprint/page_names.h b/esphome/components/nspanel_ha_blueprint/page_names.h new file mode 100644 index 0000000..e773391 --- /dev/null +++ b/esphome/components/nspanel_ha_blueprint/page_names.h @@ -0,0 +1,67 @@ +// page_names.h +#pragma once + +#include +#include +#include + +namespace nspanel_ha_blueprint { + + /** + * @file page_names.h + * Defines constants and functions related to page names for "NSPanel HA Blueprint" project.. + */ + + // Constants + /** + * A compile-time constant array containing the names of pages. + * These names correspond to various pages of the Nextion TFT file in use, + * such as settings, home, weather information, and more. + */ + constexpr std::array page_names = { + "home", + "weather01", + "weather02", + "weather03", + "weather04", + "weather05", + "climate", + "settings", + "boot", + "screensaver", + "light", + "cover", + "buttonpage01", + "buttonpage02", + "buttonpage03", + "buttonpage04", + "notification", + "qrcode", + "entitypage01", + "entitypage02", + "entitypage03", + "entitypage04", + "fan", + "alarm", + "keyb_num", + "media_player", + "confirm" + }; + + /** + * Retrieves the index of a given page name within the page_names array. + * + * @param page_name The name of the page to find. + * @return The index of the page_name in the page_names array. If the page_name + * is not found, returns UINT8_MAX as an indicator that no matching page was found. + */ + inline uint8_t get_page_id(const std::string& page_name) { + for (uint8_t i = 0; i < page_names.size(); ++i) { + if (strcmp(page_names[i], page_name.c_str()) == 0) { + return i; // Return the index if found + } + } + return UINT8_MAX; // Return UINT8_MAX if not found + } + +} // namespace nspanel_ha_blueprint \ No newline at end of file diff --git a/esphome/components/nspanel_ha_blueprint/relays.h b/esphome/components/nspanel_ha_blueprint/relays.h new file mode 100644 index 0000000..d2cadf6 --- /dev/null +++ b/esphome/components/nspanel_ha_blueprint/relays.h @@ -0,0 +1,44 @@ +// relays.h +#pragma once + +#include + +namespace nspanel_ha_blueprint { + + /** + * @enum RelaySettings + * Represents the settings for relays as individual bits within a uint8_t value. Each + * enum value corresponds to a specific bit position that represents a distinct setting + * for the relays. This allows for efficient storage and manipulation of multiple relay + * settings within a single byte. + * + * Bits are allocated as follows: + * - Bit 0: Relay 1 - Local control enabled. + * - Bit 1: Relay 1 - Fallback mode enabled. + * - Bits 2-3: Reserved for future use. + * - Bit 4: Relay 2 - Local control enabled. + * - Bit 5: Relay 2 - Fallback mode enabled. + * - Bits 6-7: Reserved for future use. + * + * Usage involves bitwise operations to set, clear, and check these settings within a + * uint8_t variable. This approach enables compact representation and easy manipulation + * of relay settings. + */ + enum RelaySettings { + Relay1_Local = 1 << 0, ///< Bit 0: Enables local control for Relay 1. + Relay1_Fallback = 1 << 1, ///< Bit 1: Enables fallback mode for Relay 1. + // Bits 2 and 3 are reserved for future expansion. + Relay2_Local = 1 << 4, ///< Bit 4: Enables local control for Relay 2. + Relay2_Fallback = 1 << 5, ///< Bit 5: Enables fallback mode for Relay 2. + // Bits 6 and 7 are reserved for future expansion. + }; + + void update_relay_setting(uint8_t& settings, bool condition, RelaySettings flag) { + if (condition) { + settings |= flag; // Set bit + } else { + settings &= ~flag; // Clear bit + } + } + +} // namespace nspanel_ha_blueprint diff --git a/esphome/components/nspanel_ha_blueprint/text.h b/esphome/components/nspanel_ha_blueprint/text.h new file mode 100644 index 0000000..75a8902 --- /dev/null +++ b/esphome/components/nspanel_ha_blueprint/text.h @@ -0,0 +1,48 @@ +// text.h +#pragma once + +#include +#include + +namespace nspanel_ha_blueprint { + + /** + * Copies the contents of a std::string to a fixed-size char array, ensuring + * null termination of the string within the array. This function template + * automatically deduces the size of the destination char array at compile time, + * minimizing the risk of buffer overflow. It's designed for use with fixed-size + * char arrays only, not pointers or dynamically allocated memory. + * + * Template Parameter: + * N - The size of the destination char array. This value is deduced automatically + * and must be greater than 0. + * + * Parameters: + * dest - A reference to the destination char array where the string should be copied. + * The array must have a size that can accommodate the source string plus a + * null terminator. If the source string is longer than the destination array, + * it will be truncated. + * src - The source std::string to copy. This string's contents are copied into the + * destination array up to the array's capacity minus one, to leave space for + * the null terminator. + * + * Usage Example: + * char destination[11]; + * std::string source = "Hello"; + * nspanel_ha_blueprint::copyStringToCharArray(destination, source); + * + * Note: The destination array is always null-terminated, even if the source string + * is truncated to fit into the array. + */ + template + void copyStringToCharArray(char (&dest)[N], const std::string& src) { + static_assert(N > 0, "Destination array size must be greater than 0."); + + // Copy up to N - 1 characters to ensure there's room for the null terminator + std::strncpy(dest, src.c_str(), N - 1); + + // Manually null-terminate the destination array + dest[N - 1] = '\0'; + } + +} // namespace nspanel_ha_blueprint diff --git a/esphome/components/nspanel_ha_blueprint/versioning.h b/esphome/components/nspanel_ha_blueprint/versioning.h new file mode 100644 index 0000000..60f93a7 --- /dev/null +++ b/esphome/components/nspanel_ha_blueprint/versioning.h @@ -0,0 +1,25 @@ +// versioning.h +#pragma once +#include // For sscanf + +namespace nspanel_ha_blueprint { + + /** + * Compares two version strings by major and minor version numbers. + * Assumes version strings are in the format "major.minor". + * + * @param version1 First version string to compare. + * @param version2 Second version string to compare. + * @return true if the major and minor versions are equal, false otherwise. + */ + inline bool compare_versions(const char* version1, const char* version2) { + int major1 = 0, minor1 = 0; + int major2 = 0, minor2 = 0; + + sscanf(version1, "%d.%d", &major1, &minor1); + sscanf(version2, "%d.%d", &major2, &minor2); + + return (major1 == major2) && (minor1 == minor2); + } + +} // namespace nspanel_ha_blueprint diff --git a/esphome/nspanel_esphome_addon_upload_tft.yaml b/esphome/nspanel_esphome_addon_upload_tft.yaml index be61ab6..a1fa1cd 100644 --- a/esphome/nspanel_esphome_addon_upload_tft.yaml +++ b/esphome/nspanel_esphome_addon_upload_tft.yaml @@ -138,8 +138,11 @@ script: disp1->hide_component("bt_accept"); disp1->hide_component("bt_clear"); } - disp1->set_component_text_printf("confirm.title", "Upload TFT\\r%s", - id(framework) == 1 ? "Arduino" : (id(framework) == 2 ? "ESP-IDF" : "Unknown")); + #ifdef ARDUINO + disp1->set_component_text_printf("confirm.title", "Upload TFT\\rArduino"); + #elif defined(USE_ESP_IDF) + disp1->set_component_text_printf("confirm.title", "Upload TFT\\rESP-IDF"); + #endif page_id->update(); - id: report_settings diff --git a/esphome/nspanel_esphome_advanced.yaml b/esphome/nspanel_esphome_advanced.yaml index 675a365..a97c37a 100644 --- a/esphome/nspanel_esphome_advanced.yaml +++ b/esphome/nspanel_esphome_advanced.yaml @@ -7,6 +7,13 @@ ##### ATTENTION: This will add advanced elements to the core system and requires the core part. ##### ##################################################################################################### --- +substitutions: + ############################## + ## Change only in your ## + ## local yaml substitutions ## + web_password: ${wifi_password} + ############################## + button: - name: Exit reparse platform: template @@ -19,6 +26,8 @@ button: - logger.log: "Button pressed: Exit reparse" - script.execute: exit_reparse +captive_portal: + script: - id: exit_reparse mode: restart @@ -85,4 +94,12 @@ time: then: - component.update: api_timestamp - component.update: device_timestamp + +##### Web server ##### +web_server: + id: web_server_std + port: 80 + auth: + username: admin + password: ${web_password} ... diff --git a/esphome/nspanel_esphome_core.yaml b/esphome/nspanel_esphome_core.yaml index a53e3d4..c23280d 100644 --- a/esphome/nspanel_esphome_core.yaml +++ b/esphome/nspanel_esphome_core.yaml @@ -15,7 +15,6 @@ substitutions: ap_password: ${wifi_password} ota_password: ${wifi_password} web_password: ${wifi_password} - wifi_timeout: '15' temp_units: "°C" invalid_cooldown: "100ms" ##### DON'T CHANGE THIS ###### @@ -24,6 +23,10 @@ substitutions: ##### External components ##### external_components: + - source: + type: git + path: https://github.com/Blackymas/NSPanel_HA_Blueprint/esphome/components + ref: dev # To do: Change it for releasing - source: type: git url: https://github.com/edwardtfn/esphome @@ -108,8 +111,6 @@ wifi: then: - script.execute: watchdog -captive_portal: - ##### OTA PASSWORD ##### ota: id: ota_std @@ -118,6 +119,9 @@ ota: reboot_timeout: 3min num_attempts: 3 +##### Adds custom library for NSPanel HA Blueprint project +nspanel_ha_blueprint: + ##### JSON - Used to parse json and for Upload TFT ##### json: @@ -163,19 +167,11 @@ time: - logger.log: "System clock synchronized" - script.execute: refresh_datetime -##### Web server ##### -web_server: - id: web_server_std - port: 80 - auth: - username: admin - password: ${web_password} - ##### START - API CONFIGURATION ##### # yamllint disable rule:comments-indentation api: id: api_server - reboot_timeout: 0s + reboot_timeout: 60min on_client_connected: - script.execute: watchdog on_client_disconnected: @@ -240,7 +236,7 @@ api: disp1->send_command_printf("%sicon.font=%" PRIu32, id.c_str(), icon_font); disp1->set_component_foreground_color(btnbri.c_str(), txt_color); disp1->set_component_foreground_color(btntext.c_str(), txt_color); - set_component_color->execute(btnicon.c_str(), icon_color); + disp1->set_component_font_color(btnicon.c_str(), esphome::display::ColorUtil::color_to_565(esphome::Color(icon_color[0], icon_color[1], icon_color[2]))); disp1->set_component_text_printf(btnicon.c_str(), "%s", icon.c_str()); display_wrapped_text->execute(btntext.c_str(), label.c_str(), 10); if (strcmp(bri.c_str(), "0") != 0) @@ -306,7 +302,8 @@ api: then: - lambda: |- if (!id(is_uploading_tft)) - set_component_color->execute(id, color); + disp1->set_component_font_color(id.c_str(), esphome::display::ColorUtil::color_to_565(esphome::Color(color[0], color[1], color[2]))); + # Component Hide Service # Allows for dynamic interface changes by hiding specified components on the display. @@ -470,7 +467,8 @@ api: - lambda: |- if (!id(is_uploading_tft) and !(id.empty())) { if (!(icon.empty())) disp1->set_component_text_printf("%s_icon", id.c_str(), icon.c_str()); - if (icon_color.size() == 3) set_component_color->execute((id + "_icon").c_str(), icon_color); + if (icon_color.size() == 3) + disp1->set_component_font_color((id + "_icon").c_str(), esphome::display::ColorUtil::color_to_565(esphome::Color(icon_color[0], icon_color[1], icon_color[2]))); bool IsCurrentPage = true; size_t dotPos = id.find("."); if (dotPos != std::string::npos) { @@ -553,9 +551,9 @@ api: # appearance, and interactive components. Facilitates a broad spectrum of personalization options. # # Parameters: - # - date_color (int[]): RGB color array for the date display (RGB565 format). + # - date_color (int[]): RGB color array for the date display. # - time_format (string): Time display format string, utilizing standard formatting symbols. - # - time_color (int[]): RGB color array for the time display (RGB565 format). + # - time_color (int[]): RGB color array for the time display. # - meridiem (string[]): Optional array for AM/PM labels if included in time format. # - chip_font (int): Font Id for chip icons displayed on the "Home" page. # - custom_buttons_font (int): Font Id for icons on custom buttons. @@ -632,14 +630,17 @@ api: // Localization ESP_LOGV(TAG, "Load localization"); id(mui_time_format) = time_format; - id(mui_meridiem) = meridiem; - + if (meridiem.size() == 2) { + id(mui_meridiem)[0] = meridiem[0]; + id(mui_meridiem)[1] = meridiem[1]; + } + // Date/Time colors ESP_LOGV(TAG, "Load date/time colors"); - set_component_color->execute("home.date", date_color); - set_component_color->execute("home.time", time_color); id(home_date_color) = esphome::display::ColorUtil::color_to_565(esphome::Color(date_color[0], date_color[1], date_color[2])); id(home_time_color) = esphome::display::ColorUtil::color_to_565(esphome::Color(time_color[0], time_color[1], time_color[2])); + disp1->set_component_font_color("home.date", id(home_date_color)); + disp1->set_component_font_color("home.time", id(home_time_color)); // Chips icon size ESP_LOGV(TAG, "Chips size"); @@ -667,22 +668,27 @@ api: ESP_LOGV(TAG, "Set Notification button"); disp1->send_command_printf("is_notification=%i", (notification_text->state.empty() and notification_label->state.empty()) ? 0 : 1); disp1->set_component_text_printf("home.bt_notific", "%s", notification_icon.c_str()); - set_component_color->execute("home.bt_notific", notification_unread->state ? notification_icon_color_unread : notification_icon_color_normal); - id(home_notify_icon_color_normal) = notification_icon_color_normal; - id(home_notify_icon_color_unread) = notification_icon_color_unread; + id(home_notify_icon_color_normal) = esphome::display::ColorUtil::color_to_565(esphome::Color(notification_icon_color_normal[0], + notification_icon_color_normal[1], + notification_icon_color_normal[2])); + id(home_notify_icon_color_unread) = esphome::display::ColorUtil::color_to_565(esphome::Color(notification_icon_color_unread[0], + notification_icon_color_unread[1], + notification_icon_color_unread[2])); + disp1->set_component_font_color("home.bt_notific", notification_unread->state ? id(home_notify_icon_color_unread) : id(home_notify_icon_color_normal)); // QRCode button ESP_LOGV(TAG, "Set QRCode button"); disp1->send_command_printf("is_qrcode=%i", qrcode ? 1 : 0); disp1->set_component_text_printf("home.bt_qrcode", "%s", qrcode_icon.c_str()); - set_component_color->execute("home.bt_qrcode", qrcode_icon_color); + disp1->set_component_font_color("home.bt_qrcode", esphome::display::ColorUtil::color_to_565(esphome::Color(qrcode_icon_color[0], qrcode_icon_color[1], qrcode_icon_color[2]))); // Entities pages button ESP_LOGV(TAG, "Set Entities button"); disp1->send_command_printf("is_entities=%i", entities_pages ? 1 : 0); disp1->set_component_text_printf("home.bt_entities", "%s", entities_pages_icon.c_str()); - //set_component_color->execute("home.bt_entities", entities_pages_icon_color); - set_component_color->execute("home.bt_entities", entities_pages_icon_color); + disp1->set_component_font_color("home.bt_entities", esphome::display::ColorUtil::color_to_565(esphome::Color(entities_pages_icon_color[0], + entities_pages_icon_color[1], + entities_pages_icon_color[2]))); blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 1)); } @@ -735,11 +741,11 @@ api: # Parameters: # - relay1_local_control (bool): Enable/disable local control for Relay 1. # - relay1_icon (string): Icon for Relay 1 (e.g., "lightbulb"). - # - relay1_icon_color (int): 16-bit RGB color for Relay 1's icon. Use 63488 for red (0xF800 in hex). + # - relay1_icon_color (int): RGB color array for Relay 1's icon. # - relay1_fallback (bool): Fallback state for Relay 1 in case of communication loss. # - relay2_local_control (bool): Enable/disable local control for Relay 2. # - relay2_icon (string): Icon for Relay 2 (e.g., "power"). - # - relay2_icon_color (int): 16-bit RGB color for Relay 2's icon. Example green color: 2016 (0x07E0 in hex). + # - relay2_icon_color (int): RGB color array for Relay 2's icon. # - relay2_fallback (bool): Fallback state for Relay 2 in case of communication loss. # Example service call: @@ -747,11 +753,11 @@ api: # data: # relay1_local_control: true # relay1_icon: "lightbulb" - # relay1_icon_color: 63488 # Red in 16-bit color + # relay1_icon_color: [248, 0, 0] # Red # relay1_fallback: false # relay2_local_control: true # relay2_icon: "power" - # relay2_icon_color: 2016 # Green in 16-bit color + # relay2_icon_color: [0, 252, 0] # Green # relay2_fallback: true # # NOTE: Replace with your panel's name as configured in Home Assistant. @@ -761,25 +767,45 @@ api: variables: relay1_local_control: bool relay1_icon: string - relay1_icon_color: int + relay1_icon_color: int[] relay1_fallback: bool relay2_local_control: bool relay2_icon: string - relay2_icon_color: int + relay2_icon_color: int[] relay2_fallback: bool then: - - script.execute: - id: relay_settings - relay1_local_control: !lambda "return relay1_local_control;" - relay1_icon: !lambda "return relay1_icon;" - relay1_icon_color: !lambda "return relay1_icon_color;" - relay1_fallback: !lambda "return relay1_fallback;" - relay2_local_control: !lambda "return relay2_local_control;" - relay2_icon: !lambda "return relay2_icon;" - relay2_icon_color: !lambda "return relay2_icon_color;" - relay2_fallback: !lambda "return relay2_fallback;" - - script.wait: relay_settings - lambda: |- + if (!id(is_uploading_tft)) { + using namespace nspanel_ha_blueprint; + using namespace esphome::display; + + // Relay settings + update_relay_setting(id(relay_settings), relay1_local_control, RelaySettings::Relay1_Local); + update_relay_setting(id(relay_settings), relay1_fallback, RelaySettings::Relay1_Fallback); + update_relay_setting(id(relay_settings), relay2_local_control, RelaySettings::Relay2_Local); + update_relay_setting(id(relay_settings), relay2_fallback, RelaySettings::Relay2_Fallback); + + // Relay icons + if (not relay1_icon.empty()) copyStringToCharArray(id(home_relay1_icon), relay1_icon); + if (not relay2_icon.empty()) copyStringToCharArray(id(home_relay2_icon), relay2_icon); + + // Relay icon's colors + if (relay1_icon_color.size() == 3) { + id(home_relay1_icon_color) = ColorUtil::color_to_565(esphome::Color(relay1_icon_color[0], + relay1_icon_color[1], + relay1_icon_color[2])); + disp1->set_component_font_color("home.icon_top_01", id(home_relay1_icon_color)); + } + if (relay2_icon_color.size() == 3) { + id(home_relay2_icon_color) = ColorUtil::color_to_565(esphome::Color(relay2_icon_color[0], + relay2_icon_color[1], + relay2_icon_color[2])); + disp1->set_component_font_color("home.icon_top_02", id(home_relay2_icon_color)); + } + + // Refresh display + refresh_relays->execute(); + } blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 4)); # Notification Clear Service @@ -823,7 +849,7 @@ api: if (!id(is_uploading_tft)) { ESP_LOGV("service.notification_show", "Starting"); - disp1->goto_page("notification"); + goto_page->execute("notification"); disp1->set_component_text_printf("notification.notifi_label", "%s", label.c_str()); display_wrapped_text->execute("notification.notifi_text01", message.c_str(), display_mode->state == 2 ? 23 : 32); @@ -1202,7 +1228,7 @@ api: if (!id(is_uploading_tft)) { disp1->set_component_text_printf("qrcode.qrcode_label", "%s", title.c_str()); disp1->set_component_text_printf("qrcode.qrcode_value", "%s", qrcode.c_str()); - if (show) disp1->goto_page("qrcode"); + if (show) goto_page->execute("qrcode"); blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 2)); } @@ -1271,10 +1297,17 @@ api: - lambda: |- if (!id(is_uploading_tft) and !(id.empty())) { if (!(icon.empty())) disp1->set_component_text_printf("%s_icon", id.c_str(), icon.c_str()); - if (icon_color.size() == 3) set_component_color->execute((id + "_icon").c_str(), icon_color); + if (icon_color.size() == 3) + disp1->set_component_font_color((id + "_icon").c_str(), esphome::display::ColorUtil::color_to_565(esphome::Color(icon_color[0], + icon_color[1], + icon_color[2]))); + if (!(name.empty())) disp1->set_component_text_printf("%s_label", id.c_str(), name.c_str()); if (!(value.empty())) disp1->set_component_text_printf("%s", id.c_str(), value.c_str()); - if (value_color.size() == 3) set_component_color->execute(id.c_str(), value_color); + 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], + value_color[2]))); } # Wake Up Service @@ -1301,7 +1334,7 @@ api: then: - lambda: |- if (not id(is_uploading_tft)) { - if (current_page->state == "screensaver") disp1->goto_page(wakeup_page_name->state.c_str()); + if (current_page->state == "screensaver") goto_page->execute(wakeup_page_name->state.c_str()); if (reset_timer) timer_reset_all->execute(wakeup_page_name->state.c_str()); else { @@ -1332,6 +1365,8 @@ api: } # yamllint enable rule:comments-indentation +debug: + ##### START - DISPLAY START CONFIGURATION ##### display: - id: disp1 @@ -1344,33 +1379,50 @@ display: static const char *const TAG = "display.disp1.on_page"; if (id(is_uploading_tft)) { ESP_LOGD(TAG, "Page changed ignored as a TFT upload is in progress"); - } else if (x > id(page_names).size()) { + } else if (x > page_names.size()) { ESP_LOGW(TAG, "Invalid page index: %i", int(x)); } else { ESP_LOGD(TAG, "Nextion page changed"); - ESP_LOGD(TAG, "New page: %s (%i)" , id(page_names)[x].c_str(), x); + ESP_LOGD(TAG, "New page: %s (%i)" , page_names[x], x); page_id->update(); - if (current_page->state != id(page_names)[x].c_str() or x == 9) { - current_page->publish_state(id(page_names)[x].c_str()); - page_changed->execute(id(page_names)[x].c_str()); + if (current_page->state != page_names[x] or x == 9) { + current_page->publish_state(page_names[x]); + page_changed->execute(page_names[x]); } } on_touch: lambda: |- static const char *const TAG = "display.disp1.on_touch"; ESP_LOGV(TAG, "Nextion touch event detected!"); - ESP_LOGV(TAG, "Page: %s", id(page_names)[page_id].c_str()); + ESP_LOGV(TAG, "Page: %s", page_names[page_id]); ESP_LOGV(TAG, "Component Id: %i", component_id); ESP_LOGV(TAG, "Event type: %s", touch_event ? "Press" : "Release"); - timer_reset_all->execute(id(page_names)[page_id].c_str()); + timer_reset_all->execute(page_names[page_id]); ##### START - GLOBALS CONFIGURATION ##### globals: - ##### Wi-Fi timeout ##### - - id: wifi_timeout - type: uint + + ####### Relay settings ####### + # Bit # Settings # + # 0 # Relay 1 - Local # + # 1 # Relay 1 - Fallback # + # 2 # reserved # + # 3 # reserved # + # 4 # Relay 2 - Local # + # 5 # Relay 2 - Fallback # + # 6 # reserved # + # 7 # reserved # + ############################## + - id: relay_settings + type: uint8_t + restore_value: true + initial_value: '0' + + ##### Versioning ##### + - id: version_blueprint + type: char[10] restore_value: false - initial_value: ${wifi_timeout} + initial_value: '' ##### Is uploading TFT ##### - id: is_uploading_tft @@ -1386,7 +1438,7 @@ globals: ###### Last volume level from Home Assistant ###### - id: last_volume_level - type: uint + type: uint8_t restore_value: false initial_value: '0' @@ -1402,34 +1454,12 @@ globals: restore_value: false initial_value: '0' - ###### Relay fallback even when buttons have other entities? ###### - - id: relay_1_fallback - type: bool - restore_value: true - initial_value: 'false' - - id: relay_2_fallback - type: bool - restore_value: true - initial_value: 'false' - ##### Is embedded thermostat set as main climate entity? ##### - id: is_embedded_thermostat type: bool restore_value: true initial_value: 'false' - ##### Save Display Brightness for NSPanel reboot ##### - - id: display_brightness_global - type: uint - restore_value: true - initial_value: '100' - - ##### Save Display DIM Brightness for NSPanel reboot - - id: display_dim_brightness_global - type: uint - restore_value: true - initial_value: '10' - ##### Is embedded sensor used for indoor temperature? ##### - id: embedded_indoor_temp type: bool @@ -1438,20 +1468,20 @@ globals: ##### Date/time formats ##### - id: home_date_color - type: uint + type: uint16_t restore_value: true initial_value: '65535' - - id: mui_time_format type: std::string restore_value: true + max_restore_data_length: 15 initial_value: '"%H:%M"' - id: home_time_color - type: uint + type: uint16_t restore_value: true initial_value: '65535' - id: mui_meridiem - type: std::vector + type: std::array restore_value: false initial_value: '{"AM", "PM"}' @@ -1467,19 +1497,19 @@ globals: ##### Chips ##### - id: home_chip_font_id - type: uint + type: uint8_t restore_value: true initial_value: '7' #### Custom buttons #### - id: home_custom_buttons_font_id - type: uint + type: uint8_t restore_value: true initial_value: '8' ##### Relay icons ##### - id: home_relay1_icon - type: std::string + type: char[4] restore_value: true initial_value: '' - id: home_relay1_icon_color @@ -1488,7 +1518,7 @@ globals: initial_value: '65535' - id: home_relay2_icon - type: std::string + type: char[4] restore_value: true initial_value: '' - id: home_relay2_icon_color @@ -1497,10 +1527,10 @@ globals: initial_value: '65535' - id: home_notify_icon_color_normal - type: std::vector + type: uint16_t restore_value: false - id: home_notify_icon_color_unread - type: std::vector + type: uint16_t restore_value: false ##### Screensaver ##### @@ -1509,52 +1539,13 @@ globals: restore_value: true initial_value: 'false' - id: screensaver_display_time_font - type: int + type: uint8_t restore_value: true initial_value: '6' - id: screensaver_display_time_color - type: std::vector + type: uint16_t restore_value: false - initial_value: '{64, 64, 64}' - - - id: page_names - type: std::vector - restore_value: false - initial_value: - '{ - "home", - "weather01", - "weather02", - "weather03", - "weather04", - "weather05", - "climate", - "settings", - "boot", - "screensaver", - "light", - "cover", - "buttonpage01", - "buttonpage02", - "buttonpage03", - "buttonpage04", - "notification", - "qrcode", - "entitypage01", - "entitypage02", - "entitypage03", - "entitypage04", - "fan", - "alarm", - "keyb_num", - "media_player", - "confirm" - }' - - - id: framework - type: uint8_t - restore_value: false - initial_value: '0' # 0 = unknown, 1 = Arduino, 2 = ESP-IDF + initial_value: '16904' - id: page_entity_value_horizontal_alignment type: uint8_t @@ -1590,9 +1581,9 @@ binary_sensor: - if: condition: or: - - switch.is_on: relay1_local + - lambda: return (id(relay_settings) & nspanel_ha_blueprint::RelaySettings::Relay1_Local); - and: - - lambda: !lambda return id(relay_1_fallback); + - lambda: return (id(relay_settings) & nspanel_ha_blueprint::RelaySettings::Relay1_Fallback); - or: - not: - api.connected: @@ -1630,9 +1621,9 @@ binary_sensor: - if: condition: or: - - switch.is_on: relay2_local + - lambda: return (id(relay_settings) & nspanel_ha_blueprint::RelaySettings::Relay2_Local); - and: - - lambda: !lambda return id(relay_2_fallback); + - lambda: return (id(relay_settings) & nspanel_ha_blueprint::RelaySettings::Relay2_Fallback); - or: - not: - api.connected: @@ -1671,6 +1662,8 @@ binary_sensor: publish_initial_state: true entity_category: diagnostic icon: mdi:tablet-dashboard + lambda: |- + return disp1->is_setup(); ##### API connection status - name: Status @@ -1722,10 +1715,9 @@ number: step: 1 restore_value: true optimistic: true - set_action: + on_value: then: - lambda: |- - id(display_brightness_global) = int(x); disp1->send_command_printf("brightness=%i", int(x)); disp1->send_command_printf("settings.brightslider.val=%i", int(x)); if (current_page->state != "screensaver") @@ -1749,13 +1741,12 @@ number: step: 1 restore_value: true optimistic: true - set_action: + on_value: then: - lambda: |- - id(display_dim_brightness_global) = int(x); 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 <= id(display_dim_brightness_global))) + 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)); @@ -1774,10 +1765,9 @@ number: step: 1 restore_value: true optimistic: true - set_action: + on_value: then: - lambda: |- - id(display_dim_brightness_global) = int(x); disp1->send_command_printf("brightness_sleep=%i", int(x)); page_screensaver->execute(); @@ -1795,7 +1785,7 @@ number: restore_value: true internal: false optimistic: true - set_action: + on_value: - logger.log: Temperature correction changed. - delay: 1s - lambda: temp_nspanel->publish_state(temp_nspanel->raw_state); @@ -1813,7 +1803,7 @@ number: optimistic: true icon: mdi:timer unit_of_measurement: "s" - set_action: + on_value: - lambda: timer_page->execute(current_page->state.c_str(), int(x)); - name: Timeout Dimming platform: template @@ -1827,7 +1817,7 @@ number: optimistic: true icon: mdi:timer unit_of_measurement: "s" - set_action: + on_value: - lambda: timer_dim->execute(current_page->state.c_str(), int(x)); - name: Timeout Sleep platform: template @@ -1841,7 +1831,7 @@ number: optimistic: true icon: mdi:timer unit_of_measurement: "s" - set_action: + on_value: - lambda: |- timer_dim->execute(current_page->state.c_str(), int(timeout_dim->state)); timer_sleep->execute(current_page->state.c_str(), int(x)); @@ -1904,6 +1894,10 @@ select: ##### START - SENSOR CONFIGURATION ##### sensor: + - platform: debug + free: + name: "Heap Free" + ##### Blueprint status ##### # Bit # Settings step # # 0 # reserved # @@ -2028,11 +2022,11 @@ sensor: ESP_LOGD(TAG, "New page Id: %i", int(x)); if (id(is_uploading_tft)) { ESP_LOGD(TAG, "Skipping actions as a TFT upload is in progress"); - } else if (x > id(page_names).size()) { + } else if (x > page_names.size()) { ESP_LOGW(TAG, "Invalid page index: %i", int(x)); - } else if (current_page->state != id(page_names)[x].c_str()) { - current_page->publish_state(id(page_names)[x].c_str()); - page_changed->execute(id(page_names)[x].c_str()); + } else if (current_page->state != page_names[x]) { + current_page->publish_state(page_names[x]); + page_changed->execute(page_names[x]); } ##### Display mode (1 = EU, 2 = US, 3 = US Landscape) @@ -2080,12 +2074,12 @@ switch: - wait_until: condition: - lambda: !lambda return (blueprint_status->state > 99); - - lambda: set_component_color->execute("home.bt_notific", id(home_notify_icon_color_unread)); + - lambda: disp1->set_component_font_color("home.bt_notific", id(home_notify_icon_color_unread)); on_turn_off: - wait_until: condition: - lambda: !lambda return (blueprint_status->state > 99); - - lambda: set_component_color->execute("home.bt_notific", id(home_notify_icon_color_normal)); + - lambda: disp1->set_component_font_color("home.bt_notific", id(home_notify_icon_color_normal)); ##### Notification sound ##### - name: Notification sound @@ -2139,38 +2133,12 @@ switch: - lambda: !lambda return disp1->is_setup(); timeout: 20s - lambda: |- - if (id(setup_sequence_completed)) { - nextion_init->publish_state(disp1->is_setup()); - disp1->goto_page(wakeup_page_name->state.c_str()); - } + nextion_init->publish_state(disp1->is_setup()); + goto_page->execute(wakeup_page_name->state.c_str()); on_turn_off: - lambda: |- nextion_init->publish_state(false); - ##### Relay Local control ##### - - name: Relay 1 Local - platform: template - id: relay1_local - entity_category: config - optimistic: true - restore_mode: RESTORE_DEFAULT_OFF - internal: true - on_turn_on: - - logger.log: "Relay 1 Local turned On!" - on_turn_off: - - logger.log: "Relay 1 Local turned Off!" - - name: Relay 2 Local - platform: template - id: relay2_local - entity_category: config - optimistic: true - restore_mode: RESTORE_DEFAULT_OFF - internal: true - on_turn_on: - - logger.log: "Relay 2 Local turned On!" - on_turn_off: - - logger.log: "Relay 2 Local turned Off!" - ##### START - TEXT SENSOR CONFIGURATION ##### text_sensor: ##### Device name - Used by bluepring to find service's names ##### @@ -2277,7 +2245,7 @@ text_sensor: } else if (event == "click" and page == "home" and component == "climate") { detailed_entity->publish_state((id(is_embedded_thermostat)) ? "embedded_climate" : ""); disp1->set_component_value("climate.embedded", id(is_embedded_thermostat) ? 1 : 0); - disp1->goto_page("climate"); + goto_page->execute("climate"); } else if (page == "light" or page == "climate" or page == "notification") { // Generic event auto ha_event = new esphome::api::CustomAPIDevice(); ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", { @@ -2297,7 +2265,7 @@ text_sensor: std::string title = doc["mui"]; if (code_format == "number" and (key == "disarm" or code_arm_req == "1")) { - disp1->goto_page("keyb_num"); + goto_page->execute("keyb_num"); disp1->set_component_value("keyb_num.page_id", 23); //Calling from Alarm page disp1->set_component_text_printf("keyb_num.domain", "%s", page.c_str()); disp1->set_component_text_printf("keyb_num.key", "%s", key.c_str()); @@ -2326,7 +2294,7 @@ text_sensor: service_call_alarm_control_panel->execute(entity.c_str(), key.c_str(), code_format.c_str(), pin.c_str()); } else if (base_domain == "" or base_domain.empty()) base_domain = "home"; - disp1->goto_page(base_domain.c_str()); + goto_page->execute(base_domain.c_str()); } else if (page == "light") ha_call_service->execute("light.turn_on", key.c_str(), value.c_str(), entity.c_str()); else if (page == "media_player") { @@ -2336,54 +2304,13 @@ text_sensor: } ##### Versioning ##### - - id: version_blueprint - name: Version Blueprint - platform: template - entity_category: diagnostic - icon: mdi:tag-text-outline - internal: false - update_interval: never - lambda: |- - return {"unknown"}; - on_value: - - lambda: |- - static const char *const TAG = "text_sensor.version_blueprint"; - ESP_LOGD(TAG, "Blueprint version: %s", x.c_str()); - disp1->set_component_text_printf("boot.bluep_version", "%s", x.c_str()); - if (current_page->state == "boot") { - disp1->send_command_printf("tm_esphome.en=0"); - page_boot->execute(); - timer_reset_all->execute("boot"); - } - check_versions->execute(); - - - id: version_esphome - name: Version ESPHome - platform: template - entity_category: diagnostic - icon: mdi:tag-text-outline - internal: false - lambda: |- - return {"${version}"}; - on_value: - - lambda: |- - static const char *const TAG = "text_sensor.version_esphome"; - ESP_LOGD(TAG, "ESPHome version: %s", x.c_str()); - disp1->set_component_text_printf("boot.esph_version", x.c_str()); - if (current_page->state == "boot") { - disp1->send_command_printf("tm_esphome.en=0"); - page_boot->execute(); - timer_reset_all->execute("boot"); - } - check_versions->execute(); - - id: version_tft name: Version TFT platform: nextion component_name: boot.tft_version - entity_category: diagnostic - icon: mdi:tag-text-outline - internal: false + #entity_category: diagnostic + #icon: mdi:tag-text-outline + internal: true update_interval: never on_value: - lambda: |- @@ -2420,37 +2347,17 @@ script: - wait_until: condition: - lambda: |- - auto compareVersions = [](const char* version1, const char* version2) -> bool - { - int major1 = 0, minor1 = 0; - int major2 = 0, minor2 = 0; - - sscanf(version1, "%d.%d", &major1, &minor1); - sscanf(version2, "%d.%d", &major2, &minor2); - - return (major1 == major2) && (minor1 == minor2); - }; - return (compareVersions("${version}", version_tft->state.c_str()) and compareVersions("${version}", version_blueprint->state.c_str())); + return (compare_versions("${version}", version_tft->state.c_str()) and compare_versions("${version}", id(version_blueprint))); timeout: 60s - lambda: |- if (id(is_uploading_tft)) check_versions->stop(); static const char *const TAG = "script.check_versions"; - auto compareVersions = [](const char* version1, const char* version2) -> bool - { - int major1 = 0, minor1 = 0; - int major2 = 0, minor2 = 0; - - sscanf(version1, "%d.%d", &major1, &minor1); - sscanf(version2, "%d.%d", &major2, &minor2); - - return (major1 == major2) && (minor1 == minor2); - }; ESP_LOGD(TAG, "Versions:"); ESP_LOGD(TAG, " ESPHome: ${version}"); ESP_LOGD(TAG, " TFT: %s", version_tft->state.c_str()); - if (not compareVersions("${version}", version_tft->state.c_str())) ESP_LOGE(TAG, "TFT version mismatch!"); - ESP_LOGD(TAG, " Blueprint: %s", version_blueprint->state.c_str()); - if (not compareVersions("${version}", version_blueprint->state.c_str())) ESP_LOGE(TAG, "Blueprint version mismatch!"); + if (not compare_versions("${version}", version_tft->state.c_str())) ESP_LOGE(TAG, "TFT version mismatch!"); + ESP_LOGD(TAG, " Blueprint: %s", id(version_blueprint)); + if (not compare_versions("${version}", id(version_blueprint))) ESP_LOGE(TAG, "Blueprint version mismatch!"); auto ha_event = new esphome::api::CustomAPIDevice(); ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", @@ -2458,7 +2365,7 @@ script: {"type", "version"}, {"tft", version_tft->state.c_str()}, {"esphome", "${version}"}, - {"blueprint", version_blueprint->state.c_str()} + {"blueprint", id(version_blueprint)} }); - id: display_embedded_temp @@ -2527,7 +2434,9 @@ script: static const char *const TAG = "script.global_settings"; // Blueprint version ESP_LOGV(TAG, "Check Blueprint version"); - version_blueprint->publish_state(blueprint_version.c_str()); + nspanel_ha_blueprint::copyStringToCharArray(id(version_blueprint), blueprint_version); + disp1->set_component_text_printf("boot.bluep_version", "%s", blueprint_version.c_str()); + if (current_page->state == "boot") page_boot->execute(); check_versions->execute(); // Embedded thermostat @@ -2547,7 +2456,7 @@ script: ESP_LOGV(TAG, "Setup screensaver page"); id(screensaver_display_time) = screensaver_time; id(screensaver_display_time_font) = screensaver_time_font; - id(screensaver_display_time_color) = screensaver_time_color; + id(screensaver_display_time_color) = esphome::display::ColorUtil::color_to_565(esphome::Color(screensaver_time_color[0], screensaver_time_color[1], screensaver_time_color[2])); page_screensaver->execute(); // Entities pages alignment @@ -2592,12 +2501,21 @@ script: rtttl: 'two short:d=4,o=5,b=100:16e6,16e6' - lambda: |- ESP_LOGD("script.global_settings", "Jump to wake-up page: %s", wakeup_page_name->state.c_str()); - disp1->goto_page(wakeup_page_name->state.c_str()); + goto_page->execute(wakeup_page_name->state.c_str()); timer_reset_all->execute(wakeup_page_name->state.c_str()); - lambda: |- ESP_LOGV("script.global_settings", "Finished"); + - id: goto_page + mode: restart + parameters: + page: string + then: + - lambda: |- + if (current_page->state != page) + disp1->goto_page(page.c_str()); + - id: ha_button mode: parallel parameters: @@ -2667,7 +2585,7 @@ script: notification_text->publish_state(""); notification_unread->turn_off(); refresh_notification->execute(); - if (current_page->state == "notification") disp1->goto_page("home"); + if (current_page->state == "notification") goto_page->execute("home"); } - id: entity_details_show @@ -2683,9 +2601,8 @@ script: if (page == "alarm_control_panel") page = "alarm"; detailed_entity->publish_state(entity); if (page == "alarm_control_panel") page = "alarm"; - std::string cmd_page = std::string("page ") + page.c_str(); - disp1->send_command_printf(cmd_page.c_str()); - set_page_id->execute("back_page_id", back_page.c_str()); + disp1->send_command_printf("page %s", page.c_str()); + disp1->send_command_printf("back_page_id=%" PRIu8, get_page_id(back_page.c_str())); if (page == "climate") disp1->set_component_value("embedded", (entity == "embedded_climate") ? 1 : 0); } @@ -2701,8 +2618,11 @@ script: static const char *const TAG = "script.page_blank"; ESP_LOGV(TAG, "Construct blank page"); disp1->set_component_text_printf("esp_version", "ESP: ${version}"); // ESPHome version - disp1->set_component_text_printf("framework", "%s", id(framework) == 1 ? "Arduino" : - (id(framework) == 2 ? "ESP-IDF" : "Unknown")); // ESPHome framework + #ifdef ARDUINO + disp1->set_component_text_printf("framework", "Arduino"); + #elif defined(USE_ESP_IDF) + disp1->set_component_text_printf("framework", "ESP-IDF"); + #endif disp1->send_command_printf("tm_esphome.en=0"); - id: page_boot @@ -2714,10 +2634,12 @@ script: set_brightness->execute(100); disp1->set_component_text_printf("boot.esph_version", "${version}"); // ESPHome version - disp1->set_component_text_printf("framework", "%s", id(framework) == 1 ? "Arduino" : - (id(framework) == 2 ? "ESP-IDF" : "Unknown")); // ESPHome framework + #ifdef ARDUINO + disp1->set_component_text_printf("framework", "Arduino"); + #elif defined(USE_ESP_IDF) + disp1->set_component_text_printf("framework", "ESP-IDF"); + #endif disp1->send_command_printf("tm_esphome.en=0"); - // disp1->show_component("bt_reboot"); - id: page_buttonpage mode: restart @@ -2758,7 +2680,7 @@ script: static const char *const TAG = "script.page_changed"; // Go to boot page if not initiated - if (page != "boot" and not nextion_init->state) disp1->goto_page("boot"); + if (not nextion_init->state) goto_page->execute("boot"); // Reset globals if (page != "alarm" && //DEBOG page != "climate" && @@ -2921,11 +2843,10 @@ script: if (current_page->state == "screensaver" and not id(is_uploading_tft)) { static const char *const TAG = "script.page_screensaver"; ESP_LOGV(TAG, "Updating screensaver page"); - set_page_id->execute("back_page_id", wakeup_page_name->state.c_str()); - // disp1->send_command_printf("back_page_id=%i", id(wakeup_page_id)); + disp1->send_command_printf("back_page_id=%" PRIu8, get_page_id(wakeup_page_name->state.c_str())); if (id(screensaver_display_time)) { disp1->send_command_printf("screensaver.text.font=%i", id(screensaver_display_time_font)); - set_component_color->execute("screensaver.text",id(screensaver_display_time_color)); + disp1->set_component_font_color("screensaver.text", id(screensaver_display_time_color)); disp1->show_component("text"); refresh_datetime->execute(); } else { @@ -3025,7 +2946,7 @@ script: static const char *const TAG = "script.refresh_notification"; bool is_notification = ((not notification_text->state.empty()) or (not notification_label->state.empty())); ESP_LOGV(TAG, "Notification: %s", YESNO(is_notification)); - disp1->send_command_printf("is_notification=%i", is_notification ? 0 : 1); + disp1->send_command_printf("is_notification=%i", is_notification ? 1 : 0); if (current_page->state == "home") { if (is_notification) { disp1->show_component("bt_notific"); @@ -3037,18 +2958,18 @@ script: condition: - lambda: return (blueprint_status->state > 99); - lambda: |- - set_component_color->execute("home.bt_notific", notification_unread->state ? id(home_notify_icon_color_unread) : id(home_notify_icon_color_normal)); + disp1->set_component_font_color("home.bt_notific", notification_unread->state ? id(home_notify_icon_color_unread) : id(home_notify_icon_color_normal)); - id: refresh_relays mode: restart then: - lambda: |- // Chips - Relays - disp1->set_component_text_printf("home.icon_top_01", "%s", (relay_1->state) ? id(home_relay1_icon).c_str() : "\uFFFF"); - disp1->set_component_text_printf("home.icon_top_02", "%s", (relay_2->state) ? id(home_relay2_icon).c_str() : "\uFFFF"); + disp1->set_component_text_printf("home.icon_top_01", "%s", (relay_1->state) ? id(home_relay1_icon) : "\uFFFF"); + disp1->set_component_text_printf("home.icon_top_02", "%s", (relay_2->state) ? id(home_relay2_icon) : "\uFFFF"); // Hardware buttons bars - Fallback mode - if (relay1_local->state) disp1->send_command_printf("home.left_bt_pic.val=%i", (relay_1->state) ? 1 : 0); - if (relay2_local->state) disp1->send_command_printf("home.right_bt_pic.val=%i", (relay_2->state) ? 1 : 0); + if (id(relay_settings) & nspanel_ha_blueprint::RelaySettings::Relay1_Local) disp1->send_command_printf("home.left_bt_pic.val=%i", (relay_1->state) ? 1 : 0); + if (id(relay_settings) & nspanel_ha_blueprint::RelaySettings::Relay2_Local) disp1->send_command_printf("home.right_bt_pic.val=%i", (relay_2->state) ? 1 : 0); - id: refresh_wifi_icon mode: restart @@ -3067,66 +2988,14 @@ script: "\uE5A9"); // mdi:wifi-off } - - id: relay_settings - mode: restart - parameters: - relay1_local_control: bool - relay1_icon: string - relay1_icon_color: int - relay1_fallback: bool - relay2_local_control: bool - relay2_icon: string - relay2_icon_color: int - relay2_fallback: bool - then: - - if: - condition: - lambda: 'return id(is_uploading_tft);' - then: - - script.stop: relay_settings - - lambda: |- - static const char *const TAG = "script.relay_settings"; - // Relays - ESP_LOGV(TAG, "Setup relays"); - relay1_local->publish_state(relay1_local_control); - relay2_local->publish_state(relay2_local_control); - id(relay_1_fallback) = relay1_fallback; - id(relay_2_fallback) = relay2_fallback; - disp1->set_component_font_color("home.icon_top_01", relay1_icon_color); - disp1->set_component_font_color("home.icon_top_02", relay2_icon_color); - disp1->set_component_text_printf("home.icon_top_01", "%s", relay1_icon.c_str()); - disp1->set_component_text_printf("home.icon_top_02", "%s", relay2_icon.c_str()); - id(home_relay1_icon) = relay1_icon.c_str(); - id(home_relay2_icon) = relay2_icon.c_str(); - id(home_relay1_icon_color) = relay1_icon_color; - id(home_relay2_icon_color) = relay2_icon_color; - ESP_LOGV(TAG, "Finished"); - - id: restore_settings mode: restart then: - - lambda: |- - ESP_LOGD("script.restore_settings", "Restoring settings"); - - #ifdef ARDUINO - id(framework) = 1; - #elif defined(USE_ESP_IDF) - id(framework) = 2; - #endif - - // ESP_LOGV(TAG, "Restoring wake-up page selector"); - // auto wakeup_page_name_call = id(wakeup_page_name).make_call(); - // wakeup_page_name_call.set_option(id(page_names)[id(wakeup_page_id)]); - // wakeup_page_name_call.perform(); - - // id(is_restored_settings) = true; - wait_until: condition: - lambda: return (not isnan(stoi(baud_rate->state))); - lambda: |- - ESP_LOGV("script.restore_settings", "Restoring baud rate"); set_baud_rate->execute(stoi(baud_rate->state), true); - ESP_LOGV("script.restore_settings", "Done!"); - id: service_call_alarm_control_panel mode: restart @@ -3198,15 +3067,15 @@ script: - id: set_brightness mode: restart parameters: - brightness: uint + brightness: float then: - lambda: |- static const char *const TAG = "script.set_brightness"; - ESP_LOGD(TAG, "brightness: %i%%", brightness); - if (brightness == id(display_brightness_global) and current_page->state != "screensaver") + ESP_LOGD(TAG, "brightness: %.0f%%", brightness); + if (brightness == display_brightness->state and current_page->state != "screensaver") disp1->send_command_printf("wakeup_timer.en=1"); else - disp1->set_backlight_brightness(static_cast(brightness) / 100.0f); + disp1->set_backlight_brightness(brightness / 100.0f); current_brightness->update(); - delay: 5s - lambda: current_brightness->update(); @@ -3298,60 +3167,6 @@ script: } ESP_LOGD(TAG, "Finished"); - - id: set_component_color - mode: queued - parameters: - component: string - foreground: int32_t[] - then: - - lambda: |- - if (id(is_uploading_tft)) set_component_color->stop(); - static const char *const TAG = "script.set_component_color"; - ESP_LOGVV(TAG, "Starting:"); - ESP_LOGVV(TAG, " Component: %s", component.c_str()); - int fg565 = -1; - // Foreground - if (foreground.size() == 3 and - foreground[0] >= 0 and - foreground[1] >= 0 and - foreground[2] >= 0) { - ESP_LOGVV(TAG, " Foreground: {%i, %i, %i}", foreground[0], foreground[1], foreground[2]); - fg565 = ((foreground[0] & 0b11111000) << 8) | ((foreground[1] & 0b11111100) << 3) | (foreground[2] >> 3); - } - else if (foreground.size() == 1) fg565 = foreground[0]; - else { - ESP_LOGE(TAG, " Component: %s", component.c_str()); - ESP_LOGE(TAG, " Foreground size: %i", foreground.size()); - fg565 = -1; - } - ESP_LOGVV(TAG, " Foreground: %i", fg565); - if (fg565 >= 0) disp1->set_component_font_color(component.c_str(), fg565); - - - id: set_page_id - mode: queued - parameters: - variable: string - page: string - then: - - lambda: |- - static const char *const TAG = "script.set_page_id"; - ESP_LOGV(TAG, "Starting:"); - ESP_LOGV(TAG, " Variable: %s", variable.c_str()); - ESP_LOGV(TAG, " Page: %s", page.c_str()); - - auto pageIndex = [](const std::string& page_name) -> uint8_t { - for (uint8_t i = 0; i < id(page_names).size(); ++i) { - if (id(page_names)[i] == page_name) { - return i; // Return the index if found - } - } - return 0u; // Return 0 (home page) if not found - }; - - uint detected_page_id = pageIndex(page.c_str()); - ESP_LOGV(TAG, "%s=%i", variable.c_str(), detected_page_id); - disp1->send_command_printf("%s=%i", variable.c_str(), detected_page_id); - - id: setup_sequence mode: restart then: @@ -3387,7 +3202,7 @@ script: - lambda: |- static const char *const TAG = "script.setup_sequence"; ESP_LOGD(TAG, "Goto page Boot"); - disp1->goto_page("boot"); + goto_page->execute("boot"); ESP_LOGD(TAG, "Fetching TFT version"); version_tft->update(); - wait_until: @@ -3449,14 +3264,12 @@ script: - lambda: |- static const char *const TAG = "script.setup_sequence"; ESP_LOGD(TAG, "Set dimming values"); - display_brightness->publish_state(id(display_brightness_global)); - display_dim_brightness->publish_state(id(display_dim_brightness_global)); - set_brightness->execute(id(display_brightness_global)); + set_brightness->execute(display_brightness->state); ESP_LOGD(TAG, "Set page Settings"); - disp1->send_command_printf("brightness=%i", id(display_brightness_global)); - disp1->send_command_printf("settings.brightslider.val=%i", id(display_brightness_global)); - disp1->send_command_printf("brightness_dim=%i", id(display_dim_brightness_global)); - disp1->send_command_printf("settings.dimslider.val=%i", id(display_dim_brightness_global)); + disp1->send_command_printf("brightness=%i", int(display_brightness->state)); + disp1->send_command_printf("settings.brightslider.val=%i", int(display_brightness->state)); + disp1->send_command_printf("brightness_dim=%i", int(display_dim_brightness->state)); + disp1->send_command_printf("settings.dimslider.val=%i", int(display_dim_brightness->state)); disp1->send_command_printf("brightness_sleep=%i", int(display_sleep_brightness->state)); ESP_LOGD(TAG, "Report to Home Assistant"); nextion_init->publish_state(disp1->is_setup()); @@ -3482,9 +3295,9 @@ script: disp1->send_command_printf("home.bt_qrcode.font=%i", id(home_custom_buttons_font_id)); disp1->send_command_printf("home.bt_entities.font=%i", id(home_custom_buttons_font_id)); disp1->send_command_printf("home.wifi_icon.font=%i", id(home_chip_font_id)); - ESP_LOGV(TAG, "Restoring relay's icons"); - disp1->set_component_text_printf("home.icon_top_01", "%s", id(home_relay1_icon).c_str()); - disp1->set_component_text_printf("home.icon_top_02", "%s", id(home_relay2_icon).c_str()); + ESP_LOGV(TAG, "Restoring relay's icon colors"); + disp1->set_component_font_color("home.icon_top_01", id(home_relay1_icon_color)); + disp1->set_component_font_color("home.icon_top_02", id(home_relay2_icon_color)); timer_reset_all->execute("boot"); notification_clear->execute(); id(setup_sequence_completed) = true; @@ -3497,7 +3310,7 @@ script: state: boot timeout: 10s - lambda: |- - if (current_page->state == "boot") disp1->goto_page(wakeup_page_name->state.c_str()); + if (current_page->state == "boot") goto_page->execute(wakeup_page_name->state.c_str()); else: # Unknown TFT - lambda: |- static const char *const TAG = "script.setup_sequence"; @@ -3558,13 +3371,10 @@ script: refresh_datetime->stop(); refresh_relays->stop(); refresh_wifi_icon->stop(); - relay_settings->stop(); service_call_alarm_control_panel->stop(); set_baud_rate->stop(); set_brightness->stop(); set_climate->stop(); - set_component_color->stop(); - set_page_id->stop(); setup_sequence->stop(); timer_dim->stop(); timer_page->stop(); @@ -3615,7 +3425,7 @@ script: current_page->state != "screensaver") { ESP_LOGD("script.timer_page", "Fallback to page Home"); - disp1->goto_page("home"); + goto_page->execute("home"); } - id: timer_dim # Handles the brightness dimming after a timeout mode: restart @@ -3625,12 +3435,12 @@ script: then: - lambda: |- ESP_LOGV("script.timer_dim", "Reset timer: %is", timeout); - if (current_brightness->state <= id(display_dim_brightness_global) + if (current_brightness->state <= display_dim_brightness->state and page != "screensaver" and page != "boot" and page != "blank-screensaver") { ESP_LOGD("script.timer_dim", "Waking up on page: %s", page.c_str()); - set_brightness->execute(id(display_brightness_global)); + set_brightness->execute(display_brightness->state); } - if: condition: @@ -3642,9 +3452,9 @@ script: current_page->state != "blank-screensaver" and current_page->state != "boot" and timeout >= 1) { - set_brightness->execute(id(display_dim_brightness_global)); + set_brightness->execute(display_dim_brightness->state); } - - id: timer_sleep # Handles the sleep (go to screensaver page) after a timeout + - id: timer_sleep # Handles the sleep (go to screensaver page) after a timeout mode: restart parameters: page: string @@ -3663,7 +3473,7 @@ script: current_page->state != "boot" and timeout >= 1) { ESP_LOGD("script.timer_sleep", "Going to sleep from page %s", current_page->state.c_str()); - disp1->goto_page("screensaver"); + goto_page->execute("screensaver"); set_brightness->execute(display_sleep_brightness->state); } @@ -3802,7 +3612,6 @@ script: // report Wi-Fi status bool wifi_connected = wifi_component->is_connected(); if (wifi_connected) { - id(wifi_timeout) = ${wifi_timeout}; float rssi = wifi_rssi->state; std::string rssi_status = "Unknown"; if (rssi > -50) rssi_status = "Excellent"; @@ -3815,15 +3624,7 @@ script: else ESP_LOGE(TAG, "Wi-Fi: %s (%.0f dBm)", rssi_status.c_str(), rssi); } else { - ESP_LOGW(TAG, "Wi-Fi: DISCONNECTED"); - if (id(wifi_timeout) > 0) { - id(wifi_timeout)--; - ESP_LOGI(TAG, "Retrying Wi-Fi connection"); - wifi_component->retry_connect(); - } else { - ESP_LOGE(TAG, "Restarting ESP due to a Wi-Fi timeout..."); - App.safe_reboot(); - } + ESP_LOGE(TAG, "Wi-Fi: DISCONNECTED"); } // report API status @@ -3831,7 +3632,7 @@ script: if (api_connected) { ESP_LOGI(TAG, "API: Connected"); } else { - ESP_LOGW(TAG, "API: DISCONNECTED"); + ESP_LOGE(TAG, "API: DISCONNECTED"); blueprint_status->publish_state(0); if (current_page->state != "blank" and current_page->state != "boot" and @@ -3840,7 +3641,7 @@ script: current_page->state != "settings" and current_page->state != "qrcode") { ESP_LOGI(TAG, "Fallback to page Home"); - disp1->goto_page("home"); + goto_page->execute("home"); } } @@ -3849,7 +3650,7 @@ script: // Report blueprint version ESP_LOGI(TAG, "Blueprint:"); if (blueprint_status->state > 99) { - ESP_LOGI(TAG, " Version: %s", version_blueprint->state.c_str()); + ESP_LOGI(TAG, " Version: %s", id(version_blueprint)); ESP_LOGI(TAG, " Init steps: %i (%0.1f%%)", int(blueprint_status->raw_state), blueprint_status->state); } else { ESP_LOGW(TAG, " Init steps: %i (%0.1f%%)", int(blueprint_status->raw_state), blueprint_status->state); @@ -3868,17 +3669,17 @@ script: ESP_LOGI(TAG, " Version: ${version}"); // Report framework #ifdef ARDUINO + ESP_LOGI(TAG, " Framework: Arduino"); size_t total_heap_size = ESP.getHeapSize(); size_t free_heap_size = ESP.getFreeHeap(); #elif defined(USE_ESP_IDF) + ESP_LOGI(TAG, " Framework: ESP-IDF"); size_t total_heap_size = heap_caps_get_total_size(MALLOC_CAP_DEFAULT); size_t free_heap_size = esp_get_free_heap_size(); #endif if (total_heap_size != 0) ESP_LOGI(TAG, " Heap: %zu bytes (%d%%)", free_heap_size, int(round(((float)free_heap_size / total_heap_size) * 100.0f))); - ESP_LOGI(TAG, " Framework: %s", id(framework) == 1 ? "Arduino" : - (id(framework) == 2 ? "ESP-IDF" : "Unknown")); // ESPHome framework // Report UART ESP_LOGI(TAG, "UART:"); diff --git a/hmi/nspanel_eu.HMI b/hmi/nspanel_eu.HMI index fdaec77c555f34c452d6882f0a58f3eb0901663c..bf1b7140a4b47a3927eeca4e8cbc1f8e43821978 100644 GIT binary patch delta 1007 zcmd7P$8*d97{>A6mfx-!ha?BtJ@^xhQw|7L)C3`-M3)dHNR(BAsQa+1mlZ6c1X07X zu`7Cs=%Q`(8is>0zoUbLuNi-X@y>i^o_FS*c@A%~xze3P^t&0(u&0K9nXNKyR*P9$ zR8rPHJ2%JpJMnbCVVNv{H2pLR(P*`CHMA~p)U@>*krczQnMTa#N=tL*R|RXsdESgb z$P)~CY_P)tCrr2!A&Z~s6DxbP3OWoB&J=IIS)koRNQD5~_f8}a`1}aa3l&`@WqM;h50u9#)jnpWO))?rvYH<>$5XEVnK@4>`i#QTEhkBgH1zf}>T*eh# z#Wh^V4K&~;8gUD^aR+yC5BKo^57C52c#J1_if2fo8PD+oFYyYm@dj`44)5^+AMpt- L_{>kgr2BpYxQ~Q_ delta 1018 zcmd7LS96R36vpv)vw1fcE|T2r?ie$!-0c_>E)cBfT@cYrh!TYGDnZmd?5e?vs7dtB zvXNJBQKLlL=ru892D2Z*UwjAS%>2%C@tm0xtu0Q7BK*U2U`NXBtgfIAf;K zIDfLzZ5idHqq@d0Qd;I^{jC~zW;4rZNTjPOiuhg0$?+NGrYn^0ZBrQZ1cDw1;^0I) zOt|czCn5XSKYp11y)fnt#Jmo~!HIa7aM>|$LejVY;UE(8h6~hI?bKeW>Y$G5B)>W< zO$3iT^ViaHrmSP!}V+B^C5W*^~MgT#q!CI`tdThW(6k!uKV+%sqiehX- z3ASSgc48NHV-HGEhH~u1KJ3Q<9K<0U#t|IFF&xJUoWv=dMi^&s773>8L%r?yeRcbP1C{=NU;qFB diff --git a/hmi/nspanel_us_land.HMI b/hmi/nspanel_us_land.HMI index e30e16c5afd97975083e29ad7d10c48e45380c5d..ee2ab9b28c55e5e91f5744ef99dfc52819ce9c75 100644 GIT binary patch delta 29588 zcmeHwd3+nyz3<3&99v!_*|KcO%YiJmJkB9DGN(>UWo4@gDvjam*7c z1VJ#Z`-ExPf~P*$;=QI9mm`0(J@bU0PBJcA{Nryb-g!cKj&ZNOWBk2%bLat+E9;il z&=!-cyZ`g^gc((%P)5L`V!nEwaCvDO%6K5c4{r(;XSuR|B8BR+T-_b_&lBV_qi3Ma zd(BuV{m;x3ddiKKmcV#0`M(R@0w|x}8CnG>^CI(vPh>_t9*D4MaGtQJz<|=$I{s#> z8hRU0X1*9QXS=!=?Yd6b=uD5Ld3?{nk?Vvl>DdO_$KN|CguavQ%90(S8v&)Te!kFb zPebV(-*eWi`NAh$>3dz{@BRF1p?*NwbzSHLp#15M`9fbBgDn%@H+#)|p}WL@(mVmm z;%7s%a$H&VS3}>(aY^o-^M#PnQE0|$rmMWzihIjy(oiOh^P=Q@!NSt_CalUgC-(~P zX5n7;d8VISam}^f&s_Xjqo*KTX6_YIL|Lh_T%q7HDVtx=H5xx{?G+w1fh_;7Ug(E3TPo=alA!Xk&!6PO=6 z6e0X}uW(z2d?v8fm+}^b-kapiK2WtFRGI7SzOrM1up>iG6W%!O%L|0h+l+SvCLn6= zRiSHhU0IR7(BfQIcX;svAwNIe6GSq0II++IVL@s7o_{=*T=Zn<5THzbKJ-^WDg5mM z;ZI|Tax9d@8w-Tbr03K;VNO0h^o7Z;>??};LO+`9{OY+c^$C5=#tUhgjGB@E@L&2y zXsvw$^8fA8KEWp%{eX$Fj|uf}g|_1rx0)7){xsRyQ-0pU5wZ+4Ln4gnTq`aVPMM8{ z<_WJT`P{;gFVC5M4CUwZoISN)T`1J1sSRu~j@mBz&O+gq=M6aR!y&F@64X+_*tmH?Ch!g#?ORTgWyyAHMfs{$$gveg!z{=WY${2Ar24yIp8+CFv&!{s~leI(DaUti?cFV1kH=6)p*} zLTC1`XWkX+DRg!x4*XKcxyaC1SgUd3=;Zm!h3CQsDNkTIVfAq6?m}1Ask=h!3tiox z`0Inh$t#VKw~a@m;G6T1@UfZ3z0L{uu3HoO8=&;5Ay<*B`?HP>!UGQ*0=8rPP=v(h zEy6`P={fnwgK2p=bZwC<>&a_Ei;G-61Aly3u+KN%H=Z*E;n?T53IFzgjC;)!26H%P zdnjJy%$l<~^w%QSWv@*Ajqs_RM%VTUa3&A^MtHL*O_?p@2Xp+dJB0a5(ytg#n8G)I z@k;2DV&`QuFL+g0m}YHZg64YR^s8J+nMj$0-w`fA`aQJRQw5i0Kl;wAg1i=r3-aPd(F_aV`x}t`lKoh zW}cg%g-xFteQx&rxnKFg{40Lq%{=^D+qwL^dk%OBMWqtA3m}`g^w)I|Ct5485ZazsoQo~Zr=eDbq7q;9WYUM zz(m~v6Lp6rck>R*F@RH!{;1`2m3SJuga_}p`uLds*el7U*;5UNb41O#4 z?cjHUbAp!#uLyoO_`Tqj!K;Gb5B?x{b?}{Q0&A+|M zHxKE$%~ihn@4c}Z(cgEoa@IdP_v>=N{Az{i>zhAke)UYYGvkA1ywS{c`{u7V&D(4$ z%eWEEY0qo!l~>ISY@T&R`~lOGTWq~~o4-7#_b-;syPUm?KPlzBCzva;gn941C%i|Y ztlUyTxDye!M%3REOr~O#=@6ID_uam07xc|paLrGy3Iv+_7JR>{^E?4z-{%D3D?*ci z5alG3uvqX4h(rC-Bm@P&&?yL|S%R1qpxT#mgcnWCRGXYD{Hv)2HA&b#S=ef7rN&?6 z3qn>KuN4TtF|||elSRTgQwP<$Ey4??PO1%Ag~qHdsyR!AKbyMgb(S*WSHgwD#{^+} znb4Vav2d9n{JC71pFLZEYkhU4P;7((1QQ$gCySS@tdTFcY-P24`E$$6@_FK)3gyec zyNc1J)%oiV^@$Jq_o#fu=CECU?!fLM`SSbaGI>Yt%E}Qi1udm}v~w|Qymq)a?kR3i z3la^5T7zQN8)9ayA!5d5v_4gs@F+!EUBnWfcHF*Ce%PZl&|8b7(p?I(9?)usVk~cu z?=X9&CCTp8+mz~fXRI>btvaJKBIV&(ibJnI6UiFT>QiNj`bgfv`ebR?lPc6aN{Qx9 z73=P#E$mT>k*t~``}2D%9NN zo@AjX=}5SXJgOt^R?Ja%#DQc+z1$NsC)`d?(yY~`Dv->cWQFD~Kn^H*VRxofaUH?6 zQR00SctalEfHIbcRDn9m$e2>ii%Yx#m2`*91ET=O12$fu z!dC0vGg#w>!>oAM>#r#WY09ztS2*R!9RtO(KXQKoCoswU#?VlbSrfGiD^AoYtO%%P zL_w?|QLnK4{zir6MVl1%vQJ^_b!02GfWq!;RciLMD^&-)=wGROcY2UFChtTMZHO%M zC{^_;2(Kzpt8PRQo~B~Glxpon0TnxokTc38p8xZ*?`H<`|8@xSzWY~9l^5-nJrc9* z8erBz&2uus)+GYR*%H0&bX9+M(wUes?C2N&VvmYbQYCS5TFRDaJoTEg>vbCN3d0@-N<$@zijMn*x;w>)<3R?V7%PZ-RLYW+rI3L%%<=ljNEV|SvS_{f zzATKk0-XS89=sm_GqAz{SXhr2CEUX_2v8n>c^rc;f;_=^Xslo~8hZpoufuFKTo%_4 zjsHs)g+-527^N&ek|n4%k`Wuo2k&1|BR_Y`iVFFH+n1L})&UUy?jg_F2-~0qPP087 zXDavh3|Ay(MatshUmfC9#U2-@I6No!J-k2B2nL>ZqB!2*n0nmSU$5E^DW&0h25Agz zCTOft44va0+)o^p$@nEr1VWK|VKNW_Oya9i2noOn35axZK|vS@%N4FyiVw!hk>Kz)=1FAJFPD|S2;?!hGLYxvS)x~7l9=+#;Yq#Ehm@R%?Z#?@)W*!(kGO%xq zrj+k~Z`Wk5o#Y*T{S_pW9FpZI))tr|8XXly&~A`N@r*or38H8_(y#prdj=BeQI8Js zwbHW}4?WP1>T$PH16hZnI$9Td28BEMqFbrb>hkbd zrIe_PY;!BFaGgrmESkb^sT$ke762>!BYPCr_ zm7+iL0@9`rNY5V`|EJf{wZbq}VxkYtnm_c;hu$#cn_;XFS5@pC&z(aLaQ4^uXxfJ_jZTnygisMIXt)TozC3k7P!2;3Mk@fJx5E^oPn)jbi*!I%S9n-zRj za-cC{)u*3|xoFS<*6+UGy?QmO) zx|LN&_37tMRze^y)_f-`^t!X{F?(2?a=H|nE@n%J(^YFitW_)NA;#ivDR?1 z@IWIlchUwE6yj2as#6L*YFWKXdDH{BsX8=Rk5s~pqq5wSD)6Y~nj3azIhMfeNlN9A zw`NGak&=ux&0Rr7!VN1i!c91=T7f49(~Vnj8Z0<>IYc%oAsBEms7o?u$wGqJmP(s# zF$_?(#yz&ECsqtuX#<%+l&4A?PUJ&Vm&O~;*1t9|s5hu(iCM|=#0)5USZgr3YZ9%; z*}aURl55t~D-6F(*Va?*GGV*C_w++I|lfh^tVN z{a7Z_^0a8YjjtN@;35{7GuDukEj4p}ffc8@!eSu8rxi6yP&M7+B&2A&BE0YF~Nu;=Y0SqjKOixT&Yt(4vgzfdK>OvOs@hnlhQJpMtbq)WTqLAxL)P& zr5B87HNz0%;N)9>`9LYDew(b`aevwfC>?-c*{!+3N-yia6KqY}>6&O~vNF*Sb=gfQs5ZiVHh)1MBS1j%lwE6*C&g^4aW=iG%A*O<5^Jppx#K*W2#!JiU011 z&r-|b#Jb|0YRxeiN-GaF!OYIzHE`rjIdEgY-LNz+|K*x|dG_Mh^T@MyN+mD>Eqk=O zb20YPG2a>XXj`nNzhjuRgEKw@M$fERSrn^(RBMdb8_wkIX|!wH9ah2f@Z#VGW1Lo? z!~za0fu*EjO~6zjJIsJ60&X9Og?qo4FGNMhv5nd2K1qXssbIki-A1bU_EemqHwS=RUB^u zF(1`@DT^jbwHUiNJqavMQ%TW)VB2-K;y4yKahE&gdSht%xl@O8h~+U_E)ooBkydA5 zJ40mP9<(HxoREKHH1h2wFfq2iwdb2 zsfxBAuZgz8WQ2NJN=Qhx2J)o7Chi8sMykM|7A$0C)J;^ZxJax(l(;;x zl6qI%Z3ih6iRIAu*jf;Uuu`UMSw~c=QkJeLUqz1N1AqWd}TtG7mV`endyFK4h&1hfl zN#&CoTZRY~2F@Pvs4rovkJjgrg*B-@mRrxZd6MhV_Coz8AZ&7dETzrEZoi`^?5NH|nV<>ZS%bL>TN0z3&_3Ss5|Sz}NR@^cNY*PaaT+sTuCPxB(+~Z9SXb-l^3x~Za!d9U4rPyL$#13;) z_r^-YzEn-rhteOZ)_rPe)Ss$~N`AF8UI+YE>UFdXiMpd;$*4PAmqPuIo(M&u;Gp0l zMzS~T24TT5h?qPO8FlJiZY+$l)SZHgSp&NT7g?T28SX5HjPg7N85?##O4UgoaxB!< z?iieq)MHpl&w7LlaFN6<8-)48ZV0@( z>|?>5$sVY!*ftnXK;3pXT$$JwPf@fkm4}hQ^TFEH#fbdL)3Q>Ddu9v(JOER;PEzvG zNDKish*&*JK1Prib{7Du)l=wEUM3Tc*ZJGPQAE#0o~>@EMAQMU5TltyFXdN7m=;XB zx+D#+2#-Dh-J7|@i|=)VBX6uHYCNnfkQ=tj3ZRzQR?vj;IdqaLl(+%^y9 zqgn$P7c>BBaUY?ZEC8ENwP6~(nrKpZ9Sqh4#a0(9M&t&fDpL*DlcN_S%coTLND-{8 ziVLMQ>M2WC?XVH38U-WP;U95zIr!#HUWc*dfLtM?$tWRcz@GGRuUq!RHF zSY=>kg%mnfBB-dmf^f-W-(EhQv}SQSW^&oea(UjIH*%$YP^(9VnoqsPw3%n&B#VDL zT%kjQICQZ#X4l2KVT1tO!zH>1!$_|?J)lg2^RM|8ATDX!tv9BqVn5gzBU{G-!3`3D zRZ_1~SrMBEFxKM^e6Qv&*sEz3gmot2rzWq^APDDY z3!gFhg)0Q%njGQQoB-8MeR$UHD#38`Y8(!tb-& zsrGD@(3I0bwLhIF{2{xOYL844zMs=YwLFi|oYPIO>uwTmGhHa)2;w%curudk;XdsB zv;}rnb6NVXHAhj{KT5F4zt*x z5*Eb6Gy!|)0F9+*mRf$G0iZ@k}@PmSzV`W-PS-2IB zXe(^w)>Nt9W;g!P1vTQPU2CDP4m?tUM_tfVRTR%11W-dG-uR z_)g5R>!|KI^+(2v_UJ8dGT3+BD#I?s%)_jv9~LF6goQcGD*C}FkSt<@%1WbRQ<9NU zkYtu93;~#zB22zQ5kVNU)_G3rf>MpQo{emLqbwTi&dcYXO$stT;7TTX8>Brk|MtYE{@DtB(6w!e0Zm$^wx}SY*rn@a}vu zmKCV-r`T$L%o)YK9DmZOA*T4a--Xr<{#ZG+=KA5b^GGA%hHdpL4!_D?@*@J_lOin7 z4}Fi@d796m`4BGgIdpW(kCJLvS$>#wIbLP3!X+xq)(ncT({h1O*nvYBo$qQu*poN;RgqMlY5`!s5oArZB#2xv4Xj#0w zDB=72Dw&7UrSu`t73;*&WJYO>8^bmxlqr)VGtEqW1QjE=8NpT>dqA9Ipfio%48HC3 z2wwmU6AJ==HN8MlU=0hFUO>K3Tm2CS-G0$ukzVZzWAQ5tQc!qvjz2{-0ZMVD=_X24 zBqgg5NejfPIDs*P+G-L8@nxlHaj8L?8A@GkkS=>RP0H)iq`kH6tgC+VSpnYkc(Y~;J`Fgo|OdD>LOEn0G!lJ-48jM_K*TAxHXyDnf5)Hcz@M+-O zVB-+<39km^3St8)SYbc76f_L)8>nwXp4Z=jd`lOyO(Yf`)+t{*6WLM`!;OpYvg(60 za@Ssj0a?WXu})H%<4~uLSJ)F>Z{Z!bJw1^)E^X1S;SjD%^bU13mGQRY8&Hz zeLa)@sk3Kn?*9_<4;VJwP&zC)!WZBH=S03BD_D9dt?n9!plzA?jZVau5X7Bzm{lWw za)MRt5urEjn%FoD6pGM9Mg1bqSrjD1c7?s9iybMpQS3>vN5z>*wp;`QBt-GAEZ%u) zVB>zRGsU#l!`3|=5!QUh5r;5{drvs~TO&+sI9{D-mQu`Ae;S*D3WGm`jlmdOS{q@L znv-nB)M2)@nXQ~M%<^^kl*=ieKzwP1ZY4%dA&1anC4qFJ-FyZCju}kIsa^M_yFu*X zLC75dk)Va>7CKnquu>Cm!3G?p0B;BYoKdyFl|@~rW{3#Xi^BxWqWjUknA9|c2DKE& z2|!U4dK_i|jTbrEi~6w{1(5=UPq_-ZM1kALaXXU(8?2x|oue!205mo))PX7J%MZwb zA+(R?8m}99g4g3bk5Vob@}e+I7ezWBX@cibKHhL(7_-s&Y~q9o%!6iW%#|<42tF`q z3*7{=Fe58gg)4Ptg$;lmEkZvDKtzzJ8uy~rX4AX?YV)HZO05Z>JiK*n<)`6)iP(}9 zMURn{heZ)F#ORnj&g&pHw0~GGzf!x_AzyG{ZIQgAb#0#9dD+V8BbuR7o;_#P=q1-6 zl~V2iHlE6NAJyy6kpV~?fr%|XCLU&$d&Jt~to)Eze}wHq6Gn|0(YQf+_O;!8*zt?;~02F zU+6T-DQuT%=Uq6oFcCczM=EoVLz0Mf&=O8F20acvl;HsQyF|sDt_}5=Ao4Z8(TaTC z{$uS)<{}ZK0|AvfS;|%Lq!m`JLohJ1pV^9;81)0QpvG5-Q=l%%XXBF{`_G#VW^ebd84<&7KdRAdiw?%N?=nNf9 zN>%HgbFWqG*E^JI|JgSOB6$OYZ}+Ix;D>)Fc?&5k&VncvMVx>WXQ?bC&KPEo5g9%! zc1PF?x(Jk+_lV7Bn01dX;viMoZcUtihE>7S>BeCZw*HMFy=myJ9N?!!HZA`n*k(j~ zKvP&{+P@pqMt7mr*;XC~ID)O{j0weYfXQOY#ZfY$iqbNM8&Y9zNcEE;W!5Rr$H@;Y zDz9r^XjNq zy$@wo*sHFkQg2E=jy+6h)hGI*uw{)lOw$0?}5K1<@w>zf|$2&;YB>jQAVBW%go| z78p_pP zMn$$a#+#RW8R0n#Vw=BzX;X?fgb@R#8x#}90@Tnom2{1Ts6l@e)d^f;J*shuqAJmp zrk*jyF|QF>Iua=s;XoHb&Nr(?AoBO;?)Nu+V9TJqEO%$A{7~-B9O=lB(HAfp1n8Y6 z_`j5Y@_+O)3f|-Iq#U?xW#b4lR?4ZFYiuKzX;%mgS}A)HdoA*VxjQHE$)_=(ux(;j z#K^W&jc1cdP)x`+QX9{nr`l-tf)_qiya`!?XINJFDvgk8>7093hJVHPk7ySO(yr7S z1Jjmb`H59X));w|pztitK^ggn12jOTA(eVR98d-X78u)CLqkUfBy`S4JmyW54~S6i zHRAM>tV(pBWEEkt9+ELCHl6@Wi%_1}M1&l(Bt+Oj#hTbcF$xh9tU!dgvXN>Ug64vj z+jT?}F{ov4kak;l49H?HB|4#>4v};#IMjx>6((Rfrauf;P~&#iWxyu!OnE zL`nkM3Qt+#ghSQSwV|=%+yLNqYFamqDkO6GN&ir9iV9GU0gcp+yT~QO6yr~F{RO>; zx2IK?PLD!yLYkB4h8PDBh;cr=6xn~xk*eLKwUX6|cM;fX!713OC)oxZym6mkiFTzr zTz__-W4DGa`nd1ZYXi#0fkCYs8(*ZM?iR(171C1*TP)6kQ7_I^*jmz4lf)hzAQii? z;gk^DaJc7yh{L3|L9y{Lb3kQHJIShdAL}7iweEGUsxX(5U2%1-rR%oHs}sXuXCJur=^y*j}hr^5a<*+<1!k)het!4s#PR z1QAfDOw4#VsNqu&sYot!*F^GbB8ZJ zuNo9U_8bMFXo>NhWHoFT-V)_hlklbr_a7n2^431g!>evR+5$vd{IQjXdd^^L(5EcX z#eYXkFyTF|;Zzf32aMZE;>^=*r3kuTEF$KyRD?ZO13^6{S*?lU)D(<5Y@k%mJa^>H zfr0%;^v;N@^Xxv!HmJ9`!mTHj@1G_ebbvJiWZa5AJq z-HZKSfH%WD&Qx$*1b9P)B~$U0f%4696i~A0U5cluQbw<$c7lxVcCw@MQKA0%fLKNb zcs^`y-ZLL#rm4K-kCf3Hw)&+CtmHNy4m)5(T-KG{F zg0yd6 z+Le37DIniHWWKLI)()aBfP>U9gm9XDmpmL9e4nv+&&;*?^1S8C?PFMz+mjS@kiT`) zzCw9*?#}#Ctu$JjedGuvu-J$l!hcBakcUIRD_ybtcR7@#frBK`;G=TsMv^b>_#2Xs z3t9^owBy*!5WOUzIje!~@Fe$fdB7kIgCeZNlEiG}Sik|s%SO9d{Rntu95-TU_;7f_ z@Q^_dX+9lagD*z##`GkD!VA0vz>xKvr6I^6K&(WdhC^sb`Z0{IdnI}%-EZW5#0=w} z1Dn7eBKS70jzHe1a?cVEk9Pj>78(dRgrH#X1{8uSm4HP!EJDnfNPtG7*v8#6{`N85 z-q)J$OYh(iqI`J96BF+lL(f1{FQu38HyKaUT=@IO_A|!JCpWG$J!0UBPYs`%@ITsx zoMjODJy-56lxP2H)mVne(0!p)vaCD~q3a}N}HzEqUGf+h+S_6rLx8?Zp?G$kO zv@hkPu=sjEHsB)II8rKfFDZdY1;o?KzE}mgV5={Nqj@SyoK+hsjdlX^vM&M|M^8yq z!N!D7slXECVpA%~a(%Q##h&-YoWw|v`6AA!pH1>t!7d8pQvqccqp$>WEQ;qeD?=m{W(gK0 z#utTrv%pz>)-be5Ahzll%$b)1$-LcqHyrpttZ)ys9$OuVSPyh65+wPseQ!G)b^mbr zURX?;ci0te#cqoapBA-L`y(}Qm0g7L#Vtw&u=Yr^icPu*%8D>>xha6X9b9gwEgww7 z@1U4|zJ{$i%}Wv;s?bl{YNcVUW&w8O2p0C+a2&%Mk#L7sP-1Hz4?q>BD`1-vKf(~1 zVx&3!0D1!iZP{f&+KFQdAZ(yHp-aIn>FuhR9Sds4?jZ#Nw)k-(6D0L(zv_@EPWpI2 zaYSL_tO=wlD6W_YL@MIVv1*vZuGZtOc#Gn~CawdfK~L7)(oC%6yL6J2ahvOjvb2utSG^24w*2GrY6{h{LJdGInh4!-plLn4}hbOfihSG+c z+@h8o01t;+B4vqYYy=Ni#HB!zY1p}o1|rqrfP$T0Ff$keR9*!?LHEH>00Z0^zyTI4 zN{ILvEc%2WY=Tcp$jZQ?(FQQZ_pn1qn&f#uHp@O!ID~M~uA^P5OY4Fme`E+0 zRw;txD`9_3!}(2^vPyM7%vyG*q>Ccl6*w}hMMNZ@CM$Rw=!=v>9*m^J%l)kAjqa1m z6Icby?zjD?UdzX1%HSvBgN9>U`M`$5PeHp49G-((NVLV9g9%)CiHjIEGCd?zS z@SSlWi|T&VNVZ`M0^O`SuO?8n#HmAiyCKZC8@O{(7;gq-xS#-4kRVUPe2_wM`H(}% z$vDK4LpHJJ6CJ~hJ(B1`{oX`Rl2z?LinyW`A6~sfx)1r@M#s~ zA`Uf{bLRlGKkYax+*~1-HfP5vgh%cIjSx*XGB!f;beS)1eR$-QRgA1XSiiXrSsKMEaeG zs>rW2$T&t%ZzB)#8`l5*<0qM!JEa0p3;n&t7&IYJ21dpju@yuHjE7X2hA=V_EXRHePhB@OMSUe|1A>kQi?bdgyfBx$c?o4S3`h9NqW_68b^`teVRs9fQE;u0TMxZ29Fkmy-5fYq9$)- zI~vTs6r>2Ag^fUbfDhcj(0Fvc58t*B=|4lERG5V@1K?%i(KWt^R77vh_QOWUqf30T zVjiHl+h?PmclwgB!Fa?sVgtSVQi#e&SQSQFLj9_AF9e6eT(C(Xwh;2g=*j}D(uZ?3 z=!h-x801=tK@>VkNIIfk9M3~X2UMgpz{({*KS~1`r@F|yhC_^xfzh)h>cyuA=t-Io z$8cDwpAn~G69r!#p(mI+!mvmfFwRm?R3GB>&Hylo1F}fix2Gc)Nz%JGor9jh0v*pW z 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 DAbnV~ diff --git a/hmi/nspanel_us_land.tft b/hmi/nspanel_us_land.tft index 2df06218dae4c3cc6b341e0cd57c158edb0091f2..22ffbe66c2f5d09b3220d3683f693b5569a2e2c5 100644 GIT binary patch delta 74153 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 Z#ZnMwg$%BS{8ka~xM|h(ehI&C{~s%Xpb7v0 delta 28160 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 - {% set temp_color = + {{ hardware.relays.relay1.icon_color_rgb if hardware.relays.relay1.icon_color_rgb is defined and hardware.relays.relay1.icon_color_rgb is sequence and hardware.relays.relay1.icon_color_rgb | count == 3 else nextion.color.on - %} - {{ - ((temp_color[0] //(2**3)) *(2**11)) + - ((temp_color[1] //(2**2)) *(2**5)) + - ((temp_color[2] //(2**3))) }} relay1_fallback: '{{ hardware.relays.relay1.fallback }}' relay2_local_control: '{{ hardware.buttons.right.entity == relay02_entity }}' @@ -7200,18 +7195,13 @@ action: ) }} relay2_icon_color: > - {% set temp_color = + {{ hardware.relays.relay2.icon_color_rgb if hardware.relays.relay2.icon_color_rgb is defined and hardware.relays.relay2.icon_color_rgb is sequence and hardware.relays.relay2.icon_color_rgb | count == 3 else nextion.color.on - %} - {{ - ((temp_color[0] //(2**3)) *(2**11)) + - ((temp_color[1] //(2**2)) *(2**5)) + - ((temp_color[2] //(2**3))) }} relay2_fallback: '{{ hardware.relays.relay2.fallback }}' continue_on_error: true diff --git a/prebuilt/nspanel_esphome_prebuilt.yaml b/prebuilt/nspanel_esphome_prebuilt.yaml index b59bceb..5f817c8 100644 --- a/prebuilt/nspanel_esphome_prebuilt.yaml +++ b/prebuilt/nspanel_esphome_prebuilt.yaml @@ -24,7 +24,7 @@ external_components: packages: core_package: !include ../esphome/nspanel_esphome_core.yaml - upload_tft_package: !include ../esphome/nspanel_esphome_addon_upload_tft.yaml + #upload_tft_package: !include ../esphome/nspanel_esphome_addon_upload_tft.yaml api: services: @@ -93,18 +93,18 @@ script: static const char *const TAG = "prebuilt.script.watchdog"; ESP_LOGI(TAG, "Pre-built version: ${pre_built}"); -select: - - id: !extend tft_file_model - platform: template - options: - - "NSPanel Blank" - - "NSPanel EU" - - "NSPanel US" - - "NSPanel US Landscape" - - "NSPanel EU (CJK languages)" - - "NSPanel US (CJK languages)" - - "NSPanel US Landscape (CJK languages)" - initial_option: "NSPanel Blank" +#select: +# - id: !extend tft_file_model +# platform: template +# options: +# - "NSPanel Blank" +# - "NSPanel EU" +# - "NSPanel US" +# - "NSPanel US Landscape" +# - "NSPanel EU (CJK languages)" +# - "NSPanel US (CJK languages)" +# - "NSPanel US Landscape (CJK languages)" +# initial_option: "NSPanel Blank" text_sensor: - id: firmware_url @@ -114,11 +114,10 @@ text_sensor: internal: true icon: mdi:cloud-download -web_server: - id: web_server_std - auth: !remove +web_server: !remove wifi: + networks: !remove ap: {} power_save_mode: LIGHT # To make it compatible with BLE ...