Smart Home Fix

Detect a server cupboard cooling fault early

A cupboard can heat slowly for hours after a fan stops, or spike after someone closes a door that is normally left ajar. You can catch both patterns earlier by comparing cupboard temperature with the room, the rate of rise, equipment load and whether ventilation should be running.

The fault this project detects

This project detects inadequate heat removal around an NBN modem, router, switch, NAS or Home Assistant host. It looks for a rising cupboard-to-room temperature difference while the equipment is producing heat, then reserves Critical for a site-specific last-line temperature or corroborated cooling failure.

The worked structure assumes a ventilated indoor communications cupboard with one cupboard probe, one nearby room probe, a monitored equipment power feed and a fan status entity. Replace every example entity with the names used by your installation.

Server cupboard fault evidence flow Multiple measurements are combined into advisory, warning and critical fault states. Cupboard temperatureRoom temperatureEquipment powerVentilation state Evidence model direction + context agreement + duration Advisory dashboard only Warning one push Critical repeat + audible
Room comparison separates a hot day from trapped heat; rate, load and fan context show whether the cupboard is failing to shed it.

The evidence model

A useful alarm does not promote one noisy reading straight to an emergency. It combines measurements that fail in different ways:

  1. Value: cupboard temperature provides the final safety limit.
  2. Direction: a positive derivative shows heat accumulating rather than merely being warm.
  3. Agreement: the cupboard-to-room delta confirms local heat build-up.
  4. Context and time: load and fan state explain whether heat and ventilation should be present, while persistence rejects brief CPU bursts.

A single probe cannot tell the difference between a hot house and failed cupboard airflow. The second temperature probe and operating-state signals make that distinction visible.

Freshness is separate evidence

An entity can keep a plausible old value without becoming unavailable. Inspect last_changed on a source that should genuinely change, and work out the longest gap you'd expect between meaningful state changes on a normal day by reading that entity's own History graph over a few representative days. If the measured process can legitimately stay flat, add a changing source heartbeat or timestamp; do not treat an unchanged temperature, level or power value as proof that the transport is alive.

Rate maths: never copy a gradient

The Trend integration expresses min_gradient in units per second. The arithmetic example from the project brief is 4 °C/hour ÷ 3600 = 0.00111 °C/s. That is an example conversion, not a threshold for this equipment. Calculate the rate from your own timestamped readings: pull two points from the cupboard probe's History graph, take the temperature change between them in °C, divide by the number of seconds between those two timestamps, and that gives you the site-specific gradient for your equipment. A Derivative helper can instead display a friendlier per-hour unit when configured that way.

Collect this data before choosing a threshold

Record several normal daily load cycles, including a hot afternoon and any backup, scan or media workload that raises NAS or server power.

MeasurementWhy it mattersRequired value
sensor.server_cupboard_temperatureActual cupboard curveRead the min, max and average straight off this sensor's 24-hour History graph
sensor.study_temperatureAmbient comparisonPull matching timestamped readings from the room sensor's History graph for the same 24 hours
sensor.server_cupboard_temperature_rateSpeed of heat accumulationNote the normal °C/h range on a healthy day, then compare it with a day when airflow was known to be reduced or blocked
sensor.server_rack_powerHeat-load proxyRecord the idle and busy wattage range from this sensor's History across a normal day and a heavy workload
binary_sensor.server_cupboard_fan_runningCooling contextCapture the on/off trace from History, and confirm with power, tachometer or airflow feedback that "on" really means air is moving

The experiment that makes this article defensible

  1. Record a normal low-load cycle and a normal high-load cycle with the cupboard in its usual state.
  2. Compare cupboard temperature, room temperature, rate, fan state and power on one History timeline.
  3. If safe for the equipment, test the ordinary door positions users actually leave it in; do not block vents or stop cooling to manufacture a fault.
  4. After any genuine fan or airflow fault, preserve the trace and recovery time before changing thresholds.

Keep your own record as you go: save the timestamped readings from your test cycles, note the Home Assistant version you're running, note the exact entity IDs you used, and take one or two History or dashboard screenshots. Do not stage an unsafe electrical, refrigeration, pressure, battery or water fault merely to make a graph.

Create the helpers

Use Settings → Devices & services → Helpers. Create the named Number or Toggle helpers before loading the templates. Where this guide uses a Derivative, Statistics, History Stats, Integral, Schedule or Utility Meter helper, the current Home Assistant interface can create it; the source links below describe the current options.

