Virtual Thermostat made from Nest and Z-Wave Temperature Sensor

I feel that it is important that I describe my setup, what works, how it works, and what I’m trying to do now before I get into the configuration.

I have a nest thermostat on the first floor and a Z-Wave multisensor on the second floor. As of now, I have a handful of automations that allow me to manipulate the nest thermostat to keep the second floor at a constant temperature. This works great and has for years.

How this works…When I select Home Assistant to do temperature control off of the second floor, it will artificially bump up the target temperature of the nest by the difference between the first floor and the second floor until the second floor hits the desired temperature.

Example:

  • Mode: Heat
  • Second Floor Target Temp = 72
  • Nest Target Temp = 72
  • Second Floor Current Temp = 70
  • First Floor Current Temp = 72

Action: Increase Nest Target Temp to 74 and stay there until Second Floor Current Temp = 72. Once this is completed, Set Nest Target Temp back to 72.

Once again, this works great! I will share my current solution at the end as others might want to use this.

But now onto the problem!
I am trying to convert these automations into a generic thermostat and a switch. This will allow me to use the lovelace thermostat card and make it look more “real”.

The current idea is to use 2 generic thermostats (heat/cool) and 2 switches (heat/cool). I’m sure I’ll be able to refine this to 1 switch in the future but for simplicity, I’m avoiding that for now.

What happens… The generic thermostats seem to trigger the turn_on state about 80% of the time. I’m assuming things are getting into weird states and that is why it isn’t 100%. The real problem seems to be involved with the turn_off state within the switches and I’m at a loss as to why they don’t get triggered. Maybe I’m using the value_template field of the switches incorrectly or some of my assumptions are incorrect.

Here is the code for the important pieces. I have not included some smaller pieces as I don’t think they’re important and I’m pretty confident they work. I will post them if others think it will help.

Old Code (Automation Method):

- alias: Local Temperature Control Mode
  initial_state: on
  trigger:
    platform: state
    entity_id: input_select.temperature_control_mode
    to: 'Main Floor'

  action:
    - service: input_number.set_value
      data_template:
        entity_id: input_number.previous_remote_temp
        value: '{{ states.input_number.target_temperature.state }}'

    - service: input_number.set_value
      data_template:
        entity_id: input_number.target_temperature
        value: '{{ states.input_number.previous_local_temp.state }}'

    - service: automation.turn_off
      entity_id: automation.reach_desired_remote_temperature

    - service: automation.turn_on
      entity_id: automation.set_main_floor_target_temperature

    - service: automation.turn_on
      entity_id: automation.set_nest_target_temperature

    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: '{{ states.input_number.target_temperature.state }}'

- alias: Remote Temperature Control Mode
  initial_state: on
  trigger:
    platform: state
    entity_id: input_select.temperature_control_mode
    to: 'Second Floor'

  action:
    - service: input_number.set_value
      data_template:
        entity_id: input_number.previous_local_temp
        value: '{{ states.input_number.target_temperature.state }}'

    - service: input_number.set_value
      data_template:
        entity_id: input_number.target_temperature

        value: '{{ states.input_number.previous_remote_temp.state }}'

    - service: automation.turn_off
      entity_id: automation.set_main_floor_target_temperature

    - service: automation.turn_off
      entity_id: automation.set_nest_target_temperature

    - service: automation.turn_on
      entity_id: automation.reach_desired_remote_temperature

    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: '{{ states.input_number.target_temperature.state }}'

