The dual_smart_thermostat is an enhanced version of generic thermostat implemented in Home Assistant.
| Feature | Icon | Documentation |
|---|---|---|
| Heater/Cooler Mode (Heat-Cool) | ![]() |
docs |
| Heater Only Mode | ![]() |
docs |
| Cooler Only Mode | ![]() |
docs |
| Two Stage (AUX) Heating Mode | ![]() |
docs |
| Fan Only Mode | ![]() |
docs |
| Fan With Cooler Mode | ![]() |
docs |
| Fan Speed Control | ![]() |
docs |
| Dry Mode (Humidity Control) | ![]() |
docs |
| Heat Pump Mode | ![]() |
docs |
| Floor Temperature Control | ![]() |
docs |
| Window/Door Sensor Integration (Openings) | ![]() |
docs |
| Preset Modes Support | docs | |
| HVAC Action Reason Tracking | docs |
Looking for ready-to-use configurations? Check out our examples directory with:
- Basic Configurations - Simple setups for heater-only, cooler-only, heat pumps, and dual-mode systems
- Advanced Features - Floor heating limits, two-stage heating, opening detection, and presets
- Integration Patterns - Smart scheduling and automation examples
- Single-Mode Thermostat Wrapper - Create Nest-like "Keep Between" functionality on single-mode thermostats
Each example includes complete YAML configurations with detailed explanations, troubleshooting tips, and best practices.
If both heater and cooler entities configured. The thermostat can control heating and cooling and you are able to set min/max low and min/max high temperatures.
In this mode you can turn the thermostat to heat only, cooler only and back to heat/cool mode.
If the fan entity is set the thermostat can control the fan mode of the AC. The fan will turn on when the temperature is above the target temperature and the fan_hot_tolerance is not reached. If the temperature is above the target temperature and the fan_hot_tolerance is reached the AC will turn on.
If only the heater entity is set the thermostat works only in heater mode.
Two stage or AUX heating can be enabled by adding the required configuration entities: secondary_heater, secondary heater_timeout. If these are set the feature will enable automatically.
Optionally you can set secondary heater_dual_mode to true to turn on the secondary heater together with the primary heater.
If the timeout ends and the heater was on for the whole time, the thermostat switches to the secondary heater. In this case, the primary heater (heater) will be turned off. This will be remembered for the day it turned on, and in the next heating cycle, the secondary heater will turn on automatically.
On the following day the primary heater will turn on again, and the second stage will again only turn on after a timeout.
If the third secondary heater_dual_mode is set to true, the secondary heater will be turned on together with the primary heater.
secondary_heater: switch.study_secondary_heater # <-- required
secondary_heater_timeout: 00:00:30 # <-- required
secondary_heater_timeout: true # <-- optionalIf the fan_mode entity is set to true the thermostat works only in fan mode. The heater entity will be treated as a fan only device.
heater: switch.study_heater
fan_mode: trueIf the ac_mode is set to true and the fan entity is also set, the heater entity will be treated as a cooler (AC) device with an additional fan device. This will allow not only the use of a separate physical fan device but also turning on the fan mode of an AC using advanced switches.
With this setup, you can use your AC's fan mode more easily.
heater: switch.study_heater
ac_mode: true
fan: switch.study_fanIf you also set the fan_hot_tolerance the fan will turn on when the temperature is above the target temperature and the fan_hot_tolerance is not reached. If the temperature is above the target temperature and the fan_hot_tolerance is reached the AC will turn on.
heater: switch.study_heater
ac_mode: true
fan: switch.study_fan
fan_hot_tolerance: 0.5If you set the fan_hot_tolerance, outside_sensor and the fan_air_outside the fan will turn on only if the outside temperature is colder than the inside temperature and the fan_hot_tolerance is not reached. If the outside temperature is colder than the inside temperature and the fan_hot_tolerance is reached the AC will turn on.
The dual_smart_thermostat automatically detects and enables fan speed control when you configure a fan entity that supports speed capabilities. This allows you to control your HVAC fan speeds (low, medium, high, auto) directly from the thermostat interface, just like built-in thermostats.
The thermostat automatically detects whether your fan entity supports speed control based on its capabilities:
- Native fan entities (
fandomain) withpreset_modeorpercentageattributes → Fan speed control enabled automatically - Switch entities (
switchdomain) → Traditional on/off control (backward compatible)
No configuration changes required - the thermostat detects capabilities at runtime.
climate:
- platform: dual_smart_thermostat
name: My Thermostat
heater: switch.study_heater
fan: fan.hvac_fan # Native fan entity - speeds automatically detected
target_sensor: sensor.study_temperatureWith this configuration, you'll see fan speed controls in the thermostat UI allowing you to select speeds like "auto", "low", "medium", "high" depending on what your fan entity supports.
Existing configurations using switch entities continue working unchanged:
climate:
- platform: dual_smart_thermostat
name: My Thermostat
heater: switch.study_heater
fan: switch.fan_relay # Switch entity - on/off only (no speed control)
target_sensor: sensor.study_temperatureFan speed control works seamlessly with all existing fan-related features:
- FAN_ONLY Mode: Fan runs at selected speed in fan-only mode
- Fan with AC (
fan_on_with_ac): Fan runs at selected speed when AC is active - Fan Hot Tolerance: Fan activates at selected speed when temperature tolerance is exceeded
- Heat Pump Mode: Fan speed applies to both heating and cooling operations
Your fan speed selection persists across heating/cooling cycles and restarts.
If you currently use a switch entity for your fan but want speed control, you can create a template fan entity that wraps your switch. Here are several examples:
This example uses an input_select helper to provide speed presets:
# Helper for fan speed selection
input_select:
hvac_fan_speed:
name: HVAC Fan Speed
options:
- "auto"
- "low"
- "medium"
- "high"
initial: "auto"
# Template fan wrapping switch + speed control
fan:
- platform: template
fans:
hvac_fan:
friendly_name: "HVAC Fan"
value_template: "{{ is_state('switch.fan_relay', 'on') }}"
preset_mode_template: "{{ states('input_select.hvac_fan_speed') }}"
preset_modes:
- "auto"
- "low"
- "medium"
- "high"
turn_on:
service: switch.turn_on
target:
entity_id: switch.fan_relay
turn_off:
service: switch.turn_off
target:
entity_id: switch.fan_relay
set_preset_mode:
service: input_select.select_option
target:
entity_id: input_select.hvac_fan_speed
data:
option: "{{ preset_mode }}"
# Use in thermostat
climate:
- platform: dual_smart_thermostat
name: My Thermostat
heater: switch.study_heater
fan: fan.hvac_fan # Uses template fan with speed control
target_sensor: sensor.study_temperatureThis example uses an input_number helper for percentage-based speed control:
# Helper for fan percentage
input_number:
hvac_fan_speed:
name: HVAC Fan Speed
min: 0
max: 100
step: 1
unit_of_measurement: "%"
# Template fan with percentage support
fan:
- platform: template
fans:
hvac_fan:
friendly_name: "HVAC Fan"
value_template: "{{ is_state('switch.fan_relay', 'on') }}"
percentage_template: "{{ states('input_number.hvac_fan_speed') | int }}"
turn_on:
service: switch.turn_on
target:
entity_id: switch.fan_relay
turn_off:
service: switch.turn_off
target:
entity_id: switch.fan_relay
set_percentage:
- service: input_number.set_value
target:
entity_id: input_number.hvac_fan_speed
data:
value: "{{ percentage }}"
# Use in thermostat
climate:
- platform: dual_smart_thermostat
name: My Thermostat
heater: switch.study_heater
fan: fan.hvac_fan # Percentage-based speed control
target_sensor: sensor.study_temperatureFor fans controlled via Broadlink, IR blaster, or RF remote:
# Helpers to track state
input_boolean:
fan_state:
name: Fan State
input_select:
hvac_fan_speed:
name: HVAC Fan Speed
options:
- "low"
- "medium"
- "high"
initial: "low"
# Template fan for IR/RF control
fan:
- platform: template
fans:
hvac_fan:
friendly_name: "HVAC Fan"
value_template: "{{ is_state('input_boolean.fan_state', 'on') }}"
preset_mode_template: "{{ states('input_select.hvac_fan_speed') }}"
preset_modes:
- "low"
- "medium"
- "high"
turn_on:
- service: input_boolean.turn_on
target:
entity_id: input_boolean.fan_state
- service: remote.send_command
target:
entity_id: remote.living_room
data:
command: "fan_on"
turn_off:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.fan_state
- service: remote.send_command
target:
entity_id: remote.living_room
data:
command: "fan_off"
set_preset_mode:
- service: input_select.select_option
target:
entity_id: input_select.hvac_fan_speed
data:
option: "{{ preset_mode }}"
- service: remote.send_command
target:
entity_id: remote.living_room
data:
command: "fan_{{ preset_mode }}"
# Use in thermostat
climate:
- platform: dual_smart_thermostat
name: My Thermostat
heater: switch.study_heater
fan: fan.hvac_fan # IR/RF controlled with speed support
target_sensor: sensor.study_temperatureBenefits of Template Fans:
- Use existing switch hardware without buying new devices
- Add speed control functionality to any fan
- Automatic detection by the thermostat
- Full UI integration with speed controls
- Works with IR/RF remotes, relays, or any controllable device
Reference: Home Assistant Template Fan Documentation
Some AC systems have independent fan controls to cycle the house air for filtering or humidity control, without using the heating or cooling elements. Central AC systems require the thermostat to turn on both the AC wire ("Y" wire) and the air-handler/fan wire ("G" wire) to activate the AC
This feature lets you do just that.
To use this feature, you need to set the heater entity, the ac_mode, and the fan) entity and the fan_on_with_ac to true.
heater: switch.study_heater
ac_mode: true
fan: switch.study_fan
fan_on_with_ac: trueIf only the cooler entity is set, the thermostat works only in cooling mode.
If the dryer entity is set, the thermostat can switch to dry mode. The dryer will turn on when the humidity is above the target humidity and the moist_tolerance is not reached. If the humidity is above the target humidity and the moist_tolerance is reached, the dryer will stop.
heater: switch.study_heater
target_sensor: sensor.study_temperature
ac_mode: true
dryer: switch.study_dryer
humidity_sensor: sensor.study_humidity
moist_tolerance: 5
dry_tolerance: 5heater: switch.study_heater
cooler: switch.study_cooler
target_sensor: sensor.study_temperature
dryer: switch.study_dryer
humidity_sensor: sensor.study_humidity
moist_tolerance: 5
dry_tolerance: 5This setup allows you to use a single switch for both heating and cooling. To enable this mode, you define only a single switch for the heater and set your heat pump's current state (heating or cooling) as for the heat_pump_cooling attribute. This must be an entity ID of a sensor with a state of on or off.
The entity can be a Boolean input for manual control or an entity provided by the heat pump.
heater: switch.study_heat_pump
target_sensor: sensor.study_temperature
heat_pump_cooling: sensor.study_heat_pump_stateheater: switch.study_heat_pump
target_sensor: sensor.study_temperature
heat_pump_cooling: sensor.study_heat_pump_state
heat_cool_mode: trueheating (heat_pump_cooling: false):
- heat/cool
- heat
- off
cooling (heat_pump_cooling: true):
- heat/cool
- cool
- off
heater: switch.study_heat_pump
target_sensor: sensor.study_temperature
heat_pump_cooling: sensor.study_heat_pump_state
heat_cool_mode: false # <-- or not setheating (heat_pump_cooling: false):
- heat
- off
cooling (heat_pump_cooling: true):
- cool
- off
The dual_smart_thermostat can turn off heating or cooling when a window or door is opened and turn it back on when the door or window is closed, saving energy.
The openings configuration variable accepts a list of opening entities and opening objects.
An opening entity is a sensor that can be in two states: on or off. If the state is on, the opening is considered open; if the state is off, the opening is considered closed.
The opening object can contain a timeout and a closing_timeout property that defines the time for which the opening is still considered closed or open, even if the state is on or off. This is useful if you want to ignore windows that are only open or closed for a short time.
The openings_scope configuration variable defines the scope of the openings. If set to all or not defined, any open openings will turn off the current HVAC device, and it will be in the idle state. If set, only devices that are operating in the defined HVAC modes will be turned off. For example, if set to heat, only the heater will be turned off if any of the openings are open.
openings_scope: [heat, cool, heat_cool, fan_only, dry]openings_scope:
- heat
- cool# Example configuration.yaml entry
climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_heater
cooler: switch.study_cooler
openings:
- binary_sensor.window1
- entity_id: binary_sensor.window2
timeout: 00:00:30
- entity_id: binary_sensor.window3
timeout: 00:00:30
closing_timeout: 00:00:15
openings_scope: [heat, cool]
target_sensor: sensor.study_temperatureThe dual_smart_thermostat can control the floor heating temperature. The thermostat can turn off if the floor heating reaches the maximum allowed temperature you define in order to protect the floor from overheating and damage.
These limits also can be set in presets.
The dual_smart_thermostat can turn off if the floor heating reaches the maximum allowed temperature you define in order to protect the floor from overheating and damage.
There is a default value of 28 degrees Celsius as per inustry recommendations.
To enable this protection you need to set two variables:
floor_sensor: sensor.floor_temp
max_floor_temp: 28You can also set the max_floor_temp in the presets configuration. This will allow you to set different maximum floor temperatures for different presets.
floor_sensor: sensor.floor_temp
max_floor_temp: 28
preset_name:
max_floor_temp: 25The dual_smart_thermostat can turn on if the floor temperature reaches the minimum required temperature you define in order to protect the floor from freezing or to keep it on a comfortable temperature.
floor_sensor: sensor.floor_temp
min_floor_temp: 5You can also set the min_floor_temp in the presets configuration. This will allow you to set different minimum floor temperatures for different presets.
floor_sensor: sensor.floor_temp
min_floor_temp: 5
preset_name:
min_floor_temp: 8# Example configuration.yaml entry
climate:
- platform: dual_smart_thermostat
name: Study
unique_id: study
heater: switch.study_heater
cooler: switch.study_cooler
target_sensor: sensor.study_temperature
floor_sensor: sensor.floor_temp
max_floor_temp: 28
min_floor_temp: 5Currently supported presets are:
To set presets you need to add entries for them in the configuration file like this:
You have 6 options here:
- Set the
temperaturefor heat, cool or fan-only mode - Set the
target_temp_lowandtarget_temp_highfor heat_cool mode. Iftemperatureis not set buttarget_temp_lowandtarget_temp_highare set, thetemperaturewill be picked based on hvac mode. For heat mode it will betarget_temp_lowand for cool, fan_only mode it will betarget_temp_high - Set the
humidityfor dry mode - Set
min_floor_tempfor floor heating temperature control - Set
max_floor_tempfor floor heating temperature control - Set all above
preset_name:
temperature: 13
humidity: 50 # <-- only if dry mode configured
target_temp_low: 12
target_temp_high: 14
min_floor_temp: 5
max_floor_temp: 28State attribute: hvac_action_reason
The dual_smart_thermostat will set the hvac_action attribute to heating, cooling, idle or off based on the current state of the thermostat. The hvac_action attribute is used to indicate the current action of the thermostat. The dual_smart_thermostat will also set the hvac_action_reason attribute based on the current state of the thermostat. The hvac_action_reason attribute is used to indicate the reason for the current action of the thermostat.
The hvac_action_reason attribute is grouped by internal and external values.
The internal values can be set by the component only and the external values can be set by the user or automations.
| Value | Description |
|---|---|
none |
No action reason |
target_temp_not_reached |
The target temperature has not been reached |
target_temp_not_reached_with_fan |
The target temperature has not been reached trying it with a fan |
target_temp_reached |
The target temperature has been reached |
target_humidity_reached |
The target humidity has been reached |
target_humidity_not_reached |
The target humidity has not been reached |
misconfiguration |
The thermostat is misconfigured |
opening |
The thermostat is idle because an opening is open |
limit |
The thermostat is idle because the floor temperature is at the limit |
overheat |
The thermostat is idle because the floor temperature is too high |
temperature_sensor_stalled |
The thermostat is idle because the temperature sensor is not provided data for the defined time that could indicate a malfunctioning sensor |
humidity_sensor_sstalled |
The thermostat is idle because the temperature sensor is not provided data for the defined time that could indicate a malfunctioning sensor |
| Value | Description |
|---|---|
none |
No action reason |
presence |
the last HVAc action was triggered by presence |
schedule |
the last HVAc action was triggered by schedule |
emergency |
the last HVAc action was triggered by emergency |
malfunction |
the last HVAc action was triggered by malfunction |
dial_smart_thermostat.set_hvac_action_reason is exposed for automations to set the hvac_action_reason attribute. The service accepts the following parameters:
| Parameter | Description | Type | Required |
|---|---|---|---|
| entity_id | The entity id of the thermostat | string | yes |
| hvac_action_reason | The reason for the current action of the thermostat | HVACActionReasonExternal | yes |
(required) (string) Name of thermostat
default: Dual Smart
(optional) (string) the unique id for the thermostat. It allows you to customize it in the UI and to assign the component to an area.
_default: none
(required) (string) "entity_id for heater switch, must be a toggle device. Becomes air conditioning switch when ac_mode is set to true"
(optional, required for two stage heating) (string) "entity_id for secondary heater switch, must be a toggle device.
(optional, required for two stage heating) (time, integer) Set a minimum amount of time that the switch specified in the heater option must be in its ON state before secondary heater devices needs to be turned on.
(optional, (bool) If set true the secondary (aux) heater will be turned on together with the primary heater.
(optional) (string) "entity_id for cooler switch, must be a toggle device."
(optional) (bool) If set to true the heater entity will be treated as a fan only device.
(optional) (string) "entity_id for fan switch, must be a toggle device."
(optional) (float) Temperature range above hot_tolerance where the fan is used instead of the AC. This creates an intermediate zone where the fan attempts to cool before engaging the AC.
Example: With target temperature 25°C, hot_tolerance 1°C, and fan_hot_tolerance 0.5°C:
- At 26°C (target + hot_tolerance): Fan turns on
- At 26.5°C (target + hot_tolerance + fan_hot_tolerance): AC turns on (fan turns off)
This feature helps save energy by using the fan for minor temperature increases before engaging the more power-intensive AC.
default: 0.5
requires: fan
(optional) (string) entity_id for an input_boolean or binary_sensor that dynamically enables/disables the fan_hot_tolerance feature.
- When the toggle entity is
on(or not configured): The fan_hot_tolerance feature is active - When the toggle entity is
off: The AC is used immediately whenhot_toleranceis exceeded (bypasses fan zone)
Useful for automations that disable fan-first behavior during extreme heat, high humidity, or other conditions where immediate AC is preferred.
default: Feature enabled (behaves as if toggle is on)
requires: fan
(optional) (boolean) If set to true the fan will be turned on together with the AC. This is useful for central AC systems that require the fan to be turned on together with the AC.
requires: fan
(optional) (boolean) "If set to true the fan will be turned on only if the outside temperature is colder than the inside temperature and the fan_hot_tolerance is not reached. If the outside temperature is colder than the inside temperature and the fan_hot_tolerance is reached the AC will turn on."
requires: fan , sensor_outside
(optional) (string) "entity_id for dryer switch, must be a toggle device."
(optional) (float) Set a minimum amount of difference between the humidity read by the sensor specified in the humidity_sensor option and the target humidity that must change prior to being switched on. For example, if the target humidity is 50 and the tolerance is 5 the dryer will start when the sensor equals or goes below 45.
requires: dryer, humidity_sensor
(optional) (float) Set a minimum amount of difference between the humidity read by the sensor specified in the humidity_sensor option and the target humidity that must change prior to being switched off. For example, if the target humidity is 50 and the tolerance is 5 the dryer will stop when the sensor equals or goes above 55.
requires: dryer, humidity_sensor
(optional) (string) "entity_id for a humidity sensor, humidity_sensor.state must be humidity."
(required) (string) "entity_id for a temperature sensor, target_sensor.state must be temperature."
(optional) (timedelta) Set a delay for the target sensor to be considered not stalled. If the sensor is not available for the specified time or doesn't get updated the thermostat will be turned off.
requires: target_sensor and/or huidity_sensor
(optional) (string) "entity_id for the floor temperature sensor, floor_sensor.state must be temperature."
(optional) (string) "entity_id for the outside temperature sensor, oustide_sensor.state must be temperature."
(optional) (list) "list of opening entity_id's and/or objects for detecting open windows or doors that will idle the thermostat until any of them are open. Note: if min_floor_temp is set and the floor temperature is below the minimum temperature, the thermostat will not idle even if any of the openings are open."
entity_id: <value> The entity id of the opening bstate sensor (string)
timeout: <value> The time for which the opening is still considered closed even if the state of the sensor is on (timedelta)
closing_timeout: <value> The time for which the opening is still considered open even if the state of the sensor is off (timedelta)
(optional) (array[string]) "The scope of the openings. If set to [all] or not defined, any open openings will turn off the current hvac device and it will be in the idle state. If set, only devices that operating in the defined HVAC modes will be turned off. For example, if set to heat only the heater will be turned off if any of the openings are open."
default: all
options:
- all
- heat
- cool
- heat_cool
- fan_only
(optional) (string) "entity_id for the heat pump cooling state sensor, heat_pump_cooling.state must be on or off."
enables heat pump mode
(optional) (float)
default: 7
(optional) (float)
default: 35
(optional) (float)
default: 28
(optional) (float)
(optional) (float) Set initial target temperature. If this variable is not set, it will retain the target temperature set before restart if available.
(optional) (float) Set initial target low temperature. If this variable is not set, it will retain the target low temperature set before restart if available.
(optional) (float) Set initial target high temperature. If this variable is not set, it will retain the target high temperature set before restart if available.
(optional) (boolean) Set the switch specified in the heater option to be treated as a cooling device instead of a heating device. This parameter will be ignored if cooler entity is defined.
default: false
(optional) (boolean) If variable target_temp_low and target_temp_high are not set, this parameter must be set to true to enable the heat_cool mode.
default: false
(optional) (time, integer) Set a minimum amount of time that the switch specified in the heater and/or cooler option must be in its current state prior to being switched either off or on. This option will be ignored if the keep_alive option is set.
(optional) (float) Set a minimum amount of difference between the temperature read by the sensor specified in the target_sensor option and the target temperature that must change prior to being switched on. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will start when the sensor equals or goes below 24.5.
default: 0.3
(optional) (float) Set a minimum amount of difference between the temperature read by the sensor specified in the target_sensor option and the target temperature that must change prior to being switched off. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will stop when the sensor equals or goes above 25.5.
default: 0.3
(optional) (float) [Dual-mode systems only] Set a mode-specific tolerance for heating operations. Only available for systems that support both heating and cooling (heater_cooler or heat_pump system types).
When configured, this tolerance is used instead of cold_tolerance when the system is actively heating. This allows you to have different tolerance values for heating vs cooling operations.
Example use case: Tight temperature control during heating (±0.3°C) while allowing looser control during cooling (±2.0°C) for energy savings.
Priority: If set, heat_tolerance takes priority over cold_tolerance for heating operations.
Availability:
- ✅ Available:
heater_cooler,heat_pump(dual-mode systems) - ❌ Not available:
simple_heater,ac_only(single-mode systems use legacy tolerances)
default: Uses cold_tolerance if not set
(optional) (float) [Dual-mode systems only] Set a mode-specific tolerance for cooling operations. Only available for systems that support both heating and cooling (heater_cooler or heat_pump system types).
When configured, this tolerance is used instead of hot_tolerance when the system is actively cooling. This allows you to have different tolerance values for heating vs cooling operations.
Example use case: Allow wider temperature swings during cooling to reduce energy consumption while maintaining comfort.
Priority: If set, cool_tolerance takes priority over hot_tolerance for cooling operations.
Availability:
- ✅ Available:
heater_cooler,heat_pump(dual-mode systems) - ❌ Not available:
simple_heater,ac_only(single-mode systems use legacy tolerances)
default: Uses hot_tolerance if not set
(optional) (time, integer) Set a keep-alive interval. If set, the switch specified in the heater and/or cooler option will be triggered every time the interval elapses. Use with heaters and A/C units that shut off if they don't receive a signal from their remote for a while. Use also with switches that might lose state. The keep-alive call is done with the current valid climate integration state (either on or off). When keep_alive is set the min_cycle_duration option will be ignored.
default: 300 seconds (5 minutes)
Note: Some AC units (like certain Hitachi models) beep with each command they receive. If your AC beeps excessively every few minutes, the keep-alive feature may be sending redundant commands. You can disable keep-alive by setting it to 0:
keep_alive: 0 # Disables keep-alive to prevent beeping(optional) (string) Set the initial HVAC mode. Valid values are off, heat, cool or heat_cool. Value has to be double quoted. If this parameter is not set, it is preferable to set a keep_alive value. This is helpful to align any discrepancies between dual_smart_thermostat heater and cooler state.
NOTE! If this is set, the saved state will not be restored after HA restarts.
(optional) (list) Set the temperatures used by preset_mode: away. If this is not specified, the preset mode feature will not be available.
Possible values are:
temperature: <value> The preset temperature to use in heat or cool mode (float)
target_temp_low: <value> The preset low temperature to use in heat_cool mode (float)
target_temp_high: <value> The preset high temperature to use in heat_cool mode (float)
(optional) (list) Set the temperature used by preset_mode: eco. If this is not specified, the preset mode feature will not be available.
Possible values are:
temperature: <value> The preset temperature to use in heat or cool mode (float)
target_temp_low: <value> The preset low temperature to use in heat_cool mode (float)
target_temp_high: <value> The preset high temperature to use in heat_cool mode (float)
(optional) (list) Set the temperature used by preset_mode: home. If this is not specified, the preset mode feature will not be available.
Possible values are:
temperature: <value> The preset temperature to use in heat or cool mode (float)
target_temp_low: <value> The preset low temperature to use in heat_cool mode (float)
target_temp_high: <value> The preset high temperature to use in heat_cool mode (float)
(optional) (list) Set the temperature used by preset_mode: comfort. If this is not specified, the preset mode feature will not be available.
Possible values are:
temperature: <value> The preset temperature to use in heat or cool mode (float)
target_temp_low: <value> The preset low temperature to use in heat_cool mode (float)
target_temp_high: <value> The preset high temperature to use in heat_cool mode (float)
(optional) (list) Set the temperature used by preset_mode: sleep. If this is not specified, the preset mode feature will not be available.
Possible values are:
temperature: <value> The preset temperature to use in heat or cool mode (float)
target_temp_low: <value> The preset low temperature to use in heat_cool mode (float)
target_temp_high: <value> The preset high temperature to use in heat_cool mode (float)
(optional) (list) Set the temperature used by preset_mode: Anti Freeze. If this is not specified, the preset mode feature will not be available.
Possible values are:
temperature: <value> The preset temperature to use in heat or cool mode (float)
target_temp_low: <value> The preset low temperature to use in heat_cool mode (float)
target_temp_high: <value> The preset high temperature to use in heat_cool mode (float)
(optional) (list) Set the temperature used by preset_mode: Activity. If this is not specified, the preset mode feature will not be available.
Possible values are:
temperature: <value> The preset temperature to use in heat or cool mode (float)
target_temp_low: <value> The preset low temperature to use in heat_cool mode (float)
target_temp_high: <value> The preset high temperature to use in heat_cool mode (float)
(optional) (list) Set the temperature used by preset_mode: Boost. If this is not specified, the preset mode feature will not be available.
This preset mode only works in heat or cool mode because boosting temperatures on heat_cools
mode will require setting target_temp_low higher than target_temp_high and vice versa.
Possible values are:
temperature: <value> The preset temperature to use in heat or cool mode (float)
(optional) (float) The desired precision for this device. Can be used to match your actual thermostat's precision. Supported values are 0.1, 0.5 and 1.0.
default: 0.5 for Celsius and 1.0 for Fahrenheit.
(optional) (float) The desired step size for setting the target temperature. Supported values are 0.1, 0.5 and 1.0.
default: Value used for precision
Problem: Your air conditioner or heater beeps every few minutes (typically every 5 minutes) even when no temperature changes occur.
Root Cause: The keep_alive feature defaults to 300 seconds (5 minutes) and sends periodic commands to keep devices synchronized. Some HVAC units (like certain Hitachi AC models) beep audibly with each command they receive, including these keep-alive commands.
Solution: Disable the keep-alive feature by setting it to 0 in your configuration:
climate:
- platform: dual_smart_thermostat
name: My Thermostat
heater: switch.my_heater
target_sensor: sensor.my_temperature
keep_alive: 0 # Disables keep-alive to prevent beepingWhen to use keep_alive: The keep-alive feature is useful for:
- HVAC units that turn off automatically if they don't receive commands regularly
- Switches that might lose state over time
- Maintaining synchronization between the thermostat and physical device
If your HVAC device doesn't have these issues, you can safely disable keep-alive.
Related: GitHub Issue #461
Installation is via the Home Assistant Community Store (HACS), which is the best place to get third-party integrations for Home Assistant. Once you have HACS set up, simply search the Integrations section for Dual Smart Thermostat.
climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_heater
target_sensor: sensor.study_temperature
initial_hvac_mode: "heat"For two stage heating both the heater and secondary_heater must be defined. The secondary_heater will be turned on only if the heater is on for the amount of time defined in secondary_heater_timeout.
climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_heater
secondary_heater: switch.study_secondary_heater # <-requred
secondary_heater_timeout: 00:00:30 # <-requred
target_sensor: sensor.study_temperature
initial_hvac_mode: "heat"climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_cooler
ac_mode: true # <-important
target_sensor: sensor.study_temperature
initial_hvac_mode: "cool"climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_heater
target_sensor: sensor.study_temperature
initial_hvac_mode: "heat"
floor_sensor: sensor.floor_temp # <-required
max_floor_temp: 28 # <-required
min_floor_temp: 20 # <-requiredThis mode is used when you want (and can) control both the heater and the cooler. In this mode the target_temp_low and target_temp_high must be set.
In this mode you can switch between heating and cooling by setting the hvac_mode to heat or cool or heat_cool.
climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_heater # <-required
cooler: switch.study_cooler # <-required
target_sensor: sensor.study_temperature
heat_cool_mode: true # <-required
initial_hvac_mode: "heat_cool"climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_heater
cooler: switch.study_cooler
target_sensor: sensor.study_temperature
openings: # <-required
- binary_sensor.window1
- binary_sensor.window2
- entity_id: binary_sensor.window3
timeout: 00:00:30 # <-optionalThe dual_smart_thermostat supports multiple tolerance configurations to prevent the heater or cooler from switching on and off too frequently.
The basic tolerance variables cold_tolerance and hot_tolerance work for all system types. These variables are used to prevent the heater or cooler from switching on and off too frequently. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will start when the sensor equals or goes below 24.5. The heater will stop when the sensor equals or goes above 25.5. This prevents the heater from switching on and off too frequently when the temperature is close to the target temperature.
If the thermostat is set to heat_cool mode the tolerance will work in the same way for both the heater and the cooler.
For systems that support both heating and cooling (heater_cooler or heat_pump system types), you can optionally configure separate tolerances for heating vs cooling operations using heat_tolerance and cool_tolerance.
Tolerance Selection Priority:
- Mode-specific tolerance (if configured):
heat_tolerancefor heating,cool_tolerancefor cooling - Legacy tolerance:
cold_tolerance/hot_tolerance - Default: 0.3°C/°F
Example: Tight heating control with loose cooling for energy savings:
climate:
- platform: dual_smart_thermostat
name: Living Room
heater: switch.heater
cooler: switch.ac_unit
target_sensor: sensor.temperature
heat_tolerance: 0.3 # Tight control during heating (±0.3°C)
cool_tolerance: 2.0 # Loose control during cooling (±2.0°C) - saves energySystem Type Availability:
- ✅
heater_cooler- Full support for heat_tolerance and cool_tolerance - ✅
heat_pump- Full support for heat_tolerance and cool_tolerance - ❌
simple_heater- Use cold_tolerance only (heating-only system) - ❌
ac_only- Use hot_tolerance only (cooling-only system)
climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_heater
cooler: switch.study_cooler
target_sensor: sensor.study_temperature
cold_tolerance: 0.3
hot_tolerance: 0climate:
- platform: dual_smart_thermostat
name: Study
heater: switch.study_heater
cooler: switch.study_cooler
secondary_heater: switch.study_secondary_heater
secondary_heater_timeout: 00:00:30
target_sensor: sensor.study_temperature
floor_sensor: sensor.floor_temp
max_floor_temp: 28
openings:
- binary_sensor.window1
- binary_sensor.window2
- entity_id: binary_sensor.window3
timeout: 00:00:30
min_temp: 10
max_temp: 28
ac_mode: false
target_temp: 17
target_temp_high: 26
target_temp_low: 23
cold_tolerance: 0.3
hot_tolerance: 0
min_cycle_duration:
minutes: 5
keep_alive:
minutes: 3
initial_hvac_mode: "off" # hvac mode will reset to this value after restart
away: # this preset will be available for all hvac modes
temperature: 13
target_temp_low: 12
target_temp_high: 14
home: # this preset will be available only for heat or cool hvac mode
temperature: 21
precision: 0.1
target_temp_step: 0.5I am happy to help the Home Assistant community but I do it in my free time at the cost of spending less time with my family. Feel free to motivate me and appreciate my sacrifice by donating:
The Dual Smart Thermostat supports two development workflows: Docker-based and VS Code DevContainer. Both approaches provide consistent, isolated development environments with Home Assistant 2025.1.0+.
📚 Comprehensive Docker Development Guide - Complete documentation for Docker-based development, testing with multiple HA versions, and CI/CD integration.
📋 Development Guidelines - Detailed coding standards, architecture overview, and contribution requirements.
Option 1: Docker Workflow (Recommended for CI/CD and version testing)
# Build development environment with HA 2025.1.0
docker-compose build dev
# Run all tests
./scripts/docker-test
# Run linting checks
./scripts/docker-lint
# Open interactive shell
./scripts/docker-shell
# Test with different HA version
HA_VERSION=2025.2.0 docker-compose build devOption 2: VS Code DevContainer (Recommended for interactive development)
Open the project in VS Code and select "Reopen in Container" when prompted. The DevContainer will automatically set up the development environment.
Run all tests:
pytest
# or with Docker:
./scripts/docker-testRun specific test file:
pytest tests/test_heater_mode.py
# or with Docker:
./scripts/docker-test tests/test_heater_mode.pyRun specific test function:
pytest tests/test_heater_mode.py::test_heater_mode_onRun tests with pattern matching:
pytest -k "heater"Run with verbose output and debug logging:
pytest -v --log-cli-level=DEBUGRun with coverage report:
pytest --cov --cov-report=htmlRun config flow tests only:
pytest tests/config_flow/All code must pass linting checks before committing. The following tools are required:
# Check all linting rules
isort . --check-only --diff # Import sorting
black --check . # Code formatting
flake8 . # Style/linting
codespell # Spell checking
ruff check . # Modern Python linter
# Auto-fix issues
isort . # Fix imports
black . # Fix formatting
ruff check . --fix # Fix ruff issues
# Or use Docker to run all checks
./scripts/docker-lint # Check all
./scripts/docker-lint --fix # Auto-fixPre-commit hooks (automatically runs linting on commit):
pre-commit install # Install hooks
pre-commit run --all-files # Run manuallyThe Docker workflow makes it easy to test with different HA versions:
# Test with HA 2025.1.0 (default)
docker-compose build dev
./scripts/docker-test
# Test with HA 2025.2.0
HA_VERSION=2025.2.0 docker-compose build dev
./scripts/docker-test
# Test with latest HA
HA_VERSION=latest docker-compose build dev
./scripts/docker-test- README-DOCKER.md - Docker workflow, troubleshooting, and advanced usage
- CLAUDE.md - Architecture, development rules, and testing strategy
- Examples Directory - Ready-to-use configuration examples
- GitHub Issues - Bug reports and feature requests
- Home Assistant Developer Docs - Official HA development documentation
Before submitting a pull request:
- ✅ All tests pass:
pytestor./scripts/docker-test - ✅ All linting passes:
./scripts/docker-lintor run linters individually - ✅ Add tests for new features
- ✅ Update documentation if needed
- ✅ Follow the patterns in CLAUDE.md
Configuration Flow Changes: If you add or modify configuration options, you must integrate them into the appropriate configuration flows (config, reconfigure, or options). See CLAUDE.md Configuration Flow Integration for detailed requirements.