Helper or derived entityPurposeSetting
sensor.server_cupboard_temperature_rateDerivative helper in °C/hSet the time window and healthy rise-rate range from the History data you measured above
input_number.server_cupboard_min_load_wMinimum load for performance checksSet this from the idle/busy wattage range you recorded for the rack power sensor
input_number.server_cupboard_warning_delta_cAbnormal cupboard-minus-room deltaSet this above the largest cupboard-minus-room gap you saw on a normal day
input_number.server_cupboard_warning_rate_c_per_hourAbnormal positive rise rateSet this above the healthy rise rate you measured for this cupboard
input_number.server_cupboard_hold_minutesPersistence before WarningSet this long enough to reject the brief spikes you saw in your own History, short enough to still catch a real fault
input_number.server_cupboard_critical_temperature_cLast-line cupboard thresholdSet this to the manufacturer's or your own last-line safe limit for the equipment in this cupboard
input_boolean.server_cupboard_monitoring_enabledMaster notification gateLeave this off until you've validated every threshold above against your own data, then switch it on
input_number.server_cupboard_critical_repeat_minutesCritical repeat spacingChoose any repeat interval greater than zero that matches how urgently you want reminders

Current Home Assistant YAML

Merge top-level keys with your existing configuration instead of duplicating them. Replace the example entity IDs consistently. If you keep automations in automations.yaml, paste only the automation list items there, without a top-level automation: wrapper.

Combine thermal rise, ambient delta and operating context

Create the Derivative helper from the cupboard probe with °C per hour as its displayed time unit. The template then checks relationships rather than blindly copying a universal temperature.

template:
  - sensor:
      - name: "Server cupboard room delta"
        unique_id: server_cupboard_room_delta
        default_entity_id: sensor.server_cupboard_room_delta
        device_class: temperature
        state_class: measurement
        unit_of_measurement: "°C"
        availability: >
          {{ has_value('sensor.server_cupboard_temperature')
             and has_value('sensor.study_temperature') }}
        state: >
          {{ ((states('sensor.server_cupboard_temperature') | float)
              - (states('sensor.study_temperature') | float)) | round(2) }}

  - binary_sensor:
      - name: "Server cupboard cooling warning"
        unique_id: server_cupboard_cooling_warning
        default_entity_id: binary_sensor.server_cupboard_cooling_warning
        device_class: problem
        delay_on:
          minutes: >
            {{ states('input_number.server_cupboard_hold_minutes') | int(0) }}
        availability: >
          {{ has_value('sensor.server_cupboard_room_delta')
             and has_value('sensor.server_cupboard_temperature_rate')
             and has_value('sensor.server_rack_power') }}
        state: >
          {% set loaded = (states('sensor.server_rack_power') | float)
             >= (states('input_number.server_cupboard_min_load_w') | float) %}
          {% set hot_delta = (states('sensor.server_cupboard_room_delta') | float)
             >= (states('input_number.server_cupboard_warning_delta_c') | float) %}
          {% set rising = (states('sensor.server_cupboard_temperature_rate') | float)
             >= (states('input_number.server_cupboard_warning_rate_c_per_hour') | float) %}
          {{ loaded and hot_delta and rising }}
        attributes:
          reason: >-
            Cupboard-room delta is {{ states('sensor.server_cupboard_room_delta') }} °C,
            rate is {{ states('sensor.server_cupboard_temperature_rate') }} °C/h,
            load is {{ states('sensor.server_rack_power') }} W and fan is
            {{ states('binary_sensor.server_cupboard_fan_running') }}.

      - name: "Server cupboard cooling critical"
        unique_id: server_cupboard_cooling_critical
        default_entity_id: binary_sensor.server_cupboard_cooling_critical
        device_class: problem
        availability: >
          {{ has_value('sensor.server_cupboard_temperature') }}
        state: >
          {{ (states('sensor.server_cupboard_temperature') | float)
             >= (states('input_number.server_cupboard_critical_temperature_c') | float)
             or (is_state('binary_sensor.server_cupboard_cooling_warning', 'on')
                 and is_state('binary_sensor.server_cupboard_fan_running', 'off')) }}

Warning and Critical notification actions

Replace the mobile notification action and media-player entity with your own. Put critical-monitoring-alert.mp3 in /media, test it manually, and leave the monitoring Toggle off until calibration is complete.

