Climate: Dual Mode Generic Thermostat

I’ve got a Dyson AM09 with both heat and cool (fan) functionality that I control with a Broadlink RM. This is in a nursery, with a temp sensor, that gets fairly chilly in winter months. I wanted to be able to set/control the AM09 via a thermostat entity in both HomeKit and Google Assistant.

Anyway, the standard generic_thermostat component is awesome, but I wanted to include both heat and cool functionality. This component is 99% the same as the main HA generic thermostat, I’ve just added a cooler entity (aped from the discussion here) and removed ac_mode since it isn’t needed when explicit heater and cooler entities exist.

Anyway, component is here: https://github.com/zacs/ha-dualmodegeneric

…and can be configured like so:

climate:
  - platform: dualmode_generic
    name: My Thermostat
    heater: switch.heater
    cooler: switch.fan
    target_sensor: sensor.my_temp_sensor

Hopefully it is of use to more than just myself :slight_smile:.

5 Likes

It’s exactly what I needed, and seems to be working great. I get heat, cool and off. Shouldn’t “auto” mode be available also? Thanks!

Wauw! I was also looking for that! Would you also be willing to implement a boost mode? This would then result in two extra entities (heater_boost and cooler_boost)? Any chance of pushing this to the main repo?

Yes, I think Auto would be fairly easy to implement. I can take a look when I’m not traveling!

Unfortunately I doubt this would make it as an actual component in the main repo — I don’t have enough patience to do things the right way and write tests, etc.

Zacs;
This is great, I’d be very interested in auto mode.
Much thanks!
a

1 Like

brilliant! Thanks…

could you help me understand how I can automatically switch between heat and cool mode based on temp.

I don’t really want a target temp as I am happy for the temp to fluctutate between 15 - 30 degrees. I just dont want the temp to go over or below that range… would this be via a climate set mode automation? Thanks

think I got it…

- id: '1582115889677'
  alias: MJ Thermostat Cool
  description: ''
  trigger:
  - above: '20'
    entity_id: sensor.mj_ave_temp
    platform: numeric_state
  condition: []
  action:
  - data:
      entity_id: climate.mj_thermostat
      hvac_mode: cool
    service: climate.set_hvac_mode
- id: '1582116016514'
  alias: MJ THermostat Heat
  description: ''
  trigger:
  - below: '20'
    entity_id: sensor.mj_ave_temp
    platform: numeric_state
  condition: []
  action:
  - service: climate.set_hvac_mode
    data:
      hvac_mode: heat
      entity_id: climate.mj_thermostat

Nice workaround! In addition to setting the mode, you may want to also set the target. If you switch to cool mode but the actual temp is below the target temp, nothing will turn on.

good shout

- id: '1582115889677'
  alias: MJ Thermostat Cool
  description: ''
  trigger:
  - above: '25'
    entity_id: sensor.mj_ave_temp
    platform: numeric_state
  condition: []
  action:
  - data:
      entity_id: climate.mj_thermostat
      hvac_mode: cool
      temperature: 25
    service: climate.set_hvac_mode
- id: '1582116016514'
  alias: MJ Thermostat Heat
  description: ''
  trigger:
  - below: '16'
    entity_id: sensor.mj_ave_temp
    platform: numeric_state
  condition: []
  action:
  - data:
      entity_id: climate.mj_thermostat
      hvac_mode: heat
      temperature: 15
    service: climate.set_hvac_mode

This component is very handy, but without the Auto mode its like missing the key feature :slight_smile:

Hi @zacs Do you mind sharing your entire config for this to work including how you set up the switches?

I have a split AC/Heater and the indoor unit sits in a small hallway, meaning the internal thermometer gets to hot while heating or to cold while cooling so I cant use that for reliable temperatures around the house. So I would need to use another thermometer sitting in another room to control the Daikin unit.

Thanks and best regards

Sure, mine is pretty straightforward:

climate:
  - platform: dualmode_generic
    name: Nursery
    heater: switch.nursery_heater
    cooler: switch.nursery_fan
    target_sensor: sensor.nursery_temperature

In this case the sensor.nursery_temperature is a z-wave sensor sitting in a corner of our nursery. Each of the switches is actually a switch template that calls some Broadlink RF codes to turn my Dyson device onto heat or cool. I can paste more of the templating if that would be helpful as well.

Thanks for sharing and thanks for the dualmode_generic component.
My “heater” and “cooler” is the same unit and it´s not just on/off to have it working nicely.
It sits in a small entry where the temperature gets to hot and to cold so the internal sensor stops it from heating or cooling when the small entry reaches the set target temperature.
I figured if I raise the target temperature with approx. 6°C on a cloudy day (lets say from 22 to 28) I get a nice 22°C around the rest of the house.
This works until the sun decides to show up and in this case the house gets way to hot so I needed a way to use an external sensor with some logic to control the set temperature.
This package is what I ended up doing and it´s still far from perfect since it does not cover all events that can happen but It works for now.
The climate.daikin is my “fake” heater exposed to google and climate.daikinxrh30 is the real heater.
I share for now if anybody else is interested and I will update if I make some changes in the future.

