From d81deae3bef24f561565a35f4037d05314ebf573 Mon Sep 17 00:00:00 2001 From: zorruno Date: Sun, 30 Aug 2026 15:35:24 +1200 Subject: [PATCH] various tidyups --- .ha_run.lock | 2 +- packages/fridge_freezer_monitoring.yaml | 108 ++++++++++++++----- packages/fridge_temperatures.yaml | 9 +- packages/xmas_tree_water_warning.yaml | 8 +- packages/xmas_tree_water_warning.yaml.bak | 120 ++++++++++++++++++++++ 5 files changed, 214 insertions(+), 33 deletions(-) create mode 100644 packages/xmas_tree_water_warning.yaml.bak diff --git a/.ha_run.lock b/.ha_run.lock index b502ba8..18c2a6e 100644 --- a/.ha_run.lock +++ b/.ha_run.lock @@ -1 +1 @@ -{"pid": 71, "version": 1, "ha_version": "2026.8.3", "start_ts": 1787912264.3193414} \ No newline at end of file +{"pid": 71, "version": 1, "ha_version": "2026.8.3", "start_ts": 1788051080.3314128} \ No newline at end of file diff --git a/packages/fridge_freezer_monitoring.yaml b/packages/fridge_freezer_monitoring.yaml index 3dcfc40..0f18ea5 100644 --- a/packages/fridge_freezer_monitoring.yaml +++ b/packages/fridge_freezer_monitoring.yaml @@ -1,8 +1,10 @@ #:########################################################################################:# # PACKAGE: Fridge and Freezer Monitoring # FILE: packages/fridge_freezer_monitoring.yaml -# VERSION: v1.10.0 2026-08-29 -# - Adds Downstairs Freezer temperature, battery, door, humidity, and shared +# VERSION: v1.11.0 2026-08-30 +# - Moves Main Kitchen Fridge temperature, humidity, and battery monitoring to +# X65TS and adds X64LS illuminance-based door monitoring. +# - v1.10.0 2026-08-29: Adds Downstairs Freezer temperature, battery, door, humidity, and shared # Downstairs Fridge/Freezer plug monitoring. # - Uses a four-hour sustained low-draw power-loss rule for all monitored # fridges and freezers, including the shared Drinks Fridge/Freezer plug. @@ -38,7 +40,7 @@ # - Measures the temperature change rate over a smoothed 30-minute window. # - Requires an issue condition to persist for one minute, except Main Kitchen # Fridge low-power draw, which persists for five minutes. -# - Treats the Drinks and Downstairs Fridges as open at 10 lx or higher. An +# - Treats the Main Kitchen, Drinks, and Downstairs Fridges as open at 10 lx or higher. An # open door is an issue after seven minutes, while temperature alerting # stays deferred through a 15-minute post-close recovery window. # - Exposes one issue binary sensor per appliance and one summary sensor. @@ -51,8 +53,8 @@ # - Main Fridge power below 1 W is treated as loss only after five minutes to # avoid normal compressor-off periods; unplugged or unavailable monitoring # remains an issue after one minute. -# - Main Fridge temperature is adjusted by -0.84 C to match the reference -# thermometer. +# - Main Fridge X65TS temperature is adjusted by -0.84 C to match the +# reference thermometer. # - Wine-fridge ESPHome telemetry is intentionally excluded. #:########################################################################################:# # DEPENDENCIES: @@ -68,6 +70,7 @@ # - sensor.drinks_fridge_light_sensor_x35ls_illuminance # - sensor.downstairs_fridge_light_sensor_x58ls_illuminance # - sensor.downstairs_freezer_light_sensor_x61ls_illuminance +# - sensor.main_kitchen_fridge_light_sensor_x64ls_illuminance #:########################################################################################:# sensor: @@ -293,6 +296,27 @@ template: # DOOR DETECTION #:######################################################################################:# - binary_sensor: + - name: "Main Kitchen Fridge Door Open" + unique_id: main_kitchen_fridge_door_open + device_class: door + state: >- + {{ + has_value('sensor.main_kitchen_fridge_light_sensor_x64ls_illuminance') + and (states('sensor.main_kitchen_fridge_light_sensor_x64ls_illuminance') | float(0)) >= 10 + }} + + # Retain the grace period after closure so the fridge can start cooling. + - name: "Main Kitchen Fridge Temperature Recovery Grace" + unique_id: main_kitchen_fridge_temperature_recovery_grace + delay_off: "00:15:00" + state: "{{ is_state('binary_sensor.main_kitchen_fridge_door_open', 'on') }}" + + - name: "Main Kitchen Fridge Door Open Too Long" + unique_id: main_kitchen_fridge_door_open_too_long + device_class: problem + delay_on: "00:07:00" + state: "{{ is_state('binary_sensor.main_kitchen_fridge_door_open', 'on') }}" + - name: "Drinks Fridge Door Open" unique_id: drinks_fridge_door_open device_class: door @@ -418,15 +442,21 @@ template: delay_on: "00:01:00" state: >- {% set temperature_missing = not has_value('sensor.main_kitchen_fridge_temperature_calibrated') %} - {% set battery_missing = not has_value('sensor.unk_manufacturer_unk_model_battery') %} + {% set battery_missing = not has_value('sensor.main_kitchen_fridge_temperature_x65ts_battery') %} + {% set illuminance_missing = not has_value('sensor.main_kitchen_fridge_light_sensor_x64ls_illuminance') %} {% set power_missing = not has_value('sensor.upstairs_fridge_power_x31pp_power') %} {% set percentile = states('sensor.main_kitchen_fridge_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [10, percentile + 0.5] | min if percentile is not none else 10 %} {{ temperature_missing - or battery_missing - or (states('sensor.unk_manufacturer_unk_model_battery') | float(-1)) <= 20 - or (not temperature_missing and (states('sensor.main_kitchen_fridge_temperature_calibrated') | float(0)) > threshold) + or illuminance_missing + or (states('sensor.main_kitchen_fridge_temperature_x65ts_battery') | float(-1)) <= 20 + or is_state('binary_sensor.main_kitchen_fridge_door_open_too_long', 'on') + or ( + not temperature_missing + and not is_state('binary_sensor.main_kitchen_fridge_temperature_recovery_grace', 'on') + and (states('sensor.main_kitchen_fridge_temperature_calibrated') | float(0)) > threshold + ) or states('switch.upstairs_fridge_power_x31pp') != 'on' or power_missing or is_state('binary_sensor.main_kitchen_fridge_power_draw_lost', 'on') @@ -436,14 +466,18 @@ template: issues: >- {% set ns = namespace(items=[]) %} {% set temperature_missing = not has_value('sensor.main_kitchen_fridge_temperature_calibrated') %} - {% set battery_missing = not has_value('sensor.unk_manufacturer_unk_model_battery') %} + {% set battery_missing = not has_value('sensor.main_kitchen_fridge_temperature_x65ts_battery') %} + {% set illuminance_missing = not has_value('sensor.main_kitchen_fridge_light_sensor_x64ls_illuminance') %} {% set power_missing = not has_value('sensor.upstairs_fridge_power_x31pp_power') %} {% set percentile = states('sensor.main_kitchen_fridge_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [10, percentile + 0.5] | min if percentile is not none else 10 %} - {% if temperature_missing or battery_missing %} - {% set ns.items = ns.items + ['Monitor offline'] %} + {% if temperature_missing %} + {% set ns.items = ns.items + ['Temperature monitor unavailable'] %} {% endif %} - {% if not battery_missing and (states('sensor.unk_manufacturer_unk_model_battery') | float(-1)) <= 20 %} + {% if illuminance_missing %} + {% set ns.items = ns.items + ['Door monitor unavailable'] %} + {% endif %} + {% if not battery_missing and (states('sensor.main_kitchen_fridge_temperature_x65ts_battery') | float(-1)) <= 20 %} {% set ns.items = ns.items + ['Low monitor battery'] %} {% endif %} {% if states('switch.upstairs_fridge_power_x31pp') in ['unknown', 'unavailable'] or power_missing %} @@ -453,7 +487,11 @@ template: {% elif is_state('binary_sensor.main_kitchen_fridge_power_draw_lost', 'on') %} {% set ns.items = ns.items + ['Power lost - no power draw'] %} {% endif %} - {% if not temperature_missing and (states('sensor.main_kitchen_fridge_temperature_calibrated') | float(0)) > threshold %} + {% if is_state('binary_sensor.main_kitchen_fridge_door_open_too_long', 'on') %} + {% set ns.items = ns.items + ['Door open too long'] %} + {% elif not temperature_missing and is_state('binary_sensor.main_kitchen_fridge_temperature_recovery_grace', 'on') %} + {% set ns.items = ns.items + ['Temperature alert delayed - door recently open'] %} + {% elif not temperature_missing and (states('sensor.main_kitchen_fridge_temperature_calibrated') | float(0)) > threshold %} {% if (states('sensor.main_kitchen_fridge_temperature_change_rate') | float(0)) >= 2 %} {% set ns.items = ns.items + ['Warming rapidly - possible open door'] %} {% else %} @@ -467,8 +505,11 @@ template: {% set percentile = states('sensor.main_kitchen_fridge_temperature_95th_percentile_7_days') | float(none) %} {{ [10, percentile + 0.5] | min if percentile is not none else 10 }} change_rate: "{{ states('sensor.main_kitchen_fridge_temperature_change_rate') }}" - battery: "{{ states('sensor.unk_manufacturer_unk_model_battery') }}" + humidity: "{{ states('sensor.main_kitchen_fridge_temperature_x65ts_humidity') }}" + battery: "{{ states('sensor.main_kitchen_fridge_temperature_x65ts_battery') }}" power: "{{ states('sensor.upstairs_fridge_power_x31pp_power') }}" + door_open: "{{ is_state('binary_sensor.main_kitchen_fridge_door_open', 'on') }}" + illuminance: "{{ states('sensor.main_kitchen_fridge_light_sensor_x64ls_illuminance') }}" - name: "Drinks Fridge Issue" unique_id: drinks_fridge_issue @@ -477,12 +518,13 @@ template: state: >- {% set temperature_missing = not has_value('sensor.drinks_fridge_environment_lcd_btt01_temperature') %} {% set battery_missing = not has_value('sensor.drinks_fridge_environment_lcd_btt01_battery') %} + {% set illuminance_missing = not has_value('sensor.drinks_fridge_light_sensor_x35ls_illuminance') %} {% set power_missing = not has_value('sensor.drinks_fridge_power_x59pm_power') %} {% set percentile = states('sensor.drinks_fridge_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [10, percentile + 3] | min if percentile is not none else 10 %} {{ temperature_missing - or battery_missing + or illuminance_missing or (states('sensor.drinks_fridge_environment_lcd_btt01_battery') | float(-1)) <= 20 or states('switch.drinks_fridge_power_x59pm') != 'on' or power_missing @@ -500,11 +542,15 @@ template: {% set ns = namespace(items=[]) %} {% set temperature_missing = not has_value('sensor.drinks_fridge_environment_lcd_btt01_temperature') %} {% set battery_missing = not has_value('sensor.drinks_fridge_environment_lcd_btt01_battery') %} + {% set illuminance_missing = not has_value('sensor.drinks_fridge_light_sensor_x35ls_illuminance') %} {% set power_missing = not has_value('sensor.drinks_fridge_power_x59pm_power') %} {% set percentile = states('sensor.drinks_fridge_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [10, percentile + 3] | min if percentile is not none else 10 %} - {% if temperature_missing or battery_missing %} - {% set ns.items = ns.items + ['Monitor offline'] %} + {% if temperature_missing %} + {% set ns.items = ns.items + ['Temperature monitor unavailable'] %} + {% endif %} + {% if illuminance_missing %} + {% set ns.items = ns.items + ['Door monitor unavailable'] %} {% endif %} {% if not battery_missing and (states('sensor.drinks_fridge_environment_lcd_btt01_battery') | float(-1)) <= 20 %} {% set ns.items = ns.items + ['Low monitor battery'] %} @@ -591,12 +637,13 @@ template: state: >- {% set temperature_missing = not has_value('sensor.downstairs_fridge_temperature_x43ts_temperature') %} {% set battery_missing = not has_value('sensor.downstairs_fridge_temperature_x43ts_battery') %} + {% set illuminance_missing = not has_value('sensor.downstairs_fridge_light_sensor_x58ls_illuminance') %} {% set power_missing = not has_value('sensor.downstairs_fridge_power_x41pp_power') %} {% set percentile = states('sensor.downstairs_fridge_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [10, percentile + 0.5] | min if percentile is not none else 10 %} {{ temperature_missing - or battery_missing + or illuminance_missing or (states('sensor.downstairs_fridge_temperature_x43ts_battery') | float(-1)) <= 20 or is_state('binary_sensor.downstairs_fridge_door_open_too_long', 'on') or ( @@ -614,11 +661,15 @@ template: {% set ns = namespace(items=[]) %} {% set temperature_missing = not has_value('sensor.downstairs_fridge_temperature_x43ts_temperature') %} {% set battery_missing = not has_value('sensor.downstairs_fridge_temperature_x43ts_battery') %} + {% set illuminance_missing = not has_value('sensor.downstairs_fridge_light_sensor_x58ls_illuminance') %} {% set power_missing = not has_value('sensor.downstairs_fridge_power_x41pp_power') %} {% set percentile = states('sensor.downstairs_fridge_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [10, percentile + 0.5] | min if percentile is not none else 10 %} - {% if temperature_missing or battery_missing %} - {% set ns.items = ns.items + ['Monitor offline'] %} + {% if temperature_missing %} + {% set ns.items = ns.items + ['Temperature monitor unavailable'] %} + {% endif %} + {% if illuminance_missing %} + {% set ns.items = ns.items + ['Door monitor unavailable'] %} {% endif %} {% if not battery_missing and (states('sensor.downstairs_fridge_temperature_x43ts_battery') | float(-1)) <= 20 %} {% set ns.items = ns.items + ['Low monitor battery'] %} @@ -850,16 +901,21 @@ template: state: >- {{ 'Issue' - if is_state('binary_sensor.drinks_fridge_door_open_too_long', 'on') + if is_state('binary_sensor.main_kitchen_fridge_door_open_too_long', 'on') + or is_state('binary_sensor.drinks_fridge_door_open_too_long', 'on') or is_state('binary_sensor.downstairs_fridge_door_open_too_long', 'on') + or is_state('binary_sensor.downstairs_freezer_door_open_too_long', 'on') else 'Open' - if is_state('binary_sensor.drinks_fridge_door_open', 'on') + if is_state('binary_sensor.main_kitchen_fridge_door_open', 'on') + or is_state('binary_sensor.drinks_fridge_door_open', 'on') or is_state('binary_sensor.downstairs_fridge_door_open', 'on') + or is_state('binary_sensor.downstairs_freezer_door_open', 'on') else 'Issue' if expand( 'binary_sensor.main_kitchen_fridge_issue', 'binary_sensor.drinks_fridge_issue', 'binary_sensor.downstairs_fridge_issue', + 'binary_sensor.downstairs_freezer_issue', 'binary_sensor.wine_fridge_issue', 'binary_sensor.drinks_freezer_issue', 'binary_sensor.vertical_freezer_issue' @@ -876,6 +932,7 @@ template: 'binary_sensor.main_kitchen_fridge_issue', 'binary_sensor.drinks_fridge_issue', 'binary_sensor.downstairs_fridge_issue', + 'binary_sensor.downstairs_freezer_issue', 'binary_sensor.wine_fridge_issue', 'binary_sensor.drinks_freezer_issue', 'binary_sensor.vertical_freezer_issue' @@ -887,8 +944,10 @@ template: open_count: >- {{ [ + is_state('binary_sensor.main_kitchen_fridge_door_open', 'on'), is_state('binary_sensor.drinks_fridge_door_open', 'on'), - is_state('binary_sensor.downstairs_fridge_door_open', 'on') + is_state('binary_sensor.downstairs_fridge_door_open', 'on'), + is_state('binary_sensor.downstairs_freezer_door_open', 'on') ] | select | list @@ -900,6 +959,7 @@ template: 'binary_sensor.main_kitchen_fridge_issue', 'binary_sensor.drinks_fridge_issue', 'binary_sensor.downstairs_fridge_issue', + 'binary_sensor.downstairs_freezer_issue', 'binary_sensor.wine_fridge_issue', 'binary_sensor.drinks_freezer_issue', 'binary_sensor.vertical_freezer_issue' diff --git a/packages/fridge_temperatures.yaml b/packages/fridge_temperatures.yaml index 078a30d..8298ee6 100644 --- a/packages/fridge_temperatures.yaml +++ b/packages/fridge_temperatures.yaml @@ -1,8 +1,8 @@ #:########################################################################################:# # Package: Refrigerator Temperature MQTT Publishing # File: fridge_temperatures.yaml -# Version: v1.1.0 -# Date: 2026-08-17 +# Version: v1.2.0 +# Date: 2026-08-30 # # Purpose: # Publishes refrigerator and freezer temperature changes to household MQTT @@ -19,6 +19,7 @@ # - Home Assistant MQTT integration and broker connectivity. # # Version History: +# - v1.2.0 (2026-08-30): Moved the calibrated main fridge source to X65TS. # - v1.1.0 (2026-08-17): Added the -0.84 C main fridge sensor calibration. # - v1.0.0 (2024-09-04): Initial refrigerator temperature publishing package. #:########################################################################################:# @@ -30,9 +31,9 @@ template: device_class: temperature state_class: measurement unit_of_measurement: "°C" - availability: "{{ has_value('sensor.main_kitchen_fridge_temp_zth08') }}" + availability: "{{ has_value('sensor.main_kitchen_fridge_temperature_x65ts_temperature') }}" state: >- - {{ (states('sensor.main_kitchen_fridge_temp_zth08') | float - 0.84) | round(2) }} + {{ (states('sensor.main_kitchen_fridge_temperature_x65ts_temperature') | float - 0.84) | round(2) }} automation: - id: publish_fridge_temps_drinks_fridge diff --git a/packages/xmas_tree_water_warning.yaml b/packages/xmas_tree_water_warning.yaml index 9c373ab..d013f8e 100644 --- a/packages/xmas_tree_water_warning.yaml +++ b/packages/xmas_tree_water_warning.yaml @@ -21,12 +21,12 @@ ############################################################################################ # VARIABLES (YAML ANCHORS) - HA SAFE CONTAINER -# - We store anchors under a valid customize dictionary. -# - Customizing a non-existent entity is harmless. +# - We store anchors under an existing helper customization so Spook does not +# flag an unknown entity. ############################################################################################ homeassistant: customize: - sensor.xmas_tree_package_anchors: + input_boolean.enable_xmas_tree_warning_light: water_sensor_input: &water_sensor_input binary_sensor.xmas_tree_water_x32ld_water_leak warning_light_output: &warning_light_output switch.spare_tuya_smart_plug_zigbee_x31sp @@ -117,4 +117,4 @@ automation: default: - service: homeassistant.turn_off target: - entity_id: *warning_light_output + entity_id: *warning_light_output \ No newline at end of file diff --git a/packages/xmas_tree_water_warning.yaml.bak b/packages/xmas_tree_water_warning.yaml.bak new file mode 100644 index 0000000..9c373ab --- /dev/null +++ b/packages/xmas_tree_water_warning.yaml.bak @@ -0,0 +1,120 @@ +############################################################################################ +# PACKAGE: Xmas Tree Water Warning Light +# FILE: xmas_tree_water_warning.yaml +# +# PURPOSE: +# - Monitors a water sensor. +# - If the sensor reads FALSE (off) AND the warning light is enabled: +# 1) Blink immediately when the sensor first goes false +# 2) Continue blinking every 5 minutes while it remains false +# +# DESIGN GUARANTEE: +# - The warning light ALWAYS ends in the same state it was in +# before the automation started. +# +# IMPORTANT: +# - Home Assistant templates cannot safely use YAML anchors inside states(). +# - If you change the entity ids below, update BOTH: +# - the anchors, and +# - the two template strings in conditions/action variables. +############################################################################################ + +############################################################################################ +# VARIABLES (YAML ANCHORS) - HA SAFE CONTAINER +# - We store anchors under a valid customize dictionary. +# - Customizing a non-existent entity is harmless. +############################################################################################ +homeassistant: + customize: + sensor.xmas_tree_package_anchors: + water_sensor_input: &water_sensor_input binary_sensor.xmas_tree_water_x32ld_water_leak + warning_light_output: &warning_light_output switch.spare_tuya_smart_plug_zigbee_x31sp + +############################################################################################ +# ENABLE CONTROL +############################################################################################ +input_boolean: + enable_xmas_tree_warning_light: + name: Enable Xmas Tree Warning Light + icon: mdi:alarm-light-outline + +############################################################################################ +# MIRRORED BINARY SENSOR (AS REQUESTED) +############################################################################################ +template: + - binary_sensor: + - name: Enable Xmas Tree Warning Light + unique_id: enable_xmas_tree_warning_light_binary_sensor + icon: mdi:alarm-light-outline + state: "{{ is_state('input_boolean.enable_xmas_tree_warning_light', 'on') }}" + +############################################################################################ +# AUTOMATION: Flash warning light immediately and then every 5 minutes while sensor is FALSE +############################################################################################ +automation: + - id: xmas_tree_water_warning_light_flash + alias: "Xmas Tree - Water Warning Light Flash" + description: > + If enabled and the water sensor reads false (off), blink immediately on transition + to false, and also every 5 minutes while it stays false. + mode: single + trigger: + # Immediate blink when the water sensor transitions to FALSE (off) + - platform: state + entity_id: *water_sensor_input + to: "off" + + # Repeat reminder every 5 minutes while condition remains true + - platform: time_pattern + minutes: "/5" + + condition: + # Only run if the warning feature is enabled + - condition: state + entity_id: input_boolean.enable_xmas_tree_warning_light + state: "on" + + # Only run if water sensor is FALSE (off) and valid + - condition: template + value_template: "{{ states('binary_sensor.xmas_tree_water_x32ld_water_leak') == 'off' }}" + + # Only run if warning light output is available + - condition: template + value_template: "{{ states('switch.spare_tuya_smart_plug_zigbee_x31sp') not in ['unknown','unavailable'] }}" + + action: + # Capture original state so we can restore it at the end + - variables: + original_state: "{{ states('switch.spare_tuya_smart_plug_zigbee_x31sp') }}" + + # Blink pattern: + # - Toggle for 1 second + # - Toggle back for 1 second + # - Repeat 5 times + - repeat: + count: 5 + sequence: + - service: homeassistant.toggle + target: + entity_id: *warning_light_output + - delay: + seconds: 1 + - service: homeassistant.toggle + target: + entity_id: *warning_light_output + - delay: + seconds: 1 + + # Restore the original state explicitly + - choose: + - conditions: + - condition: template + value_template: "{{ original_state == 'on' }}" + sequence: + - service: homeassistant.turn_on + target: + entity_id: *warning_light_output + default: + - service: homeassistant.turn_off + target: + entity_id: *warning_light_output