Smarter Thermostat with Home Assistant Dark _ □ ✕
  • Posts
  • About
  • Debug
Post

Smarter Thermostat with Home Assistant

Published
2026-08-03 11:59 UTC (precision: second)
Source feed
Michael Harley (All content) excluded
Link
https://michaelharley.net/posts/2026/08/03/smarter-thermostat-with-home-assistant/
Canonical
https://michaelharley.net/posts/2026/08/03/smarter-thermostat-with-home-assistant
Length
9650 runes
Extracted text
Permalink A smart thermostat was one of the first connected devices I purchased when I started building our smart home. I wanted automatic presence detection so the thermostat could set an away mode when we leave and "we're home" mode without us having to do anything. Never mind the fact that Rachelle and I both work from home and are both homebodies. On the rare occasion that we're both away from home, the house knows it, automatically! ;) Throughout the year, I'd find myself fiddling with the set points though. 68°F (20°C) in February feels different than 68°F in August. So twice a year, as the seasons started to tip over into warmer or colder weather, I'd change the upper and lower bounds. It feels weird to run the AC in winter or the heat in summer. We sleep with the thermostat set at 65°F (18°C). Our preference for day time temp is 68°F to 72°F (20°C to 22°C). So when we wake up, and toggle sleep mode off in Home Assistant, the thermostat sets itself to the day time values and turns on the heat to raise the temp from 65 to 68; even in summer. That bothers me. I'd rather the thermostat just set the lowest temp to 68 and allow the house temp to naturally rise. Plus, I have all these damn sensors in the house, outside the house, and a weather forecast available. I feel like I have all the inputs needed to make some data-driven decisions. My climate dashboard. Goal # My goal was to stop running heat on summer mornings and AC on winter evenings. The set points stay fixed. What changes is the HVAC mode: heat-only, cool-only, or both. Data # For Home Assistant to pick the right mode, it needs to know what's going on outside, so I made a couple of template sensors. Outdoor Blend # It all comes down to one template sensor: sensor.outdoor_temp_blend. It's the average of three numbers: the current outdoor temperature, today's forecast high, and today's forecast low. The result tells me what kind of day this is rather than what the temperature is right this minute. That blend feeds sensor.thermostat_season, which labels the day heating, cooling or shoulder-season. The season is what picks the HVAC mode. Why average instead of just reading the current outdoor temp? A 50°F dawn before a 75°F afternoon shouldn't kick the heater on for an hour at sunrise. The blend flattens out the time-of-day swings so the thermostat isn't reacting to a number that's about to change anyway. - sensor: - name: "Outdoor Temp Blend" unique_id: outdoor_temp_blend device_class: temperature unit_of_measurement: "°F" state_class: measurement availability: >- {{ state_attr('weather.forecast_12_grimmauld_place', 'temperature') is not none and states('sensor.today_forecast_high') | float(none) is not none and states('sensor.today_forecast_low') | float(none) is not none }} state: >- {% set current = state_attr('weather.forecast_12_grimmauld_place', 'temperature') | float %} {% set high = states('sensor.today_forecast_high') | float %} {% set low = states('sensor.today_forecast_low') | float %} {{ ((current + high + low) / 3) | round(1) }} Thermostat Season # sensor.thermostat_season reads the outdoor blend and labels the day as one of three states: cool once blend reaches 68°F, holds until blend drops below 65°F heat once blend falls to 55°F, holds until blend rises above 58°F heat_cool for everything in between The mode scripts (thermostat_active_mode, thermostat_sleep_mode, thermostat_away_mode) read this sensor to decide whether the thermostat should run cooling only, heating only, or both with a deadband. That 3°F gap on each side is on purpose. Without it, a blend hovering right at 68°F would flip the HVAC mode every time the value bounced from 67.9 to 68.1 across forecast updates. The hysteresis means the season has to meaningfully change before the mode does. Once it decides it's summer, it doesn't second-guess itself over one cool morning. - sensor: - name: "Thermostat Season" unique_id: thermostat_season icon: >- {% set s = this.state %} {% if s == 'cool' %}mdi:snowflake {% elif s == 'heat' %}mdi:fire {% else %}mdi:swap-vertical{% endif %} availability: >- {{ states('sensor.outdoor_temp_blend') | float(none) is not none }} state: >- {% set blend = states('sensor.outdoor_temp_blend') | float %} {% set prev = this.state if this.state in ['heat','cool','heat_cool'] else 'heat_cool' %} {% if prev == 'cool' %} { {{ 'heat_cool' if blend < 65 else 'cool' }} {% elif prev == 'heat' %} { {{ 'heat_cool' if blend > 58 else 'heat' }} {% else %} { {% if blend >= 68 %}cool {% elif blend <= 55 %}heat {% else %}heat_cool {% endif %} {% endif %} Helpers # I built some helpers that allow me to easily set the temperature range. They're just input_number helpers, one pair per mode: day, night and away. The scripts below read them by name, so when I nudge the day time high from 72 to 71 on my dashboard, that's the number the thermostat gets the next time anything runs. Putting it all together now # Three scripts do the actual work: thermostat_active_mode, thermostat_sleep_mode and thermostat_away_mode. They're deliberately dumb. Each one reads sensor.thermostat_season, picks a branch, and writes to the thermostat. That's the whole job. None of them know a thing about the weather. Here's the day time one. I trimmed a couple of housekeeping steps off the end (it also flips some booleans and sets the fan mode) to keep the important part visible: thermostat_active_mode: alias: Thermostat active mode sequence: - choose: - conditions: - condition: state entity_id: sensor.thermostat_season state: cool sequence: - action: climate.set_temperature target: entity_id: climate.t6_pro_z_wave_programmable_thermostat data: hvac_mode: cool temperature: "{{ states('input_number.thermostat_day_temp_high') | float }}" - conditions: - condition: state entity_id: sensor.thermostat_season state: heat sequence: - action: climate.set_temperature target: entity_id: climate.t6_pro_z_wave_programmable_thermostat data: hvac_mode: heat temperature: "{{ states('input_number.thermostat_day_temp_low') | float }}" default: - action: climate.set_temperature target: entity_id: climate.t6_pro_z_wave_programmable_thermostat data: hvac_mode: heat_cool target_temp_high: "{{ states('input_number.thermostat_day_temp_high') | float }}" target_temp_low: "{{ states('input_number.thermostat_day_temp_low') | float }}" mode: single In cool season the script sets hvac_mode: cool and one number, the day time high. That's it. No lower bound anywhere in that call, so nothing can ask for heat. Winter is the same trick backwards, hvac_mode: heat and just the day time low. Only the shoulder season sends both numbers as a deadband, which is what I had before. Sleep and away are the same shape. They just read their own pair of helpers. What calls the scripts # Mostly, nothing new. My wake, sleep and away automations already called these three scripts, because that's how the house has worked for a while now. They picked up the season awareness for free. I didn't have to touch a single one of them. So that's cool. The one thing I did have to add is an automation for when the season itself changes: - id: thermostat_reevaluate_on_season_change alias: Thermostat, re-evaluate on season change triggers: - trigger: state entity_id: sensor.thermostat_season to: - heat - cool - heat_cool conditions: - condition: template value_template: >- {{ trigger.from_state is not none and trigger.from_state.state in ['heat','cool','heat_cool'] }} actions: - choose: - conditions: - condition: state entity_id: input_boolean.thermostat_away_mode state: "on" sequence: - action: script.thermostat_away_mode - conditions: - condition: state entity_id: input_boolean.thermostat_night_mode state: "on" sequence: - action: script.thermostat_sleep_mode - conditions: - condition: state entity_id: input_boolean.thermostat_day_mode state: "on" sequence: - action: script.thermostat_active_mode mode: single Without it, a season flip in the middle of a Tuesday afternoon would just sit there. Nothing would be calling a script until bedtime. This one re-runs whichever mode is already active so the thermostat catches up. The condition looks fussier than it needs to be. It's there because the season sensor drops to unavailable for a split second now and then, usually when the weather integration hiccups or Home Assistant restarts. Without the guard, every one of those blips would re-fire the thermostat. I went back through the last couple of weeks of history and it happened 16 times. So, worth having. Conclusion # So, back to the thing that bugged me. Summer morning, house is sitting at 65, I toggle sleep mode off. The season is cool, so the script sends cool-only at 72 and there's no lower bound for the furnace to chase. The house drifts up to 68 on its own, for free, the way it would have anyway if I'd just left it alone. I pulled the history to make sure I wasn't fooling myself. Over the last twelve days there were 13 mornings where the house was sitting at exactly 65°F when we got up, and the furnace ran for zero minutes. Under the old setup that's 13 mornings of running heat in the middle of summer. I should say though, it's August. The heat branch has never actually fired for real. I've read it a dozen times and I think it's right. Ask me again in December. I'm not an HVAC guy and there's almost certainly a smarter way to do this. What's your approach? Does your thermostat know what time of year it is, or do you fiddle with it twice a year like I used to? Michael Harley Author Also on Mastodon (opens in a new tab) Lemmy (opens in a new tab) Bluesky (opens in a new tab)
Stored topics

