Design: Server-room dynamic thermal management (rolling setpoint + pre-cool + solar)

:thermometer::snowflake: Server-Room Dynamic Thermal Management — System Design

Goal: Home Assistant continuously finds and holds the sweet-spot setpoint for the
server-room minisplit — on a rolling basis — while absorbing on-demand HPC gear
(rented / brought up for big jobs), using calendar lookahead to pre-cool, and
preferentially spending excess solar instead of grid watts.

Status: Design v1 (2026-09-03) · Tracking:
#614 (implementation) ·
#629 (Carrier dongle, Friday 2026-09-04) ·
#760 (SolArk solar) ·
#344 (HA platform)
· First calibration dataset captured 2026-09-03 (see §4).


1. :bullseye: The problem, stated precisely

The server room is a thermal system with three forcing inputs and one actuator:

Input Nature Known to HA?
:abacus: Gear heat load watts → BTU (1 W = 3.412 BTU/h), step changes when rental/HPC gear energizes Yes — that’s the design’s core bet
:sun: Envelope load heat ingress from ambient + sun (weather-driven, forecastable) Yes (met.no + room sensors)
:high_voltage: Energy source economics grid $/kWh vs free excess solar Yes (smart meter + SolArk #760)
:snowflake: Actuator minisplit setpoint (BTU capacity follows ΔT) Yes after dongle pairing (#629)

Sweet spot = the setpoint that minimizes energy_cost × cooling_energy subject to
hard equipment-thermal constraints — recomputed on a rolling basis as load, weather,
and solar change. Not a fixed number; a policy.

2. :world_map: Architecture

flowchart TB
    subgraph SENSE[📡 Sensing]
        T[Thermal fleet<br/>7 hosts silicon + rack + ambient<br/>+ minisplit coil/return via dongle]
        P[Power<br/>whole-house meter · node watts<br/>RENTAL BANK metering NEW]
        SOL[SolArk #760<br/>production · battery SOC · grid flow]
        W[Weather/met.no<br/>ambient + forecast]
        CAL[📅 Job calendar<br/>rental/HPC schedule]
    end

    subgraph BRAIN[🧠 Home Assistant — pfv-bms]
        LOAD[Load model<br/>watts→BTU timeline<br/>calendar lookahead]
        THERM[Thermal model<br/>rolling learned response<br/>setpoint→cooling-rate curves]
        ECON[Cost model<br/>$/kWh · solar excess windows]
        OPT[🎯 Setpoint policy engine<br/>optimize subject to guardrails]
        ADMIT[⚡ Load admission<br/>gear on/off interlocks]
    end

    subgraph ACT[⚙️ Actuation]
        AC[❄️ Minisplit setpoint<br/>via dongle · rate-limited]
        GEAR[🔌 Rental gear banks<br/>metered PDU / relays]
        ALERT[📱 Alerts + logbook rationale<br/>companion apps]
    end

    T & P & SOL & W & CAL --> BRAIN
    LOAD & THERM & ECON --> OPT
    OPT --> AC
    OPT --> ADMIT --> GEAR
    BRAIN --> ALERT

3. :puzzle_piece: Components (what exists / what’s new)

Exists today :white_check_mark:

  • Full thermal sensor fleet (plant_snmp: 7 hosts, rack temp, ambients, iDRAC ΔT)
    • tiered alert thresholds (drives/DIMM 140°F, NVMe 150°F, PCH 160°F, GPU 185°F, ambient 104°F).
  • Whole-house kWh (Smart Meter Texas), tsys6/7 node watts, garage PDU amps, UPS runtime.
  • Weather + forecast (met.no, verified live).
  • Calendar platform (HA) — ready to ingest a job calendar.
  • Alerting + arm-switch pattern (doorman’s founder-level control idiom, reusable).
  • First calibration dataset (see §4).

Lands Friday (#629) :soon_arrow:

  • Carrier dongle → HA climate entity for the minisplit. Design assumption:
    setpoint read and write. If write is unavailable, the system degrades
    gracefully to Advisory mode (§6) until hardware allows write.

New hardware/decisions :new_button:

  1. Rental-bank power metering (per bank or per circuit):
    metered PDU (APC pattern) or CT-clamp energy monitor (Shelly EM class).
    Required: per-bank watts so HA can turn “rental coming online” into exact BTU.
  2. Rental-bank control (optional per bank): relay/PDU outlet switching —
    enables admission control (§5.3). Manual-energize banks still work with
    metering-only (HA reacts instead of gates).
  3. SolArk RS485 integration (#760) for production/battery/SOC.

4. :bar_chart: The thermal model — “rolling sweet spot” mechanics

Calibration data point #1 (2026-09-03, manual): setpoint 80→70°F produced
intake −9°F, rack −4.9°F, hottest drive −1.8°F over ~6 h. Drives are
airflow-bound (known tsys4 cage issue) — room air alone doesn’t fix them;
the optimizer must learn per-sensor response, not just room average.

Model per sensor class (room air, rack, drives, silicon):

T_sensor(t+1) = T_sensor(t) + (P_gear·k1 + P_envelope(ambient,sun)·k2
                − P_cool(setpoint, T_room)·k3) · dt / C_sensor
  • Coefficients learned by rolling regression over the last N days
    (recomputed nightly; HA utility_meter + recorder history feed it).
  • Cool-down/heating rates per setpoint per ambient band → pre-cool lead time
    is computed, not guessed: lead = ΔT_needed / learned_rate.

Optimizer (v2, closed loop): every 15–30 min, pick setpoint ∈ allowed band
minimizing predicted cost, subject to: P95(sensor temps) under thresholds,
minisplit duty-cycle sanity, solar-excess window schedule. Hysteresis: max
2°F step, 20-min dwell minimum (minisplits hate churn).

5. :high_voltage: On-demand gear: watts/BTU admission + pre-cool

  1. Calendar ingestion (hpc-jobs calendar, any CalDAV source): job entries
    carry bank IDs + expected watts (or HA looks up per-bank draw from history).
  2. Pre-cool trigger: at job_start − lead, cool toward pre-cool target
    (computed so room hits job-optimal at start).
  3. Solar preference: if SolArk reports excess production (or forecast high),
    schedule pre-cool INTO the surplus window — “store cold” in the room’s thermal
    mass instead of exporting/curtailing. Battery SOC arbitrates: surplus-first,
    battery-second, grid-last (policy knob).
  4. Admission control (v3): a bank may be energized only if
    cooling_capacity ≥ bank_BTU + existing_load within the alert thresholds —
    else HA alerts with the wait/lead-time estimate. Founder override always wins
    (revenue gear never silently blocked; policy = warn-and-allow by default).

6. :vertical_traffic_light: Phased rollout

Phase What Gate
0 — Baseline (Fri) Dongle paired; climate entity live; HA dashboards show setpoint + full thermal fleet; data collection ON #629 UAT
1 — Advisory Policy engine computes recommended setpoint + pre-cool advice; displays + notifies; no actuation; thermal model training begins 1 week clean data
2 — Closed loop Auto setpoint within conservative band (68–80°F), hysteresis + dwell, manual-override detection (housemate turns dial → respected N hours, then advisory nudge) Founder acceptance of advisory accuracy
3 — Full orchestration Calendar pre-cool + solar-aware scheduling + bank admission control + rolling optimizer widening the band Solar live (#760) + rental metering installed

7. :shield: Guardrails (non-negotiable)

  • Arm switch (doorman idiom): Off / Advisory / Auto — founder-level control.
  • Alert tiers remain the hard ceiling — the optimizer may never trade
    equipment safety for cost.
  • Every setpoint change logged to logbook with rationale (predicted load,
    solar window, model confidence).
  • Manual override (physical dial) detected via setpoint deltas → policy:
    respect 4 h, notify, then resume advisory.
  • UPS-on-battery → max-cooling bias + load-shed advisory (cooling is the
    priority consumer of runtime).
  • All config in KNEL/pfv-bms repo (deploy pipeline with ha core check gate).

8. :red_question_mark: Open questions (founder)

  1. Job calendar source — which system holds the rental/HPC schedule
    (Google/CalDAV/gitea/HA-local)? Who writes entries?
  2. Rental banks — how many circuits/banks, typical wattage, metered-PDU vs
    CT-clamp preference, and should banks be relay-controllable or metering-only?
  3. Setpoint band + override policy — OK with 68–80°F working band and the
    4-hour manual-override respect window?
  4. Electricity plan — flat or time-of-use? (Pre-cool economics change.)
  5. Minisplit nameplate (Friday #629): model + BTU rating — confirms capacity
    ceiling for admission math; and does the dongle expose setpoint WRITE?

Design v1 by the HA/plant lane, 2026-09-03. Implementation tracked on [#614];
discussion in this topic.