automation:
  - id: server_cupboard_warning_push
    alias: "Server cupboard - Warning push"
    mode: single
    triggers:
      - trigger: state
        entity_id: binary_sensor.server_cupboard_cooling_warning
        to: "on"
    conditions:
      - condition: state
        entity_id: input_boolean.server_cupboard_monitoring_enabled
        state: "on"
    actions:
      - action: notify.mobile_app_your_phone
        data:
          title: "Server cupboard warning"
          message: >-
            {{ state_attr('binary_sensor.server_cupboard_cooling_warning', 'reason')
                or 'The warning condition is active. Check Home Assistant for evidence.' }}

  - id: server_cupboard_critical_repeat
    alias: "Server cupboard - Critical repeat"
    mode: restart
    triggers:
      - trigger: state
        entity_id: binary_sensor.server_cupboard_cooling_critical
        to: "on"
    conditions:
      - condition: state
        entity_id: input_boolean.server_cupboard_monitoring_enabled
        state: "on"
    actions:
      - action: notify.mobile_app_your_phone
        data:
          title: "Server cupboard critical"
          message: "The critical condition is active. Check the equipment and evidence now."
      - action: media_player.play_media
        target:
          entity_id: media_player.alert_speaker
        data:
          media_content_id: "media-source://media_source/local/critical-monitoring-alert.mp3"
          media_content_type: "audio/mpeg"
      - repeat:
          while:
            - condition: state
              entity_id: binary_sensor.server_cupboard_cooling_critical
              state: "on"
            - condition: template
              value_template: >-
                {{ states('input_number.server_cupboard_critical_repeat_minutes') | int(0) > 0 }}
          sequence:
            - delay:
                minutes: >-
                  {{ states('input_number.server_cupboard_critical_repeat_minutes') | int(0) }}
            - condition: state
              entity_id: binary_sensor.server_cupboard_cooling_critical
              state: "on"
            - action: notify.mobile_app_your_phone
              data:
                title: "Server cupboard still critical"
                message: "The critical condition remains active."
            - action: media_player.play_media
              target:
                entity_id: media_player.alert_speaker
              data:
                media_content_id: "media-source://media_source/local/critical-monitoring-alert.mp3"
                media_content_type: "audio/mpeg"

Why the logic works

The derivative spots accumulation, the room delta localises it to the cupboard, and power proves there is a meaningful heat load. Fan disagreement raises urgency, while the absolute threshold remains a last line rather than the only detector.

The design is intentionally diagnostic rather than controlling. It reports an impossible or abnormal relationship; it does not bypass equipment protection or decide that a single suspected cause is proven.

Severity tiers without alert fatigue

TierMeaningAction
AdvisoryThe thermal delta or rise rate is outside its recent healthy envelope.Dashboard only
WarningLoad, local temperature difference and rising temperature persist together.One push notification
CriticalThe site-specific temperature limit is crossed, or the corroborated warning coincides with a stopped fan.Push, repeat and audible alert

A weak clue remains visible without interrupting you. A Warning requires persistence or corroboration. Critical is reserved for direct danger or several independent signals. That separation is what stops a useful monitoring system becoming notification wallpaper.

Troubleshooting

Busy workloads trigger warnings
Capture those workloads and either widen the healthy envelope or add a workload-state condition.
The room sensor is too far away
Place the reference in the same thermal zone but outside the cupboard exhaust plume.
Fan says on but is physically stopped
Use fan power, current, tachometer or airflow where possible; a commanded state is not proof.
Rate is noisy
Lengthen the Derivative helper time window and confirm probe updates are frequent enough.
Warnings arrive after a Home Assistant restart
Require fresh sensor timestamps and an appropriate persistence period before enabling notifications.

Safety and limits

Do not obstruct ventilation or deliberately overheat electronics. Keep mains work, fixed power monitoring and fan wiring with a licensed electrician where required. An alarm is not a substitute for equipment thermal protection or backups.

If a webhook is added later, treat its webhook_id like a password, keep local_only: true unless a reviewed design requires otherwise, and never use an unauthenticated webhook for locks, garage doors, power isolation or anything destructive.

Official sources

These are the primary documentation pages used for the design. Check them against the Home Assistant or ESPHome version you are actually running.

Related Smart Home Fix guides

FAQ

What temperature is too hot for a server cupboard?

Use the limits for the actual equipment and probe location, then validate them against normal hot-day data. There is no trustworthy universal cupboard value.

Why compare the cupboard with the room?

The difference isolates local heat build-up. A heatwave may warm both sensors, while failed cupboard airflow usually increases the gap.

Does a fan entity prove that air is moving?

Not necessarily. A command or relay state can remain on with a stalled fan. Power, tachometer or airflow feedback gives stronger evidence.

How do I choose the temperature rate?

Measure normal rise rates in °C per hour, then set a threshold outside that envelope. Convert any per-second Trend value before comparing it.

Can this replace server monitoring and backups?

No. It adds environmental fault evidence. Keep equipment health monitoring, graceful shutdown, tested backups and manufacturer thermal protection.