- alias: Reach Desired Remote Temperature
  initial_state: off
  trigger:
    platform: state
    entity_id: sensor.baby_room_temperature

  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_select.temperature_control_mode
        state: 'Second Floor'

      - condition: template
        value_template: '{{ states.sensor.dining_room_thermostat_hvac_state.state == "off" }}'

      - condition: template
        value_template: >
          {% if states.sensor.dining_room_thermostat_operation_mode.state == 'heat' %}
              {{ (states.sensor.baby_room_temperature.state | float + 2) <= (states.input_number.target_temperature.state | float)}}
          {% elif states.sensor.dining_room_thermostat_operation_mode.state == 'cool' %}
              {{ (states.sensor.baby_room_temperature.state | float - 2) >= (states.input_number.target_temperature.state | float)}}
          {% else %}
              False
          {% endif %}

  action:
    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: >
              {% if float(states.sensor.baby_room_temperature.state) > float(states.sensor.dining_room_thermostat_temperature.state) %}
                 {{ float(states.sensor.dining_room_thermostat_temperature.state) - ((float(states.sensor.baby_room_temperature.state) - float(states.sensor.dining_room_thermostat_temperature.state)) | abs) }}
              {% elif float(states.sensor.baby_room_temperature.state) < float(states.sensor.dining_room_thermostat_temperature.state) %}
                 {{ float(states.sensor.dining_room_thermostat_temperature.state) + ((float(states.sensor.baby_room_temperature.state) - float(states.sensor.dining_room_thermostat_temperature.state)) | abs) }}
              {% endif %}

    - service: homeassistant.turn_on
      entity_id: automation.desired_remote_temperature_achieved

    - service: homeassistant.turn_off
      entity_id: automation.reach_desired_remote_temperature

- alias: Desired Remote Temperature Achieved
  initial_state: off
  trigger:
    platform: state
    entity_id: sensor.baby_room_temperature

  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_select.temperature_control_mode
        state: 'Second Floor'

      - condition: template
        value_template: '{{ states.sensor.dining_room_thermostat_hvac_state.state != "off" }}'

      - condition: template
        value_template: >
          {% if states.sensor.dining_room_thermostat_operation_mode.state == 'heat' %}
              {{ (states.sensor.baby_room_temperature.state | float) >= (states.input_number.target_temperature.state | float) }}
          {% elif states.sensor.dining_room_thermostat_operation_mode.state == 'cool' %}
              {{ (states.sensor.baby_room_temperature.state | float) <= (states.input_number.target_temperature.state | float) }}
          {% else %}
              False
          {% endif %}

  action:
    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: '{{ states.input_number.previous_local_temp.state }}'

    - service: homeassistant.turn_on
      entity_id: automation.reach_desired_remote_temperature

    - service: homeassistant.turn_off
      entity_id: automation.desired_remote_temperature_achieved

- alias: Set Nest Target Temperature
  initial_state: on
  trigger:
    platform: state
    entity_id: input_number.target_temperature

  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_select.temperature_control_mode
        state: 'Main Floor'

      - condition: template
        value_template: '{{ states.sensor.dining_room_thermostat_target.state != states.input_number.target_temperature.state }}'

  action:
    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: '{{ states.input_number.target_temperature.state }}'

- alias: Set Main Floor Target Temperature
  initial_state: on
  trigger:
    platform: state
    entity_id: sensor.dining_room_thermostat_target

  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_select.temperature_control_mode
        state: 'Main Floor'

      - condition: template
        value_template: '{{ states.sensor.dining_room_thermostat_target.state != states.input_number.target_temperature.state }}'

  action:
    - service: input_number.set_value
      data_template:
        entity_id: input_number.target_temperature
        value: '{{ states.sensor.dining_room_thermostat_target.state }}'

- alias: Smart Temperature Control
  initial_state: on
  trigger:
    platform: state
    entity_id: device_tracker.baby_camera

  action:
    - service: input_select.select_option
      data_template:
        entity_id: input_select.temperature_control_mode
        option: >
          {% if states.device_tracker.baby_camera.state == 'not_home' %}
              Main Floor
          {% elif states.device_tracker.baby_camera.state == 'home' %}
              Second Floor
          {% else %}
              Main Floor
          {% endif %}

Now the new method…(Once again, only showing the relevant big pieces. I can show more if needed)

