Skip to content

Food Temperature Monitoring Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Add a "🧊 Food Temperature" section to the Climate tab (3 color-coded tiles) plus two-tier, debounced food-safety alerts for the three garage fridge/freezer sensors.

Architecture: One consolidated multi-trigger automation per appliance (soft / hard / health / recover branches via trigger.id + choose). Native HA numeric_state above:+for: triggers provide the debounce (the for: timer resets the instant temp drops, so door/loading spikes never fire). Two input_booleans per appliance track alert state (gates the all-clear and matches recipients). Dashboard tiles reuse the existing custom:button-card pattern.

Tech Stack: Home Assistant YAML automations, input_boolean helpers, Lovelace custom:button-card JSON, pytest static/structural test suite, CI deploy pipeline.

Reference spec: docs/superpowers/specs/2026-06-08-food-temperature-monitoring-design.md


Entities (verified live 2026-06-08, all °F)

Appliance Temperature Battery Green ceiling Soft Hard
Refrigerator sensor.refrigerator_temperature_sensor_temperature sensor.refrigerator_temperature_sensor_battery 40 >42 / 45m >52 / 10m
Refrigerator Freezer sensor.refrigerator_freezer_temperature_sensor_temperature sensor.refrigerator_freezer_temperature_sensor_battery 10 >10 / 60m >25 / 15m
Deep Freezer sensor.freezer_temperature_sensor_temperature sensor.freezer_temperature_sensor_battery 5 >5 / 60m >20 / 20m

Helpers (2 input_boolean per appliance)

Appliance "any alert active" "critical active"
Refrigerator input_boolean.food_alert_refrigerator input_boolean.food_critical_refrigerator
Refrigerator Freezer input_boolean.food_alert_refrigerator_freezer input_boolean.food_critical_refrigerator_freezer
Deep Freezer input_boolean.food_alert_freezer input_boolean.food_critical_freezer

Recipient logic: soft + health → Louis only. Hard → both phones, critical (DND bypass). All-clear → Louis always; Lindsay too only if the critical flag is on (i.e. she was woken). Soft branch is suppressed while the critical flag is on (no "warm" after "FAILING"). This faithfully implements the spec's "all-clear to the same set that got the alert."

