Hobby projects → The house cools its own drives
Guide · Home Assistant · running since 2026 in a Bangkok condo

Teach the house to cool its own hard drives

A NAS full of spinning disks and a tropical climate are a bad combination in the hot season. Our server closet has no dedicated cooling — but the room it sits off has an air conditioner the house can already control. This guide wires the two together in Home Assistant: when the hottest drive crosses a line, the aircon comes on and cools it back down, then everything returns to normal. No new hardware, one sensor and two automations.

The pattern, before the code

This is a thermostat you build yourself, and every piece generalises to other problems (a greenhouse fan, a router in a hot cabinet). Four ideas do all the work:

Watch the worst case, not the average. Drives fail individually, so the signal is the hottest drive, not the mean. One template sensor reduces however many disks you have to a single number.

Turn on above one line, off below a lower one. If the same threshold both starts and stops the cooling, the system flaps on and off around it. A two degree gap — on at 55°C, release at 53°C — keeps it decisive.

Remember why the aircon is on. A helper toggle records “cooling is active because of the drives”, so the release automation only touches the aircon when it was the one that started it — it will never fight you for the remote on an evening you happened to want the room cold anyway.

Escalate when the plan is not working. If temperatures keep climbing anyway, or the cooling has been running for half an hour without winning, tell a human instead of quietly trying forever.

What you need

Step 1 — one number for “how hot are the drives”

A template sensor that takes the maximum across your disk temperature sensors. List your own entities; drives that are spun down or unavailable are simply ignored.
configuration.yaml (or a package file)
template:
  - sensor:
      - name: "NAS max disk temp"
        unique_id: nas_max_disk_temp
        unit_of_measurement: "°C"
        state: >
          {{ [ states('sensor.disk1_temp'),
               states('sensor.disk2_temp'),
               states('sensor.disk3_temp') ]
             | map('float', default=0) | max | round(0) }}
Also create a helper toggle: Settings → Devices & Services → Helpers → Toggle, named input_boolean.drive_cooling_active. It is the system’s memory of whether it turned the aircon on.

Step 2 — the automation that cools

Fires when the hottest drive crosses 55°C: sets the aircon to a firm cool, raises the flag, sends a heads-up. A second trigger at 60°C is the “this is not working” alarm.
Automation (YAML view)
alias: Drives hot - cool the room
triggers:
  - trigger: numeric_state
    entity_id: sensor.nas_max_disk_temp
    above: 55
    id: hot
  - trigger: numeric_state
    entity_id: sensor.nas_max_disk_temp
    above: 60
    id: very_hot
actions:
  - choose:
      - conditions: [{ condition: trigger, id: hot }]
        sequence:
          - action: climate.set_temperature
            target: { entity_id: climate.living_room_ac }
            data: { temperature: 23, hvac_mode: cool }
          - action: climate.set_fan_mode
            target: { entity_id: climate.living_room_ac }
            data: { fan_mode: high }
          - action: input_boolean.turn_on
            target: { entity_id: input_boolean.drive_cooling_active }
          - action: notify.mobile_app_phone
            data: { message: "Drives at {{ states('sensor.nas_max_disk_temp') }} C - aircon on." }
      - conditions: [{ condition: trigger, id: very_hot }]
        sequence:
          - action: notify.mobile_app_phone
            data: { message: "Drives still climbing: {{ states('sensor.nas_max_disk_temp') }} C. Check the NAS." }
mode: single

Step 3 — the automation that lets go

Below 53°C — note the deliberate gap — the aircon is released, but only if the flag says this system switched it on. A second trigger catches the stuck case: cooling active for 30 minutes means something needs a human.
Automation (YAML view)
alias: Drives cool - release the aircon
triggers:
  - trigger: numeric_state
    entity_id: sensor.nas_max_disk_temp
    below: 53
    id: cooled
  - trigger: state
    entity_id: input_boolean.drive_cooling_active
    to: "on"
    for: "00:30:00"
    id: stuck
conditions:
  - condition: state
    entity_id: input_boolean.drive_cooling_active
    state: "on"
actions:
  - choose:
      - conditions: [{ condition: trigger, id: cooled }]
        sequence:
          - action: climate.turn_off
            target: { entity_id: climate.living_room_ac }
          - action: input_boolean.turn_off
            target: { entity_id: input_boolean.drive_cooling_active }
          - action: notify.mobile_app_phone
            data: { message: "Drives back to {{ states('sensor.nas_max_disk_temp') }} C - aircon released." }
      - conditions: [{ condition: trigger, id: stuck }]
        sequence:
          - action: notify.mobile_app_phone
            data: { message: "Drive cooling has run 30 minutes without winning - worth a look." }
mode: single

Tuning notes from a year of hot seasons

Pick thresholds from your drives, not ours. Most mechanical drives are rated to 60°C or 65°C; we chose 55 as the action line so the response starts well before the rating, and the gap down to 53 stops the on-off flapping.

Count only the drives that matter. SSDs report alarming-looking numbers quite happily; ours are excluded from the max sensor, which watches the spinning drives only.

The stuck-alarm earns its keep. A closed door, a heavy copy job, a failing fan — the cases where cooling cannot win are exactly the ones you want to hear about within the half hour, not at bedtime.

The same skeleton — worst-case sensor, two lines with a gap, a memory flag, an escalation — is reusable for almost anything you can measure and one thing you can switch.