Every stored assignment is listed, including rows below the current cutoff (0.75), so a missing tag can be explained.

Concept Code Score Meter Margin Origin Path
science and technology science-and-technology 0.97 3.35 direct science and technology
economy, business and finance economy-business-and-finance 0.93 2.58 ↑derived from 20000231 economy, business and finance
products and services products-and-services 0.93 2.58 ↑derived from 20000231 economy, business and finance > products and services
computing and information technology computing-and-information-technology 0.93 2.58 ↑derived from 20000231 economy, business and finance > products and services > computing and information technology
software and applications software-and-applications 0.93 2.58 direct economy, business and finance > products and services > computing and information technology > software and applications
technology and engineering technology-and-engineering 0.91 2.25 direct science and technology > technology and engineering
information technology and computer science information-technology-and-computer 0.90 2.23 direct science and technology > technology and engineering > information technology and computer science
weather weather 0.87 1.88 ↑derived from 20001128 weather
weather forecast weather-forecast 0.87 1.88 direct weather > weather forecast
environment environment 0.63 0.53 direct environment
Information security information-security 0.61 0.44 direct science and technology > technology and engineering > information technology and computer science > Information security
labour labour 0.59 0.37 direct labour
Open source open-source 0.50 -0.01 direct economy, business and finance > products and services > computing and information technology > software and applications > Open source
climate change climate-change 0.49 -0.02 direct environment > climate change
Software development software-development 0.48 -0.09 direct economy, business and finance > products and services > computing and information technology > software and applications > Software development
social sciences social-sciences 0.48 -0.09 ↑derived from 20000747 science and technology > social sciences
history history 0.48 -0.09 direct science and technology > social sciences > history
artificial intelligence artificial-intelligence 0.41 -0.35 direct science and technology > technology and engineering > information technology and computer science > artificial intelligence
lifestyle and leisure lifestyle-and-leisure 0.29 -0.90 ↑derived from 20000538 lifestyle and leisure
leisure leisure 0.29 -0.90 direct lifestyle and leisure > leisure
Self-hosting self-hosting 0.18 -1.52 direct economy, business and finance > products and services > computing and information technology > Self-hosting
game game 0.14 -1.79 ↑derived from local:tabletop-gaming lifestyle and leisure > leisure > game
Tabletop gaming tabletop-gaming 0.14 -1.79 direct lifestyle and leisure > leisure > game > Tabletop gaming
health health 0.13 -1.88 direct health
arts, culture, entertainment and media arts-culture-entertainment-and-media 0.12 -2.00 ↑derived from 20000051 arts, culture, entertainment and media
mass media mass-media 0.12 -2.00 ↑derived from 20000051 arts, culture, entertainment and media > mass media
television television 0.12 -2.00 direct arts, culture, entertainment and media > mass media > television
social media social-media 0.10 -2.25 ↑derived from local:blogging arts, culture, entertainment and media > mass media > social media
Blogging blogging 0.10 -2.25 direct arts, culture, entertainment and media > mass media > social media > Blogging
media and entertainment industry media-and-entertainment-industry 0.09 -2.30 ↑derived from 20000306 economy, business and finance > products and services > media and entertainment industry
books and publishing books-and-publishing 0.09 -2.30 direct economy, business and finance > products and services > media and entertainment industry > books and publishing
health treatment and procedure health-treatment-and-procedure 0.09 -2.34 direct health > health treatment and procedure
natural science natural-science 0.09 -2.35 direct science and technology > natural science
hobby hobby 0.08 -2.48 direct lifestyle and leisure > leisure > hobby
society society 0.08 -2.49 ↑derived from 20001300 society
fundamental rights fundamental-rights 0.08 -2.49 ↑derived from 20001300 society > fundamental rights
privacy privacy 0.08 -2.49 direct society > fundamental rights > privacy
family family 0.07 -2.56 direct society > family
video game video-game 0.06 -2.69 ↑derived from local:mmorpg lifestyle and leisure > leisure > game > video game
MMORPG mmorpg 0.06 -2.69 direct lifestyle and leisure > leisure > game > video game > MMORPG
streaming service streaming-service 0.06 -2.69 direct economy, business and finance > products and services > media and entertainment industry > streaming service
human interest human-interest 0.06 -2.73 direct human interest
arts and entertainment arts-and-entertainment 0.06 -2.74 ↑derived from 20000013 arts, culture, entertainment and media > arts and entertainment
literature literature 0.06 -2.74 direct arts, culture, entertainment and media > arts and entertainment > literature
sport sport 0.05 -2.85 direct sport
scientific research scientific-research 0.05 -2.89 ↑derived from 20000739 science and technology > scientific research
scientific exploration scientific-exploration 0.05 -2.89 ↑derived from 20000739 science and technology > scientific research > scientific exploration
space exploration space-exploration 0.05 -2.89 direct science and technology > scientific research > scientific exploration > space exploration
animation animation 0.04 -3.10 direct arts, culture, entertainment and media > arts and entertainment > animation
biology biology 0.04 -3.14 direct science and technology > natural science > biology
Cryptography cryptography 0.04 -3.21 direct science and technology > technology and engineering > information technology and computer science > Cryptography
politics and government politics-and-government 0.03 -3.62 direct politics and government
film industry film-industry 0.03 -3.63 direct economy, business and finance > products and services > media and entertainment industry > film industry
podcast podcast 0.02 -3.67 direct economy, business and finance > products and services > media and entertainment industry > podcast
disease and condition disease-and-condition 0.02 -3.81 ↑derived from 20000458 health > disease and condition
mental health and disorder mental-health-and-disorder 0.02 -3.81 direct health > disease and condition > mental health and disorder
competition discipline competition-discipline 0.02 -3.83 ↑derived from 20001183 sport > competition discipline
eSports esports 0.02 -3.83 direct sport > competition discipline > eSports
travel and tourism travel-and-tourism 0.02 -3.94 direct lifestyle and leisure > leisure > travel and tourism
crime, law and justice crime-law-and-justice 0.02 -3.95 ↑derived from 20000086 crime, law and justice
crime crime 0.02 -3.95 ↑derived from 20000086 crime, law and justice > crime
cyber crime cyber-crime 0.02 -3.95 direct crime, law and justice > crime > cyber crime
education education 0.02 -3.97 direct education
public health public-health 0.02 -3.99 direct health > public health
disaster, accident and emergency incident disaster-accident-and-emergency-incident 0.02 -4.01 direct disaster, accident and emergency incident
culture culture 0.02 -4.13 direct arts, culture, entertainment and media > culture
exercise and fitness exercise-and-fitness 0.01 -4.32 direct lifestyle and leisure > wellness > exercise and fitness
wellness wellness 0.01 -4.32 ↑derived from 20001239 lifestyle and leisure > wellness
card game card-game 0.01 -4.40 direct lifestyle and leisure > leisure > game > card game
consumer goods consumer-goods 0.01 -4.41 ↑derived from 20001160 economy, business and finance > products and services > consumer goods
handicrafts handicrafts 0.01 -4.41 direct economy, business and finance > products and services > consumer goods > handicrafts
board game board-game 0.01 -4.70 direct lifestyle and leisure > leisure > game > board game
award and prize award-and-prize 0.01 -4.81 direct human interest > award and prize
conflict, war and peace conflict-war-and-peace 0.01 -4.83 direct conflict, war and peace
religion religion 0.01 -5.01 direct religion
visual arts visual-arts 0.01 -5.10 ↑derived from 20000036 arts, culture, entertainment and media > arts and entertainment > visual arts
photography photography 0.01 -5.10 direct arts, culture, entertainment and media > arts and entertainment > visual arts > photography
diet diet 0.00 -5.34 direct health > health treatment and procedure > diet
music music 0.00 -5.45 direct arts, culture, entertainment and media > arts and entertainment > music
lifestyle lifestyle 0.00 -5.74 ↑derived from 20001257 lifestyle and leisure > lifestyle
house and home house-and-home 0.00 -5.74 ↑derived from 20001257 lifestyle and leisure > lifestyle > house and home
gardening gardening 0.00 -5.74 direct lifestyle and leisure > lifestyle > house and home > gardening
food and drink enthusiasm food-and-drink-enthusiasm 0.00 -5.75 direct lifestyle and leisure > leisure > hobby > food and drink enthusiasm

← Back to posts

feedtagger 0.1.0-dev model: modernbert-base-zeroshot-v2.0/int8@all-s256 647 posts 0 unclassified min score 0.75