Handling stale/broken sensors that drive thermostats

Okay so this has been more complicated than I envisaged due to the fact the HA resets the last_changed value of sensors when restarting (when e.g. testing new config) so as per Real state last_changed and a reimplementation for SQLite, a bunch of new “proxy” sensors (rlc_) which store real time since last change as an hourly value, I have finished the script which will:

  1. Take either automation or climate as setting to check/switch off
  2. Work with either single or multiple values for automation/climate
  3. Have a quick yaml parse to check for errors

It is all in the notebook linked above, but for those that prefer reading in the forum here is the code that I have tested and working well:

# escape any ' with ''
settings = [
    {"id": "attic_office_stale", 
     "alias": "Attic Office", 
     "sensor": "attic_office_temperature",
     "rlc_sensor": "rlc_attic_office_temperature",
     "timeout": 4, 
     "automations": ["attic_heating_on","attic_heating_on_f"]},
    {"id": "pip_room_stale", 
     "alias": "Pip''s room", 
     "sensor": "pip_temperature", 
     "rlc_sensor": "rlc_pip_temperature",
     "timeout": 4, 
     "automations": ["turn_pip_s_heating_on"]},
    {"id": "kitchen_stale",
     "alias": "Kitchen", 
     "sensor": "kitchen_temperature", 
     "rlc_sensor": "rlc_kitchen_temperature",
     "timeout": 4, 
     "automations": ["kitchen_heating_on"]},
    {"id": "tackroom_stale", 
     "alias": "Tackroom", 
     "sensor": "tackroom_temperature",
     "rlc_sensor": "rlc_tackroom_temperature",
     "timeout": 4, 
     "climates": ["tack_room"]},
    {"id": "greenhouse_stale",
     "alias": "Greenhouse", 
     "sensor": "greenhouse_temperature",
     "rlc_sensor": "rlc_greenhouse_temperature",
     "timeout": 4, 
     "climates": ["greenhouse"]},
    {"id": "hot_water_stale", 
     "alias": "Hot Water", 
     "sensor": "hot_water",
     "rlc_sensor": "rlc_hot_water",
     "timeout": 4, 
     "automations": ["hot_water_bath","hot_water"]}
]

config=""
for item in settings:
    if "automations" in item:
        is_automation = True
        is_climate = False
    elif "climates" in item:
        is_automation = False
        is_climate = True        
    else:
        raise Exception(f"No automations or climates defined for {item['id']}")
        
    config += f"""
- id: '{item["id"]}'
  alias: 'Stale sensor: {item["alias"]}'
  description: ''
  trigger:
    - platform: time_pattern
      minutes: 7
  condition:
  - condition: and
    conditions:
    - condition: numeric_state
      entity_id: sensor.{item["rlc_sensor"]}
      above: '{item["timeout"]}'
"""

    if (is_automation and len(item["automations"]) > 1) or is_climate and len(item["climates"]) >1:
        config += f"""
    - condition: or
      conditions:
"""
        if is_automation:
            for automation in item["automations"]:
                config += f"""
      - condition: state
        entity_id: automation.{automation}
        state: 'on'
"""
        elif is_climate:
            for climate in item["climates"]:
                config+= f"""
      - condition: state
        entity_id: climate.{item["climate"]}
        state: heat
"""
    elif is_automation:
        config+=f"""
    - condition: state
      entity_id: automation.{item["automations"][0]}
      state: 'on'
"""
    elif is_climate:
        config+=f"""
    - condition: state
      entity_id: climate.{item["climates"][0]}
      state: heat        
"""

    config += f"""
  action:
"""
    if is_automation:
        for automation in item["automations"]:
            config += f"""
  - data: {{}}
    entity_id: automation.{automation}
    service: automation.turn_off
"""
    elif is_climate:
        for climate in item["climates"]:
            config += f"""
  - data: {{}}
    entity_id: climate.{automation}
    service: climate.turn_off
"""
            
    config += f"""
  - data:
      message: Disabling {item["alias"]}
      title: 'Stale sensor: {item["sensor"]}'
    service: notify.mobile_app_pixel_2
  mode: single
"""

try:
    yaml.safe_load(config)
    print(config)
except:
    print("YAML failed to parse")

I decided not to automatically re-enable automations/climates and skip the NA sensor view. The notifications to my mobile phone work well enough.