climate:
  - platform: generic_thermostat
    name: Upstairs Heat
    initial_operation_mode: "off"
    heater: switch.upstairs_heat
    ac_mode: false
    target_sensor: sensor.baby_room_temperature
    target_temp: 71
    cold_tolerance: 2
    precision: 0.1
    max_temp: 90
    min_temp: 40

  - platform: generic_thermostat
    name: Upstairs Cool
    initial_operation_mode: "off"
    heater: switch.upstairs_cool
    ac_mode: true
    target_sensor: sensor.baby_room_temperature
    target_temp: 74
    hot_tolerance: 2
    precision: 0.1
    max_temp: 90
    min_temp: 40

- platform: template
  switches:
    upstairs_heat:
      friendly_name: "Upstairs Heat"
      value_template: "{{ (float(states.climate.upstairs_heat.attributes.current_temperature) <= float(states.climate.upstairs_heat.attributes.temperature - 2) and is_state ('climate.upstairs_heat', 'idle')) or (float(states.climate.upstairs_heat.attributes.current_temperature) <= float(states.climate.upstairs_heat.attributes.temperature) and is_state ('climate.upstairs_heat', 'heat')) }}"
      turn_on:
        - service: climate.set_temperature
          data_template:
            entity_id: climate.dining_room
            temperature: >
              {{ float(states.climate.dining_room.attributes.current_temperature) + ((float(states.climate.upstairs_heat.attributes.current_temperature) - float(states.climate.dining_room.attributes.current_temperature)) | abs) }}
      turn_off:
        service: climate.set_temperature
        data_template:
          entity_id: climate.dining_room
          temperature: '{{ states.input_number.previous_local_temp_heat.state }}'

- platform: template
  switches:
    upstairs_cool:
      friendly_name: "Upstairs Cool"
      value_template: "{{ (float(states.climate.upstairs_cool.attributes.current_temperature) >= float(states.climate.upstairs_cool.attributes.temperature + 2) and is_state ('climate.upstairs_cool', 'idle')) or (float(states.climate.upstairs_cool.attributes.current_temperature) >= float(states.climate.upstairs_cool.attributes.temperature) and is_state ('climate.upstairs_cool', 'cool')) }}"
      turn_on:
        - service: climate.set_temperature
          data_template:
            entity_id: climate.dining_room
            temperature: >
              {{ float(states.climate.dining_room.attributes.current_temperature) - ((float(states.climate.upstairs_heat.attributes.current_temperature) - float(states.climate.dining_room.attributes.current_temperature)) | abs) }}
      turn_off:
        service: climate.set_temperature
        data_template:
          entity_id: climate.dining_room
          temperature: '{{ states.input_number.previous_local_temp_cool.state }}'

Hopefully this is an obvious fix and I’ve just been staring at this problem for too long. :slight_smile:

Thanks in advance for any help solving this! It should be pretty slick once I’m done. :slight_smile:

1 Like

I use an input boolean like second_floor_heat and second_floor_cool. The generic_thermostat turns those on when it wants heat or cooling. All of the input booleans are in a group. Then I have four automations like this (one each for heating on, heating off, cooling on, and cooling off):

 - alias: HVAC Heater Control On
  id: 'hvac_heater_control_on'
  trigger:
    - platform: state
      entity_id: sensor.hvac_temperature
    - platform: state
      entity_id: group.house_heaters
  condition:
    - condition: state
      entity_id: group.house_heaters
      state: 'on'
    - condition: template
      value_template: '{{ is_state_attr("climate.hvac_heat","operation_mode","heat") }}'
  action:
    - service: climate.set_temperature
      data_template:
        entity_id: climate.hvac_heat
        temperature: '{{ states.sensor.hvac_temperature.state | float + 2 }}'

Thanks for the idea! Looks like using a switch was a bit convoluted.
Using a boolean input in conjunction with a handful of automations for the heater in the generic thermostat is a better approach.

