diff --git a/nspanel_blueprint.yaml b/nspanel_blueprint.yaml index 619ceb8..eb63b42 100644 --- a/nspanel_blueprint.yaml +++ b/nspanel_blueprint.yaml @@ -843,19 +843,6 @@ blueprint: default: ' ' selector: *placeholder-selector ##### PLACEHOLDER ###################################################################### - hold_delay: - name: Delay for HW-Button hold in seconds - VALUE - description: > - *SYSTEM settings* - - *Time in seconds to detect a hold*' - default: 1.0 - selector: - number: - min: 1.0 - max: 10.0 - step: 1.0 - unit_of_measurement: seconds left_button_entity: name: Left hardware button - ENTITY (Optional) description: > @@ -3478,6 +3465,7 @@ trigger_variables: right_button: 'binary_sensor.{{ nspanel_name }}_right_button' nextion_inited: 'binary_sensor.{{ nspanel_name }}_nextion_display' nspanelevent: 'sensor.{{ nspanel_name }}_nspanel_event' + currentpage: 'sensor.{{ nspanel_name }}_current_page' relay01_entity: 'switch.{{ nspanel_name }}_relay_1' relay02_entity: 'switch.{{ nspanel_name }}_relay_2' nspaneltemp: 'sensor.{{ nspanel_name }}_temperature' @@ -3617,6 +3605,7 @@ variables: unavailable: "" page: + current: '{{ states(currentpage) }}' home: "home" weatherpages: - "weather01" @@ -5517,6 +5506,14 @@ variables: trigger: ##### Trigger - General ################################################################################################################# + + ##### Panel Event - This is the new main event handler introduced on v4.0 to get info from the panel ##### + - id: nspanel_event + platform: event + event_type: esphome.nspanel_ha_blueprint + event_data: + device_id: !input 'nspanel_name' + ##### Reboot - Trigger 'nspanel_boot_init' ##### - platform: template value_template: '{{ is_state(nextion_inited, "on") | default(false) if nextion_inited is string else false }}' @@ -5524,20 +5521,6 @@ 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 service call ##### - - platform: event - event_type: esphome.nspanel_service_call - event_data: - device_id: !input 'nspanel_name' - id: service_call - ##### NSPanel event changed ##### - platform: event event_type: state_changed @@ -6047,16 +6030,6 @@ trigger: ##### Trigger - Hardware buttons ################################################################################################################# - ##### Left Button - Trigger 'left_button_press' ##### - - platform: template - value_template: '{{ is_state(left_button, "on") | default(false) if left_button is string else false }}' - id: left_button_press - - ##### Right Button - Trigger 'right_button_press' ##### - - platform: template - value_template: '{{ is_state(right_button, "on") | default(false) if right_button is string else false }}' - id: right_button_press - ##### Left Button - State 'left_button_state' ##### - platform: state entity_id: !input 'left_button_entity' @@ -6168,2248 +6141,2377 @@ condition: action: - alias: Main choices choose: - ##### DATE AND TIME ##### - - alias: 'Date & Time' + ##### DATE ##### + - alias: 'Date' conditions: - condition: trigger id: time_state sequence: - - &refresh-datetime - if: '{{ true }}' - then: - ##### NSPanel Date ##### - - service: '{{ nextion.command.text_printf }}' - data: - component: home.date - message: > - {{ - as_timestamp(now()) - | timestamp_custom - ( - date_format - | replace("%A", (dict.values(mui[language].weekdays) | list)[now().weekday()]) - | replace("%a", (dict.values(mui[language].weekdays_short) | list)[now().weekday()]) - | replace("%B", (dict.values(mui[language].months) | list)[now().month-1]) - | replace("%b", (dict.values(mui[language].months_short) | list)[now().month-1]) - ) - }} - continue_on_error: true - - ##### BOOT NSPANEL - boot init ##### - - alias: Boot init - conditions: - - condition: trigger - id: nspanel_boot_init - - '{{ nspanel_event.page == page.boot }}' - sequence: &boot_init_sequence - ##### NSPanel boot init only ##### - - delay: - milliseconds: 100 - - alias: Publish blueprint version + ##### NSPanel Date ##### + - &refresh-date service: '{{ nextion.command.text_printf }}' data: - component: boot.bluep_version - message: '{{ blueprint_version }}' - continue_on_error: true - - &delay-default - delay: - milliseconds: '{{ delay_value }}' - - - &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' - - - &variables-home_buttons - variables: - bt_entities: - enabled: !input 'entitypages_enabled' - icon: !input 'home_button06_icon' #EDCF - color_rgb: !input 'home_button06_icon_color' - bt_qrcode: - enabled: !input 'qrcode_enabled' - icon: !input 'home_button05_icon' #E432 - color_rgb: !input 'home_button05_icon_color' - title: !input 'qrcode_label' - qrcode: !input 'qrcode_value' - - - &variables-date_time - variables: - display: - date: - format: - color_rgb: !input 'date_label_color' - time: - format: !input 'time_format' - color_rgb: !input 'time_label_color' - - - service: '{{ nextion.command.page_home_settings }}' - data: - qrcode: '{{ bt_qrcode.enabled }}' - qrcode_icon: > - {{ - all_icons[bt_qrcode.icon.split(":")[1]] | default(bt_qrcode.icon - if bt_qrcode.icon is string - else all_icons["format-list-bulleted-square"]) - }} - qrcode_icon_color: > - {{ - bt_qrcode.color_rgb - if is_number(bt_qrcode.color_rgb) - else - ((bt_qrcode.color_rgb[0] //(2**3)) *(2**11))+ - ((bt_qrcode.color_rgb[1] //(2**2)) *(2**5))+ - (bt_qrcode.color_rgb[2] //(2**3)) - }} - entities_pages: '{{ bt_entities.enabled }}' - entities_pages_icon: > - {{ - all_icons[bt_entities.icon.split(":")[1]] | default(bt_entities.icon - if bt_entities.icon is string - else all_icons["format-list-bulleted-square"]) - }} - entities_pages_icon_color: > - {{ - bt_entities.color_rgb - if is_number(bt_entities.color_rgb) - else - ((bt_entities.color_rgb[0] //(2**3)) *(2**11))+ - ((bt_entities.color_rgb[1] //(2**2)) *(2**5))+ - (bt_entities.color_rgb[2] //(2**3)) - }} - alarm_state: > - {{ - states(alarm) | default("") - if alarm is string and alarm | length > 0 - else "" - }} - continue_on_error: true - - *delay-default - - - service: '{{ nextion.command.qrcode }}' - data: - title: '{{ bt_qrcode.title }}' - qrcode: '{{ bt_qrcode.qrcode }}' - show: false - continue_on_error: true - - *delay-default - - - 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: > - {{ - display.date.color_rgb - if is_number(display.date.color_rgb) - else ((display.date.color_rgb[0] //(2**3)) *(2**11))+((display.date.color_rgb[1] //(2**2)) *(2**5))+(display.date.color_rgb[2] //(2**3)) - }} - time_format: '{{ display.time.format }}' - time_color: > - {{ - display.time.color_rgb - if is_number(display.time.color_rgb) - else ((display.time.color_rgb[0] //(2**3)) *(2**11))+((display.time.color_rgb[1] //(2**2)) *(2**5))+(display.time.color_rgb[2] //(2**3)) - }} - embedded_climate: '{{ embedded_climate }}' - wakeup_page: '{{ wakeup_page }}' - continue_on_error: true - - *delay-default - - - &clear_notification - service: '{{ nextion.command.notification_clear }}' - data: {} - continue_on_error: true - - *delay-default - - ###### NSPanel beep ###### - - delay: - milliseconds: 2000 - - if: '{{ is_state(notification_sound, "on") }}' - then: - - service: '{{ nextion.command.play_rtttl }}' - data: - song_str: 'two short:d=4,o=5,b=100:16e6,16e6' - continue_on_error: true - - ##### Update Date & Time before showing the Home page ##### - - *refresh-datetime - - ##### NSPanel boot init finished and jump to Home Page##### - - *variables_wakeup_page - - *delay-default - - &jump_wakeup_page - service: '{{ nextion.command.printf }}' - data: - cmd: "page {{ wakeup_page }}" + component: home.date + message: > + {{ + as_timestamp(now()) + | timestamp_custom + ( + date_format + | replace("%A", (dict.values(mui[language].weekdays) | list)[now().weekday()]) + | replace("%a", (dict.values(mui[language].weekdays_short) | list)[now().weekday()]) + | replace("%B", (dict.values(mui[language].months) | list)[now().month-1]) + | replace("%b", (dict.values(mui[language].months_short) | list)[now().month-1]) + ) + }} continue_on_error: true - ##### Service call ##### - - alias: NSPanel service call + ##### NSPanel event ##### + - alias: NSPanel event conditions: - condition: trigger - id: service_call - - '{{ trigger.event.data.service is defined and trigger.event.data.service is string and trigger.event.data.service | length > 0 }}' - - '{{ trigger.event.data.service is not match "alarm_control_panel." }}' # Prevent the use of this call for alarm control due to safety reasons - - '{{ trigger.event.data.entity is defined and trigger.event.data.entity is string and trigger.event.data.entity | length > 0 }}' + id: nspanel_event sequence: - &variable_nspanel_event variables: nspanel_event: '{{ trigger.event.data }}' - - if: '{{ nspanel_event.key is defined and nspanel_event.key is string and nspanel_event.key | length > 0 }}' - then: - - service: '{{ nspanel_event.service }}' - data: { "{{ nspanel_event.key }}": "{{ nspanel_event.value }}" } - target: - entity_id: '{{ nspanel_event.entity }}' - continue_on_error: true - else: - - service: '{{ nspanel_event.service }}' - target: - entity_id: '{{ nspanel_event.entity }}' - continue_on_error: true - - ##### Page changed ##### - - alias: Page changed - conditions: - - condition: trigger - id: page_changed - sequence: - - *variable_nspanel_event - choose: - ## PAGE HOME ## - - alias: Home page - conditions: '{{ nspanel_event.page == page.home }}' - sequence: &refresh_page_home - ##### Set entity variable ##### - - &set_entity_variable + ##### BOOT NSPANEL - boot init ##### + - alias: Boot init + conditions: + - '{{ nspanel_event.type == "boot"}}' + - '{{ nspanel_event.step is defined and nspanel_event.step in ["nextion_init", "timeout"]}}' + sequence: &boot_init_sequence + ##### NSPanel boot init only ##### + - delay: + milliseconds: 100 + - alias: Publish blueprint version service: '{{ nextion.command.text_printf }}' data: - component: home.entity - message: '{{ climate }}' + component: boot.bluep_version + message: '{{ blueprint_version }}' continue_on_error: true + - &delay-default + delay: + milliseconds: '{{ delay_value }}' - ##### Weather Icon Home Page ##### + - &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' + + - &variables-home_buttons + variables: + bt_entities: + enabled: !input 'entitypages_enabled' + icon: !input 'home_button06_icon' #EDCF + color_rgb: !input 'home_button06_icon_color' + bt_qrcode: + enabled: !input 'qrcode_enabled' + icon: !input 'home_button05_icon' #E432 + color_rgb: !input 'home_button05_icon_color' + title: !input 'qrcode_label' + qrcode: !input 'qrcode_value' + + - &variables-date_time + variables: + display: + date: + format: + color_rgb: !input 'date_label_color' + time: + format: !input 'time_format' + color_rgb: !input 'time_label_color' + + - service: '{{ nextion.command.page_home_settings }}' + data: + qrcode: '{{ bt_qrcode.enabled }}' + qrcode_icon: > + {{ + all_icons[bt_qrcode.icon.split(":")[1]] | default(bt_qrcode.icon + if bt_qrcode.icon is string + else all_icons["format-list-bulleted-square"]) + }} + qrcode_icon_color: > + {{ + bt_qrcode.color_rgb + if is_number(bt_qrcode.color_rgb) + else + ((bt_qrcode.color_rgb[0] //(2**3)) *(2**11))+ + ((bt_qrcode.color_rgb[1] //(2**2)) *(2**5))+ + (bt_qrcode.color_rgb[2] //(2**3)) + }} + entities_pages: '{{ bt_entities.enabled }}' + entities_pages_icon: > + {{ + all_icons[bt_entities.icon.split(":")[1]] | default(bt_entities.icon + if bt_entities.icon is string + else all_icons["format-list-bulleted-square"]) + }} + entities_pages_icon_color: > + {{ + bt_entities.color_rgb + if is_number(bt_entities.color_rgb) + else + ((bt_entities.color_rgb[0] //(2**3)) *(2**11))+ + ((bt_entities.color_rgb[1] //(2**2)) *(2**5))+ + (bt_entities.color_rgb[2] //(2**3)) + }} + alarm_state: > + {{ + states(alarm) | default("") + if alarm is string and alarm | length > 0 + else "" + }} + continue_on_error: true + - *delay-default + + - service: '{{ nextion.command.qrcode }}' + data: + title: '{{ bt_qrcode.title }}' + qrcode: '{{ bt_qrcode.qrcode }}' + show: false + continue_on_error: true + - *delay-default + + - 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: > + {{ + display.date.color_rgb + if is_number(display.date.color_rgb) + else ((display.date.color_rgb[0] //(2**3)) *(2**11))+((display.date.color_rgb[1] //(2**2)) *(2**5))+(display.date.color_rgb[2] //(2**3)) + }} + time_format: '{{ display.time.format }}' + time_color: > + {{ + display.time.color_rgb + if is_number(display.time.color_rgb) + else ((display.time.color_rgb[0] //(2**3)) *(2**11))+((display.time.color_rgb[1] //(2**2)) *(2**5))+(display.time.color_rgb[2] //(2**3)) + }} + embedded_climate: '{{ embedded_climate }}' + wakeup_page: '{{ wakeup_page }}' + continue_on_error: true + - *delay-default + + - &clear_notification + service: '{{ nextion.command.notification_clear }}' + data: {} + continue_on_error: true - *delay-default - - &refresh-page_home-weather_pic + + ###### NSPanel beep ###### + - delay: + milliseconds: 2000 + - if: '{{ is_state(notification_sound, "on") }}' + then: + - service: '{{ nextion.command.play_rtttl }}' + data: + song_str: 'two short:d=4,o=5,b=100:16e6,16e6' + continue_on_error: true + + ##### Update Date & Time before showing the Home page ##### + - *refresh-date + + ##### NSPanel boot init finished and jump to Home Page##### + - *variables_wakeup_page + - *delay-default + - &jump_wakeup_page 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) }} + cmd: "page {{ wakeup_page }}" continue_on_error: true - - &refresh-page_home-outdoor_temp - if: '{{ true }}' - then: - ##### NSPanel Outdoor Temp ##### - - variables: - outdoor_temp_entity: !input 'outdoortemp' - outdoor_temp_state: > - {{ - states(outdoor_temp_entity, rounded=true) | default("unavailable") - if outdoor_temp_entity is string and outdoor_temp_entity is match "sensor." and has_value(outdoor_temp_entity) - 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: - - variables: - outdoor_temp_color_rgb: !input 'home_outdoor_temp_label_color' - ### LABEL Outdoor Temp Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.outdoor_temp - message: > - {{ - outdoor_temp_color_rgb - if is_number(outdoor_temp_color_rgb) - else ((outdoor_temp_color_rgb[0] //(2**3)) *(2**11))+((outdoor_temp_color_rgb[1] //(2**2)) *(2**5))+(outdoor_temp_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_entity: !input 'indoortemp' - indoor_temp: - state: '{{ states(indoor_temp_entity, rounded=true) | default("unavailable") if indoor_temp_entity is string and indoor_temp_entity is match "sensor." else states(nspaneltemp, rounded=true) }}' - units: > - {{ - state_attr(indoor_temp_entity, "unit_of_measurement") | default(temperature_units) - if - indoor_temp_entity is string and - indoor_temp_entity is match "sensor." and - state_attr(indoor_temp_entity, "unit_of_measurement") is string and - state_attr(indoor_temp_entity, "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 - ) - }} - icon: - icon: !input 'home_indoor_temp_icon' #E50E - color_rgb: !input 'home_indoor_temp_icon_color' - label: - color_rgb: !input 'home_indoor_temp_label_color' - - - if: '{{ is_number(indoor_temp.state) }}' - then: - ### ICON Indoor Temp Font Color ### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.indoortempicon - message: > - {{ - indoor_temp.icon.color_rgb - if is_number(indoor_temp.icon.color_rgb) - else ((indoor_temp.icon.color_rgb[0] //(2**3)) *(2**11))+((indoor_temp.icon.color_rgb[1] //(2**2)) *(2**5))+(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[indoor_temp.icon.icon.split(":")[1]] | default(all_icons.unknown) - if indoor_temp.icon.icon.split(":") | count > 0 - else - ( - indoor_temp.icon.icon - if 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: > - {{ - indoor_temp.label.color_rgb - if is_number(indoor_temp.label.color_rgb) - else ((indoor_temp.label.color_rgb[0] //(2**3)) *(2**11))+((indoor_temp.label.color_rgb[1] //(2**2)) *(2**5))+(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 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(all_icons.unknown) }} - {% else %}{{ all_icons.blank }} - {% 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: - bt_notific: - icon: !input 'home_button04_icon' #E1ED - color_rgb: - "on": !input 'home_button04_icon_color01' - "off": !input 'home_button04_icon_color02' - notification_text_state: '{{ states(notification_text) | default(None) if notification_text is string else None }}' - set_button04_icon: > - {{ - all_icons[bt_notific.icon.split(":")[1]] | default(bt_notific.icon if bt_notific.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: > - {{ - ( - bt_notific.color_rgb[notification_unread_state] - if is_number(bt_notific.color_rgb[notification_unread_state]) - else - ((bt_notific.color_rgb[notification_unread_state][0] //(2**3)) *(2**11))+ - ((bt_notific.color_rgb[notification_unread_state][1] //(2**2)) *(2**5))+ - (bt_notific.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.bt_notific - message: '{{ set_button04_icon }}' - continue_on_error: true - - ##### SET ICON Font Color - Notify ##### - - *delay-default - - service: '{{ nextion.command.font_color }}' - data: - component: home.bt_notific - message: '{{ set_button04_icon_font }}' - continue_on_error: true - - ##### Show/Hide notification button ##### - - *delay-default - - service: '{{ nextion.command.printf }}' - data: - cmd: is_notification={{ 1 if notification_unread_state == "on" and notification_text_state | length > 0 and set_button04_icon != all_icons.blank else 0 }} - continue_on_error: true - #- service: '{{ nextion.command.show if notification_unread_state == "on" and notification_text_state | length > 0 and set_button04_icon != all_icons.blank else nextion.command.hide }}' - # data: - # component: home.bt_notific - # 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 ALARM ## - - alias: Alarm settings page - conditions: '{{ nspanel_event.page == page.alarm }}' - sequence: &refresh-page_alarm - - variables: - alarm_entity: !input alarm - - if: '{{ alarm_entity is string and alarm_entity is match "alarm_control_panel." }}' - then: - - variables: #https://github.com/home-assistant/core/blob/dev/homeassistant/components/alarm_control_panel/const.py - alarm: - state: '{{ states(alarm_entity) | default("unavailable") }}' - friendly_name: '{{ state_attr(alarm_entity, "friendly_name") }}' - supported_features: '{{ state_attr(alarm_entity, "supported_features") | int(0) }}' - code_format: '{{ state_attr(alarm_entity, "code_format") }}' - code_arm_required: '{{ state_attr(alarm_entity, "code_arm_required") }}' - - condition: '{{ alarm.supported_features > 0 }}' - - service: '{{ nextion.command.alarm_settings }}' - data: - page_title: '{{ alarm.friendly_name }}' - state: '{{ alarm.state }}' - supported_features: '{{ alarm.supported_features }}' - code_format: '{{ alarm.code_format if alarm.code_format else "none" }}' - code_arm_required: '{{ alarm.code_arm_required if alarm.code_arm_required else false }}' - entity: '{{ alarm_entity }}' - mui_alarm: '{{ dict.values(mui[language].alarm) | list }}' - continue_on_error: true - - ## 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 }}' + ##### Service call ##### + - alias: NSPanel service call + conditions: + - '{{ nspanel_event.type == "service_call"}}' + - '{{ nspanel_event.service is defined and nspanel_event.service is string and nspanel_event.service | length > 0 }}' + - '{{ nspanel_event.service is not match "alarm_control_panel." }}' # Prevent the use of this call for alarm control due to safety reasons + - '{{ nspanel_event.entity is defined and nspanel_event.entity is string and nspanel_event.entity | length > 0 }}' 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 }}' + - if: '{{ nspanel_event.key is defined and nspanel_event.key is string and nspanel_event.key | length > 0 }}' + then: + - service: '{{ nspanel_event.service }}' + data: { "{{ nspanel_event.key }}": "{{ nspanel_event.value }}" } + target: + entity_id: '{{ nspanel_event.entity }}' + continue_on_error: true + else: + - service: '{{ nspanel_event.service }}' + target: + entity_id: '{{ nspanel_event.entity }}' + continue_on_error: true - ##### 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 + ##### Page changed ##### + - alias: Page changed + conditions: + - '{{ nspanel_event.type == "page_changed"}}' + 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 - ##### 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 + ##### 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 - ##### 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] }}' + - &refresh-page_home-outdoor_temp + if: '{{ true }}' then: - - service: '{{ nextion.command.printf }}' + ##### NSPanel Outdoor Temp ##### + - variables: + outdoor_temp_entity: !input 'outdoortemp' + outdoor_temp_state: > + {{ + states(outdoor_temp_entity, rounded=true) | default("unavailable") + if outdoor_temp_entity is string and outdoor_temp_entity is match "sensor." and has_value(outdoor_temp_entity) + 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: + - variables: + outdoor_temp_color_rgb: !input 'home_outdoor_temp_label_color' + ### LABEL Outdoor Temp Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.outdoor_temp + message: > + {{ + outdoor_temp_color_rgb + if is_number(outdoor_temp_color_rgb) + else ((outdoor_temp_color_rgb[0] //(2**3)) *(2**11))+((outdoor_temp_color_rgb[1] //(2**2)) *(2**5))+(outdoor_temp_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_entity: !input 'indoortemp' + indoor_temp: + state: '{{ states(indoor_temp_entity, rounded=true) | default("unavailable") if indoor_temp_entity is string and indoor_temp_entity is match "sensor." else states(nspaneltemp, rounded=true) }}' + units: > + {{ + state_attr(indoor_temp_entity, "unit_of_measurement") | default(temperature_units) + if + indoor_temp_entity is string and + indoor_temp_entity is match "sensor." and + state_attr(indoor_temp_entity, "unit_of_measurement") is string and + state_attr(indoor_temp_entity, "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 + ) + }} + icon: + icon: !input 'home_indoor_temp_icon' #E50E + color_rgb: !input 'home_indoor_temp_icon_color' + label: + color_rgb: !input 'home_indoor_temp_label_color' + + - if: '{{ is_number(indoor_temp.state) }}' + then: + ### ICON Indoor Temp Font Color ### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.indoortempicon + message: > + {{ + indoor_temp.icon.color_rgb + if is_number(indoor_temp.icon.color_rgb) + else ((indoor_temp.icon.color_rgb[0] //(2**3)) *(2**11))+((indoor_temp.icon.color_rgb[1] //(2**2)) *(2**5))+(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[indoor_temp.icon.icon.split(":")[1]] | default(all_icons.unknown) + if indoor_temp.icon.icon.split(":") | count > 0 + else + ( + indoor_temp.icon.icon + if 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: > + {{ + indoor_temp.label.color_rgb + if is_number(indoor_temp.label.color_rgb) + else ((indoor_temp.label.color_rgb[0] //(2**3)) *(2**11))+((indoor_temp.label.color_rgb[1] //(2**2)) *(2**5))+(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: - 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) + 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 - ##### Display temperature min/max when available + ##### 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 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(all_icons.unknown) }} + {% else %}{{ all_icons.blank }} + {% 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: + bt_notific: + icon: !input 'home_button04_icon' #E1ED + color_rgb: + "on": !input 'home_button04_icon_color01' + "off": !input 'home_button04_icon_color02' + notification_text_state: '{{ states(notification_text) | default(None) if notification_text is string else None }}' + set_button04_icon: > + {{ + all_icons[bt_notific.icon.split(":")[1]] | default(bt_notific.icon if bt_notific.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: > + {{ + ( + bt_notific.color_rgb[notification_unread_state] + if is_number(bt_notific.color_rgb[notification_unread_state]) + else + ((bt_notific.color_rgb[notification_unread_state][0] //(2**3)) *(2**11))+ + ((bt_notific.color_rgb[notification_unread_state][1] //(2**2)) *(2**5))+ + (bt_notific.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.bt_notific + message: '{{ set_button04_icon }}' + continue_on_error: true + + ##### SET ICON Font Color - Notify ##### + - *delay-default + - service: '{{ nextion.command.font_color }}' + data: + component: home.bt_notific + message: '{{ set_button04_icon_font }}' + continue_on_error: true + + ##### Show/Hide notification button ##### + - *delay-default + - service: '{{ nextion.command.printf }}' + data: + cmd: is_notification={{ 1 if notification_unread_state == "on" and notification_text_state | length > 0 and set_button04_icon != all_icons.blank else 0 }} + continue_on_error: true + #- service: '{{ nextion.command.show if notification_unread_state == "on" and notification_text_state | length > 0 and set_button04_icon != all_icons.blank else nextion.command.hide }}' + # data: + # component: home.bt_notific + # 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: - 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 }}' + 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 ALARM ## + - alias: Alarm settings page + conditions: '{{ nspanel_event.page == page.alarm }}' + sequence: &refresh-page_alarm + - variables: + alarm_entity: !input alarm + - if: '{{ alarm_entity is string and alarm_entity is match "alarm_control_panel." }}' + then: + - variables: #https://github.com/home-assistant/core/blob/dev/homeassistant/components/alarm_control_panel/const.py + alarm: + state: '{{ states(alarm_entity) | default("unavailable") }}' + friendly_name: '{{ state_attr(alarm_entity, "friendly_name") }}' + supported_features: '{{ state_attr(alarm_entity, "supported_features") | int(0) }}' + code_format: '{{ state_attr(alarm_entity, "code_format") }}' + code_arm_required: '{{ state_attr(alarm_entity, "code_arm_required") }}' + - condition: '{{ alarm.supported_features > 0 }}' + - service: '{{ nextion.command.alarm_settings }}' + data: + page_title: '{{ alarm.friendly_name }}' + state: '{{ alarm.state }}' + supported_features: '{{ alarm.supported_features }}' + code_format: '{{ alarm.code_format if alarm.code_format else "none" }}' + code_arm_required: '{{ alarm.code_arm_required if alarm.code_arm_required else false }}' + entity: '{{ alarm_entity }}' + mui_alarm: '{{ dict.values(mui[language].alarm) | list }}' + continue_on_error: true + + ## 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: '{{ page_name }}.temperature' ### Temperature MIN/MAX ### - message: '{{ temperature_string }}' + component: '{{ "entity%02d_label" | format(entity_page_index + 1) }}' + message: '{{ entity_pages_labels[entity_page_index].label }}' continue_on_error: true - *delay-default - - ##### fields 1 to 5 (Parameters) ##### + ##### Entities ##### - 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 + 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 }}.value01' - message: '{{ mui[language].unavailable }}' + component: '{{ page_name }}.day' + message: '{{ (dict.values(mui[language].relative_day) | list)[page_index] }}' continue_on_error: true - *delay-default - else: *forecast_unavailable - ## PAGE NOTIFICATION ## - - alias: Notification page + ##### 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 + + ##### Button click ##### + - alias: Button click conditions: - - '{{ nspanel_event.page == page.notification }}' - - '{{ confirmation_message is string and states(confirmation_message) | default("unavailable") != "on" }}' + - '{{ nspanel_event.type == "button_click"}}' 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 + - choose: + - alias: Hardware buttons + conditions: + - '{{ nspanel_event.component in ["hw_bt_left", "hw_bt_right"] }}' + sequence: + - *variables_hardware + - variables: + button_context: '{{ hardware.buttons.left if nspanel_event.component == "hw_bt_left" else hardware.buttons.right }}' + button_domain: > + {{ + button_context.entity.split(".")[0] | default("unknown") + if + button_context.entity is string and + button_context.entity | length > 0 and + button_context.entity.split(".") | count > 0 + else "unknown" + }} + - choose: + - alias: Long click + conditions: + - '{{ nspanel_event.command == "long_click"}}' + sequence: + - choose: + - alias: Default + conditions: + - '{{ button_context.hold_select == "Default" and button_context.entity | length > 0 }}' + - '{{ button_domain in ["climate", "cover", "light"] }}' + - '{{ button_domain != "cover" or state_attr(button_context.entity, "supported_features") | int(0) | bitwise_and(4) > 0 }}' + - '{{ button_domain != "fan" or state_attr(button_context.entity, "supported_features") | int(0) | bitwise_and(1) > 0 }}' + - '{{ button_domain != "light" or state_attr(button_context.entity, "supported_color_modes") | default("unknown") | string not in ["unknown", "onoff", enum.color_mode.unknown, enum.color_mode.onoff, "", none] }}' + sequence: + - service: '{{ nextion.command.open_entity_settings_page }}' + data: + page: '{{ button_domain }}' + page_label: '{{ (button_context.name | replace("\\r", " ")) if button_context.name is string and button_context.name | length > 0 else state_attr(button_context.entity, "friendly_name") }}' + page_icon: '{{ nextion.icon.domain[button_domain] }}' + page_icon_color: -1 #No color set + entity: '{{ button_context.entity }}' + back_page: '{{ page.home }}' + continue_on_error: true + - alias: Custom action - Left + conditions: '{{ button_context.hold_select == "Custom Action" and nspanel_event.component == "hw_bt_left" }}' + sequence: !input left_button_hold_custom_action + - alias: Custom action - Right + conditions: '{{ button_context.hold_select == "Custom Action" and nspanel_event.component == "hw_bt_right" }}' + sequence: !input right_button_hold_custom_action + - alias: Short click + conditions: + - '{{ nspanel_event.command == "short_click"}}' + sequence: + - condition: '{{ button_context.entity | length > 0 }}' + - condition: '{{ nspanel_event.component != "hw_bt_left" or hardware.buttons.left.entity != relay01_entity }}' ### Prevents blueprint control when locally controlled + - condition: '{{ nspanel_event.component != "hw_bt_right" 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 + {% elif button_domain in ["button", "input_button"] %} + {{ button_domain }}.press + {% elif button_domain in ["scene", "script"] %} + {{ button_domain }}.turn_on + {% else %} + homeassistant.update_entity + {% endif %} + data: + entity_id: '{{ button_context.entity }}' + continue_on_error: true + + ##### Other events ##### + - alias: Other events + conditions: + - '{{ nspanel_event.type == "generic"}}' + sequence: + - choose: + - 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"] }}' + - '{{ nspanel_event.entity is defined and nspanel_event.entity is string and nspanel_event.entity | length > 0 }}' + sequence: + - service: light.turn_on + data: + entity_id: '{{ nspanel_event.entity }}' + '{{ nspanel_event.component }}': '{{ nspanel_event.value }}' + continue_on_error: true + + - alias: Jump to climate page + conditions: + - '{{ nspanel_event.page == page.home }} ' + - '{{ nspanel_event.component == "climate" }}' + - '{{ nspanel_event.value == "release" }}' + - '{{ climate | length > 0 }} ' + sequence: + - service: '{{ nextion.command.open_entity_settings_page }}' + data: + page: 'climate' + page_label: '{{ state_attr(climate, "friendly_name") }}' + page_icon: '' + page_icon_color: -1 #No color set + entity: '{{ climate }}' + back_page: '{{ page.home }}' + continue_on_error: true + + - alias: Show button - Notification clear + conditions: + - '{{ nspanel_event.page == page.notification }}' + - '{{ nspanel_event.component == "clear" }}' + - '{{ confirmation_message is string and states(confirmation_message) | default("unavailable") != "on" }}' + sequence: + - service: switch.turn_off + data: + entity_id: '{{ notification_unread }}' + continue_on_error: true + - service: '{{ nextion.command.printf }}' + data: + cmd: 'page {{ page.home }}' + continue_on_error: true + + - alias: Show button - Notification accept + conditions: + - '{{ nspanel_event.page == page.notification }}' + - '{{ nspanel_event.component == "accept" }}' + - '{{ confirmation_message is string and states(confirmation_message) | default("unavailable") != "on" }}' + sequence: + - service: switch.turn_off + data: + entity_id: '{{ notification_unread }}' + continue_on_error: true + - service: '{{ nextion.command.notification_clear }}' + data: {} + continue_on_error: true + - service: '{{ nextion.command.printf }}' + data: + cmd: 'page {{ page.home }}' + continue_on_error: true ##### NSPanel event ##### - - alias: NSPanel Event + - alias: Legacy NSPanel Event conditions: - condition: trigger id: nspanelevent_changed sequence: - alias: NSPanel Event component changed choose: - - 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"] }}' - - '{{ nspanel_event.entity is defined and nspanel_event.entity is string and nspanel_event.entity | length > 0 }}' - sequence: - - service: light.turn_on - data: - entity_id: '{{ nspanel_event.entity }}' - '{{ nspanel_event.component }}': '{{ nspanel_event.value }}' - continue_on_error: true - - alias: climate custom button press conditions: - '{{ nspanel_event.page == page.climate }}' @@ -8638,73 +8740,6 @@ action: else: - *service-button_changed - - alias: Jump to climate page - conditions: - - '{{ nspanel_event.page == page.home }} ' - - '{{ nspanel_event.component == "climate" }}' - - '{{ nspanel_event.value == "release" }}' - - '{{ climate | length > 0 }} ' - sequence: - - service: '{{ nextion.command.open_entity_settings_page }}' - data: - page: 'climate' - page_label: '{{ state_attr(climate, "friendly_name") }}' - page_icon: '' - page_icon_color: -1 #No color set - entity: '{{ climate }}' - back_page: '{{ page.home }}' - continue_on_error: true - - #- alias: Jump to notification page - # conditions: - # - '{{ nspanel_event.page == page.home }}' - # - '{{ nspanel_event.component == "button04" }}' - # - '{{ notification_text is string and states(notification_text) | length > 0 }}' - # sequence: - # - service: '{{ nextion.command.printf }}' - # data: - # cmd: 'page {{ page.notification }}' - # continue_on_error: true - - - alias: Show button - Notification clear - conditions: - - '{{ nspanel_event.page == page.notification }}' - - '{{ nspanel_event.component == "clear" }}' - - '{{ confirmation_message is string and states(confirmation_message) | default("unavailable") != "on" }}' - sequence: - - service: switch.turn_off - data: - entity_id: '{{ notification_unread }}' - continue_on_error: true - - service: '{{ nextion.command.printf }}' - data: - cmd: 'page {{ page.home }}' - continue_on_error: true - - - alias: Show button - Notification accept - conditions: - - '{{ nspanel_event.page == page.notification }}' - - '{{ nspanel_event.component == "accept" }}' - - '{{ confirmation_message is string and states(confirmation_message) | default("unavailable") != "on" }}' - sequence: - - service: switch.turn_off - data: - entity_id: '{{ notification_unread }}' - continue_on_error: true - - service: '{{ nextion.command.notification_clear }}' - data: {} - continue_on_error: true - - service: '{{ nextion.command.printf }}' - data: - cmd: 'page {{ page.home }}' - continue_on_error: true - - - alias: Boot timed out - conditions: - - '{{ nspanel_event.page == page.boot }}' - - '{{ nspanel_event.component == "timeout" }}' - sequence: *boot_init_sequence - ##### BOOT NSPANEL - automation reload ##### - alias: Automation reloaded conditions: @@ -8715,19 +8750,19 @@ action: sequence: ##### Update global settings (as user may have changed something) ##### - *global_settings - - *refresh-datetime + - *refresh-date - choose: ## PAGE HOME ## - - conditions: '{{ nspanel_event.page == page.home }}' + - conditions: '{{ page.current == page.home }}' sequence: *refresh_page_home ## PAGE BUTTON PAGES 01 - 04 ## - - conditions: '{{ nspanel_event.page in page.buttonpages }}' + - conditions: '{{ page.current in page.buttonpages }}' sequence: *refresh_page_buttonpage ## ENTITY PAGES ## - - conditions: '{{ nspanel_event.page in page.entitypages }}' + - conditions: '{{ page.current in page.entitypages }}' sequence: *refresh-entity_pages ##### UPDATE BUTTONS PAGES - button page / light page / cover page ##### @@ -8770,14 +8805,14 @@ action: - '{{ trigger.to_state.state not in ["unavailable", "unknown", "", None] }}' - condition: or conditions: - - '{{ nspanel_event.page in page.buttonpages }}' - - '{{ nspanel_event.page in [page.light, page.cover, page.climate, page.fan, page.alarm] }}' + - '{{ page.current in page.buttonpages }}' + - '{{ page.current in [page.light, page.cover, page.climate, page.fan, page.alarm] }}' sequence: - choose: - alias: "Button pages" conditions: - - '{{ nspanel_event.page in page.buttonpages }}' + - '{{ page.current in page.buttonpages }}' sequence: #*refresh_page_buttonpage - *variables-page_buttons - repeat: @@ -8785,7 +8820,7 @@ action: {{ button_pages_buttons | selectattr("page", "defined") - | selectattr("page", "eq", nspanel_event.page) + | selectattr("page", "eq", page.current) | selectattr("entity", "defined") | selectattr("entity", "eq", trigger.entity_id) | list @@ -8793,27 +8828,27 @@ action: sequence: *display-button_page_button - alias: 'Light settings page' conditions: - - '{{ nspanel_event.page == page.light }}' + - '{{ page.current == page.light }}' - '{{ trigger.entity_id is match "light." }}' sequence: *refresh_page_light - alias: 'Cover settings page' conditions: - - '{{ nspanel_event.page == page.cover }}' + - '{{ page.current == page.cover }}' - '{{ trigger.entity_id is match "cover." }}' sequence: *refresh_page_cover - alias: 'Climate page' conditions: - - '{{ nspanel_event.page == page.climate }}' + - '{{ page.current == page.climate }}' - '{{ trigger.entity_id is match "climate." }}' sequence: *refresh_page_climate - alias: 'Fan page' conditions: - - '{{ nspanel_event.page == page.fan }}' + - '{{ page.current == page.fan }}' - '{{ trigger.entity_id is match "fan." }}' sequence: *refresh_page_fan #- alias: 'Alarm page' # conditions: - # - '{{ nspanel_event.page == page.alarm }}' + # - '{{ page.current == page.alarm }}' # sequence: *refresh-page_alarm ##### UPDATE ENTITY PAGES ##### @@ -8825,7 +8860,7 @@ action: - trigger_entitypage02 - trigger_entitypage03 - trigger_entitypage04 - - '{{ nspanel_event.page in page.entitypages }}' + - '{{ page.current in page.entitypages }}' sequence: - *variables-entity_pages - repeat: @@ -8833,7 +8868,7 @@ action: {{ entity_pages_entities | selectattr("page", "defined") - | selectattr("page", "eq", nspanel_event.page) + | selectattr("page", "eq", page.current) | selectattr("entity", "defined") | selectattr("entity", "eq", trigger.entity_id) | list @@ -8849,7 +8884,7 @@ action: - climate_value02_state - climate_value03_state - climate_value04_state - - '{{ nspanel_event.page == page.climate }}' + - '{{ page.current == page.climate }}' sequence: - *variables-climate_page - repeat: @@ -8869,7 +8904,7 @@ action: id: - climate_button08_state - climate_button09_state - - '{{ nspanel_event.page == page.climate }}' + - '{{ page.current == page.climate }}' sequence: - *climate-update_custom_buttons @@ -8879,7 +8914,7 @@ action: - condition: trigger id: climate_state - '{{ trigger.event.data.new_state.state not in ["unavailable", "unknown", "", None] }}' - - '{{ nspanel_event.page == page.climate }}' + - '{{ page.current == page.climate }}' sequence: - variables: climate_entity: '{{ trigger.event.data.entity_id }}' @@ -8893,7 +8928,7 @@ action: - condition: trigger id: alarm_state #- '{{ trigger.event.data.new_state.state not in ["unavailable", "unknown", "", None] }}' - #- '{{ nspanel_event.page == page.alarm }}' + #- '{{ page.current == page.alarm }}' sequence: *refresh-page_alarm ########## TRIGGER - HOME PAGE ########### @@ -8906,7 +8941,7 @@ action: - home_value01_state - home_value02_state - home_value03_state - - '{{ nspanel_event.page == page.home }}' + - '{{ page.current == page.home }}' sequence: - *variables-home_page_values - repeat: @@ -8929,7 +8964,7 @@ action: - chip05_state - chip06_state - chip07_state - - '{{ nspanel_event.page == page.home and trigger.event.data.new_state.state not in ["unavailable", "unknown", "", None] }}' + - '{{ page.current == page.home and trigger.event.data.new_state.state not in ["unavailable", "unknown", "", None] }}' sequence: - *variables-home_page_status_bar - repeat: @@ -8950,75 +8985,11 @@ action: id: - notification_text_state - notification_unread_state - - '{{ nspanel_event.page == page.home }}' + - '{{ page.current == page.home }}' - '{{ trigger.event.data.new_state.state not in ["unavailable", "unknown", "", None] }}' sequence: - *refresh-page_home-notifications_icon - ##### HW BUTTON - press ##### - - alias: Hardware button - Press - conditions: - - condition: trigger - id: - - left_button_press - - right_button_press - sequence: - - *variables_hardware - - variables: - button_context: '{{ hardware.buttons.left if trigger.id == "left_button_press" else hardware.buttons.right }}' - button_domain: > - {{ - button_context.entity.split(".")[0] | default("unknown") - if - button_context.entity is string and - button_context.entity | length > 0 and - button_context.entity.split(".") | count > 0 - else "unknown" - }} - - wait_template: '{{ is_state(left_button if trigger.id == "left_button_press" else right_button, "off") }}' - timeout: !input hold_delay - continue_on_timeout: true - - if: '{{ not wait.completed }}' - then: # Hold - Long press - - choose: - - conditions: - - '{{ button_context.hold_select == "Default" and button_context.entity | length > 0 }}' - - '{{ button_domain in ["climate", "cover", "light"] }}' - - '{{ button_domain != "cover" or state_attr(button_context.entity, "supported_features") | int(0) | bitwise_and(4) > 0 }}' - - '{{ button_domain != "fan" or state_attr(button_context.entity, "supported_features") | int(0) | bitwise_and(1) > 0 }}' - - '{{ button_domain != "light" or state_attr(button_context.entity, "supported_color_modes") | default("unknown") | string not in ["unknown", "onoff", enum.color_mode.unknown, enum.color_mode.onoff, "", none] }}' - sequence: - - service: '{{ nextion.command.open_entity_settings_page }}' - data: - page: '{{ button_domain }}' - page_label: '{{ (button_context.name | replace("\\r", " ")) if button_context.name is string and button_context.name | length > 0 else state_attr(button_context.entity, "friendly_name") }}' - page_icon: '{{ nextion.icon.domain[button_domain] }}' - page_icon_color: -1 #No color set - entity: '{{ button_context.entity }}' - back_page: '{{ page.home }}' - continue_on_error: true - - conditions: '{{ button_context.hold_select == "Custom Action" and trigger.id == "left_button_press" }}' - sequence: !input left_button_hold_custom_action - - conditions: '{{ button_context.hold_select == "Custom Action" and trigger.id == "right_button_press" }}' - sequence: !input right_button_hold_custom_action - else: # Single Click - - condition: '{{ button_context.entity | length > 0 }}' - - 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 - {% elif button_domain in ["button", "input_button"] %} - {{ button_domain }}.press - {% elif button_domain in ["scene", "script"] %} - {{ button_domain }}.turn_on - {% else %} - homeassistant.update_entity - {% endif %} - data: - entity_id: '{{ button_context.entity }}' - continue_on_error: true - ##### HW BUTTON - state ##### - alias: Hardware button - State conditions: @@ -9026,7 +8997,7 @@ action: id: - left_button_state - right_button_state - - '{{ nspanel_event.page == page.home }}' + - '{{ page.current == page.home }}' - '{{ trigger.to_state.state not in ["unavailable", "unknown", "", None] }}' sequence: ##### SET hardware Button PIC on Home Page #### @@ -9047,7 +9018,7 @@ action: conditions: - condition: trigger id: outdoortemp_state - - '{{ nspanel_event.page == page.home and is_number(trigger.event.data.new_state.state) }}' + - '{{ page.current == page.home and is_number(trigger.event.data.new_state.state) }}' sequence: - *refresh-page_home-outdoor_temp @@ -9058,7 +9029,7 @@ action: id: - indoortemp_state - nspaneltemp_state - - '{{ nspanel_event.page == page.home and is_number(trigger.event.data.new_state.state) }}' + - '{{ page.current == page.home and is_number(trigger.event.data.new_state.state) }}' sequence: - *refresh-page_home-indoor_temp @@ -9067,7 +9038,7 @@ action: conditions: - condition: trigger id: weather_state_change - - '{{ nspanel_event.page == page.home and trigger.event.data.new_state.state not in ["unavailable", "unknown", "", None] }}' + - '{{ page.current == page.home and trigger.event.data.new_state.state not in ["unavailable", "unknown", "", None] }}' sequence: - *refresh-page_home-outdoor_temp - *delay-default @@ -9090,7 +9061,7 @@ action: conditions: - condition: trigger id: sleep_mode_state - - "{{ nspanel_event.page == page.screensaver }}" + - "{{ page.current == page.screensaver }}" - "{{ trigger.event.data.old_state.state == 'on' }}" - "{{ trigger.event.data.new_state.state == 'off' }}" sequence: diff --git a/nspanel_esphome.yaml b/nspanel_esphome.yaml index c81e007..45dd604 100644 --- a/nspanel_esphome.yaml +++ b/nspanel_esphome.yaml @@ -723,15 +723,47 @@ binary_sensor: pin: number: 14 inverted: true - on_click: - then: - - if: - condition: - - switch.is_on: relay1_local - then: - - switch.toggle: relay_1 - - script.execute: - id: refresh_relays + on_multi_click: + - timing: &double_click-timing + - ON for at most 0.8s + - OFF for at most 0.8s + - ON for at most 0.8s + - OFF for at least 0.2s + then: + - logger.log: "Left button - Double click" + - script.execute: + id: ha_button + page: !lambda return id(current_page).state; + component: "hw_bt_left" + command: "double_click" + + - timing: &long_click-timing + - ON for 0.8s to 2s + - OFF for at least 0.5s + then: + - logger.log: "Left button - Long click" + - script.execute: + id: ha_button + page: !lambda return id(current_page).state; + component: "hw_bt_left" + command: "long_click" + + - timing: &short_click-timing + - ON for at most 0.8s + - OFF for at least 0.5s + then: + - logger.log: "Left button - Short click" + - if: + condition: + - switch.is_on: relay1_local + then: + - switch.toggle: relay_1 + - script.execute: refresh_relays + - script.execute: + id: ha_button + page: !lambda return id(current_page).state; + component: "hw_bt_left" + command: "short_click" ##### RIGHT BUTTON BELOW DISPLAY TO TOGGLE RELAY ##### - name: ${device_name} Right Button @@ -740,15 +772,39 @@ binary_sensor: pin: number: 27 inverted: true - on_click: - then: - - if: - condition: - - switch.is_on: relay2_local - then: - - switch.toggle: relay_2 - - script.execute: - id: refresh_relays + on_multi_click: + - timing: *double_click-timing + then: + - logger.log: "Right button - Double click" + - script.execute: + id: ha_button + page: !lambda return id(current_page).state; + component: "hw_bt_right" + command: "double_click" + + - timing: *long_click-timing + then: + - logger.log: "Right button - Long click" + - script.execute: + id: ha_button + page: !lambda return id(current_page).state; + component: "hw_bt_right" + command: "long_click" + + - timing: *short_click-timing + then: + - logger.log: "Right button - Short click" + - if: + condition: + - switch.is_on: relay2_local + then: + - switch.toggle: relay_2 + - script.execute: refresh_relays + - script.execute: + id: ha_button + page: !lambda return id(current_page).state; + component: "hw_bt_right" + command: "short_click" ##### Restart NSPanel Button - Setting Page ##### - name: ${device_name} Restart @@ -939,12 +995,21 @@ text_sensor: std::string component = doc["component"]; std::string value = doc["value"]; std::string entity = doc["entity"]; + auto ha_event = new esphome::api::CustomAPIDevice(); + ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", + { + {"type", "generic"}, + {"page", page}, + {"component", component}, + {"value", value}, + {"entity", 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", + ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", { + {"type", "page_changed"}, {"page", page}, {"entity", entity} }); @@ -977,7 +1042,16 @@ text_sensor: id(disp1).set_component_text_printf("keyb_num.benter", "%s", "\uE12B"); //mdi:check } } - else if (page=="boot" and component=="timeout" and stof(value) >= 5) id(disp1).send_command_printf("page %i", id(wakeup_page_id)); + else if (page=="boot" and component=="timeout") + { + ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", + { + {"type", "boot"}, + {"step", "timeout"}, + {"value", value} + }); + if (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 @@ -1281,6 +1355,13 @@ display: id: last_started state: !lambda 'return id(time_provider).utcnow().timestamp;' - lambda: id(disp1).set_component_text_printf("boot.ip_addr", "%s", id(ip_address).state.c_str()); + - lambda: |- + auto ha_event = new esphome::api::CustomAPIDevice(); + ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", + { + {"type", "boot"}, + {"step", "start"} + }); - delay: 1s - number.set: id: display_brightness @@ -1301,6 +1382,13 @@ display: - binary_sensor.template.publish: id: nextion_init state: true + - lambda: |- + auto ha_event = new esphome::api::CustomAPIDevice(); + ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", + { + {"type", "boot"}, + {"step", "nextion_init"} + }); #- script.execute: # id: refresh_colors - lambda: id(home_relay1_icon) = "\uE3A5"; @@ -1557,8 +1645,9 @@ script: if (service != "" and not service.empty()) { auto ha_event = new esphome::api::CustomAPIDevice(); - ha_event->fire_homeassistant_event("esphome.nspanel_service_call", + ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", { + {"type", "service_call"}, {"service", service}, {"entity", entity}, {"key", key}, @@ -1566,6 +1655,23 @@ script: }); } + - id: ha_button + mode: parallel + parameters: + page: string + component: string + command: string + then: + - lambda: |- + auto ha_event = new esphome::api::CustomAPIDevice(); + ha_event->fire_homeassistant_event("esphome.nspanel_ha_blueprint", + { + {"type", "button_click"}, + {"page", page}, + {"component", component}, + {"command", command} + }); + - id: update_alarm_icon mode: restart parameters: