Seasonal Climate Controller

Climate Seasonal Comfort Controller

A single blueprint that runs a climate device — heat pump, AC, or a heat-only / cool-only unit — the way you'd actually want it run all year: a proper comfort schedule, a winter/summer split, and a thermal-demand filter that stops it short-cycling, plus an opt-in stack of extras (presence, room vacancy, open-window response, frost protection, mould protection, condensation dry sweeps, timed boost) that you can turn on one at a time as you need them.

Almost everything is optional with sensible defaults. At its simplest you point it at a climate entity, tell it which modes the device has, give it a winter/summer helper, and enable one comfort period. Everything else layers on top only if you switch it on.

Two variants below — pick the one that matches your Home Assistant temperature unit. The sliders and internal maths are fixed to a unit, so a °F install needs the imperial variant and everything else needs metric. Don't mix them.


Import

Metric — °C (most installs outside the US)

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Imperial — °F (Home Assistant set to Fahrenheit, e.g. most US installs)

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Both variants are identical in behaviour — only the temperature units, slider ranges, and a couple of internal margins differ. If a button doesn't open the import dialog, install manually instead: Settings → Automations & Scenes → Blueprints → Import Blueprint, paste the matching Gist URL above, or download the YAML (the Gist's Raw button) into config/blueprints/automation/<your-folder>/.

Minimum Home Assistant version: 2024.10.0.


Highlights

  • Seasonal — one automation handles both heating (winter) and cooling (summer), switched by a single helper.
  • Comfort schedule — up to 10 time periods, each with its own days, window, and per-season target. The periods are the schedule and the permission gate.
  • No short-cycling — a thermal-demand filter plus compressor minimum run/off timers decide when it actually switches.
  • Opt-in everything else — presence, per-room vacancy, open-window response, frost protection, mould protection, dry/condensation sweeps, and timed boost are all independent toggles.
  • Respects you — if you grab the remote or the app and change the unit yourself, it backs off. Works even on IR-controlled units that don't report who made the change.
  • Device-aware — you select which HVAC modes your unit supports; it never tries to use one you didn't tick. Setpoints are clamped to the device's own min/max.

Features

Seasonal operation. A winter/summer binary_sensor (on = winter) drives the whole thing. The easiest source is a Threshold helper on an outdoor-temperature sensor, so the house flips to summer automatically once it warms up — but any binary sensor or a manual toggle wrapped in a template sensor works.

Comfort schedule (10 periods). Each period has winter and summer enable switches, a name, a start/end time, selected days, and separate winter and summer target temperatures. A period is active when it's enabled for the current season, the time is inside its window, and today is a selected day. If periods overlap, the most-recently-started one wins. Outside every period the unit follows your out-of-comfort action (off, or hold a setback).

Comfort bands. Targets are paired with a band (an offset). The unit doesn't chase the exact setpoint — it lets the room drift within the band before acting, which is gentler on the compressor.

Thermal-demand filter. The core anti-cycling brain. To start, the room has to be meaningfully off target (beyond the band). To keep running, it considers the room, an outdoor threshold, and — optionally — a weather forecast trend and/or an indoor temperature trend (via a Derivative sensor), so it doesn't stop a second before it's actually warm/cool enough.

Compressor protection. Independent minimum run-time and minimum off-time, so nothing flaps on and off.

Presence (optional). Configure occupancy entities (people, phones, zone.home, or sensors) and comfort only runs while someone's home — heating starts once presence is detected. A guest-mode toggle keeps the house "occupied" even when trackers leave. Leave presence empty to run purely on the schedule.

Room vacancy (optional). Point it at a per-room presence/mmWave/motion sensor and it eases the room back (then optionally off) when the room itself is empty, in two configurable stages, even while the rest of the house is occupied.

Open window / door (optional). Door and window sensors trigger a two-stage response — ease the target back first, then optionally switch off after a longer delay — so you're not heating the street.

Frost protection (optional). An absolute safety floor: if the room falls below your frost minimum it forces heat to protect the space, overriding manual holds, boost, open windows, and even summer mode. Has its own hysteresis so it doesn't chatter.

Mould protection (optional). A humidity-driven warmth floor. When indoor RH climbs past your threshold it gently holds a minimum temperature to discourage mould. Point it at a mean-RH statistics helper for sustained tracking rather than instantaneous spikes.