File Structure

  • configuration.yaml — add 6 input_boolean helpers to the existing input_boolean: block.
  • automations.yaml — add a new section (3 automations) before the trailing sections.
  • lovelace/home_command_dashboard.json — append a title card + 3-tile row to the Climate view (index 2).
  • tests/fixtures/entities.txt — surgically add the 6 sensor entities (they exist live).
  • tests/fixtures/entities_allow.txt — add the 6 input_boolean helpers (don't exist until after deploy/restart).
  • tests/test_food_temp.py — new guard test (ids present, hard tier critical, recover gated).
  • CLAUDE.md + projects/ — update automation count / section list and mark spec implemented.

Task 1: Test fixtures + guard test (red first)

Files: - Modify: tests/fixtures/entities.txt - Modify: tests/fixtures/entities_allow.txt - Create: tests/test_food_temp.py

  • [ ] Step 1: Add the 6 sensor entities to the snapshot (surgical, additions-only)

Run this from repo root (preserves existing ordering, inserts only the new lines):

python3 - <<'PYEOF'
lines=open('tests/fixtures/entities.txt').read().splitlines()
header=[l for l in lines if l.startswith('#')]
body=[l for l in lines if l and not l.startswith('#')]
existing=set(body)
new=[e for e in [
 'sensor.refrigerator_temperature_sensor_temperature',
 'sensor.refrigerator_temperature_sensor_battery',
 'sensor.refrigerator_freezer_temperature_sensor_temperature',
 'sensor.refrigerator_freezer_temperature_sensor_battery',
 'sensor.freezer_temperature_sensor_temperature',
 'sensor.freezer_temperature_sensor_battery',
] if e not in existing]
for e in new:
    pos=len(body)
    for i,l in enumerate(body):
        if l>e: pos=i; break
    body.insert(pos,e)
open('tests/fixtures/entities.txt','w').write('\n'.join(header+body)+'\n')
print('added',len(new),'entities')
PYEOF

Expected: added 6 entities

  • [ ] Step 2: Add the 6 helpers to the allowlist

Append to tests/fixtures/entities_allow.txt:

# Food Temperature monitoring helpers (configuration.yaml) — created on next HA
# restart after deploy; allowlisted until they exist in the live registry.
input_boolean.food_alert_refrigerator
input_boolean.food_critical_refrigerator
input_boolean.food_alert_refrigerator_freezer
input_boolean.food_critical_refrigerator_freezer
input_boolean.food_alert_freezer
input_boolean.food_critical_freezer
  • [ ] Step 3: Write the guard test

Create tests/test_food_temp.py:

"""Guard tests for the Food Temperature monitoring feature."""
import json

from conftest import load_yaml, read_text

FOOD_IDS = {
    "food_temp_refrigerator",
    "food_temp_refrigerator_freezer",
    "food_temp_freezer",
}


def _food_automations():
    autos = load_yaml("automations.yaml") or []
    return [a for a in autos if isinstance(a, dict) and a.get("id") in FOOD_IDS]


def test_food_temp_automations_present():
    ids = {a.get("id") for a in _food_automations()}
    assert FOOD_IDS <= ids, f"missing food-temp automations: {sorted(FOOD_IDS - ids)}"


def test_food_temp_hard_tier_is_critical():
    # Each appliance's hard tier must request a critical (DND-bypass) push.
    for a in _food_automations():
        blob = json.dumps(a)
        assert "critical" in blob, f"{a['id']} hard tier missing critical push payload"


def test_food_temp_recover_gated_by_boolean():
    # Recover branch must reference the per-appliance alert boolean so all-clear
    # only fires after a real alert.
    for a in _food_automations():
        blob = json.dumps(a)
        assert "input_boolean.food_alert_" in blob, f"{a['id']} recover not gated by boolean"
  • [ ] Step 4: Run the guard test — expect FAIL (automations not yet added)

Run:

make pytest 2>&1 | tail -20
Expected: test_food_temp_automations_present FAILS (missing all three ids); entity-reference test PASSES (snapshot+allowlist now cover the new refs, though nothing references them yet).

  • [ ] Step 5: Commit
git add tests/fixtures/entities.txt tests/fixtures/entities_allow.txt tests/test_food_temp.py
git commit -m "auto: tests — food-temp fixtures + guard test (snapshot sensors, allowlist helpers)"

Task 2: input_boolean helpers

Files: - Modify: configuration.yaml (existing input_boolean: block, currently lines ~29-35)

  • [ ] Step 1: Add the 6 helpers

In configuration.yaml, inside the existing input_boolean: block, after internet_speed_degraded: (keep the existing two), add:

  # Food Temperature monitoring — per-appliance alert-state flags.
  # food_alert_* = any alert active (gates the all-clear); food_critical_* = the
  # hard/critical tier fired (decides whether Lindsay gets the all-clear too).
  food_alert_refrigerator:
    name: "Food Alert  Refrigerator"
    icon: mdi:fridge-alert-outline
  food_critical_refrigerator:
    name: "Food Critical  Refrigerator"
    icon: mdi:fridge-alert
  food_alert_refrigerator_freezer:
    name: "Food Alert  Refrigerator Freezer"
    icon: mdi:fridge-alert-outline
  food_critical_refrigerator_freezer:
    name: "Food Critical  Refrigerator Freezer"
    icon: mdi:fridge-alert
  food_alert_freezer:
    name: "Food Alert  Deep Freezer"
    icon: mdi:snowflake-alert
  food_critical_freezer:
    name: "Food Critical  Deep Freezer"
    icon: mdi:snowflake-alert
  • [ ] Step 2: Run the suite — expect PASS

Run:

make lint && make pytest 2>&1 | tail -10
Expected: lint PASS (valid YAML); test_configuration_defines_referenced_helpers PASS; guard test still red on test_food_temp_automations_present (automations still pending).

  • [ ] Step 3: Commit
git add configuration.yaml
git commit -m "auto: configuration.yaml — add 6 input_boolean food-temp alert-state helpers"

Task 3: The three automations

Files: - Modify: automations.yaml (append a new section before the trailing # 10. String Lights … section, i.e. after the last TOU/garage section content; placement is cosmetic — appending at end of file is also fine)

  • [ ] Step 1: Add the section header + Refrigerator automation

Append to automations.yaml:

##############################################################################
# 13. FOOD TEMPERATURE — Garage fridge/freezer monitoring
#     Two-tier debounced alerts. `for:` durations absorb door/loading spikes.
#     soft+health -> Louis;  hard -> both phones (critical/DND-bypass);
#     all-clear -> Louis, plus Lindsay only if the critical tier fired.
##############################################################################

# 24. Food Temp — Refrigerator
- alias: "Food Temp  Refrigerator"
  id: food_temp_refrigerator
  description: >
    Food-safety monitoring for the garage refrigerator (safe ≤ 40°F). Soft warn
    > 42°F for 45 min; critical > 52°F for 10 min; sensor offline 30 min or
    battery < 15%; all-clear back below 40°F for 15 min.
  triggers:
    - trigger: numeric_state
      entity_id: sensor.refrigerator_temperature_sensor_temperature
      above: 42
      for: "00:45:00"
      id: soft
    - trigger: numeric_state
      entity_id: sensor.refrigerator_temperature_sensor_temperature
      above: 52
      for: "00:10:00"
      id: hard
    - trigger: state
      entity_id: sensor.refrigerator_temperature_sensor_temperature
      to: "unavailable"
      for: "00:30:00"
      id: health_offline
    - trigger: numeric_state
      entity_id: sensor.refrigerator_temperature_sensor_battery
      below: 15
      id: health_battery
    - trigger: numeric_state
      entity_id: sensor.refrigerator_temperature_sensor_temperature
      below: 40
      for: "00:15:00"
      id: recover
  conditions: []
  actions:
    - choose:
        - conditions:
            - condition: trigger
              id: hard
          sequence:
            - action: input_boolean.turn_on
              target:
                entity_id:
                  - input_boolean.food_alert_refrigerator
                  - input_boolean.food_critical_refrigerator
            - action: notify.mobile_app_louis_iphone
              data:
                title: "⚠️ Refrigerator FAILING"
                message: >-
                  Refrigerator is {{ states('sensor.refrigerator_temperature_sensor_temperature') | round(1) }}°F — food at risk. Check it now.
                data:
                  tag: "food_temp_refrigerator"
                  push:
                    interruption-level: critical
                    sound:
                      name: "default"
                      critical: 1
                      volume: 1.0
            - action: notify.mobile_app_lindsays_iphone
              data:
                title: "⚠️ Refrigerator FAILING"
                message: >-
                  Refrigerator is {{ states('sensor.refrigerator_temperature_sensor_temperature') | round(1) }}°F — food at risk. Check it now.
                data:
                  tag: "food_temp_refrigerator"
                  push:
                    interruption-level: critical
                    sound:
                      name: "default"
                      critical: 1
                      volume: 1.0
        - conditions:
            - condition: trigger
              id: soft
            - condition: state
              entity_id: input_boolean.food_critical_refrigerator
              state: "off"
          sequence:
            - action: input_boolean.turn_on
              target:
                entity_id: input_boolean.food_alert_refrigerator
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🌡️ Refrigerator warm"
                message: >-
                  Refrigerator has been {{ states('sensor.refrigerator_temperature_sensor_temperature') | round(1) }}°F for 45 min. Check the door.
                data:
                  tag: "food_temp_refrigerator"
        - conditions:
            - condition: trigger
              id: health_offline
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🔋 Refrigerator sensor offline"
                message: "The refrigerator temperature sensor has been unavailable for 30 min  the fridge isn't being monitored."
                data:
                  tag: "food_temp_refrigerator_health"
        - conditions:
            - condition: trigger
              id: health_battery
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🔋 Refrigerator sensor battery low"
                message: >-
                  The refrigerator temperature sensor battery is {{ states('sensor.refrigerator_temperature_sensor_battery') | int }}%. Replace it soon.
                data:
                  tag: "food_temp_refrigerator_health"
        - conditions:
            - condition: trigger
              id: recover
            - condition: state
              entity_id: input_boolean.food_alert_refrigerator
              state: "on"
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "✅ Refrigerator back to normal"
                message: >-
                  Refrigerator has returned to {{ states('sensor.refrigerator_temperature_sensor_temperature') | round(1) }}°F.
                data:
                  tag: "food_temp_refrigerator"
            - if:
                - condition: state
                  entity_id: input_boolean.food_critical_refrigerator
                  state: "on"
              then:
                - action: notify.mobile_app_lindsays_iphone
                  data:
                    title: "✅ Refrigerator back to normal"
                    message: >-
                      Refrigerator has returned to {{ states('sensor.refrigerator_temperature_sensor_temperature') | round(1) }}°F.
                    data:
                      tag: "food_temp_refrigerator"
            - action: input_boolean.turn_off
              target:
                entity_id:
                  - input_boolean.food_alert_refrigerator
                  - input_boolean.food_critical_refrigerator
  mode: single
  • [ ] Step 2: Add the Refrigerator Freezer automation

Append to automations.yaml:

# 25. Food Temp — Refrigerator Freezer
- alias: "Food Temp  Refrigerator Freezer"
  id: food_temp_refrigerator_freezer
  description: >
    Food-safety monitoring for the refrigerator's freezer (safe ≤ 10°F). Soft
    warn > 10°F for 60 min; critical > 25°F for 15 min; sensor offline 30 min or
    battery < 15%; all-clear back below 10°F for 15 min.
  triggers:
    - trigger: numeric_state
      entity_id: sensor.refrigerator_freezer_temperature_sensor_temperature
      above: 10
      for: "01:00:00"
      id: soft
    - trigger: numeric_state
      entity_id: sensor.refrigerator_freezer_temperature_sensor_temperature
      above: 25
      for: "00:15:00"
      id: hard
    - trigger: state
      entity_id: sensor.refrigerator_freezer_temperature_sensor_temperature
      to: "unavailable"
      for: "00:30:00"
      id: health_offline
    - trigger: numeric_state
      entity_id: sensor.refrigerator_freezer_temperature_sensor_battery
      below: 15
      id: health_battery
    - trigger: numeric_state
      entity_id: sensor.refrigerator_freezer_temperature_sensor_temperature
      below: 10
      for: "00:15:00"
      id: recover
  conditions: []
  actions:
    - choose:
        - conditions:
            - condition: trigger
              id: hard
          sequence:
            - action: input_boolean.turn_on
              target:
                entity_id:
                  - input_boolean.food_alert_refrigerator_freezer
                  - input_boolean.food_critical_refrigerator_freezer
            - action: notify.mobile_app_louis_iphone
              data:
                title: "⚠️ Fridge Freezer FAILING"
                message: >-
                  Refrigerator freezer is {{ states('sensor.refrigerator_freezer_temperature_sensor_temperature') | round(1) }}°F — food thawing. Check it now.
                data:
                  tag: "food_temp_refrigerator_freezer"
                  push:
                    interruption-level: critical
                    sound:
                      name: "default"
                      critical: 1
                      volume: 1.0
            - action: notify.mobile_app_lindsays_iphone
              data:
                title: "⚠️ Fridge Freezer FAILING"
                message: >-
                  Refrigerator freezer is {{ states('sensor.refrigerator_freezer_temperature_sensor_temperature') | round(1) }}°F — food thawing. Check it now.
                data:
                  tag: "food_temp_refrigerator_freezer"
                  push:
                    interruption-level: critical
                    sound:
                      name: "default"
                      critical: 1
                      volume: 1.0
        - conditions:
            - condition: trigger
              id: soft
            - condition: state
              entity_id: input_boolean.food_critical_refrigerator_freezer
              state: "off"
          sequence:
            - action: input_boolean.turn_on
              target:
                entity_id: input_boolean.food_alert_refrigerator_freezer
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🌡️ Fridge Freezer warm"
                message: >-
                  Refrigerator freezer has been {{ states('sensor.refrigerator_freezer_temperature_sensor_temperature') | round(1) }}°F for an hour. Check the door.
                data:
                  tag: "food_temp_refrigerator_freezer"
        - conditions:
            - condition: trigger
              id: health_offline
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🔋 Fridge Freezer sensor offline"
                message: "The refrigerator freezer temperature sensor has been unavailable for 30 min  it isn't being monitored."
                data:
                  tag: "food_temp_refrigerator_freezer_health"
        - conditions:
            - condition: trigger
              id: health_battery
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🔋 Fridge Freezer sensor battery low"
                message: >-
                  The refrigerator freezer temperature sensor battery is {{ states('sensor.refrigerator_freezer_temperature_sensor_battery') | int }}%. Replace it soon.
                data:
                  tag: "food_temp_refrigerator_freezer_health"
        - conditions:
            - condition: trigger
              id: recover
            - condition: state
              entity_id: input_boolean.food_alert_refrigerator_freezer
              state: "on"
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "✅ Fridge Freezer back to normal"
                message: >-
                  Refrigerator freezer has returned to {{ states('sensor.refrigerator_freezer_temperature_sensor_temperature') | round(1) }}°F.
                data:
                  tag: "food_temp_refrigerator_freezer"
            - if:
                - condition: state
                  entity_id: input_boolean.food_critical_refrigerator_freezer
                  state: "on"
              then:
                - action: notify.mobile_app_lindsays_iphone
                  data:
                    title: "✅ Fridge Freezer back to normal"
                    message: >-
                      Refrigerator freezer has returned to {{ states('sensor.refrigerator_freezer_temperature_sensor_temperature') | round(1) }}°F.
                    data:
                      tag: "food_temp_refrigerator_freezer"
            - action: input_boolean.turn_off
              target:
                entity_id:
                  - input_boolean.food_alert_refrigerator_freezer
                  - input_boolean.food_critical_refrigerator_freezer
  mode: single
  • [ ] Step 3: Add the Deep Freezer automation

Append to automations.yaml:

# 26. Food Temp — Deep Freezer
- alias: "Food Temp  Deep Freezer"
  id: food_temp_freezer
  description: >
    Food-safety monitoring for the garage deep freezer (safe ≤ 5°F). Soft warn
    > 5°F for 60 min; critical > 20°F for 20 min; sensor offline 30 min or
    battery < 15%; all-clear back below 5°F for 15 min.
  triggers:
    - trigger: numeric_state
      entity_id: sensor.freezer_temperature_sensor_temperature
      above: 5
      for: "01:00:00"
      id: soft
    - trigger: numeric_state
      entity_id: sensor.freezer_temperature_sensor_temperature
      above: 20
      for: "00:20:00"
      id: hard
    - trigger: state
      entity_id: sensor.freezer_temperature_sensor_temperature
      to: "unavailable"
      for: "00:30:00"
      id: health_offline
    - trigger: numeric_state
      entity_id: sensor.freezer_temperature_sensor_battery
      below: 15
      id: health_battery
    - trigger: numeric_state
      entity_id: sensor.freezer_temperature_sensor_temperature
      below: 5
      for: "00:15:00"
      id: recover
  conditions: []
  actions:
    - choose:
        - conditions:
            - condition: trigger
              id: hard
          sequence:
            - action: input_boolean.turn_on
              target:
                entity_id:
                  - input_boolean.food_alert_freezer
                  - input_boolean.food_critical_freezer
            - action: notify.mobile_app_louis_iphone
              data:
                title: "⚠️ Deep Freezer FAILING"
                message: >-
                  Deep freezer is {{ states('sensor.freezer_temperature_sensor_temperature') | round(1) }}°F — food thawing. Check it now.
                data:
                  tag: "food_temp_freezer"
                  push:
                    interruption-level: critical
                    sound:
                      name: "default"
                      critical: 1
                      volume: 1.0
            - action: notify.mobile_app_lindsays_iphone
              data:
                title: "⚠️ Deep Freezer FAILING"
                message: >-
                  Deep freezer is {{ states('sensor.freezer_temperature_sensor_temperature') | round(1) }}°F — food thawing. Check it now.
                data:
                  tag: "food_temp_freezer"
                  push:
                    interruption-level: critical
                    sound:
                      name: "default"
                      critical: 1
                      volume: 1.0
        - conditions:
            - condition: trigger
              id: soft
            - condition: state
              entity_id: input_boolean.food_critical_freezer
              state: "off"
          sequence:
            - action: input_boolean.turn_on
              target:
                entity_id: input_boolean.food_alert_freezer
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🌡️ Deep Freezer warm"
                message: >-
                  Deep freezer has been {{ states('sensor.freezer_temperature_sensor_temperature') | round(1) }}°F for an hour. Check the lid.
                data:
                  tag: "food_temp_freezer"
        - conditions:
            - condition: trigger
              id: health_offline
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🔋 Deep Freezer sensor offline"
                message: "The deep freezer temperature sensor has been unavailable for 30 min  it isn't being monitored."
                data:
                  tag: "food_temp_freezer_health"
        - conditions:
            - condition: trigger
              id: health_battery
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "🔋 Deep Freezer sensor battery low"
                message: >-
                  The deep freezer temperature sensor battery is {{ states('sensor.freezer_temperature_sensor_battery') | int }}%. Replace it soon.
                data:
                  tag: "food_temp_freezer_health"
        - conditions:
            - condition: trigger
              id: recover
            - condition: state
              entity_id: input_boolean.food_alert_freezer
              state: "on"
          sequence:
            - action: notify.mobile_app_louis_iphone
              data:
                title: "✅ Deep Freezer back to normal"
                message: >-
                  Deep freezer has returned to {{ states('sensor.freezer_temperature_sensor_temperature') | round(1) }}°F.
                data:
                  tag: "food_temp_freezer"
            - if:
                - condition: state
                  entity_id: input_boolean.food_critical_freezer
                  state: "on"
              then:
                - action: notify.mobile_app_lindsays_iphone
                  data:
                    title: "✅ Deep Freezer back to normal"
                    message: >-
                      Deep freezer has returned to {{ states('sensor.freezer_temperature_sensor_temperature') | round(1) }}°F.
                    data:
                      tag: "food_temp_freezer"
            - action: input_boolean.turn_off
              target:
                entity_id:
                  - input_boolean.food_alert_freezer
                  - input_boolean.food_critical_freezer
  mode: single
  • [ ] Step 4: Run the full suite — expect PASS (guard test now green)

Run:

make lint && make pytest 2>&1 | tail -15
Expected: lint PASS; test_food_temp_* all PASS; test_automation_ids_unique / test_automation_aliases_unique PASS (new ids/aliases unique); entity-reference PASS.

  • [ ] Step 5: Commit
git add automations.yaml
git commit -m "auto: automations.yaml — add Food Temperature section (3 two-tier debounced alerts)"

Task 4: Dashboard — Climate-tab "Food Temperature" section

Files: - Modify: lovelace/home_command_dashboard.json (Climate view, index 2)

  • [ ] Step 1: Pull the live dashboard fresh (user also edits it in the UI)

Run:

ssh -o ConnectTimeout=10 root@192.168.4.93 "cat /config/.storage/lovelace.home_command_dashboard" > /tmp/live_dash.json
python3 -c "import json,sys; a=json.dumps(json.load(open('lovelace/home_command_dashboard.json')),sort_keys=True); b=json.dumps(json.load(open('/tmp/live_dash.json')),sort_keys=True); print('IN SYNC' if a==b else 'DRIFT')"
Expected: IN SYNC. If DRIFT, copy /tmp/live_dash.json over the repo file first (cp /tmp/live_dash.json lovelace/home_command_dashboard.json) so you don't clobber UI edits, then continue.

  • [ ] Step 2: Append the section via Python script

Run:

python3 - <<'PYEOF'
import json
path='lovelace/home_command_dashboard.json'
d=json.load(open(path))
views=d['data']['config']['views']
clim=[v for v in views if v.get('path')=='climate'][0]

NAME=[{"font-size":"11px"},{"color":"rgba(255,255,255,0.8)"},{"font-weight":"500"},{"letter-spacing":"0.5px"}]
STATE=[{"font-size":"22px"},{"font-weight":"700"},{"color":"#fff"},{"margin-top":"4px"}]
LABEL=[{"font-size":"11px"},{"color":"rgba(255,255,255,0.7)"}]
ICON=[{"width":"22px"},{"color":"rgba(255,255,255,0.6)"}]

def tile(entity,name,icon,green,red):
    bg=("[[[var v=parseFloat(entity.state);"
        "if(isNaN(v))return'linear-gradient(135deg,#37474f,#546e7a)';"
        f"if(v>{red})return'linear-gradient(135deg,#b71c1c,#c62828)';"
        f"if(v>{green})return'linear-gradient(135deg,#e65100,#f57c00)';"
        "return'linear-gradient(135deg,#1b5e20,#2e7d32)';]]]")
    label=("[[[var v=parseFloat(entity.state);"
        "if(isNaN(v))return'No reading';"
        f"if(v>{red})return'⚠️ FAILING';"
        f"if(v>{green})return'Warm';"
        "return'Good';]]]")
    sd="[[[var v=parseFloat(entity.state);if(isNaN(v))return'—';return Math.round(v)+'°F';]]]"
    return {"type":"custom:button-card","entity":entity,"name":name,"icon":icon,
            "show_state":True,"show_label":True,"state_display":sd,"label":label,
            "styles":{"name":NAME,"state":STATE,"label":LABEL,"icon":ICON,
                      "card":[{"background":bg},{"border-radius":"12px"},{"padding":"14px 8px"}]}}

title={"type":"custom:mushroom-title-card","title":"\U0001f9ca Food Temperature"}
row={"type":"horizontal-stack","cards":[
    tile("sensor.refrigerator_temperature_sensor_temperature","Refrigerator","mdi:fridge-outline",40,52),
    tile("sensor.refrigerator_freezer_temperature_sensor_temperature","Fridge Freezer","mdi:fridge",10,25),
    tile("sensor.freezer_temperature_sensor_temperature","Deep Freezer","mdi:snowflake",5,20),
]}
# Idempotency guard: don't double-append if re-run
titles=[c.get('title') for c in clim['cards'] if isinstance(c,dict)]
assert "\U0001f9ca Food Temperature" not in titles, "section already present"
clim['cards'].extend([title,row])
json.dump(d,open(path,'w'),ensure_ascii=False,separators=(', ',': '))
print("appended; climate cards now", len(clim['cards']))
PYEOF
Expected: appended; climate cards now 18

  • [ ] Step 3: Validate JSON + entity refs

Run:

python3 -c "import json; json.load(open('lovelace/home_command_dashboard.json')); print('valid JSON')"
make pytest 2>&1 | tail -8
Expected: valid JSON; all tests PASS (the 3 temperature sensors are in the snapshot from Task 1; test_lovelace_has_views PASS).

  • [ ] Step 4: Commit
git add lovelace/home_command_dashboard.json
git commit -m "auto: dashboard — add Climate-tab Food Temperature section (3 color-coded tiles)"

Task 5: Docs + deploy + verify

Files: - Modify: docs/superpowers/specs/2026-06-08-food-temperature-monitoring-design.md (status) - Modify: CLAUDE.md (automation count / section list — optional, keep current)

  • [ ] Step 1: Mark the spec implemented

In the spec file, change the status line and the "Out of Scope"/header date note to reflect implementation. Edit the top *Status:* line to:

*Status: Implemented 2026-06-08 — see docs/superpowers/plans/2026-06-08-food-temperature-monitoring.md*

  • [ ] Step 2: Update CLAUDE.md automation inventory

In CLAUDE.md, under "### Automations (automations.yaml)", update the count and add the section. Find the line beginning 26 automations in 9 sections (or current count) and the numbered list; add:

13. Food Temperature — Garage fridge/freezer two-tier debounced food-safety alerts
Adjust the headline count to match the file's actual section/automation totals.

  • [ ] Step 3: Run the full suite one last time

Run:

make test 2>&1 | tail -20
Expected: Tiers 0–3 PASS. (Tier 1 needs Docker; if unavailable locally, CI runs it — note that and proceed.)

  • [ ] Step 4: Commit docs
git add docs/superpowers/specs/2026-06-08-food-temperature-monitoring-design.md CLAUDE.md
git commit -m "auto: docs — mark food-temp spec implemented; CLAUDE.md automation inventory"
  • [ ] Step 5: Push and verify CI

git push
gh run list --workflow=ci.yml --limit 1
Then watch to completion:
RID=$(gh run list --workflow=ci.yml --limit 1 --json databaseId -q '.[0].databaseId')
gh run watch "$RID" --exit-status --interval 15 2>&1 | tail -25
Expected: test job PASS → deploy-ha (full restart, because configuration.yaml changed) PASS → deploy-lovelace (stop→swap→start) PASS. deploy-grafana skipped.

  • [ ] Step 6: Post-deploy verification

After HA restarts, confirm the helpers were created and the tiles render:

ssh root@192.168.4.93 "ha core logs --lines 20"
Optionally fold the now-live helpers into the snapshot and drop them from the allowlist:
# (only after a green deploy; verifies they exist live)
HA_URL=$HA_URL HA_TOKEN=$HA_TOKEN scripts/refresh-entity-snapshot.sh  # review the diff carefully — see Task 1 note on snapshot churn
NOTE: make snapshot produces states-only churn (see the FLF history). Prefer leaving the 6 helpers in the allowlist unless you deliberately want to re-baseline the snapshot. If you keep them allowlisted, no further action — the feature is fully deployed.


Self-Review

Spec coverage: - Dashboard section + 3 color-coded tiles → Task 4 ✓ (bands match spec table) - Two-tier debounced alerts (soft/hard) → Task 3 ✓ (thresholds + for: per spec table) - Sensor-health tier (offline + low battery) → Task 3 ✓ (health_offline, health_battery) - All-clear gated + recipient-matched → Task 3 ✓ (2 booleans; Lindsay only if critical) - Critical/DND bypass on hard tier → Task 3 ✓ (push.interruption-level: critical) - Entity snapshot refresh → Task 1 ✓ (surgical add of 6 sensors) - Helpers in configuration.yaml → Task 2 ✓ - Tests grow with feature → Task 1 ✓ (guard test) - Deploy paths (config restart / lovelace swap / automations) → Task 5 ✓

Placeholder scan: No TBD/TODO; all code blocks are complete and literal.

Type/name consistency: Automation ids (food_temp_refrigerator, food_temp_refrigerator_freezer, food_temp_freezer), helper names (food_alert_* / food_critical_*), sensor entity_ids, and tile bands are identical across the guard test, helpers, automations, and dashboard. Tile green/red bands (40/52, 10/25, 5/20) match the automation soft-ceiling/hard thresholds.

Note on health_battery re-firing: numeric_state below: 15 with no for: fires once on crossing; battery is slow-moving so no spam. Acceptable; no gating boolean (separate *_health tag).