diff --git a/ReleaseNotes.md b/ReleaseNotes.md index a9a39af..996bc41 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -26,7 +26,7 @@ Since in this update lots of input to the blueprint changed, we highly recommend - [(DE) HowTo - Alle wichtigen Dinge die man wissen sollte - Update](https://github.com/Blackymas/NSPanel_HA_Blueprint/wiki/(DE)-HowTo---Alle-wichtigen-Dinge-die-man-wissen-sollte#2-update-blueprint)   ## Breaking changes -1. New requirement: Home Assistant 2023.5.0 or later. +1. New requirements: Home Assistant and ESPHome v2023.5.0 or later. 2. Exisiting users will have o select again the language for the panel, otherwise English will be used to display strings. 3. Removed entity `sensor.xxxxx_settings_entity` and service `esphome.xxxxx_set_settings_entity`. 4. Due to the changes on the time display engine, you might have to select your time format again in the blueprint settings. @@ -44,7 +44,8 @@ Since in this update lots of input to the blueprint changed, we highly recommend 9. New "Fan speed page" (#897) 10. Select wake-up page (#898) 11. Panel's local control - +12. Embedded thermostat/heater (#917) +   ## Details of all changes @@ -112,8 +113,17 @@ We are trying to make your panel as autonomous as possible by moving some of the With this version, the following engines have been moved to your panel (local control): - Time display - Physical relay control (when hardware left button is connected to relay 1 or right button to relay 2) -  +- Embedded thermostat (see bellow) +  +### 12. Embedded thermostat/heater +Now you can use your panel to control a heater and let your ambient more confortable, and you will be able to control it locally even when your Wi-Fi is out or Home Assistant is unavailable. + +===>>> Add documentation before release <<<=== +- How to enable +- Substitutions + +  ## Next topics we are currently working on See here: https://github.com/Blackymas/NSPanel_HA_Blueprint/labels/roadmap diff --git a/nspanel_blueprint.yaml b/nspanel_blueprint.yaml index f44d362..e060d26 100644 --- a/nspanel_blueprint.yaml +++ b/nspanel_blueprint.yaml @@ -3483,11 +3483,11 @@ trace: ############################################################# trigger_variables: - nspanel_name_temp: !input 'nspanel_name' - nspanel_entities: '{{ device_entities(nspanel_name_temp) }}' + nspanel_deviceid: !input 'nspanel_name' + nspanel_entities: '{{ device_entities(nspanel_deviceid) }}' nspanel_name: > {% if nspanel_entities | count > 0 %}{{ (nspanel_entities | selectattr(None, "search", "_nextion_display") | list | first).split(".")[1].split("_nextion_display")[0] }} - {% elif nspanel_name_temp is string %}{{ nspanel_name_temp | replace("-","_") | replace(" ","_") | replace("___","_") | replace("__","_") }} + {% elif nspanel_deviceid is string %}{{ nspanel_deviceid | replace("-","_") | replace(" ","_") | replace("___","_") | replace("__","_") }} {% endif %} left_button: 'binary_sensor.{{ nspanel_name }}_left_button' right_button: 'binary_sensor.{{ nspanel_name }}_right_button' @@ -3496,8 +3496,8 @@ trigger_variables: relay01_entity: 'switch.{{ nspanel_name }}_relay_1' relay02_entity: 'switch.{{ nspanel_name }}_relay_2' nspaneltemp: 'sensor.{{ nspanel_name }}_temperature' - reboot_button: 'button.{{ nspanel_name }}_restart' switch_sleep_mode: 'switch.{{ nspanel_name }}_sleep_mode' + thermostat_embedded: 'climate.{{ nspanel_name }}_thermostat' ##### notification Trigger ##### notification_unread: 'switch.{{ nspanel_name }}_notification_unread' @@ -3568,8 +3568,7 @@ variables: set_button: 'esphome.{{ nspanel_name }}_set_button' set_entity: 'esphome.{{ nspanel_name }}_set_entity' set_climate: 'esphome.{{ nspanel_name }}_set_climate' - set_localization: 'esphome.{{ nspanel_name }}_set_localization' - set_relay_local: 'esphome.{{ nspanel_name }}_set_relay_local' + global_settings: 'esphome.{{ nspanel_name }}_global_settings' icon: domain: automation: "\uF6FC" #F6FC @@ -4699,24 +4698,24 @@ variables: sat: Zaterdag sun: Zondag weekdays_short: - mon: Mon - tue: Tue - wed: Wed - thu: Thu - fri: Fri - sat: Sat - sun: Sun + mon: Ma + tue: Di + wed: Wo + thu: Do + fri: Vr + sat: Za + sun: Zo months: - jan: January - feb: February - mar: March + jan: Januari + feb: Februari + mar: Maart apr: April - may: May - jun: June - jul: July - aug: August + may: Mei + jun: Juni + jul: Juli + aug: Augustus sep: September - oct: October + oct: Oktober nov: November dec: December months_short: @@ -4724,12 +4723,12 @@ variables: feb: Feb mar: Mar apr: Apr - may: May + may: Mei jun: Jun jul: Jul aug: Aug sep: Sep - oct: Oct + oct: Okt nov: Nov dec: Dec relative_day: @@ -5315,51 +5314,38 @@ variables: date: label: color_rgb: !input 'date_label_color' - hardware: - buttons: #### HARDWARE BUTTONS ##### - left: - entity: !input 'left_button_entity' - name: !input 'left_button_name' - color_rgb: !input 'left_button_color' - hold_select: !input 'left_button_hold_select' - #custom_action: !input 'left_button_hold_custom_action' - right: - entity: !input 'right_button_entity' - name: !input 'right_button_name' - color_rgb: !input 'right_button_color' - hold_select: !input 'right_button_hold_select' - #custom_action: !input 'right_button_hold_custom_action' + ##### Climate page ##### page_climate: buttons: hvac_mode: - mode: auto - icon: calendar-sync - color: green + icon: calendar-sync #ee8d + color: green #'19818' component: button01 - mode: heat_cool - icon: autorenew - color: amber + icon: autorenew #e069 + color: amber #'65024' component: button02 - mode: heat - icon: fire - color: deep-orange + icon: fire #e237 + color: deep-orange #'64164' component: button03 - mode: cool - icon: snowflake - color: blue + icon: snowflake #e716 + color: blue #'1055' component: button04 - mode: dry - icon: water-percent - color: orange + icon: water-percent #e58d + color: orange #'64704' component: button05 - mode: fan_only - icon: fan - color: cyan + icon: fan #e20f + color: cyan #'1530' component: button06 - mode: "off" - icon: power - color: "off" + icon: power #e424 + color: "off" #'35921' component: button07 qrcode_enabled: !input 'qrcode_enabled' @@ -5370,6 +5356,7 @@ variables: outdoortemp: !input 'outdoortemp' indoortemp: !input 'indoortemp' climate: !input 'climate' + embedded_climate: '{{ climate == thermostat_embedded }}' relay_1_local_fallback: !input 'relay_1_local_fallback' relay_2_local_fallback: !input 'relay_2_local_fallback' @@ -5408,6 +5395,13 @@ trigger: seconds: 1 id: nspanel_boot_init + ##### NSPanel page changed ##### + - platform: event + event_type: esphome.nspanel_pagechange + event_data: + device_id: !input 'nspanel_name' + id: page_changed + ##### NSPanel event changed ##### - platform: event event_type: state_changed @@ -5415,10 +5409,13 @@ trigger: entity_id: '{{ nspanelevent }}' id: nspanelevent_changed - ##### Automation reload + ##### Automation reload or HA started - platform: event event_type: automation_reloaded id: automation_reloaded + - platform: homeassistant + event: start + id: ha_started ##### Trigger - Buttons - State change ################################################################################################################# ##### Button01 Sync - Trigger 'current_state_entity01' ##### @@ -6037,21 +6034,6 @@ action: if: '{{ true }}' then: ##### NSPanel Date ##### - ### DATE Font Color ### - - service: '{{ nextion.command.font_color }}' - data: - component: home.date - message: > - {{ - page_home.general.date.label.color_rgb - if is_number(page_home.general.date.label.color_rgb) - else ((page_home.general.date.label.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.date.label.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.date.label.color_rgb[2] //(2**3)) - }} - continue_on_error: true - - &delay-default - delay: - milliseconds: '{{ delay_value }}' - ### DATE Font ### - service: '{{ nextion.command.text_printf }}' data: component: home.date @@ -6068,19 +6050,6 @@ action: ) }} continue_on_error: true - ##### NSPanel Time ##### - ### TIME Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.time - message: > - {{ - page_home.general.time.label.color_rgb - if is_number(page_home.general.time.label.color_rgb) - else ((page_home.general.time.label.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.time.label.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.time.label.color_rgb[2] //(2**3)) - }} - continue_on_error: true ##### BOOT NSPANEL - boot init ##### - alias: Boot init @@ -6092,32 +6061,119 @@ action: ##### NSPanel boot init only ##### - delay: milliseconds: 100 - - service: '{{ nextion.command.text_printf }}' + - alias: Publish blueprint version + service: '{{ nextion.command.text_printf }}' data: component: boot.bluep_version message: '{{ blueprint_version }}' continue_on_error: true + - &delay-default + delay: + milliseconds: '{{ delay_value }}' - ###### Set local fallback ###### - - service: 'switch.turn_{{ "on" if relay_1_local_fallback else "off" }}' - data: - entity_id: "switch.{{ nspanel_name }}_relay_1_local_fallback" - continue_on_error: true - - service: 'switch.turn_{{ "on" if relay_2_local_fallback else "off" }}' - data: - entity_id: "switch.{{ nspanel_name }}_relay_2_local_fallback" - continue_on_error: true - - &set_relay_local - service: '{{ nextion.command.set_relay_local }}' - data: - relay1: '{{ page_home.hardware.buttons.left.entity == relay01_entity }}' - relay2: '{{ page_home.hardware.buttons.right.entity == relay02_entity }}' - continue_on_error: true + - &global_settings + if: '{{ true }}' + then: + - &variables_wakeup_page + variables: + wakeup_page_input: !input wakeup_page + wakeup_page: '{{ wakeup_page_input if is_number(wakeup_page_input) else 0 }}' + - &variables_hardware + variables: + hardware: + buttons: + left: + entity: !input 'left_button_entity' + name: !input 'left_button_name' + color_rgb: !input 'left_button_color' + hold_select: !input 'left_button_hold_select' + #custom_action: !input 'left_button_hold_custom_action' + right: + entity: !input 'right_button_entity' + name: !input 'right_button_name' + color_rgb: !input 'right_button_color' + hold_select: !input 'right_button_hold_select' + #custom_action: !input 'right_button_hold_custom_action' + relays: + relay1: + icon: !input 'relay01_icon' #E3A5 + icon_color_rgb: !input 'relay01_icon_color' + relay2: + icon: !input 'relay02_icon' #E3A8 + icon_color_rgb: !input 'relay02_icon_color' - ##### clear notification icon ##### - - service: '{{ nextion.command.notification_clear }}' + - service: '{{ nextion.command.global_settings }}' + data: + relay1_local_control: '{{ hardware.buttons.left.entity == relay01_entity }}' + relay1_icon: > + {{ + all_icons[hardware.relays.relay1.icon.split(":")[1]] | default("\uE3A5") + if hardware.relays.relay1.icon.split(":") | count > 0 + else + ( + hardware.relays.relay1.icon + if hardware.relays.relay1.icon is string + else "\uE3A5" + ) + }} + relay1_icon_color: > + {{ + hardware.relays.relay1.icon_color_rgb + if is_number(hardware.relays.relay1.icon_color_rgb) + else ((hardware.relays.relay1.icon_color_rgb[0] //(2**3)) *(2**11))+((hardware.relays.relay1.icon_color_rgb[1] //(2**2)) *(2**5))+(hardware.relays.relay1.icon_color_rgb[2] //(2**3)) + }} + relay2_local_control: '{{ hardware.buttons.right.entity == relay02_entity }}' + relay2_icon: > + {{ + all_icons[hardware.relays.relay2.icon.split(":")[1]] | default("\uE3A8") + if hardware.relays.relay2.icon.split(":") | count > 0 + else + ( + hardware.relays.relay2.icon + if hardware.relays.relay2.icon is string + else "\uE3A8" + ) + }} + relay2_icon_color: > + {{ + hardware.relays.relay2.icon_color_rgb + if is_number(hardware.relays.relay2.icon_color_rgb) + else ((hardware.relays.relay2.icon_color_rgb[0] //(2**3)) *(2**11))+((hardware.relays.relay2.icon_color_rgb[1] //(2**2)) *(2**5))+(hardware.relays.relay2.icon_color_rgb[2] //(2**3)) + }} + date_color: > + {{ + page_home.general.date.label.color_rgb + if is_number(page_home.general.date.label.color_rgb) + else ((page_home.general.date.label.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.date.label.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.date.label.color_rgb[2] //(2**3)) + }} + time_format: '{{ time_format }}' + time_color: > + {{ + page_home.general.time.label.color_rgb + if is_number(page_home.general.time.label.color_rgb) + else ((page_home.general.time.label.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.time.label.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.time.label.color_rgb[2] //(2**3)) + }} + embedded_climate: '{{ embedded_climate }}' + wakeup_page: '{{ wakeup_page }}' + continue_on_error: true + - *delay-default + + ###### Set local fallback ###### + - service: 'switch.turn_{{ "on" if relay_1_local_fallback else "off" }}' + data: + entity_id: "switch.{{ nspanel_name }}_relay_1_local_fallback" + continue_on_error: true + - service: 'switch.turn_{{ "on" if relay_2_local_fallback else "off" }}' + data: + entity_id: "switch.{{ nspanel_name }}_relay_2_local_fallback" + continue_on_error: true + - *delay-default + + - &clear_notification + service: '{{ nextion.command.notification_clear }}' data: {} continue_on_error: true + - *delay-default ###### NSPanel beep ###### - delay: @@ -6130,25 +6186,2033 @@ action: continue_on_error: true ##### Update Date & Time before showing the Home page ##### - - &set_localization - service: '{{ nextion.command.set_localization }}' - data: - time_format: '{{ time_format }}' - continue_on_error: true - *refresh-datetime ##### NSPanel boot init finished and jump to Home Page##### + - *variables_wakeup_page + - *delay-default - &jump_wakeup_page - if: '{{ true }}' - then: - - &variables_wakeup_page - variables: - wakeup_page: !input wakeup_page - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: "page {{ wakeup_page if is_number(wakeup_page) else page.home }}" - continue_on_error: true + service: '{{ nextion.command.printf }}' + data: + cmd: "page {{ wakeup_page }}" + continue_on_error: true + + ##### Page changed ##### + - alias: Page changed + conditions: + - condition: trigger + id: page_changed + sequence: + - variables: + nspanel_event: '{{ trigger.event.data }}' + - choose: + ## PAGE HOME ## + - alias: Home page + conditions: '{{ nspanel_event.page == page.home }}' + sequence: &refresh_page_home + ##### Set entity variable ##### + - &set_entity_variable + service: '{{ nextion.command.text_printf }}' + data: + component: home.entity + message: '{{ climate }}' + continue_on_error: true + + ##### Weather Icon Home Page ##### + - *delay-default + - &refresh-page_home-weather_pic + service: '{{ nextion.command.printf }}' + data: + cmd: home.weather.pic={{ nextion.pic.weather[states(weather_entity) | default("unavailable") if weather_entity is string and has_value(weather_entity) else "unavailable"] | default(None) }} + continue_on_error: true + + - &refresh-page_home-outdoor_temp + if: '{{ true }}' + then: + ##### NSPanel Outdoor Temp ##### + - variables: + outdoor_temp_state: > + {{ + states(outdoortemp, rounded=true) | default("unavailable") + if outdoortemp is string and outdoortemp is match "sensor." and has_value(outdoortemp) + else "unavailable" + }} + outdoor_temp: > + {{ + outdoor_temp_state if is_number(outdoor_temp_state) + else state_attr(weather_entity, "temperature") | default("unavailable") + if weather_entity is string else "unavailable" + }} + - if: '{{ is_number(outdoor_temp) }}' + then: + ### LABEL Outdoor Temp Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.outdoor_temp + message: > + {{ + page_home.general.outdoor_temp.label.color_rgb + if is_number(page_home.general.outdoor_temp.label.color_rgb) + else ((page_home.general.outdoor_temp.label.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.outdoor_temp.label.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.outdoor_temp.label.color_rgb[2] //(2**3)) + }} + continue_on_error: true + ### LABEL Outdoor Temp Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: home.outdoor_temp + message: '{{ outdoor_temp | round(1) ~ temperature_units }}' + continue_on_error: true + + - &refresh-page_home-indoor_temp + if: '{{ true }}' + then: + ##### NSPanel Indoor Temp ##### + - variables: + indoor_temp_state: '{{ states(indoortemp, rounded=true) | default("unavailable") if indoortemp is string and indoortemp is match "sensor." else states(nspaneltemp, rounded=true) }}' + indoor_temp_units: > + {{ + state_attr(indoortemp, "unit_of_measurement") | default(temperature_units) + if + indoortemp is string and + indoortemp is match "sensor." and + state_attr(indoortemp, "unit_of_measurement") is string and + state_attr(indoortemp, "unit_of_measurement") | length > 0 + else + ( + state_attr(nspaneltemp, "unit_of_measurement") | default(temperature_units) + if + state_attr(nspaneltemp, "unit_of_measurement") is string and + state_attr(nspaneltemp, "unit_of_measurement") | length > 0 + else temperature_units + ) + }} + - if: '{{ is_number(indoor_temp_state) }}' + then: + ### ICON Indoor Temp Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.indoortempicon + message: > + {{ + page_home.general.indoor_temp.icon.color_rgb + if is_number(page_home.general.indoor_temp.icon.color_rgb) + else ((page_home.general.indoor_temp.icon.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.indoor_temp.icon.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.indoor_temp.icon.color_rgb[2] //(2**3)) + }} + continue_on_error: true + ### ICON Indoor Temp Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: home.indoortempicon + message: > + {{ + all_icons[page_home.general.indoor_temp.icon.icon.split(":")[1]] | default(all_icons.unknown) + if page_home.general.indoor_temp.icon.icon.split(":") | count > 0 + else + ( + page_home.general.indoor_temp.icon.icon + if page_home.general.indoor_temp.icon.icon is string + else all_icons.unknown + ) + }} + continue_on_error: true + ### LABEL Indoor Temp Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.current_temp + message: > + {{ + page_home.general.indoor_temp.label.color_rgb + if is_number(page_home.general.indoor_temp.label.color_rgb) + else ((page_home.general.indoor_temp.label.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.indoor_temp.label.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.indoor_temp.label.color_rgb[2] //(2**3)) + }} + continue_on_error: true + ### LABEL Indoor Temp Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: home.current_temp + message: '{{ indoor_temp_state | round(1) ~ indoor_temp_units }}' + continue_on_error: true + + ##### NSPanel Buttons ##### + - *variables_hardware + - variables: + left_button_state: '{{ states(hardware.buttons.left.entity) | default("unavailable") if hardware.buttons.left.entity is string else "unavailable" }}' + right_button_state: '{{ states(hardware.buttons.right.entity) | default("unavailable") if hardware.buttons.right.entity is string else "unavailable" }}' + + ##### NSPanel Left Button Name ##### + - if: '{{ hardware.buttons.left.name | length > 0 }}' + then: + ### LABEL Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.left_bt_text + message: > + {{ + hardware.buttons.left.color_rgb + if is_number(hardware.buttons.left.color_rgb) + else ((hardware.buttons.left.color_rgb[0] //(2**3)) *(2**11))+((hardware.buttons.left.color_rgb[1] //(2**2)) *(2**5))+(hardware.buttons.left.color_rgb[2] //(2**3)) + }} + continue_on_error: true + ### LABEL Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: home.left_bt_text + message: '{{ hardware.buttons.left.name }}' + continue_on_error: true + + ##### SET Left Hardware Button PIC on Home Page #### + - if: '{{ left_button_state not in ["unavailable", "unknown", "", None] }}' + then: + - variables: + # Hardware Button PIC + left_hardware_button_state: '{{ nextion.pic.hardware.button[left_button_state] | default(nextion.pic.hardware.button.off) }}' + - *delay-default + - service: '{{ nextion.command.printf }}' + data: + cmd: home.left_bt_pic.pic={{ left_hardware_button_state }} + continue_on_error: true + + ##### NSPanel Right Button Name ##### + - if: '{{ hardware.buttons.right.name | length > 0 }}' + then: + ### LABEL Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.right_bt_text + message: > + {{ + hardware.buttons.right.color_rgb + if is_number(hardware.buttons.right.color_rgb) + else ((hardware.buttons.right.color_rgb[0] //(2**3)) *(2**11))+((hardware.buttons.right.color_rgb[1] //(2**2)) *(2**5))+(hardware.buttons.right.color_rgb[2] //(2**3)) + }} + continue_on_error: true + ### LABEL Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: home.right_bt_text + message: '{{ hardware.buttons.right.name }}' + continue_on_error: true + + ##### SET Right Hardware Button PIC on Home Page ##### + - if: '{{ right_button_state not in ["unavailable", "unknown", "", None] }}' + then: + - variables: + # Hardware Button PIC + right_hardware_button_state: '{{ nextion.pic.hardware.button[right_button_state] | default(nextion.pic.hardware.button.off) }}' + - *delay-default + - service: '{{ nextion.command.printf }}' + data: + cmd: home.right_bt_pic.pic={{ right_hardware_button_state }} + continue_on_error: true + + ###### Status bar ###### + - &variables-home_page_status_bar + variables: + climate_state: '{{ states(climate) | default("unavailable") if climate is string else "unavailable" }}' + hvac_action: '{{ state_attr(climate, "hvac_action") | default("unavailable") if climate is string else "unavailable" }}' + climate_action: '{{ hvac_action if hvac_action not in ["unavailable", "unknown", "", None] else climate_state }}' + home_page_status_bar: + - entity: '{{ climate }}' + icon: > + {% if "off" in climate_action %}{{ all_icons.blank }} + {% elif "heating" in climate_action or "heat" in climate_action %}{{ all_icons["thermometer-lines"] }} + {% elif "cooling" in climate_action or "cool" in climate_action %}{{ all_icons.snowflake }} + {% elif "drying" in climate_action or "dry" in climate_action %}{{ all_icons["water-percent"] }} + {% elif "fan" in climate_action or "fan_only" in climate_action %}{{ all_icons.fan }} + {% elif "heat_cool" in climate_action %}{{ all_icons.autorenew }} + {% elif "auto" in climate_action %}{{ all_icons["calendar-sync"] }} + {% elif "idle" in climate_action %}{{ all_icons.thermometer }} + {% else %}{{ all_icons.blank }} + {% endif %} + icon_color_rgb: > + {% if "off" in climate_action %}{{ nextion.color["off"] }} + {% elif "heating" in climate_action or "heat" in climate_action %}{{ nextion.color["deep-orange"]}} + {% elif "cooling" in climate_action or "cool" in climate_action %}{{ nextion.color["blue"] }} + {% elif "drying" in climate_action or "dry" in climate_action %}{{ nextion.color["orange"] }} + {% elif "fan" in climate_action or "fan_only" in climate_action %}{{ nextion.color["cyan"] }} + {% elif "heat_cool" in climate_action %}{{ nextion.color["amber"] }} + {% elif "auto" in climate_action %}{{ nextion.color["green"] }} + {% elif "idle" in climate_action %}{{ nextion.color["off"] }} + {% else %}{{ nextion.color["off"] }} + {% endif %} + page: home + component: icon_top_03 + embedded: '{{ embedded_climate }}' + - entity: !input 'chip01' + icon: !input 'chip01_icon' + icon_color_rgb: !input 'chip01_icon_color' + page: home + component: icon_top_04 + embedded: false + - entity: !input 'chip02' + icon: !input 'chip02_icon' + icon_color_rgb: !input 'chip02_icon_color' + page: home + component: icon_top_05 + embedded: false + - entity: !input 'chip03' + icon: !input 'chip03_icon' + icon_color_rgb: !input 'chip03_icon_color' + page: home + component: icon_top_06 + embedded: false + - entity: !input 'chip04' + icon: !input 'chip04_icon' + icon_color_rgb: !input 'chip04_icon_color' + page: home + component: icon_top_07 + embedded: false + - entity: !input 'chip05' + icon: !input 'chip05_icon' + icon_color_rgb: !input 'chip05_icon_color' + page: home + component: icon_top_08 + embedded: false + - entity: !input 'chip06' + icon: !input 'chip06_icon' + icon_color_rgb: !input 'chip06_icon_color' + page: home + component: icon_top_09 + embedded: false + - entity: !input 'chip07' + icon: !input 'chip07_icon' + icon_color_rgb: !input 'chip07_icon_color' + page: home + component: icon_top_10 + embedded: false + - repeat: + for_each: '{{ home_page_status_bar }}' + sequence: + - &display-home_page_status_bar + if: '{{ (not repeat.item.embedded) and repeat.item.entity is defined and repeat.item.entity is string and repeat.item.entity | length > 0 }}' + then: + - variables: + repeat_item_state: '{{ states(repeat.item.entity) | default("unavailable") }}' + repeat_item_state_is_on: > + {{ + (repeat.item.component == "icon_top_03") or + (repeat_item_state is string and repeat_item_state in ["on", "open", "opening", "true", "True"]) or + (repeat_item_state is boolean and repeat_item_state) + }} + repeat_item_icon: > + {% if repeat_item_state_is_on and repeat.item.icon is string and repeat.item.icon | length > 0 %} + {{ all_icons[repeat.item.icon.split(":")[1]] | default(repeat.item.icon) }} + {% elif repeat_item_state_is_on and state_attr(repeat.item.entity, "icon") | default("") not in ["unavailable", "unknown", "", None] %} + {{ all_icons[state_attr(repeat.item.entity, "icon").split(":")[1]] | default(all_icons.blank) }} + {% else %} + {{ all_icons.blank }} + {% endif %} + - if: '{{ repeat_item_state_is_on }}' + then: + ### ICON Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: '{{ repeat.item.page }}.{{ repeat.item.component }}' + message: > + {{ + repeat.item.icon_color_rgb + if is_number(repeat.item.icon_color_rgb) + else + ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ + ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ + (repeat.item.icon_color_rgb[2] //(2**3)) + }} + continue_on_error: true + ### ICON Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ repeat.item.page }}.{{ repeat.item.component }}' + message: '{{ repeat_item_icon }}' + continue_on_error: true + # {{ is_state(repeat.item.entity, "on") | default(False) if repeat.item.entity is string else "unavailable" }} + + ##### HOME VALUE 01 - 03 + - *delay-default + - &variables-home_page_values + variables: + home_page_values: + - entity: !input 'home_value01' + icon: !input 'home_value01_icon' + icon_color_rgb: !input 'home_value01_icon_color' + label_color_rgb: !input 'home_value01_label_color' + page: home + component: value01 + - entity: !input 'home_value02' + icon: !input 'home_value02_icon' + icon_color_rgb: !input 'home_value02_icon_color' + label_color_rgb: !input 'home_value02_label_color' + page: home + component: value02 + - entity: !input 'home_value03' + icon: !input 'home_value03_icon' + icon_color_rgb: !input 'home_value03_icon_color' + label_color_rgb: !input 'home_value03_label_color' + page: home + component: value03 + - repeat: + for_each: '{{ home_page_values }}' + sequence: + - &display_value + if: '{{ repeat.item.entity is string and repeat.item.entity is match "sensor." and has_value(repeat.item.entity) }}' + then: + - if: '{{ repeat.item.icon | length > 0 }}' + then: + ### ICON Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: '{{ repeat.item.page }}.{{ repeat.item.component }}_icon' + message: > + {{ + repeat.item.icon_color_rgb + if is_number(repeat.item.icon_color_rgb) + else + ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ + ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ + (repeat.item.icon_color_rgb[2] //(2**3)) + }} + continue_on_error: true + ### ICON Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ repeat.item.page }}.{{ repeat.item.component }}_icon' + message: > + {% if repeat.item.icon is string %} + {{ + all_icons[repeat.item.icon.split(":")[1]] | default(all_icons.unknown) + if repeat.item.icon.split(":") | count > 0 + else repeat.item.icon + }} + {% else %}{{ all_icons.unknown }} + {% endif %} + continue_on_error: true + - variables: + repeat_item_state: '{{ states(repeat.item.entity, rounded=true) | default("unavailable") }}' + repeat_item_state_available: '{{ repeat_item_state not in ["unavailable", "unknown", "", None] }}' + - condition: '{{ repeat_item_state_available }}' + ### LABEL Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: '{{ repeat.item.page }}.{{ repeat.item.component }}{{ "_state" if repeat.item.page == page.home }}' + message: > + {{ + repeat.item.label_color_rgb + if is_number(repeat.item.label_color_rgb) + else + ((repeat.item.label_color_rgb[0] //(2**3)) *(2**11))+ + ((repeat.item.label_color_rgb[1] //(2**2)) *(2**5))+ + (repeat.item.label_color_rgb[2] //(2**3)) + }} + continue_on_error: true + ### LABEL Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ repeat.item.page }}.{{ repeat.item.component }}{{ "_state" if repeat.item.page == page.home }}' + message: > + {{ + (repeat_item_state ~ (state_attr(repeat.item.entity, "unit_of_measurement") | default("") if state_attr(repeat.item.entity, "unit_of_measurement") is string else "")) + if is_number(repeat_item_state) + else repeat_item_state + }} + continue_on_error: true + + ##### Set notify icon ##### + - &refresh-page_home-notifications_icon + if: '{{ true }}' + then: + - variables: + notification_unread_state: '{{ states(notification_unread) | default("unavailable") if notification_unread is string else "unavailable" }}' + - condition: '{{ notification_unread_state in ["on", "off"] }}' + - variables: + notification_text_state: '{{ states(notification_text) | default(None) if notification_text is string else None }}' + set_button04_icon: > + {{ + all_icons[page_home.buttons[3].icon.split(":")[1]] | default(page_home.buttons[3].icon if page_home.buttons[3].icon is string else all_icons.unknown) + if notification_unread_state == "on" and notification_text_state | length > 0 + else all_icons.blank + }} + set_button04_icon_font: > + {{ + ( + page_home.buttons[3].color_rgb[notification_unread_state] + if is_number(page_home.buttons[3].color_rgb[notification_unread_state]) + else + ((page_home.buttons[3].color_rgb[notification_unread_state][0] //(2**3)) *(2**11))+ + ((page_home.buttons[3].color_rgb[notification_unread_state][1] //(2**2)) *(2**5))+ + (page_home.buttons[3].color_rgb[notification_unread_state][2] //(2**3)) + ) + if notification_unread_state in ["on", "off"] and notification_text_state | length > 0 + else nextion.color.grey_light + }} + ##### SET ICON Font - Notify ##### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: home.button04_icon + message: '{{ set_button04_icon }}' + continue_on_error: true + + ##### SET ICON Font Color - Notify ##### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.button04_icon + message: '{{ set_button04_icon_font }}' + continue_on_error: true + + ###### QR Code - Icon ###### + - *delay-default + - if: '{{ qrcode_enabled == true }}' + then: # Display QR code icon + ### ICON Font Color ### + - service: '{{ nextion.command.font_color }}' + data: + component: home.button05_icon + message: > + {{ + page_home.buttons[4].color_rgb.on + if is_number(page_home.buttons[4].color_rgb.on) + else + ((page_home.buttons[4].color_rgb.on[0] //(2**3)) *(2**11))+ + ((page_home.buttons[4].color_rgb.on[1] //(2**2)) *(2**5))+ + (page_home.buttons[4].color_rgb.on[2] //(2**3)) + }} + continue_on_error: true + ### ICON Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: home.button05_icon + message: > + {{ + all_icons[page_home.buttons[4].icon.split(":")[1]] | default(page_home.buttons[4].icon + if page_home.buttons[4].icon is string + else all_icons.unknown) + }} + continue_on_error: true + else: # Display blank icon + - service: '{{ nextion.command.text_printf }}' + data: + component: home.button05_icon + message: '{{ all_icons.blank }}' + continue_on_error: true + + ###### ENTITIES - Icon ###### + - *delay-default + - if: '{{ entitypages_enabled }}' + then: # Display entities icon + ### ICON Font Color ### + - service: '{{ nextion.command.font_color }}' + data: + component: home.button06_icon + message: > + {{ + page_home.buttons[5].color_rgb.on + if is_number(page_home.buttons[5].color_rgb.on) + else + ((page_home.buttons[5].color_rgb.on[0] //(2**3)) *(2**11))+ + ((page_home.buttons[5].color_rgb.on[1] //(2**2)) *(2**5))+ + (page_home.buttons[5].color_rgb.on[2] //(2**3)) + }} + continue_on_error: true + ### ICON Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: home.button06_icon + message: > + {{ + all_icons[page_home.buttons[5].icon.split(":")[1]] | default(page_home.buttons[5].icon + if page_home.buttons[5].icon is string + else all_icons.unknown) + }} + continue_on_error: true + ### Show icon ### + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: button06_icon + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: button06 + continue_on_error: true + else: # Hide icon + - *delay-default + - service: '{{ nextion.command.hide }}' + data: + component: button06_icon + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.hide }}' + data: + component: button06 + continue_on_error: true + + ## BUTTON PAGES 01 - 04 ## + - alias: Button pages + conditions: '{{ nspanel_event.page in page.buttonpages }}' + sequence: &refresh_page_buttonpage + - &variables-page_buttons + variables: + button_page_index: '{{ (nspanel_event.page[-2:] | int(-1)) - 1 }}' + first_button: '{{ button_page_index * 8 }}' + last_button: '{{ first_button + 8 }}' + ##### BUTTON Page Labels ##### + button_pages_labels: + - label: !input button_page01_label + - label: !input button_page02_label + - label: !input button_page03_label + - label: !input button_page04_label + ##### BUTTONS Page - Buttons 1 - 32 ##### + button_pages_buttons: + - entity: !input 'entity01' + name: !input 'entity01_name' + icon: !input 'entity01_icon' + icon_color_rgb: !input 'entity01_icon_color' + confirm: !input 'entity01_confirm' + page: buttonpage01 + component: button01 + - entity: !input 'entity02' + name: !input 'entity02_name' + icon: !input 'entity02_icon' + icon_color_rgb: !input 'entity02_icon_color' + confirm: !input 'entity02_confirm' + page: buttonpage01 + component: button02 + - entity: !input 'entity03' + name: !input 'entity03_name' + icon: !input 'entity03_icon' + icon_color_rgb: !input 'entity03_icon_color' + confirm: !input 'entity03_confirm' + page: buttonpage01 + component: button03 + - entity: !input 'entity04' + name: !input 'entity04_name' + icon: !input 'entity04_icon' + icon_color_rgb: !input 'entity04_icon_color' + confirm: !input 'entity04_confirm' + page: buttonpage01 + component: button04 + - entity: !input 'entity05' + name: !input 'entity05_name' + icon: !input 'entity05_icon' + icon_color_rgb: !input 'entity05_icon_color' + confirm: !input 'entity05_confirm' + page: buttonpage01 + component: button05 + - entity: !input 'entity06' + name: !input 'entity06_name' + icon: !input 'entity06_icon' + icon_color_rgb: !input 'entity06_icon_color' + confirm: !input 'entity06_confirm' + page: buttonpage01 + component: button06 + - entity: !input 'entity07' + name: !input 'entity07_name' + icon: !input 'entity07_icon' + icon_color_rgb: !input 'entity07_icon_color' + confirm: !input 'entity07_confirm' + page: buttonpage01 + component: button07 + - entity: !input 'entity08' + name: !input 'entity08_name' + icon: !input 'entity08_icon' + icon_color_rgb: !input 'entity08_icon_color' + confirm: !input 'entity08_confirm' + page: buttonpage01 + component: button08 + - entity: !input 'entity09' + name: !input 'entity09_name' + icon: !input 'entity09_icon' + icon_color_rgb: !input 'entity09_icon_color' + confirm: !input 'entity09_confirm' + page: buttonpage02 + component: button01 + - entity: !input 'entity10' + name: !input 'entity10_name' + icon: !input 'entity10_icon' + icon_color_rgb: !input 'entity10_icon_color' + confirm: !input 'entity10_confirm' + page: buttonpage02 + component: button02 + - entity: !input 'entity11' + name: !input 'entity11_name' + icon: !input 'entity11_icon' + icon_color_rgb: !input 'entity11_icon_color' + confirm: !input 'entity11_confirm' + page: buttonpage02 + component: button03 + - entity: !input 'entity12' + name: !input 'entity12_name' + icon: !input 'entity12_icon' + icon_color_rgb: !input 'entity12_icon_color' + confirm: !input 'entity12_confirm' + page: buttonpage02 + component: button04 + - entity: !input 'entity13' + name: !input 'entity13_name' + icon: !input 'entity13_icon' + icon_color_rgb: !input 'entity13_icon_color' + confirm: !input 'entity13_confirm' + page: buttonpage02 + component: button05 + - entity: !input 'entity14' + name: !input 'entity14_name' + icon: !input 'entity14_icon' + icon_color_rgb: !input 'entity14_icon_color' + confirm: !input 'entity14_confirm' + page: buttonpage02 + component: button06 + - entity: !input 'entity15' + name: !input 'entity15_name' + icon: !input 'entity15_icon' + icon_color_rgb: !input 'entity15_icon_color' + confirm: !input 'entity15_confirm' + page: buttonpage02 + component: button07 + - entity: !input 'entity16' + name: !input 'entity16_name' + icon: !input 'entity16_icon' + icon_color_rgb: !input 'entity16_icon_color' + confirm: !input 'entity16_confirm' + page: buttonpage02 + component: button08 + - entity: !input 'entity17' + name: !input 'entity17_name' + icon: !input 'entity17_icon' + icon_color_rgb: !input 'entity17_icon_color' + confirm: !input 'entity17_confirm' + page: buttonpage03 + component: button01 + - entity: !input 'entity18' + name: !input 'entity18_name' + icon: !input 'entity18_icon' + icon_color_rgb: !input 'entity18_icon_color' + confirm: !input 'entity18_confirm' + page: buttonpage03 + component: button02 + - entity: !input 'entity19' + name: !input 'entity19_name' + icon: !input 'entity19_icon' + icon_color_rgb: !input 'entity19_icon_color' + confirm: !input 'entity19_confirm' + page: buttonpage03 + component: button03 + - entity: !input 'entity20' + name: !input 'entity20_name' + icon: !input 'entity20_icon' + icon_color_rgb: !input 'entity20_icon_color' + confirm: !input 'entity20_confirm' + page: buttonpage03 + component: button04 + - entity: !input 'entity21' + name: !input 'entity21_name' + icon: !input 'entity21_icon' + icon_color_rgb: !input 'entity21_icon_color' + confirm: !input 'entity21_confirm' + page: buttonpage03 + component: button05 + - entity: !input 'entity22' + name: !input 'entity22_name' + icon: !input 'entity22_icon' + icon_color_rgb: !input 'entity22_icon_color' + confirm: !input 'entity22_confirm' + page: buttonpage03 + component: button06 + - entity: !input 'entity23' + name: !input 'entity23_name' + icon: !input 'entity23_icon' + icon_color_rgb: !input 'entity23_icon_color' + confirm: !input 'entity23_confirm' + page: buttonpage03 + component: button07 + - entity: !input 'entity24' + name: !input 'entity24_name' + icon: !input 'entity24_icon' + icon_color_rgb: !input 'entity24_icon_color' + confirm: !input 'entity24_confirm' + page: buttonpage03 + component: button08 + - entity: !input 'entity25' + name: !input 'entity25_name' + icon: !input 'entity25_icon' + icon_color_rgb: !input 'entity25_icon_color' + confirm: !input 'entity25_confirm' + page: buttonpage04 + component: button01 + - entity: !input 'entity26' + name: !input 'entity26_name' + icon: !input 'entity26_icon' + icon_color_rgb: !input 'entity26_icon_color' + confirm: !input 'entity26_confirm' + page: buttonpage04 + component: button02 + - entity: !input 'entity27' + name: !input 'entity27_name' + icon: !input 'entity27_icon' + icon_color_rgb: !input 'entity27_icon_color' + confirm: !input 'entity27_confirm' + page: buttonpage04 + component: button03 + - entity: !input 'entity28' + name: !input 'entity28_name' + icon: !input 'entity28_icon' + icon_color_rgb: !input 'entity28_icon_color' + confirm: !input 'entity28_confirm' + page: buttonpage04 + component: button04 + - entity: !input 'entity29' + name: !input 'entity29_name' + icon: !input 'entity29_icon' + icon_color_rgb: !input 'entity29_icon_color' + confirm: !input 'entity29_confirm' + page: buttonpage04 + component: button05 + - entity: !input 'entity30' + name: !input 'entity30_name' + icon: !input 'entity30_icon' + icon_color_rgb: !input 'entity30_icon_color' + confirm: !input 'entity30_confirm' + page: buttonpage04 + component: button06 + - entity: !input 'entity31' + name: !input 'entity31_name' + icon: !input 'entity31_icon' + icon_color_rgb: !input 'entity31_icon_color' + confirm: !input 'entity31_confirm' + page: buttonpage04 + component: button07 + - entity: !input 'entity32' + name: !input 'entity32_name' + icon: !input 'entity32_icon' + icon_color_rgb: !input 'entity32_icon_color' + confirm: !input 'entity32_confirm' + page: buttonpage04 + component: button08 + - if: '{{ button_page_index >= 0 and button_page_index <= 3 }}' + then: + ##### Button Page Label ##### + - if: '{{ button_pages_labels[button_page_index].label | length > 0 }}' + then: + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ "bpage%02d_label" | format(button_page_index+1) }}' + message: '{{ button_pages_labels[button_page_index].label }}' + continue_on_error: true + + ###### Display page while other elements are still loading ##### + - variables: + show_while_loading: !input 'show_while_loading' + - if: '{{ show_while_loading }}' + then: + - *delay-default + - service: '{{ nextion.command.show_all }}' + continue_on_error: true + + ##### NSPanel build Button page ##### + - repeat: + for_each: '{{ button_pages_buttons[first_button:last_button] }}' + sequence: &display-button_page_button + - if: > + {{ + repeat.item.entity is string and + repeat.item.entity | length > 0 and + repeat.item.entity.split(".") | default([]) | count > 0 + }} + then: + - variables: + item_domain: '{{ repeat.item.entity.split(".")[0] | default("unknown") }}' + current_entity_state: '{{ states(repeat.item.entity) | default("unavailable") }}' + current_entity_state_available: '{{ current_entity_state not in ["unavailable"] }}' + # Button PIC GRAY/WHITE + btn_pic: > + {{ + nextion.pic.button.on + if current_entity_state in ["on", "open", "opening", "home"] + or (item_domain == "climate" and current_entity_state != "off") + or (item_domain in ["button","input_button","scene"] and trigger.id is match "current_state_entity") + else nextion.pic.button.off + }} + # TEXT, BRIGHTNESS and ICON Background + btn_bg: > + {{ + nextion.color.white + if current_entity_state in ["on", "open", "opening", "home"] + or (item_domain == "climate" and current_entity_state != "off") + or (item_domain in ["button","input_button","scene"] and trigger.id is match "current_state_entity") + else nextion.color.grey_dark + }} + # ICON Font Color + btn_icon_font: > + {% if not current_entity_state_available %}{{ nextion.color.red }} + {% elif current_entity_state in ["off", "closed", "closing"] or (item_domain == "person" and current_entity_state != "home") %} + {{ nextion.color.grey_light }} + {% elif item_domain in ["button", "input_button", "scene"] and trigger.id is match "current_state_entity" %} + {{ + repeat.item.icon_color_rgb + if is_number(repeat.item.icon_color_rgb) + else + ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ + ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ + (repeat.item.icon_color_rgb[2] //(2**3)) + }} + {% elif item_domain in ["button", "input_button", "scene"] %}{{ nextion.color.grey_light }} + {% elif current_entity_state in ["on", "open", "opening", "home"] or (item_domain == "climate" and current_entity_state != "off") %} + {{ + repeat.item.icon_color_rgb + if is_number(repeat.item.icon_color_rgb) + else + ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ + ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ + (repeat.item.icon_color_rgb[2] //(2**3)) + }} + {% else %}{{ nextion.color.red }} + {% endif %} + # LABEL Font Color + btn_txt_font: >- + {% if not current_entity_state_available %}{{ nextion.color.white }} + {% elif current_entity_state in ["off", "closed", "closing"] or (item_domain == "person" and current_entity_state != "home") %} + {{ nextion.color.white }} + {% elif item_domain in ["button", "input_button", "scene"] and trigger.id is match "current_state_entity" %} + {{ nextion.color.grey_dark }} + {% elif item_domain in ["button", "input_button", "scene"] %} + {{ nextion.color.white }} + {% elif current_entity_state in ["on", "open", "opening", "home"] or (item_domain == "climate" and current_entity_state != "off") %} + {{ nextion.color.grey_dark }} + {% else %} + {{ nextion.color.white }} + {% endif %} + # BRIGHTNESS Font Color + btn_bri_font: '{{ btn_txt_font }}' #'{{ nextion.color.grey_dark }}' + # ICON Value + btn_icon: > + {% if not current_entity_state_available %}{{ nextion.icon.domain.unknown }} + {% elif repeat.item.icon | length > 0 %} + {{ + all_icons[repeat.item.icon.split(":")[1]] | default(all_icons.unknown) + if repeat.item.icon.split(":") | count > 0 + else repeat.item.icon + }} + {% elif repeat.item.entity and repeat.item.entity.split(".") | count > 1 %} + {{ nextion.icon.domain[repeat.item.entity.split(".")[0] if repeat.item.entity else "unknown"] }} + {% else %}{{ nextion.icon.domain.unknown }} + {% endif %} + # LABEL Value + btn_label: '{{ repeat.item.name }}' + # BRIGHTNESS Value + btn_bri_txt: >- + {% if not current_entity_state_available %} 0 + {% elif item_domain == "light" and current_entity_state == "on" and state_attr(repeat.item.entity, "brightness") != None %} + {{ (state_attr(repeat.item.entity, "brightness") | int * 100 /255) | round(0) }}% + {% elif item_domain == "fan" and current_entity_state == "on" and state_attr(repeat.item.entity, "percentage") != None %} + {{ state_attr(repeat.item.entity, "percentage") | round(0, default=0) }}% + {% elif item_domain == "cover" and current_entity_state in ["open", "opening", "closing"] and state_attr(repeat.item.entity, "current_position") != None %} + {{ (state_attr(repeat.item.entity, "current_position") | int(100)) | round(0) }}% + {% elif item_domain == "climate" and current_entity_state != "off" and state_attr(repeat.item.entity, 'current_temperature') != None %} + {{ (state_attr(repeat.item.entity, 'current_temperature') | float) | round(0) }}{{ temperature_units }} + {% else -%} 0 + {% endif -%} + - *delay-default + - service: '{{ nextion.command.set_button }}' + data: + btn_id: '{{ repeat.item.page }}.{{ repeat.item.component }}' + btn_pic: '{{ btn_pic }}' + btn_bg: '{{ btn_bg }}' + btn_icon_font: '{{ btn_icon_font }}' + btn_txt_font: '{{ btn_txt_font }}' + btn_bri_font: '{{ btn_bri_font }}' + btn_icon: '{{ btn_icon }}' + btn_label: '{{ btn_label }}' + btn_bri_txt: '{{ btn_bri_txt }}' + continue_on_error: true + - if: '{{ item_domain in ["button","input_button","scene"] and trigger.id is match "current_state_entity" }}' + then: + - delay: + milliseconds: 800 + - service: '{{ nextion.command.set_button }}' + data: + btn_id: '{{ repeat.item.page }}.{{ repeat.item.component }}' + btn_pic: '{{ nextion.pic.button.off }}' + btn_bg: '{{ nextion.color.grey_dark }}' + btn_icon_font: > + {{ + repeat.item.icon_color_rgb + if is_number(repeat.item.icon_color_rgb) + else + ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ + ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ + (repeat.item.icon_color_rgb[2] //(2**3)) + }} + btn_txt_font: '{{ nextion.color.white }}' + btn_bri_font: '{{ btn_bri_font }}' + btn_icon: '{{ btn_icon }}' + btn_label: '{{ btn_label }}' + btn_bri_txt: '{{ btn_bri_txt }}' + continue_on_error: true + ###### SHOW All component when page loading done ##### + - if: '{{ not show_while_loading }}' + then: + - *delay-default + - service: '{{ nextion.command.show_all }}' + continue_on_error: true + else: + ###### Show empty page ##### + - *delay-default + - service: '{{ nextion.command.show_all }}' + continue_on_error: true + + ## PAGE LIGHT ## + - alias: Light settings page + conditions: '{{ nspanel_event.page == page.light }}' + sequence: &refresh_page_light + - variables: + supported_color_modes: '{{ state_attr(nspanel_event.entity, "supported_color_modes") | default("unknown") }}' + color_mode_color: > + {{ + "hs" in supported_color_modes + or "xy" in supported_color_modes + or "rgb" in supported_color_modes + or "rgbw" in supported_color_modes + or "rgbww" in supported_color_modes + }} + color_mode_color_temp: '{{ "color_temp" in supported_color_modes }}' + #color_mode_brightness: > + # {{ + # "brightness" in supported_color_modes + # or "white" in supported_color_modes + # or color_mode_color + # or color_mode_color_temp + # }} + ##### LIGHT State ##### + - variables: + curr_brightness: '{{ (state_attr(nspanel_event.entity, "brightness") | int(0) * 100 / 255) | round(0) }}' + - *delay-default + - service: '{{ nextion.command.value }}' + data: + component: light.lightslider + message: '{{ curr_brightness }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: light.light_value + message: '{{ curr_brightness }}%' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: light.light_value_2 + message: '{{ curr_brightness }}%' + continue_on_error: true + + ##### LIGHT Check Color_Temp Value is available when yes send some current Values ##### + - if: '{{ color_mode_color_temp }}' + then: + - variables: + curr_color_temp: '{{ state_attr(nspanel_event.entity, "color_temp") | int(-1) }}' + min_mireds: '{{ state_attr(nspanel_event.entity, "min_mireds") | int(153) }}' + max_mireds: '{{ state_attr(nspanel_event.entity, "max_mireds") | int(500) }}' + - variables: + curr_color_temp: > + {{ + curr_color_temp + if curr_color_temp >= min_mireds and curr_color_temp <= max_mireds + else ((min_mireds+max_mireds)/2) | int(327) + }} + - condition: '{{ is_number(curr_color_temp) }}' + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: light.temp_value + message: '{{ curr_color_temp }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: light.temp_value_2 + message: '{{ curr_color_temp }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.value }}' + data: + component: light.tempslider + message: '{{ curr_color_temp }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.printf }}' + data: + cmd: tempslider.minval={{ min_mireds }} + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.printf }}' + data: + cmd: tempslider.maxval={{ max_mireds }} + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: temp_button + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: temp_value_2 + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: temp_touch + continue_on_error: true + + ##### Hide color button when not supported ##### + - if: '{{ color_mode_color }}' + then: + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: color_button + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: color_touch + continue_on_error: true + + ## PAGE COVER ## + - alias: Cover settings page + conditions: '{{ nspanel_event.page == page.cover }}' + sequence: &refresh_page_cover + ##### COVER State + - service: '{{ nextion.command.value }}' + data: + component: cover.coverslider + message: '{{ (state_attr(nspanel_event.entity, "current_position") | int ) | round(0) }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: cover.cover_value + message: '{{ (state_attr(nspanel_event.entity, "current_position") | int ) | round(0) }} %' + continue_on_error: true + + ##### COVER Battery ICON Yes / NO ##### + - variables: + battery_level: > + {% if state_attr(nspanel_event.entity, "battery") | default("unavailable") not in ["unavailable", "unknown", "", None] %} + {{ state_attr(nspanel_event.entity, "battery") | default("unavailable") }} + {% elif expand(device_entities(device_id(nspanel_event.entity))) + | selectattr("attributes.device_class", "defined") + | selectattr("attributes.device_class", "eq", "battery") + | map(attribute="state") + | map("float") + | list + | count > 0 %} + {{ + expand(device_entities(device_id(nspanel_event.entity))) + | selectattr("attributes.device_class", "defined") + | selectattr("attributes.device_class", "eq", "battery") + | map(attribute="state") | map("float") + | list + | first + | round(0) + }} + {% elif has_value(nspanel_event.entity | replace("cover.","sensor.") ~ "_battery") %} + {{ states(nspanel_event.entity | replace("cover.","sensor.") ~ "_battery", rounded=true) | default("unavailable") }} + {% elif has_value(nspanel_event.entity | replace("cover.","sensor.") | replace("cover", "battery")) %} + {{ states(nspanel_event.entity | replace("cover.","sensor.") | replace("cover", "battery"), rounded=true) | default("unavailable") }} + {% else %} unavailable + {% endif %} + - if: '{{ is_number(battery_level) }}' + then: + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: cover.battery_value + message: '{{ battery_level }} %' + continue_on_error: true + ### ICON Battery Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: cover.battery_icon + message: '{{ nextion.color.grey_super_light }}' + continue_on_error: true + ### ICON Battery Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: cover.battery_icon + message: '{{ all_icons["battery-medium"] }}' + continue_on_error: true + + ## PAGE FAN ## + - alias: Fan settings page + conditions: '{{ nspanel_event.page == page.fan }}' + sequence: &refresh_page_fan + - variables: + fan: + supported_features: '{{ state_attr(nspanel_event.entity, "supported_features") | int(0) }}' + percentage: > + {{ + state_attr(nspanel_event.entity, "percentage") | int(0) + if is_state(nspanel_event.entity, 'on') + else 0 + }} + steps: > + {% set percentage_step = state_attr(nspanel_event.entity, "percentage_step") | float(0) %} + {{ + (100/percentage_step) | round(0) | int(0) + if percentage_step > 0 + else 0 + }} + - condition: '{{ fan.steps > 0 and fan.supported_features | bitwise_and(1) > 0 }}' + - service: '{{ nextion.command.value }}' + data: + component: fanslider + message: '{{ ((fan.percentage / 100) * fan.steps) | round(0) | int(0) }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.printf }}' + data: + cmd: fanslider.maxval={{ fan.steps }} + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: fan_value + message: '{{ fan.percentage }}%' + continue_on_error: true + - service: '{{ nextion.command.printf }}' + data: + cmd: button_up.pco={{ nextion.color.grey_white if fan.percentage < 100 else nextion.color.grey_dark }} + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.printf }}' + data: + cmd: button_down.pco={{ nextion.color.grey_white if fan.percentage > 0 else nextion.color.grey_dark }} + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.printf }}' + data: + cmd: button_off.pco={{ nextion.color.grey_white if fan.percentage > 0 else nextion.color.grey_dark }} + continue_on_error: true + - *delay-default + + ## PAGE CLIMATE ## + - alias: Climate page + conditions: '{{ nspanel_event.page == page.climate }}' + sequence: &refresh_page_climate + - &variables-climate_entity + variables: + climate_entity: '{{ nspanel_event.entity if nspanel_event.entity is defined }}' + settings_entity_domain: > + {{ + climate_entity.split(".")[0] + if + climate_entity is defined and + climate_entity is string and + climate_entity.split(".") | count > 0 + else "unknown" + }} + hvac_modes: '{{ state_attr(climate_entity, "hvac_modes") if settings_entity_domain == "climate" }}' + + - condition: '{{ settings_entity_domain == "climate" }}' + - service: '{{ nextion.command.text_printf }}' + data: + component: page_label + message: '{{ state_attr(climate_entity, "friendly_name") }}' + continue_on_error: true + ##### Values ##### + - &variables-climate_page + variables: + climate_page_entities: + - entity: !input 'climate_value01' + icon: !input 'climate_value01_icon' + icon_color_rgb: !input 'climate_value01_icon_color' + label_color_rgb: !input 'climate_value01_label_color' + page: climate + component: value01 + - entity: !input 'climate_value02' + icon: !input 'climate_value02_icon' + icon_color_rgb: !input 'climate_value02_icon_color' + label_color_rgb: !input 'climate_value02_label_color' + page: climate + component: value02 + - entity: !input 'climate_value03' + icon: !input 'climate_value03_icon' + icon_color_rgb: !input 'climate_value03_icon_color' + label_color_rgb: !input 'climate_value03_label_color' + page: climate + component: value03 + - entity: !input 'climate_value04' + icon: !input 'climate_value04_icon' + icon_color_rgb: !input 'climate_value04_icon_color' + label_color_rgb: !input 'climate_value04_label_color' + page: climate + component: value04 + - repeat: + for_each: '{{ climate_page_entities }}' + sequence: *display_value + + ##### Slider & climate values ##### + - &climate-update_slider + if: '{{ not (climate_entity == climate and embedded_climate) }}' + then: + - variables: + current_temp: '{{ state_attr(climate_entity, "current_temperature") | float(-999) | round(1) }}' + target_temp: > + {{ + state_attr(climate_entity, "temperature") | float(-999) | round(1) + if has_value(climate_entity) + else -999 + }} + temp_offset: '{{ (state_attr(climate_entity, "min_temp") | float(5) * 10) | round(0) | int }}' + max_temp: '{{ (state_attr(climate_entity, "max_temp") | float(25) * 10) | round(0) | int }}' + temp_step: > + {% set target_temp_step = state_attr(climate_entity, "target_temp_step") %} + {% if not is_number(target_temp_step) %} + {% set target_temp_step = state_attr(climate_entity, "target_temperature_step") %} + {% endif %} + {% set target_temp_step = target_temp_step | float(0.5) | abs %} + {{ ((10 * target_temp_step) | round(0) | int) if is_number(target_temp_step) and target_temp_step > 0 else 10 }} + total_steps: '{{ ((max_temp-temp_offset)/temp_step) | round(0) | int }}' + climate_state: '{{ states(climate_entity) | default("unavailable") if climate_entity is string else "unavailable" }}' + hvac_action: '{{ state_attr(climate_entity, "hvac_action") }}' + climate_action: '{{ hvac_action if hvac_action not in ["unavailable", "unknown", "", None] else climate_state }}' + climate_icon: > + {% if "off" in climate_action %}{{ all_icons.blank }} + {% elif "heating" in climate_action or "heat" in climate_action %}{{ all_icons["thermometer-lines"] }} + {% elif "cooling" in climate_action or "cool" in climate_action %}{{ all_icons.snowflake }} + {% elif "drying" in climate_action or "dry" in climate_action %}{{ all_icons["water-percent"] }} + {% elif "fan" in climate_action or "fan_only" in climate_action %}{{ all_icons.fan }} + {% elif "heat_cool" in climate_action %}{{ all_icons.autorenew }} + {% elif "auto" in climate_action %}{{ all_icons["calendar-sync"] }} + {% elif "idle" in climate_action %}{{ all_icons.thermometer }} + {% else %}{{ all_icons.blank }} + {% endif %} + - *delay-default + - service: '{{ nextion.command.set_climate }}' + data: + current_temp: '{{ current_temp }}' + target_temp: '{{ target_temp }}' + temp_step: '{{ temp_step }}' + total_steps: '{{ total_steps }}' + temp_offset: '{{ temp_offset }}' + climate_icon: '{{ climate_icon }}' + embedded_climate: '{{ embedded_climate }}' + continue_on_error: true + + ##### Climate buttons ##### + - &climate-update_buttons + if: '{{ not (climate_entity == climate and embedded_climate) }}' + then: + - repeat: + for_each: '{{ page_climate.buttons.hvac_mode }}' + sequence: + - condition: '{{ repeat.item.mode in hvac_modes }}' + - *delay-default + ### ICON Font Color ### + - service: '{{ nextion.command.font_color }}' + data: + component: 'climate.{{ repeat.item.component }}_icon' + message: > + {{ + nextion.color[repeat.item.color] + if states(climate_entity) == repeat.item.mode + else nextion.color.disabled + }} + continue_on_error: true + ### ICON Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: 'climate.{{ repeat.item.component }}_icon' + message: '{{ all_icons[repeat.item.icon] }}' + continue_on_error: true + ### Enable button click ### + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: '{{ repeat.item.component }}' + continue_on_error: true + + ##### Climate custom buttons ##### + - &climate-update_custom_buttons + if: '{{ true }}' + then: + - &climate-update_custom_buttons-variables + variables: + climate_custom_buttons: + - entity: !input climate_button08 + icon: !input climate_button08_icon + icon_color_rgb: !input climate_button08_icon_color + component: button08 + - entity: !input climate_button09 + icon: !input climate_button09_icon + icon_color_rgb: !input climate_button09_icon_color + component: button09 + - &climate-update_custom_buttons-update + repeat: + for_each: '{{ climate_custom_buttons }}' + sequence: + - condition: '{{ repeat.item.entity is defined and repeat.item.entity is string and repeat.item.entity | length > 0 }}' + - variables: + entity_domain: > + {{ + repeat.item.entity.split(".")[0] + if + repeat.item.entity is defined and + repeat.item.entity is string and + repeat.item.entity.split(".") | count > 0 + else "unknown" + }} + - condition: '{{ entity_domain != "unknown" }}' + - *delay-default + ### ICON Font Color ### + - service: '{{ nextion.command.font_color }}' + data: + component: 'climate.{{ repeat.item.component }}_icon' + message: > + {{ + ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+(repeat.item.icon_color_rgb[2] //(2**3)) + if states(repeat.item.entity) in ["on", "true", true, "open", "opening"] + else nextion.color.disabled + }} + continue_on_error: true + ### ICON Font ### + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: 'climate.{{ repeat.item.component }}_icon' + message: > + {{ + all_icons[repeat.item.icon.split(":")[1]] | default(all_icons.unknown) + if repeat.item.icon is defined and repeat.item.icon is string and ":" in repeat.item.icon and repeat.item.icon.split(":") | count > 0 + else nextion.icon.domain[entity_domain] | default(all_icons.unknown) + }} + continue_on_error: true + ### Enable button click ### + - *delay-default + - service: '{{ nextion.command.show }}' + data: + component: '{{ repeat.item.component }}' + continue_on_error: true + + ## ENTITY PAGES 01 - 04 ## + - alias: Entity pages + conditions: '{{ nspanel_event.page in page.entitypages }}' + sequence: &refresh-entity_pages + - &variables-entity_pages + variables: + ##### Entity pages ##### + entitypages_value_alignment_temp: !input 'entitypages_value_alignment' + entitypages_value_alignment: > + {{ + entitypages_value_alignment_temp | int + if + is_number(entitypages_value_alignment_temp) and + entitypages_value_alignment_temp | int >= 0 and + entitypages_value_alignment_temp | int <= 2 + else 0 + }} + entity_pages_labels: + - label: !input 'entity_page01_label' + - label: !input 'entity_page02_label' + - label: !input 'entity_page03_label' + - label: !input 'entity_page04_label' + entity_pages_entities: + - entity: !input 'entities_entity01' + name: !input 'entities_entity01_name' + icon: !input 'entities_entity01_icon' + page: entitypage01 + component: value01 + - entity: !input 'entities_entity02' + name: !input 'entities_entity02_name' + icon: !input 'entities_entity02_icon' + page: entitypage01 + component: value02 + - entity: !input 'entities_entity03' + name: !input 'entities_entity03_name' + icon: !input 'entities_entity03_icon' + page: entitypage01 + component: value03 + - entity: !input 'entities_entity04' + name: !input 'entities_entity04_name' + icon: !input 'entities_entity04_icon' + page: entitypage01 + component: value04 + - entity: !input 'entities_entity05' + name: !input 'entities_entity05_name' + icon: !input 'entities_entity05_icon' + page: entitypage01 + component: value05 + - entity: !input 'entities_entity06' + name: !input 'entities_entity06_name' + icon: !input 'entities_entity06_icon' + page: entitypage01 + component: value06 + - entity: !input 'entities_entity07' + name: !input 'entities_entity07_name' + icon: !input 'entities_entity07_icon' + page: entitypage01 + component: value07 + - entity: !input 'entities_entity08' + name: !input 'entities_entity08_name' + icon: !input 'entities_entity08_icon' + page: entitypage01 + component: value08 + - entity: !input 'entities_entity09' + name: !input 'entities_entity09_name' + icon: !input 'entities_entity09_icon' + page: entitypage02 + component: value01 + - entity: !input 'entities_entity10' + name: !input 'entities_entity10_name' + icon: !input 'entities_entity10_icon' + page: entitypage02 + component: value02 + - entity: !input 'entities_entity11' + name: !input 'entities_entity11_name' + icon: !input 'entities_entity11_icon' + page: entitypage02 + component: value03 + - entity: !input 'entities_entity12' + name: !input 'entities_entity12_name' + icon: !input 'entities_entity12_icon' + page: entitypage02 + component: value04 + - entity: !input 'entities_entity13' + name: !input 'entities_entity13_name' + icon: !input 'entities_entity13_icon' + page: entitypage02 + component: value05 + - entity: !input 'entities_entity14' + name: !input 'entities_entity14_name' + icon: !input 'entities_entity14_icon' + page: entitypage02 + component: value06 + - entity: !input 'entities_entity15' + name: !input 'entities_entity15_name' + icon: !input 'entities_entity15_icon' + page: entitypage02 + component: value07 + - entity: !input 'entities_entity16' + name: !input 'entities_entity16_name' + icon: !input 'entities_entity16_icon' + page: entitypage02 + component: value08 + - entity: !input 'entities_entity17' + name: !input 'entities_entity17_name' + icon: !input 'entities_entity17_icon' + page: entitypage03 + component: value01 + - entity: !input 'entities_entity18' + name: !input 'entities_entity18_name' + icon: !input 'entities_entity18_icon' + page: entitypage03 + component: value02 + - entity: !input 'entities_entity19' + name: !input 'entities_entity19_name' + icon: !input 'entities_entity19_icon' + page: entitypage03 + component: value03 + - entity: !input 'entities_entity20' + name: !input 'entities_entity20_name' + icon: !input 'entities_entity20_icon' + page: entitypage03 + component: value04 + - entity: !input 'entities_entity21' + name: !input 'entities_entity21_name' + icon: !input 'entities_entity21_icon' + page: entitypage03 + component: value05 + - entity: !input 'entities_entity22' + name: !input 'entities_entity22_name' + icon: !input 'entities_entity22_icon' + page: entitypage03 + component: value06 + - entity: !input 'entities_entity23' + name: !input 'entities_entity23_name' + icon: !input 'entities_entity23_icon' + page: entitypage03 + component: value07 + - entity: !input 'entities_entity24' + name: !input 'entities_entity24_name' + icon: !input 'entities_entity24_icon' + page: entitypage03 + component: value08 + - entity: !input 'entities_entity25' + name: !input 'entities_entity25_name' + icon: !input 'entities_entity25_icon' + page: entitypage04 + component: value01 + - entity: !input 'entities_entity26' + name: !input 'entities_entity26_name' + icon: !input 'entities_entity26_icon' + page: entitypage04 + component: value02 + - entity: !input 'entities_entity27' + name: !input 'entities_entity27_name' + icon: !input 'entities_entity27_icon' + page: entitypage04 + component: value03 + - entity: !input 'entities_entity28' + name: !input 'entities_entity28_name' + icon: !input 'entities_entity28_icon' + page: entitypage04 + component: value04 + - entity: !input 'entities_entity29' + name: !input 'entities_entity29_name' + icon: !input 'entities_entity29_icon' + page: entitypage04 + component: value05 + - entity: !input 'entities_entity30' + name: !input 'entities_entity30_name' + icon: !input 'entities_entity30_icon' + page: entitypage04 + component: value06 + - entity: !input 'entities_entity31' + name: !input 'entities_entity31_name' + icon: !input 'entities_entity31_icon' + page: entitypage04 + component: value07 + - entity: !input 'entities_entity32' + name: !input 'entities_entity32_name' + icon: !input 'entities_entity32_icon' + page: entitypage04 + component: value08 + - variables: + entity_page_index: '{{ (nspanel_event.page[-2:] | int(-1)) - 1 }}' + first_entity: '{{ entity_page_index * 8 }}' + last_entity: '{{ first_entity + 8 }}' + ##### Entity page - Label ##### + - if: '{{ entity_pages_labels[entity_page_index].label | length > 0 }}' + then: + - service: '{{ nextion.command.text_printf }}' + data: + 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] }}' + sequence: &update-entity_page_entity + - if: '{{ repeat.item.entity is string and repeat.item.entity | length > 0 }}' + then: + - variables: + repeat_item_state: '{{ states(repeat.item.entity, rounded=true) | default("unavailable") }}' + repeat_item_icon: > + {% if repeat.item.icon is string and repeat.item.icon | length > 0 %} + {{ + all_icons[repeat.item.icon.split(":")[1]] | default(all_icons.unknown) + if repeat.item.icon.split(":") | count > 0 + else repeat.item.icon + }} + {% elif state_attr(repeat.item.entity, "icon") | default("") not in ["unavailable", "unknown", "", None] %} + {{ all_icons[state_attr(repeat.item.entity, "icon").split(":")[1]] | default(None) }} + {% endif %} + - service: '{{ nextion.command.set_entity }}' + data: + ent_id: '{{ repeat.item.page }}.{{ repeat.item.component }}' + ent_icon: '{{ repeat_item_icon if repeat_item_icon else all_icons.blank }}' + ent_label: >- + {%- if repeat.item.name | length > 0 -%} {{ repeat.item.name }} + {%- elif repeat_item_state in ["unavailable", "unknown", "", None] -%} {{ repeat.item.entity }} + {%- else -%} {{ state_attr(repeat.item.entity, "friendly_name") | default(mui[language].no_name) }} + {%- endif -%} + ent_value: '{{ repeat_item_state ~ ((state_attr(repeat.item.entity, "unit_of_measurement") | default("")) if state_attr(repeat.item.entity, "unit_of_measurement") is string else "") }}' + ent_value_xcen: '{{ entitypages_value_alignment }}' + continue_on_error: true + + ## PAGE WEATHER (WEATHER01 to WEATHER05) ## + - alias: Weather pages + conditions: '{{ nspanel_event.page in page.weatherpages }}' + sequence: + - variables: + weather_attribution: '{{ state_attr(weather_entity, "attribution") if weather_entity is string }}' + weather_type: > + {% if not weather_attribution %} unavailable + {% elif "AccuWeather" in weather_attribution %} AccuWeather + {% elif "OpenWeatherMap" in weather_attribution %} OpenWeather + {% elif "SMHI" in weather_attribution %} SMHI + {% elif "met.no" in weather_attribution %} Met.no + {% elif "Météo-France" in weather_attribution %} Meteo_France + {% else %} Other + {% endif %} + weather_units: + hours_of_sun: '{{ state_attr(weather_entity, "hours_of_sun_unit") | default("h") if weather_entity is string and state_attr(weather_entity, "hours_of_sun_unit") else "h" }}' + precipitation: '{{ state_attr(weather_entity, "precipitation_unit") | default("") if weather_entity is string and state_attr(weather_entity, "precipitation_unit") }}' + precipitation_probability: '{{ state_attr(weather_entity, "precipitation_probability_unit") | default("%") if weather_entity is string and state_attr(weather_entity, "precipitation_probability_unit") else "%" }}' + pressure: '{{ state_attr(weather_entity, "pressure_unit") | default("") if weather_entity is string and state_attr(weather_entity, "pressure_unit") }}' + #temperature: '{{ state_attr(weather_entity, "temperature_unit") | default("°") if weather_entity is string and state_attr(weather_entity, "temperature_unit") else "°" }}' + thunderstorm_probability: '{{ state_attr(weather_entity, "thunderstorm_probability_unit") | default("%") if weather_entity is string and state_attr(weather_entity, "thunderstorm_probability_unit") else "%" }}' + uv_index: '{{ state_attr(weather_entity, "uv_index_unit") | default("") if weather_entity is string and state_attr(weather_entity, "uv_index_unit") }}' + #visibility: '{{ state_attr(weather_entity, "visibility_unit") | default("") if weather_entity is string and state_attr(weather_entity, "visibility_unit") }}' + wind_speed: '{{ state_attr(weather_entity, "wind_speed_unit") | default("") if weather_entity is string and state_attr(weather_entity, "wind_speed_unit") }}' + page_name: '{{ nspanel_event.page }}' + page_index: '{{ (page_name[-2:] | int(0)) - 1 }}' + + ##### Display relative day ##### + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ page_name }}.day' + message: '{{ (dict.values(mui[language].relative_day) | list)[page_index] }}' + continue_on_error: true + - *delay-default + + ##### Display date (long) ##### + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ page_name }}.date' + message: > + {{ + as_timestamp(now() + timedelta(days= (page_index))) + | timestamp_custom + ( + date_format + | replace("%A", (dict.values(mui[language].weekdays) | list)[(now() + timedelta(days= (page_index))).weekday()]) + | replace("%a", (dict.values(mui[language].weekdays_short) | list)[(now() + timedelta(days= (page_index))).weekday()]) + | replace("%B", (dict.values(mui[language].months) | list)[(now() + timedelta(days= (page_index))).month-1]) + | replace("%b", (dict.values(mui[language].months_short) | list)[(now() + timedelta(days= (page_index))).month-1]) + ) + }} + continue_on_error: true + - *delay-default + + ##### Display weather data only when available ##### + - variables: + datetime_is_string: '{{ state_attr(weather_entity, "forecast")[0] is defined and state_attr(weather_entity, "forecast")[0]["datetime"] is string }}' + forecast_day: > + {% if datetime_is_string %} + {{ + state_attr(weather_entity, "forecast") | default([]) + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | timestamp_local ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | timestamp_local ) + | list + }} + {% else %} + [ + { + 'datetime': '{{ state_attr(weather_entity, "forecast") | default([]) + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="datetime") | list | first | as_timestamp | timestamp_local + if state_attr(weather_entity, "forecast") | default([]) + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="datetime") | list | count > 0 + else "" }}', + 'condition': '{{ state_attr(weather_entity, "forecast") | default([]) + | selectattr("condition", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="condition") | list | first + if state_attr(weather_entity, "forecast") | default([]) + | selectattr("condition", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="condition") | list | count > 0 + else "" }}', + 'temperature': '{{ state_attr(weather_entity, "forecast") | default([]) + | selectattr("temperature", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="temperature") | list | first + if state_attr(weather_entity, "forecast") | default([]) + | selectattr("temperature", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="temperature") | list | count > 0 + else "" }}', + 'templow': '{{ state_attr(weather_entity, "forecast") | default([]) + | selectattr("templow", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="templow") | list | first + if state_attr(weather_entity, "forecast") | default([]) + | selectattr("templow", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="templow") | list | count > 0 + else "" }}', + 'precipitation': '{{ state_attr(weather_entity, "forecast") | default([]) + | selectattr("precipitation", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="precipitation") | list | first + if state_attr(weather_entity, "forecast") | default([]) + | selectattr("precipitation", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="precipitation") | list | count > 0 + else "" }}', + 'wind_speed': '{{ state_attr(weather_entity, "forecast") | default([]) + | selectattr("wind_speed", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="wind_speed") | list | first + if state_attr(weather_entity, "forecast") | default([]) + | selectattr("wind_speed", "defined") + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) + | map(attribute="wind_speed") | list | count > 0 + else "" }}' + } + ] + {% endif %} + - if: '{{ forecast_day | count > 0 or page_index == 0 }}' + then: # Display forecast + - variables: + metnoweather: '{{ weather_type == "Met.no" }}' + metnoweather_hourly_forecast: '{{ state_attr(weather_entity ~ "_hourly", "forecast") if metnoweather and has_value(weather_entity ~ "_hourly") }}' + forecast_day: > + {% if forecast_day | count > 0 %}{{ forecast_day }} + {% elif metnoweather and metnoweather_hourly_forecast %} + {{ metnoweather_hourly_forecast + | selectattr("datetime", "defined") + | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | timestamp_local ) + | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | timestamp_local ) + | list + }} + {% endif %} + - variables: + forecast_day: > + {% if forecast_day | count > 0 %}{{ forecast_day }} + {% elif page_index == 0 %} + [ + { + 'condition': '{{ states(weather_entity) }}', + 'temperature': '{{ state_attr(weather_entity, "temperature") }}', + 'wind_speed': '{{ state_attr(weather_entity, "wind_speed") }}' + } + ] + {% endif %} + - if: '{{ forecast_day | count > 0 }}' + then: + - variables: + accuweather: '{{ weather_type == "AccuWeather" }}' + accuweather_day_name: '{{ "day_" ~ page_index }}' + accuweather_sensor_prefix: '{{ "sensor." ~ (weather_entity | replace("weather.","")) ~ "_" }}' + accuweather_sensor_sufix: '{{ "_" ~ page_index ~ "d" }}' + temp_min: > + {{ forecast_day | selectattr("templow", "defined") | map(attribute="templow") | map("float") | list | min + if forecast_day | selectattr("templow", "defined") | map(attribute="templow") | map("float") | list | count > 0 + else forecast_day | selectattr("temperature", "defined") | rejectattr("temperature", "eq", "") | map(attribute="temperature") | map("float") | list | min | default("unknown") + }} + temp_max: > + {{ + forecast_day | selectattr("temperature", "defined") | rejectattr("temperature", "eq", "") | map(attribute="temperature") | map("float") | list | max + if forecast_day | selectattr("temperature", "defined") | rejectattr("temperature", "eq", "") | map(attribute="temperature") | map("float") | list | count > 0 + }} + condition: > + {{ + forecast_day | selectattr("condition", "defined") | rejectattr("condition", "eq", "") | map(attribute="condition") | list | first + if forecast_day | selectattr("condition", "defined") | rejectattr("condition", "eq", "") | map(attribute="condition") | list | count > 0 + }} + precipitation: > + {{ + forecast_day | selectattr("precipitation", "defined") | rejectattr("precipitation", "eq", "") | map(attribute="precipitation") | map("float") | list | sum | round(0) + if forecast_day | selectattr("precipitation", "defined") | rejectattr("precipitation", "eq", "") | map(attribute="precipitation") | map("float") | list | count > 0 + }} + precipitation_probability: > + {{ + forecast_day | selectattr("precipitation_probability", "defined") | rejectattr("precipitation_probability", "eq", "") | map(attribute="precipitation_probability") | map("float") | list | max | round(0) + if forecast_day | selectattr("precipitation_probability", "defined") | rejectattr("precipitation_probability", "eq", "") | map(attribute="precipitation_probability") | map("float") | list | count > 0 + }} + pressure: > + {{ + forecast_day | selectattr("pressure", "defined") | rejectattr("pressure", "eq", "") | map(attribute="pressure") | map("float") | list | max | round(0) + if forecast_day | selectattr("pressure", "defined") | rejectattr("pressure", "eq", "") | map(attribute="pressure") | map("float") | list | count > 0 + }} + wind_speed: > + {{ + forecast_day | selectattr("wind_speed", "defined") | rejectattr("wind_speed", "eq", "") | map(attribute="wind_speed") | map("float") | list | max | round(0) + if forecast_day | selectattr("wind_speed", "defined") | rejectattr("wind_speed", "eq", "") | map(attribute="wind_speed") | map("float") | list | count > 0 + }} + hours_of_sun: > + {{ + states(accuweather_sensor_prefix ~ "hours_of_sun" ~ accuweather_sensor_sufix, rounded=true) | default("unknown") + if accuweather + else + ( + forecast_day | selectattr("hours_of_sun", "defined") | rejectattr("hours_of_sun", "eq", "") | map(attribute="hours_of_sun") | map("float") | list | sum | round(0) + if forecast_day | selectattr("hours_of_sun", "defined") | rejectattr("hours_of_sun", "eq", "") | map(attribute="hours_of_sun") | map("float") | list | count > 0 + ) + }} + uv_index: > + {{ + states(accuweather_sensor_prefix ~ "uv_index" ~ accuweather_sensor_sufix, rounded=true) | default("unknown") + if accuweather + else + ( + forecast_day | selectattr("uv_index", "defined") | rejectattr("uv_index", "eq", "") | map(attribute="uv_index") | map("float") | list | max | round(0) + if forecast_day | selectattr("uv_index", "defined") | rejectattr("uv_index", "eq", "") | map(attribute="uv_index") | map("float") | list | count > 0 + ) + }} + thunderstorm_probability: > + {{ + states(accuweather_sensor_prefix ~ "thunderstorm_probability_day" ~ accuweather_sensor_sufix, rounded=true) | default("unknown") + if accuweather + else + ( + forecast_day | selectattr("thunderstorm_probability", "defined") | rejectattr("thunderstorm_probability", "eq", "") | map(attribute="thunderstorm_probability") | map("float") | list | max | round(0) + if forecast_day | selectattr("thunderstorm_probability", "defined") | rejectattr("thunderstorm_probability", "eq", "") | map(attribute="thunderstorm_probability") | map("float") | list | count > 0 + ) + }} + parameters: + - name: hours_of_sun + visibility: '{{ is_number(hours_of_sun) }}' + value: '{{ (hours_of_sun ~ " " ~ weather_units.hours_of_sun) if is_number(hours_of_sun) }}' + icon: '{{ nextion.icon.weather.sun }}' + - name: thunderstorm_probability + visibility: '{{ is_number(thunderstorm_probability) }}' + value: '{{ (thunderstorm_probability ~ weather_units.thunderstorm_probability) if is_number(thunderstorm_probability) }}' + icon: '{{ nextion.icon.weather.lightning }}' + - name: precipitation + visibility: '{{ is_number(precipitation) or is_number(precipitation_probability) }}' + value: > + {{ (precipitation ~ " " ~ weather_units.precipitation) if is_number(precipitation) }} + {{ "-" if is_number(precipitation) and is_number(precipitation_probability) }} + {{ (precipitation_probability ~ weather_units.precipitation_probability) if is_number(precipitation_probability) }} + icon: '{{ nextion.icon.weather.rain }}' + - name: uv_index + visibility: '{{ is_number(uv_index) }}' + value: > + {{ (state_attr(accuweather_sensor_prefix ~ "uv_index" ~ accuweather_sensor_sufix, "level") | default(None) ~ ": ") if weather_type == "AccuWeather" }} + {{ (uv_index ~ weather_units.uv_index) if is_number(uv_index) }} + icon: '{{ nextion.icon.weather.protect }}' + - name: wind_speed + visibility: '{{ is_number(wind_speed) }}' + value: '{{ (wind_speed ~ " " ~ weather_units.wind_speed) if is_number(wind_speed) }}' + icon: '{{ nextion.icon.weather.wind }}' + - name: pressure + visibility: '{{ is_number(pressure) }}' + value: '{{ (pressure ~ " " ~ weather_units.pressure) if is_number(pressure) }}' + icon: '{{ nextion.icon.weather.gauge }}' + + ##### Display weather PIC when available + - if: '{{ condition not in ["unknown", None] }}' + then: + - service: '{{ nextion.command.printf }}' + data: + cmd: > + {{ page_name }}.weather_icon.pic={{ + nextion.pic.weather[states(weather_entity) | default("unavailable") if weather_entity is string else "unavailable"] | default(None) + if condition == "unknown" and page_name == page.weatherpages[0] + else nextion.pic.weather[condition] | default(None) + }} + continue_on_error: true + - *delay-default + + ##### Display temperature min/max when available + - variables: + temperature_string: > + {{ (temp_min | round(0) ~ temperature_units) if is_number(temp_min) }} + {{ "/" if is_number(temp_min) and is_number(temp_max) and temp_min != temp_max }} + {{ (temp_max | round(0) ~ temperature_units) if is_number(temp_max) and temp_min != temp_max }} + - if: '{{ (is_number(temp_min) or is_number(temp_max)) and temperature_string is string and temperature_string | length > 0 }}' + then: + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ page_name }}.temperature' ### Temperature MIN/MAX ### + message: '{{ temperature_string }}' + continue_on_error: true + - *delay-default + + ##### fields 1 to 5 (Parameters) ##### + - repeat: + for_each: '{{ (parameters | selectattr("visibility", "eq", true) | list)[:5] }}' + sequence: + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ page_name }}.value0{{ repeat.index }}' + message: '{{ repeat.item.value }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ page_name }}.value0{{ repeat.index }}_icon' + message: '{{ repeat.item.icon }}' + continue_on_error: true + - *delay-default + else: &forecast_unavailable + - service: '{{ nextion.command.text_printf }}' + data: + component: '{{ page_name }}.value01' + message: '{{ mui[language].unavailable }}' + continue_on_error: true + - *delay-default + else: *forecast_unavailable + + ## PAGE NOTIFICATION ## + - alias: Notification page + conditions: + - '{{ nspanel_event.page == page.notification }}' + - '{{ confirmation_message is string and states(confirmation_message) | default("unavailable") != "on" }}' + sequence: + - service: '{{ nextion.command.text_printf }}' + data: + component: notification.notifi_text01 + message: '{{ states(notification_text) | default(mui[language].unavailable) if notification_text is string else mui[language].unavailable }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: notification.notifi_label + message: '{{ states(notification_label) | default(mui[language].unavailable) if notification_label is string else mui[language].unavailable }}' + continue_on_error: true + + ## PAGE QR Code ## + - alias: QRCode page + conditions: '{{ nspanel_event.page == page.qrcode }}' + sequence: + - variables: + qrcode_label: !input 'qrcode_label' + qrcode_value: !input 'qrcode_value' + - if: '{{ qrcode_label | length > 0 }}' + then: + - service: '{{ nextion.command.text_printf }}' + data: + component: 'qrcode_label' + message: '{{ qrcode_label }}' + continue_on_error: true + - *delay-default + - service: '{{ nextion.command.text_printf }}' + data: + component: 'qrcode_value' + message: '{{ qrcode_value }}' + continue_on_error: true ##### NSPanel event ##### - alias: NSPanel Event @@ -6158,2021 +8222,7 @@ action: sequence: - alias: NSPanel Event component changed choose: - - alias: Page changed - conditions: '{{ nspanel_event.component == "currentpage" }}' - sequence: - - choose: - ## PAGE HOME ## - - alias: Home page - conditions: '{{ nspanel_event.page == page.home }}' - sequence: &refresh_page_home - ##### Set entity variable ##### - - &set_entity_variable - service: '{{ nextion.command.text_printf }}' - data: - component: home.entity - message: '{{ climate }}' - continue_on_error: true - - ##### Weather Icon Home Page ##### - - *delay-default - - &refresh-page_home-weather_pic - service: '{{ nextion.command.printf }}' - data: - cmd: home.weather.pic={{ nextion.pic.weather[states(weather_entity) | default("unavailable") if weather_entity is string and has_value(weather_entity) else "unavailable"] | default(None) }} - continue_on_error: true - - - &refresh-page_home-outdoor_temp - if: '{{ true }}' - then: - ##### NSPanel Outdoor Temp ##### - - variables: - outdoor_temp_state: > - {{ - states(outdoortemp, rounded=true) | default("unavailable") - if outdoortemp is string and outdoortemp is match "sensor." and has_value(outdoortemp) - else "unavailable" - }} - outdoor_temp: > - {{ - outdoor_temp_state if is_number(outdoor_temp_state) - else state_attr(weather_entity, "temperature") | default("unavailable") - if weather_entity is string else "unavailable" - }} - - if: '{{ is_number(outdoor_temp) }}' - then: - ### LABEL Outdoor Temp Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.outdoor_temp - message: > - {{ - page_home.general.outdoor_temp.label.color_rgb - if is_number(page_home.general.outdoor_temp.label.color_rgb) - else ((page_home.general.outdoor_temp.label.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.outdoor_temp.label.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.outdoor_temp.label.color_rgb[2] //(2**3)) - }} - continue_on_error: true - ### LABEL Outdoor Temp Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: home.outdoor_temp - message: '{{ outdoor_temp | round(1) ~ temperature_units }}' - continue_on_error: true - - - &refresh-page_home-indoor_temp - if: '{{ true }}' - then: - ##### NSPanel Indoor Temp ##### - - variables: - indoor_temp_state: '{{ states(indoortemp, rounded=true) | default("unavailable") if indoortemp is string and indoortemp is match "sensor." else states(nspaneltemp, rounded=true) }}' - indoor_temp_units: > - {{ - state_attr(indoortemp, "unit_of_measurement") | default(temperature_units) - if - indoortemp is string and - indoortemp is match "sensor." and - state_attr(indoortemp, "unit_of_measurement") is string and - state_attr(indoortemp, "unit_of_measurement") | length > 0 - else - ( - state_attr(nspaneltemp, "unit_of_measurement") | default(temperature_units) - if - state_attr(nspaneltemp, "unit_of_measurement") is string and - state_attr(nspaneltemp, "unit_of_measurement") | length > 0 - else temperature_units - ) - }} - - if: '{{ is_number(indoor_temp_state) }}' - then: - ### ICON Indoor Temp Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.indoortempicon - message: > - {{ - page_home.general.indoor_temp.icon.color_rgb - if is_number(page_home.general.indoor_temp.icon.color_rgb) - else ((page_home.general.indoor_temp.icon.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.indoor_temp.icon.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.indoor_temp.icon.color_rgb[2] //(2**3)) - }} - continue_on_error: true - ### ICON Indoor Temp Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: home.indoortempicon - message: > - {{ - all_icons[page_home.general.indoor_temp.icon.icon.split(":")[1]] | default(all_icons.unknown) - if page_home.general.indoor_temp.icon.icon.split(":") | count > 0 - else - ( - page_home.general.indoor_temp.icon.icon - if page_home.general.indoor_temp.icon.icon is string - else all_icons.unknown - ) - }} - continue_on_error: true - ### LABEL Indoor Temp Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.current_temp - message: > - {{ - page_home.general.indoor_temp.label.color_rgb - if is_number(page_home.general.indoor_temp.label.color_rgb) - else ((page_home.general.indoor_temp.label.color_rgb[0] //(2**3)) *(2**11))+((page_home.general.indoor_temp.label.color_rgb[1] //(2**2)) *(2**5))+(page_home.general.indoor_temp.label.color_rgb[2] //(2**3)) - }} - continue_on_error: true - ### LABEL Indoor Temp Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: home.current_temp - message: '{{ indoor_temp_state | round(1) ~ indoor_temp_units }}' - continue_on_error: true - - ##### NSPanel Buttons ##### - - variables: - left_button_state: '{{ states(page_home.hardware.buttons.left.entity) | default("unavailable") if page_home.hardware.buttons.left.entity is string else "unavailable" }}' - right_button_state: '{{ states(page_home.hardware.buttons.right.entity) | default("unavailable") if page_home.hardware.buttons.right.entity is string else "unavailable" }}' - - ##### NSPanel Left Button Name ##### - - if: '{{ page_home.hardware.buttons.left.name | length > 0 }}' - then: - ### LABEL Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.left_bt_text - message: > - {{ - page_home.hardware.buttons.left.color_rgb - if is_number(page_home.hardware.buttons.left.color_rgb) - else ((page_home.hardware.buttons.left.color_rgb[0] //(2**3)) *(2**11))+((page_home.hardware.buttons.left.color_rgb[1] //(2**2)) *(2**5))+(page_home.hardware.buttons.left.color_rgb[2] //(2**3)) - }} - continue_on_error: true - ### LABEL Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: home.left_bt_text - message: '{{ page_home.hardware.buttons.left.name }}' - continue_on_error: true - - ##### SET Left Hardware Button PIC on Home Page #### - - if: '{{ left_button_state not in ["unavailable", "unknown", "", None] }}' - then: - - variables: - # Hardware Button PIC - left_hardware_button_state: '{{ nextion.pic.hardware.button[left_button_state] | default(nextion.pic.hardware.button.off) }}' - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: home.left_bt_pic.pic={{ left_hardware_button_state }} - continue_on_error: true - - ##### NSPanel Right Button Name ##### - - if: '{{ page_home.hardware.buttons.right.name | length > 0 }}' - then: - ### LABEL Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.right_bt_text - message: > - {{ - page_home.hardware.buttons.right.color_rgb - if is_number(page_home.hardware.buttons.right.color_rgb) - else ((page_home.hardware.buttons.right.color_rgb[0] //(2**3)) *(2**11))+((page_home.hardware.buttons.right.color_rgb[1] //(2**2)) *(2**5))+(page_home.hardware.buttons.right.color_rgb[2] //(2**3)) - }} - continue_on_error: true - ### LABEL Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: home.right_bt_text - message: '{{ page_home.hardware.buttons.right.name }}' - continue_on_error: true - - ##### SET Right Hardware Button PIC on Home Page ##### - - if: '{{ right_button_state not in ["unavailable", "unknown", "", None] }}' - then: - - variables: - # Hardware Button PIC - right_hardware_button_state: '{{ nextion.pic.hardware.button[right_button_state] | default(nextion.pic.hardware.button.off) }}' - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: home.right_bt_pic.pic={{ right_hardware_button_state }} - continue_on_error: true - - ###### Status bar ###### - - &variables-home_page_status_bar - variables: - climate_state: '{{ states(climate) | default("unavailable") if climate is string else "unavailable" }}' - hvac_action: '{{ state_attr(climate, "hvac_action") | default("unavailable") if climate is string else "unavailable" }}' - climate_action: '{{ hvac_action if hvac_action not in ["unavailable", "unknown", "", None] else climate_state }}' - home_page_status_bar: - - entity: '{{ relay01_entity }}' - icon: !input 'relay01_icon' #E3A5 - icon_color_rgb: !input 'relay01_icon_color' - page: home - component: icon_top_01 - - entity: '{{ relay02_entity }}' - icon: !input 'relay02_icon' #E3A8 - icon_color_rgb: !input 'relay02_icon_color' - page: home - component: icon_top_02 - - entity: '{{ climate }}' - icon: > - {% if "off" in climate_action %}{{ all_icons.blank }} - {% elif "heating" in climate_action or "heat" in climate_action %}{{ all_icons["thermometer-lines"] }} - {% elif "cooling" in climate_action or "cool" in climate_action %}{{ all_icons.snowflake }} - {% elif "drying" in climate_action or "dry" in climate_action %}{{ all_icons["water-percent"] }} - {% elif "fan" in climate_action or "fan_only" in climate_action %}{{ all_icons.fan }} - {% elif "heat_cool" in climate_action %}{{ all_icons.autorenew }} - {% elif "auto" in climate_action %}{{ all_icons["calendar-sync"] }} - {% elif "idle" in climate_action %}{{ all_icons.thermometer }} - {% else %}{{ all_icons.blank }} - {% endif %} - icon_color_rgb: > - {% if "off" in climate_action %}{{ nextion.color["off"] }} - {% elif "heating" in climate_action or "heat" in climate_action %}{{ nextion.color["deep-orange"]}} - {% elif "cooling" in climate_action or "cool" in climate_action %}{{ nextion.color["blue"] }} - {% elif "drying" in climate_action or "dry" in climate_action %}{{ nextion.color["orange"] }} - {% elif "fan" in climate_action or "fan_only" in climate_action %}{{ nextion.color["cyan"] }} - {% elif "heat_cool" in climate_action %}{{ nextion.color["amber"] }} - {% elif "auto" in climate_action %}{{ nextion.color["green"] }} - {% elif "idle" in climate_action %}{{ nextion.color["off"] }} - {% else %}{{ nextion.color["off"] }} - {% endif %} - page: home - component: icon_top_03 - - entity: !input 'chip01' - icon: !input 'chip01_icon' - icon_color_rgb: !input 'chip01_icon_color' - page: home - component: icon_top_04 - - entity: !input 'chip02' - icon: !input 'chip02_icon' - icon_color_rgb: !input 'chip02_icon_color' - page: home - component: icon_top_05 - - entity: !input 'chip03' - icon: !input 'chip03_icon' - icon_color_rgb: !input 'chip03_icon_color' - page: home - component: icon_top_06 - - entity: !input 'chip04' - icon: !input 'chip04_icon' - icon_color_rgb: !input 'chip04_icon_color' - page: home - component: icon_top_07 - - entity: !input 'chip05' - icon: !input 'chip05_icon' - icon_color_rgb: !input 'chip05_icon_color' - page: home - component: icon_top_08 - - entity: !input 'chip06' - icon: !input 'chip06_icon' - icon_color_rgb: !input 'chip06_icon_color' - page: home - component: icon_top_09 - - entity: !input 'chip07' - icon: !input 'chip07_icon' - icon_color_rgb: !input 'chip07_icon_color' - page: home - component: icon_top_10 - - repeat: - for_each: '{{ home_page_status_bar }}' - sequence: - - &display-home_page_status_bar - if: '{{ repeat.item.entity is defined and repeat.item.entity is string and repeat.item.entity | length > 0 }}' - then: - - variables: - repeat_item_state: '{{ states(repeat.item.entity) | default("unavailable") }}' - repeat_item_state_is_on: > - {{ - (repeat.item.component == "icon_top_03") or - (repeat_item_state is string and repeat_item_state in ["on", "open", "opening", "true", "True"]) or - (repeat_item_state is boolean and repeat_item_state) - }} - repeat_item_icon: > - {% if repeat_item_state_is_on and repeat.item.icon is string and repeat.item.icon | length > 0 %} - {{ all_icons[repeat.item.icon.split(":")[1]] | default(repeat.item.icon) }} - {% elif repeat_item_state_is_on and state_attr(repeat.item.entity, "icon") | default("") not in ["unavailable", "unknown", "", None] %} - {{ all_icons[state_attr(repeat.item.entity, "icon").split(":")[1]] | default(all_icons.blank) }} - {% else %} - {{ all_icons.blank }} - {% endif %} - - if: '{{ repeat_item_state_is_on }}' - then: - ### ICON Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: '{{ repeat.item.page }}.{{ repeat.item.component }}' - message: > - {{ - repeat.item.icon_color_rgb - if is_number(repeat.item.icon_color_rgb) - else - ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ - ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ - (repeat.item.icon_color_rgb[2] //(2**3)) - }} - continue_on_error: true - ### ICON Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ repeat.item.page }}.{{ repeat.item.component }}' - message: '{{ repeat_item_icon }}' - continue_on_error: true - # {{ is_state(repeat.item.entity, "on") | default(False) if repeat.item.entity is string else "unavailable" }} - - ##### HOME VALUE 01 - 03 - - *delay-default - - &variables-home_page_values - variables: - home_page_values: - - entity: !input 'home_value01' - icon: !input 'home_value01_icon' - icon_color_rgb: !input 'home_value01_icon_color' - label_color_rgb: !input 'home_value01_label_color' - page: home - component: value01 - - entity: !input 'home_value02' - icon: !input 'home_value02_icon' - icon_color_rgb: !input 'home_value02_icon_color' - label_color_rgb: !input 'home_value02_label_color' - page: home - component: value02 - - entity: !input 'home_value03' - icon: !input 'home_value03_icon' - icon_color_rgb: !input 'home_value03_icon_color' - label_color_rgb: !input 'home_value03_label_color' - page: home - component: value03 - - repeat: - for_each: '{{ home_page_values }}' - sequence: - - &display_value - if: '{{ repeat.item.entity is string and repeat.item.entity is match "sensor." and has_value(repeat.item.entity) }}' - then: - - if: '{{ repeat.item.icon | length > 0 }}' - then: - ### ICON Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: '{{ repeat.item.page }}.{{ repeat.item.component }}_icon' - message: > - {{ - repeat.item.icon_color_rgb - if is_number(repeat.item.icon_color_rgb) - else - ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ - ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ - (repeat.item.icon_color_rgb[2] //(2**3)) - }} - continue_on_error: true - ### ICON Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ repeat.item.page }}.{{ repeat.item.component }}_icon' - message: > - {% if repeat.item.icon is string %} - {{ - all_icons[repeat.item.icon.split(":")[1]] | default(all_icons.unknown) - if repeat.item.icon.split(":") | count > 0 - else repeat.item.icon - }} - {% else %}{{ all_icons.unknown }} - {% endif %} - continue_on_error: true - - variables: - repeat_item_state: '{{ states(repeat.item.entity, rounded=true) | default("unavailable") }}' - repeat_item_state_available: '{{ repeat_item_state not in ["unavailable", "unknown", "", None] }}' - - condition: '{{ repeat_item_state_available }}' - ### LABEL Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: '{{ repeat.item.page }}.{{ repeat.item.component }}{{ "_state" if repeat.item.page == page.home }}' - message: > - {{ - repeat.item.label_color_rgb - if is_number(repeat.item.label_color_rgb) - else - ((repeat.item.label_color_rgb[0] //(2**3)) *(2**11))+ - ((repeat.item.label_color_rgb[1] //(2**2)) *(2**5))+ - (repeat.item.label_color_rgb[2] //(2**3)) - }} - continue_on_error: true - ### LABEL Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ repeat.item.page }}.{{ repeat.item.component }}{{ "_state" if repeat.item.page == page.home }}' - message: > - {{ - (repeat_item_state ~ (state_attr(repeat.item.entity, "unit_of_measurement") | default("") if state_attr(repeat.item.entity, "unit_of_measurement") is string else "")) - if is_number(repeat_item_state) - else repeat_item_state - }} - continue_on_error: true - - ##### Set notify icon ##### - - &refresh-page_home-notifications_icon - if: '{{ true }}' - then: - - variables: - notification_unread_state: '{{ states(notification_unread) | default("unavailable") if notification_unread is string else "unavailable" }}' - - condition: '{{ notification_unread_state in ["on", "off"] }}' - - variables: - notification_text_state: '{{ states(notification_text) | default(None) if notification_text is string else None }}' - set_button04_icon: > - {{ - all_icons[page_home.buttons[3].icon.split(":")[1]] | default(page_home.buttons[3].icon if page_home.buttons[3].icon is string else all_icons.unknown) - if notification_unread_state == "on" and notification_text_state | length > 0 - else all_icons.blank - }} - set_button04_icon_font: > - {{ - ( - page_home.buttons[3].color_rgb[notification_unread_state] - if is_number(page_home.buttons[3].color_rgb[notification_unread_state]) - else - ((page_home.buttons[3].color_rgb[notification_unread_state][0] //(2**3)) *(2**11))+ - ((page_home.buttons[3].color_rgb[notification_unread_state][1] //(2**2)) *(2**5))+ - (page_home.buttons[3].color_rgb[notification_unread_state][2] //(2**3)) - ) - if notification_unread_state in ["on", "off"] and notification_text_state | length > 0 - else nextion.color.grey_light - }} - ##### SET ICON Font - Notify ##### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: home.button04_icon - message: '{{ set_button04_icon }}' - continue_on_error: true - - ##### SET ICON Font Color - Notify ##### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.button04_icon - message: '{{ set_button04_icon_font }}' - continue_on_error: true - - ###### QR Code - Icon ###### - - *delay-default - - if: '{{ qrcode_enabled == true }}' - then: # Display QR code icon - ### ICON Font Color ### - - service: '{{ nextion.command.font_color }}' - data: - component: home.button05_icon - message: > - {{ - page_home.buttons[4].color_rgb.on - if is_number(page_home.buttons[4].color_rgb.on) - else - ((page_home.buttons[4].color_rgb.on[0] //(2**3)) *(2**11))+ - ((page_home.buttons[4].color_rgb.on[1] //(2**2)) *(2**5))+ - (page_home.buttons[4].color_rgb.on[2] //(2**3)) - }} - continue_on_error: true - ### ICON Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: home.button05_icon - message: > - {{ - all_icons[page_home.buttons[4].icon.split(":")[1]] | default(page_home.buttons[4].icon - if page_home.buttons[4].icon is string - else all_icons.unknown) - }} - continue_on_error: true - else: # Display blank icon - - service: '{{ nextion.command.text_printf }}' - data: - component: home.button05_icon - message: '{{ all_icons.blank }}' - continue_on_error: true - - ###### ENTITIES - Icon ###### - - *delay-default - - if: '{{ entitypages_enabled }}' - then: # Display entities icon - ### ICON Font Color ### - - service: '{{ nextion.command.font_color }}' - data: - component: home.button06_icon - message: > - {{ - page_home.buttons[5].color_rgb.on - if is_number(page_home.buttons[5].color_rgb.on) - else - ((page_home.buttons[5].color_rgb.on[0] //(2**3)) *(2**11))+ - ((page_home.buttons[5].color_rgb.on[1] //(2**2)) *(2**5))+ - (page_home.buttons[5].color_rgb.on[2] //(2**3)) - }} - continue_on_error: true - ### ICON Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: home.button06_icon - message: > - {{ - all_icons[page_home.buttons[5].icon.split(":")[1]] | default(page_home.buttons[5].icon - if page_home.buttons[5].icon is string - else all_icons.unknown) - }} - continue_on_error: true - else: # Display blank icon - - service: '{{ nextion.command.text_printf }}' - data: - component: home.button06_icon - message: '{{ all_icons.blank }}' - continue_on_error: true - - ## BUTTON PAGES 01 - 04 ## - - alias: Button pages - conditions: '{{ nspanel_event.page in page.buttonpages }}' - sequence: &refresh_page_buttonpage - - &variables-page_buttons - variables: - button_page_index: '{{ (nspanel_event.page[-2:] | int(-1)) - 1 }}' - first_button: '{{ button_page_index * 8 }}' - last_button: '{{ first_button + 8 }}' - ##### BUTTON Page Labels ##### - button_pages_labels: - - label: !input button_page01_label - - label: !input button_page02_label - - label: !input button_page03_label - - label: !input button_page04_label - ##### BUTTONS Page - Buttons 1 - 32 ##### - button_pages_buttons: - - entity: !input 'entity01' - name: !input 'entity01_name' - icon: !input 'entity01_icon' - icon_color_rgb: !input 'entity01_icon_color' - confirm: !input 'entity01_confirm' - page: buttonpage01 - component: button01 - - entity: !input 'entity02' - name: !input 'entity02_name' - icon: !input 'entity02_icon' - icon_color_rgb: !input 'entity02_icon_color' - confirm: !input 'entity02_confirm' - page: buttonpage01 - component: button02 - - entity: !input 'entity03' - name: !input 'entity03_name' - icon: !input 'entity03_icon' - icon_color_rgb: !input 'entity03_icon_color' - confirm: !input 'entity03_confirm' - page: buttonpage01 - component: button03 - - entity: !input 'entity04' - name: !input 'entity04_name' - icon: !input 'entity04_icon' - icon_color_rgb: !input 'entity04_icon_color' - confirm: !input 'entity04_confirm' - page: buttonpage01 - component: button04 - - entity: !input 'entity05' - name: !input 'entity05_name' - icon: !input 'entity05_icon' - icon_color_rgb: !input 'entity05_icon_color' - confirm: !input 'entity05_confirm' - page: buttonpage01 - component: button05 - - entity: !input 'entity06' - name: !input 'entity06_name' - icon: !input 'entity06_icon' - icon_color_rgb: !input 'entity06_icon_color' - confirm: !input 'entity06_confirm' - page: buttonpage01 - component: button06 - - entity: !input 'entity07' - name: !input 'entity07_name' - icon: !input 'entity07_icon' - icon_color_rgb: !input 'entity07_icon_color' - confirm: !input 'entity07_confirm' - page: buttonpage01 - component: button07 - - entity: !input 'entity08' - name: !input 'entity08_name' - icon: !input 'entity08_icon' - icon_color_rgb: !input 'entity08_icon_color' - confirm: !input 'entity08_confirm' - page: buttonpage01 - component: button08 - - entity: !input 'entity09' - name: !input 'entity09_name' - icon: !input 'entity09_icon' - icon_color_rgb: !input 'entity09_icon_color' - confirm: !input 'entity09_confirm' - page: buttonpage02 - component: button01 - - entity: !input 'entity10' - name: !input 'entity10_name' - icon: !input 'entity10_icon' - icon_color_rgb: !input 'entity10_icon_color' - confirm: !input 'entity10_confirm' - page: buttonpage02 - component: button02 - - entity: !input 'entity11' - name: !input 'entity11_name' - icon: !input 'entity11_icon' - icon_color_rgb: !input 'entity11_icon_color' - confirm: !input 'entity11_confirm' - page: buttonpage02 - component: button03 - - entity: !input 'entity12' - name: !input 'entity12_name' - icon: !input 'entity12_icon' - icon_color_rgb: !input 'entity12_icon_color' - confirm: !input 'entity12_confirm' - page: buttonpage02 - component: button04 - - entity: !input 'entity13' - name: !input 'entity13_name' - icon: !input 'entity13_icon' - icon_color_rgb: !input 'entity13_icon_color' - confirm: !input 'entity13_confirm' - page: buttonpage02 - component: button05 - - entity: !input 'entity14' - name: !input 'entity14_name' - icon: !input 'entity14_icon' - icon_color_rgb: !input 'entity14_icon_color' - confirm: !input 'entity14_confirm' - page: buttonpage02 - component: button06 - - entity: !input 'entity15' - name: !input 'entity15_name' - icon: !input 'entity15_icon' - icon_color_rgb: !input 'entity15_icon_color' - confirm: !input 'entity15_confirm' - page: buttonpage02 - component: button07 - - entity: !input 'entity16' - name: !input 'entity16_name' - icon: !input 'entity16_icon' - icon_color_rgb: !input 'entity16_icon_color' - confirm: !input 'entity16_confirm' - page: buttonpage02 - component: button08 - - entity: !input 'entity17' - name: !input 'entity17_name' - icon: !input 'entity17_icon' - icon_color_rgb: !input 'entity17_icon_color' - confirm: !input 'entity17_confirm' - page: buttonpage03 - component: button01 - - entity: !input 'entity18' - name: !input 'entity18_name' - icon: !input 'entity18_icon' - icon_color_rgb: !input 'entity18_icon_color' - confirm: !input 'entity18_confirm' - page: buttonpage03 - component: button02 - - entity: !input 'entity19' - name: !input 'entity19_name' - icon: !input 'entity19_icon' - icon_color_rgb: !input 'entity19_icon_color' - confirm: !input 'entity19_confirm' - page: buttonpage03 - component: button03 - - entity: !input 'entity20' - name: !input 'entity20_name' - icon: !input 'entity20_icon' - icon_color_rgb: !input 'entity20_icon_color' - confirm: !input 'entity20_confirm' - page: buttonpage03 - component: button04 - - entity: !input 'entity21' - name: !input 'entity21_name' - icon: !input 'entity21_icon' - icon_color_rgb: !input 'entity21_icon_color' - confirm: !input 'entity21_confirm' - page: buttonpage03 - component: button05 - - entity: !input 'entity22' - name: !input 'entity22_name' - icon: !input 'entity22_icon' - icon_color_rgb: !input 'entity22_icon_color' - confirm: !input 'entity22_confirm' - page: buttonpage03 - component: button06 - - entity: !input 'entity23' - name: !input 'entity23_name' - icon: !input 'entity23_icon' - icon_color_rgb: !input 'entity23_icon_color' - confirm: !input 'entity23_confirm' - page: buttonpage03 - component: button07 - - entity: !input 'entity24' - name: !input 'entity24_name' - icon: !input 'entity24_icon' - icon_color_rgb: !input 'entity24_icon_color' - confirm: !input 'entity24_confirm' - page: buttonpage03 - component: button08 - - entity: !input 'entity25' - name: !input 'entity25_name' - icon: !input 'entity25_icon' - icon_color_rgb: !input 'entity25_icon_color' - confirm: !input 'entity25_confirm' - page: buttonpage04 - component: button01 - - entity: !input 'entity26' - name: !input 'entity26_name' - icon: !input 'entity26_icon' - icon_color_rgb: !input 'entity26_icon_color' - confirm: !input 'entity26_confirm' - page: buttonpage04 - component: button02 - - entity: !input 'entity27' - name: !input 'entity27_name' - icon: !input 'entity27_icon' - icon_color_rgb: !input 'entity27_icon_color' - confirm: !input 'entity27_confirm' - page: buttonpage04 - component: button03 - - entity: !input 'entity28' - name: !input 'entity28_name' - icon: !input 'entity28_icon' - icon_color_rgb: !input 'entity28_icon_color' - confirm: !input 'entity28_confirm' - page: buttonpage04 - component: button04 - - entity: !input 'entity29' - name: !input 'entity29_name' - icon: !input 'entity29_icon' - icon_color_rgb: !input 'entity29_icon_color' - confirm: !input 'entity29_confirm' - page: buttonpage04 - component: button05 - - entity: !input 'entity30' - name: !input 'entity30_name' - icon: !input 'entity30_icon' - icon_color_rgb: !input 'entity30_icon_color' - confirm: !input 'entity30_confirm' - page: buttonpage04 - component: button06 - - entity: !input 'entity31' - name: !input 'entity31_name' - icon: !input 'entity31_icon' - icon_color_rgb: !input 'entity31_icon_color' - confirm: !input 'entity31_confirm' - page: buttonpage04 - component: button07 - - entity: !input 'entity32' - name: !input 'entity32_name' - icon: !input 'entity32_icon' - icon_color_rgb: !input 'entity32_icon_color' - confirm: !input 'entity32_confirm' - page: buttonpage04 - component: button08 - - if: '{{ button_page_index >= 0 and button_page_index <= 3 }}' - then: - ##### Button Page Label ##### - - if: '{{ button_pages_labels[button_page_index].label | length > 0 }}' - then: - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ "bpage%02d_label" | format(button_page_index+1) }}' - message: '{{ button_pages_labels[button_page_index].label }}' - continue_on_error: true - - ###### Display page while other elements are still loading ##### - - variables: - show_while_loading: !input 'show_while_loading' - - if: '{{ show_while_loading }}' - then: - - *delay-default - - service: '{{ nextion.command.show_all }}' - continue_on_error: true - - ##### NSPanel build Button page ##### - - repeat: - for_each: '{{ button_pages_buttons[first_button:last_button] }}' - sequence: &display-button_page_button - - if: > - {{ - repeat.item.entity is string and - repeat.item.entity | length > 0 and - repeat.item.entity.split(".") | default([]) | count > 0 - }} - then: - - variables: - item_domain: '{{ repeat.item.entity.split(".")[0] | default("unknown") }}' - current_entity_state: '{{ states(repeat.item.entity) | default("unavailable") }}' - current_entity_state_available: '{{ current_entity_state not in ["unavailable"] }}' - # Button PIC GRAY/WHITE - btn_pic: > - {{ - nextion.pic.button.on - if current_entity_state in ["on", "open", "opening", "home"] - or (item_domain == "climate" and current_entity_state != "off") - or (item_domain in ["button","input_button","scene"] and trigger.id is match "current_state_entity") - else nextion.pic.button.off - }} - # TEXT, BRIGHTNESS and ICON Background - btn_bg: > - {{ - nextion.color.white - if current_entity_state in ["on", "open", "opening", "home"] - or (item_domain == "climate" and current_entity_state != "off") - or (item_domain in ["button","input_button","scene"] and trigger.id is match "current_state_entity") - else nextion.color.grey_dark - }} - # ICON Font Color - btn_icon_font: > - {% if not current_entity_state_available %}{{ nextion.color.red }} - {% elif current_entity_state in ["off", "closed", "closing"] or (item_domain == "person" and current_entity_state != "home") %} - {{ nextion.color.grey_light }} - {% elif item_domain in ["button", "input_button", "scene"] and trigger.id is match "current_state_entity" %} - {{ - repeat.item.icon_color_rgb - if is_number(repeat.item.icon_color_rgb) - else - ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ - ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ - (repeat.item.icon_color_rgb[2] //(2**3)) - }} - {% elif item_domain in ["button", "input_button", "scene"] %}{{ nextion.color.grey_light }} - {% elif current_entity_state in ["on", "open", "opening", "home"] or (item_domain == "climate" and current_entity_state != "off") %} - {{ - repeat.item.icon_color_rgb - if is_number(repeat.item.icon_color_rgb) - else - ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ - ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ - (repeat.item.icon_color_rgb[2] //(2**3)) - }} - {% else %}{{ nextion.color.red }} - {% endif %} - # LABEL Font Color - btn_txt_font: >- - {% if not current_entity_state_available %}{{ nextion.color.white }} - {% elif current_entity_state in ["off", "closed", "closing"] or (item_domain == "person" and current_entity_state != "home") %} - {{ nextion.color.white }} - {% elif item_domain in ["button", "input_button", "scene"] and trigger.id is match "current_state_entity" %} - {{ nextion.color.grey_dark }} - {% elif item_domain in ["button", "input_button", "scene"] %} - {{ nextion.color.white }} - {% elif current_entity_state in ["on", "open", "opening", "home"] or (item_domain == "climate" and current_entity_state != "off") %} - {{ nextion.color.grey_dark }} - {% else %} - {{ nextion.color.white }} - {% endif %} - # BRIGHTNESS Font Color - btn_bri_font: '{{ btn_txt_font }}' #'{{ nextion.color.grey_dark }}' - # ICON Value - btn_icon: > - {% if not current_entity_state_available %}{{ nextion.icon.domain.unknown }} - {% elif repeat.item.icon | length > 0 %} - {{ - all_icons[repeat.item.icon.split(":")[1]] | default(all_icons.unknown) - if repeat.item.icon.split(":") | count > 0 - else repeat.item.icon - }} - {% elif repeat.item.entity and repeat.item.entity.split(".") | count > 1 %} - {{ nextion.icon.domain[repeat.item.entity.split(".")[0] if repeat.item.entity else "unknown"] }} - {% else %}{{ nextion.icon.domain.unknown }} - {% endif %} - # LABEL Value - btn_label: '{{ repeat.item.name }}' - # BRIGHTNESS Value - btn_bri_txt: >- - {% if not current_entity_state_available %} 0 - {% elif item_domain == "light" and current_entity_state == "on" and state_attr(repeat.item.entity, "brightness") != None %} - {{ (state_attr(repeat.item.entity, "brightness") | int * 100 /255) | round(0) }}% - {% elif item_domain == "fan" and current_entity_state == "on" and state_attr(repeat.item.entity, "percentage") != None %} - {{ state_attr(repeat.item.entity, "percentage") | round(0, default=0) }}% - {% elif item_domain == "cover" and current_entity_state in ["open", "opening", "closing"] and state_attr(repeat.item.entity, "current_position") != None %} - {{ (state_attr(repeat.item.entity, "current_position") | int(100)) | round(0) }}% - {% elif item_domain == "climate" and current_entity_state != "off" and state_attr(repeat.item.entity, 'current_temperature') != None %} - {{ (state_attr(repeat.item.entity, 'current_temperature') | float) | round(0) }}{{ temperature_units }} - {% else -%} 0 - {% endif -%} - - *delay-default - - service: '{{ nextion.command.set_button }}' - data: - btn_id: '{{ repeat.item.page }}.{{ repeat.item.component }}' - btn_pic: '{{ btn_pic }}' - btn_bg: '{{ btn_bg }}' - btn_icon_font: '{{ btn_icon_font }}' - btn_txt_font: '{{ btn_txt_font }}' - btn_bri_font: '{{ btn_bri_font }}' - btn_icon: '{{ btn_icon }}' - btn_label: '{{ btn_label }}' - btn_bri_txt: '{{ btn_bri_txt }}' - continue_on_error: true - - if: '{{ item_domain in ["button","input_button","scene"] and trigger.id is match "current_state_entity" }}' - then: - - delay: - milliseconds: 800 - - service: '{{ nextion.command.set_button }}' - data: - btn_id: '{{ repeat.item.page }}.{{ repeat.item.component }}' - btn_pic: '{{ nextion.pic.button.off }}' - btn_bg: '{{ nextion.color.grey_dark }}' - btn_icon_font: > - {{ - repeat.item.icon_color_rgb - if is_number(repeat.item.icon_color_rgb) - else - ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+ - ((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+ - (repeat.item.icon_color_rgb[2] //(2**3)) - }} - btn_txt_font: '{{ nextion.color.white }}' - btn_bri_font: '{{ btn_bri_font }}' - btn_icon: '{{ btn_icon }}' - btn_label: '{{ btn_label }}' - btn_bri_txt: '{{ btn_bri_txt }}' - continue_on_error: true - ###### SHOW All component when page loading done ##### - - if: '{{ not show_while_loading }}' - then: - - *delay-default - - service: '{{ nextion.command.show_all }}' - continue_on_error: true - else: - ###### Show empty page ##### - - *delay-default - - service: '{{ nextion.command.show_all }}' - continue_on_error: true - - ## PAGE LIGHT ## - - alias: Light settings page - conditions: '{{ nspanel_event.page == page.light }}' - sequence: &refresh_page_light - - variables: - supported_color_modes: '{{ state_attr(nspanel_event.entity, "supported_color_modes") | default("unknown") }}' - color_mode_color: > - {{ - "hs" in supported_color_modes - or "xy" in supported_color_modes - or "rgb" in supported_color_modes - or "rgbw" in supported_color_modes - or "rgbww" in supported_color_modes - }} - color_mode_color_temp: '{{ "color_temp" in supported_color_modes }}' - #color_mode_brightness: > - # {{ - # "brightness" in supported_color_modes - # or "white" in supported_color_modes - # or color_mode_color - # or color_mode_color_temp - # }} - ##### LIGHT State ##### - - variables: - curr_brightness: '{{ (state_attr(nspanel_event.entity, "brightness") | int(0) * 100 / 255) | round(0) }}' - - *delay-default - - service: '{{ nextion.command.value }}' - data: - component: light.lightslider - message: '{{ curr_brightness }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: light.light_value - message: '{{ curr_brightness }}%' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: light.light_value_2 - message: '{{ curr_brightness }}%' - continue_on_error: true - - ##### LIGHT Check Color_Temp Value is available when yes send some current Values ##### - - if: '{{ color_mode_color_temp }}' - then: - - variables: - curr_color_temp: '{{ state_attr(nspanel_event.entity, "color_temp") | int(-1) }}' - min_mireds: '{{ state_attr(nspanel_event.entity, "min_mireds") | int(153) }}' - max_mireds: '{{ state_attr(nspanel_event.entity, "max_mireds") | int(500) }}' - - variables: - curr_color_temp: > - {{ - curr_color_temp - if curr_color_temp >= min_mireds and curr_color_temp <= max_mireds - else ((min_mireds+max_mireds)/2) | int(327) - }} - - condition: '{{ is_number(curr_color_temp) }}' - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: light.temp_value - message: '{{ curr_color_temp }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: light.temp_value_2 - message: '{{ curr_color_temp }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.value }}' - data: - component: light.tempslider - message: '{{ curr_color_temp }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: tempslider.minval={{ min_mireds }} - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: tempslider.maxval={{ max_mireds }} - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.show }}' - data: - component: temp_button - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.show }}' - data: - component: temp_value_2 - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.show }}' - data: - component: temp_touch - continue_on_error: true - - ##### Hide color button when not supported ##### - - if: '{{ color_mode_color }}' - then: - - *delay-default - - service: '{{ nextion.command.show }}' - data: - component: color_button - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.show }}' - data: - component: color_touch - continue_on_error: true - - ## PAGE COVER ## - - alias: Cover settings page - conditions: '{{ nspanel_event.page == page.cover }}' - sequence: &refresh_page_cover - ##### COVER State - - service: '{{ nextion.command.value }}' - data: - component: cover.coverslider - message: '{{ (state_attr(nspanel_event.entity, "current_position") | int ) | round(0) }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: cover.cover_value - message: '{{ (state_attr(nspanel_event.entity, "current_position") | int ) | round(0) }} %' - continue_on_error: true - - ##### COVER Battery ICON Yes / NO ##### - - variables: - battery_level: > - {% if state_attr(nspanel_event.entity, "battery") | default("unavailable") not in ["unavailable", "unknown", "", None] %} - {{ state_attr(nspanel_event.entity, "battery") | default("unavailable") }} - {% elif expand(device_entities(device_id(nspanel_event.entity))) - | selectattr("attributes.device_class", "defined") - | selectattr("attributes.device_class", "eq", "battery") - | map(attribute="state") - | map("float") - | list - | count > 0 %} - {{ - expand(device_entities(device_id(nspanel_event.entity))) - | selectattr("attributes.device_class", "defined") - | selectattr("attributes.device_class", "eq", "battery") - | map(attribute="state") | map("float") - | list - | first - | round(0) - }} - {% elif has_value(nspanel_event.entity | replace("cover.","sensor.") ~ "_battery") %} - {{ states(nspanel_event.entity | replace("cover.","sensor.") ~ "_battery", rounded=true) | default("unavailable") }} - {% elif has_value(nspanel_event.entity | replace("cover.","sensor.") | replace("cover", "battery")) %} - {{ states(nspanel_event.entity | replace("cover.","sensor.") | replace("cover", "battery"), rounded=true) | default("unavailable") }} - {% else %} unavailable - {% endif %} - - if: '{{ is_number(battery_level) }}' - then: - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: cover.battery_value - message: '{{ battery_level }} %' - continue_on_error: true - ### ICON Battery Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: cover.battery_icon - message: '{{ nextion.color.grey_super_light }}' - continue_on_error: true - ### ICON Battery Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: cover.battery_icon - message: '{{ all_icons["battery-medium"] }}' - continue_on_error: true - - ## PAGE FAN ## - - alias: Fan settings page - conditions: '{{ nspanel_event.page == page.fan }}' - sequence: &refresh_page_fan - - variables: - fan: - supported_features: '{{ state_attr(nspanel_event.entity, "supported_features") | int(0) }}' - percentage: > - {{ - state_attr(nspanel_event.entity, "percentage") | int(0) - if is_state(nspanel_event.entity, 'on') - else 0 - }} - steps: > - {% set percentage_step = state_attr(nspanel_event.entity, "percentage_step") | float(0) %} - {{ - (100/percentage_step) | round(0) | int(0) - if percentage_step > 0 - else 0 - }} - - condition: '{{ fan.steps > 0 and fan.supported_features | bitwise_and(1) > 0 }}' - - service: '{{ nextion.command.value }}' - data: - component: fanslider - message: '{{ ((fan.percentage / 100) * fan.steps) | round(0) | int(0) }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: fanslider.maxval={{ fan.steps }} - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: fan_value - message: '{{ fan.percentage }}%' - continue_on_error: true - - service: '{{ nextion.command.printf }}' - data: - cmd: button_up.pco={{ nextion.color.grey_white if fan.percentage < 100 else nextion.color.grey_dark }} - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: button_down.pco={{ nextion.color.grey_white if fan.percentage > 0 else nextion.color.grey_dark }} - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: button_off.pco={{ nextion.color.grey_white if fan.percentage > 0 else nextion.color.grey_dark }} - continue_on_error: true - - *delay-default - - ## PAGE CLIMATE ## - - alias: Climate page - conditions: '{{ nspanel_event.page == page.climate }}' - sequence: &refresh_page_climate - - &variables-climate_entity - variables: - climate_entity: '{{ nspanel_event.entity if nspanel_event.entity is defined }}' - settings_entity_domain: > - {{ - climate_entity.split(".")[0] - if - climate_entity is defined and - climate_entity is string and - climate_entity.split(".") | count > 0 - else "unknown" - }} - hvac_modes: '{{ state_attr(climate_entity, "hvac_modes") if settings_entity_domain == "climate" }}' - - - if: '{{ climate_entity == climate }}' - then: - - service: '{{ nextion.command.text_printf }}' - data: - component: page_label - message: '{{ state_attr(climate, "friendly_name") }}' - continue_on_error: true - - if: '{{ settings_entity_domain == "climate" }}' - then: - ##### Values ##### - - &variables-climate_page - variables: - climate_page_entities: - - entity: !input 'climate_value01' - icon: !input 'climate_value01_icon' - icon_color_rgb: !input 'climate_value01_icon_color' - label_color_rgb: !input 'climate_value01_label_color' - page: climate - component: value01 - - entity: !input 'climate_value02' - icon: !input 'climate_value02_icon' - icon_color_rgb: !input 'climate_value02_icon_color' - label_color_rgb: !input 'climate_value02_label_color' - page: climate - component: value02 - - entity: !input 'climate_value03' - icon: !input 'climate_value03_icon' - icon_color_rgb: !input 'climate_value03_icon_color' - label_color_rgb: !input 'climate_value03_label_color' - page: climate - component: value03 - - entity: !input 'climate_value04' - icon: !input 'climate_value04_icon' - icon_color_rgb: !input 'climate_value04_icon_color' - label_color_rgb: !input 'climate_value04_label_color' - page: climate - component: value04 - - repeat: - for_each: '{{ climate_page_entities }}' - sequence: *display_value - - ##### Slider & climate values ##### - - &climate-update_slider - if: '{{ true }}' - then: - - variables: - current_temp: '{{ state_attr(climate_entity, "current_temperature") | float(-999) | round(1) }}' - target_temp: > - {{ - state_attr(climate_entity, "temperature") | float(-999) | round(1) - if has_value(climate_entity) - else -999 - }} - temp_offset: '{{ (state_attr(climate_entity, "min_temp") | float(5) * 10) | round(0) | int }}' - max_temp: '{{ (state_attr(climate_entity, "max_temp") | float(25) * 10) | round(0) | int }}' - temp_step: > - {% set target_temp_step = state_attr(climate_entity, "target_temp_step") %} - {% if not is_number(target_temp_step) %} - {% set target_temp_step = state_attr(climate_entity, "target_temperature_step") %} - {% endif %} - {% set target_temp_step = target_temp_step | float(0.5) | abs %} - {{ ((10 * target_temp_step) | round(0) | int) if is_number(target_temp_step) and target_temp_step > 0 else 10 }} - total_steps: '{{ ((max_temp-temp_offset)/temp_step) | round(0) | int }}' - climate_state: '{{ states(climate_entity) | default("unavailable") if climate_entity is string else "unavailable" }}' - hvac_action: '{{ state_attr(climate_entity, "hvac_action") }}' - climate_action: '{{ hvac_action if hvac_action not in ["unavailable", "unknown", "", None] else climate_state }}' - climate_icon: > - {% if "off" in climate_action %}{{ all_icons.blank }} - {% elif "heating" in climate_action or "heat" in climate_action %}{{ all_icons["thermometer-lines"] }} - {% elif "cooling" in climate_action or "cool" in climate_action %}{{ all_icons.snowflake }} - {% elif "drying" in climate_action or "dry" in climate_action %}{{ all_icons["water-percent"] }} - {% elif "fan" in climate_action or "fan_only" in climate_action %}{{ all_icons.fan }} - {% elif "heat_cool" in climate_action %}{{ all_icons.autorenew }} - {% elif "auto" in climate_action %}{{ all_icons["calendar-sync"] }} - {% elif "idle" in climate_action %}{{ all_icons.thermometer }} - {% else %}{{ all_icons.blank }} - {% endif %} - - *delay-default - - service: '{{ nextion.command.set_climate }}' - data: - current_temp: '{{ current_temp }}' - target_temp: '{{ target_temp }}' - temp_step: '{{ temp_step }}' - total_steps: '{{ total_steps }}' - slider_val: '{{ ((10*target_temp - temp_offset) / temp_step) | round(0) | int }}' - temp_offset: '{{ temp_offset }}' - climate_icon: '{{ climate_icon }}' - continue_on_error: true - - ##### Climate buttons ##### - - &climate-update_buttons - repeat: - for_each: '{{ page_climate.buttons.hvac_mode }}' - sequence: - - condition: '{{ repeat.item.mode in hvac_modes }}' - - *delay-default - ### ICON Font Color ### - - service: '{{ nextion.command.font_color }}' - data: - component: 'climate.{{ repeat.item.component }}_icon' - message: > - {{ - nextion.color[repeat.item.color] - if states(climate_entity) == repeat.item.mode - else nextion.color.disabled - }} - continue_on_error: true - ### ICON Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: 'climate.{{ repeat.item.component }}_icon' - message: '{{ all_icons[repeat.item.icon] }}' - continue_on_error: true - ### Enable button click ### - - *delay-default - - service: '{{ nextion.command.show }}' - data: - component: '{{ repeat.item.component }}' - continue_on_error: true - - ##### Climate custom buttons ##### - - &climate-update_custom_buttons - if: '{{ true }}' - then: - - &climate-update_custom_buttons-variables - variables: - climate_custom_buttons: - - entity: !input climate_button08 - icon: !input climate_button08_icon - icon_color_rgb: !input climate_button08_icon_color - component: button08 - - entity: !input climate_button09 - icon: !input climate_button09_icon - icon_color_rgb: !input climate_button09_icon_color - component: button09 - - &climate-update_custom_buttons-update - repeat: - for_each: '{{ climate_custom_buttons }}' - sequence: - - condition: '{{ repeat.item.entity is defined and repeat.item.entity is string and repeat.item.entity | length > 0 }}' - - variables: - entity_domain: > - {{ - repeat.item.entity.split(".")[0] - if - repeat.item.entity is defined and - repeat.item.entity is string and - repeat.item.entity.split(".") | count > 0 - else "unknown" - }} - - condition: '{{ entity_domain != "unknown" }}' - - *delay-default - ### ICON Font Color ### - - service: '{{ nextion.command.font_color }}' - data: - component: 'climate.{{ repeat.item.component }}_icon' - message: > - {{ - ((repeat.item.icon_color_rgb[0] //(2**3)) *(2**11))+((repeat.item.icon_color_rgb[1] //(2**2)) *(2**5))+(repeat.item.icon_color_rgb[2] //(2**3)) - if states(repeat.item.entity) in ["on", "true", true, "open", "opening"] - else nextion.color.disabled - }} - continue_on_error: true - ### ICON Font ### - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: 'climate.{{ repeat.item.component }}_icon' - message: > - {{ - all_icons[repeat.item.icon.split(":")[1]] | default(all_icons.unknown) - if repeat.item.icon is defined and repeat.item.icon is string and ":" in repeat.item.icon and repeat.item.icon.split(":") | count > 0 - else nextion.icon.domain[entity_domain] | default(all_icons.unknown) - }} - continue_on_error: true - ### Enable button click ### - - *delay-default - - service: '{{ nextion.command.show }}' - data: - component: '{{ repeat.item.component }}' - continue_on_error: true - #else: - ##### Return to Home page in case is not a climate entity ##### - #- *jump_wakeup_page - - ## ENTITY PAGES 01 - 04 ## - - alias: Entity pages - conditions: '{{ nspanel_event.page in page.entitypages }}' - sequence: &refresh-entity_pages - - &variables-entity_pages - variables: - ##### Entity pages ##### - entitypages_value_alignment_temp: !input 'entitypages_value_alignment' - entitypages_value_alignment: > - {{ - entitypages_value_alignment_temp | int - if - is_number(entitypages_value_alignment_temp) and - entitypages_value_alignment_temp | int >= 0 and - entitypages_value_alignment_temp | int <= 2 - else 0 - }} - entity_pages_labels: - - label: !input 'entity_page01_label' - - label: !input 'entity_page02_label' - - label: !input 'entity_page03_label' - - label: !input 'entity_page04_label' - entity_pages_entities: - - entity: !input 'entities_entity01' - name: !input 'entities_entity01_name' - icon: !input 'entities_entity01_icon' - page: entitypage01 - component: value01 - - entity: !input 'entities_entity02' - name: !input 'entities_entity02_name' - icon: !input 'entities_entity02_icon' - page: entitypage01 - component: value02 - - entity: !input 'entities_entity03' - name: !input 'entities_entity03_name' - icon: !input 'entities_entity03_icon' - page: entitypage01 - component: value03 - - entity: !input 'entities_entity04' - name: !input 'entities_entity04_name' - icon: !input 'entities_entity04_icon' - page: entitypage01 - component: value04 - - entity: !input 'entities_entity05' - name: !input 'entities_entity05_name' - icon: !input 'entities_entity05_icon' - page: entitypage01 - component: value05 - - entity: !input 'entities_entity06' - name: !input 'entities_entity06_name' - icon: !input 'entities_entity06_icon' - page: entitypage01 - component: value06 - - entity: !input 'entities_entity07' - name: !input 'entities_entity07_name' - icon: !input 'entities_entity07_icon' - page: entitypage01 - component: value07 - - entity: !input 'entities_entity08' - name: !input 'entities_entity08_name' - icon: !input 'entities_entity08_icon' - page: entitypage01 - component: value08 - - entity: !input 'entities_entity09' - name: !input 'entities_entity09_name' - icon: !input 'entities_entity09_icon' - page: entitypage02 - component: value01 - - entity: !input 'entities_entity10' - name: !input 'entities_entity10_name' - icon: !input 'entities_entity10_icon' - page: entitypage02 - component: value02 - - entity: !input 'entities_entity11' - name: !input 'entities_entity11_name' - icon: !input 'entities_entity11_icon' - page: entitypage02 - component: value03 - - entity: !input 'entities_entity12' - name: !input 'entities_entity12_name' - icon: !input 'entities_entity12_icon' - page: entitypage02 - component: value04 - - entity: !input 'entities_entity13' - name: !input 'entities_entity13_name' - icon: !input 'entities_entity13_icon' - page: entitypage02 - component: value05 - - entity: !input 'entities_entity14' - name: !input 'entities_entity14_name' - icon: !input 'entities_entity14_icon' - page: entitypage02 - component: value06 - - entity: !input 'entities_entity15' - name: !input 'entities_entity15_name' - icon: !input 'entities_entity15_icon' - page: entitypage02 - component: value07 - - entity: !input 'entities_entity16' - name: !input 'entities_entity16_name' - icon: !input 'entities_entity16_icon' - page: entitypage02 - component: value08 - - entity: !input 'entities_entity17' - name: !input 'entities_entity17_name' - icon: !input 'entities_entity17_icon' - page: entitypage03 - component: value01 - - entity: !input 'entities_entity18' - name: !input 'entities_entity18_name' - icon: !input 'entities_entity18_icon' - page: entitypage03 - component: value02 - - entity: !input 'entities_entity19' - name: !input 'entities_entity19_name' - icon: !input 'entities_entity19_icon' - page: entitypage03 - component: value03 - - entity: !input 'entities_entity20' - name: !input 'entities_entity20_name' - icon: !input 'entities_entity20_icon' - page: entitypage03 - component: value04 - - entity: !input 'entities_entity21' - name: !input 'entities_entity21_name' - icon: !input 'entities_entity21_icon' - page: entitypage03 - component: value05 - - entity: !input 'entities_entity22' - name: !input 'entities_entity22_name' - icon: !input 'entities_entity22_icon' - page: entitypage03 - component: value06 - - entity: !input 'entities_entity23' - name: !input 'entities_entity23_name' - icon: !input 'entities_entity23_icon' - page: entitypage03 - component: value07 - - entity: !input 'entities_entity24' - name: !input 'entities_entity24_name' - icon: !input 'entities_entity24_icon' - page: entitypage03 - component: value08 - - entity: !input 'entities_entity25' - name: !input 'entities_entity25_name' - icon: !input 'entities_entity25_icon' - page: entitypage04 - component: value01 - - entity: !input 'entities_entity26' - name: !input 'entities_entity26_name' - icon: !input 'entities_entity26_icon' - page: entitypage04 - component: value02 - - entity: !input 'entities_entity27' - name: !input 'entities_entity27_name' - icon: !input 'entities_entity27_icon' - page: entitypage04 - component: value03 - - entity: !input 'entities_entity28' - name: !input 'entities_entity28_name' - icon: !input 'entities_entity28_icon' - page: entitypage04 - component: value04 - - entity: !input 'entities_entity29' - name: !input 'entities_entity29_name' - icon: !input 'entities_entity29_icon' - page: entitypage04 - component: value05 - - entity: !input 'entities_entity30' - name: !input 'entities_entity30_name' - icon: !input 'entities_entity30_icon' - page: entitypage04 - component: value06 - - entity: !input 'entities_entity31' - name: !input 'entities_entity31_name' - icon: !input 'entities_entity31_icon' - page: entitypage04 - component: value07 - - entity: !input 'entities_entity32' - name: !input 'entities_entity32_name' - icon: !input 'entities_entity32_icon' - page: entitypage04 - component: value08 - - variables: - entity_page_index: '{{ (nspanel_event.page[-2:] | int(-1)) - 1 }}' - first_entity: '{{ entity_page_index * 8 }}' - last_entity: '{{ first_entity + 8 }}' - ##### Entity page - Label ##### - - if: '{{ entity_pages_labels[entity_page_index].label | length > 0 }}' - then: - - service: '{{ nextion.command.text_printf }}' - data: - 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] }}' - sequence: &update-entity_page_entity - - if: '{{ repeat.item.entity is string and repeat.item.entity | length > 0 }}' - then: - - variables: - repeat_item_state: '{{ states(repeat.item.entity, rounded=true) | default("unavailable") }}' - repeat_item_icon: > - {% if repeat.item.icon is string and repeat.item.icon | length > 0 %} - {{ - all_icons[repeat.item.icon.split(":")[1]] | default(all_icons.unknown) - if repeat.item.icon.split(":") | count > 0 - else repeat.item.icon - }} - {% elif state_attr(repeat.item.entity, "icon") | default("") not in ["unavailable", "unknown", "", None] %} - {{ all_icons[state_attr(repeat.item.entity, "icon").split(":")[1]] | default(None) }} - {% endif %} - - service: '{{ nextion.command.set_entity }}' - data: - ent_id: '{{ repeat.item.page }}.{{ repeat.item.component }}' - ent_icon: '{{ repeat_item_icon if repeat_item_icon else all_icons.blank }}' - ent_label: >- - {%- if repeat.item.name | length > 0 -%} {{ repeat.item.name }} - {%- elif repeat_item_state in ["unavailable", "unknown", "", None] -%} {{ repeat.item.entity }} - {%- else -%} {{ state_attr(repeat.item.entity, "friendly_name") | default(mui[language].no_name) }} - {%- endif -%} - ent_value: '{{ repeat_item_state ~ ((state_attr(repeat.item.entity, "unit_of_measurement") | default("")) if state_attr(repeat.item.entity, "unit_of_measurement") is string else "") }}' - ent_value_xcen: '{{ entitypages_value_alignment }}' - continue_on_error: true - - ## PAGE WEATHER (WEATHER01 to WEATHER05) ## - - alias: Weather pages - conditions: '{{ nspanel_event.page in page.weatherpages }}' - sequence: - - variables: - weather_attribution: '{{ state_attr(weather_entity, "attribution") if weather_entity is string }}' - weather_type: > - {% if not weather_attribution %} unavailable - {% elif "AccuWeather" in weather_attribution %} AccuWeather - {% elif "OpenWeatherMap" in weather_attribution %} OpenWeather - {% elif "SMHI" in weather_attribution %} SMHI - {% elif "met.no" in weather_attribution %} Met.no - {% elif "Météo-France" in weather_attribution %} Meteo_France - {% else %} Other - {% endif %} - weather_units: - hours_of_sun: '{{ state_attr(weather_entity, "hours_of_sun_unit") | default("h") if weather_entity is string and state_attr(weather_entity, "hours_of_sun_unit") else "h" }}' - precipitation: '{{ state_attr(weather_entity, "precipitation_unit") | default("") if weather_entity is string and state_attr(weather_entity, "precipitation_unit") }}' - precipitation_probability: '{{ state_attr(weather_entity, "precipitation_probability_unit") | default("%") if weather_entity is string and state_attr(weather_entity, "precipitation_probability_unit") else "%" }}' - pressure: '{{ state_attr(weather_entity, "pressure_unit") | default("") if weather_entity is string and state_attr(weather_entity, "pressure_unit") }}' - #temperature: '{{ state_attr(weather_entity, "temperature_unit") | default("°") if weather_entity is string and state_attr(weather_entity, "temperature_unit") else "°" }}' - thunderstorm_probability: '{{ state_attr(weather_entity, "thunderstorm_probability_unit") | default("%") if weather_entity is string and state_attr(weather_entity, "thunderstorm_probability_unit") else "%" }}' - uv_index: '{{ state_attr(weather_entity, "uv_index_unit") | default("") if weather_entity is string and state_attr(weather_entity, "uv_index_unit") }}' - #visibility: '{{ state_attr(weather_entity, "visibility_unit") | default("") if weather_entity is string and state_attr(weather_entity, "visibility_unit") }}' - wind_speed: '{{ state_attr(weather_entity, "wind_speed_unit") | default("") if weather_entity is string and state_attr(weather_entity, "wind_speed_unit") }}' - page_name: '{{ nspanel_event.page }}' - page_index: '{{ (page_name[-2:] | int(0)) - 1 }}' - - ##### Display relative day ##### - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ page_name }}.day' - message: '{{ (dict.values(mui[language].relative_day) | list)[page_index] }}' - continue_on_error: true - - *delay-default - - ##### Display date (long) ##### - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ page_name }}.date' - message: > - {{ - as_timestamp(now() + timedelta(days= (page_index))) - | timestamp_custom - ( - date_format - | replace("%A", (dict.values(mui[language].weekdays) | list)[(now() + timedelta(days= (page_index))).weekday()]) - | replace("%a", (dict.values(mui[language].weekdays_short) | list)[(now() + timedelta(days= (page_index))).weekday()]) - | replace("%B", (dict.values(mui[language].months) | list)[(now() + timedelta(days= (page_index))).month-1]) - | replace("%b", (dict.values(mui[language].months_short) | list)[(now() + timedelta(days= (page_index))).month-1]) - ) - }} - continue_on_error: true - - *delay-default - - ##### Display weather data only when available ##### - - variables: - datetime_is_string: '{{ state_attr(weather_entity, "forecast")[0] is defined and state_attr(weather_entity, "forecast")[0]["datetime"] is string }}' - forecast_day: > - {% if datetime_is_string %} - {{ - state_attr(weather_entity, "forecast") | default([]) - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | timestamp_local ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | timestamp_local ) - | list - }} - {% else %} - [ - { - 'datetime': '{{ state_attr(weather_entity, "forecast") | default([]) - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="datetime") | list | first | as_timestamp | timestamp_local - if state_attr(weather_entity, "forecast") | default([]) - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="datetime") | list | count > 0 - else "" }}', - 'condition': '{{ state_attr(weather_entity, "forecast") | default([]) - | selectattr("condition", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="condition") | list | first - if state_attr(weather_entity, "forecast") | default([]) - | selectattr("condition", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="condition") | list | count > 0 - else "" }}', - 'temperature': '{{ state_attr(weather_entity, "forecast") | default([]) - | selectattr("temperature", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="temperature") | list | first - if state_attr(weather_entity, "forecast") | default([]) - | selectattr("temperature", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="temperature") | list | count > 0 - else "" }}', - 'templow': '{{ state_attr(weather_entity, "forecast") | default([]) - | selectattr("templow", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="templow") | list | first - if state_attr(weather_entity, "forecast") | default([]) - | selectattr("templow", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="templow") | list | count > 0 - else "" }}', - 'precipitation': '{{ state_attr(weather_entity, "forecast") | default([]) - | selectattr("precipitation", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="precipitation") | list | first - if state_attr(weather_entity, "forecast") | default([]) - | selectattr("precipitation", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="precipitation") | list | count > 0 - else "" }}', - 'wind_speed': '{{ state_attr(weather_entity, "forecast") | default([]) - | selectattr("wind_speed", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="wind_speed") | list | first - if state_attr(weather_entity, "forecast") | default([]) - | selectattr("wind_speed", "defined") - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | as_datetime ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | as_datetime ) - | map(attribute="wind_speed") | list | count > 0 - else "" }}' - } - ] - {% endif %} - - if: '{{ forecast_day | count > 0 or page_index == 0 }}' - then: # Display forecast - - variables: - metnoweather: '{{ weather_type == "Met.no" }}' - metnoweather_hourly_forecast: '{{ state_attr(weather_entity ~ "_hourly", "forecast") if metnoweather and has_value(weather_entity ~ "_hourly") }}' - forecast_day: > - {% if forecast_day | count > 0 %}{{ forecast_day }} - {% elif metnoweather and metnoweather_hourly_forecast %} - {{ metnoweather_hourly_forecast - | selectattr("datetime", "defined") - | selectattr("datetime", ">=", (today_at("00:00") + timedelta(days= page_index)) | as_timestamp | timestamp_local ) - | selectattr("datetime", "<", (today_at("00:00") + timedelta(days= (page_index+1))) | as_timestamp | timestamp_local ) - | list - }} - {% endif %} - - variables: - forecast_day: > - {% if forecast_day | count > 0 %}{{ forecast_day }} - {% elif page_index == 0 %} - [ - { - 'condition': '{{ states(weather_entity) }}', - 'temperature': '{{ state_attr(weather_entity, "temperature") }}', - 'wind_speed': '{{ state_attr(weather_entity, "wind_speed") }}' - } - ] - {% endif %} - - if: '{{ forecast_day | count > 0 }}' - then: - - variables: - accuweather: '{{ weather_type == "AccuWeather" }}' - accuweather_day_name: '{{ "day_" ~ page_index }}' - accuweather_sensor_prefix: '{{ "sensor." ~ (weather_entity | replace("weather.","")) ~ "_" }}' - accuweather_sensor_sufix: '{{ "_" ~ page_index ~ "d" }}' - temp_min: > - {{ forecast_day | selectattr("templow", "defined") | map(attribute="templow") | map("float") | list | min - if forecast_day | selectattr("templow", "defined") | map(attribute="templow") | map("float") | list | count > 0 - else forecast_day | selectattr("temperature", "defined") | rejectattr("temperature", "eq", "") | map(attribute="temperature") | map("float") | list | min | default("unknown") - }} - temp_max: > - {{ - forecast_day | selectattr("temperature", "defined") | rejectattr("temperature", "eq", "") | map(attribute="temperature") | map("float") | list | max - if forecast_day | selectattr("temperature", "defined") | rejectattr("temperature", "eq", "") | map(attribute="temperature") | map("float") | list | count > 0 - }} - condition: > - {{ - forecast_day | selectattr("condition", "defined") | rejectattr("condition", "eq", "") | map(attribute="condition") | list | first - if forecast_day | selectattr("condition", "defined") | rejectattr("condition", "eq", "") | map(attribute="condition") | list | count > 0 - }} - precipitation: > - {{ - forecast_day | selectattr("precipitation", "defined") | rejectattr("precipitation", "eq", "") | map(attribute="precipitation") | map("float") | list | sum | round(0) - if forecast_day | selectattr("precipitation", "defined") | rejectattr("precipitation", "eq", "") | map(attribute="precipitation") | map("float") | list | count > 0 - }} - precipitation_probability: > - {{ - forecast_day | selectattr("precipitation_probability", "defined") | rejectattr("precipitation_probability", "eq", "") | map(attribute="precipitation_probability") | map("float") | list | max | round(0) - if forecast_day | selectattr("precipitation_probability", "defined") | rejectattr("precipitation_probability", "eq", "") | map(attribute="precipitation_probability") | map("float") | list | count > 0 - }} - pressure: > - {{ - forecast_day | selectattr("pressure", "defined") | rejectattr("pressure", "eq", "") | map(attribute="pressure") | map("float") | list | max | round(0) - if forecast_day | selectattr("pressure", "defined") | rejectattr("pressure", "eq", "") | map(attribute="pressure") | map("float") | list | count > 0 - }} - wind_speed: > - {{ - forecast_day | selectattr("wind_speed", "defined") | rejectattr("wind_speed", "eq", "") | map(attribute="wind_speed") | map("float") | list | max | round(0) - if forecast_day | selectattr("wind_speed", "defined") | rejectattr("wind_speed", "eq", "") | map(attribute="wind_speed") | map("float") | list | count > 0 - }} - hours_of_sun: > - {{ - states(accuweather_sensor_prefix ~ "hours_of_sun" ~ accuweather_sensor_sufix, rounded=true) | default("unknown") - if accuweather - else - ( - forecast_day | selectattr("hours_of_sun", "defined") | rejectattr("hours_of_sun", "eq", "") | map(attribute="hours_of_sun") | map("float") | list | sum | round(0) - if forecast_day | selectattr("hours_of_sun", "defined") | rejectattr("hours_of_sun", "eq", "") | map(attribute="hours_of_sun") | map("float") | list | count > 0 - ) - }} - uv_index: > - {{ - states(accuweather_sensor_prefix ~ "uv_index" ~ accuweather_sensor_sufix, rounded=true) | default("unknown") - if accuweather - else - ( - forecast_day | selectattr("uv_index", "defined") | rejectattr("uv_index", "eq", "") | map(attribute="uv_index") | map("float") | list | max | round(0) - if forecast_day | selectattr("uv_index", "defined") | rejectattr("uv_index", "eq", "") | map(attribute="uv_index") | map("float") | list | count > 0 - ) - }} - thunderstorm_probability: > - {{ - states(accuweather_sensor_prefix ~ "thunderstorm_probability_day" ~ accuweather_sensor_sufix, rounded=true) | default("unknown") - if accuweather - else - ( - forecast_day | selectattr("thunderstorm_probability", "defined") | rejectattr("thunderstorm_probability", "eq", "") | map(attribute="thunderstorm_probability") | map("float") | list | max | round(0) - if forecast_day | selectattr("thunderstorm_probability", "defined") | rejectattr("thunderstorm_probability", "eq", "") | map(attribute="thunderstorm_probability") | map("float") | list | count > 0 - ) - }} - parameters: - - name: hours_of_sun - visibility: '{{ is_number(hours_of_sun) }}' - value: '{{ (hours_of_sun ~ " " ~ weather_units.hours_of_sun) if is_number(hours_of_sun) }}' - icon: '{{ nextion.icon.weather.sun }}' - - name: thunderstorm_probability - visibility: '{{ is_number(thunderstorm_probability) }}' - value: '{{ (thunderstorm_probability ~ weather_units.thunderstorm_probability) if is_number(thunderstorm_probability) }}' - icon: '{{ nextion.icon.weather.lightning }}' - - name: precipitation - visibility: '{{ is_number(precipitation) or is_number(precipitation_probability) }}' - value: > - {{ (precipitation ~ " " ~ weather_units.precipitation) if is_number(precipitation) }} - {{ "-" if is_number(precipitation) and is_number(precipitation_probability) }} - {{ (precipitation_probability ~ weather_units.precipitation_probability) if is_number(precipitation_probability) }} - icon: '{{ nextion.icon.weather.rain }}' - - name: uv_index - visibility: '{{ is_number(uv_index) }}' - value: > - {{ (state_attr(accuweather_sensor_prefix ~ "uv_index" ~ accuweather_sensor_sufix, "level") | default(None) ~ ": ") if weather_type == "AccuWeather" }} - {{ (uv_index ~ weather_units.uv_index) if is_number(uv_index) }} - icon: '{{ nextion.icon.weather.protect }}' - - name: wind_speed - visibility: '{{ is_number(wind_speed) }}' - value: '{{ (wind_speed ~ " " ~ weather_units.wind_speed) if is_number(wind_speed) }}' - icon: '{{ nextion.icon.weather.wind }}' - - name: pressure - visibility: '{{ is_number(pressure) }}' - value: '{{ (pressure ~ " " ~ weather_units.pressure) if is_number(pressure) }}' - icon: '{{ nextion.icon.weather.gauge }}' - - ##### Display weather PIC when available - - if: '{{ condition not in ["unknown", None] }}' - then: - - service: '{{ nextion.command.printf }}' - data: - cmd: > - {{ page_name }}.weather_icon.pic={{ - nextion.pic.weather[states(weather_entity) | default("unavailable") if weather_entity is string else "unavailable"] | default(None) - if condition == "unknown" and page_name == page.weatherpages[0] - else nextion.pic.weather[condition] | default(None) - }} - continue_on_error: true - - *delay-default - - ##### Display temperature min/max when available - - variables: - temperature_string: > - {{ (temp_min | round(0) ~ temperature_units) if is_number(temp_min) }} - {{ "/" if is_number(temp_min) and is_number(temp_max) and temp_min != temp_max }} - {{ (temp_max | round(0) ~ temperature_units) if is_number(temp_max) and temp_min != temp_max }} - - if: '{{ (is_number(temp_min) or is_number(temp_max)) and temperature_string is string and temperature_string | length > 0 }}' - then: - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ page_name }}.temperature' ### Temperature MIN/MAX ### - message: '{{ temperature_string }}' - continue_on_error: true - - *delay-default - - ##### fields 1 to 5 (Parameters) ##### - - repeat: - for_each: '{{ (parameters | selectattr("visibility", "eq", true) | list)[:5] }}' - sequence: - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ page_name }}.value0{{ repeat.index }}' - message: '{{ repeat.item.value }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ page_name }}.value0{{ repeat.index }}_icon' - message: '{{ repeat.item.icon }}' - continue_on_error: true - - *delay-default - else: &forecast_unavailable - - service: '{{ nextion.command.text_printf }}' - data: - component: '{{ page_name }}.value01' - message: '{{ mui[language].unavailable }}' - continue_on_error: true - - *delay-default - else: *forecast_unavailable - - ## PAGE NOTIFICATION ## - - alias: Notification page - conditions: - - '{{ nspanel_event.page == page.notification }}' - - '{{ confirmation_message is string and states(confirmation_message) | default("unavailable") != "on" }}' - sequence: - - service: '{{ nextion.command.text_printf }}' - data: - component: notification.notifi_text01 - message: '{{ states(notification_text) | default(mui[language].unavailable) if notification_text is string else mui[language].unavailable }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: notification.notifi_label - message: '{{ states(notification_label) | default(mui[language].unavailable) if notification_label is string else mui[language].unavailable }}' - continue_on_error: true - - ## PAGE QR Code ## - - alias: QRCode page - conditions: '{{ nspanel_event.page == page.qrcode }}' - sequence: - - variables: - qrcode_label: !input 'qrcode_label' - qrcode_value: !input 'qrcode_value' - - if: '{{ qrcode_label | length > 0 }}' - then: - - service: '{{ nextion.command.text_printf }}' - data: - component: 'qrcode_label' - message: '{{ qrcode_label }}' - continue_on_error: true - - *delay-default - - service: '{{ nextion.command.text_printf }}' - data: - component: 'qrcode_value' - message: '{{ qrcode_value }}' - continue_on_error: true - - ## PAGE Screen saver ## - - alias: Screen saver page - conditions: '{{ nspanel_event.page == page.screensaver }}' - sequence: - ##### Set wake-up screen ##### - - *variables_wakeup_page - - service: '{{ nextion.command.value }}' - data: - component: orign - message: "{{ wakeup_page | int(0) if is_number(wakeup_page) else 0 }}" - continue_on_error: true - - *delay-default - - - alias: light settings # rgb_color, brightness, color_temp + - alias: light settings # rgb_color, brightness, color_temp - This was kept until we find a solution to call with rgb_color directly from ESPHome conditions: - '{{ nspanel_event.page == page.light }}' - '{{ nspanel_event.component in ["rgb_color", "brightness_pct", "color_temp"] }}' @@ -8184,94 +8234,6 @@ action: '{{ nspanel_event.component }}': '{{ nspanel_event.value }}' continue_on_error: true - - alias: fan percentage - conditions: - - '{{ nspanel_event.page == page.fan }}' - - '{{ nspanel_event.component == "percentage" }}' - - '{{ nspanel_event.entity is defined and nspanel_event.entity is string and nspanel_event.entity | length > 0 }}' - sequence: - - if: '{{ nspanel_event.value > 0 }}' - then: - - service: fan.turn_on - data: - percentage: '{{ nspanel_event.value }}' - target: - entity_id: '{{ nspanel_event.entity }}' - continue_on_error: true - else: - - service: fan.turn_off - target: - entity_id: '{{ nspanel_event.entity }}' - continue_on_error: true - #- if: '{{ true }}' - # then: *refresh_page_fan - - - alias: coversetting position - conditions: - - '{{ nspanel_event.component == "cover_position" }}' - - '{{ nspanel_event.entity is defined and nspanel_event.entity is string and nspanel_event.entity | length > 0 }}' - sequence: - - service: 'cover.set_cover_position' - data: - entity_id: '{{ nspanel_event.entity }}' - position: '{{ nspanel_event.value }}' - continue_on_error: true - - - alias: coversetting open close stop - conditions: - - '{{ nspanel_event.component in ["open_cover", "close_cover", "stop_cover"] }}' - - '{{ nspanel_event.entity is defined and nspanel_event.entity is string and nspanel_event.entity | length > 0 }}' - sequence: - - service: 'cover.{{ nspanel_event.component }}' - data: - entity_id: '{{ nspanel_event.entity }}' - continue_on_error: true - - - alias: climate climateslider - conditions: - - '{{ nspanel_event.page == page.climate }}' - - '{{ nspanel_event.component == "climate_position" }}' - - '{{ nspanel_event.entity is defined and nspanel_event.entity is string and nspanel_event.entity | length > 0 }}' - sequence: - - if: '{{ not is_state(nspanel_event.entity, "off") }}' - then: - - variables: - new_setpoint: '{{ nspanel_event.value | int(-999) }}' - - if: '{{ is_number(new_setpoint) and new_setpoint > -999 }}' - then: - - service: climate.set_temperature - data: - entity_id: '{{ nspanel_event.entity }}' - temperature: '{{ new_setpoint / 10 }}' - continue_on_error: true - - - alias: climate button press - conditions: - - '{{ nspanel_event.page == page.climate }}' - - '{{ nspanel_event.component in ["button01", "button02", "button03", "button04", "button05", "button06", "button07"] }}' - - '{{ nspanel_event.value == "release" }}' - - '{{ nspanel_event.entity is defined and nspanel_event.entity is string and nspanel_event.entity | length > 0 }}' - sequence: - - *variables-climate_entity - - variables: - new_hvac_mode: > - {{ - page_climate.buttons.hvac_mode - | selectattr("component", "defined") - | selectattr("component", "eq", nspanel_event.component) - | map(attribute="mode") - | list - | first - }} - - condition: '{{ new_hvac_mode in hvac_modes and new_hvac_mode in ["off", "heat", "cool", "heat_cool", "auto", "dry", "fan_only"] }}' - - service: climate.set_hvac_mode - data: - hvac_mode: '{{ new_hvac_mode }}' - target: - entity_id: '{{ climate_entity }}' - continue_on_error: true - - *climate-update_buttons - - alias: climate custom button press conditions: - '{{ nspanel_event.page == page.climate }}' @@ -8343,9 +8305,9 @@ action: {% set button_wait = states(nspanelevent) %} {% set button_wait = button_wait | from_json if button_wait is string and button_wait not in ["unavailable", "unknown", "", None] else { "page": "unknown", "component": "unknown", "value": "unknown" } %} {{ - button_wait.page == nspanel_event.page and - button_wait.component == nspanel_event.component and - button_wait.value == "release" + button_wait.page is defined and button_wait.page == nspanel_event.page and + button_wait.component is defined and button_wait.component == nspanel_event.component and + button_wait.value is defined and button_wait.value == "release" }} timeout: seconds: 1 @@ -8517,16 +8479,6 @@ action: back_page: '{{ page.home }}' continue_on_error: true - - alias: Jump to weather page - conditions: - - '{{ nspanel_event.page == page.home }}' - - '{{ nspanel_event.component == "weather" }}' - sequence: - - service: '{{ nextion.command.printf }}' - data: - cmd: 'page {{ page.weatherpages[0] }}' - continue_on_error: true - - alias: Jump to QR code page conditions: - '{{ qrcode_enabled == true }}' @@ -8599,16 +8551,16 @@ action: - '{{ nspanel_event.component == "timeout" }}' sequence: *boot_init_sequence - ##### BOOT NSPANEL - automation reload ##### - alias: Automation reloaded conditions: - condition: trigger - id: automation_reloaded + id: + - automation_reloaded + - ha_started sequence: - ##### Update Date & Time before showing the pages (just in case display format changed) ##### - - *set_localization - - *set_relay_local + ##### Update global settings (as user may have changed something) ##### + - *global_settings - *refresh-datetime - choose: @@ -8844,8 +8796,9 @@ action: - left_button_press - right_button_press sequence: + - *variables_hardware - variables: - button_context: '{{ page_home.hardware.buttons.left if trigger.id == "left_button_press" else page_home.hardware.buttons.right }}' + button_context: '{{ hardware.buttons.left if trigger.id == "left_button_press" else hardware.buttons.right }}' button_domain: > {{ button_context.entity.split(".")[0] | default("unknown") @@ -8883,8 +8836,8 @@ action: sequence: !input right_button_hold_custom_action else: # Single Click - condition: '{{ button_context.entity | length > 0 }}' - - condition: '{{ trigger.id != "left_button_press" or page_home.hardware.buttons.left.entity != relay01_entity }}' ### Prevents blueprint control when locally controlled - - condition: '{{ trigger.id != "right_button_press" or page_home.hardware.buttons.right.entity != relay02_entity }}' ### Prevents blueprint control when locally controlled + - condition: '{{ trigger.id != "left_button_press" or hardware.buttons.left.entity != relay01_entity }}' ### Prevents blueprint control when locally controlled + - condition: '{{ trigger.id != "right_button_press" or hardware.buttons.right.entity != relay02_entity }}' ### Prevents blueprint control when locally controlled - service: > {% if button_domain in ["light", "switch", "cover", "input_boolean", "automation", "fan"] %} {{ button_domain }}.toggle diff --git a/nspanel_esphome.yaml b/nspanel_esphome.yaml index f3ecb96..a710590 100644 --- a/nspanel_esphome.yaml +++ b/nspanel_esphome.yaml @@ -8,6 +8,7 @@ ##### ADVANCED CONFIGURATION - activate only when you know what you do ############################## substitutions: + verbose_log: "false" baud_rate: "115200" # requires 115200 if tft is installed but can be changed to 9600 if tft upload fails and nextion switches to 9600 # ## usage of secrets-file ## -> comment in ###### Change ME ###### # device_name: "nspanel-name" # Wird im Blueprint benötigt! @@ -27,6 +28,20 @@ substitutions: time_source: "homeassistant" # Either "homeassistant" or "sntp" are supported + ### Local thermostat defaults ### + # https://esphome.io/components/climate/thermostat.html + embedded_thermostat_disabled: "true" + embedded_thermostat_temp_units: "°C" + embedded_thermostat_heater_relay: "1" # Select 1 for "Relay 1" or 2 for "Relay 2" + embedded_thermostat_min_heating_off_time: "300" + embedded_thermostat_min_heating_run_time: "300" + embedded_thermostat_min_idle_time: "30" + # https://esphome.io/components/climate/index.html#base-climate-configuration + embedded_thermostat_visual_min_temperature: "5" + embedded_thermostat_visual_max_temperature: "25" + embedded_thermostat_visual_temperature_step: "0.5" + + ###### USE THIS ONLY FOR YOUR FIRST TFT UPLOAD ###### AND IF EXIT-REPARSE BUTTON FAILS ###### ONCE IT WORKED, REMOVE THESE LINES @@ -64,7 +79,7 @@ wifi: ##### ESPHOME CONFIGURATION ##### esphome: name: ${device_name} - min_version: 2022.10.2 + min_version: 2023.5.0 ##### TYPE OF ESP BOARD ##### esp32: @@ -128,39 +143,13 @@ time: on_time: - seconds: 0 then: - - wait_until: - binary_sensor.is_on: nextion_init - - &refresh_datetime - lambda: |- - std::string time_format_str = id(mui_time_format); - if (time_format_str.find("%p") != std::string::npos) - { - std::string meridiem_text = id(time_provider).now().strftime("%p"); - id(disp1).set_component_text_printf("home.meridiem", "%s", meridiem_text.c_str()); - } - else { id(disp1).set_component_text_printf("home.meridiem", " "); } - if (time_format_str.find("%-H") != std::string::npos) { time_format_str = time_format_str.replace(time_format_str.find("%-H"), sizeof("%-H")-1, to_string((int)(id(time_provider).now().hour))); } - if (time_format_str.find("%-I") != std::string::npos) - { - if (id(time_provider).now().hour>12) - { - time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, to_string((int)(id(time_provider).now().hour-12))); - } - else if (id(time_provider).now().hour==0) - { - time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, "12"); - } - else - { - time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, to_string((int)(id(time_provider).now().hour))); - } - } - std::string time_text = id(time_provider).now().strftime(time_format_str); - id(disp1).set_component_text_printf("home.time", "%s", time_text.c_str()); + - script.execute: + id: refresh_datetime on_time_sync: then: - logger.log: "Synchronized system clock" - - *refresh_datetime + - script.execute: + id: refresh_datetime ##### START - BUTTON CONFIGURATION ##### button: @@ -200,6 +189,7 @@ button: ##### START - API CONFIGURATION ##### api: + id: api_server ##### advanced config - activate to use api_password ##### # password: ${api_password} services: @@ -384,39 +374,32 @@ api: target_temp: float temp_step: int total_steps: int - slider_val: int temp_offset: int climate_icon: string + embedded_climate: bool then: - - wait_until: - binary_sensor.is_on: nextion_init - lambda: |- - id(disp1).send_command_printf("climateslider.maxval=%i", total_steps); - id(disp1).set_component_value("temp_offset", temp_offset); - id(disp1).set_component_value("temp_step", temp_step); - id(disp1).set_component_text_printf("current_temp", "%.1f°", current_temp); - id(disp1).show_component("current_temp"); - id(disp1).show_component("current_icon"); - if (target_temp > -999) + if (${verbose_log}) { - id(disp1).set_component_value("climateslider", slider_val); - id(disp1).set_component_text_printf("target_temp", "%.1f°", target_temp); - id(disp1).set_component_text_printf("target_icon", "%s", climate_icon.c_str()); - id(disp1).show_component("target_icon"); - id(disp1).show_component("target_temp"); - id(disp1).show_component("climateslider"); - id(disp1).show_component("decrease_temp"); - id(disp1).show_component("increase_temp"); - } - else - { - id(disp1).hide_component("target_icon"); - id(disp1).hide_component("target_temp"); - id(disp1).hide_component("climateslider"); - id(disp1).hide_component("decrease_temp"); - id(disp1).hide_component("increase_temp"); + ESP_LOGD("api.service.set_climate", "climateslider.maxval=%i", total_steps); + ESP_LOGD("api.service.set_climate", "temp_offset=%f", temp_offset); + ESP_LOGD("api.service.set_climate", "temp_step=%f", temp_step); + ESP_LOGD("api.service.set_climate", "current_temp=%f", current_temp); + ESP_LOGD("api.service.set_climate", "target_temp=%f", target_temp); + ESP_LOGD("api.service.set_climate", "target_icon=%s", climate_icon.c_str()); + ESP_LOGD("api.service.set_climate", "embedded=%i", (embedded_climate) ? 1 : 0); } + - script.execute: + id: set_climate + current_temp: !lambda "return current_temp;" + target_temp: !lambda "return target_temp;" + 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: @@ -510,25 +493,63 @@ api: id(disp1).send_command_printf("%s", entxcen.c_str()); } - ##### Service for localization of global vars ##### - - service: set_localization + ##### Service for transferring global settings from the blueprint to ESPHome ##### + - service: global_settings variables: + relay1_local_control: bool + relay1_icon: string + relay1_icon_color: int + relay2_local_control: bool + relay2_icon: string + relay2_icon_color: int + date_color: int time_format: string + time_color: int + embedded_climate: bool + wakeup_page: int then: + ## Logs - lambda: |- - ESP_LOGD("set_localization", "time_format: %s", time_format.c_str()); - id(mui_time_format) = time_format; - - *refresh_datetime + if (${verbose_log}) + { + ESP_LOGD("global_settings", "relay1_local_control: %i", (relay1_local_control) ? 1 : 0); + ESP_LOGD("global_settings", "relay2_local_control: %i", (relay2_local_control) ? 1 : 0); + ESP_LOGD("global_settings", "date_color: %i", date_color); + ESP_LOGD("global_settings", "time_format: %s", time_format.c_str()); + ESP_LOGD("global_settings", "time_color: %i", time_color); + ESP_LOGD("global_settings", "embedded_climate: %i", (embedded_climate and not (${embedded_thermostat_disabled})) ? 1 : 0); + } - ##### Service for setting relays as locally controlled ##### - - service: set_relay_local - variables: - relay1: bool - relay2: bool - then: + ## Relays - lambda: |- - id(relay1_local).publish_state(relay1); - id(relay2_local).publish_state(relay2); + id(relay1_local).publish_state(relay1_local_control); + id(relay2_local).publish_state(relay2_local_control); + id(home_relay1_icon) = relay1_icon.c_str(); + id(home_relay2_icon) = relay2_icon.c_str(); + id(home_relay1_icon_color) = relay1_icon_color; + id(home_relay2_icon_color) = relay2_icon_color; + + ## Localization + - lambda: id(mui_time_format) = time_format; + + ## Date/Time colors + - lambda: |- + id(home_date_color) = date_color; + id(home_time_color) = time_color; + + ## Embedded thermostat + - lambda: id(is_embedded_thermostat) = (embedded_climate and not (${embedded_thermostat_disabled})); + + ## Wakeup page + - lambda: id(wakeup_page_id) = wakeup_page; + + ## Refresh colors of global components + - script.execute: + id: refresh_colors + + ## Update home page + - script.execute: + id: update_page_home ##### START - GLOBALS CONFIGURATION ##### globals: @@ -556,11 +577,59 @@ globals: # type: std::string # restore_value: no # initial_value: '"%A, %d.%m"' + - id: home_date_color + type: int + restore_value: true + initial_value: '65535' + - id: mui_time_format type: std::string restore_value: no initial_value: '"%H:%M"' + - id: home_time_color + type: int + restore_value: true + initial_value: '65535' + + ##### Is embedded thermostat set as main climate entity? ##### + - id: is_embedded_thermostat + type: bool + restore_value: true + initial_value: 'false' + ##### Is embedded thermostat visible on climate page? ##### + - id: is_embedded_thermostat_visible + type: bool + restore_value: false + initial_value: 'false' + + ##### Relay icons ##### + - id: home_relay1_icon + type: std::string + restore_value: false + initial_value: '' + + - id: home_relay1_icon_color + type: int + restore_value: true + initial_value: '65535' + + - id: home_relay2_icon + type: std::string + restore_value: false + initial_value: '' + + - id: home_relay2_icon_color + type: int + restore_value: true + initial_value: '65535' + + ##### Wakeup page ID ##### + - id: wakeup_page_id + type: int + restore_value: true + initial_value: '0' + ##### START - BINARY SENSOR CONFIGURATION ##### binary_sensor: @@ -583,15 +652,8 @@ binary_sensor: api.connected: then: - switch.toggle: relay_1 - - if: - condition: - switch.is_on: relay_1 - then: - - lambda: id(disp1).send_command_printf("home.left_bt_pic.pic=78"); - - lambda: id(disp1).send_command_printf("home.icon_top_01","\U0000E3A5"); - else: - - lambda: id(disp1).send_command_printf("home.left_bt_pic.pic=77"); - - lambda: id(disp1).send_command_printf("home.icon_top_01","\U0000FFFF"); + - script.execute: + id: refresh_relays ##### RIGHT BUTTON BELOW DISPLAY TO TOGGLE RELAY ##### - name: ${device_name} Right Button @@ -612,15 +674,8 @@ binary_sensor: api.connected: then: - switch.toggle: relay_2 - - if: - condition: - switch.is_on: relay_2 - then: - - lambda: id(disp1).send_command_printf("home.right_bt_pic.pic=78"); - - lambda: id(disp1).send_command_printf("home.icon_top_02","\U0000E3A8"); - else: - - lambda: id(disp1).send_command_printf("home.right_bt_pic.pic=77"); - - lambda: id(disp1).send_command_printf("home.icon_top_02","\U0000FFFF"); + - script.execute: + id: refresh_relays ##### Restart NSPanel Button - Setting Page ##### - name: ${device_name} Restart @@ -663,28 +718,11 @@ binary_sensor: ##### API connection status - platform: status name: ${device_name} Status + id: api_status on_state: then: - - &update_wifi_icon - if: - condition: - wifi.connected: - then: - - if: - condition: - api.connected: - then: - - lambda: id(disp1).set_component_value("home.api",1); - - lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\U0000E5A8"); - - lambda: id(disp1).set_component_font_color("home.wifi_icon", 33808); - else: - - lambda: id(disp1).set_component_value("home.api",0); - - lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\U0000F256"); - - lambda: id(disp1).set_component_font_color("home.wifi_icon", 63488); - else: - - lambda: id(disp1).set_component_value("home.api",0); - - lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\U0000E5A9"); - - lambda: id(disp1).set_component_font_color("home.wifi_icon", 63488); + - script.execute: + id: refresh_wifi_icon ##### START - SENSOR CONFIGURATION ##### sensor: @@ -699,7 +737,8 @@ sensor: platform: wifi_signal update_interval: 60s on_value: - - *update_wifi_icon + - script.execute: + id: refresh_wifi_icon ##### INTERNAL TEMPERATURE SENSOR, ADC VALUE ##### - id: ntc_source @@ -768,6 +807,13 @@ sensor: ##### START - TEXT SENSOR CONFIGURATION ##### text_sensor: + ##### Current page name ##### + - name: ${device_name} current page + platform: template + id: current_page + internal: false + disabled_by_default: true + ##### ESPhome version used to compile the app ##### - platform: version name: ${device_name} ESPhome Version @@ -809,6 +855,141 @@ text_sensor: then: - lambda: |- id(page_timer)->execute(int(id(page_timeout).state)); + DynamicJsonDocument doc(1024); + deserializeJson(doc, x); + std::string page = doc["page"]; + std::string component = doc["component"]; + std::string value = doc["value"]; + std::string entity = doc["entity"]; + if (component=="currentpage") + { + ESP_LOGD("nspanelevent", "New page: %s", page.c_str()); + auto ha_event = new esphome::api::CustomAPIDevice(); + ha_event->fire_homeassistant_event("esphome.nspanel_pagechange", + { + {"page", page}, + {"entity", entity} + }); + id(current_page).publish_state(page); + if (page=="home") id(update_page_home).execute(); + else if (page=="screensaver") id(disp1).set_component_value("orign", id(wakeup_page_id)); + else if (page=="climate" and id(is_embedded_thermostat_visible)) id(update_page_climate); + } + else if (page=="boot" and component=="timeout" and stof(value) >= 5) id(disp1).send_command_printf("page %i", id(wakeup_page_id)); + + ##### NSPanel event - Execute actions from ESPHome - NO push to HA ##### + - name: ${device_name} NSPanel local event + platform: nextion + nextion_id: disp1 + id: disp1_local_event + component_name: localevent + internal: True + filters: + - lambda: |- + x = x.c_str(); + x.shrink_to_fit(); + return x; + on_value: + then: + - lambda: |- + id(page_timer)->execute(int(id(page_timeout).state)); + DynamicJsonDocument doc(1024); + deserializeJson(doc, x); + std::string domain = doc["domain"]; + std::string key = doc["key"]; + std::string value = doc["value"]; + std::string entity = doc["entity"]; + int embedded = doc["embedded"]; + if (${verbose_log}) + { + ESP_LOGD("text_sensor.localevent", "domain=%s", domain.c_str()); + ESP_LOGD("text_sensor.localevent", "key=%s", key.c_str()); + ESP_LOGD("text_sensor.localevent", "value=%s", value.c_str()); + ESP_LOGD("text_sensor.localevent", "entity=%s", entity.c_str()); + ESP_LOGD("text_sensor.localevent", "embedded=%i", embedded); + } + id(is_embedded_thermostat_visible) = (domain == "climate" and embedded == 1); + if (id(is_embedded_thermostat_visible) and (not ${embedded_thermostat_disabled})) + { + if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "Embedded thermostat is visible"); + auto call = id(thermostat_embedded).make_call(); + if (key == "set_temperature") + { + if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "set_temperature=%f", stof(value) / 10); + call.set_target_temperature(stof(value) / 10); + } + else if (key == "hvac_mode") + { + if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "set_mode=%s", value); + call.set_mode(value); + } + call.perform(); + } + else if (entity != "" and not entity.empty() and entity != "embedded_climate") + { + HomeassistantServiceResponse resp; + HomeassistantServiceMap resp_kv; + if (domain == "climate") + { + if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "HA controlled climate"); + if (key == "set_temperature") + { + resp.service = "climate.set_temperature"; + resp_kv.key = "temperature"; + resp_kv.value = to_string(stof(value) / 10); + if (${verbose_log}) + { + ESP_LOGD("text_sensor.localevent", "resp.service=%s", resp.service.c_str()); + ESP_LOGD("text_sensor.localevent", "resp_kv.key=%s", resp_kv.key.c_str()); + ESP_LOGD("text_sensor.localevent", "resp_kv.value=%s", resp_kv.value.c_str()); + } + resp.data.push_back(resp_kv); + } + else if (key == "hvac_mode") + { + resp.service = "climate.set_hvac_mode"; + resp_kv.key = "hvac_mode"; + resp_kv.value = value; + resp.data.push_back(resp_kv); + } + } + else if (domain == "cover") + { + if (key == "position") + { + resp.service = "cover.set_cover_position"; + resp_kv.key = key; + resp_kv.value = value; + resp.data.push_back(resp_kv); + } + else resp.service = std::string("cover.") + key.c_str(); + } + else if (domain == "fan") + { + if (key == "stop") + { + resp.service = "fan.turn_off"; + } + else + { + resp.service = "fan.turn_on"; + resp_kv.key = key; + resp_kv.value = value; + resp.data.push_back(resp_kv); + } + } + else if (domain == "light") + { + resp.service = "light.turn_on"; + resp_kv.key = key; + resp_kv.value = value; + resp.data.push_back(resp_kv); + } + resp_kv.key = "entity_id"; + resp_kv.value = entity; + resp.data.push_back(resp_kv); + id(api_server).send_homeassistant_service_call(resp); + } ##### touchevent sensor, Reset the page timeout ##### - id: disp1_touchevent @@ -1058,7 +1239,13 @@ display: - binary_sensor.template.publish: id: nextion_init state: true + - logger.log: "Nextion start - Prepare home page" + - script.execute: + id: refresh_colors + - lambda: id(home_relay1_icon) = "\uE3A5"; + - lambda: id(home_relay1_icon) = "\uE3A8"; - logger.log: "Nextion start - Done!" + #on_page: # Couldn't make this trigger to work, so used text_sensor nspanelevent and localevent instead ### Script for page_timer script: @@ -1067,15 +1254,417 @@ script: parameters: delay: int then: - - lambda: ESP_LOGD("nspanel", "start page-timer delay %i", int(id(page_timeout).state)); + - lambda: if (${verbose_log}) ESP_LOGD("script.page_timer", "start page-timer delay %i", int(id(page_timeout).state)); - delay: !lambda return delay *1000; - lambda: |- DynamicJsonDocument doc(1024); deserializeJson(doc, id(disp1_nspanel_event).state); std::string page = doc["page"]; - if (page == "home" or page == "screensaver" or page == "boot" or int(id(page_timeout).state) == 0) { - ESP_LOGD("nspanel", "no page-jump"); - } else { - ESP_LOGD("nspanel", "timer->home"); - id(disp1).send_command_printf("page 0"); - } + if (page == "home" or page == "screensaver" or page == "boot" or int(id(page_timeout).state) == 0) + { + if (${verbose_log}) ESP_LOGD("script.page_timer", "no page-jump"); + } + else + { + if (${verbose_log}) ESP_LOGD("script.page_timer", "timer->home"); + id(disp1).send_command_printf("page 0"); + } + + - id: set_climate + mode: restart + parameters: + current_temp: float + target_temp: float + temp_step: int + total_steps: int + temp_offset: int + climate_icon: string + embedded_climate: bool + then: + - if: + condition: + - binary_sensor.is_on: nextion_init + - text_sensor.state: # Is climate page visible? + id: current_page + state: 'climate' + then: + - lambda: |- + if (${verbose_log}) + { + ESP_LOGD("script.set_climate", "climateslider.maxval=%i", total_steps); + ESP_LOGD("script.set_climate", "temp_offset=%i", temp_offset); + ESP_LOGD("script.set_climate", "temp_step=%i", temp_step); + ESP_LOGD("script.set_climate", "current_temp=%f", current_temp); + ESP_LOGD("script.set_climate", "target_temp=%f", target_temp); + ESP_LOGD("script.set_climate", "target_icon=%s", climate_icon.c_str()); + ESP_LOGD("script.set_climate", "embedded=%i", (embedded_climate) ? 1 : 0); + } + id(is_embedded_thermostat_visible) = embedded_climate; + id(disp1).send_command_printf("climateslider.maxval=%i", total_steps); + id(disp1).set_component_value("temp_offset", temp_offset); + id(disp1).set_component_value("temp_step", temp_step); + id(disp1).set_component_text_printf("current_temp", "%.1f°", current_temp); + id(disp1).show_component("current_temp"); + id(disp1).show_component("current_icon"); + if (target_temp > -999) + { + float slider_val = round(((10*target_temp) - temp_offset) / temp_step); + if (${verbose_log}) ESP_LOGD("script.set_climate", "climateslider=%f", slider_val); + id(disp1).set_component_value("climateslider", slider_val); + id(disp1).set_component_text_printf("target_temp", "%.1f°", target_temp); + id(disp1).set_component_text_printf("target_icon", "%s", climate_icon.c_str()); + id(disp1).show_component("target_icon"); + id(disp1).show_component("target_temp"); + id(disp1).show_component("climateslider"); + id(disp1).show_component("decrease_temp"); + id(disp1).show_component("increase_temp"); + } + else + { + id(disp1).hide_component("target_icon"); + id(disp1).hide_component("target_temp"); + id(disp1).hide_component("climateslider"); + id(disp1).hide_component("decrease_temp"); + id(disp1).hide_component("increase_temp"); + } + id(disp1).set_component_value("embedded", (embedded_climate) ? 1 : 0); + + - id: refresh_colors ## Refresh colors of global components + mode: restart + then: + - if: + condition: + - binary_sensor.is_on: nextion_init + #- text_sensor.state: # Is home page visible? + # id: current_page + # state: 'home' + then: + - lambda: |- + id(disp1).set_component_font_color("home.date", id(home_date_color)); + id(disp1).set_component_font_color("home.time", id(home_time_color)); + id(disp1).set_component_font_color("home.icon_top_01", id(home_relay1_icon_color)); + id(disp1).set_component_font_color("home.icon_top_02", id(home_relay2_icon_color)); + + - id: refresh_datetime + mode: restart + then: + - if: + condition: + - binary_sensor.is_on: nextion_init + #- text_sensor.state: # Is home page visible? + # id: current_page + # state: 'home' + then: + - lambda: |- + std::string time_format_str = id(mui_time_format); + if (time_format_str.find("%p") != std::string::npos) + { + std::string meridiem_text = id(time_provider).now().strftime("%p"); + id(disp1).set_component_text_printf("home.meridiem", "%s", meridiem_text.c_str()); + } + else { id(disp1).set_component_text_printf("home.meridiem", " "); } + if (time_format_str.find("%-H") != std::string::npos) { time_format_str = time_format_str.replace(time_format_str.find("%-H"), sizeof("%-H")-1, to_string((int)(id(time_provider).now().hour))); } + if (time_format_str.find("%-I") != std::string::npos) + { + if (id(time_provider).now().hour>12) + { + time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, to_string((int)(id(time_provider).now().hour-12))); + } + else if (id(time_provider).now().hour==0) + { + time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, "12"); + } + else + { + time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, to_string((int)(id(time_provider).now().hour))); + } + } + std::string time_text = id(time_provider).now().strftime(time_format_str); + id(disp1).set_component_text_printf("home.time", "%s", time_text.c_str()); + + - id: refresh_relays + mode: restart + then: + - if: + condition: + - binary_sensor.is_on: nextion_init + #- text_sensor.state: # Is home page visible? + # id: current_page + # state: 'home' + then: + - lambda: |- + // Chips - Relays + if (id(relay_1).state) id(disp1).set_component_text_printf("home.icon_top_01", "%s", id(home_relay1_icon).c_str()); + else id(disp1).set_component_text_printf("home.icon_top_01", "\uFFFF"); + if (id(relay_2).state) id(disp1).set_component_text_printf("home.icon_top_02", "%s", id(home_relay2_icon).c_str()); + else id(disp1).set_component_text_printf("home.icon_top_02", "\uFFFF"); + // Hardware buttons - Fallback mode + if (id(relay_1).state or (id(relay1_fallback).state and not id(api_status).state)) id(disp1).send_command_printf("home.left_bt_pic.pic=%i", (id(relay_1).state) ? 78 : 77); + if (id(relay_2).state or (id(relay2_fallback).state and not id(api_status).state)) id(disp1).send_command_printf("home.right_bt_pic.pic=%i", (id(relay_2).state) ? 78 : 77); + + - id: refresh_chips_climate + mode: restart + then: + - if: + condition: + - binary_sensor.is_on: nextion_init + - lambda: !lambda 'return (not ${embedded_thermostat_disabled});' + - lambda: !lambda 'return id(is_embedded_thermostat);' + then: + - lambda: |- + if (${verbose_log}) ESP_LOGD("script.refresh_chips_climate", "thermostat_embedded.action=%i", int(id(thermostat_embedded).action)); + switch (int(id(thermostat_embedded).action)) // CLIMATE_ACTION_OFF = 0, CLIMATE_ACTION_COOLING = 2, CLIMATE_ACTION_HEATING = 3, CLIMATE_ACTION_IDLE = 4, CLIMATE_ACTION_DRYING = 5, CLIMATE_ACTION_FAN = 6 + { + case 0: //CLIMATE_ACTION_OFF + if (${verbose_log}) ESP_LOGD("script.refresh_chips_climate", "thermostat_embedded.mode=%i", int(id(thermostat_embedded).mode)); + switch (int(id(thermostat_embedded).mode)) // CLIMATE_MODE_OFF = 0, CLIMATE_MODE_HEAT_COOL = 1, CLIMATE_MODE_COOL = 2, CLIMATE_MODE_HEAT = 3, CLIMATE_MODE_FAN_ONLY = 4, CLIMATE_MODE_DRY = 5, CLIMATE_MODE_AUTO = 6 + { + case 0: //CLIMATE_MODE_OFF + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uFFFF"); // (E424) Don't show icon when off + id(disp1).set_component_font_color("home.icon_top_03", 35921); + break; + case 1: //CLIMATE_MODE_HEAT_COOL + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE069"); + id(disp1).set_component_font_color("home.icon_top_03", 35921); + break; + case 2: //CLIMATE_MODE_COOL + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE716"); + id(disp1).set_component_font_color("home.icon_top_03", 35921); + break; + case 3: //CLIMATE_MODE_HEAT + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE237"); + id(disp1).set_component_font_color("home.icon_top_03", 35921); + break; + case 4: //CLIMATE_MODE_FAN_ONLY + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE20F"); + id(disp1).set_component_font_color("home.icon_top_03", 35921); + break; + case 5: //CLIMATE_MODE_DRY + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE58D"); + id(disp1).set_component_font_color("home.icon_top_03", 35921); + break; + case 6: //CLIMATE_MODE_AUTO + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uEE8D"); + id(disp1).set_component_font_color("home.icon_top_03", 35921); + break; + } + case 2: //CLIMATE_ACTION_COOLING + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE716"); + id(disp1).set_component_font_color("home.icon_top_03", 1055); + break; + case 3: //CLIMATE_ACTION_HEATING + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE237"); + id(disp1).set_component_font_color("home.icon_top_03", 64164); + break; + case 4: //CLIMATE_ACTION_IDLE + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE50E"); // mdi:thermometer + id(disp1).set_component_font_color("home.icon_top_03", 35921); + break; + case 5: //CLIMATE_ACTION_DRYING + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE58D"); + id(disp1).set_component_font_color("home.icon_top_03", 64704); + break; + case 6: //CLIMATE_ACTION_FAN + id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE20F"); + id(disp1).set_component_font_color("home.icon_top_03", 1530); + break; + } + + - id: refresh_wifi_icon + mode: restart + then: + - if: + condition: + - binary_sensor.is_on: nextion_init + #- text_sensor.state: # Is home page visible? + # id: current_page + # state: 'home' + then: + # Update Wi-Fi icon + - if: + condition: + wifi.connected: + then: + - if: + condition: + api.connected: + then: + - lambda: id(disp1).set_component_value("home.api",1); + - lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\uE5A8"); + - lambda: id(disp1).set_component_font_color("home.wifi_icon", 33808); + else: + - lambda: id(disp1).set_component_value("home.api",0); + - lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\uF256"); + - lambda: id(disp1).set_component_font_color("home.wifi_icon", 63488); + else: + - lambda: id(disp1).set_component_value("home.api",0); + - lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\uE5A9"); + - lambda: id(disp1).set_component_font_color("home.wifi_icon", 63488); + + - id: update_page_home + mode: restart + then: + - script.execute: + id: refresh_datetime + - script.execute: + id: refresh_relays + - script.execute: + id: refresh_wifi_icon + - script.execute: + id: refresh_chips_climate + - if: + condition: + - binary_sensor.is_on: nextion_init + - text_sensor.state: # Is home page visible? + id: current_page + state: 'home' + then: + - lambda: |- + // Update home.entity variable + if (id(is_embedded_thermostat) and not (${embedded_thermostat_disabled})) id(disp1).set_component_text_printf("home.entity", "embedded_climate"); + else id(disp1).set_component_text_printf("home.entity", ""); + + - id: update_page_climate + mode: restart + then: + - if: + condition: + - binary_sensor.is_on: nextion_init + - text_sensor.state: # Is climate page visible? + id: current_page + state: 'climate' + - lambda: !lambda return id(is_embedded_thermostat_visible); + - lambda: !lambda 'return (not ${embedded_thermostat_disabled});' + then: # Embedded thermostat is visible + # Update slider, current temperature & target temperature + - script.execute: + id: set_climate + current_temp: !lambda "return id(thermostat_embedded).current_temperature;" + target_temp: !lambda "return id(thermostat_embedded).target_temperature;" + temp_step: !lambda "return int(round(${embedded_thermostat_visual_temperature_step}*10));" + total_steps: !lambda |- + float temp_step = ${embedded_thermostat_visual_temperature_step}; + float temp_offset = ${embedded_thermostat_visual_min_temperature}; + float temp_max = ${embedded_thermostat_visual_max_temperature}; + float total_steps = (temp_max-temp_offset)/temp_step; + return int(round(total_steps)); + slider_val: !lambda |- + float temp_step = ${embedded_thermostat_visual_temperature_step}; + float temp_offset = ${embedded_thermostat_visual_min_temperature}; + return int(round((10*id(thermostat_embedded).target_temperature-temp_offset)/temp_step)); + temp_offset: !lambda "return int(round(${embedded_thermostat_visual_min_temperature}*10));" + climate_icon: "" + embedded_climate: True + + # Update target temp icon + - lambda: |- + if (${verbose_log}) ESP_LOGD("script.update_page_climate", "thermostat_embedded.action=%i", int(id(thermostat_embedded).action)); + switch (int(id(thermostat_embedded).action)) // CLIMATE_ACTION_OFF = 0, CLIMATE_ACTION_COOLING = 2, CLIMATE_ACTION_HEATING = 3, CLIMATE_ACTION_IDLE = 4, CLIMATE_ACTION_DRYING = 5, CLIMATE_ACTION_FAN = 6 + { + case 0: //CLIMATE_ACTION_OFF + if (${verbose_log}) ESP_LOGD("script.update_page_climate", "thermostat_embedded.mode=%i", int(id(thermostat_embedded).mode)); + switch (int(id(thermostat_embedded).mode)) // CLIMATE_MODE_OFF = 0, CLIMATE_MODE_HEAT_COOL = 1, CLIMATE_MODE_COOL = 2, CLIMATE_MODE_HEAT = 3, CLIMATE_MODE_FAN_ONLY = 4, CLIMATE_MODE_DRY = 5, CLIMATE_MODE_AUTO = 6 + { + case 0: //CLIMATE_MODE_OFF + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uFFFF"); // (E424) Don't show icon when off + id(disp1).set_component_font_color("climate.target_icon", 35921); + break; + case 1: //CLIMATE_MODE_HEAT_COOL + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE069"); + id(disp1).set_component_font_color("climate.target_icon", 35921); + break; + case 2: //CLIMATE_MODE_COOL + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE716"); + id(disp1).set_component_font_color("climate.target_icon", 1055); + break; + case 3: //CLIMATE_MODE_HEAT + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE237"); + id(disp1).set_component_font_color("climate.target_icon", 64164); + break; + case 4: //CLIMATE_MODE_FAN_ONLY + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE20F"); + id(disp1).set_component_font_color("climate.target_icon", 35921); + break; + case 5: //CLIMATE_MODE_DRY + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE58D"); + id(disp1).set_component_font_color("climate.target_icon", 64704); + break; + case 6: //CLIMATE_MODE_AUTO + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uEE8D"); + id(disp1).set_component_font_color("climate.target_icon", 35921); + break; + } + case 2: //CLIMATE_ACTION_COOLING + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE716"); + id(disp1).set_component_font_color("climate.target_icon", 1055); + break; + case 3: //CLIMATE_ACTION_HEATING + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE237"); + id(disp1).set_component_font_color("climate.target_icon", 64164); + break; + case 4: //CLIMATE_ACTION_IDLE + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE50E"); // mdi:thermometer + id(disp1).set_component_font_color("climate.target_icon", 35921); + break; + case 5: //CLIMATE_ACTION_DRYING + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE58D"); + id(disp1).set_component_font_color("climate.target_icon", 64704); + break; + case 6: //CLIMATE_ACTION_FAN + id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE20F"); + id(disp1).set_component_font_color("climate.target_icon", 1530); + break; + } + + # Update buttons bar + - lambda: |- + if (${verbose_log}) ESP_LOGD("script.update_page_climate", "Updating buttons bar"); + // Hide not supported hotspots + id(disp1).hide_component("climate.button01"); + id(disp1).hide_component("climate.button02"); + id(disp1).show_component("climate.button03"); //Heat + id(disp1).hide_component("climate.button04"); + id(disp1).hide_component("climate.button05"); + id(disp1).hide_component("climate.button06"); + id(disp1).show_component("climate.button07"); //Off + // Set buttons colors + id(disp1).set_component_font_color("climate.button01_icon", 10597); + id(disp1).set_component_font_color("climate.button02_icon", 10597); + id(disp1).set_component_font_color("climate.button03_icon", (id(thermostat_embedded).mode==climate::CLIMATE_MODE_HEAT) ? 64164 : 48631); + id(disp1).set_component_font_color("climate.button04_icon", 10597); + id(disp1).set_component_font_color("climate.button05_icon", 10597); + id(disp1).set_component_font_color("climate.button06_icon", 10597); + id(disp1).set_component_font_color("climate.button07_icon", (id(thermostat_embedded).mode==climate::CLIMATE_MODE_OFF) ? 35921 : 48631); + +climate: + - platform: thermostat + name: ${device_name} Thermostat + id: thermostat_embedded + sensor: temp_nspanel + min_heating_off_time: ${embedded_thermostat_min_heating_off_time}s + min_heating_run_time: ${embedded_thermostat_min_heating_run_time}s + min_idle_time: ${embedded_thermostat_min_idle_time}s + visual: + min_temperature: ${embedded_thermostat_visual_min_temperature} ${embedded_thermostat_temp_units} + max_temperature: ${embedded_thermostat_visual_max_temperature} ${embedded_thermostat_temp_units} + temperature_step: ${embedded_thermostat_visual_temperature_step} ${embedded_thermostat_temp_units} + # target_temperature: 0.5 #!lambda "return ${embedded_thermostat_visual_target_temperature_step};" + # current_temperature: 0.1 #!lambda "return ${embedded_thermostat_visual_current_temperature_step};" + heat_action: + - switch.turn_on: relay_${embedded_thermostat_heater_relay} + idle_action: + - switch.turn_off: relay_${embedded_thermostat_heater_relay} + default_preset: "Off" + on_boot_restore_from: memory + preset: + - name: "Off" + default_target_temperature_low: ${embedded_thermostat_visual_min_temperature} ${embedded_thermostat_temp_units} + mode: "off" + - name: Home + default_target_temperature_low: 21 ${embedded_thermostat_temp_units} + internal: ${embedded_thermostat_disabled} + on_state: + - logger.log: Climate state changed - Start + - script.execute: + id: update_page_climate + - logger.log: Climate state changed - End diff --git a/nspanel_eu.HMI b/nspanel_eu.HMI index 931d30a..90146fd 100644 Binary files a/nspanel_eu.HMI and b/nspanel_eu.HMI differ diff --git a/nspanel_eu.tft b/nspanel_eu.tft index 194604c..e32dffc 100644 Binary files a/nspanel_eu.tft and b/nspanel_eu.tft differ diff --git a/nspanel_eu_code/cover.txt b/nspanel_eu_code/cover.txt index 5e930e7..cf0ae97 100644 --- a/nspanel_eu_code/cover.txt +++ b/nspanel_eu_code/cover.txt @@ -141,9 +141,9 @@ Slider coverslider Touch Release Event covx coverslider.val,va1.txt,0,0 cover_value.txt=va1.txt+"%" - coversetting.txt="{\"page\": \"cover\", \"component\": \"cover_position\", \"value\": "+va1.txt+", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"position\", \"value\": "+va1.txt+", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -161,9 +161,9 @@ Button cover_open Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"open_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"open_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -181,9 +181,9 @@ Button cover_close Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"close_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"close_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -201,9 +201,9 @@ Button cover_stop Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"stop_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"stop_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 diff --git a/nspanel_us.HMI b/nspanel_us.HMI index 51bea4b..6475fd0 100644 Binary files a/nspanel_us.HMI and b/nspanel_us.HMI differ diff --git a/nspanel_us.tft b/nspanel_us.tft index 780cf28..01dc86c 100644 Binary files a/nspanel_us.tft and b/nspanel_us.tft differ diff --git a/nspanel_us_code/cover.txt b/nspanel_us_code/cover.txt index 5e930e7..cf0ae97 100644 --- a/nspanel_us_code/cover.txt +++ b/nspanel_us_code/cover.txt @@ -141,9 +141,9 @@ Slider coverslider Touch Release Event covx coverslider.val,va1.txt,0,0 cover_value.txt=va1.txt+"%" - coversetting.txt="{\"page\": \"cover\", \"component\": \"cover_position\", \"value\": "+va1.txt+", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"position\", \"value\": "+va1.txt+", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -161,9 +161,9 @@ Button cover_open Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"open_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"open_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -181,9 +181,9 @@ Button cover_close Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"close_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"close_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -201,9 +201,9 @@ Button cover_stop Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"stop_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"stop_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 diff --git a/nspanel_us_land.HMI b/nspanel_us_land.HMI index 81be635..a832e11 100644 Binary files a/nspanel_us_land.HMI and b/nspanel_us_land.HMI differ diff --git a/nspanel_us_land.tft b/nspanel_us_land.tft index 3bc5db4..99d7dc1 100644 Binary files a/nspanel_us_land.tft and b/nspanel_us_land.tft differ diff --git a/nspanel_us_land_code/cover.txt b/nspanel_us_land_code/cover.txt index 5e930e7..cf0ae97 100644 --- a/nspanel_us_land_code/cover.txt +++ b/nspanel_us_land_code/cover.txt @@ -141,9 +141,9 @@ Slider coverslider Touch Release Event covx coverslider.val,va1.txt,0,0 cover_value.txt=va1.txt+"%" - coversetting.txt="{\"page\": \"cover\", \"component\": \"cover_position\", \"value\": "+va1.txt+", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"position\", \"value\": "+va1.txt+", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -161,9 +161,9 @@ Button cover_open Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"open_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"open_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -181,9 +181,9 @@ Button cover_close Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"close_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"close_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 @@ -201,9 +201,9 @@ Button cover_stop Events Touch Press Event - coversetting.txt="{\"page\": \"cover\", \"component\": \"stop_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" + coversetting.txt="{\"domain\": \"cover\", \"key\": \"stop_cover\", \"value\": \"press\", \"entity\": \""+home.entity.txt+"\"}" printh 92 - prints "nspanelevent",0 + prints "localevent",0 printh 00 prints coversetting.txt,0 printh 00 diff --git a/nspanel_us_land_code/instructions.md b/nspanel_us_land_code/instructions.md index 7a21753..32378ff 100644 --- a/nspanel_us_land_code/instructions.md +++ b/nspanel_us_land_code/instructions.md @@ -1,4 +1,4 @@ -# Creating `nspanel_us_land.hmi` from `nspanel_eu.hmi`: +# Creating `nspanel_us_land.HMI` from `nspanel_eu.HMI`: ## Program.s: Comment out `lcd_dev fffb 0002 0000 0020`