Condensation / dry mode (optional). A moisture-mitigation mode, not a comfort mode. In winter it estimates the indoor dew point and runs short dry (dehumidify) sweeps when condensation is likely on the glass. It never overrides heating, yields to an open window or a manual change immediately, and has its own minimum-run, minimum-off, and maximum-runtime caps. Run it only when the house is empty, only inside a scheduled window, or both.

Timed boost (optional). A dashboard button that temporarily nudges the target (warmer in winter, cooler in summer) by your amount for a set duration, then switches itself off. Works relative to the active period — or to the setback when no period is active.

Manual-override awareness. If you change the unit yourself it stands down for a configurable period. The recommended path uses an input_boolean helper, which makes this reliable even on IR/SmartIR/Broadlink setups that can't tell HA who changed the device; there's also a context-based fallback if you don't add a helper.

Quality-of-life. Optional one-line status string written to an input_text helper (great on a dashboard), an unavailable-entity guard, and automatic setpoint clamping to the device's reported limits.


Requirements

Required

  • A climate entity — heat pump, AC, or a heat-only / cool-only device.
  • The list of HVAC modes your device actually supports (heat / cool / dry).
  • A winter/summer binary_sensor (on = winter). A Threshold helper on an outdoor temperature sensor is the easy option.
  • At least one enabled comfort period (otherwise there's nothing scheduled to run).

Optional — add only for the features you want

  • Manual-override helper — an input_boolean. Strongly recommended for IR-controlled units.
  • Room temperature sensor — otherwise it uses the climate entity's own reading.
  • Outdoor temperature sensor — improves the keep-running logic; required for dry/condensation.
  • Indoor temperature + humidity sensors — required for dry/condensation (dew-point).
  • Humidity sensor — for mould protection.
  • Weather entity — for the forecast-trend keep-running option.
  • Derivative sensor — for the indoor-trend start option.
  • Occupancy entitiesperson, device_tracker, zone, or binary_sensor.
  • Room presence sensor(s) — mmWave / motion, for room-vacancy easing.
  • Window/door binary_sensors — for the open-window response.
  • input_boolean helpers — guest mode, dry-mode runtime switch, boost button.
  • input_text helper — for the status string.

How it works (the model)

  • Periods are both the schedule and the gate. When a period is active it sets the target; when none is active the unit follows your out-of-comfort action.
  • Bands are offsets, not absolute temperatures. They define how far the room may drift before the unit acts.
  • The thermal-demand filter decides when, not what. Targets come from the schedule; this layer decides whether running is actually justified right now.
  • Priority, roughly top to bottom:
    1. Climate entity unavailable → do nothing.
    2. Frost protection (if enabled) overrides everything else.
    3. Your manual changes are respected (except while frost protection is active).
    4. Normal operation follows the schedule for the active season, with boost, room-vacancy, and open-window responses layered on, mould protection acting as a warmth floor, and dry sweeps running opportunistically only when heating isn't needed.
    5. Compressor timers and the demand filter govern the actual switching throughout.

Quick start (minimal setup)

  1. Import the variant that matches your unit system and create an automation from it.
  2. Climate Device — pick your climate entity and tick the modes it supports.
  3. Season Mode — choose your winter/summer binary_sensor.
  4. Comfort Period 1 — enable it for the season(s) you want, set the days, the time window, and a target temperature.
  5. Save. That's a working schedule with anti-cycling.
  6. Add features later by opening the relevant (collapsed) section and switching it on.

Notes & caveats

  • Pick the variant that matches your HA temperature unit and don't mix them — the slider labels, ranges, and a few internal margins are baked into each unit. The two are otherwise behaviourally identical.
  • IR / SmartIR / Broadlink units: add the manual-override input_boolean helper. Without it, manual changes from a physical remote can't be detected reliably (the integration doesn't preserve change context).
  • After updating the blueprint, reload it and — if any inputs changed — re-create the automation from it. Home Assistant caches blueprint inputs, so an automation built from an older copy keeps the old fields and layout until re-created.
  • Tested on real hardware, including an IR-controlled Hitachi heat pump via SmartIR.

Changelog

  • v1.0 — initial release (metric and imperial variants).

Feedback, bug reports, and feature ideas welcome — reply below.

1 Like

Feature request: setpoint compensation so the room sensor actually drives the device's internal target (like "follow me")

Thanks for this blueprint — really well thought out.

One issue I'm running into: when a room temperature sensor is configured, it's correctly used to decide when the unit should run (the thermal demand filter) — but the temperature value sent to the climate entity is still the raw comfort target itself. So if I set a winter target of 22°C, the blueprint sends exactly 22°C to the device's climate.set_temperature.