I haven’t had a chance to test it but the definition of all of this seems much more straight forward.

I’ll provide an update later tonight or tomorrow hopefully :slight_smile:

Did some quick tests with notifications and it’s definitely responsive in the way I expect. Tonight I’ll test with real thermostat interaction and post results.

If all goes well, I’ll share my config if anyone else is looking to do a similar configuration (add remote sensors to nest that don’t suck).

This idea worked great! It’s running super smooth now. Thanks a bunch!

For those that might be interested in what I did…the relevant code is below.

At some point I plan to consolidate these automations with more robust templating but for now…this will do. :slight_smile:

climate:
  - platform: generic_thermostat
    name: Upstairs Heat
    initial_operation_mode: "off"
    heater: input_boolean.upstairs_heat
    ac_mode: false
    target_sensor: sensor.baby_room_temperature
    target_temp: 71
    cold_tolerance: 2
    precision: 0.1
    max_temp: 90
    min_temp: 40

  - platform: generic_thermostat
    name: Upstairs Cool
    initial_operation_mode: "off"
    heater: input_boolean.upstairs_cool
    ac_mode: true
    target_sensor: sensor.baby_room_temperature
    target_temp: 74
    hot_tolerance: 2
    precision: 0.1
    max_temp: 90
    min_temp: 40
input_boolean:
  upstairs_heat:
    name: Upstairs Heater State  
    initial: off

  upstairs_cool:
    name: Upstairs Air Conditioner State  
    initial: off

input_number:
  previous_local_temp_heat:
    name: Previous Local Heat Temperature
    initial: 71
    min: 0
    max: 100
    step: 1
    mode: box
    icon: mdi:gauge

  previous_local_temp_cool:
    name: Previous Local Cool Temperature
    initial: 75
    min: 0
    max: 100
    step: 1
    mode: box
    icon: mdi:gauge

- alias: Upstairs Heat On
  initial_state: on
  trigger:
    platform: state
    entity_id: input_boolean.upstairs_heat
    to: 'on'

  action:
    - service: notify.push
      data_template:
        title: "Climate Heat"
        message: "Heat On"

    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: >
              {{ float(states.climate.dining_room.attributes.current_temperature) + ((float(states.climate.upstairs_heat.attributes.current_temperature) - float(states.climate.dining_room.attributes.current_temperature)) | abs) }}

- alias: Upstairs Cool On
  initial_state: on
  trigger:
    platform: state
    entity_id: input_boolean.upstairs_cool
    to: 'on'

  action:
    - service: notify.push
      data_template:
        title: "Climate Cool"
        message: "Cool On"

    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: >
              {{ float(states.climate.dining_room.attributes.current_temperature) - ((float(states.climate.upstairs_heat.attributes.current_temperature) - float(states.climate.dining_room.attributes.current_temperature)) | abs) }}

- alias: Upstairs Heat Off
  initial_state: on
  trigger:
    platform: state
    entity_id: input_boolean.upstairs_heat
    to: 'off'

  action:
    - service: notify.push
      data_template:
        title: "Climate Heat"
        message: "Heat Off"

    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: '{{ states.input_number.previous_local_temp_heat.state }}'

- alias: Upstairs Cool Off
  initial_state: on
  trigger:
    platform: state
    entity_id: input_boolean.upstairs_cool
    to: 'off'

  action:
    - service: notify.push
      data_template:
        title: "Climate Cool"
        message: "Cool Off"

    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: '{{ states.input_number.previous_local_temp_cool.state }}'

- alias: Upstairs Cool Temperature Control Mode 
  initial_state: on
  trigger: 
    platform: state
    entity_id: input_select.temperature_control_mode
    to: 'Second Floor'

  condition:
    condition: template
    value_template: '{{ states.sensor.dining_room_thermostat_operation_mode.state == "cool"  }}'

  action:
    - service: input_number.set_value
      data_template:
        entity_id: input_number.previous_local_temp_cool
        value: '{{ states.sensor.dining_room_thermostat_target.state }}'

    - service: climate.turn_on
      entity_id: climate.upstairs_cool