I know there are probably some amateur errors and this can for sure be done in a much cleaner way but here it comes:

input_boolean:
  daikin_varmepump_varme:
    name: Daikin heat boolean for automations
  daikin_varmepump_kyla:
    name: Daikin cool boolean for automations
input_number:
  daikin_temperature_offset:
    name: Daikin temperature offset while heating or cooling
    min: 1
    max: 10
    step: 1

switch:
  - platform: template
    switches:
      daikin_varmepump_varme:
        value_template: "{{ is_state('input_boolean.daikin_varmepump_varme', 'on') }}"
        turn_on:
          service: input_boolean.turn_on
          data:
            entity_id: input_boolean.daikin_varmepump_varme
        turn_off:
          service: input_boolean.turn_off
          data:
            entity_id: input_boolean.daikin_varmepump_varme
  - platform: template
    switches:
      daikin_varmepump_kyla:
        value_template: "{{ is_state('input_boolean.daikin_varmepump_kyla', 'on') }}"
        turn_on:
          service: input_boolean.turn_on
          data:
            entity_id: input_boolean.daikin_varmepump_kyla
        turn_off:
          service: input_boolean.turn_off
          data:
            entity_id: input_boolean.daikin_varmepump_kyla
climate:
  - platform: dualmode_generic
    heater: switch.daikin_varmepump_varme
    cooler: switch.daikin_varmepump_kyla
    reverse_cycle: false
    name: Daikin
    target_sensor: sensor.hall_temp_degc
    min_temp: 18
    max_temp: 31
    target_temp: 22
    cold_tolerance: 0.3
    hot_tolerance: 0.3
    min_cycle_duration:
      minutes: 5
    precision: 0.1

automation:
  - alias: Turn off daikin
    description: ''
    trigger:
      platform: state
      entity_id: climate.daikin
      to: 'off'
    action:
      - service: climate.turn_off
        data:
          entity_id: climate.daikinxrh30
  - alias: Change daikin to heat
    description: ''
    trigger:
      platform: state
      entity_id: climate.daikin
      to: 'heat'
    action:
     - service: climate.set_hvac_mode
       data:
         entity_id: climate.daikinxrh30
         hvac_mode: heat
  - alias: Change daikin to cool
    description: ''
    trigger:
      platform: state
      entity_id: climate.daikin
      to: 'cool'
    action:
     - service: climate.set_hvac_mode
       data:
         entity_id: climate.daikinxrh30
         hvac_mode: cool
  - alias: Turn on daikin heat
    description: ''
    trigger:
      platform: state
      entity_id: input_boolean.daikin_varmepump_varme
      to: 'on'
      for:
        seconds: 2
    action:
    - data_template:
        temperature: "{{ state_attr('climate.daikin', 'temperature') + states('input_number.daikin_temperature_offset') | float }}"
      entity_id: climate.daikinxrh30
      service: climate.set_temperature
    - service: climate.set_hvac_mode
      data:
        entity_id: climate.daikinxrh30
        hvac_mode: heat
  - alias: Turn off daikin heat
    description: ''
    trigger:
      platform: state
      entity_id: input_boolean.daikin_varmepump_varme
      to: 'off'
    condition:
    - condition: state
      entity_id: climate.daikin
      state: heat
    action:
    - data_template:
        temperature: "{{ state_attr('climate.daikin', 'temperature') | float }}"
      entity_id: climate.daikinxrh30
      service: climate.set_temperature
  - alias: Turn on daikin cool
    description: ''
    trigger:
      platform: state
      entity_id: input_boolean.daikin_varmepump_kyla
      to: 'on'
      for:
        seconds: 2
    action:
    - data_template:
        temperature: "{{ state_attr('climate.daikin', 'temperature') - states('input_number.daikin_temperature_offset') | float }}"
      entity_id: climate.daikinxrh30
      service: climate.set_temperature
    - service: climate.set_hvac_mode
      data:
        entity_id: climate.daikinxrh30
        hvac_mode: cool
  - alias: Turn off daikin cool
    description: ''
    trigger:
      platform: state
      entity_id: input_boolean.daikin_varmepump_kyla
      to: 'off'
    condition:
    - condition: state
      entity_id: climate.daikin
      state: cool
    action:
    - data_template:
        temperature: "{{ state_attr('climate.daikin', 'temperature') | float }}"
      entity_id: climate.daikinxrh30
      service: climate.set_temperature

Nice, changing set point based on the sun is a fun optimization!

Hello @zacs, thanks for the good work, I really like this auto option, it should be part of the generic thermostat I think…
I looked at your code to check if the Away Mode was an option, and I found that:
Removed PRESET_AWAY as I don't need it, may reimplement it later
Still a chance to get this option back one day? That’s the only option I’m missing.
Thanks!

Sorry for not seeing this earlier, but away_temp is definitely supported in the component. If you want to try it out, let me know if you run into issues!

To use it just add away_temp as one of the config variables when you ad the component.

Hello @zacs,

Awesome work, thanks. I already set up to control my server room temperature via a Broadlink RM4 mini and a portable AC/Heater.

This thing has a remote control with temperature settings, and I grabbed all the IR codes for all temperature levels and all fan speeds for heating and cooling.

