#:########################################################################################:# # PACKAGE: Fridge and Freezer Monitoring # FILE: packages/fridge_freezer_monitoring.yaml # 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. # - v1.8.4 2026-08-24: Flags a Drinks Fridge power loss only after six # continuous hours below 1 W. # - v1.8.3 2026-08-24: Excludes normal Drinks Fridge compressor-off cycles # from power alerts. # - v1.8.2 2026-08-24: Widens Drinks Fridge and Vertical Freezer adaptive # temperature ceilings to accommodate confirmed normal compressor cycles. # - v1.8.1 2026-08-24: Excludes normal Vertical Freezer compressor-off cycles # from power alerts and widens its learned temperature allowance. # - v1.8.0 2026-08-24: Adds Drinks Fridge and Vertical Freezer plug monitoring. # - Adds illuminance-based door monitoring for the Downstairs Fridge. # - Adds Drinks Fridge door detection from its illuminance sensor. # - Makes an open door an issue after seven minutes and delays temperature # alerts while the door is open or cooling recovers after closure. # - v1.5.0 2026-08-22 # - Requires five minutes of sub-1 W draw before reporting Main Kitchen Fridge power loss. # - Moved Main Kitchen Fridge power monitoring to the Zigbee X31PP plug. # - v1.3.0 2026-08-17: Uses the calibrated Main Kitchen Fridge temperature entity throughout. # - Uses the calibrated Main Kitchen Fridge temperature entity throughout. # - Added adaptive temperature, battery, humidity, and power monitoring for # the Downstairs Fridge. # - v1.1.0 2026-08-16: Reduced the adaptive temperature margin and issue persistence time. #:########################################################################################:# # PURPOSE: # Detect missing monitoring, low batteries, loss of Main Fridge power, and # abnormal warming, including door-aware handling where a sensor is fitted. #:########################################################################################:# # BEHAVIOUR: # - Learns a continuously rolling seven-day average and 95th percentile. # - Limits each learned threshold with an appliance-specific safety maximum. # - 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 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. #:########################################################################################:# # NOTES: # - Learned thresholds use the lower of 95th percentile + 0.5 C or the fixed # safety limit, so a cooling failure cannot gradually become normal. # - A rise rate of at least 2 C/hour labels an over-temperature condition as # possible open-door warming; slower warming is labelled loss of cooling. # - 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 X65TS temperature is adjusted by -0.84 C to match the # reference thermometer. # - Wine-fridge ESPHome telemetry is intentionally excluded. #:########################################################################################:# # DEPENDENCIES: # - Seven configured fridge/freezer temperature and battery monitor pairs. # - switch.upstairs_fridge_power_x31pp # - sensor.upstairs_fridge_power_x31pp_power # - switch.downstairs_fridge_power_x41pp # - sensor.downstairs_fridge_power_x41pp_power # - switch.drinks_fridge_power_x59pm # - sensor.drinks_fridge_power_x59pm_power # - switch.vertical_freezer_power_x60pm # - sensor.vertical_freezer_power_x60pm_power # - 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: #:######################################################################################:# # MAIN KITCHEN FRIDGE #:######################################################################################:# - platform: statistics name: "Main Kitchen Fridge Temperature Average 7 Days" unique_id: main_kitchen_fridge_temperature_average_7_days entity_id: sensor.main_kitchen_fridge_temperature_calibrated state_characteristic: average_step max_age: days: 7 precision: 1 - platform: statistics name: "Main Kitchen Fridge Temperature 95th Percentile 7 Days" unique_id: main_kitchen_fridge_temperature_95th_percentile_7_days entity_id: sensor.main_kitchen_fridge_temperature_calibrated state_characteristic: percentile percentile: 95 max_age: days: 7 precision: 1 - platform: derivative name: "Main Kitchen Fridge Temperature Change Rate" unique_id: main_kitchen_fridge_temperature_change_rate source: sensor.main_kitchen_fridge_temperature_calibrated unit_time: h time_window: "00:30:00" max_sub_interval: "00:05:00" round: 1 #:######################################################################################:# # DRINKS FRIDGE #:######################################################################################:# - platform: statistics name: "Drinks Fridge Temperature Average 7 Days" unique_id: drinks_fridge_temperature_average_7_days entity_id: sensor.drinks_fridge_environment_lcd_btt01_temperature state_characteristic: average_step max_age: days: 7 precision: 1 - platform: statistics name: "Drinks Fridge Temperature 95th Percentile 7 Days" unique_id: drinks_fridge_temperature_95th_percentile_7_days entity_id: sensor.drinks_fridge_environment_lcd_btt01_temperature state_characteristic: percentile percentile: 95 max_age: days: 7 precision: 1 - platform: derivative name: "Drinks Fridge Temperature Change Rate" unique_id: drinks_fridge_temperature_change_rate source: sensor.drinks_fridge_environment_lcd_btt01_temperature unit_time: h time_window: "00:30:00" max_sub_interval: "00:05:00" round: 1 #:######################################################################################:# # DOWNSTAIRS FRIDGE #:######################################################################################:# - platform: statistics name: "Downstairs Fridge Temperature Average 7 Days" unique_id: downstairs_fridge_temperature_average_7_days entity_id: sensor.downstairs_fridge_temperature_x43ts_temperature state_characteristic: average_step max_age: days: 7 precision: 1 - platform: statistics name: "Downstairs Fridge Temperature 95th Percentile 7 Days" unique_id: downstairs_fridge_temperature_95th_percentile_7_days entity_id: sensor.downstairs_fridge_temperature_x43ts_temperature state_characteristic: percentile percentile: 95 max_age: days: 7 precision: 1 - platform: derivative name: "Downstairs Fridge Temperature Change Rate" unique_id: downstairs_fridge_temperature_change_rate source: sensor.downstairs_fridge_temperature_x43ts_temperature unit_time: h time_window: "00:30:00" max_sub_interval: "00:05:00" round: 1 #:######################################################################################:# # DOWNSTAIRS FREEZER #:######################################################################################:# - platform: statistics name: "Downstairs Freezer Temperature Average 7 Days" unique_id: downstairs_freezer_temperature_average_7_days entity_id: sensor.downstairs_freezer_temperature_x62ts_temperature state_characteristic: average_step max_age: days: 7 precision: 1 - platform: statistics name: "Downstairs Freezer Temperature 95th Percentile 7 Days" unique_id: downstairs_freezer_temperature_95th_percentile_7_days entity_id: sensor.downstairs_freezer_temperature_x62ts_temperature state_characteristic: percentile percentile: 95 max_age: days: 7 precision: 1 - platform: derivative name: "Downstairs Freezer Temperature Change Rate" unique_id: downstairs_freezer_temperature_change_rate source: sensor.downstairs_freezer_temperature_x62ts_temperature unit_time: h time_window: "00:30:00" max_sub_interval: "00:05:00" round: 1 #:######################################################################################:# # WINE FRIDGE #:######################################################################################:# - platform: statistics name: "Wine Fridge Temperature Average 7 Days" unique_id: wine_fridge_temperature_average_7_days entity_id: sensor.lcd_temp_humidity_btt04_temperature state_characteristic: average_step max_age: days: 7 precision: 1 - platform: statistics name: "Wine Fridge Temperature 95th Percentile 7 Days" unique_id: wine_fridge_temperature_95th_percentile_7_days entity_id: sensor.lcd_temp_humidity_btt04_temperature state_characteristic: percentile percentile: 95 max_age: days: 7 precision: 1 - platform: derivative name: "Wine Fridge Temperature Change Rate" unique_id: wine_fridge_temperature_change_rate source: sensor.lcd_temp_humidity_btt04_temperature unit_time: h time_window: "00:30:00" max_sub_interval: "00:05:00" round: 1 #:######################################################################################:# # DRINKS FREEZER #:######################################################################################:# - platform: statistics name: "Drinks Freezer Temperature Average 7 Days" unique_id: drinks_freezer_temperature_average_7_days entity_id: sensor.drinks_freezer_environment_lcd_btt02_temperature state_characteristic: average_step max_age: days: 7 precision: 1 - platform: statistics name: "Drinks Freezer Temperature 95th Percentile 7 Days" unique_id: drinks_freezer_temperature_95th_percentile_7_days entity_id: sensor.drinks_freezer_environment_lcd_btt02_temperature state_characteristic: percentile percentile: 95 max_age: days: 7 precision: 1 - platform: derivative name: "Drinks Freezer Temperature Change Rate" unique_id: drinks_freezer_temperature_change_rate source: sensor.drinks_freezer_environment_lcd_btt02_temperature unit_time: h time_window: "00:30:00" max_sub_interval: "00:05:00" round: 1 #:######################################################################################:# # VERTICAL FREEZER #:######################################################################################:# - platform: statistics name: "Vertical Freezer Temperature Average 7 Days" unique_id: vertical_freezer_temperature_average_7_days entity_id: sensor.lcd_temp_humidity_btt03_temperature state_characteristic: average_step max_age: days: 7 precision: 1 - platform: statistics name: "Vertical Freezer Temperature 95th Percentile 7 Days" unique_id: vertical_freezer_temperature_95th_percentile_7_days entity_id: sensor.lcd_temp_humidity_btt03_temperature state_characteristic: percentile percentile: 95 max_age: days: 7 precision: 1 - platform: derivative name: "Vertical Freezer Temperature Change Rate" unique_id: vertical_freezer_temperature_change_rate source: sensor.lcd_temp_humidity_btt03_temperature unit_time: h time_window: "00:30:00" max_sub_interval: "00:05:00" round: 1 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 state: >- {{ has_value('sensor.drinks_fridge_light_sensor_x35ls_illuminance') and (states('sensor.drinks_fridge_light_sensor_x35ls_illuminance') | float(0)) >= 10 }} # Retain the grace period after closure so the fridge can start cooling. - name: "Drinks Fridge Temperature Recovery Grace" unique_id: drinks_fridge_temperature_recovery_grace delay_off: "00:15:00" state: "{{ is_state('binary_sensor.drinks_fridge_door_open', 'on') }}" - name: "Drinks Fridge Door Open Too Long" unique_id: drinks_fridge_door_open_too_long device_class: problem delay_on: "00:07:00" state: "{{ is_state('binary_sensor.drinks_fridge_door_open', 'on') }}" - name: "Downstairs Fridge Door Open" unique_id: downstairs_fridge_door_open device_class: door state: >- {{ has_value('sensor.downstairs_fridge_light_sensor_x58ls_illuminance') and (states('sensor.downstairs_fridge_light_sensor_x58ls_illuminance') | float(0)) >= 10 }} # Retain the grace period after closure so the fridge can start cooling. - name: "Downstairs Fridge Temperature Recovery Grace" unique_id: downstairs_fridge_temperature_recovery_grace delay_off: "00:15:00" state: "{{ is_state('binary_sensor.downstairs_fridge_door_open', 'on') }}" - name: "Downstairs Fridge Door Open Too Long" unique_id: downstairs_fridge_door_open_too_long device_class: problem delay_on: "00:07:00" state: "{{ is_state('binary_sensor.downstairs_fridge_door_open', 'on') }}" - name: "Downstairs Freezer Door Open" unique_id: downstairs_freezer_door_open device_class: door state: >- {{ has_value('sensor.downstairs_freezer_light_sensor_x61ls_illuminance') and (states('sensor.downstairs_freezer_light_sensor_x61ls_illuminance') | float(0)) >= 10 }} # Retain the grace period after closure so the freezer can start cooling. - name: "Downstairs Freezer Temperature Recovery Grace" unique_id: downstairs_freezer_temperature_recovery_grace delay_off: "00:15:00" state: "{{ is_state('binary_sensor.downstairs_freezer_door_open', 'on') }}" - name: "Downstairs Freezer Door Open Too Long" unique_id: downstairs_freezer_door_open_too_long device_class: problem delay_on: "00:07:00" state: "{{ is_state('binary_sensor.downstairs_freezer_door_open', 'on') }}" #:######################################################################################:# # APPLIANCE ISSUE SENSORS #:######################################################################################:# - binary_sensor: - name: "Main Kitchen Fridge Power Draw Lost" unique_id: main_kitchen_fridge_power_draw_lost device_class: problem delay_on: "04:00:00" state: >- {{ is_state('switch.upstairs_fridge_power_x31pp', 'on') and has_value('sensor.upstairs_fridge_power_x31pp_power') and (states('sensor.upstairs_fridge_power_x31pp_power') | float(0)) < 1 }} # A compressor-off interval is normal; measure the uninterrupted idle time # explicitly so a template reload cannot restore this as an active fault. - name: "Drinks Fridge Power Draw Lost" unique_id: drinks_fridge_power_draw_lost device_class: problem state: >- {% set power = states('sensor.drinks_fridge_power_x59pm_power') | float(none) %} {% set changed = states.sensor.drinks_fridge_power_x59pm_power.last_changed %} {{ is_state('switch.drinks_fridge_power_x59pm', 'on') and power is not none and power < 1 and (now() - changed).total_seconds() >= 4 * 60 * 60 }} - name: "Downstairs Fridge Power Draw Lost" unique_id: downstairs_fridge_power_draw_lost device_class: problem state: >- {% set power = states('sensor.downstairs_fridge_power_x41pp_power') | float(none) %} {% set changed = states.sensor.downstairs_fridge_power_x41pp_power.last_changed %} {{ is_state('switch.downstairs_fridge_power_x41pp', 'on') and power is not none and power < 1 and (now() - changed).total_seconds() >= 4 * 60 * 60 }} - name: "Vertical Freezer Power Draw Lost" unique_id: vertical_freezer_power_draw_lost device_class: problem state: >- {% set power = states('sensor.vertical_freezer_power_x60pm_power') | float(none) %} {% set changed = states.sensor.vertical_freezer_power_x60pm_power.last_changed %} {{ is_state('switch.vertical_freezer_power_x60pm', 'on') and power is not none and power < 1 and (now() - changed).total_seconds() >= 4 * 60 * 60 }} - name: "Main Kitchen Fridge Issue" unique_id: main_kitchen_fridge_issue device_class: problem 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.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 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') }} attributes: appliance: "Main Kitchen Fridge" issues: >- {% set ns = namespace(items=[]) %} {% set temperature_missing = not has_value('sensor.main_kitchen_fridge_temperature_calibrated') %} {% 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 %} {% 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.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 %} {% set ns.items = ns.items + ['Power monitoring unavailable'] %} {% elif not is_state('switch.upstairs_fridge_power_x31pp', 'on') %} {% set ns.items = ns.items + ['Power switched off'] %} {% elif is_state('binary_sensor.main_kitchen_fridge_power_draw_lost', 'on') %} {% set ns.items = ns.items + ['Power lost - no power draw'] %} {% endif %} {% 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 %} {% set ns.items = ns.items + ['Temperature too high - possible loss of cooling'] %} {% endif %} {% endif %} {{ ns.items }} temperature: "{{ states('sensor.main_kitchen_fridge_temperature_calibrated') }}" average_7_days: "{{ states('sensor.main_kitchen_fridge_temperature_average_7_days') }}" alert_threshold: >- {% 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') }}" 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 device_class: problem delay_on: "00:01:00" 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 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 or is_state('binary_sensor.drinks_fridge_power_draw_lost', 'on') or is_state('binary_sensor.drinks_fridge_door_open_too_long', 'on') or ( not temperature_missing and not is_state('binary_sensor.drinks_fridge_temperature_recovery_grace', 'on') and (states('sensor.drinks_fridge_environment_lcd_btt01_temperature') | float(0)) > threshold ) }} attributes: appliance: "Drinks Fridge" issues: >- {% 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 %} {% 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'] %} {% endif %} {% if states('switch.drinks_fridge_power_x59pm') in ['unknown', 'unavailable'] or power_missing %} {% set ns.items = ns.items + ['Power monitoring unavailable'] %} {% elif not is_state('switch.drinks_fridge_power_x59pm', 'on') %} {% set ns.items = ns.items + ['Power switched off'] %} {% elif is_state('binary_sensor.drinks_fridge_power_draw_lost', 'on') %} {% set ns.items = ns.items + ['Power lost - no power draw for 4 hours'] %} {% endif %} {% if is_state('binary_sensor.drinks_fridge_door_open_too_long', 'on') %} {% set ns.items = ns.items + ['Door open too long'] %} {% elif not temperature_missing and is_state('binary_sensor.drinks_fridge_temperature_recovery_grace', 'on') %} {% set ns.items = ns.items + ['Temperature alert delayed - door recently open'] %} {% elif not temperature_missing and (states('sensor.drinks_fridge_environment_lcd_btt01_temperature') | float(0)) > threshold %} {% if (states('sensor.drinks_fridge_temperature_change_rate') | float(0)) >= 2 %} {% set ns.items = ns.items + ['Warming rapidly - possible open door'] %} {% else %} {% set ns.items = ns.items + ['Temperature too high - possible loss of cooling'] %} {% endif %} {% endif %} {{ ns.items }} temperature: "{{ states('sensor.drinks_fridge_environment_lcd_btt01_temperature') }}" average_7_days: "{{ states('sensor.drinks_fridge_temperature_average_7_days') }}" alert_threshold: >- {% set percentile = states('sensor.drinks_fridge_temperature_95th_percentile_7_days') | float(none) %} {{ [10, percentile + 3] | min if percentile is not none else 10 }} change_rate: "{{ states('sensor.drinks_fridge_temperature_change_rate') }}" battery: "{{ states('sensor.drinks_fridge_environment_lcd_btt01_battery') }}" power: "{{ states('sensor.drinks_fridge_power_x59pm_power') }}" door_open: "{{ is_state('binary_sensor.drinks_fridge_door_open', 'on') }}" illuminance: "{{ states('sensor.drinks_fridge_light_sensor_x35ls_illuminance') }}" - name: "Wine Fridge Issue" unique_id: wine_fridge_issue device_class: problem delay_on: "00:01:00" state: >- {% set temperature_missing = not has_value('sensor.lcd_temp_humidity_btt04_temperature') %} {% set battery_missing = not has_value('sensor.lcd_temp_humidity_btt04_battery') %} {% set percentile = states('sensor.wine_fridge_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [9, percentile + 0.5] | min if percentile is not none else 9 %} {{ temperature_missing or battery_missing or (states('sensor.lcd_temp_humidity_btt04_battery') | float(-1)) <= 20 or (not temperature_missing and (states('sensor.lcd_temp_humidity_btt04_temperature') | float(0)) > threshold) }} attributes: appliance: "Wine Fridge" issues: >- {% set ns = namespace(items=[]) %} {% set temperature_missing = not has_value('sensor.lcd_temp_humidity_btt04_temperature') %} {% set battery_missing = not has_value('sensor.lcd_temp_humidity_btt04_battery') %} {% set percentile = states('sensor.wine_fridge_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [9, percentile + 0.5] | min if percentile is not none else 9 %} {% if temperature_missing or battery_missing %} {% set ns.items = ns.items + ['Monitor offline'] %} {% endif %} {% if not battery_missing and (states('sensor.lcd_temp_humidity_btt04_battery') | float(-1)) <= 20 %} {% set ns.items = ns.items + ['Low monitor battery'] %} {% endif %} {% if not temperature_missing and (states('sensor.lcd_temp_humidity_btt04_temperature') | float(0)) > threshold %} {% if (states('sensor.wine_fridge_temperature_change_rate') | float(0)) >= 2 %} {% set ns.items = ns.items + ['Warming rapidly - possible open door'] %} {% else %} {% set ns.items = ns.items + ['Temperature too high - possible loss of cooling'] %} {% endif %} {% endif %} {{ ns.items }} temperature: "{{ states('sensor.lcd_temp_humidity_btt04_temperature') }}" average_7_days: "{{ states('sensor.wine_fridge_temperature_average_7_days') }}" alert_threshold: >- {% set percentile = states('sensor.wine_fridge_temperature_95th_percentile_7_days') | float(none) %} {{ [9, percentile + 0.5] | min if percentile is not none else 9 }} change_rate: "{{ states('sensor.wine_fridge_temperature_change_rate') }}" battery: "{{ states('sensor.lcd_temp_humidity_btt04_battery') }}" - name: "Downstairs Fridge Issue" unique_id: downstairs_fridge_issue device_class: problem delay_on: "00:01:00" 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 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 ( not temperature_missing and not is_state('binary_sensor.downstairs_fridge_temperature_recovery_grace', 'on') and (states('sensor.downstairs_fridge_temperature_x43ts_temperature') | float(0)) > threshold ) or states('switch.downstairs_fridge_power_x41pp') != 'on' or power_missing or is_state('binary_sensor.downstairs_fridge_power_draw_lost', 'on') }} attributes: appliance: "Downstairs Fridge" issues: >- {% 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 %} {% 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'] %} {% endif %} {% if states('switch.downstairs_fridge_power_x41pp') in ['unknown', 'unavailable'] or power_missing %} {% set ns.items = ns.items + ['Power monitoring unavailable'] %} {% elif not is_state('switch.downstairs_fridge_power_x41pp', 'on') %} {% set ns.items = ns.items + ['Power switched off'] %} {% elif is_state('binary_sensor.downstairs_fridge_power_draw_lost', 'on') %} {% set ns.items = ns.items + ['Power lost - no power draw for 4 hours'] %} {% endif %} {% if is_state('binary_sensor.downstairs_fridge_door_open_too_long', 'on') %} {% set ns.items = ns.items + ['Door open too long'] %} {% elif not temperature_missing and is_state('binary_sensor.downstairs_fridge_temperature_recovery_grace', 'on') %} {% set ns.items = ns.items + ['Temperature alert delayed - door recently open'] %} {% elif not temperature_missing and (states('sensor.downstairs_fridge_temperature_x43ts_temperature') | float(0)) > threshold %} {% if (states('sensor.downstairs_fridge_temperature_change_rate') | float(0)) >= 2 %} {% set ns.items = ns.items + ['Warming rapidly - possible open door'] %} {% else %} {% set ns.items = ns.items + ['Temperature too high - possible loss of cooling'] %} {% endif %} {% endif %} {{ ns.items }} temperature: "{{ states('sensor.downstairs_fridge_temperature_x43ts_temperature') }}" humidity: "{{ states('sensor.downstairs_fridge_temperature_x43ts_humidity') }}" average_7_days: "{{ states('sensor.downstairs_fridge_temperature_average_7_days') }}" alert_threshold: >- {% set percentile = states('sensor.downstairs_fridge_temperature_95th_percentile_7_days') | float(none) %} {{ [10, percentile + 0.5] | min if percentile is not none else 10 }} change_rate: "{{ states('sensor.downstairs_fridge_temperature_change_rate') }}" battery: "{{ states('sensor.downstairs_fridge_temperature_x43ts_battery') }}" power: "{{ states('sensor.downstairs_fridge_power_x41pp_power') }}" door_open: "{{ is_state('binary_sensor.downstairs_fridge_door_open', 'on') }}" illuminance: "{{ states('sensor.downstairs_fridge_light_sensor_x58ls_illuminance') }}" - name: "Downstairs Freezer Issue" unique_id: downstairs_freezer_issue device_class: problem delay_on: "00:01:00" state: >- {% set temperature_missing = not has_value('sensor.downstairs_freezer_temperature_x62ts_temperature') %} {% set battery_missing = not has_value('sensor.downstairs_freezer_temperature_x62ts_battery') %} {% set power_missing = not has_value('sensor.downstairs_fridge_power_x41pp_power') %} {% set percentile = states('sensor.downstairs_freezer_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [-5, percentile + 0.5] | min if percentile is not none else -5 %} {{ temperature_missing or battery_missing or (states('sensor.downstairs_freezer_temperature_x62ts_battery') | float(-1)) <= 20 or is_state('binary_sensor.downstairs_freezer_door_open_too_long', 'on') or ( not temperature_missing and not is_state('binary_sensor.downstairs_freezer_temperature_recovery_grace', 'on') and (states('sensor.downstairs_freezer_temperature_x62ts_temperature') | float(0)) > threshold ) or states('switch.downstairs_fridge_power_x41pp') != 'on' or power_missing or is_state('binary_sensor.downstairs_fridge_power_draw_lost', 'on') }} attributes: appliance: "Downstairs Freezer" issues: >- {% set ns = namespace(items=[]) %} {% set temperature_missing = not has_value('sensor.downstairs_freezer_temperature_x62ts_temperature') %} {% set battery_missing = not has_value('sensor.downstairs_freezer_temperature_x62ts_battery') %} {% set power_missing = not has_value('sensor.downstairs_fridge_power_x41pp_power') %} {% set percentile = states('sensor.downstairs_freezer_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [-5, percentile + 0.5] | min if percentile is not none else -5 %} {% if temperature_missing or battery_missing %} {% set ns.items = ns.items + ['Monitor offline'] %} {% endif %} {% if not battery_missing and (states('sensor.downstairs_freezer_temperature_x62ts_battery') | float(-1)) <= 20 %} {% set ns.items = ns.items + ['Low monitor battery'] %} {% endif %} {% if states('switch.downstairs_fridge_power_x41pp') in ['unknown', 'unavailable'] or power_missing %} {% set ns.items = ns.items + ['Shared power monitoring unavailable'] %} {% elif not is_state('switch.downstairs_fridge_power_x41pp', 'on') %} {% set ns.items = ns.items + ['Shared power switched off'] %} {% elif is_state('binary_sensor.downstairs_fridge_power_draw_lost', 'on') %} {% set ns.items = ns.items + ['Shared power lost - no power draw for 4 hours'] %} {% endif %} {% if is_state('binary_sensor.downstairs_freezer_door_open_too_long', 'on') %} {% set ns.items = ns.items + ['Door open too long'] %} {% elif not temperature_missing and is_state('binary_sensor.downstairs_freezer_temperature_recovery_grace', 'on') %} {% set ns.items = ns.items + ['Temperature alert delayed - door recently open'] %} {% elif not temperature_missing and (states('sensor.downstairs_freezer_temperature_x62ts_temperature') | float(0)) > threshold %} {% if (states('sensor.downstairs_freezer_temperature_change_rate') | float(0)) >= 2 %} {% set ns.items = ns.items + ['Warming rapidly - possible open door'] %} {% else %} {% set ns.items = ns.items + ['Temperature too high - possible loss of cooling'] %} {% endif %} {% endif %} {{ ns.items }} temperature: "{{ states('sensor.downstairs_freezer_temperature_x62ts_temperature') }}" humidity: "{{ states('sensor.downstairs_freezer_temperature_x62ts_humidity') }}" average_7_days: "{{ states('sensor.downstairs_freezer_temperature_average_7_days') }}" alert_threshold: >- {% set percentile = states('sensor.downstairs_freezer_temperature_95th_percentile_7_days') | float(none) %} {{ [-5, percentile + 0.5] | min if percentile is not none else -5 }} change_rate: "{{ states('sensor.downstairs_freezer_temperature_change_rate') }}" battery: "{{ states('sensor.downstairs_freezer_temperature_x62ts_battery') }}" power: "{{ states('sensor.downstairs_fridge_power_x41pp_power') }}" door_open: "{{ is_state('binary_sensor.downstairs_freezer_door_open', 'on') }}" illuminance: "{{ states('sensor.downstairs_freezer_light_sensor_x61ls_illuminance') }}" - name: "Drinks Freezer Issue" unique_id: drinks_freezer_issue device_class: problem delay_on: "00:01:00" state: >- {% set temperature_missing = not has_value('sensor.drinks_freezer_environment_lcd_btt02_temperature') %} {% set battery_missing = not has_value('sensor.drinks_freezer_environment_lcd_btt02_battery') %} {% set power_missing = not has_value('sensor.drinks_fridge_power_x59pm_power') %} {% set percentile = states('sensor.drinks_freezer_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [-5, percentile + 0.5] | min if percentile is not none else -5 %} {{ temperature_missing or battery_missing or (states('sensor.drinks_freezer_environment_lcd_btt02_battery') | float(-1)) <= 20 or states('switch.drinks_fridge_power_x59pm') != 'on' or power_missing or is_state('binary_sensor.drinks_fridge_power_draw_lost', 'on') or (not temperature_missing and (states('sensor.drinks_freezer_environment_lcd_btt02_temperature') | float(0)) > threshold) }} attributes: appliance: "Drinks Freezer" issues: >- {% set ns = namespace(items=[]) %} {% set temperature_missing = not has_value('sensor.drinks_freezer_environment_lcd_btt02_temperature') %} {% set battery_missing = not has_value('sensor.drinks_freezer_environment_lcd_btt02_battery') %} {% set power_missing = not has_value('sensor.drinks_fridge_power_x59pm_power') %} {% set percentile = states('sensor.drinks_freezer_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [-5, percentile + 0.5] | min if percentile is not none else -5 %} {% if temperature_missing or battery_missing %} {% set ns.items = ns.items + ['Monitor offline'] %} {% endif %} {% if not battery_missing and (states('sensor.drinks_freezer_environment_lcd_btt02_battery') | float(-1)) <= 20 %} {% set ns.items = ns.items + ['Low monitor battery'] %} {% endif %} {% if states('switch.drinks_fridge_power_x59pm') in ['unknown', 'unavailable'] or power_missing %} {% set ns.items = ns.items + ['Power monitoring unavailable'] %} {% elif not is_state('switch.drinks_fridge_power_x59pm', 'on') %} {% set ns.items = ns.items + ['Power switched off'] %} {% elif is_state('binary_sensor.drinks_fridge_power_draw_lost', 'on') %} {% set ns.items = ns.items + ['Power lost - no power draw for 4 hours'] %} {% endif %} {% if not temperature_missing and (states('sensor.drinks_freezer_environment_lcd_btt02_temperature') | float(0)) > threshold %} {% if (states('sensor.drinks_freezer_temperature_change_rate') | float(0)) >= 2 %} {% set ns.items = ns.items + ['Warming rapidly - possible open door'] %} {% else %} {% set ns.items = ns.items + ['Temperature too high - possible loss of cooling'] %} {% endif %} {% endif %} {{ ns.items }} temperature: "{{ states('sensor.drinks_freezer_environment_lcd_btt02_temperature') }}" average_7_days: "{{ states('sensor.drinks_freezer_temperature_average_7_days') }}" alert_threshold: >- {% set percentile = states('sensor.drinks_freezer_temperature_95th_percentile_7_days') | float(none) %} {{ [-5, percentile + 0.5] | min if percentile is not none else -5 }} change_rate: "{{ states('sensor.drinks_freezer_temperature_change_rate') }}" battery: "{{ states('sensor.drinks_freezer_environment_lcd_btt02_battery') }}" power: "{{ states('sensor.drinks_fridge_power_x59pm_power') }}" - name: "Vertical Freezer Issue" unique_id: vertical_freezer_issue device_class: problem delay_on: "00:01:00" state: >- {% set temperature_missing = not has_value('sensor.lcd_temp_humidity_btt03_temperature') %} {% set battery_missing = not has_value('sensor.lcd_temp_humidity_btt03_battery') %} {% set power_missing = not has_value('sensor.vertical_freezer_power_x60pm_power') %} {% set percentile = states('sensor.vertical_freezer_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [-10, percentile + 6] | min if percentile is not none else -10 %} {{ temperature_missing or battery_missing or (states('sensor.lcd_temp_humidity_btt03_battery') | float(-1)) <= 20 or states('switch.vertical_freezer_power_x60pm') != 'on' or power_missing or is_state('binary_sensor.vertical_freezer_power_draw_lost', 'on') or (not temperature_missing and (states('sensor.lcd_temp_humidity_btt03_temperature') | float(0)) > threshold) }} attributes: appliance: "Vertical Freezer" issues: >- {% set ns = namespace(items=[]) %} {% set temperature_missing = not has_value('sensor.lcd_temp_humidity_btt03_temperature') %} {% set battery_missing = not has_value('sensor.lcd_temp_humidity_btt03_battery') %} {% set power_missing = not has_value('sensor.vertical_freezer_power_x60pm_power') %} {% set percentile = states('sensor.vertical_freezer_temperature_95th_percentile_7_days') | float(none) %} {% set threshold = [-10, percentile + 6] | min if percentile is not none else -10 %} {% if temperature_missing or battery_missing %} {% set ns.items = ns.items + ['Monitor offline'] %} {% endif %} {% if not battery_missing and (states('sensor.lcd_temp_humidity_btt03_battery') | float(-1)) <= 20 %} {% set ns.items = ns.items + ['Low monitor battery'] %} {% endif %} {% if states('switch.vertical_freezer_power_x60pm') in ['unknown', 'unavailable'] or power_missing %} {% set ns.items = ns.items + ['Power monitoring unavailable'] %} {% elif not is_state('switch.vertical_freezer_power_x60pm', 'on') %} {% set ns.items = ns.items + ['Power switched off'] %} {% elif is_state('binary_sensor.vertical_freezer_power_draw_lost', 'on') %} {% set ns.items = ns.items + ['Power lost - no power draw for 4 hours'] %} {% endif %} {% if not temperature_missing and (states('sensor.lcd_temp_humidity_btt03_temperature') | float(0)) > threshold %} {% if (states('sensor.vertical_freezer_temperature_change_rate') | float(0)) >= 2 %} {% set ns.items = ns.items + ['Warming rapidly - possible open door'] %} {% else %} {% set ns.items = ns.items + ['Temperature too high - possible loss of cooling'] %} {% endif %} {% endif %} {{ ns.items }} temperature: "{{ states('sensor.lcd_temp_humidity_btt03_temperature') }}" average_7_days: "{{ states('sensor.vertical_freezer_temperature_average_7_days') }}" alert_threshold: >- {% set percentile = states('sensor.vertical_freezer_temperature_95th_percentile_7_days') | float(none) %} {{ [-10, percentile + 6] | min if percentile is not none else -10 }} change_rate: "{{ states('sensor.vertical_freezer_temperature_change_rate') }}" battery: "{{ states('sensor.lcd_temp_humidity_btt03_battery') }}" power: "{{ states('sensor.vertical_freezer_power_x60pm_power') }}" #:######################################################################################:# # SUMMARY STATUS #:######################################################################################:# - sensor: - name: "Fridge Freezer Monitor Status" unique_id: fridge_freezer_monitor_status icon: mdi:fridge-alert-outline state: >- {{ 'Issue' 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.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' ) | selectattr('state', 'eq', 'on') | list | count > 0 else 'OK' }} attributes: issue_count: >- {{ 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' ) | selectattr('state', 'eq', 'on') | list | count }} 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_freezer_door_open', 'on') ] | select | list | count }} issues: >- {% set ns = namespace(items=[]) %} {% set monitors = [ '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' ] %} {% for monitor in monitors if is_state(monitor, 'on') %} {% set appliance = state_attr(monitor, 'appliance') or state_attr(monitor, 'friendly_name') %} {% set reasons = state_attr(monitor, 'issues') or ['Issue detected'] %} {% for reason in reasons %} {% set ns.items = ns.items + [appliance ~ ': ' ~ reason] %} {% endfor %} {% endfor %} {{ ns.items }}