From 65d37d7403744b4042409817376fd344b3aedbcf Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 17 Feb 2024 21:05:56 +0100 Subject: [PATCH] Rebuilding API (INCOMPLETE) --- ReleaseNotes.md | 501 +----- docs/api.md | 395 +++++ .../pics/Nextion_Components_Home_Chips_EU.png | Bin 0 -> 26883 bytes .../pics/Nextion_Components_Home_Chips_US.png | Bin 0 -> 21925 bytes ...tion_Components_Home_Custom_Buttons_EU.png | Bin 0 -> 26640 bytes ...tion_Components_Home_Custom_Buttons_US.png | Bin 0 -> 21721 bytes esphome/nspanel_esphome_core.yaml | 1361 ++++++++++------- nspanel_blueprint.yaml | 97 +- tft/nspanel_eu.HMI | Bin 15035221 -> 15035221 bytes tft/nspanel_eu_code/boot.txt | 2 +- tft/nspanel_eu_code/home.txt | 20 +- tft/nspanel_us.HMI | Bin 14820802 -> 14820802 bytes tft/nspanel_us_code/boot.txt | 2 +- tft/nspanel_us_code/home.txt | 20 +- 14 files changed, 1233 insertions(+), 1165 deletions(-) create mode 100644 docs/api.md create mode 100644 docs/pics/Nextion_Components_Home_Chips_EU.png create mode 100644 docs/pics/Nextion_Components_Home_Chips_US.png create mode 100644 docs/pics/Nextion_Components_Home_Custom_Buttons_EU.png create mode 100644 docs/pics/Nextion_Components_Home_Custom_Buttons_US.png diff --git a/ReleaseNotes.md b/ReleaseNotes.md index a548be9..34d6f05 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,4 +1,4 @@ -# v4.2 - Multi-Alarm Control, Dual Thermostat Functionality, and More +# v4.3 - TItle to be defined ## Support this project **As always, we would like to thank everyone who donated to this project and remind you that every donation helps to support this project @@ -7,7 +7,7 @@ So if you haven't made a donation yet, now would be a good time ;)** [![Paypal](https://user-images.githubusercontent.com/41958506/212499642-b2fd097a-0938-4bfc-b37b-74df64592c58.png)](https://www.paypal.com/donate/?hosted_button_id=S974SWQMB8PB2) ## General -Hello everyone! Our latest update introduces a range of enhancements designed to elevate your experience with the firmware. +Hello everyone! Our latest update introduces a range of enhancements designed to elevate your experience with the firmware. This update is focused on improving user interaction, featuring a more intuitive TFT upload process and expanded customization options. Key enhancements include upgraded functionality for Alarm Control Panels & Thermostats and new personalization features on the Home page. @@ -41,9 +41,9 @@ Updates may come with changes on the blueprint inputs and we highly recommend yo We did our best to support your legacy settings and avoid breaking your system, but please double check your settings if you see something not working as expected. ### Files that need to be reloaded -1. nspanel_eu.tft, nspanel_us.tft or nspanel_us_land.tft - v4.2 -2. nspanel_esphome.yaml - v4.2 -3. nspanel_blueprint.yaml - v4.2 +1. nspanel_eu.tft, nspanel_us.tft or nspanel_us_land.tft - v4.3 +2. nspanel_esphome.yaml - v4.3 +3. nspanel_blueprint.yaml - v4.3 ### Instructions *You can find the update procedures following here:* @@ -51,473 +51,25 @@ We did our best to support your legacy settings and avoid breaking your system, - [How To - All important thing you should know - Update](https://github.com/Blackymas/NSPanel_HA_Blueprint/blob/main/docs/howto.md#update-blueprint) ## Breaking changes -1. **The `background` parameter in the `esphome.xxxxx_set_component_color` service is now deprecated.** -2. **On the home and climate pages, the button layout has changed.** +1. **Default framework changed to `esp-idf`**. Please follow instructions bellow for more details on how to proceed. +2. **All services have changed**. If you are using the services directly in your automations, please look at our [API documentation](https://github.com/Blackymas/NSPanel_HA_Blueprint/blob/main/docs/api.md). - This adjustment was made to incorporate additional custom buttons on the home page and new target temperature indicators on the climate page. -3. **Wi-Fi power save mode has been reset to `NONE` as the default setting.** - - This was the standard up to v4.0. We switched it in v4.1 to accommodate Bluetooth components. - However, since this feature isn't widely used, we're reverting to the original setting. - For Bluetooth usage, you should manually set a different mode, such as `LIGHT`, as shown in the customization example below: - - ```yaml - ##### My customization - Start ##### - # Enable Bluetooth proxy - bluetooth_proxy: - # Set Wi-Fi power save mode to "LIGHT" as required for Bluetooth on ESP32 - wifi: - power_save_mode: LIGHT - ##### My customization - End ##### - ``` - -4. **The default baud rate for advanced mode has been reverted to 115200 bps** to resolve issues that arise when rendering button pages. -5. **ESPHome v2023.12.0 has been established as the minimum required version** to ensure optimal performance and compatibility, particularly with the recent developments in the Nextion component. -6. **The Alarm section in the blueprint settings has been removed.** All configurations previously made in this section have been automatically transferred to custom button 07. -7. **Documentation in German is no longer maintained.** This change allows us to more efficiently update and maintain the remaining documentation. -8. **Reversion to "Toggle" as default action for automation entities on buttons.** - - In this update, we have reverted the default action for button presses on automation. entities back to "Toggle". - This change reverses the adjustment made in version 4.1, where "Trigger" was set as the default action. - - **Impact:** - - - **Default Behavior:** With this reversion, pressing a button linked to an automation entity will now enable or disable the automation, as it did prior to version 4.1. - - **Consistency and Familiarity:** This change aims to align with user expectations and maintain consistency in the user experience. - - **Customization Still Available:** Users still have the option to manually set "Trigger" as the action for their `automation.` entities, as per their preference. - - We understand that changes in default settings can impact your workflows. - We appreciate your understanding as we strive to balance new features with the familiarity and stability of the user experience. -9. **Advanced mode no longer enforces a specific framework.** - - In the past, the enforcement of framework changes by the advanced mode could inadvertently result in users switching frameworks during OTA updates. - This practice was not recommended due to the potential for errors. -10. **Alarm page cannot be used as wake-up page.** - - As multiple alarms are supported now, the system don't know which one to show on the wake-up. -11. **Refinement of Icon Presentation** - - In our continuous pursuit to refine the user experience, we've embarked on standardizing the presentation of icons across various pages. - This initiative may lead to noticeable changes in icon appearance and behavior, - encompassing aspects like the introduction of new colors for alarms, lights, climate icons, - and the adoption of fresh icon designs. - - Our aim with these modifications is to establish a default presentation that adheres to a unified standard, - enhancing both consistency and user experience across the platform. - While this approach simplifies the overall design, - it still preserves a degree of customization through blueprint settings. - - The complexity of managing icon colors, especially for items with multi-state attributes like alarm control panels, - climate controls, and lights with adjustable brightness and color, has prompted us to rethink our approach. - The current version implements domain-based colors for multi-state items, with specific states for alarms and climate being hard-coded. - We're exploring ways to offer user-selectable colors for these states in future updates. - - The new color logic is as follows: - - For the `off` state: Icons will be displayed in **gray**. - - For states other than `off`: - - For alarms and climate: Each state will have a specific, hard-coded color for immediate recognition. - - For lights: If supported, the icon will reflect the light's RGB color, proportionally dimmed to match its brightness. - - For other domains: Icons will be displayed in **white**. - - This transition towards a more standardized icon presentation is a step forward in enhancing - the interface's intuitiveness while balancing it with the flexibility of customization. - We understand this change might be an adjustment from previous versions, - but our goal is to streamline the experience without compromising the essence of personalization. -12. ***Action required:* Reset your wake-up page selection.** - - We've enhanced the wake-up page functionality for improved consistency after power cycles. - Previously, the system didn't remember your selection following a power outage. - With our latest update, this issue has been resolved. - However, this improvement requires you to reselect your wake-up page one more time. - Thanks for your cooperation in making these settings more reliable for everyday use. + We understand this change can require substantial work on the exisiting automations with direct calls to the panel's services, + however this will significantly improve the future customizations by providing compreensive documentation, standardizing and optimizing services calls. ## Overview of noteworthy changes -1. Standardized entity icons -2. Additional custom buttons on Home page -3. Outdoor temperature selectable font size -4. Select icon size for button's pages -5. Support to CJK languages (experimental) -6. Improved TFT transfer -7. Multiple Alarm Control Panels -8. Support to `remote` -9. Home page chips now supports covers -10. Chips can be inverted -11. Add swipe control to screensaver page -12. Enhanced control for automation entities on buttons -13. Dual thermostat controller support (add-on) -14. Dual thermostat display -15. Enhanced timeout flexibility for Sleep, Page fallback, and Dimming settings -16. Display time on the `screensaver` page +1. New default framework +2. Performance improvements ## Details of noteworthy changes -### 1. Standardized entity icons -The engine for defining the icons to be shown and it's color was standardize between all the pages, so now you may see a more consistent User's Interface. -We've upgraded the icon definition engine to standardize the appearance and color of icons across all pages. -This enhancement leads to a more consistent and harmonious user interface. -Expect a unified visual experience that aligns the look and feel of icons throughout the system, contributing to improved navigation and aesthetic appeal. +### 1. New default framework +Some text -### 2. Additional custom buttons on Home page -It's that simple, now you have 7 buttons where used to be 3. -Well, where used to be 4, as the space previously used by the Alarm button is now a custom button. 😉 - -### 3. Outdoor temperature selectable font size -Now you can select the font size of your outdoor temperature display: - -![image](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/263f14d6-39f7-41d2-ae12-72f403d811fd) - -> [!IMPORTANT] -> Long text with bigger fonts may exceed the limit of space reserved for this with incomplete information shown. - -### 4. Select icon size for button's pages -You can also select the size of the icons on the buttos pages: - -![image](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/239e2682-c73b-44cc-b1fa-2bf07aeba78b) - -![image](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/8e650642-c117-44eb-9150-c5c360075922) - -![image](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/9f6471ae-2404-468a-82f1-208e807200bf) - -### 5. Support to CJK languages (experimental) -You will find 3 new TFT files on the repository for the CJK languages. -These files are considerably bigger, as the fonts requires more memory, but it should work fine in your panel. - -> [!WARNING] -> This is an experimental feature and may contain bugs or some areas missing translations. -> Please report if you find anything. - -### 6. Improved TFT transfer - -#### TFT file selectors (`ESP-IDF` framework required) -We still working on the Upload TFT engine to make it easier for new and for experienced users. -Now, together with the **Update TFT Display** button, you will find also **Update TFT Display - Model** and **Update TFT Display - Branch** (disabled by default) -where you can select the different model you are using and the upload URL will be automatically adjusted to donwload the file directly from the repository on GitHub, -making this process much easier and removing the need of flashing your device every time you want to play with another TFT file or install the `nspanel_blank.tft`. - -![image](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/73d5e552-f52f-41ee-8008-136596035004) - -If you have issues with the remote file and want to transfer it from your local server just as before, simply select **Use nextion_update_url** in **Update TFT Display - Model**. - -And you also can use the `esphome.xxxxx_upload_tft_url` service to upload using an alternative URL without the need of flash your panel again. - -#### Alternative baud rate for TFT uploads -You can select an alternative baud rate for your TFT uploads. -This allows faster TFT uploads and also supports displays previously set with baud rates not supported by this project (currently 115200 bps and 921600 bps). - -In most cases, this would be set for a higher value to enable faster TFT transfer. -The default is 921600 bps, the maximum supported by Nextion displays. - -You can also use this if you have issues with TFT transfer and want to try a lower baud rate as in some rare cases Nextion will automatically set itself to 9600 bps. - -To enable an alternative upload TFT baud rate, add the desired value in your substitutions like this: - -```yaml - upload_tft_baud_rate: "9600" -``` - -If an invalid value is entered, 115200 bps will be used. -The default value for this substitution (in case it is not set by users) will be 921600 bps. - -During a TFT transfer, the system will always fall back to the standard baud rate (115200 bps) if other tentatives fails. - -### 7. Multiple Alarm Control Panels -Now alarm control panel entities can be assigned to chips and custom buttons in the Home page, -but also to hardware buttons, any of the buttons pages or entities pages, similarly to other domains. - -### 8. Support to `remote` -Entities from the `remote` domain can now be assigned to buttons (hardware and buttons pages) and custom buttons (home and climate pages). - -The service `remote.toggle` will be called when the button is pressed. - -### 9. Home page chips now supports covers -That's it! -Chips now include cover visualization, allowing for easy monitoring of covers directly from the Home page. - -### 10. Chips can be inverted -Chips now have an 'invert' option on the blueprint settings, providing additional customization flexibility. - -### 11. Add swipe control to Screensaver page -You can now navigate to button pages from the screensaver while your panel is in sleep mode. -The swipe controls will function as they do on the Home page. - -### 12. Enhanced control for automation entities on buttons -We're pleased to announce a new feature that offers users enhanced control over their smart home setups. -Now, you can globally select between **Toggle** and **Trigger** actions for button presses on automation entities. -This update allows for a more personalized and efficient control experience, tailored to your unique preferences. - -#### Key features -- **Toggle:** This action will enable or disable the automation. -It is set as the default option. -- **Trigger:** This action will execute the automation regardless of its current state, providing more flexibility. - -#### How to use -1. Access your configuration for automation under **Services** > **Automations & Scenes**. -Select the automation associated with your panel setup. -1. Scroll to the **ADVANCED SETTINGS** section at the end. -1. From the menu, select your desired action - either **Toggle** or **Trigger**. -1. Apply the setting. -This will then be effective across all relevant entities. - -This update is specially designed to cater to diverse user needs, ensuring a more intuitive and responsive smart home experience. - -### 13. Dual thermostat controller support (add-on) -This new add-on enables the NSPanel to manage both heating and cooling systems simultaneously, utilizing its dual relay functionality. - -#### Key Benefits -- **Versatility:** Users can now configure their NSPanel to control both a heater and a cooler, offering increased flexibility in home temperature management. -- **Efficiency:** With the ability to control both heating and cooling systems, the NSPanel can more effectively maintain desired temperatures, potentially leading to energy savings. -- **User Experience:** This update simplifies the user interface by integrating control of both systems into one panel, making it easier to manage home climate. - -#### Implementation Details -- The dual thermostat controller functionality allows the NSPanel to utilize both of its relays for separate heating and cooling control. - - Users can configure their heating and cooling settings independently, providing a tailored climate control experience. -- The update is designed to be intuitive and user-friendly, ensuring a seamless transition for existing NSPanel users. -- Additional Documentation: For detailed instructions and configurations, refer to the updated [add-on climate documentation](docs/addon_climate.md). - -> [!NOTE] -> This feature enhances the capabilities of the NSPanel without compromising its existing functionalities. -> Users who previously used the NSPanel for single-mode (either heating or cooling) control can -> now explore dual-mode options for a more comprehensive climate control solution. - -### 14. Enhanced Climate Page for Dual Thermostat Control -Leveraging the capabilities introduced with the Dual Thermostat Controller add-on, -the existing climate page has been upgraded to support systems with dual temperature settings. -This enhancement allows users to effectively manage thermostats that require both heating and cooling target temperatures, directly from the NSPanel's interface. -While originally developed to complement the new add-on, -this refined climate page also seamlessly extends its functionality to existing thermostats with dual set capabilities, -offering a more versatile and comprehensive climate control experience. - -### 15. Enhanced timeout flexibility for Sleep, Page fallback, and Dimming settings -We have improved the functionality of our system by enhancing the timeout flexibility for Sleep, Page Fallback, and Dimming settings. -The previous timeout limit of 300 seconds has been significantly expanded, offering users more control and customization options for these specific features. -This enhancement allows for a wider range of use cases, catering to the needs of those who require extended periods for these settings, -while still maintaining the option for shorter durations. - -### 16. Display time on the `screensaver` page -In this release, we're excited to introduce a new feature that adds a display time function to the Screensaver page. -With this update, our screensaver not only serves its primary purpose of saving screen energy but also doubles as an informative display, -reflecting our commitment to creating practical, user-friendly solutions. - -You can enable this feature on the bueprint settings, at the **CUSTOM CONFIGURATION** section (almost at the end). -You also have to set **Display Brightness Sleep** at the device's page (**Settings** > **Devices & Services** > **ESPHome**). - -## Patch v4.2.1 -This patch introduces minor updates aimed at enhancing the user experience and improving documentation. -Below you'll find a detailed list of the changes. -While this update is designed to integrate smoothly with existing v4.2 components, -we recommend updating all components to this latest version for the best performance. - -**Overview of Changes:** -| Change | Criticality | Affected Components | -| :-- | :--: | :--: | -| Resolved issue with the dimming feature not activating when button is held (#1599) | Minor | `Blueprint` | -| Added "Clean Build Files" step to the update instructions for clarity (#1601) | Minor | `Documentation` | - -**Key Improvements:** -- **Dimming Feature Fix**: Addressed an issue where the long press in the buttons was not responding as expected, -ensuring smoother light control from your panel. -- **Updated Documentation**: Enhanced our update instructions to include a "Clean Build Files" step, -providing clearer guidance for a more efficient update process. - -## Patch v4.2.2 -This update focuses on enhancing user customization, improving data management, and addressing a range of issues from minor to critical in our system. -Our commitment is to continuously enhance the usability, stability, and efficiency of your panel. - -> [!IMPORTANT] -> **Breaking Change**: This patch requires Home Assistant v2023.12.0 or higher to ensure compatibility with new enhancements. -> Please update your Home Assistant installation before applying this patch. - -We encourage all users to update their components to this latest version for the best experience. -While this update is compatible with existing v4.2 components, it offers optimized performance and usability when fully updated. - -**Overview of Changes:** -| Change | Criticality | Affected Components | -| :-- | :--: | :--: | -| Fix Climate page not working with embedded climate (#1627) | Critical | `ESPHome` | -| Fix weather pages crashing when Home Assistant OS is not used (#1630) | Medium | `Blueprint` | -| Correct Wi-Fi reference in QR code example (#1609) | Minor | `Documentation` | -| Resolve incorrect labelling on entity pages (#1628) | Minor | `Blueprint` | -| Address lights with no brightness in 'supported_features' (#1633) | Minor | `Blueprint` | -| Implement ISO8601 date formatting option (#1610) | Enhancement | `Blueprint` | -| Enable dynamic icons based on device_class (#1635) | Enhancement | `Blueprint` | - -**Key Improvements:** -- **Fix for Climate Page Not Working**: Resolved a critical issue with the climate page not functioning properly when using embedded climate controls (#1627). -This fix enhances system stability and user interaction with climate features. -- **Fix for Weather Pages Crashing**: Addressed an issue causing weather pages to crash when Home Assistant Operating System (HAOS) is not used (#1630). -This fix ensures stability and reliability of weather-related features across various Home Assistant installation modes, such as containerized installations and other setups. -- **Wi-Fi Reference Update in QR Code Example**: Updated the Wi-Fi reference in the QR code example (#1609) to prevent user errors during system setup. -This documentation change aids in clearer guidance for configuring connections. -- **Entity Pages Configuration Labeling**: Corrected the labeling of options in the blueprint for entity page configuration (#1628). -This fix ensures that the labels accurately reflect the behavior on the pages, enhancing the configuration experience. -- **Handling Lights Without Brightness Feature**: Improved handling of information for lights that lack brightness control in 'supported_features' (#1633). -This update resolves the issue of incorrectly displaying brightness levels (as "0%") for lights that do not support brightness adjustment, ensuring more accurate display of buttons. -- **ISO8601 Date Formatting**: Introduced ISO8601 date formatting (#1610) to improve date readability and consistency in the user interface. -- **Dynamic Icons Using Device Class**: Upgraded dynamic icons to utilize device_class (#1635), offering a more intuitive and context-aware UI. - -For support, feedback, or detailed information about this update, -visit our [GitHub repository](https://github.com/Blackymas/NSPanel_HA_Blueprint) -or our [online documentation](https://github.com/Blackymas/NSPanel_HA_Blueprint/blob/main/docs/README.md). - -Special thanks to @PaulAntonDeen and @illuzn for their invaluable contributions to these enhancements. -Your feedback and support are crucial to our continuous improvement. - -## Patch v4.2.3 -This release focuses on targeted bug fixes and enhancements to enhance system stability and functionality. -Major updates include a critical fix for an issue where the panel was getting warmer, -potentially affecting temperature measurements, and resolving a crash issue associated with climate entity rendering. -Additionally, this update introduces new Dutch/Italian date formats for improved localization, -and addresses a minor display issue with the battery indicator on cover pages. - -> [!IMPORTANT] -> **Breaking Change**: If you are using the [Customization "Set display as a light"](https://github.com/Blackymas/NSPanel_HA_Blueprint/blob/main/docs/customization.md#set-display-as-a-light), -> please update the code related to the extension to `script`/`set_brightness` as the global variable `display_last_brightness` was replaced by the new sensor `current_brightness`. - -We highly recommend all users update their components to this latest version for an optimized and enhanced user experience. -While this update is compatible with existing v4.2 components, fully updating ensures the best performance and usability. - -**Overview of Changes:** - -| Change | Criticality | Affected Components | -| :-- | :--: | :--: | -| Fix device temperature gap since v4.2 (#1620) | Critical | `Blueprint`
`ESPHome`
`TFT` | -| Fix crash when rendering a climate entity with no `hvac_action` (#1647) | Critical | `Blueprint` | -| Fix battery indicator not showing on cover pages (#1661) | Minor | `Blueprint` | -| Add Dutch/Italian date format (#1658) | Enhancement | `Blueprint` | - - -**Key Improvements:** -- **Fix for Device Temperature Gap**: Resolved a critical issue with the panel getting warmer on v4.2, potentially affecting its temperature measurements. -- **Fix for Crash When Rendering Climate Entities**: Resolves a critical issue where the blueprint was crashing when trying to render icons for climate entities with no `hvac_action` attributes, -preventing pages from being fully loaded. -- **Fix for Battery Indicator**: Addressed the issue where the battery indicator was not displayed correctly on cover pages. As a bonus, the icon now changes based on the battery level. 😉 -- **Add Dutch/Italian Date Format**: Expanding the existing pre-listed date formats, we've added the **Weekday, DD-MM (ex. "Friday, 22-03")** format. -This enhancement caters to our Dutch and Italian users, adding a touch of local flair to their experience. - -A special thanks to the community for their support and contributions, -especially to @andythomas for providing useful, informative, and visually appealing temperature trend plots, -and to @shing6326 for their dedicated efforts in resolving the crashes with climate entities. -Your contributions significantly enhance the quality of our project. - -For support, feedback, or detailed information about this update, -visit our [GitHub repository](https://github.com/Blackymas/NSPanel_HA_Blueprint) -or our [online documentation](https://github.com/Blackymas/NSPanel_HA_Blueprint/blob/main/docs/README.md). - -## Patch v4.2.4 -In this update, we have focused on addressing critical issues to enhance the reliability and performance of our system. - -We highly recommend all users update their components to this latest version for an optimized and enhanced user experience. -While this update is compatible with existing v4.2 components, fully updating ensures the best performance and usability. - -**Overview of Changes:** - -| Change | Criticality | Affected Components | -| :-- | :--: | :--: | -| Nextion is not connected for too long time on boot (#1667 & #1674) | Critical | `TFT` | -| Panel not starting when no climate entity is selected (#1676 & #1677) | Critical | `Blueprint` | - - -**Key Improvements:** -- **Fix for Panel Long Boot Process**: Resolved an issue with the panel taking an excessively long time on the Boot page ("Initializing..."), -ensuring a quicker startup and more efficient operation. -- **Fix for Panel Not Starting Without Climate Entity**: Addressed a critical issue where the panel would not start if no climate entity was selected. -This fix ensures smooth operation and startup of the panel regardless of the climate entity configuration. - -For support, feedback, or detailed information about this update, -visit our [GitHub repository](https://github.com/Blackymas/NSPanel_HA_Blueprint) -or our [online documentation](https://github.com/Blackymas/NSPanel_HA_Blueprint/blob/main/docs/README.md). - -## Patch v4.2.5: Celebrating 1000 Stars with Enhanced Functionality and UI Improvements -With the 1000-star milestone on GitHub, we're excited to share Patch v4.2.5, enhancing the NSPanel Firmware experience in our spare time. -This update refines climate and cover entity interfaces, boosts hardware button responsiveness, and integrates ESPHome's `friendly_name` for easier device identification. -We've also expanded direct TFT download capabilities from GitHub to Arduino users, previously only available for ESP-IDF users. -This achievement, powered by community support, is a significant stride towards [simplifying user experience for NSPanel firmware](https://github.com/Blackymas/NSPanel_HA_Blueprint/discussions/1602). - -We highly recommend all users update their components to this latest version for an optimized and enhanced user experience. -While this update is compatible with existing v4.2 components, fully updating ensures the best performance and usability. - -**Breaking Changes:** -1. **Custom entities naming changed**. -If your setup includes custom entities using `${device_name}` in their names, please modify these entities to omit `${device_name}`. -ESPHome will now automatically append the `friendly_name` or `device_name` from your substitutions, simplifying the naming process. -2. **Show while loading** input is deprecated and all pages will show components while loading as default. - -Additionally, we have updated our documentation to include comprehensive guidelines on memory requirements for ESPHome, -ensuring users are well-informed for successful system setup and operation. - -**Overview of Changes:** - -| Change | Criticality | Affected Components | -| :-- | :--: | :--: | -| Long Click Action Bug Fix for Hardware Button (#1637) | Minor | `Blueprint` | -| Fix Display of Climate Icons on Chips (#1675) | Minor | `Blueprint` | -| Fix Icon's Colors on Home Page Values (#1694) | Minor | `Blueprint` | -| Alarm Keypad Arm/Disarm Fix for US Model (#1705) | Minor | `TFT file (US model only)` | -| LocalTuya Fan Speed Controls Fix (#1706) | Minor | `Blueprint` | -| Fix Update of Media Player Icons on Custom Buttons (#1716) | Minor | `Blueprint` | -| Motion Sensor for Display Wake-Up (#1687) | Enhancement | `Blueprint` | -| Enhanced Consistency in Cover Controls (#1688) | Enhancement | `Blueprint` | -| Support ESPHome `friendly_name` (#1719) | Enhancement | `Blueprint`
`ESPHome` | -| Improved Blueprint Input Clarity (#1722 and #1782) | Enhancement | `Blueprint` | -| Enhanced Memory Allocation When Uploading TFT (`esp-idf` only) | Enhancement | `ESPHome` | -| Expanded Visualization on Chips | Enhancement | `Blueprint` | -| Direct TFT Transfer from GitHub for Arduino Users | Enhancement | `ESPHome` | -| Baud Rate Adjustment for TFT Uploads | Enhancement | `ESPHome` | -| Update docs (install.md) with memory requirements (#1720) | Documentation | `Documentation` | - - -**Key Improvements:** -- **Long Click Action Bug Fix for Hardware Button**: Resolved a bug where long click actions weren't executing on hardware buttons if no entity was assigned. -This fix ensures that custom actions linked to long presses will now run as intended, regardless of whether an entity is assigned to the button, -offering greater flexibility and reliability in user interactions. -- **Fix Display of Climate Icons on Chips**: Enhanced the functionality of climate entity chips. -Now, the chip is displayed only when the climate entity has an active HVAC action. -This change ensures that the chip is hidden in states like 'Idle' or other non-active states, -aligning the display behavior with the operational status of the climate entity for improved accuracy and user experience. -- **Fix Icon's Colors on Home Page Values**: Addressed an issue where icons for sensor domain entities on the home page were incorrectly displaying colors indicative of an "entity disabled" state. -This fix ensures that the icons now reflect their correct status, enhancing the visual accuracy and user interface experience. -- **Alarm Keypad Arm/Disarm Fix for US Model**: Resolved an issue specific to the US model, where alarms requiring a PIN to arm/disarm were not functioning correctly. -This fix, applied through an update to the TFT file, ensures that users with these alarm systems can reliably arm and disarm their security devices. -- **LocalTuya Fan Speed Controls Fix**: Addressed an issue with LocalTuya fan speed controls not functioning as expected. -Users can now seamlessly adjust their fan speeds through the Blueprint interface, enhancing control and interaction with smart home devices. -- **Fix Update of Media Player Icons on Custom Buttons**: Corrected an issue where media player icons on custom buttons were not updating in real-time to reflect the current playback status. -This fix ensures that the icons dynamically represent the media player's state, improving the interface's responsiveness and accuracy. -- **Motion Sensor for Display Wake-Up**: We've introduced a feature that allows users to specify a motion, -presence, or door sensor to wake up the display, enhancing the panel's responsiveness. -Each detected motion or sensor activation event triggers the panel to wake up, with the sleep timer resetting with every new detection. -While the panel will still follow the pre-set sleep duration settings regardless of continuous motion or sensor activity, -this initial implementation marks a significant step in making user interactions more dynamic and intuitive. -- **Enhanced Consistency in Cover Controls**: Building upon the dynamic icons feature introduced in v4.2.2, we've further refined the cover controls. -This enhancement extends the use of `device_class` to the detailed cover page, -ensuring that the icons for opening and closing covers are more accurately representative of the cover type. -This update is a step towards our goal of extending context-aware UI enhancements to other supported domains in future updates. -- **Friendly Name Support in ESPHome**: Enhanced the Blueprint's compatibility with ESPHome's `friendly_name` feature. -Previously, using `friendly_name` could disrupt communication between the Blueprint and ESPHome due to mismatches in entity and service names. -This update resolves these issues, ensuring seamless integration. -Users can now utilize the `friendly_name` substitution in their YAML setup to assign more intuitive and descriptive names to their devices, -significantly improving the ease of device identification and overall user experience. -- **Improved Blueprint Input Clarity**: Simplified the naming of Blueprint inputs for better clarity and ease of use. -Labels such as *"Button14"* have been updated to more descriptive formats like *"Button page 2, Button 6"*. -Additionally, terms like "LABEL COLOR" have been refined to "Text Color" to improve understanding related to sensor display customization. -- **Enhanced Memory Allocation When Uploading TFT (esp-idf only)**: Optimized memory management for TFT uploads on systems using the `esp-idf` framework. -This update prevents simultaneous HTTP server connections, allowing for better memory allocation and reducing the risk of system crashes due to memory shortages. -It does not affect users on the `arduino` framework. -Additionally, the log for the upload process now includes detailed memory usage data, providing valuable insights for troubleshooting and system performance enhancement. -- **Expanded Visualization on Chips**: Enhanced the home page chips to display not only Media Player entities but now also Fan entities, among previously supported domains. -This update broadens the informative visual feedback available on the home page, offering a more comprehensive and visually engaging snapshot of the device status within the user interface. -- **Direct TFT Transfer from GitHub for Arduino Users**: Enhanced the TFT update process for Arduino users by enabling direct transfer of TFT files from GitHub to Nextion displays, -mirroring the functionality previously available to ESP-IDF users. -This streamlines the update workflow, removing the need for intermediate steps like using a local HTTP server, and making it easier to keep Nextion displays up-to-date. -- **Documentation Update on Memory Requirements for ESPHome**: Enhanced the installation documentation to include detailed guidelines on memory requirements. -This update addresses frequent user-reported compilation errors in ESPHome, attributed to insufficient memory on compiler servers. -The updated section outlines recommended memory configurations for different installation scenarios, ensuring smoother compilation processes. -A special acknowledgement to @andythomas for his valuable contributions to this update. -- **Baud Rate Adjustment for TFT Uploads**: This update introduces a baud rate selector in the device's "Configuration" area, -allowing users to lower the transfer speed from the default 921600 bps. -This enhancement is particularly useful for troubleshooting transfer issues, -providing a straightforward method to improve transfer reliability by adjusting the speed to accommodate different system capabilities. +### 2. Performance improvements +Some text +## Support For support, feedback, or detailed information about this update, visit our [GitHub repository](https://github.com/Blackymas/NSPanel_HA_Blueprint) or our [online documentation](https://github.com/Blackymas/NSPanel_HA_Blueprint/blob/main/docs/README.md). @@ -526,27 +78,6 @@ or our [online documentation](https://github.com/Blackymas/NSPanel_HA_Blueprint/ Discover what's next and what we are working on right now in our [Milestones](https://github.com/Blackymas/NSPanel_HA_Blueprint/milestones?direction=asc&sort=title&state=open). ## Special thanks to -- @bluefoxlee: - - For the CJK fonts and all the support with #1359 - - Update T.Chinese translation and Zi fonts, #1453 - - Update CJK fonts and T.Chinese translation, #1532 - - Update CJK character set for future reference, #1589 -- @Floppe - Fix entity pages labels, #1455 -- @tikismoke - Customization guide: Exposing relay fallback switch to Home Assistant, #1537 -- @andythomas: - - Allow to set upper and lower set points for embedded thermostat, #1573 - - Bugfix for embedded climate/cool functionality, #1587 - - Update docs (install.md) with memory requirements, #1720 - Patch v4.2.5 -- @PaulAntonDeen: - - Add ISO8601 date formatting as an option, #1610 - Patch v4.2.2 - - Fix Wi-Fi reference on QR code example, #1609 - Patch v4.2.2 -- @illuzn: - - Fix for Lights with no brightness supported_features, #1633 - Patch v4.2.2 - - Implement dynamic icons and use device_class, #1635 - Patch v4.2.2 - - Fixes the incorrect labelling of the entity pages alignment, #1628 - Patch v4.2.2 -- @bkbartk - Add Dutch/Italian date format, #1658 - Patch v4.2.3 -- @shing6326 - Fix crash on climate page, fix climate icon and color, #1649 - Patch v4.2.3 -- And to all users who helped with the tests during development and beta. ## Previous releases - [v4.2.4 - Critical bug fixes](https://github.com/Blackymas/NSPanel_HA_Blueprint/releases/tag/v4.2.4) diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..ced364a --- /dev/null +++ b/docs/api.md @@ -0,0 +1,395 @@ +# API +This document provides details on custom services designed for integration with Home Assistant, including their usage, parameters, and examples. + +## Service Documentation +### Command Service +Sends a custom command directly to the display, enabling direct interaction and dynamic content updates. + +**Usage:** +This service is particularly useful for advanced customizations and direct display manipulations, such as showing messages, updating statuses, or any other display-centric commands. + +**Parameters:** +- `cmd` (string): The command string to be sent to the display. Please refer to [The Nextion Instruction Set](https://nextion.tech/instruction-set/) for a comprehensive list of commands supported. + +**Home Assistant Example:** +```yaml +service: esphome._command +data: + cmd: "page home" # Go to page "Home" +``` + +> [!NOTE] +> Replace `` with your specific panel name as configured in Home Assistant to ensure correct service execution. +> +> Ensure the command string (`cmd`) is properly formatted according to your display's command processing capabilities. + +### Component Color Service +Changes the foreground color of a specified component on the display, enabling dynamic color updates for user interface customization. + +**Usage:** +This service is ideal for creating visually dynamic interfaces, allowing elements to change color based on conditions, events, or user actions, such as indicating status changes or highlighting specific UI components. + +**Parameters:** +- `id` (string): Identifier of the component whose color will be updated. It is essential that this matches the component's ID in your display layout to ensure the correct element is targeted. +- `color` (int[]): The new color for the component, specified as an RGB array (e.g., `[255, 0, 0]` for red). + +**Home Assistant Example:** +```yaml +service: esphome._component_color +data: + id: "home.time" + color: [255, 0, 0] # Changes the component's color to red +``` +> [!NOTE] +> Replace `` with your specific panel name as configured in Home Assistant to ensure correct service execution. +> +> Ensure the `id` and color parameters accurately target and define the new color for the component. + +### Component Hide Service +Hides a specified component on the display, allowing for dynamic interface changes. + +**Usage:** +This service is ideal for creating interactive user interfaces that adapt by hiding certain elements based on user actions, conditions, or events. + +**Parameters:** +- `id` (string): Identifier of the component to be hidden. It is crucial that this matches the component's ID in your display layout to ensure the correct element is hidden. + +**Home Assistant Example:** +```yaml +service: esphome._component_hide +data: + id: "date" # Hides the date display on Home page +``` +> [!NOTE] +> Replace with your specific panel name as configured in Home Assistant to ensure correct service execution. +> +> Ensure the id matches the component on your display you wish to hide. + +> [!IMPORTANT] +> This command only works when the page is visible and cannot contain the page id in the component id. +> +> If the component being hidden is not part of the current page, the command will fail and an error message will be logged. + +### Component Show Service +Makes a specified component visible on the display again, allowing for dynamic interface reversals. + +**Usage:** +This service is essential for creating interactive user interfaces that can show elements previously hidden, based on user actions, conditions, or events, thereby restoring elements to the user interface as needed. + +**Parameters:** +- `id` (string): Identifier of the component to be shown. It's crucial that this matches the component's ID in your display layout to ensure the correct element is made visible. + +**Home Assistant Example:** +```yaml +service: esphome._component_show +data: + id: "date" # Shows the date display on the Home page if it was previously hidden +``` +> [!NOTE] +> Replace `` with your specific panel name as configured in Home Assistant to ensure correct service execution. +> +> Ensure the `id` precisely matches the component on your display you wish to make visible again. + +### Component Text Service +Updates the text of a specified component on the display, enabling dynamic text content updates. + +**Usage:** +Ideal for user interfaces that require real-time text updates, such as status messages, labels, or any text-based information display. + +**Parameters:** +- `id` (string): Identifier of the component whose text will be updated. Ensure this matches the component's ID in your display layout. +- `text` (string): The new text content to display. This can include static text or dynamic information passed at runtime. + +**Home Assistant Example:** +```yaml +service: esphome._component_text +data: + id: "home.time" + text: "12:34" +``` +> [!NOTE] +> Replace `` with your specific panel name as configured in Home Assistant to ensure correct service execution. +> +> Make sure the `id` corresponds to the correct component on your display for the text update to work as intended. + +### Component Value Service +Updates the value of a specified component on the display, enabling dynamic value updates. + +**Usage:** +Ideal for interfaces requiring real-time updates of numerical values, such as counters, temperature readings, or any numeric indicators. + +**Parameters:** +- `id` (string): Identifier of the component whose value will be updated. It's crucial this matches the component's ID in your display layout accurately. +- `val` (int): The new integer value to be set for the component. This can represent various data types, depending on the component's purpose (e.g., temperature, humidity levels). + +**Home Assistant Example:** +```yaml +service: esphome._component_val +data: + id: "cover.coverslider" + val: 25 +``` +> [!NOTE] +> Replace `` with your specific panel name as configured in Home Assistant to ensure correct service execution. +> +> Ensure the `id` accurately matches the component on your display to successfully update its value. + +### Icon Service +Updates a chip or custom button's icon, color, and visibility within Home Assistant. + +**Usage:** +This service is ideal for dynamically updating icons on your Panel, allowing for a customizable and interactive user interface. + +**Parameters:** +- `id` (string): Identifier of the chip or button component. Refer to "[Screen components](#screen-components)" for more details. +- `icon` (string): Icon codepoint from [HASwitchPlate Material Design Icons](https://htmlpreview.github.io/?https://github.com/jobr99/Generate-HASP-Fonts/blob/master/cheatsheet.html). Example: "/uE6E8" for `mdi:lightbulb-on-outline`. +- `icon_color` (int[]): RGB color array for the icon. Example: `[0, 255, 0]` for green. +- `visible` (bool): Flag indicating whether the icon should be visible (`true`) or hidden (`false`). + +**Home Assistant Example:** +```yaml +service: esphome._icon +data: + id: "home.chip03" + icon: "/uE6E8" # Example for mdi:lightbulb-on-outline + icon_color: [0, 255, 0] # Green + visible: true +``` +> [!NOTE] +> Ensure the placeholder `` is replaced with the specific panel name you will need to reference in your Home Assistant configuration. + +### Initialization Service: `init_global` +Transfers global settings from the blueprint to ESPHome, +ensuring that ESPHome is configured with the necessary parameters for operation according to the blueprint specifications. + +**Usage:** +This service is crucial during the initialization phase or when global settings need to be updated to reflect changes in the blueprint. +It configures ESPHome with settings that affect overall functionality and user interface aspects. + +**Parameters:** +- `blueprint_version` (string): Specifies the version of the blueprint being used. +- `embedded_climate` (bool): Indicates whether climate control is embedded in the panel. +- `embedded_climate_friendly_name` (string): Provides a friendly name for the embedded climate control. +- `embedded_indoor_temperature` (bool): Determines if indoor temperature display is enabled. +- `mui_please_confirm` (string): Localized (language based) message used for asking for confirmation in the UI. +- `mui_unavailable` (string): Localized (language based) message used for indicating unavailability in the UI. +- `screensaver_time` (bool): Enables or disables the screensaver time display. +- `screensaver_time_color` (int[]): Specifies the RGB color array for the screensaver time display. + +**Home Assistant Example:** +```yaml +service: esphome._global_settings +data: + blueprint_version: "4.2.5" + embedded_climate: true + embedded_climate_friendly_name: "Home Climate" + embedded_indoor_temperature: true + mui_please_confirm: "Confirme, por favor." + mui_unavailable: "Indisponível" + screensaver_time: true + screensaver_time_color: [165, 42, 42] # Reddish-brown +``` +> [!NOTE] +> Replace `` with the specific name of your panel configured in Home Assistant. +> +> This service should be called to update ESPHome with the latest global settings as specified in your blueprint. + +### Initialization Service: `init_relays` +Configures relay settings in ESPHome according to the specifications provided in the blueprint, +ensuring each relay operates with the correct parameters for control, appearance, and fallback behavior. + +**Usage:** +This service is essential for initializing or updating relay configurations to reflect changes in the blueprint. +It tailors ESPHome's relay operations for specific use cases, including local control capabilities, iconography, color indications, and fallback states. + +**Parameters:** +- `relay1_local_control` (bool): Enables or disables local control for Relay 1. +- `relay1_icon` (string): Specifies the icon for Relay 1. +- `relay1_icon_color` (int): Sets the RGB565 color 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): Specifies the icon for Relay 2. +- `relay2_icon_color` (int): Sets the RGB565 color for Relay 2's icon. +- `relay2_fallback` (bool): Determines the fallback state for Relay 2 in case of communication loss. + +**Home Assistant Example:** +```yaml +service: esphome._init_relays +data: + relay1_local_control: true + relay1_icon: "lightbulb" + relay1_icon_color: 16711680 # Red + relay1_fallback: false + relay2_local_control: true + relay2_icon: "power" + relay2_icon_color: 65280 # 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. + +### Notification Clear Service +Removes any displayed notification from the screen, allowing the display to return to its normal state or view. + +**Usage:** +This service is essential after displaying notifications or alerts. +It ensures the user interface remains clean and uncluttered by clearing messages once they are no longer needed or have been acknowledged. + +**Home Assistant Example:** +```yaml +service: esphome._notification_clear +``` +> [!NOTE] +> Replace `` with the specific name of your panel configured in Home Assistant to ensure the service executes correctly. +This simple action clears the current notification from the display, maintaining a tidy interface. + +### Notification Show Service +Displays a notification message on the screen, enabling dynamic presentation of information or alerts. + +**Usage:** +Designed for scenarios requiring immediate feedback or notification on the display, +this service is suitable for showing alerts, informational messages, or updates directly on the screen interface. + +**Parameters:** +- `label` (string): A brief title or label for the notification, typically displayed in a prominent, bold format. +- `message` (string): The detailed message or content of the notification, providing the main information or alert to the user. +The system will automatically wrap text to fit the display unless `\r` is used to insert manual line breaks. When `\r` is present, the system respects only the line breaks provided by the user, enhancing message formatting control. + +**Home Assistant Example:** +```yaml +service: esphome._notification_show +data: + label: "Security Alert" + message: "Front door opened at 10:30 PM\rPlease check the entrance." +``` +> [!NOTE] +> Replace `` with the specific name of your panel configured in Home Assistant. +> The action ensures the service executes correctly, displaying the notification with the specified `label` and `message`. +> +> Utilize `\r` within the message for custom line breaks, offering precise formatting control. + +### RTTTL Play Service +Plays melodies encoded in the RTTTL format, enabling the integration of audio feedback or alerts with simple text-based melody strings. + +**Usage:** +This service is perfect for projects requiring audio signals, such as notifications, alerts, or simple melodies, by interpreting RTTTL (Ring Tone Text Transfer Language) strings. +RTTTL is a compact format for storing melody sequences in a text-based format, making it ideal for simple audio devices like buzzers. + +**Parameters:** +- `tone` (string): The RTTTL string representing the melody to be played. +This string must conform to the RTTTL format, which includes the melody's name, default settings, and a sequence of notes. + +**Example Tones:** +For example tones and further inspiration, you can visit examples of RTTTL songs [here](https://codebender.cc/sketch:109888#RTTTL%20Songs.ino). + +**Home Assistant Example:** +```yaml +service: esphome._rtttl_play +data: + tone: "d=4,o=5,b=140:c,e,g,8p,c6,e6,g6,8p,c7,p" +``` +> [!NOTE] +> Replace `` with your specific panel name as configured in Home Assistant to ensure correct service execution. +> +> Ensure the `tone` parameter contains a valid RTTTL string to successfully play the melody. + +### Value Service +Updates an entity to display specific values, allowing for dynamic updates of icons, names, and value colors within Home Assistant. + +**Usage:** +This service is intended for entities that need to display information dynamically, such as sensor readings or state values, with customized icons, names, and color coding for both icon and value. + +**Parameters:** +- `id` (string): Identifier of the entity being updated. For details on entity identifiers, refer to "[Screen components](#screen-components)". +- `icon` (string): Icon codepoint from [HASwitchPlate Material Design Icons](https://htmlpreview.github.io/?https://github.com/jobr99/Generate-HASP-Fonts/blob/master/cheatsheet.html). Indicates the icon displayed next to the value. +- `icon_color` (int[]): RGB color array for the icon, allowing for custom icon colors. +- `name` (string): The display name for the entity, useful for labeling the value displayed. +- `value` (string): The actual value to be displayed next to the icon and name. +- `value_color` (int[]): RGB color array for the value text, enabling custom coloring of the displayed value. + +**Home Assistant Example:** +```yaml +service: esphome._value +data: + id: "sensor.temperature" + icon: "/uE6E8" # Example for mdi:thermometer + icon_color: [255, 0, 0] # Red + name: "Temperature" + value: "75°F" + value_color: [255, 255, 0] # Yellow +``` +> [!NOTE] +> Replace `` with your specific panel name as configured in Home Assistant to ensure correct service execution. + +### Wake Up Service +Activates the display from a screensaver or low-power state, enabling dynamic interface adjustments based on user interactions or automated triggers. + +**Usage:** +Ideal for scenarios requiring the display to become active upon certain events, such as motion detection, thereby conserving energy while ensuring the display is available when needed. + +**Parameters:** +- `reset_timer` (bool): Determines whether to reset the sleep and dimming timers upon waking up the display. +Setting this to `true` ensures the display remains active during user presence, while `false` retains the existing timer durations. + +**Home Assistant Example:** +```yaml +service: esphome._wake_up +data: + reset_timer: true +``` +> [!NOTE] +> Replace `` with the specific name of your panel configured in Home Assistant. +> This action ensures the service is executed correctly, waking the display and optionally resetting timers based on the reset_timer parameter. + +**Practical Use Case: Motion Sensor Activation:** +This service can be seamlessly integrated with a motion sensor to wake the display when motion is detected, making it instantly usable. +Additionally, if the display is already awake, calling this service with `reset_timer: true` can reset the sleep timer, +keeping the display active as long as there is movement and allowing it to sleep normally once no motion is detected. + +The example bellow integrates the `wake_up` service with a motion sensor to ensure the display wakes or remains awake during periods of activity, reverting to sleep mode after inactivity. + +```yaml +automation: + - alias: "Wake Display on Motion" + trigger: + - platform: state + entity_id: binary_sensor.motion_sensor_123 + to: 'on' + action: + - service: esphome._wake_up + data: + reset_timer: true + mode: restart +``` +> [!NOTE] +> Adjust `` and `binary_sensor.motion_sensor_123` to your actual panel and sensor entity IDs. +> This setup ensures the display is responsive to environmental conditions, enhancing user interaction while managing energy consumption efficiently. + +## Screen components + +### Page Home - Chips +![Image](pics/Nextion_Components_Home_Chips_EU.png) +![Image](pics/Nextion_Components_Home_Chips_US.png) + +#### User-defined Chips +- **Description**: Chips are icons that are shown in specific situations or hidden. Their behaviour is controlled by the blueprint. +- **Type**: Icon only with no touch commands. +- **Availability**: Global (available even when page is not visible). +- **Ids**: `home.chip01` to `home.chip07`. + +#### Relays Chips +- **Description**: Icons representing each of the relays states. +- **Type**: Icon only with no touch commands. +- **Availability**: Global (available even when page is not visible). +- **Ids**: `home.chip_relay1` and `home.chip_relay2`. + +#### Climate Chip +- **Description**: Icon representing the state of the main climate entity. +- **Type**: Icon only with no touch commands. +- **Availability**: Global (available even when page is not visible). +- **Ids**: `home.chip_relay1` and `home.chip_relay2`. + +### Page Home - Custom buttons +![Image](pics/Nextion_Components_Home_Custom_Buttons_EU.png) +![Image](pics/Nextion_Components_Home_Custom_Buttons_US.png) diff --git a/docs/pics/Nextion_Components_Home_Chips_EU.png b/docs/pics/Nextion_Components_Home_Chips_EU.png new file mode 100644 index 0000000000000000000000000000000000000000..416fdf4008b2eb4180c4f7c484878f6ffc8dc5dc GIT binary patch literal 26883 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 literal 0 HcmV?d00001 diff --git a/docs/pics/Nextion_Components_Home_Chips_US.png b/docs/pics/Nextion_Components_Home_Chips_US.png new file mode 100644 index 0000000000000000000000000000000000000000..8491b0211fdaad6a310d398bab725e7fba1aace8 GIT binary patch literal 21925 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 literal 0 HcmV?d00001 diff --git a/docs/pics/Nextion_Components_Home_Custom_Buttons_EU.png b/docs/pics/Nextion_Components_Home_Custom_Buttons_EU.png new file mode 100644 index 0000000000000000000000000000000000000000..d07857a27878fc624fc9c392b280ffd7bc90865c GIT binary patch literal 26640 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 literal 0 HcmV?d00001 diff --git a/esphome/nspanel_esphome_core.yaml b/esphome/nspanel_esphome_core.yaml index d5d99bc..adb9ab2 100644 --- a/esphome/nspanel_esphome_core.yaml +++ b/esphome/nspanel_esphome_core.yaml @@ -170,613 +170,851 @@ api: - script.execute: watchdog services: - ##### Updates an entity ##### - - service: entity - variables: - id: string - icon: string - icon_color: int[] - name: string - color: int[] - value: string - value_color: int[] - then: - - lambda: |- - if (!id(is_uploading_tft)) { - // Do something - } + # Command Service + # Sends custom commands directly to the display for dynamic interactions and updates. + # + # Usage: Useful for advanced customizations like displaying messages, updating statuses, + # or executing specific display commands. + # + # Parameter: + # - cmd (string): Command string to be sent. Refer to the Nextion Instruction Set for + # supported commands: https://nextion.tech/instruction-set/ + # + # Example service call: + # service: esphome._command + # data: + # cmd: "page home" # Command to navigate to the "Home" page. + # + # NOTE: Replace with the specific panel name in your Home Assistant setup. + # Ensure the cmd string is properly formatted for your display's command set. + - service: command + variables: + cmd: string + then: + - lambda: |- + if (!id(is_uploading_tft)) + disp1->send_command_printf("%s", cmd.c_str()); - ##### Service to send a command "printf" directly to the display ##### - - service: send_command_printf - variables: - cmd: string - then: - - lambda: |- - if (!id(is_uploading_tft)) - disp1->send_command_printf("%s", cmd.c_str()); + # Component Color Service + # Changes the foreground color of specified components on the display for dynamic UI customization. + # + # Usage: Perfect for creating visually dynamic interfaces. Use this service to change component colors based on conditions, events, + # or user actions, such as indicating status changes or highlighting elements. + # + # Parameters: + # - id (string): Identifier of the component to change color. Ensure this matches the component's ID in your display layout. + # - color (int[]): New color for the component, specified as an RGB array (e.g., [255, 0, 0] for red). + # + # Example service call: + # service: esphome._component_color + # data: + # id: "home.time" # Component ID whose color will be changed. + # color: [255, 0, 0] # New color to apply to the component, making it red. + # + # NOTE: Replace with the specific panel name in your Home Assistant setup to ensure correct service execution. + # Ensure the 'id' and 'color' parameters accurately specify the component and the new color to be applied. + - service: component_color + variables: + id: string + color: int[] + then: + - lambda: |- + if (!id(is_uploading_tft)) + set_component_color->execute(id, color); - ##### Service to send a command "text_printf" directly to the display ##### - - service: send_command_text_printf - variables: - component: string - message: string - then: - - lambda: |- - if (!id(is_uploading_tft)) - disp1->set_component_text_printf(component.c_str(), "%s", message.c_str()); + # Component Hide Service + # Allows for dynamic interface changes by hiding specified components on the display. + # + # Usage: Ideal for interactive user interfaces that need to adapt by hiding elements based on user actions, conditions, or events. + # + # Parameters: + # - id (string): Identifier of the component to be hidden. Ensure this matches the component's ID in your display layout. + # + # Example service call: + # service: esphome._component_hide + # data: + # id: "date" # Example: Hides the date display on the Home page. + # + # NOTE: Replace with the specific panel name in your Home Assistant setup to ensure correct service execution. + # Ensure the 'id' matches the component on your display you wish to hide. + # + # IMPORTANT: This service functions only when the target page is visible. The component id should not include the page id. + # If the component being hidden is not on the current page, the command will fail, and an error message will be logged. + - service: component_hide + variables: + id: string + then: + - lambda: |- + if (!id(is_uploading_tft)) + disp1->hide_component(id.c_str()); - ##### Service to send a command "component_value (Dualstate Button)" directly to the display ##### - - service: send_command_value - variables: - component: string - val: int - then: - - lambda: |- - if (!id(is_uploading_tft)) - disp1->set_component_value(component.c_str(), val); + # Component Show Service + # Enables dynamic interface changes by making specified components visible on the display again. + # + # Usage: Perfect for interactive user interfaces that adapt by showing elements based on user actions, conditions, or events. + # + # Parameters: + # - id (string): The component's identifier to be made visible. This must match your display's component ID accurately. + # + # Example service call: + # service: esphome._component_show + # data: + # id: "date" # Example: Makes the date display visible on the Home page if previously hidden. + # + # NOTE: Replace with the specific panel name in your Home Assistant setup to ensure correct service execution. + # Confirm the 'id' correctly targets the component you wish to show for effective interface adaptation. + - service: component_show + variables: + id: string + then: + - lambda: |- + if (!id(is_uploading_tft)) + disp1->show_component(id.c_str()); - ##### Service to send a command "hide componente" directly to the display ##### - - service: send_command_hide - variables: - component: string - then: - - lambda: |- - if (!id(is_uploading_tft)) - disp1->hide_component(component.c_str()); + # Component Text Service + # Updates text content for a specified component on the display, ideal for dynamic updates. + # + # Usage: Perfect for updating status messages, labels, or any text-based information in real-time. + # + # Parameters: + # - id (string): Identifier of the component. Ensure it matches the component's ID in your display layout. + # - text (string): New text content to be displayed. Supports both static and dynamic content. + # + # Example service call: + # service: esphome._component_text + # data: + # id: "home.time" # Component ID to update. Match this with your display's component ID. + # text: "12:34" # New text content to display. + # + # NOTE: Replace with your specific panel name in your Home Assistant setup. + # Ensure 'id' accurately targets the correct component for the text update to be successful. + - service: component_text + variables: + id: string + text: string + then: + - lambda: |- + if (!id(is_uploading_tft)) + disp1->set_component_text_printf(id.c_str(), "%s", text.c_str()); - ##### Service to send a command "show componente" directly to the display ##### - - service: send_command_show - variables: - component: string - then: - - lambda: |- - if (!id(is_uploading_tft)) - disp1->show_component(component.c_str()); + # Component Value Service + # This service updates the numerical value of a specified component on the display, + # ideal for dynamic changes like counters or sensor readings. + # + # Usage: Perfect for real-time updates of numeric values such as temperature, humidity, or progress indicators. + # + # Parameters: + # - id (string): Identifier of the component to update. Must match the component's ID in the display layout. + # - val (int): New integer value to set for the component. Adjust based on the data type you're displaying. + # + # Example service call: + # service: esphome._component_val + # data: + # id: "cover.coverslider" # Ensure this ID matches your component's ID on the display. + # val: 25 # New value to be displayed. + # + # NOTE: Replace with the specific panel name in your Home Assistant setup. + # Confirm the 'id' correctly targets the intended component for the update. + - service: component_val + variables: + id: string + val: int + then: + - lambda: |- + if (!id(is_uploading_tft)) + disp1->set_component_value(id.c_str(), val); - ##### Service to send a command "font color" directly to the display ##### - - service: set_component_color - variables: - component: string - foreground: int[] - then: - - lambda: |- - if (!id(is_uploading_tft)) - set_component_color->execute(component, foreground); - - ##### Service to play a rtttl tones ##### - # Example tones : https://codebender.cc/sketch:109888#RTTTL%20Songs.ino - - service: play_rtttl - variables: - song_str: string - then: - - rtttl.play: - rtttl: !lambda 'return song_str;' - - #### Service to populate the alarm settings page ##### - - service: alarm_settings - variables: - page_title: string - state: string - supported_features: int - code_format: string - code_arm_required: bool - entity: string - mui_alarm: string[] - then: - - lambda: |- - // Is page Alarm visible? - if (current_page->state == "alarm" and not id(is_uploading_tft)) // To do: This page constructor should be moved to Blueprint - { // Update alarm page - detailed_entity->publish_state(entity); - - // Alarm page - Header - update_alarm_icon->execute("icon_state", state.c_str()); - if (page_title.find("\\r") != std::string::npos) { - page_title = page_title.replace(page_title.find("\\r"), 2, " "); - } - disp1->set_component_text_printf("page_label", "%s", page_title.c_str()); - disp1->set_component_text_printf("code_format", "%s", code_format.c_str()); - if (code_arm_required) disp1->set_component_text_printf("code_arm_req", "1"); - else disp1->set_component_text_printf("code_arm_req", "0"); - - // Alarm page - Button's text - display_wrapped_text->execute("bt_home_text", mui_alarm[0].c_str(), 10); - display_wrapped_text->execute("bt_away_text", mui_alarm[1].c_str(), 10); - display_wrapped_text->execute("bt_night_text", mui_alarm[2].c_str(), 10); - display_wrapped_text->execute("bt_vacat_text", mui_alarm[3].c_str(), 10); - display_wrapped_text->execute("bt_bypass_text", mui_alarm[4].c_str(), 10); - display_wrapped_text->execute("bt_disarm_text", mui_alarm[5].c_str(), 10); - - // Alarm page - Buttons - if (supported_features & 1 or state == "armed_home") // Alarm - Button - Home - { - disp1->send_command_printf("bt_home_pic.pic=%i", (state == "armed_home") ? 43 : 42); - disp1->set_component_background_color("bt_home_text", (state == "armed_home") ? 19818 : 52857); - disp1->set_component_background_color("bt_home_icon", (state == "armed_home") ? 19818 : 52857); - disp1->set_component_font_color("bt_home_text", (state == "armed_home") ? 65535 : 0); - disp1->set_component_font_color("bt_home_icon", (state == "armed_home") ? 65535 : 0); - if (state == "armed_home") disp1->hide_component("bt_home"); else disp1->show_component("bt_home"); - } - if (supported_features & 2 or state == "armed_away") // Alarm - Button - Away - { - disp1->send_command_printf("bt_away_pic.pic=%i", (state == "armed_away") ? 43 : 42); - disp1->set_component_background_color("bt_away_text", (state == "armed_away") ? 19818 : 52857); - disp1->set_component_background_color("bt_away_icon", (state == "armed_away") ? 19818 : 52857); - disp1->set_component_font_color("bt_away_text", (state == "armed_away") ? 65535 : 0); - disp1->set_component_font_color("bt_away_icon", (state == "armed_away") ? 65535 : 0); - if (state == "armed_away") disp1->hide_component("bt_away"); else disp1->show_component("bt_away"); - } - if (supported_features & 4 or state == "armed_night") // Alarm - Button - Night - { - disp1->send_command_printf("bt_night_pic.pic=%i", (state == "armed_night") ? 43 : 42); - disp1->set_component_background_color("bt_night_text", (state == "armed_night") ? 19818 : 52857); - disp1->set_component_background_color("bt_night_icon", (state == "armed_night") ? 19818 : 52857); - disp1->set_component_font_color("bt_night_text", (state == "armed_night") ? 65535 : 0); - disp1->set_component_font_color("bt_night_icon", (state == "armed_night") ? 65535 : 0); - if (state == "armed_night") disp1->hide_component("bt_night"); else disp1->show_component("bt_night"); - } - if (supported_features & 32 or state == "armed_vacation") // Alarm - Button - Vacation - { - disp1->send_command_printf("bt_vacat_pic.pic=%i", (state == "armed_vacation") ? 43 : 42); - disp1->set_component_background_color("bt_vacat_text", (state == "armed_vacation") ? 19818 : 52857); - disp1->set_component_background_color("bt_vacat_icon", (state == "armed_vacation") ? 19818 : 52857); - disp1->set_component_font_color("bt_vacat_text", (state == "armed_vacation") ? 65535 : 0); - disp1->set_component_font_color("bt_vacat_icon", (state == "armed_vacation") ? 65535 : 0); - if (state == "armed_vacation") disp1->hide_component("bt_vacat"); else disp1->show_component("bt_vacat"); - } - if (supported_features & 16 or state == "armed_bypass") // Alarm - Button - Custom bypass - { - disp1->send_command_printf("bt_bypass_pic.pic=%i", (state == "armed_bypass") ? 43 : 42); - disp1->set_component_background_color("bt_bypass_text", (state == "armed_bypass") ? 19818 : 52857); - disp1->set_component_background_color("bt_bypass_icon", (state == "armed_bypass") ? 19818 : 52857); - disp1->set_component_font_color("bt_bypass_text", (state == "armed_bypass") ? 65535 : 0); - disp1->set_component_font_color("bt_bypass_icon", (state == "armed_bypass") ? 65535 : 0); - if (state == "armed_bypass") disp1->hide_component("bt_bypass"); else disp1->show_component("bt_bypass"); - } - if ( true ) // Alarm - Button - Disarm - { - disp1->send_command_printf("bt_disarm_pic.pic=%i", (state == "disarmed") ? 43 : 42); - disp1->set_component_background_color("bt_disarm_text", (state == "disarmed") ? 19818 : 52857); - disp1->set_component_background_color("bt_disarm_icon", (state == "disarmed") ? 19818 : 52857); - disp1->set_component_font_color("bt_disarm_text", (state == "disarmed") ? 65535 : 0); - disp1->set_component_font_color("bt_disarm_icon", (state == "disarmed") ? 65535 : 0); - if (state == "disarmed") disp1->hide_component("bt_disarm"); else disp1->show_component("bt_disarm"); - } + # Icon Service + # This service updates a chip or custom button's icon, color, and visibility within Home Assistant. + # + # Usage: Ideal for dynamically updating icons on your Panel for a customizable UI. + # + # Parameters: + # - id (string): Identifier of the component. See "Screen components" in the documentation. + # - icon (string): Icon codepoint, e.g., "/uE6E8" for `mdi:lightbulb-on-outline`. + # - icon_color (int[]): RGB color array for the icon, e.g., [0, 255, 0] for green. + # - visible (bool): Set to `true` for visible or `false` for hidden. + # + # Example service call: + # service: esphome._icon + # data: + # id: "home.chip03" + # icon: "/uE6E8" # Example for mdi:lightbulb-on-outline + # icon_color: [0, 255, 0] # Green + # visible: true + # + # NOTE: Replace with the specific panel name for your Home Assistant configuration. + - service: icon + variables: + id: string + icon: string + icon_color: int[] + visible: bool + then: + - lambda: |- + if (!id(is_uploading_tft)) { + // Do something } + # Init Global Service Configuration + # Transfers global settings from the blueprint to ESPHome, configuring the necessary parameters for optimal operation. + # + # Usage: Essential during initialization or when updating global settings to reflect changes in the blueprint. Affects overall functionality and UI aspects. + # + # Parameters: + # - blueprint_version (string): Version of the blueprint in use. + # - embedded_climate (bool): Indicates if climate control is integrated. + # - embedded_climate_friendly_name (string): Friendly name for the climate control feature. + # - embedded_indoor_temperature (bool): Enables indoor temperature display. + # - mui_please_confirm (string): Localized message for confirmation prompts. + # - mui_unavailable (string): Localized message indicating unavailability. + # - screensaver_time (bool): Toggles the screensaver time display. + # - screensaver_time_color (int[]): RGB color for the screensaver time display, e.g., [165, 42, 42] for reddish-brown. + # + # Example service call: + service: esphome._init_global + data: + blueprint_version: "4.2.5" + embedded_climate: true + embedded_climate_friendly_name: "Home Climate" + embedded_indoor_temperature: true + mui_please_confirm: "Confirme, por favor." + mui_unavailable: "Indisponível" + screensaver_time: true + screensaver_time_color: [165, 42, 42] # Reddish-brown + # + # NOTE: Replace with your panel's specific name as configured in Home Assistant. + # This initialization should occur to align ESPHome with the current global settings outlined in your blueprint. + - service: init_global + variables: + blueprint_version: string + embedded_climate: bool + embedded_climate_friendly_name: string + embedded_indoor_temperature: bool + mui_please_confirm: string + mui_unavailable: string + screensaver_time: bool + screensaver_time_color: int[] + then: + - script.execute: + id: global_settings + blueprint_version: !lambda "return blueprint_version;" + embedded_climate: !lambda "return embedded_climate;" + embedded_climate_friendly_name: !lambda "return embedded_climate_friendly_name;" + embedded_indoor_temperature: !lambda "return embedded_indoor_temperature;" + mui_please_confirm: !lambda "return mui_please_confirm;" + mui_unavailable: !lambda "return mui_unavailable;" + screensaver_time: !lambda "return screensaver_time;" + screensaver_time_color: !lambda "return screensaver_time_color;" + - script.wait: global_settings + - lambda: |- + blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 5)); + ##### Service for transferring relay's settings from the blueprint to ESPHome ##### - - service: relay_settings - variables: - 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: - - 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: |- - blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 4)); + - service: init_relays + variables: + 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: + - 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: |- + blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 4)); - ##### Service for transferring global settings from the blueprint to ESPHome ##### - - service: global_settings - variables: - blueprint_version: string - embedded_climate: bool - embedded_climate_friendly_name: string - embedded_indoor_temperature: bool - temperature_unit_is_fahrenheit: bool # Deprecated - mui_please_confirm: string - mui_unavailable: string - screensaver_time: bool - screensaver_time_color: int[] - then: - - script.execute: - id: global_settings - blueprint_version: !lambda "return blueprint_version;" - embedded_climate: !lambda "return embedded_climate;" - embedded_climate_friendly_name: !lambda "return embedded_climate_friendly_name;" - embedded_indoor_temperature: !lambda "return embedded_indoor_temperature;" - # temperature_unit_is_fahrenheit: !lambda "return temperature_unit_is_fahrenheit;" # Deprecated - mui_please_confirm: !lambda "return mui_please_confirm;" - mui_unavailable: !lambda "return mui_unavailable;" - screensaver_time: !lambda "return screensaver_time;" - screensaver_time_color: !lambda "return screensaver_time_color;" - - script.wait: global_settings - - lambda: |- - blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 5)); + # Notification Clear Service + # This service removes any displayed notifications from the screen, helping to keep the user interface clean and focused on its primary functions. + # + # Usage: Perfect for scenarios where temporary notifications or alerts need to be dismissed manually or automatically after a certain period or event. + # + # Example service call: + # service: esphome._notification_clear + # + # NOTE: Replace with the actual name of your panel configured in Home Assistant. + # Invoking this service clears any active notifications, ensuring the display is ready for the next interaction or message. + - service: notification_clear + then: + - script.execute: notification_clear - ##### Service to show a notification-message on the screen ##### - - service: notification_show - variables: - label: string - message: string - then: - - lambda: |- - if (!id(is_uploading_tft)) { - ESP_LOGV("service.notification_show", "Starting"); + # Notification Show Service + # Displays a notification message on the screen, useful for alerts or informational updates. + # + # Usage: Ideal for immediate feedback or notifications through the display interface. Automatic text wrapping occurs to fit the display, but manual line breaks can be inserted with `\r` for precise formatting. + # + # Parameters: + # - label (string): Title or label for the notification, displayed in a prominent format. + # - message (string): Detailed message or content of the notification. Include `\r` to insert a line break, allowing for custom formatting. Without `\r`, text will wrap according to the display's dimensions. + # + # Example service call: + # service: esphome._notification_show + # data: + # label: "Security Alert" + # message: "Front door opened at 10:30 PM\rPlease check the entrance." + # + # NOTE: The use of `\r` for line breaks allows for tailored notification appearance on the display. + - service: notification_show + variables: + label: string + message: string + then: + - lambda: |- + if (!id(is_uploading_tft)) { + ESP_LOGV("service.notification_show", "Starting"); - disp1->goto_page("notification"); - disp1->set_component_text_printf("notification.notifi_label", "%s", label.c_str()); + disp1->goto_page("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); + display_wrapped_text->execute("notification.notifi_text01", message.c_str(), display_mode->state == 2 ? 23 : 32); - notification_label->publish_state(label.c_str()); - notification_text->publish_state(message.c_str()); - timer_reset_all->execute(current_page->state.c_str()); - refresh_notification->execute(); - notification_unread->turn_on(); - if (notification_sound->state) buzzer->play("two short:d=4,o=5,b=100:16e6,16e6"); - } + notification_label->publish_state(label.c_str()); + notification_text->publish_state(message.c_str()); + timer_reset_all->execute(current_page->state.c_str()); + refresh_notification->execute(); + notification_unread->turn_on(); + if (notification_sound->state) buzzer->play("two short:d=4,o=5,b=100:16e6,16e6"); + } - ##### Service to clear the notification ##### - - service: notification_clear - then: - - script.execute: notification_clear + # RTTTL Play Service + # Plays melodies encoded in RTTTL format, suitable for audio feedback, notifications, or simple tunes. + # + # Usage: Ideal for projects that require audio signals like notifications, alerts, or melodies. + # RTTTL (Ring Tone Text Transfer Language) is a compact, text-based format for storing melodies, + # making it perfect for use with simple audio devices such as buzzers. + # + # Parameters: + # - tone (string): The RTTTL string for the melody to be played. It should follow the RTTTL format, + # including the melody's name, default settings, and a sequence of notes. + # + # Example tones and inspiration can be found here: https://codebender.cc/sketch:109888#RTTTL%20Songs.ino + # + # Home Assistant Example: + # service: esphome._rtttl_play + # data: + # tone: "d=4,o=5,b=140:c,e,g,8p,c6,e6,g6,8p,c7,p" # Example RTTTL melody string + # + # NOTE: Replace with the specific panel name in your Home Assistant setup + # to ensure correct execution. Ensure the 'tone' parameter contains a valid RTTTL string + # for successful melody playback. + - service: rtttl_play + variables: + tone: string + then: + - rtttl.play: + rtttl: !lambda 'return tone;' + + # Value Service + # Updates an entity to display specific values with dynamic icons, names, and color codes. + # + # Usage: Perfect for entities requiring dynamic information display like sensor readings or state values. + # Customize with icons, names, and colors for a personalized UI experience. + # + # Parameters: + # - id (string): Identifier of the entity. See "Screen components" for entity IDs. + # - icon (string): Icon codepoint (e.g., "/uE6E8" for mdi:thermometer) from HASwitchPlate Material Design Icons. + # - icon_color (int[]): RGB color array for the icon (e.g., [255, 0, 0] for red). + # - name (string): Display name for the entity (e.g., "Temperature"). + # - value (string): Actual value to display (e.g., "75°F"). + # - value_color (int[]): RGB color array for the value text (e.g., [255, 255, 0] for yellow). + # + # Example service call: + # service: esphome._value + # data: + # id: "sensor.temperature" + # icon: "/uE6E8" # mdi:thermometer + # icon_color: [255, 0, 0] # Red + # name: "Temperature" + # value: "75°F" + # value_color: [255, 255, 0] # Yellow + # + # NOTE: Ensure to replace with the specific panel name configured in your Home Assistant. + - service: value + variables: + id: string + icon: string + icon_color: int[] + name: string + value: string + value_color: int[] + then: + - lambda: |- + if (!id(is_uploading_tft)) { + // Do something + } + + # Wake Up Service + # Activates the display from a screensaver or low-power state, ideal for scenarios where the display + # needs to become active upon user interaction or automated triggers, such as motion detection. + # + # Usage: Ensures energy conservation by keeping the display off when not in use, and available when needed. + # + # Parameters: + # - reset_timer (bool): Determines whether to reset the sleep and dimming timers upon waking the display. + # Setting this to true keeps the display active during user presence, while false retains the current timer settings. + # + # Example service call: + # service: esphome._wake_up + # data: + # reset_timer: true # Ensures the display remains active during user presence, resets timers. + # + # NOTE: Replace with the actual name of your panel configured in Home Assistant to ensure + # the service executes correctly. This configuration wakes the display and optionally resets timers based + # on the 'reset_timer' parameter. + - service: wake_up + variables: + reset_timer: bool + then: + - lambda: |- + if (not id(is_uploading_tft)) { + if (current_page->state == "screensaver") disp1->goto_page(wakeup_page_name->state.c_str()); + if (reset_timer) + timer_reset_all->execute(wakeup_page_name->state.c_str()); + else { + timer_sleep->execute(wakeup_page_name->state.c_str(), int(timeout_sleep->state)); + timer_dim->execute(wakeup_page_name->state.c_str(), int(timeout_dim->state)); + } + } + + #### Service to populate the alarm settings page ##### + - service: alarm_settings + variables: + page_title: string + state: string + supported_features: int + code_format: string + code_arm_required: bool + entity: string + mui_alarm: string[] + then: + - lambda: |- + // Is page Alarm visible? + if (current_page->state == "alarm" and not id(is_uploading_tft)) // To do: This page constructor should be moved to Blueprint + { // Update alarm page + detailed_entity->publish_state(entity); + + // Alarm page - Header + update_alarm_icon->execute("icon_state", state.c_str()); + if (page_title.find("\\r") != std::string::npos) { + page_title = page_title.replace(page_title.find("\\r"), 2, " "); + } + disp1->set_component_text_printf("page_label", "%s", page_title.c_str()); + disp1->set_component_text_printf("code_format", "%s", code_format.c_str()); + if (code_arm_required) disp1->set_component_text_printf("code_arm_req", "1"); + else disp1->set_component_text_printf("code_arm_req", "0"); + + // Alarm page - Button's text + display_wrapped_text->execute("bt_home_text", mui_alarm[0].c_str(), 10); + display_wrapped_text->execute("bt_away_text", mui_alarm[1].c_str(), 10); + display_wrapped_text->execute("bt_night_text", mui_alarm[2].c_str(), 10); + display_wrapped_text->execute("bt_vacat_text", mui_alarm[3].c_str(), 10); + display_wrapped_text->execute("bt_bypass_text", mui_alarm[4].c_str(), 10); + display_wrapped_text->execute("bt_disarm_text", mui_alarm[5].c_str(), 10); + + // Alarm page - Buttons + if (supported_features & 1 or state == "armed_home") // Alarm - Button - Home + { + disp1->send_command_printf("bt_home_pic.pic=%i", (state == "armed_home") ? 43 : 42); + disp1->set_component_background_color("bt_home_text", (state == "armed_home") ? 19818 : 52857); + disp1->set_component_background_color("bt_home_icon", (state == "armed_home") ? 19818 : 52857); + disp1->set_component_font_color("bt_home_text", (state == "armed_home") ? 65535 : 0); + disp1->set_component_font_color("bt_home_icon", (state == "armed_home") ? 65535 : 0); + if (state == "armed_home") disp1->hide_component("bt_home"); else disp1->show_component("bt_home"); + } + if (supported_features & 2 or state == "armed_away") // Alarm - Button - Away + { + disp1->send_command_printf("bt_away_pic.pic=%i", (state == "armed_away") ? 43 : 42); + disp1->set_component_background_color("bt_away_text", (state == "armed_away") ? 19818 : 52857); + disp1->set_component_background_color("bt_away_icon", (state == "armed_away") ? 19818 : 52857); + disp1->set_component_font_color("bt_away_text", (state == "armed_away") ? 65535 : 0); + disp1->set_component_font_color("bt_away_icon", (state == "armed_away") ? 65535 : 0); + if (state == "armed_away") disp1->hide_component("bt_away"); else disp1->show_component("bt_away"); + } + if (supported_features & 4 or state == "armed_night") // Alarm - Button - Night + { + disp1->send_command_printf("bt_night_pic.pic=%i", (state == "armed_night") ? 43 : 42); + disp1->set_component_background_color("bt_night_text", (state == "armed_night") ? 19818 : 52857); + disp1->set_component_background_color("bt_night_icon", (state == "armed_night") ? 19818 : 52857); + disp1->set_component_font_color("bt_night_text", (state == "armed_night") ? 65535 : 0); + disp1->set_component_font_color("bt_night_icon", (state == "armed_night") ? 65535 : 0); + if (state == "armed_night") disp1->hide_component("bt_night"); else disp1->show_component("bt_night"); + } + if (supported_features & 32 or state == "armed_vacation") // Alarm - Button - Vacation + { + disp1->send_command_printf("bt_vacat_pic.pic=%i", (state == "armed_vacation") ? 43 : 42); + disp1->set_component_background_color("bt_vacat_text", (state == "armed_vacation") ? 19818 : 52857); + disp1->set_component_background_color("bt_vacat_icon", (state == "armed_vacation") ? 19818 : 52857); + disp1->set_component_font_color("bt_vacat_text", (state == "armed_vacation") ? 65535 : 0); + disp1->set_component_font_color("bt_vacat_icon", (state == "armed_vacation") ? 65535 : 0); + if (state == "armed_vacation") disp1->hide_component("bt_vacat"); else disp1->show_component("bt_vacat"); + } + if (supported_features & 16 or state == "armed_bypass") // Alarm - Button - Custom bypass + { + disp1->send_command_printf("bt_bypass_pic.pic=%i", (state == "armed_bypass") ? 43 : 42); + disp1->set_component_background_color("bt_bypass_text", (state == "armed_bypass") ? 19818 : 52857); + disp1->set_component_background_color("bt_bypass_icon", (state == "armed_bypass") ? 19818 : 52857); + disp1->set_component_font_color("bt_bypass_text", (state == "armed_bypass") ? 65535 : 0); + disp1->set_component_font_color("bt_bypass_icon", (state == "armed_bypass") ? 65535 : 0); + if (state == "armed_bypass") disp1->hide_component("bt_bypass"); else disp1->show_component("bt_bypass"); + } + if ( true ) // Alarm - Button - Disarm + { + disp1->send_command_printf("bt_disarm_pic.pic=%i", (state == "disarmed") ? 43 : 42); + disp1->set_component_background_color("bt_disarm_text", (state == "disarmed") ? 19818 : 52857); + disp1->set_component_background_color("bt_disarm_icon", (state == "disarmed") ? 19818 : 52857); + disp1->set_component_font_color("bt_disarm_text", (state == "disarmed") ? 65535 : 0); + disp1->set_component_font_color("bt_disarm_icon", (state == "disarmed") ? 65535 : 0); + if (state == "disarmed") disp1->hide_component("bt_disarm"); else disp1->show_component("bt_disarm"); + } + } ##### Service to open information for settings-page(s) - - service: open_entity_settings_page - variables: - page: string - page_label: string - page_icon: string - page_icon_color: int[] - entity: string - back_page: string - then: - - script.execute: - id: open_entity_settings_page - page: !lambda "return page;" - page_label: !lambda "return page_label;" - page_icon: !lambda "return page_icon;" - page_icon_color: !lambda "return page_icon_color;" - entity: !lambda "return entity;" - back_page: !lambda "return back_page;" + - service: open_entity_settings_page + variables: + page: string + page_label: string + page_icon: string + page_icon_color: int[] + entity: string + back_page: string + then: + - script.execute: + id: open_entity_settings_page + page: !lambda "return page;" + page_label: !lambda "return page_label;" + page_icon: !lambda "return page_icon;" + page_icon_color: !lambda "return page_icon_color;" + entity: !lambda "return entity;" + back_page: !lambda "return back_page;" # Service to show a QR code on the display (ex. for WiFi password) - - service: qrcode - variables: - title: string - qrcode: string - show: bool - then: - - lambda: |- - 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"); - blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 2)); - } + - service: qrcode + variables: + title: string + qrcode: string + show: bool + then: + - lambda: |- + 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"); + blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 2)); + } #### Service to set climate state #### - - service: set_climate - variables: - current_temp: float - supported_features: int - target_temp: float - target_temp_high: float - target_temp_low: float - temp_step: int - total_steps: int - temp_offset: int - climate_icon: string - embedded_climate: bool - entity: string - then: - - if: - condition: - lambda: 'return not id(is_uploading_tft);' - then: - - lambda: |- - if (current_page->state == "climate") detailed_entity->publish_state(entity); - - script.execute: - id: set_climate - current_temp: !lambda "return current_temp;" - supported_features: !lambda "return supported_features;" - target_temp: !lambda "return target_temp;" - target_temp_high: !lambda "return target_temp_high;" - target_temp_low: !lambda "return target_temp_low;" - temp_step: !lambda "return temp_step;" - total_steps: !lambda "return total_steps;" - temp_offset: !lambda "return temp_offset;" - climate_icon: !lambda "return climate_icon;" - embedded_climate: !lambda "return embedded_climate;" + - service: set_climate + variables: + current_temp: float + supported_features: int + target_temp: float + target_temp_high: float + target_temp_low: float + temp_step: int + total_steps: int + temp_offset: int + climate_icon: string + embedded_climate: bool + entity: string + then: + - if: + condition: + lambda: 'return not id(is_uploading_tft);' + then: + - lambda: |- + if (current_page->state == "climate") detailed_entity->publish_state(entity); + - script.execute: + id: set_climate + current_temp: !lambda "return current_temp;" + supported_features: !lambda "return supported_features;" + target_temp: !lambda "return target_temp;" + target_temp_high: !lambda "return target_temp_high;" + target_temp_low: !lambda "return target_temp_low;" + temp_step: !lambda "return temp_step;" + total_steps: !lambda "return total_steps;" + temp_offset: !lambda "return temp_offset;" + climate_icon: !lambda "return climate_icon;" + embedded_climate: !lambda "return embedded_climate;" #### Service to set the buttons #### - - service: set_button - variables: - page: string - id: string - state: bool - icon: string - icon_color: int[] - icon_font: int - bri: string - label: string - then: - - lambda: |- - if (page == current_page->state and not id(is_uploading_tft)) { - std::string btnicon = id.c_str() + std::string("icon"); - std::string btntext = id.c_str() + std::string("text"); - std::string btnbri = id.c_str() + std::string("bri"); - std::string btnpic = id.c_str() + std::string("pic"); - uint8_t bg_pic = state ? 47 : 46; - uint16_t txt_color = state ? 10597 : 65535; - disp1->send_command_printf("%spic.picc=%u", id.c_str(), bg_pic); - disp1->send_command_printf("%sbri.picc=%u", id.c_str(), bg_pic); - disp1->send_command_printf("%stext.picc=%u", id.c_str(), bg_pic); - disp1->send_command_printf("%sicon.picc=%u", id.c_str(), bg_pic); - 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_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) - disp1->set_component_text_printf(btnbri.c_str(), "%s", bri.c_str()); - else - disp1->set_component_text_printf(btnbri.c_str(), " "); - disp1->show_component(btnpic.c_str()); - disp1->show_component(btnicon.c_str()); - disp1->show_component(btntext.c_str()); - disp1->show_component(btnbri.c_str()); - disp1->show_component(id.c_str()); - } else { - ESP_LOGW("service.set_button", "Skipping button `%s.%s` update.", page.c_str(), id.c_str()); - } - - ##### SERVICE TO WAKE UP THE DISPLAY ##### - - service: wake_up - variables: - reset_timer: bool - then: - - lambda: |- - if (not id(is_uploading_tft)) { - if (current_page->state == "screensaver") disp1->goto_page(wakeup_page_name->state.c_str()); - if (reset_timer) - timer_reset_all->execute(wakeup_page_name->state.c_str()); - else { - timer_sleep->execute(wakeup_page_name->state.c_str(), int(timeout_sleep->state)); - timer_dim->execute(wakeup_page_name->state.c_str(), int(timeout_dim->state)); + - service: set_button + variables: + page: string + id: string + state: bool + icon: string + icon_color: int[] + icon_font: int + bri: string + label: string + then: + - lambda: |- + if (page == current_page->state and not id(is_uploading_tft)) { + std::string btnicon = id.c_str() + std::string("icon"); + std::string btntext = id.c_str() + std::string("text"); + std::string btnbri = id.c_str() + std::string("bri"); + std::string btnpic = id.c_str() + std::string("pic"); + uint8_t bg_pic = state ? 47 : 46; + uint16_t txt_color = state ? 10597 : 65535; + disp1->send_command_printf("%spic.picc=%u", id.c_str(), bg_pic); + disp1->send_command_printf("%sbri.picc=%u", id.c_str(), bg_pic); + disp1->send_command_printf("%stext.picc=%u", id.c_str(), bg_pic); + disp1->send_command_printf("%sicon.picc=%u", id.c_str(), bg_pic); + 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_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) + disp1->set_component_text_printf(btnbri.c_str(), "%s", bri.c_str()); + else + disp1->set_component_text_printf(btnbri.c_str(), " "); + disp1->show_component(btnpic.c_str()); + disp1->show_component(btnicon.c_str()); + disp1->show_component(btntext.c_str()); + disp1->show_component(btnbri.c_str()); + disp1->show_component(id.c_str()); + } else { + ESP_LOGW("service.set_button", "Skipping button `%s.%s` update.", page.c_str(), id.c_str()); } - } #### Service to set the entities #### - - service: set_entity - variables: - ent_id: string - ent_icon: string - ent_label: string - ent_value: string - ent_value_xcen: string - then: - - lambda: |- - if (not id(is_uploading_tft)) { - std::string enticon = ent_id.c_str() + std::string("_pic"); - std::string entlabel = ent_id.c_str() + std::string("_label"); - std::string entxcen = ent_id.c_str() + std::string(".xcen=") + ent_value_xcen.c_str(); - disp1->set_component_text_printf(enticon.c_str(), "%s", ent_icon.c_str()); - if (strcmp(ent_icon.c_str(), "0") != 0) disp1->set_component_text_printf(enticon.c_str(), "%s", ent_icon.c_str()); - disp1->set_component_text_printf(entlabel.c_str(), "%s", ent_label.c_str()); - disp1->set_component_text_printf(ent_id.c_str(), "%s", ent_value.c_str()); - if (strcmp(ent_value_xcen.c_str(), "0") != 0) disp1->send_command_printf("%s", entxcen.c_str()); - } + - service: set_entity + variables: + ent_id: string + ent_icon: string + ent_label: string + ent_value: string + ent_value_xcen: string + then: + - lambda: |- + if (not id(is_uploading_tft)) { + std::string enticon = ent_id.c_str() + std::string("_pic"); + std::string entlabel = ent_id.c_str() + std::string("_label"); + std::string entxcen = ent_id.c_str() + std::string(".xcen=") + ent_value_xcen.c_str(); + disp1->set_component_text_printf(enticon.c_str(), "%s", ent_icon.c_str()); + if (strcmp(ent_icon.c_str(), "0") != 0) disp1->set_component_text_printf(enticon.c_str(), "%s", ent_icon.c_str()); + disp1->set_component_text_printf(entlabel.c_str(), "%s", ent_label.c_str()); + disp1->set_component_text_printf(ent_id.c_str(), "%s", ent_value.c_str()); + if (strcmp(ent_value_xcen.c_str(), "0") != 0) disp1->send_command_printf("%s", entxcen.c_str()); + } #### Service to populate the page Home ##### - - service: page_home - variables: - date_color: int - time_format: string - time_color: int - meridiem: string[] - chip_font_size: int - custom_buttons_font_size: int - notification_icon: string - notification_icon_color_normal: int[] - notification_icon_color_unread: int[] - qrcode: bool - qrcode_icon: string - qrcode_icon_color: int[] - entities_pages: bool - entities_pages_icon: string - entities_pages_icon_color: int[] - then: - - lambda: |- - if (not id(is_uploading_tft)) { - static const char *const TAG = "service.page_home"; - ESP_LOGV(TAG, "date_color: %" PRIi32, date_color); - ESP_LOGV(TAG, "time_format: %s", time_format.c_str()); - ESP_LOGV(TAG, "time_color: %" PRIi32, time_color); - ESP_LOGV(TAG, "meridiem: %i", meridiem.size()); - ESP_LOGV(TAG, "chip_font_size: %" PRIi32, chip_font_size); - ESP_LOGV(TAG, "custom_buttons_font_size: %" PRIi32, custom_buttons_font_size); - ESP_LOGV(TAG, "notification_icon: %s", notification_icon.c_str()); - ESP_LOGV(TAG, "notification_icon_color_normal: %i", notification_icon_color_normal.size()); - ESP_LOGV(TAG, "notification_icon_color_unread: %i", notification_icon_color_unread.size()); - ESP_LOGV(TAG, "qrcode: %s", YESNO(qrcode)); - ESP_LOGV(TAG, "qrcode_icon: %s", qrcode_icon.c_str()); - ESP_LOGV(TAG, "qrcode_icon_color: %i", qrcode_icon_color.size()); - ESP_LOGV(TAG, "entities_pages: %s", YESNO(entities_pages)); - ESP_LOGV(TAG, "entities_pages_icon: %s", entities_pages_icon.c_str()); - ESP_LOGV(TAG, "entities_pages_icon_color: %i", entities_pages_icon_color.size()); + - service: page_home + variables: + date_color: int + time_format: string + time_color: int + meridiem: string[] + chip_font_size: int + custom_buttons_font_size: int + notification_icon: string + notification_icon_color_normal: int[] + notification_icon_color_unread: int[] + qrcode: bool + qrcode_icon: string + qrcode_icon_color: int[] + entities_pages: bool + entities_pages_icon: string + entities_pages_icon_color: int[] + then: + - lambda: |- + if (not id(is_uploading_tft)) { + static const char *const TAG = "service.page_home"; + ESP_LOGV(TAG, "date_color: %" PRIi32, date_color); + ESP_LOGV(TAG, "time_format: %s", time_format.c_str()); + ESP_LOGV(TAG, "time_color: %" PRIi32, time_color); + ESP_LOGV(TAG, "meridiem: %i", meridiem.size()); + ESP_LOGV(TAG, "chip_font_size: %" PRIi32, chip_font_size); + ESP_LOGV(TAG, "custom_buttons_font_size: %" PRIi32, custom_buttons_font_size); + ESP_LOGV(TAG, "notification_icon: %s", notification_icon.c_str()); + ESP_LOGV(TAG, "notification_icon_color_normal: %i", notification_icon_color_normal.size()); + ESP_LOGV(TAG, "notification_icon_color_unread: %i", notification_icon_color_unread.size()); + ESP_LOGV(TAG, "qrcode: %s", YESNO(qrcode)); + ESP_LOGV(TAG, "qrcode_icon: %s", qrcode_icon.c_str()); + ESP_LOGV(TAG, "qrcode_icon_color: %i", qrcode_icon_color.size()); + ESP_LOGV(TAG, "entities_pages: %s", YESNO(entities_pages)); + ESP_LOGV(TAG, "entities_pages_icon: %s", entities_pages_icon.c_str()); + ESP_LOGV(TAG, "entities_pages_icon_color: %i", entities_pages_icon_color.size()); - // Localization - ESP_LOGV(TAG, "Load localization"); - id(mui_time_format) = time_format; - id(mui_meridiem) = meridiem; + // Localization + ESP_LOGV(TAG, "Load localization"); + id(mui_time_format) = time_format; + id(mui_meridiem) = meridiem; - // Date/Time colors - ESP_LOGV(TAG, "Load date/time colors"); - disp1->set_component_font_color("home.date", date_color); - disp1->set_component_font_color("home.time", time_color); - id(home_date_color) = date_color; - id(home_time_color) = time_color; + // Date/Time colors + ESP_LOGV(TAG, "Load date/time colors"); + disp1->set_component_font_color("home.date", date_color); + disp1->set_component_font_color("home.time", time_color); + id(home_date_color) = date_color; + id(home_time_color) = time_color; - // Chips icon size - ESP_LOGV(TAG, "Chips size"); - for (int i = 1; i <= 10; ++i) { - disp1->send_command_printf("home.icon_top_%02d.font=%" PRIu32, i, chip_font_size); + // Chips icon size + ESP_LOGV(TAG, "Chips size"); + for (int i = 1; i <= 10; ++i) { + disp1->send_command_printf("home.icon_top_%02d.font=%" PRIu32, i, chip_font_size); + } + disp1->send_command_printf("home.wifi_icon.font=%" PRIu32, chip_font_size); + id(home_chip_font_size) = chip_font_size; + + // Custom buttons icon size + ESP_LOGV(TAG, "Custom buttons sizes"); + id(home_custom_buttons_font_size) = custom_buttons_font_size; + for (int i = 1; i <= 7; ++i) { + disp1->send_command_printf("home.button%02d.font=%i", i, id(home_custom_buttons_font_size)); + } + disp1->send_command_printf("home.bt_notific.font=%i", id(home_custom_buttons_font_size)); + disp1->send_command_printf("home.bt_qrcode.font=%i", id(home_custom_buttons_font_size)); + disp1->send_command_printf("home.bt_entities.font=%i", id(home_custom_buttons_font_size)); + + // Notification button + 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; + + // 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); + + // 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); + + blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 1)); } - disp1->send_command_printf("home.wifi_icon.font=%" PRIu32, chip_font_size); - id(home_chip_font_size) = chip_font_size; - - // Custom buttons icon size - ESP_LOGV(TAG, "Custom buttons sizes"); - id(home_custom_buttons_font_size) = custom_buttons_font_size; - for (int i = 1; i <= 7; ++i) { - disp1->send_command_printf("home.button%02d.font=%i", i, id(home_custom_buttons_font_size)); - } - disp1->send_command_printf("home.bt_notific.font=%i", id(home_custom_buttons_font_size)); - disp1->send_command_printf("home.bt_qrcode.font=%i", id(home_custom_buttons_font_size)); - disp1->send_command_printf("home.bt_entities.font=%i", id(home_custom_buttons_font_size)); - - // Notification button - 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; - - // 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); - - // 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); - - blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 1)); - } #### Service to populate the page Settings ##### - - service: page_settings - variables: - reboot: string - brightness: string - bright: string - dim: string - then: - - lambda: |- - if (not id(is_uploading_tft)) { - if (not reboot.empty()) disp1->set_component_text_printf("settings.lbl_reboot", " %s", reboot.c_str()); - disp1->set_component_text_printf("settings.lbl_brightness", " %s", brightness.c_str()); - display_wrapped_text->execute("settings.lbl_bright", bright.c_str(), display_mode->state == 2 ? 25 : 10); - display_wrapped_text->execute("settings.lbl_dim", dim.c_str(), display_mode->state == 2 ? 25 : 10); - blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 3)); - } + - service: page_settings + variables: + reboot: string + brightness: string + bright: string + dim: string + then: + - lambda: |- + if (not id(is_uploading_tft)) { + if (not reboot.empty()) disp1->set_component_text_printf("settings.lbl_reboot", " %s", reboot.c_str()); + disp1->set_component_text_printf("settings.lbl_brightness", " %s", brightness.c_str()); + display_wrapped_text->execute("settings.lbl_bright", bright.c_str(), display_mode->state == 2 ? 25 : 10); + display_wrapped_text->execute("settings.lbl_dim", dim.c_str(), display_mode->state == 2 ? 25 : 10); + blueprint_status->publish_state(int(blueprint_status->raw_state) | (1 << 3)); + } #### Service to populate the media player page ##### - - service: media_player - variables: - entity: string - state: string - is_volume_muted: bool - friendly_name: string - volume_level: int - media_title: string - media_artist: string - media_duration: float - media_position: float - media_position_delta: float - supported_features: int - then: - - lambda: |- - if (current_page->state == "media_player" and not id(is_uploading_tft)) { - detailed_entity->publish_state(entity); - disp1->set_component_text_printf("page_label", "%s", friendly_name.c_str()); - display_wrapped_text->execute("track", media_title.c_str(), display_mode->state == 2 ? 16 : 27); - display_wrapped_text->execute("artist", media_artist.c_str(), display_mode->state == 2 ? 26 : 40); + - service: media_player + variables: + entity: string + state: string + is_volume_muted: bool + friendly_name: string + volume_level: int + media_title: string + media_artist: string + media_duration: float + media_position: float + media_position_delta: float + supported_features: int + then: + - lambda: |- + if (current_page->state == "media_player" and not id(is_uploading_tft)) { + detailed_entity->publish_state(entity); + disp1->set_component_text_printf("page_label", "%s", friendly_name.c_str()); + display_wrapped_text->execute("track", media_title.c_str(), display_mode->state == 2 ? 16 : 27); + display_wrapped_text->execute("artist", media_artist.c_str(), display_mode->state == 2 ? 26 : 40); - // on/off button - if (supported_features & 128 and state == "off") { //TURN_ON - disp1->set_component_foreground_color("bt_on_off", 65535); - disp1->show_component("bt_on_off"); - } else if (supported_features & 256 and state != "off") { //TURN_OFF - disp1->set_component_foreground_color("bt_on_off", 10597); - disp1->show_component("bt_on_off"); - } else disp1->hide_component("bt_on_off"); + // on/off button + if (supported_features & 128 and state == "off") { //TURN_ON + disp1->set_component_foreground_color("bt_on_off", 65535); + disp1->show_component("bt_on_off"); + } else if (supported_features & 256 and state != "off") { //TURN_OFF + disp1->set_component_foreground_color("bt_on_off", 10597); + disp1->show_component("bt_on_off"); + } else disp1->hide_component("bt_on_off"); - // play/pause button - if ((supported_features & 512 or supported_features & 16384) and state != "playing" and state != "off") { //PLAY_MEDIA+PLAY - disp1->set_component_text_printf("bt_play_pause", "%s", "\uE409"); // mdi:play - disp1->show_component("bt_play_pause"); - } else if (supported_features & 1 and state == "playing" ) { //PAUSE - disp1->set_component_text_printf("bt_play_pause", "%s", "\uE3E3"); // mdi:pause - disp1->show_component("bt_play_pause"); - } else disp1->hide_component("bt_play_pause"); + // play/pause button + if ((supported_features & 512 or supported_features & 16384) and state != "playing" and state != "off") { //PLAY_MEDIA+PLAY + disp1->set_component_text_printf("bt_play_pause", "%s", "\uE409"); // mdi:play + disp1->show_component("bt_play_pause"); + } else if (supported_features & 1 and state == "playing" ) { //PAUSE + disp1->set_component_text_printf("bt_play_pause", "%s", "\uE3E3"); // mdi:pause + disp1->show_component("bt_play_pause"); + } else disp1->hide_component("bt_play_pause"); - // bt_prev button - PREVIOUS_TRACK - if (supported_features & 16 and state != "off") disp1->show_component("bt_prev"); else disp1->hide_component("bt_prev"); - // bt_next button - NEXT_TRACK - if (supported_features & 32 and state != "off") disp1->show_component("bt_next"); else disp1->hide_component("bt_next"); + // bt_prev button - PREVIOUS_TRACK + if (supported_features & 16 and state != "off") disp1->show_component("bt_prev"); else disp1->hide_component("bt_prev"); + // bt_next button - NEXT_TRACK + if (supported_features & 32 and state != "off") disp1->show_component("bt_next"); else disp1->hide_component("bt_next"); - // Stop button - STOP - //if (supported_features & 4096 and (state == "playing" or state == "paused")) disp1->show_component("bt_stop"); else disp1->hide_component("bt_stop"); + // Stop button - STOP + //if (supported_features & 4096 and (state == "playing" or state == "paused")) disp1->show_component("bt_stop"); else disp1->hide_component("bt_stop"); - // mute/unmute button - VOLUME_MUTE - disp1->set_component_value("is_muted", is_volume_muted ? 1 : 0); - if (supported_features & 8 and is_volume_muted) { // unmute - disp1->set_component_text_printf("bt_mute", "%s", "\uEE07"); // mdi:volume-variant-off - disp1->show_component("bt_mute"); - } else if (supported_features & 8) { // mute - disp1->set_component_text_printf("bt_mute", "%s", "\uE57E"); // mdi:volume-low - disp1->show_component("bt_mute"); - } else disp1->hide_component("bt_mute"); + // mute/unmute button - VOLUME_MUTE + disp1->set_component_value("is_muted", is_volume_muted ? 1 : 0); + if (supported_features & 8 and is_volume_muted) { // unmute + disp1->set_component_text_printf("bt_mute", "%s", "\uEE07"); // mdi:volume-variant-off + disp1->show_component("bt_mute"); + } else if (supported_features & 8) { // mute + disp1->set_component_text_printf("bt_mute", "%s", "\uE57E"); // mdi:volume-low + disp1->show_component("bt_mute"); + } else disp1->hide_component("bt_mute"); - // VOLUME_SET - if (supported_features & 4) { - if (volume_level != id(last_volume_level)) { - id(last_volume_level) = volume_level; - disp1->set_component_text_printf("vol_text", "%" PRIu32 "%%", volume_level); - disp1->set_component_value("vol_slider", volume_level); + // VOLUME_SET + if (supported_features & 4) { + if (volume_level != id(last_volume_level)) { + id(last_volume_level) = volume_level; + disp1->set_component_text_printf("vol_text", "%" PRIu32 "%%", volume_level); + disp1->set_component_value("vol_slider", volume_level); + } + disp1->show_component("vol_slider"); + disp1->show_component("bt_vol_down"); + disp1->show_component("bt_vol_up"); + disp1->show_component("vol_text"); + } else { + disp1->hide_component("vol_slider"); + disp1->hide_component("bt_vol_down"); + disp1->hide_component("bt_vol_up"); + disp1->hide_component("vol_text"); } - disp1->show_component("vol_slider"); - disp1->show_component("bt_vol_down"); - disp1->show_component("bt_vol_up"); - disp1->show_component("vol_text"); - } else { - disp1->hide_component("vol_slider"); - disp1->hide_component("bt_vol_down"); - disp1->hide_component("bt_vol_up"); - disp1->hide_component("vol_text"); - } - if (media_duration > 0) { - if (media_duration != id(last_media_duration) or media_position != id(last_media_position)) { - id(last_media_duration) = media_duration; - id(last_media_position) = media_position; - disp1->set_component_value("prg_current", int(round(min(media_position + media_position_delta, media_duration)))); + if (media_duration > 0) { + if (media_duration != id(last_media_duration) or media_position != id(last_media_position)) { + id(last_media_duration) = media_duration; + id(last_media_position) = media_position; + disp1->set_component_value("prg_current", int(round(min(media_position + media_position_delta, media_duration)))); + } + disp1->set_component_value("prg_total", int(round(media_duration))); + disp1->send_command_printf("prg_timer.en=%i", (state == "playing") ? 1 : 0); + disp1->show_component("time_current"); + disp1->show_component("time_total"); + disp1->show_component("time_progress"); + } else { + disp1->send_command_printf("prg_timer.en=0"); + disp1->hide_component("time_current"); + disp1->hide_component("time_total"); + disp1->hide_component("time_progress"); } - disp1->set_component_value("prg_total", int(round(media_duration))); - disp1->send_command_printf("prg_timer.en=%i", (state == "playing") ? 1 : 0); - disp1->show_component("time_current"); - disp1->show_component("time_total"); - disp1->show_component("time_progress"); - } else { - disp1->send_command_printf("prg_timer.en=0"); - disp1->hide_component("time_current"); - disp1->hide_component("time_total"); - disp1->hide_component("time_progress"); } - } ##### START - DISPLAY START CONFIGURATION ##### display: @@ -1967,7 +2205,6 @@ script: embedded_climate: bool embedded_climate_friendly_name: string embedded_indoor_temperature: bool - # temperature_unit_is_fahrenheit: bool # Deprecated mui_please_confirm: string mui_unavailable: string screensaver_time: bool diff --git a/nspanel_blueprint.yaml b/nspanel_blueprint.yaml index a9ac13e..0ed5885 100644 --- a/nspanel_blueprint.yaml +++ b/nspanel_blueprint.yaml @@ -119,18 +119,6 @@ blueprint: - label: 'H.MM 24H (ex. 8.30 and 20.30)' value: '%-H.%M' - delay: - name: Delay to avoid synchronization problem - description: > - *SYSTEM settings* - - *Synchronization issues may occur due to the **NETWORK / WLAN**. To avoid this, please select the delay (milliseconds) between each instruction sent to the panel* - default: '1' - selector: - number: - min: 0 - max: 100 - ##### WEATHER - Page Home / Weather 01-04 ##### ##### PLACEHOLDER ###################################################################### placeholder01: @@ -3409,7 +3397,7 @@ variables: settings: 'esphome.{{ nspanel_name }}_page_settings' qrcode: 'esphome.{{ nspanel_name }}_qrcode' relays: 'esphome.{{ nspanel_name }}_relay_settings' - command: 'esphome.{{ nspanel_name }}_send_command_printf' + command: 'esphome.{{ nspanel_name }}_command' page: alarm: 'esphome.{{ nspanel_name }}_alarm_settings' button: 'esphome.{{ nspanel_name }}_set_button' @@ -6039,9 +6027,6 @@ variables: color: "off" component: button07 - ###### SYNC SETTINGS ##### - delay_value: !input 'delay' - ###### MEDIA PLAYER REFRESH ##### media_player_update_interval: !input 'media_player_update_interval' media_player_update_delay: !input 'media_player_update_delay' @@ -7103,9 +7088,6 @@ action: }} entities_pages_icon_color: '{{ bt_entities.color_rgb }}' continue_on_error: true - - &delay-default - delay: - milliseconds: '{{ delay_value }}' ##### NSPanel Left Button Name ##### - if: '{{ hardware.buttons.left.name | length > 0 }}' @@ -7116,14 +7098,12 @@ action: component: home.left_bt_text foreground: '{{ [ hardware.buttons.left.color_rgb ] if is_number(hardware.buttons.left.color_rgb) else hardware.buttons.left.color_rgb }}' continue_on_error: true - - *delay-default ### LABEL Font ### - service: '{{ nspanel.service.component.text }}' data: component: home.left_bt_text message: '{{ hardware.buttons.left.name }}' continue_on_error: true - - *delay-default ##### NSPanel Right Button Name ##### - if: '{{ hardware.buttons.right.name | length > 0 }}' @@ -7134,14 +7114,12 @@ action: component: home.right_bt_text foreground: '{{ [ hardware.buttons.right.color_rgb ] if is_number(hardware.buttons.right.color_rgb) else hardware.buttons.right.color_rgb }}' continue_on_error: true - - *delay-default ### LABEL Font ### - service: '{{ nspanel.service.component.text }}' data: component: home.right_bt_text message: '{{ hardware.buttons.right.name }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.init.page.qrcode }}' data: @@ -7149,7 +7127,6 @@ action: qrcode: '{{ bt_qrcode.qrcode }}' show: false continue_on_error: true - - *delay-default - service: '{{ nspanel.service.init.page.settings}}' data: @@ -7158,7 +7135,6 @@ action: bright: '{{ mui[language].settings.bright }}' dim: '{{ mui[language].settings.dim }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.init.relays }}' data: @@ -7217,7 +7193,6 @@ action: }} relay2_fallback: '{{ hardware.relays.relay2.fallback }}' continue_on_error: true - - *delay-default - variables: climate_friendly_name: '{{ state_attr(climate, "friendly_name") if climate is string and climate is match "climate." else "" }}' @@ -7227,18 +7202,11 @@ action: embedded_climate: '{{ climate == thermostat_embedded }}' embedded_climate_friendly_name: '{{ climate_friendly_name if climate_friendly_name else "" }}' embedded_indoor_temperature: '{{ embedded_indoor_temperature }}' - temperature_unit_is_fahrenheit: > # Deprecated - {{ - state_attr((nspaneltemp if embedded_indoor_temperature else indoor_temperature_sensor), "unit_of_measurement") - | default("") - | upper in ["°F", "F"] - }} mui_please_confirm: '{{ mui[language].please_confirm }}' mui_unavailable: '{{ mui[language].unavailable }}' screensaver_time: '{{ screensaver_display_time if screensaver_display_time is boolean else false }}' screensaver_time_color: '{{ screensaver_display_time_font_color if screensaver_display_time_font_color is sequence else [64, 64, 64] }}' continue_on_error: true - - *delay-default ##### Update Date & Time before showing the Home page ##### - *refresh-date @@ -7352,7 +7320,6 @@ action: sequence: &refresh_page_home ##### Weather Icon Home Page ##### - - *delay-default - &refresh-page_home-weather_pic service: '{{ nspanel.service.command }}' data: @@ -7386,20 +7353,17 @@ action: outdoor_temp_color_rgb: !input 'home_outdoor_temp_label_color' home_outdoor_temp_font: !input 'home_outdoor_temp_font' ### LABEL Outdoor Temp Font Color ### - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: home.outdoor_temp foreground: '{{ outdoor_temp_color_rgb }}' continue_on_error: true ### LABEL Outdoor Temp Font ### - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: home.outdoor_temp message: '{{ outdoor_temp | round(1) ~ temperature_units }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.command }}' data: cmd: 'home.outdoor_temp.font={{ home_outdoor_temp_font if is_number(home_outdoor_temp_font) else "4" }}' @@ -7442,14 +7406,12 @@ action: - if: '{{ is_number(indoor_temp.state) }}' then: ### ICON Indoor Temp Font Color ### - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: home.indoortempicon foreground: '{{ indoor_temp.icon.color_rgb }}' continue_on_error: true ### ICON Indoor Temp Font ### - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: home.indoortempicon @@ -7466,7 +7428,6 @@ action: }} continue_on_error: true ### LABEL Indoor Temp Font Color ### - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: home.current_temp @@ -7475,7 +7436,6 @@ action: - if: '{{ not embedded_indoor_temperature }}' then: ### LABEL Indoor Temp Font ### - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: home.current_temp @@ -7492,14 +7452,12 @@ action: right_button_state: '{{ states(hardware.buttons.right.entity) | default("unavailable") if hardware.buttons.right.entity is string else "unavailable" }}' - if: '{{ left_button_state and left_button_state not in enum.states.unknown }}' then: - - *delay-default - service: '{{ nspanel.service.command }}' data: cmd: home.left_bt_pic.val={{ 1 if left_button_state in enum.states.on else 0 }} continue_on_error: true - if: '{{ right_button_state and right_button_state not in enum.states.unknown }}' then: - - *delay-default - service: '{{ nspanel.service.command }}' data: cmd: home.right_bt_pic.val={{ 1 if right_button_state in enum.states.on else 0 }} @@ -7556,7 +7514,6 @@ action: component: 'home.{{ repeat.item.component }}' foreground: '{{ entity.icon_color }}' continue_on_error: true - - *delay-default - if: '{{ entity.icon is defined }}' then: - service: '{{ nspanel.service.component.text }}' @@ -7566,7 +7523,6 @@ action: continue_on_error: true - if: '{{ page.current == page.home }}' then: - - *delay-default - service: '{{ nspanel.service.component.show }}' data: component: '{{ repeat.item.component }}' @@ -7598,14 +7554,12 @@ action: }} then: ### ICON Font Color ### - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: home.icon_top_03 foreground: '{{ entity.icon_color }}' continue_on_error: true ### ICON Font ### - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: home.icon_top_03 @@ -7704,7 +7658,6 @@ action: ### ICON Font Color ### - if: '{{ entity.icon_color is defined }}' then: - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: 'home.{{ repeat.item.component }}' @@ -7713,7 +7666,6 @@ action: ### ICON Font ### - if: '{{ entity.icon is defined }}' then: - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: 'home.{{ repeat.item.component }}' @@ -7730,7 +7682,6 @@ action: - *hide-home_page-status_bar_chip ##### HOME VALUE 01 - 03 - - *delay-default - &variables-home_page_values variables: home_page_values: @@ -7777,21 +7728,18 @@ action: - if: '{{ entity_has_value }}' then: ### ICON Font Color ### - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: '{{ repeat.item.page }}.{{ repeat.item.component }}_icon' foreground: '{{ repeat.item.icon_color_rgb }}' continue_on_error: true ### ICON Font ### - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: '{{ repeat.item.page }}.{{ repeat.item.component }}_icon' message: '{{ entity.icon }}' continue_on_error: true ### LABEL Font Color ### - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: '{{ repeat.item.page }}.{{ repeat.item.component }}{{ "_state" if repeat.item.page == page.home }}' @@ -7806,7 +7754,6 @@ action: if unit_of_measurement_raw and unit_of_measurement_raw is string and unit_of_measurement_raw | length > 0 else "" }} - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: '{{ repeat.item.page }}.{{ repeat.item.component }}{{ "_state" if repeat.item.page == page.home }}' @@ -8089,7 +8036,6 @@ action: continue_on_error: true ###### Confirm buttons ###### - - *delay-default - service: '{{ nspanel.service.component.value }}' data: component: confirm @@ -8138,7 +8084,6 @@ action: {{ current_temperature | round(0 if current_temperature >= 100 or temperature_units in ["F", "f", "°F", "°f", "K", "k"] else 1) }}{{ temperature_units}} {% else %} 0 {% endif %} - - *delay-default - service: '{{ nspanel.service.page.button }}' data: page: '{{ repeat.item.page }}' @@ -8192,19 +8137,16 @@ action: ##### LIGHT State ##### - variables: curr_brightness: '{{ (state_attr(light_entity, "brightness") | int(0) * 100 / 255) | round(0) }}' - - *delay-default - service: '{{ nspanel.service.component.value }}' data: component: light.lightslider val: '{{ curr_brightness }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: light.light_value message: '{{ curr_brightness }}%' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: light.light_value_2 @@ -8226,45 +8168,37 @@ action: else ((min_mireds+max_mireds)/2) | int(327) }} - condition: '{{ is_number(curr_color_temp) }}' - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: light.temp_value message: '{{ curr_color_temp }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: light.temp_value_2 message: '{{ curr_color_temp }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.value }}' data: component: light.tempslider val: '{{ curr_color_temp }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.command }}' data: cmd: tempslider.minval={{ min_mireds }} continue_on_error: true - - *delay-default - service: '{{ nspanel.service.command }}' data: cmd: tempslider.maxval={{ max_mireds }} continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.show }}' data: component: temp_button continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.show }}' data: component: temp_value_2 continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.show }}' data: component: temp_touch @@ -8273,12 +8207,10 @@ action: ##### Hide color button when not supported ##### - if: '{{ color_mode_color }}' then: - - *delay-default - service: '{{ nspanel.service.component.show }}' data: component: color_button continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.show }}' data: component: color_touch @@ -8325,13 +8257,11 @@ action: component: cover.cover_open message: '{{ cover_icons.open }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: cover.cover_close message: '{{ cover_icons.close }}' continue_on_error: true - - *delay-default ##### COVER State - service: '{{ nspanel.service.component.value }}' @@ -8339,7 +8269,6 @@ action: component: cover.coverslider val: '{{ (state_attr(cover_entity, "current_position") | int ) | round(0) }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: cover.cover_value @@ -8387,21 +8316,18 @@ action: {% else %} battery-outline {% endif %} - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: cover.battery_value message: '{{ battery_level }} %' continue_on_error: true ### ICON Battery Font Color ### - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: cover.battery_icon foreground: '{{ nextion.color.grey_super_light }}' continue_on_error: true ### ICON Battery Font ### - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: cover.battery_icon @@ -8440,30 +8366,25 @@ action: component: fanslider val: '{{ ((fan.percentage / 100) * fan.steps) | round(0) | int(0) }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.command }}' data: cmd: fanslider.maxval={{ fan.steps }} continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: fan_value message: '{{ fan.percentage }}%' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: fan.button_up foreground: '{{ nextion.color.grey_white if fan.percentage < 100 else nextion.color.grey_dark }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: fan.button_down foreground: '{{ nextion.color.grey_white if fan.percentage > 0 else nextion.color.grey_dark }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: fan.button_off @@ -8712,7 +8633,6 @@ action: {% elif "idle" in climate_action %}{{ all_icons.thermometer }} {% else %}{{ all_icons.blank }} {% endif %} - - *delay-default - service: '{{ nspanel.service.page.climate }}' data: current_temp: '{{ current_temp }}' @@ -8736,7 +8656,6 @@ action: for_each: '{{ page_climate.buttons.hvac_mode }}' sequence: - condition: '{{ repeat.item.mode in hvac_modes }}' - - *delay-default ### ICON Font Color ### - service: '{{ nspanel.service.component.color }}' data: @@ -8749,14 +8668,12 @@ action: }} continue_on_error: true ### ICON Font ### - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: 'climate.{{ repeat.item.component }}' message: '{{ all_icons[repeat.item.icon] }}' continue_on_error: true ### Enable button click ### - - *delay-default - service: '{{ nspanel.service.component.show }}' data: component: '{{ repeat.item.component }}' @@ -8789,21 +8706,18 @@ action: icon_color: '{{ repeat.item.icon_color_rgb }}' - *variable_entity ### ICON Font Color ### - - *delay-default - service: '{{ nspanel.service.component.color }}' data: component: '{{ repeat.item.component }}' foreground: '{{ entity.icon_color }}' continue_on_error: true ### ICON Font ### - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: '{{ repeat.item.component }}' message: '{{ entity.icon }}' continue_on_error: true ### Enable button click ### - - *delay-default - service: '{{ nspanel.service.component.show }}' data: component: '{{ repeat.item.component }}' @@ -9004,7 +8918,6 @@ action: component: '{{ "entity%02d_label" | format(entity_page_index + 1) }}' message: '{{ entity_pages_labels[entity_page_index].label }}' continue_on_error: true - - *delay-default ##### Entities ##### - repeat: for_each: '{{ entity_pages_entities[first_entity:last_entity] }}' @@ -9088,7 +9001,6 @@ action: component: '{{ page_name }}.day' message: '{{ (dict.values(mui[language].relative_day) | list)[page_index] }}' continue_on_error: true - - *delay-default ##### Display date (long) ##### - service: '{{ nspanel.service.component.text }}' @@ -9107,7 +9019,6 @@ action: ) }} continue_on_error: true - - *delay-default ##### Display weather data only when available ##### - variables: @@ -9497,7 +9408,6 @@ action: else nextion.pic.weather[condition] | int(1) }} continue_on_error: true - - *delay-default ##### Display temperature min/max when available - variables: @@ -9512,7 +9422,6 @@ action: component: '{{ page_name }}.temperature' ### Temperature MIN/MAX ### message: '{{ temperature_string }}' continue_on_error: true - - *delay-default ##### fields 1 to 5 (Parameters) ##### - repeat: @@ -9523,20 +9432,17 @@ action: component: '{{ page_name }}.value0{{ repeat.index }}' message: '{{ repeat.item.value }}' continue_on_error: true - - *delay-default - service: '{{ nspanel.service.component.text }}' data: component: '{{ page_name }}.value0{{ repeat.index }}_icon' message: '{{ repeat.item.icon }}' continue_on_error: true - - *delay-default else: &forecast_unavailable - service: '{{ nspanel.service.component.text }}' data: component: '{{ page_name }}.value01' message: '{{ mui[language].unavailable }}' continue_on_error: true - - *delay-default else: *forecast_unavailable ##### Button click ##### @@ -10211,7 +10117,6 @@ action: - '{{ page.current == page.home and trigger.event.data.new_state.state not in enum.states.unknown }}' sequence: - *refresh-page_home-outdoor_temp - - *delay-default - *refresh-page_home-weather_pic #### TFT Upload Automation ##### diff --git a/tft/nspanel_eu.HMI b/tft/nspanel_eu.HMI index 37d4fef306c6396945535420c2a156019d23d226..2205373dfe315026cc74c028cd1280b1db0ee6ef 100644 GIT binary patch delta 2362 zcmeIyYfw~W7zgm@>;)95Faar?Vp6M}L5Brzh>%hgmZYg+nit3@DX>*Ltbhya^5}X+ zX_F(~P&P%)Oj_%BL1ax`8R{mR3*e{o}l#1xr38a#*%U!j5JZLo=9+|Xv(#YA(|k^U40K7b#&@oJ^Y%r z!-fqurFR$O?|?Fq#ApO}HBoIfc~BS`XwZ7c%1(;ZsKZ)Bj^BF53gj>b^c4ih*noEA zLA{V&Nxky*lcIdrq_r9wZE|)kYBc&zM((gt{P2e#=pD;!wb3=r{Rg`G^zIa*hx+zX z=YObPv|L^y2yWwAWzeOEk_Kq+CG@u0?=)q6!2=foR@n0x#b%gC(#3MMCM{*pG z;&@Kr7kD&};Y5CsU*aTwnP1^#9?P%tYdnrq_;nu76Zj3D$dh<7zsadQh2P?-JdNMx z={$pH@+?l{bbg0t^ShkE@A3OQhco#D{*bdco9FUJY~p!5pBM1Q`^5oe^7*&a+KU2qvomjVRnz78L8%^1T`NXqh@20nk$ouy4UYNj>w{{ zzjgeB@j~i1>A?-HPgRRTrV&*t4b?MN`V zUpkLyhcX^168Ygg^Ip+8ME9t$>&9p%s!@u5$s;3%5~QZbOgbrTgBT?Diu9c5A254PrfJsv zjI8ur>9)}sB8@&ITIF>$?n@eeb)=KfCEW!HwT~buP0#8p*i- zc(BepC6#P3hu)u&tc{|d9N6GKrjeo#EmWpEDwJt?#!Qb-J9J?;08H*6xR&%~xi!FM1c+5M89q2qs&p{AaT5PN-D-XrWmiWcS=m z(zk9Ol^rh`%0y}AV&&81MX3Ac*Z%6hS@0ZDfb!Zc&*RWna)E*9Nu_svBvG!Cdt>8w z=M^#P7m)DM^l=POPN_LMdo6am8$SPFJn2Fqau ztb|pt8j4{Jtc4Qz1RU@wtbz|z*T66YjRZh^??)q0+NvWsQ>@~ delta 1866 zcmeIxZBSHI7zglk@9tfgrI{#TSsiM0#7P|o0bjQWj4~lzEe$Oyfds`++}su+cLVM| zwD}6L$q5rBP_z<7R=aBXg2AFb&Ch-4LwDvk_c_mb z=FI)yxwq%q_K+SBE*%!8ilf%#tSXF4Gp4h^b$qgkosqC`8O7Zy=QJ_g`G*v$7SM8q zYI;r)LQ2D+NHFxHu0xi#zw9;u>Mq6X2c>QA-1;P>I`X#GRK zWLtS~rrf1N?AAdX*xAbxom+L$c8|{C(Fq`e3e+HhM)BxEVt@SqW&VpV)9%$fym|pd zP=OjG&?sJgNcix7VZi0pw^(Tw8R$`ZjFRYaN~RQ=O;1oNJxNc|9D17OQW~YxGc=Fp zlaUtCLVA{-qeZlso~I02LQClddXX~eC3=}&p=Fds*_1=C(sIhBJX%33DW6u+Yg9n1 zX$=+9S~Ah=w2sX5#t}y$|!br{&1dX>o~hGjj8a zxG>8Uso31|&pLi1yHUgIRy0&NyxkTkg7r`gCGaL}04vzwEhvRD#oJw8pCDye#&#w7 zBb5anr6efQujB4US{D?_8WhPE6p05#3J;33@t#QYV|dG0a~aR5GwXP^sa&U++U0(M zx4MfXC11|zcS3T6JkRUXZ`LTDPI*|oQ}Qtr8Z}B*mwexYe0kFPJ$y>OTdSKIRNonM(GLwy_)i}gOGa?d7MM8>D7KVjz5d;ZzXsjOJiATOf zVMMZ$1Xq}#5(#H5UxXu6&=e3U3&i4VCTaR1nyG&17kUGWi}nvR)6Tr+-uv0-^V!es zJ$t>EO7y)TjJF791@Ba2a+*(Qco->1@WUqR(cxt3md-jMln9mQ1eZnA*XKqBYk_uJ zFBmEdoR_YOU5B#HA)<~U>O=V-tKGFTW5!95YBfXxE?e82lQaTV-oMyG5UlGLpVY7O z^)VTho6Uao``f6d{@+W@jKz=J$Oo6OROPhWc`v$a?&i^6>PDSlz2UxGaLA1;s;->y z@GSK8EcW#jKm%HE03A5WzMlFkxBk~(zR<<1*u_f#4QRmubl@nvc4NBX8nheuAImr#OV4=FJ?+Vf+kl;jJ9b+c<(F`B{FBxAXJ-0!Q%< zevzYjC%?oma}4j|SdL>OzrykSDkpFvzs5AZ*c@|&D-TGX`Y z3U+({9V_WuL}^#OcCKk_RuE+?*>e?9j*<_pA#$2&c^q{Qq9v7Vd5CD4Qq!@P$gE^P zUo|)RNy8;|L2^K=80RgW8Zqa~%Z5Z}jWj)GcGW)S<0m&?H9z1X>Hf5DEVfLFun+cw z88RUYvLOd@ArJDQ0N#Q^U^oCpPz)td3U5OhyaVq-IUI!dpaR~9O85W{K@~`F7^>kz zI07~B5!6B*Sm0y$1nS`^dWwLusq^|GxroU>W%l@{Jy!l@%6;E7Lp8ijMYL3Q#*8KQTfSN;s)Z7`YWU4V{T0TJ12`J~xd-$CdH21fmipFHI&o zq2#UQ998vWqfT0k)zUjkBUUolsC7iVINVqQuJFUkxj|XxkQc1gs6OV(fbal zd9qZ^E#*XdrM~ST(KRKPSEw~Rs?@yVo+IkXFLkqxE}_v~<*W_1{*_MgNy(fDKg9KF8PY0qZFBE3zWla?W|c?Sr90?$x{}4s8(7sR^+OiT|}}C)-9iV zdwxA?zo3!q5m~MdGnb^{c3YLSB&*0#PHD9FYNY7I`714DneKCQ22yQz%n44!9MfpV z!i`wcZKj3m-n-~#mDC-Vv#{n7tNo4|V`h(XhoJp;-C+bnnY#4!L9Zx zDa}cAl(%)+x5P+>O?!QcE5>cm17ATeoP~369{QjkF2F_j8ZN6711N;aRa>cma5cUuATm3Eo delta 1754 zcmdti{Zmv`7zgm@?6SL~OwLZi0zxlNPLeguN+!M(D!m8=ifLAsxM-FX$(pjbu)q=z zEvYHA#;FJwwTJ{WSn6UCAgh5;>00^5`m*U*qrWgyzcf>`ePQp+{tHcK=5yz}=X~#- z*}2c$fstCn0Ep2J;S))eU$8kPGbfAGwPco+`gKGCyjMl7a32@ONukm3E_ti8A%h@y zRrJ&ud1LDYTA-HE0X!t4&3#>;=15=GQMC7%86zvJSgR$f0}F&?VT@h)?`01ap% z3Ur`X%<+cPzx`kTSG6(0WlRvD0WCy<4)lsK!7z5?zfkZR6LQOW9?$0mypSK}M>w4q z@nT-W8T=?e#*gz8yp%IJi=X7DIGb~L887EtUcpcEN?yg!@M?aRpX29w4ZpxI@>96&qD+@Yp zmLxgmE_N!@PT8V0(rR(U=;oxPraUA&&e%gTx6;udlZR{>GBoK|lj0n*H3{ilwJljO zHZ@zP%Bo(6UtXTGHAOBqmduic-JVz_H>NaSxI#B=Py*Y*4y8~A4k(8Ts01fe!JAMG z3^ni;xS$r^h8?gIcELNa8}>jQ)Wf^rhQ06}G=PNn;RE;(8sQ^of_>nDX4nr0;2?Yq zpFj(I3a#)N9D+7zhr{qW9Dy&O13JMAUGOD*1qyV-QTQ6Zfgbo)3Ek`+%F(4RN^2Qb zvM&r2iONIjiwdqKx;HBPVh!tw+SPsUMxvc+PA?!jtma#VL?_jL*A}7{b$?AUkzO4N zi;XB0ek83cA?j5-ca#zJg>w-FDu`Ou8`v3nXiiNen|4I*E}(S5)&Aslr8KVyhq*@@0+Yxnt~VSHvCy?60Nf8561?1XI(H;_Q$l} zuhgaauIq6WD_nB=cKd4SJXf43kN9o3$&qWp+m*IJP^TR~*VJ{&6}RkGr8&c=pV$$K z^tY78N^>Asrd+xf?3wGT5A;Dl9D@Py!ErbN-@zaZ!ATf~5%?Z{fFI!$oCZIffuG=K d_yx|wIXDj&;3AAd04~90xB^#|`ap1Y*1t~dvS$DQ diff --git a/tft/nspanel_us_code/boot.txt b/tft/nspanel_us_code/boot.txt index 5fcd7c7..f2f45ef 100644 --- a/tft/nspanel_us_code/boot.txt +++ b/tft/nspanel_us_code/boot.txt @@ -103,7 +103,7 @@ Text tft_version Dragging : 0 Send Component ID : on press and release Associated Keyboard: none - Text : 4.2.6dev + Text : 4.3dev Max. Text Size : 9 Events diff --git a/tft/nspanel_us_code/home.txt b/tft/nspanel_us_code/home.txt index a6a5668..0d62259 100644 --- a/tft/nspanel_us_code/home.txt +++ b/tft/nspanel_us_code/home.txt @@ -133,7 +133,7 @@ Text right_bt_text Text : Max. Text Size : 20 -Text icon_top_01 +Text chip_relay1 Attributes ID : 11 Scope : global @@ -143,7 +143,7 @@ Text icon_top_01 Text : Max. Text Size : 3 -Text icon_top_02 +Text chip_relay2 Attributes ID : 12 Scope : global @@ -153,7 +153,7 @@ Text icon_top_02 Text : Max. Text Size : 3 -Text icon_top_03 +Text chip_climate Attributes ID : 13 Scope : global @@ -163,7 +163,7 @@ Text icon_top_03 Text : Max. Text Size : 3 -Text icon_top_04 +Text chip01 Attributes ID : 14 Scope : global @@ -173,7 +173,7 @@ Text icon_top_04 Text : Max. Text Size : 3 -Text icon_top_05 +Text chip02 Attributes ID : 15 Scope : global @@ -183,7 +183,7 @@ Text icon_top_05 Text : Max. Text Size : 3 -Text icon_top_06 +Text chip03 Attributes ID : 16 Scope : global @@ -193,7 +193,7 @@ Text icon_top_06 Text : Max. Text Size : 3 -Text icon_top_07 +Text chip04 Attributes ID : 17 Scope : global @@ -203,7 +203,7 @@ Text icon_top_07 Text : Max. Text Size : 3 -Text icon_top_08 +Text chip05 Attributes ID : 18 Scope : global @@ -213,7 +213,7 @@ Text icon_top_08 Text : Max. Text Size : 3 -Text icon_top_09 +Text chip06 Attributes ID : 19 Scope : global @@ -223,7 +223,7 @@ Text icon_top_09 Text : Max. Text Size : 3 -Text icon_top_10 +Text chip07 Attributes ID : 20 Scope : global