diff --git a/esphome/esp-reterminal-e1001.yaml b/esphome/esp-reterminal-e1001.yaml index 893b964..fbd49d7 100644 --- a/esphome/esp-reterminal-e1001.yaml +++ b/esphome/esp-reterminal-e1001.yaml @@ -6,6 +6,13 @@ # https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-reterminal-e1001.yaml #:########################################################################################:# # VERSIONS: +# V0.6.1 2026-07-20 Allowed GPIO4/GPIO5 to be shared by the EXT1 deep-sleep +# wake source and the normal white-button binary sensors. +# V0.6 2026-07-20 Added open/closed-eye panel-state indicator, retained MQTT awake/sleep +# status publishing, and inverse black tiles for Due, Overdue and Missed/Skipped jobs. +# V0.5 2026-07-20 Added five-minute deep sleep, timer and dual white-button wake, +# retained MQTT sleep control, wake-source page selection, persistent +# per-page display hashes and short post-button interaction timeout. # V0.4 2026-07-20 Improved Jobs page with large Material Design job icons, # state icons, two-line centred job names and larger status text. # V0.3 2026-07-20 Added two-page Jobs and Appliances dashboard, white-button page @@ -35,20 +42,34 @@ # - Display busy: GPIO13, inverted #:########################################################################################:# # OPERATION: -# - The left white button selects the View Road Jobs page. -# - The right white button selects the View Road Appliances page. -# - The display is checked every 30 seconds, but the e-paper panel is refreshed only when -# the visible page content has actually changed. -# - Page changes and manual refreshes intentionally force one physical display refresh. -# - Job countdown wording changes only when its displayed hour/day value changes. -# - Job tiles use large job icons, two-line names and clear state icons. -# - OVERDUE and MISSED job tiles use a black background with white text and icons. -# - Appliance tiles show OPERATING while active. -# - A completed appliance shows inverse FINISHED for one hour after cycle completion. -# - After one hour, the appliance tile shows Completed at HH:MM with a normal background. +# - The device sleeps for five minutes, then wakes and checks the currently displayed page. +# - Either white button can wake the ESP32-S3 through EXT1 ANY_LOW. +# - A left-button wake selects View Road Jobs; a right-button wake selects Appliances. +# - The wake button is identified from the retained ESP32 EXT1 wake-status bitmask, so one +# press both wakes the device and selects the intended page. +# - Timer wakeups retain the page that was displayed before sleep. +# - Per-page display hashes survive deep sleep. The e-paper panel refreshes only when the +# selected page content, selected page, or visible awake/sleep indicator changes. +# - Two open eyes indicate an interactive/kept-awake panel. Two closed eyes are written to +# the e-paper panel before entering deep sleep. +# - After a button interaction, the device remains awake briefly so another page can be +# selected without waiting through another complete wake-and-connect cycle. # - Home Assistant entity data is imported through the encrypted ESPHome native API. -# - MQTT is not required on this ESPHome device. -# - Deep sleep remains disabled during dashboard development and OTA testing. +# - MQTT is used only for retained deep-sleep control and retained awake/sleep status. +# +# MQTT COMMANDS: +# - ${mqtt_command_topic}/deepsleep = ON +# - Allow the device to return to deep sleep. +# - ${mqtt_command_topic}/deepsleep = OFF +# - Prevent deep sleep for OTA updates and development. +# - Publish these commands as retained messages. +# MQTT STATUS: +# - ${mqtt_status_topic}/state = awake/sleeping (retained) +# - ${mqtt_status_topic}/deepsleep = ON/OFF (retained) +# +# - Deep sleep is prevented by default until the retained MQTT command is received. +# - If MQTT cannot be reached after the boot timeout, the device uses a battery-protecting +# fallback and permits the normal five-minute sleep cycle for that wake. #:########################################################################################:# #:########################################################################################:# @@ -60,16 +81,23 @@ substitutions: # Device Naming device_name: "esp-reterminal-e1001" friendly_name: "reTerminal E1001 Dashboard" - description_comment: "Seeed Studio reTerminal E1001 two-page Home Assistant jobs and appliance status dashboard with icon-based job tiles. (Layout V1.1)" + description_comment: "Seeed Studio reTerminal E1001 two-page Home Assistant jobs and appliance status dashboard with five-minute deep sleep, button wake and panel-state eyes. (Layout V1.1)" device_area: "Lounge" # Project Naming project_name: "SeeedStudio.reTerminal-E1001" - project_version: "v0.4" + project_version: "v0.6.1" # Passwords and Secrets api_key: !secret esp-api_key ota_pass: !secret esp-ota_pass + mqtt_command_main_topic: !secret mqtt_command_main_topic + mqtt_status_main_topic: !secret mqtt_status_main_topic + + # MQTT Local Controls + mqtt_device_name: "reterminal-e1001" + mqtt_command_topic: "${mqtt_command_main_topic}/${mqtt_device_name}" + mqtt_status_topic: "${mqtt_status_main_topic}/${mqtt_device_name}" # Device Settings log_level: "INFO" @@ -77,6 +105,15 @@ substitutions: finished_highlight_seconds: "3600" job_hours_display_limit: "48" + # Deep Sleep Settings + sleep_interval: "5min" + mqtt_connect_timeout: "30s" + api_connect_timeout: "30s" + ha_data_timeout: "25s" + timer_wake_awake_seconds: "8" + button_wake_awake_seconds: "30" + mqtt_enable_awake_seconds: "15" + #:########################################################################################:# # PACKAGES: # Included Common Packages @@ -98,11 +135,12 @@ packages: local_api_key: "${api_key}" #### MQTT #### - # Home Assistant entity states are imported through the ESPHome native API. - #common_mqtt: !include - # file: common/mqtt_common.yaml - # vars: - # local_device_name: "${device_name}" + # MQTT is used only for retained deep-sleep control and status. + # Home Assistant entity states still arrive through the native API. + common_mqtt: !include + file: common/mqtt_common.yaml + vars: + local_device_name: "${device_name}" #### WEB PORTAL #### #common_webportal: !include common/webportal_common_nopass.yaml @@ -131,17 +169,170 @@ esphome: version: "${project_version}" on_boot: - priority: -100 - then: - # Allow Wi-Fi, API entities and Home Assistant time to initialise. - - delay: 15s - - display.page.show: page_jobs - - globals.set: - id: current_page - value: "0" - - script.execute: - id: refresh_display_if_changed - force: true + #:####################################################################################:# + # EARLY WAKE-SOURCE DETECTION # + #:####################################################################################:# + - priority: 800 + then: + - lambda: |- + id(wakeup_cause) = static_cast(esp_sleep_get_wakeup_cause()); + id(wakeup_pin_mask) = esp_sleep_get_ext1_wakeup_status(); + id(woke_by_button) = id(wakeup_cause) == ESP_SLEEP_WAKEUP_EXT1; + + // A physical-button wake is interactive, so show open eyes. A short + // timer maintenance wake keeps the closed-eye icon unless the device + // is explicitly kept awake through MQTT. + id(panel_awake_indicator) = id(woke_by_button); + + const int previous_page = id(current_page); + + if (id(woke_by_button)) { + // GPIO5 is the left white button: Jobs. + if ((id(wakeup_pin_mask) & (1ULL << 5)) != 0) { + id(current_page) = 0; + } + // GPIO4 is the right white button: Appliances. + else if ((id(wakeup_pin_mask) & (1ULL << 4)) != 0) { + id(current_page) = 1; + } + } + + id(page_changed_on_wake) = id(current_page) != previous_page; + + ESP_LOGI( + "wakeup", + "Wake cause=%d EXT1 mask=0x%llX page=%d changed=%s", + id(wakeup_cause), + static_cast(id(wakeup_pin_mask)), + id(current_page) + 1, + id(page_changed_on_wake) ? "yes" : "no" + ); + + #:####################################################################################:# + # DEFAULT TO STAYING AWAKE UNTIL MQTT CONTROL IS RECEIVED # + #:####################################################################################:# + - priority: 600 + then: + - deep_sleep.prevent: deep_sleep_1 + - lambda: |- + id(sleep_allowed) = false; + id(boot_sequence_complete) = false; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = ${button_wake_awake_seconds} * 1000UL; + - logger.log: "Deep sleep prevented by default while waiting for retained MQTT control." + + #:####################################################################################:# + # NETWORK, HOME ASSISTANT DATA AND DISPLAY CHECK # + #:####################################################################################:# + - priority: -100 + then: + - wait_until: + condition: + mqtt.connected: + timeout: "${mqtt_connect_timeout}" + + - if: + condition: + not: + mqtt.connected: + then: + - logger.log: "MQTT unavailable after ${mqtt_connect_timeout}; allowing fallback sleep for battery protection." + - lambda: |- + id(sleep_allowed) = true; + - deep_sleep.allow: deep_sleep_1 + else: + # Allow time for the retained deepsleep command to be delivered. + - delay: 2s + + - wait_until: + condition: + api.connected: + timeout: "${api_connect_timeout}" + + - wait_until: + condition: + lambda: |- + if (!id(homeassistant_time).now().is_valid()) return false; + + if (id(current_page) == 0) { + return + id(job_dog_feeding_morning_state).has_state() && + id(job_dog_feeding_evening_state).has_state() && + id(job_dog_depooping_state).has_state() && + id(job_dishwasher_unpacked_state).has_state() && + id(job_rubbish_removed_state).has_state() && + id(job_dog_feeding_morning_due_ts).has_state() && + id(job_dog_feeding_evening_due_ts).has_state() && + id(job_dog_depooping_due_ts).has_state() && + id(job_dishwasher_unpacked_due_ts).has_state() && + id(job_rubbish_removed_due_ts).has_state(); + } + + return + id(appliance_washing_state).has_state() && + id(appliance_dryer_state).has_state() && + id(appliance_main_dishwasher_state).has_state() && + id(appliance_downstairs_dishwasher_state).has_state() && + id(appliance_evcharger_32a_state).has_state() && + id(appliance_evcharger_16a_state).has_state() && + id(appliance_ryobi_left_state).has_state() && + id(appliance_ryobi_right_state).has_state() && + id(appliance_pool_pump_state).has_state() && + id(appliance_3d_printer_state).has_state(); + timeout: "${ha_data_timeout}" + + - if: + condition: + lambda: |- + return id(current_page) == 0; + then: + - display.page.show: page_jobs + else: + - display.page.show: page_appliances + + - if: + condition: + lambda: |- + return id(homeassistant_time).now().is_valid(); + then: + - if: + condition: + lambda: |- + return id(page_changed_on_wake); + then: + - script.execute: + id: refresh_display_if_changed + force: true + else: + - script.execute: + id: refresh_display_if_changed + force: false + - script.wait: refresh_display_if_changed + else: + - logger.log: "Home Assistant time/data were unavailable; preserving the existing e-paper image." + + - if: + condition: + mqtt.connected: + then: + - mqtt.publish: + topic: "${mqtt_status_topic}/state" + payload: "awake" + qos: 1 + retain: true + + - lambda: |- + id(boot_sequence_complete) = true; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = + (id(woke_by_button) ? ${button_wake_awake_seconds} : ${timer_wake_awake_seconds}) + * 1000UL; + + ESP_LOGI( + "deep_sleep", + "Boot display check complete; inactivity window is %lu seconds", + static_cast(id(awake_timeout_ms) / 1000UL) + ); #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK: @@ -199,25 +390,80 @@ time: #:########################################################################################:# # GLOBALS: -# Display page and change-detection state +# Page, persistent display hashes, wake information and sleep-control state # https://esphome.io/components/globals.html #:########################################################################################:# globals: + # The currently visible page survives deep sleep so timer wakes retain it. - id: current_page type: int - restore_value: no + restore_value: yes initial_value: "0" - - id: last_display_signature - type: std::string - restore_value: no - initial_value: '""' + # A separate persistent hash is kept for each page. These are updated only when the + # visible content really changes, avoiding a flash write on every five-minute wake. + - id: last_jobs_signature_hash + type: uint32_t + restore_value: yes + initial_value: "0" + + - id: last_appliances_signature_hash + type: uint32_t + restore_value: yes + initial_value: "0" - id: display_update_required type: bool restore_value: no initial_value: "false" + - id: wakeup_cause + type: int + restore_value: no + initial_value: "0" + + - id: wakeup_pin_mask + type: uint64_t + restore_value: no + initial_value: "0" + + - id: woke_by_button + type: bool + restore_value: no + initial_value: "false" + + - id: page_changed_on_wake + type: bool + restore_value: no + initial_value: "false" + + # Desired icon currently rendered in the display header. This is included in the + # per-page signature, so changing eye state refreshes only when required. + - id: panel_awake_indicator + type: bool + restore_value: no + initial_value: "false" + + - id: sleep_allowed + type: bool + restore_value: no + initial_value: "false" + + - id: boot_sequence_complete + type: bool + restore_value: no + initial_value: "false" + + - id: last_activity_ms + type: uint32_t + restore_value: no + initial_value: "0" + + - id: awake_timeout_ms + type: uint32_t + restore_value: no + initial_value: "30000" + #:########################################################################################:# # FONTS: # Bold, compact fonts for clear monochrome e-paper rendering. @@ -409,6 +655,22 @@ display: } }; + auto draw_panel_state_icon = [&](int centre_x, int centre_y) { + if (id(panel_awake_indicator)) { + // Two open eyes with pupils. + it.circle(centre_x - 9, centre_y, 6, COLOR_ON); + it.circle(centre_x + 9, centre_y, 6, COLOR_ON); + it.filled_circle(centre_x - 9, centre_y, 2, COLOR_ON); + it.filled_circle(centre_x + 9, centre_y, 2, COLOR_ON); + } else { + // Two closed eyelids, each drawn as a shallow downward curve. + it.line(centre_x - 16, centre_y - 2, centre_x - 10, centre_y + 2, COLOR_ON); + it.line(centre_x - 10, centre_y + 2, centre_x - 4, centre_y - 2, COLOR_ON); + it.line(centre_x + 2, centre_y - 2, centre_x + 8, centre_y + 2, COLOR_ON); + it.line(centre_x + 8, centre_y + 2, centre_x + 14, centre_y - 2, COLOR_ON); + } + }; + auto make_date = [&]() -> std::string { if (!now.is_valid()) return "Waiting for Home Assistant time"; @@ -462,7 +724,7 @@ display: it.image(x, y, id(icon_status_complete), foreground, background); } else if (state == "Overdue") { it.image(x, y, id(icon_status_overdue), foreground, background); - } else if (state == "Missed") { + } else if (state == "Missed" || state == "Skipped") { it.image(x, y, id(icon_status_missed), foreground, background); } else if (state == "Due" || state == "Not Due") { it.image(x, y, id(icon_status_due), foreground, background); @@ -504,13 +766,16 @@ display: } } else if (state == "Due") { status = "DUE NOW"; - border = emphasis_border; + inverse = true; } else if (state == "Overdue") { status = "OVERDUE"; inverse = true; } else if (state == "Missed") { status = "MISSED"; inverse = true; + } else if (state == "Skipped") { + status = "SKIPPED"; + inverse = true; } else if (!state.empty()) { status = state; } @@ -559,6 +824,7 @@ display: // Header. it.print(14, 28, id(font_header), TextAlign::CENTER_LEFT, "View Road Jobs"); + draw_panel_state_icon(448, 28); const std::string date_text = make_date(); it.printf( screen_width - 14, @@ -651,6 +917,22 @@ display: } }; + auto draw_panel_state_icon = [&](int centre_x, int centre_y) { + if (id(panel_awake_indicator)) { + // Two open eyes with pupils. + it.circle(centre_x - 9, centre_y, 6, COLOR_ON); + it.circle(centre_x + 9, centre_y, 6, COLOR_ON); + it.filled_circle(centre_x - 9, centre_y, 2, COLOR_ON); + it.filled_circle(centre_x + 9, centre_y, 2, COLOR_ON); + } else { + // Two closed eyelids, each drawn as a shallow downward curve. + it.line(centre_x - 16, centre_y - 2, centre_x - 10, centre_y + 2, COLOR_ON); + it.line(centre_x - 10, centre_y + 2, centre_x - 4, centre_y - 2, COLOR_ON); + it.line(centre_x + 2, centre_y - 2, centre_x + 8, centre_y + 2, COLOR_ON); + it.line(centre_x + 8, centre_y + 2, centre_x + 14, centre_y - 2, COLOR_ON); + } + }; + auto make_date = [&]() -> std::string { if (!now.is_valid()) return "Waiting for Home Assistant time"; @@ -765,6 +1047,7 @@ display: // Header. it.print(14, 28, id(font_header), TextAlign::CENTER_LEFT, "View Road Appliances"); + draw_panel_state_icon(448, 28); const std::string date_text = make_date(); it.printf( screen_width - 14, @@ -860,7 +1143,7 @@ display: #:########################################################################################:# # DISPLAY CHANGE DETECTION: # Builds a signature from only the content that is currently visible. The display is not -# physically refreshed when the visible wording and style would remain unchanged. +# physically refreshed when the visible wording, style and eye indicator would remain unchanged. # https://esphome.io/components/script.html #:########################################################################################:# script: @@ -892,6 +1175,8 @@ script: }; // The visible header changes once per day, not every minute. + append_text(id(panel_awake_indicator) ? "eyes_open" : "eyes_closed"); + if (now.is_valid()) { append_number(now.year); append_number(now.day_of_year); @@ -1036,13 +1321,41 @@ script: ); } - id(display_update_required) = force || signature != id(last_display_signature); + // Persistent 32-bit FNV-1a hash. A separate value is retained for each page. + uint32_t signature_hash = 2166136261UL; + for (const unsigned char character : signature) { + signature_hash ^= static_cast(character); + signature_hash *= 16777619UL; + } + + const uint32_t previous_hash = + id(current_page) == 0 + ? id(last_jobs_signature_hash) + : id(last_appliances_signature_hash); + + id(display_update_required) = + force || previous_hash == 0 || signature_hash != previous_hash; if (id(display_update_required)) { - id(last_display_signature) = signature; - ESP_LOGI("display", "Visible display content changed; refreshing page %d", id(current_page) + 1); + if (id(current_page) == 0) { + id(last_jobs_signature_hash) = signature_hash; + } else { + id(last_appliances_signature_hash) = signature_hash; + } + + ESP_LOGI( + "display", + "Visible content changed or page selected; refreshing page %d (hash 0x%08lX)", + id(current_page) + 1, + static_cast(signature_hash) + ); } else { - ESP_LOGD("display", "Visible display content unchanged; e-paper refresh skipped"); + ESP_LOGI( + "display", + "Page %d content unchanged (hash 0x%08lX); e-paper refresh skipped", + id(current_page) + 1, + static_cast(signature_hash) + ); } - if: @@ -1059,17 +1372,134 @@ script: value: "false" #:########################################################################################:# -# PERIODIC DISPLAY CHECK: -# This checks in-memory values only; it does not refresh the e-paper unless the signature -# differs from the last rendered content. +# PERIODIC DISPLAY AND SLEEP CHECKS: +# The display check matters while MQTT OFF keeps the device awake. The sleep controller +# uses inactivity rather than a fixed run_duration, so every button press resets the timer. # https://esphome.io/components/interval.html #:########################################################################################:# interval: - interval: "${display_check_interval}" then: - - script.execute: - id: refresh_display_if_changed - force: false + - if: + condition: + lambda: |- + return id(boot_sequence_complete); + then: + - script.execute: + id: refresh_display_if_changed + force: false + + - interval: 2s + then: + - if: + condition: + lambda: |- + if (!id(boot_sequence_complete)) return false; + if (!id(sleep_allowed)) return false; + if (id(button_left).state || id(button_right).state) return false; + + const uint32_t elapsed = millis() - id(last_activity_ms); + return elapsed >= id(awake_timeout_ms); + then: + - logger.log: "Inactivity period complete; preparing to enter five-minute deep sleep." + + # Avoid immediately waking again if the user is only just releasing a button. + - delay: 350ms + + - if: + condition: + lambda: |- + return + id(sleep_allowed) && + !id(button_left).state && + !id(button_right).state; + then: + # Write the closed-eye icon before power-down. If the display already + # shows closed eyes and nothing else changed, the hash check skips the + # physical e-paper refresh. + - lambda: |- + id(panel_awake_indicator) = false; + - script.execute: + id: refresh_display_if_changed + force: false + - script.wait: refresh_display_if_changed + + - mqtt.publish: + topic: "${mqtt_status_topic}/state" + payload: "sleeping" + qos: 1 + retain: true + - delay: 350ms + - deep_sleep.enter: deep_sleep_1 + +#:########################################################################################:# +# MQTT COMMANDS: +# Retained ON allows sleep; retained OFF prevents sleep for OTA/development. +# This extends common/mqtt_common.yaml with device-specific subscriptions. +#:########################################################################################:# +mqtt: + discovery: false + + on_message: + - topic: "${mqtt_command_topic}/deepsleep" + payload: "ON" + then: + - lambda: |- + id(sleep_allowed) = true; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = ${mqtt_enable_awake_seconds} * 1000UL; + - deep_sleep.allow: deep_sleep_1 + - logger.log: "Deep sleep ALLOWED by retained MQTT command." + - mqtt.publish: + topic: "${mqtt_status_topic}/deepsleep" + payload: "ON" + qos: 1 + retain: true + + - topic: "${mqtt_command_topic}/deepsleep" + payload: "OFF" + then: + - lambda: |- + id(sleep_allowed) = false; + id(panel_awake_indicator) = true; + - deep_sleep.prevent: deep_sleep_1 + - logger.log: "Deep sleep PREVENTED by retained MQTT command." + - mqtt.publish: + topic: "${mqtt_status_topic}/deepsleep" + payload: "OFF" + qos: 1 + retain: true + - mqtt.publish: + topic: "${mqtt_status_topic}/state" + payload: "awake" + qos: 1 + retain: true + - if: + condition: + lambda: |- + return id(boot_sequence_complete); + then: + - script.execute: + id: refresh_display_if_changed + force: false + +#:########################################################################################:# +# DEEP SLEEP: +# Five-minute timer wake plus either active-low white button through ESP32-S3 EXT1. +# GPIO4 = right/appliances; GPIO5 = left/jobs. +# https://esphome.io/components/deep_sleep.html +#:########################################################################################:# +deep_sleep: + id: deep_sleep_1 + sleep_duration: "${sleep_interval}" + + esp32_ext1_wakeup: + pins: + - number: GPIO4 + allow_other_uses: true + - number: GPIO5 + allow_other_uses: true + mode: ANY_LOW #:########################################################################################:# # HOME ASSISTANT JOB STATE IMPORTS: @@ -1446,7 +1876,8 @@ light: #:########################################################################################:# # PHYSICAL BUTTONS: -# Left white selects Jobs; right white selects Appliances; green performs a forced refresh. +# Buttons reset the post-interaction timeout. During deep sleep the white buttons are also +# EXT1 wake sources; the wake-status mask selects the page before Home Assistant data loads. # https://esphome.io/components/binary_sensor/gpio.html #:########################################################################################:# binary_sensor: @@ -1464,6 +1895,10 @@ binary_sensor: - delayed_off: 20ms on_press: then: + - lambda: |- + id(panel_awake_indicator) = true; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = ${button_wake_awake_seconds} * 1000UL; - script.execute: id: refresh_display_if_changed force: true @@ -1473,6 +1908,7 @@ binary_sensor: name: "${friendly_name} Right Button - Appliances" pin: number: GPIO4 + allow_other_uses: true mode: input: true pullup: true @@ -1482,6 +1918,10 @@ binary_sensor: - delayed_off: 20ms on_press: then: + - lambda: |- + id(panel_awake_indicator) = true; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = ${button_wake_awake_seconds} * 1000UL; - if: condition: lambda: |- @@ -1494,12 +1934,17 @@ binary_sensor: - script.execute: id: refresh_display_if_changed force: true + else: + - script.execute: + id: refresh_display_if_changed + force: false - platform: gpio id: button_left name: "${friendly_name} Left Button - Jobs" pin: number: GPIO5 + allow_other_uses: true mode: input: true pullup: true @@ -1509,6 +1954,10 @@ binary_sensor: - delayed_off: 20ms on_press: then: + - lambda: |- + id(panel_awake_indicator) = true; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = ${button_wake_awake_seconds} * 1000UL; - if: condition: lambda: |- @@ -1521,6 +1970,10 @@ binary_sensor: - script.execute: id: refresh_display_if_changed force: true + else: + - script.execute: + id: refresh_display_if_changed + force: false #:########################################################################################:# # HOME ASSISTANT CONTROLS: @@ -1534,6 +1987,10 @@ button: icon: "mdi:refresh" on_press: then: + - lambda: |- + id(panel_awake_indicator) = true; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = ${button_wake_awake_seconds} * 1000UL; - script.execute: id: refresh_display_if_changed force: true @@ -1544,6 +2001,10 @@ button: icon: "mdi:clipboard-check-outline" on_press: then: + - lambda: |- + id(panel_awake_indicator) = true; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = ${button_wake_awake_seconds} * 1000UL; - if: condition: lambda: |- @@ -1556,6 +2017,10 @@ button: - script.execute: id: refresh_display_if_changed force: true + else: + - script.execute: + id: refresh_display_if_changed + force: false - platform: template id: show_appliances_page_button @@ -1563,6 +2028,10 @@ button: icon: "mdi:washing-machine" on_press: then: + - lambda: |- + id(panel_awake_indicator) = true; + id(last_activity_ms) = millis(); + id(awake_timeout_ms) = ${button_wake_awake_seconds} * 1000UL; - if: condition: lambda: |- @@ -1575,3 +2044,7 @@ button: - script.execute: id: refresh_display_if_changed force: true + else: + - script.execute: + id: refresh_display_if_changed + force: false