The problem is that the device's own internal sensor usually reads warmer than the actual room temperature in heating mode (mine reads several degrees higher once it's been running a while). So the device thinks it has reached 22°C internally and stops actively heating, even though the room (per my external sensor) is still well below 22°C. The thermal demand filter doesn't catch this either, since the climate entity's reported mode/state doesn't change — it just quietly heats less.

This is exactly the problem that some manufacturers' remote-based "follow me" / "feels like" feature solves, by using the remote's own sensor as the control reference instead of the unit's internal one — but that requires the remote to always be near the user, which isn't practical for fixed automations.

Suggestion: add an optional "setpoint compensation" feature — when a room sensor is configured, instead of sending the raw comfort target to the device, send a temperature adjusted to push the device's internal sensor past its own threshold while the room sensor still indicates it's below target. This could work as:

  • A fixed manual offset the user sets (e.g. "device reads 3°C warmer than my room sensor, so add 3°C to whatever I send it"), and/or
  • A dynamic offset based on the live difference between the room sensor and the device's own current_temperature attribute, recalculated each run while heating/cooling is active.
  • A simpler alternative to implement, if dynamic offset calculation feels like too much: when a room sensor is configured and the unit is in heating mode (or cooling mode, symmetrically), just send the device's maximum heat setpoint (e.g. 30°C, or whatever the device's max_temp is) instead of the raw comfort target, and let the device run at full output until the room sensor — not the device's internal one — reports that the target has been reached. At that point the thermal demand filter already has the logic to stop the unit. This avoids needing to know or calculate the offset between the room sensor and the device's internal sensor at all — it just lets the device push as hard as it can while the room sensor remains the sole authority on when to stop.

The thermal demand filter (start/keep-running/stop decisions) should keep using the room sensor and the raw comfort target as it does now — only the actual value sent via climate.set_temperature would get the compensation. That way the room sensor stays in control of when the device runs, and the compensation only affects how hard the device pushes while it's running.

Happy to help test if you want to try something out — I have a unit with a room sensor that reads noticeably cooler than the unit's internal sensor while heating, so it's an easy repro case.

1 Like

Apologies it's taken a moment to get back, it's been one of those weeks.
Awesome idea for inclusion.
Something I didn't think of as my wifi mini splits do quite well with compensating on their own (I barely see a 0.5°C difference between them and actual) and the one I have on my Broadlink RM4 has external temp by default.

It's built in to a beta version for testing(links below), as an optional Setpoint Compensation section that's off by default so nobody's existing setup changes.

It should work along the lines you suggested: the thermal-demand logic (start / keep-running / stop) still uses your room sensor and the raw target, untouched — only the value sent via climate.set_temperature gets compensated, and it's clamped to the device's own min/max. There's a mode select with four options:

  • Off — sends the target as-is (default).
  • Fixed offset — your "device reads N° warm" value, added to the sent setpoint. I'd start here — it keeps the inverter modulating, just biased higher.
  • Dynamic — adds the live current_temperature − room_sensor gap each run, capped by a limit you set. No calibration needed, but it relies on the device reporting current_temperature (yours does, from what you described).
  • Full output — sends the device's max setpoint (min when cooling) and lets the room sensor stop it. Simplest, but it gives up modulation and would likely overshoot/cycle, so more of a last resort.

It's symmetric for cooling, and the status line shows the sent value (e.g. Heating to 22° (sending 25°)) so you can see it working. One thing I made sure of: the manual-override detection compares against the compensated value, so the blueprint's own adjusted commands don't get mistaken for you reaching for the remote.

Would really appreciate your feedback on this one as I haven't got anything I can really test this on properly myself.
Will wait on your feedback before updating the main post.

Also wasn't sure which variant you needed so here's both.

°C variant: HVAC_Control_V1.1_beta · GitHub
°F variant: HVAC_Control_F_varient_V1.1_beta · GitHub

Cheers, and thanks again for offering to test.

Thank you so much for this — really appreciate you building it in so quickly, and exactly along the lines I described.

I'll get this set up and tested. One small wrinkle: we're in the middle of a heatwave here right now (around 40°C), so heating tests will have to wait until it's cooler. In the meantime I'll test the cooling side with Dynamic mode, since my unit does report current_temperature (it's a WiFi split, not IR), so that should be a good first test case.

Will report back with results once I've had a chance to run it properly — thanks again for the quick turnaround!