Would it be somehow possible to map the different temperature codes within the thermostat, and getting more fine grained temperature control? I mapped some temperature values to switches like this:

switch:
  - platform: broadlink
    mac: '24:DF:A7:50:07:20'
    switches:
      - name: Fisher Cooling 20C High
        command_on: JgBwAA9sDxAPEA8QDxAPEA8QDy8PLw8QDxAPEA8QDxAPDw8QDxEPEA8QDxAPEA4QDxAQDw8SDxAPEA4QEA8PEA8QDy8PEA8QEA8PEA8QDxAPEA8QDxEPLw8QDxAOEA8RDi8PEA8yDy4PLw8PDy8PbA8ADQU=
        command_off: JgBwAA9zEBEQERAQEBEQERAREBAQEg8REBEQERAQEBEQERAREBEQEBAREBEQERAQEBEQERASEBEQEBAREBEQEQ8REDIPEhAREBAQERAREBEQEQ8yDxIQERAREDEQEQ8REDIQMRAUDxEQEQ8yDzIPcxAADQU=
      - name: Fisher Cooling 24C High
        command_on: JgBwABBzDxEQERAREBEPERARETAQMhAREBAQERAREBEQEBAREBEQERARDxEQERAREBEPERASEBEQERAREBAQERAREDEQERAREBEQERAQEBEQERAREBEQMRAREBEQERAxEBEPERA0EDIPERAREDEQchAADQU=
        command_off: JgBwAA9zEBEQERAQEBEQERAREBAQEg8REBEQERAQEBEQERAREBEQEBAREBEQERAQEBEQERASEBEQEBAREBEQEQ8REDIPEhAREBAQERAREBEQEQ8yDxIQERAREDEQEQ8REDIQMRAUDxEQEQ8yDzIPcxAADQU= 
      - name: Fisher Heating 25C High
        command_on: JgBwABByEBEQERAREBEPERAREDIQMRAREBEQERAQEBEQERARDxIQEBAREBEQERAQEBEQERASEBEQEBAREBEQEQ8REDIQERAREBAQERAREBEQEBAREBIQERAQEDIQMRAxEBEQMRAUEDEQMRAQEDEQcxAADQU=
        command_off: JgBwAA9zEBEQERAQEBEQERAREBAQEg8REBEQERAQEBEQERAREBEQEBAREBEQERAQEBEQERASEBEQEBAREBEQEQ8REDIPEhAREBAQERAREBEQEQ8yDxIQERAREDEQEQ8REDIQMRAUDxEQEQ8yDzIPcxAADQU=
      - name: Fisher Heating 22C High

But obviously I am able to map only one switch to the “cool” and “heat” functions so if the temperature set on the unit is too high, under certain circumstances the result will not be the expected. Am I missing something, or is that not possible?

Thanks,
Daniel

The notion of multiple heaters/coolers is not supported. However, the logic of the entity should lend itself to just using the most powerful switch. It’s going to run until it hits the setpoint either way, the only difference in using the 20C vs 24C is probably efficiency, I would think. If you’re never making the setpoint lower than 24C though, you can of course use that switch.

To give you an example, when I used this with a Dyson heater and a Broadlink, I sent IR codes that made the heater go to 80 or 90 degrees when the heater “turned on,” because I honestly didn’t care. I knew that the component would turn the heater off as soon as it got the set point (which was obviously below 80 degrees).

Thanks, now I found something else. If I change the configuration of the thermostat and hit reload on Dualmode_generic (under YAML configuration reload) it breaks, and comes back only after a HA restart. I’m trying to find out why the AC is not switched off when the target temperature is below the threshold value (with more than 0.5 C). If I set the thermostat to Off, the AC shuts down, but seems to stay on forever with just being in AC mode.

2021-07-29 10:26:42 INFO (MainThread) [homeassistant.components.climate] Setting up climate.dualmode_generic
2021-07-29 10:26:42 ERROR (MainThread) [homeassistant.components.climate] Error adding entities for domain climate with platform dualmode_generic
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 383, in async_add_entities
    await asyncio.gather(*tasks)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 588, in _async_add_entity
    await entity.add_to_platform_finish()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 665, in add_to_platform_finish
    await self.async_added_to_hass()
  File "/config/custom_components/dualmode_generic/climate.py", line 390, in async_added_to_hass
    _async_startup()
TypeError: _async_startup() missing 1 required positional argument: 'event'
2021-07-29 10:26:42 ERROR (MainThread) [homeassistant.components.climate] Error while setting up dualmode_generic platform for climate
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 258, in _async_setup_platform
    await asyncio.gather(*pending)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 383, in async_add_entities
    await asyncio.gather(*tasks)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 588, in _async_add_entity
    await entity.add_to_platform_finish()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 665, in add_to_platform_finish
    await self.async_added_to_hass()
  File "/config/custom_components/dualmode_generic/climate.py", line 390, in async_added_to_hass
    _async_startup()
TypeError: _async_startup() missing 1 required positional argument: 'event'`

Should the thermostat survive a YAML reload, or is this expected?

thanks again
Daniel