diff --git a/esphome/esp-reterminal-e1001.yaml b/esphome/esp-reterminal-e1001.yaml index e94c8cf..4887053 100644 --- a/esphome/esp-reterminal-e1001.yaml +++ b/esphome/esp-reterminal-e1001.yaml @@ -6,6 +6,8 @@ # https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-reterminal-e1001.yaml #:########################################################################################:# # VERSIONS: +# V0.9.5 2026-08-02 Hardened deep-sleep control and startup data validation; +# added a Home Assistant wake-cause diagnostic. # V0.9.4 2026-07-30 Status sub-line shows ago/due-in time; COMPLETED shows # last_completed_human (15:45/Yesterday @/X days ago @); # PIR retrigger guard verified; renamed jobs. @@ -89,7 +91,7 @@ substitutions: # Project Naming project_name: "SeeedStudio.reTerminal-E1001" - project_version: "v0.9.4" + project_version: "v0.9.5" # Passwords and Secrets api_key: !secret esp-api_key @@ -202,6 +204,8 @@ esphome: - deep_sleep.prevent: deep_sleep_1 - lambda: |- id(sleep_allowed) = false; + id(mqtt_sleep_command_received) = false; + id(ha_data_ready) = false; id(boot_sequence_complete) = false; id(last_activity_ms) = millis(); id(awake_timeout_ms) = ${button_wake_awake_seconds} * 1000UL; @@ -212,6 +216,10 @@ esphome: #:####################################################################################:# - priority: -100 then: + - sensor.template.publish: + id: wakeup_cause_sensor + state: !lambda "return id(wakeup_cause);" + - wait_until: condition: mqtt.connected: @@ -222,14 +230,21 @@ esphome: 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 + - logger.log: "MQTT unavailable after ${mqtt_connect_timeout}." else: # Allow time for the retained deepsleep command to be delivered. - delay: 2s + - if: + condition: + lambda: |- + return !id(mqtt_sleep_command_received); + then: + - logger.log: "No valid retained deep-sleep command received; allowing normal sleep for battery protection." + - lambda: |- + id(sleep_allowed) = true; + - deep_sleep.allow: deep_sleep_1 + - wait_until: condition: api.connected: @@ -261,12 +276,34 @@ esphome: id(job_empty_washing_machine_due_ts).has_state(); timeout: "${ha_data_timeout}" + - lambda: |- + id(ha_data_ready) = + id(homeassistant_time).now().is_valid() && + 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_water_tv_pot_plant_state).has_state() && + id(job_water_table_pot_plant_state).has_state() && + id(job_empty_dryer_state).has_state() && + id(job_empty_washing_machine_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() && + id(job_water_tv_pot_plant_due_ts).has_state() && + id(job_water_table_pot_plant_due_ts).has_state() && + id(job_empty_dryer_due_ts).has_state() && + id(job_empty_washing_machine_due_ts).has_state(); + - display.page.show: page_jobs - if: condition: lambda: |- - return id(homeassistant_time).now().is_valid(); + return id(ha_data_ready); then: - script.execute: id: refresh_display_if_changed @@ -288,9 +325,11 @@ esphome: - 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; + if (!id(mqtt_sleep_command_received)) { + id(awake_timeout_ms) = + (id(woke_by_button) ? ${button_wake_awake_seconds} : ${timer_wake_awake_seconds}) + * 1000UL; + } ESP_LOGI( "deep_sleep", @@ -397,6 +436,16 @@ globals: restore_value: no initial_value: "false" + - id: mqtt_sleep_command_received + type: bool + restore_value: no + initial_value: "false" + + - id: ha_data_ready + type: bool + restore_value: no + initial_value: "false" + - id: boot_sequence_complete type: bool restore_value: no @@ -937,6 +986,50 @@ display: # https://esphome.io/components/script.html #:########################################################################################:# script: + - id: apply_sleep_allowed + mode: restart + then: + - lambda: |- + id(mqtt_sleep_command_received) = true; + 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 + + - id: apply_sleep_prevented + mode: restart + then: + - lambda: |- + id(mqtt_sleep_command_received) = true; + 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 + - id: refresh_display_if_changed mode: restart parameters: @@ -1158,7 +1251,7 @@ interval: #:########################################################################################:# # MQTT COMMANDS: -# Retained ON allows sleep; retained OFF prevents sleep for OTA/development. +# Retained On/ON allows sleep; retained Off/OFF prevents sleep for OTA/development. # This extends common/mqtt_common.yaml with device-specific subscriptions. #:########################################################################################:# mqtt: @@ -1168,44 +1261,22 @@ mqtt: - 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 + - script.execute: apply_sleep_allowed + + - topic: "${mqtt_command_topic}/deepsleep" + payload: "ON" + then: + - script.execute: apply_sleep_allowed - 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 + - script.execute: apply_sleep_prevented + + - topic: "${mqtt_command_topic}/deepsleep" + payload: "OFF" + then: + - script.execute: apply_sleep_prevented #:########################################################################################:# # DEEP SLEEP: @@ -1405,6 +1476,14 @@ text_sensor: # Job due timestamps #:########################################################################################:# sensor: + - platform: template + id: wakeup_cause_sensor + name: "${friendly_name} Wakeup Cause" + icon: mdi:power-sleep + accuracy_decimals: 0 + update_interval: never + entity_category: diagnostic + #:######################################################################################:# # Job current due timestamps # #:######################################################################################:#