- alias: Upstairs Heat Temperature Control Mode 
  initial_state: on
  trigger: 
    platform: state
    entity_id: input_select.temperature_control_mode
    to: 'Second Floor'

  condition:
    condition: template
    value_template: '{{ states.sensor.dining_room_thermostat_operation_mode.state == "heat"  }}'

  action:
    - service: input_number.set_value
      data_template:
        entity_id: input_number.previous_local_temp_heat
        value: '{{ states.sensor.dining_room_thermostat_target.state }}'

    - service: climate.turn_on
      entity_id: climate.upstairs_heat

- alias: Local Cool Temperature Control Mode
  initial_state: on
  trigger:
    platform: state
    entity_id: input_select.temperature_control_mode
    to: 'Main Floor'

  condition:
    condition: template
    value_template: '{{ states.sensor.dining_room_thermostat_operation_mode.state == "cool"  }}'

  action:
    - service: climate.turn_off
      entity_id: climate.upstairs_heat

    - service: climate.turn_off
      entity_id: climate.upstairs_heat

    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: '{{ states.input_number.previous_local_temp_cool.state }}'

- alias: Local Heat Temperature Control Mode
  initial_state: on
  trigger:
    platform: state
    entity_id: input_select.temperature_control_mode
    to: 'Main Floor'

  condition:
    condition: template
    value_template: '{{ states.sensor.dining_room_thermostat_operation_mode.state == "heat"  }}'

  action:
    - service: climate.turn_off
      entity_id: climate.upstairs_heat

    - service: climate.turn_off
      entity_id: climate.upstairs_heat

    - service: climate.set_temperature
      data_template:
        entity_id: climate.dining_room
        temperature: '{{ states.input_number.previous_local_temp_heat.state }}'

4 Likes

Hi,
I’m trying to achieve the exact same thing as the OP.
I have a Nest thermostat in my bedroom and I want to make sure that, during day, let’s say between 7am and 11pm, the temperature it looks at is the one from my livingroom temperature sensor (thanks Nest for not making available the extra temperature sensors in Europe!), so I need to bump the Nest temperature with the difference.
It’s just heating, not cooling, so it should be a little bit easier, but still I’m a bit lost since I’m fairly new to HA

Can you please share your final solution and some hints to adapt to my case? Please don’t leave out the small bits if possible :slight_smile:

My entities involved:
Livingroom temperature sensor: sensor.front_balcony_sensor_temperature (I’m still waiting for it to arrive, I’m working in advance using another temp sensor so I can then just change it in the code when it arrives)
Nest Bedroom temperature sensor: sensor.bedroom_thermostat
Nest Bedroom thermostat: climate.bedroom_thermostat
My temperature range is 16.5°C when heating is off, 20.5°C when it’s on.

Thanks a lot!

:wave:

EDIT:
Eventually I did it :slight_smile:

I realized my use case was slightly different since I don’t need to keep the temperature constant but I need to follow my Nest schedule, only looking at a different thermostat based on the time of the day.
I don’t have a cooling system (you don’t really need it in Ireland :slight_smile: ) so I got rid of all the cooling bits. I had to track the temperature changes from Nest schedule to change it accordingly to achieve the desired temperature in the other room.

Thanks to OP for giving me an excellent starting point.

Hi there,

I need to achieve the exact same thing. Having the Nest thermostat in the living room and need to have the girls room at 19 C during the going to bed time (8pm-9.30pm).

As I am pretty newbie at this, would you please recommend an automation platform? Is it running on a Red Node?

It runs on normal automations, no plugin involved… I’ll need some time to extract the actual configuration involved, as I grew bigger over time… I’ll update you as soon as I have it!

1 Like