Daikin AC full automation guide

Hi everyone,

Here is a new deep dive article, this tile on automating your Air Conditioning system entirely.
This is my most complex work so far; it required tons of iterations, trials and errors, miserable failures, rage, and tears. Now, it works and is one of the most helpful automations I run at home.
(It is presented here under the form of a package to ease maintenance)

Disclaimer

:warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning:
:arrow_right: This automation is made to be used only by EXPERIMENTED Home Assistant users.
:arrow_right: Don’t tinker with it if you don’t fully understand all lines of the code and underlying concepts.
:arrow_right: If you botch it, it can degrade your HA performance and worth even, your A/C units.
:arrow_right: The provided code works for me, but it WILL require adjustments to your context
:arrow_right: This is educational material. If you put it in production, just be mindful of potential issues
:arrow_right: Keep an eye on your logs and AC units for some days to see if everything is running as intended
:warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning:

Project goals

Have automation take care of the heat/cool cycles in the house without humans having to intervene. But if a human is overriding the automation, we don’t want to hurt their feelings and want to respect their new settings (override). We also want to account for the price of electricity, perceived temperature depending on outside temperature, and the erratic behavior of Daikin units on most fronts.

When the house is unoccupied, we want to spare energy as well. We want to handle planning per room so as not to heat / cool when unnecessary. We want the fewest potential interactions with the AC units since they quickly become “overloaded.” Since the units have an air cleaning system, before going to bed, the units run an air cleanup session to kill germs and pollen.

How it works

MAIN AUTOMATION

  • The automation is executed every 20 min and starts by setting fan speed depending on time & zone,
  • then it calculates if an override has been applied by a human using the physical remote (mode/temp is not the expected one based on the previous cycle),
  • then it calculates the new mode & temperature based on various parameters (more about than later),
  • and optionally write all actions to logs for easy tracking (usually disabled, just for debug purposes),
  • then the main loop checks if the unit isn’t disabled, unavailable, or under a not-yet expired override,
  • if an update is necessary, the automation then sends the new values to the unit,
  • Finally, we start the room air cleanup cycle only during the 8pm cycle and stop it during the 10pm one.

CALCULATE_AC_TEMP SCRIPT

This one can take any constraint you want and adjust temperatures accordingly.
Here, most room temperature varies according to:

  • whether we’re at home or not
  • whether the door/window is opened or not
  • whether we are in peak pricing of electricity
  • according to a local usage planning of the room
  • an offset related to external temperature for a better “felt temperature”
  • If all is go, we use the comfort temp, if not, we fall back to the idle temp.

CALCULATE_AC_MODE SCRIPT

All splits attached to a specific heat pump unit can only operate in heat or cool mode. You can’t have one heating and the other one cooling. So, at some point, the script needs to set all of them to heat or cool. Auto is NOT a choice, trust me (and Daikin techs). I use an average of several temperatures to adjust the balance point for heat/cool.

OTHER SCRIPTS

They are very basic, doing housekeeping job and pretty straightforward, there no need to detail them.

About Daikin AC heat pumps and air conditioning units (splits)
Daikin AC units are insanely efficient at warming/cooling, fairly good-looking, and silent, but they are a total piece of crap in pretty much all other aspects. Poor design competes with cheap components and faulty code logic. To name some problems:

  • API is limited and slow
  • Units don’t know how to measure room temp correctly (aggravated the slower the fan runs, so in silent, basically useless)
  • Temperature readings compared to real room temp are often off by 4 to 8°C…
  • Auto mode is a disaster
  • Remotes are as dumb as possible (unaware of unit state, old IR no Wifi, etc.)
  • Wifi connections are beyond sketchy
  • Newer units have cloud dependency
  • Adding the units on Vlan-separated Wifi is… tricky

My setup

  • I use Daikin AC units (2 Air-Air heat pumps units, one for the night part one for the day part of the house, and 8 splits)
  • Most of them are Stylish units but some are older, I added external Wifi controllers to avoid any cloud dependance. You can unplug the native cloud-dependant Wifi chip, add an official Daikin wifi (ESP8266 btw) or the Faikin on the proper internal port.
  • They all are on my Wifi network (added through the Onecta app)
  • You need a robust Wifi network (I use Unifi APs)
  • I use Qubino Zwave electrical measurement devices, one per heat pump

Of note

  • In France, we have a “bleu/blanc/rouge tariff”, which makes electricity 30% cheaper the whole year by 80% more expensive 22 days per year (when plants are at capacity)
  • The temp calculation script can account for whatever constraint you have. It’s as versatile as Jinja templates are.

Caveheat

You will want to adapt it to your liking but be mindful that it’s a tricky process. There are pitfalls and traps everywhere. Isolating in subscripts instead of automation, parallel execution, race conditions between scripts, Daikin AC being bad at polling too often, even worth at getting orders too frequently and much more.

<TL/DR> Use it if your an advanced user, modify meaningfully only if you’ve got a solid experience in HA.

Todo

Since Daikin splits are so poorly designed regarding temperature readings, a way to optimise this script is to have Zigbee or Zwave temperature probes in all rooms, heat +7°C compared to the target, and then return to normal when the external probe detects the proper temperature.

This is a fairly reliable way actually to compensate for the botched design of those units, another way is to add an offset to the reading but it’s not going to behave linearly in my experience. (the delta varies depending on the external temp)

Hacking into the unit S21 port to feed it back proper readings may be doable but I didn’t try, or even desolder the thermistor and take it outside of the unit, same, didn’t try. The software fix is less invasive and not a warranty buster.

Also, adding a presence detector system in the calculate_temp script could allow heating only recently occupied rooms.

Thanks

  • To the author of the Daikin integration
  • To Daikin support L2 tech who helped me understand this mess
  • A lot of forum users always here to help
  • The whole HA core crew

This is a copy-less content. Please do whatever you want with it, but keep the warning to prevent less-experimented users from hurting themselves.

__
(My other posts: 20 things wish I knew when I started Home Assistant Integrate any remote in home assistant || Anti false positive PIR+camera alarm system || Automation from zero to hero || Securing & segregating your home network || Solar panel supervision)

4 Likes
#--------------------------------------------------------------------------------------------------
# Air conditioning system management
#--------------------------------------------------------------------------------------------------
group:
  ac_splits:
    name: AC splits
    entities:
      - climate.kitchen
      - climate.library
      - climate.parent_bedroom
      - climate.kid_bedroom
      - climate.gaming_room
      - climate.living_room_1
      - climate.living_room_2
      - climate.office
  ac_streamers:
    name: Daikin A/C streamers
    entities:
      - switch.ac_kitchen_streamer
      - switch.ac_library_streamer
      - switch.ac_parent_bedroom_streamer
      - switch.ac_gaming_room_streamer
      - switch.ac_living_room_1_streamer
      - switch.ac_living_room_2_streamer
      - switch.ac_office_streamer
#---- Templated electric consumption monitoring ---------------------------------------------------
template:
  - trigger:
      - trigger: time_pattern
        minutes: /5
    sensor:
      - name: ac_energy
        availability: >         
            {{ not 'unavailable' in
              [
                states('sensor.smart_meter_pac_1_electric_consumption_kwh'),
                states('sensor.smart_meter_pac_2_electric_consumption_kwh')
              ] and not 'unknown' in
              [
                states('sensor.smart_meter_pac_1_electric_consumption_kwh'),
                states('sensor.smart_meter_pac_2_electric_consumption_kwh')
              ]
            }}
        state: "{{ states('sensor.smart_meter_pac_1_electric_consumption_kwh') | float + states('sensor.heater_energy') | float + states('sensor.smart_meter_pac_2_electric_consumption_kwh') | float }}"
#---- Various utility meters for graphs -----------------------------------------------------------
utility_meter:
  daily_ac_energy:
    source: sensor.ac_energy
    cycle: daily
  daily_pac_consumption:
    source: sensor.smart_meter_pac_1_electric_consumption_kwh
    cycle: daily
  daily_pac2_consumption:
    source: sensor.smart_meter_pac_2_electric_consumption_kwh
    cycle: daily
  weekly_ac_energy:
    source: sensor.ac_energy
    cycle: weekly
  weekly_pac_consumption:
    source: sensor.smart_meter_pac_1_electric_consumption_kwh
    cycle: weekly
  weekly_pac2_consumption:
    source: sensor.smart_meter_pac_2_electric_consumption_kwh
    cycle: weekly
  monthly_ac_energy:
    source: sensor.ac_energy
    cycle: monthly
  monthly_pac_consumption:
    source: sensor.smart_meter_pac_1_electric_consumption_kwh
    cycle: monthly
  monthly_pac2_consumption:
    source: sensor.smart_meter_pac_2_electric_consumption_kwh
    cycle: monthly
#---- AC variables for automations ----------------------------------------------------------------
input_number:
  ac_temp_office:
    name: temperature in office
    min: 12
    max: 28
  ac_temp_living:
    name: temperature in living room
    min: 12
    max: 28
  ac_temp_gaming:
    name: temperature in gaming room
    min: 12
    max: 28
  ac_temp_kitchen:
    name: temperature in kitchen
    min: 12
    max: 28
  ac_temp_parents:
    name: temperature in parents bedroom
    min: 12
    max: 28
  ac_temp_library:
    name: temperature in library
    min: 12
    max: 28
  ac_temp_kid:
    name: temperature in kid bedroom
    min: 12
    max: 28
  ac_idle_heat_temp:
    name: AC heating target temp when not supposed to really act
    min: 12
    initial: 15
    max: 28
  ac_idle_cool_temp:
    name: AC cooling target temp when not supposed to really act
    min: 12
    initial: 27
    max: 28
  office_comfort_heat_temp:
    min: 18
    initial: 19
    max: 20
  office_comfort_cool_temp:
    min: 21
    initial: 23
    max: 25
  living_comfort_heat_temp:
    min: 18
    initial: 19
    max: 20
  living_comfort_cool_temp:
    min: 23
    initial: 25
    max: 26
  kid_comfort_heat_temp:
    min: 17
    initial: 18.5
    max: 19
  kid_comfort_cool_temp:
    min: 22
    initial: 24
    max: 24
  parents_comfort_heat_temp:
    min: 18
    initial: 18.5
    max: 19
  parents_comfort_cool_temp:
    min: 22
    initial: 24
    max: 25
  gaming_comfort_heat_temp:
    min: 17
    initial: 18
    max: 20
  gaming_comfort_cool_temp:
    min: 22
    initial: 24
    max: 25
  library_comfort_heat_temp:
    min: 18
    initial: 20
    max: 21
  library_comfort_cool_temp:
    min: 23
    initial: 24
    max: 27
  kitchen_comfort_heat_temp:
    min: 19
    initial: 20
    max: 21
  kitchen_comfort_cool_temp:
    min: 22
    initial: 24
    max: 24
  temp_offset:
    min: -5
    initial: 0
    max: 5
  ref_temp:
    min: -10
    initial: 22
    max: 50
#---- AC split overrides --------------------------------------------------------------------------
input_text:
  gaming_override:
    name: Gaming room AC override
  living_override:
    name: Living room AC override
  office_override:
    name: Office AC override
  kitchen_override:
    name: Kitchen AC override
  library_override:
    name: Library AC override
  parents_override:
    name: Parents AC override
  kid_override:
    name: kid AC override
  hvac_mode:
    name: Global AC mode
  hvac_mode_backup:
    name: Previous AC mode
#---- AC split disable tag ------------------------------------------------------------------------
input_boolean:
  kitchen_ac_disable:
    name: Kitchen A/C automation disabling
  parents_ac_disable:
    name: Parents bedroom A/C automation disabling
  library_ac_disable:
    name: Library A/C automation disabling
  kid_ac_disable:
    name: kid bedroom A/C automation disabling
  office_ac_disable:
    name: Office A/C automation disabling
  gaming_ac_disable:
    name: Gaming room A/C automation disabling
  living_ac_disable:
    name: Living room A/C automation disabling
  gaming_room_door:
    name: Gaming room door dummy sensor
    initial: off
  office_window:
    name: Office window dummy sensor
    initial: off
#---- One automation to rule them all -------------------------------------------------------------
automation:
  - id: "110001"
    alias: Airco - Pilot all A/C mode & temp
    description: Set A/C modes, temperature, overrides, disabled & away, per room
    triggers:
      - trigger: time_pattern
        minutes: "/20"
    actions:
      - action: script.set_fans
      - action: script.calculate_ac_override
      - action: script.calculate_ac_mode
      - action: script.calculate_ac_temp
      - action: script.ac_logs
      - repeat:
          for_each:
            - hvac: climate.kitchen
              room: "kitchen"
              temp: input_number.ac_temp_kitchen
              door: binary_sensor.kitchen_door_sensor_access_control_kitchen
              override: input_text.kitchen_override
              disabled: input_boolean.kitchen_ac_disable
            - hvac: climate.library
              room: "library"
              temp: input_number.ac_temp_library
              door: binary_sensor.library_door_sensor_window_door_is_open
              override: input_text.library_override
              disabled: input_boolean.library_ac_disable
            - hvac: climate.parent_bedroom
              room: "Parent bedroom"
              temp: input_number.ac_temp_parents
              door: binary_sensor.parent_bedroom_door_sensor_access_control_window_door_is_open
              override: input_text.parents_override
              disabled: input_boolean.parents_ac_disable
            - hvac: climate.kid_bedroom
              room: "kid bedroom"
              temp: input_number.ac_temp_kid
              door: binary_sensor.kid_bedroom_window_sensor_window_door_is_open
              override: input_text.kid_override
              disabled: input_boolean.kid_ac_disable
            - hvac: climate.gaming_room
              room: "Gaming room"
              temp: input_number.ac_temp_gaming
              door: input_boolean.gaming_room_door
              override: input_text.gaming_override
              disabled: input_boolean.gaming_ac_disable
            - hvac: climate.living_room_1
              room: "Living room 1"
              temp: input_number.ac_temp_living
              door: binary_sensor.living_room_door_sensor_window_door_is_open_3
              override: input_text.living_override
              disabled: input_boolean.living_ac_disable
            - hvac: climate.living_room_2
              room: "Living room 2"
              temp: input_number.ac_temp_living
              door: binary_sensor.living_room_door_sensor_window_door_is_open_3
              override: input_text.living_override
              disabled: input_boolean.living_ac_disable
            - hvac: climate.office
              room: "Office"
              temp: input_number.ac_temp_office
              door: input_boolean.office_window
              override: input_text.office_override
              disabled: input_boolean.office_ac_disable
          sequence:
            # If HVAC split isn't disabled or under an override
            - if: "{{ states(repeat.item.override) == 'off' and states(repeat.item.disabled) == 'off' }}"
              then:
                # If mode or temperature are different from the newly calculated one
                - if: "{{ (states('input_text.hvac_mode') != states(repeat.item.hvac)) or (states(repeat.item.temp) | float != state_attr(repeat.item.hvac, 'temperature')) }}"
                  then:
                    - if: "{{ states(repeat.item.hvac) != 'unavailable' }}"
                      then:
                        - delay: "00:00:05"
                        # Set the new mode
                        - action: climate.set_temperature
                          data:
                            temperature: "{{ states(repeat.item.temp) | float }}"
                            hvac_mode: "{{ states('input_text.hvac_mode') }}"
                          target:
                            entity_id: "{{ repeat.item.hvac }}"
                        - action: system_log.write
                          data:
                            logger: "AC"
                            level: warning
                            message: "SET - {{ repeat.item.hvac.split('.')[1]
                              + ' was ' + states(repeat.item.hvac) + '/' + state_attr(repeat.item.hvac, 'temperature') | string()
                              + ' now is ' + states('input_text.hvac_mode') + '/' + states(repeat.item.temp) | string()
                              + ', door is '     + iif(states(repeat.item.door) == 'on', 'opened', 'closed') + '.' }}"
                      else:
                        - action: system_log.write
                          data:
                            logger: "AC"
                            level: warning
                            message: "SET - {{ repeat.item.hvac.split('.')[1] + ' is unavailable, skipping.' }}"
                  else:
                    - action: script.debug_to_log
                      data:
                        logger: "AC"
                        message: "SET - {{ repeat.item.hvac.split('.')[1] + ' settings did not change, skipping.' }}"
              else:
                - if: "{{ states(repeat.item.disabled) == 'off' }}" 
                  then:
                    - action: system_log.write
                      data:
                        logger: "AC"
                        level: warning
                        message: "SET - {{ repeat.item.hvac.split('.')[1] + ' override mode is on, skipping.' }}"
                  else:
                    - action: system_log.write
                      data:
                        logger: "AC"
                        level: warning
                        message: "SET - {{ repeat.item.hvac.split('.')[1] + ' is disabled, skipping.' }}"
      - if: "{{ now().hour == 20 }}"
        then:
          - action: switch.turn_on
            target:
              entity_id: group.ac_streamers
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: "STREAMER - on"
      - if: "{{ now().hour == 22 }}"
        then:
          - action: switch.turn_off
            target:
              entity_id: group.ac_streamers
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: "STREAMER - off"
#---- Scripts supporting AC automation ------------------------------------------------------------
script:
  #---- Calculate temperature that needs to be applied in each room -------------------------------
  calculate_ac_temp:
    sequence:
      - action: input_number.set_value
        target:
          entity_id: input_number.temp_offset
        data_template:
          value: >
            {%- if states('sensor.teleinfo_periode_tarifaire') == 'hpjr' -%}
              {{ -1 |float }}
            {% elif state_attr('weather.home','temperature') | float(19) < 0 %}
              {{ 1.0 |float }}
            {% elif (state_attr('weather.home','temperature') | float(19) > 0) and (state_attr('weather.home','temperature') | float(19) < 5) %}
              {{ 0.5 |float }}
            {%- else %}
              {{ 0 |float }}
            {%- endif -%}
      - if: "{{ is_state('input_boolean.home_away_mode','off') }}" 
        then:
          # Office
          - action: input_number.set_value
            target:
              entity_id: input_number.ac_temp_office
            data_template:
              value: >
                {% if is_state('person.me','not_home') %}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}
                {%- elif now().hour not in (2,3,4,5,6,7,8) -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.office_comfort_cool_temp')|float, states('input_number.office_comfort_heat_temp')|float + states('input_number.temp_offset')|float) }}
                {%- else -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}
                {%- endif %}
          # Living
          - action: input_number.set_value
            target:
              entity_id: input_number.ac_temp_living
            data_template:
              value: >
                {%- if now().hour not in (0,1,2,3,4,5,6,7,8) and is_state('sensor.tempo_period_isred','False')
                    and states('binary_sensor.living_room_door_sensor_window_door_is_open_3') == 'off' -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.living_comfort_cool_temp')|float, states('input_number.living_comfort_heat_temp')|float + states('input_number.temp_offset')|float) }}
                {% elif is_state('sensor.tempo_period_isred','True') and now().weekday() not in (2,5,6) -%}
                  {{ 17 | float }}
                {% elif is_state('sensor.tempo_period_isred','True') and now().weekday() in (2,5,6) -%}
                  {{ 18 | float }}
                {%- else -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}
                {%- endif %}
          # Kitchen
          - action: input_number.set_value
            target:
              entity_id: input_number.ac_temp_kitchen
            data_template:
              value: >
                {%- if (now().hour in (7,8,12,13,18,19,20,21) or (now().hour in (10,11,14,18,19,22) and now().weekday() in (2,5,6))) 
                    and states('binary_sensor.kitchen_door_sensor_access_control_kitchen') == 'off' -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.kitchen_comfort_cool_temp')|float, states('input_number.kitchen_comfort_heat_temp')|float + states('input_number.temp_offset')|float) }}
                {%- else -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}
                {%- endif %}
          # Parents
          - action: input_number.set_value
            target:
              entity_id: input_number.ac_temp_parents
            data_template:
              value: >
                {%- if now().hour in (23,0,1,2,3,4,5,6,7,8,9,13) 
                    and states('binary_sensor.parent_bedroom_door_sensor_access_control_window_door_is_open') == 'off' -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.parents_comfort_cool_temp')|float, states('input_number.parents_comfort_heat_temp')|float + states('input_number.temp_offset')|float) }}
                {%- else -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}
                {%- endif %}
          # Library
          - action: input_number.set_value
            target:
              entity_id: input_number.ac_temp_library
            data_template:
              value: >
                {%- if (8 < now().hour < 23 and is_state('person.wife','home')) 
                    and states('binary_sensor.library_door_sensor_window_door_is_open') == 'off' -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.library_comfort_cool_temp')|float, states('input_number.library_comfort_heat_temp')|float + states('input_number.temp_offset')|float) }}
                {%- else -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}
                {%- endif %}
          # kid
          - action: input_number.set_value
            target:
              entity_id: input_number.ac_temp_kid
            data_template:
              value: >
                {%- if now().hour in (18,19,20,21,22,23,0,1,2,3,4,5,6,7) or now().weekday() in (2,5,6) %}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.kid_comfort_cool_temp')|float, states('input_number.kid_comfort_heat_temp')|float + states('input_number.temp_offset')|float) }}
                {%- else -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}
                {%- endif %}
          # Gaming
          - action: input_number.set_value
            target:
              entity_id: input_number.ac_temp_gaming
            data_template:
              value: >
                {%- if is_state('sensor.tempo_period_isred', 'False') and (now().hour in (19,20) or (now().weekday() in (2,5,6) and (9 < now().hour < 22))) -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.gaming_comfort_cool_temp')|float, states('input_number.gaming_comfort_heat_temp')|float + states('input_number.temp_offset')|float) }}
                {%- else -%}
                  {{ iif(states('input_text.hvac_mode') == "cool", states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}
                {%- endif %}
        else:
          - action: input_number.set_value
            target:
              entity_id:
                - input_number.ac_temp_gaming
                - input_number.ac_temp_kid
                - input_number.ac_temp_parents
                - input_number.ac_temp_kitchen
                - input_number.ac_temp_living
                - input_number.ac_temp_library
            data:
              value: "{{ iif(states('input_text.hvac_mode') == 'cool', states('input_number.ac_idle_cool_temp')|float, states('input_number.ac_idle_heat_temp')|float) }}"
  #---- Calculate under which mode each unit should operate ---------------------------------------
  calculate_ac_mode:
    sequence:
      - if: "{{ now().hour in (9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) and now().minute == 0 }}"
        then:
          - action: input_text.set_value
            data_template:
              value: "{{states('input_text.hvac_mode')}}"
            target:
              entity_id: 
                - input_text.hvac_mode_backup
          - action: input_number.set_value
            data_template:
              value: "{{ ((states('sensor.kitchen_temp_temperature') | float(24) + states('sensor.living_temp_temperature') | float(24) + state_attr('weather.home','temperature') | float(24)) /3) | round(1) }}"
            target:
              entity_id: 
                - input_number.ref_temp
          - action: input_text.set_value
            data_template:
              value: >
                {% if states('input_number.ref_temp') | float(24) < 23.2 %}
                  {{ 'heat' }}
                {%- else -%}
                  {{ 'cool' }}
                {%- endif %}
            target:
              entity_id: 
                - input_text.hvac_mode
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: "AC-Mode set to {{ states('input_text.hvac_mode') }} because reference temperature is {{ states('input_number.ref_temp') }}"
          - if: "{{ states('input_text.hvac_mode') != states('input_text.hvac_mode_backup') }}"
            then:
              - action: system_log.write
                data:
                  logger: "AC"
                  level: warning
                  message: "AC Mode changed from {{states('input_text.hvac_mode_backup')}} to {{states('input_text.hvac_mode')}}"
1 Like
  #---- Calculate if a Human used a remote and respect their choice -------------------------------
  calculate_ac_override:
    sequence:
      - repeat:
          for_each:
            - hvac: climate.kitchen
              temp: input_number.ac_temp_kitchen
              door: binary_sensor.kitchen_door_sensor_access_control_kitchen
              override: input_text.kitchen_override
              disabled: input_boolean.kitchen_ac_disable
            - hvac: climate.library
              temp: input_number.ac_temp_library
              door: binary_sensor.library_door_sensor_window_door_is_open
              override: input_text.library_override
              disabled: input_boolean.library_ac_disable
            - hvac: climate.parent_bedroom
              temp: input_number.ac_temp_parents
              door: binary_sensor.parent_bedroom_door_sensor_access_control_window_door_is_open
              override: input_text.parents_override
              disabled: input_boolean.parents_ac_disable
            - hvac: climate.kid_bedroom
              temp: input_number.ac_temp_kid
              door: binary_sensor.kid_bedroom_window_sensor_window_door_is_open
              override: input_text.kid_override
              disabled: input_boolean.kid_ac_disable
            - hvac: climate.gaming_room
              temp: input_number.ac_temp_gaming
              door: input_boolean.gaming_room_door
              override: input_text.gaming_override
              disabled: input_boolean.gaming_ac_disable
            - hvac: climate.living_room_1
              temp: input_number.ac_temp_living
              door: binary_sensor.living_room_door_sensor_window_door_is_open_3
              override: input_text.living_override
              disabled: input_boolean.living_ac_disable
            - hvac: climate.living_room_2
              temp: input_number.ac_temp_living
              door: binary_sensor.living_room_door_sensor_window_door_is_open_3
              override: input_text.living_override
              disabled: input_boolean.living_ac_disable
            - hvac: climate.office
              temp: input_number.ac_temp_office
              door: input_boolean.office_window
              override: input_text.office_override
              disabled: input_boolean.office_ac_disable
          sequence:
            #-------- If split isn't disabled or on override yet, enabling override mode if hvac mode or temp readings and calculated aren't aligned --------#
            - if: "{{ is_state(repeat.item.override, 'off') and is_state(repeat.item.disabled, 'off') }}"
              then:
                - if: "{{ states(repeat.item.hvac) not in (states('input_text.hvac_mode'), 'unavailable') }}"
                # Mode Override
                  then:
                    - action: system_log.write
                      data:
                        logger: "AC"
                        level: warning
                        message: "Override set because MODE for {{ repeat.item.hvac.split('.')[1] + ' is set to ' + states(repeat.item.hvac) + ' instead of ' + states('input_text.hvac_mode') }}"
                    - action: input_text.set_value
                      data_template:
                        value: >
                          {% if now().hour in (23,0,1,2,3,4,5,6,7,8) %}
                            {% set duration=(8 - now().hour) %}
                          {% else %}
                            {% set duration=3 %}
                          {% endif %}
                          {{ now() + timedelta(hours=duration) }}
                      target:
                        entity_id: "{{ repeat.item.override }}"
                - if: "{{ state_attr(repeat.item.hvac, 'temperature') != 'unavailable' and state_attr(repeat.item.hvac, 'temperature') | float != states(repeat.item.temp) | float }}"
                # Temp Override
                  then:
                    - action: system_log.write
                      data:
                        logger: "AC"
                        level: warning
                        message: "{{ 'Override set because TEMP for ' + repeat.item.hvac.split('.')[1] + ' is set to ' + state_attr(repeat.item.hvac,'temperature') | string + ' / ' + states(repeat.item.hvac) + ' instead of ' + states(repeat.item.temp) | string + ' / ' + states('input_text.hvac_mode') }}"
                    - action: input_text.set_value
                      data_template:
                        value: >
                          {% if now().hour in (0,1,2,3,4,5,6,7,8) %}
                            {% set duration=(8 - now().hour) %}
                          {% else %}
                            {% set duration=2 %}
                          {% endif %}
                          {{ now() + timedelta(hours=duration) }}
                      target:
                        entity_id: "{{ repeat.item.override }}"
            #----------------------------- Override expiring ---------------------------------------------------------------------------------------#
            - if: "{{ states(repeat.item.override) != 'off' }}"
              then:
                - if: "{{ now() > states(repeat.item.override) | as_datetime }}"
                  then:
                    - action: system_log.write
                      data:
                        logger: "AC-Override expired"
                        level: warning
                        message: "{{ 'Override for ' + repeat.item.hvac.split('.')[1] + ' has expired' }}"
                    - action: input_text.set_value
                      data:
                        value: "off"
                      target:
                        entity_id: "{{ repeat.item.override }}"
                  else:
                    - action: system_log.write
                      data:
                        logger: "AC-Override"
                        level: warning
                        message: "{{ 'Override for ' + repeat.item.hvac.split('.')[1] + ' has not expired yet.' }}"
            - delay: "00:00:01"
  #---- Spit the full debug in the logs (ha core logs) --------------------------------------------
  ac_debug:
    sequence:
      - if: "{{is_state('input_boolean.debug_flag', 'on')}}"
        then:
          - action: notify.info
            data:
              title: "A/C"
              message: >
                {{"AC:"
                + "\n-----------------------------------"
                + "\n| Room    | Temp | Set | Mode, Stat, Ovrd"
                + "\n| Kitchen | " + states('sensor.ac_kitchen_inside_temperature')        + ' | ' + states('input_number.ac_temp_kitchen') + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.kitchen_door_sensor_access_control_kitchen') + ', ' + states("input_text.kitchen_override")
                + "\n| Library | " + states('sensor.ac_library_inside_temperature')        + ' | ' + states('input_number.ac_temp_library') + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.library_door_sensor_window_door_is_open') + ', ' + states("input_text.library_override")
                + "\n| Parents | " + states('sensor.ac_parent_bedroom_inside_temperature') + ' | ' + states('input_number.ac_temp_parents') + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.parent_bedroom_door_sensor_access_control_window_door_is_open') + ', ' + states("input_text.parents_override")
                + "\n| kid   | " + states('sensor.kid_bedroom_inside_temperature')       + ' | ' + states('input_number.ac_temp_kid')   + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.kid_bedroom_window_sensor_window_door_is_open') + ', ' + states("input_text.kid_override")
                + "\n| Gaming  | " + states('sensor.gaming_room_inside_temperature')         + ' | ' + states('input_number.ac_temp_gaming')  + ' | ' + states('input_text.hvac_mode') + ', off, ' + states('input_text.gaming_override')
                + "\n| Living  | " + states('sensor.living_room_1_inside_temperature')       + ' | ' + states('input_number.ac_temp_living')  + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.living_room_door_sensor_window_door_is_open_3') + ', ' + states('input_text.living_override')
                + "\n| Office  | " + states('sensor.office_inside_temperature')            + ' | ' + states('input_number.ac_temp_office')  + ' | ' + states('input_text.hvac_mode') + ', off, ' + states('input_text.office_override')
                + "\n------------------------------------"}}
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: >
                {{"AC:"
                + "\n-----------------------------------"
                + "\n| Room    | Temp | Set | Mode, Stat, Ovrd"
                + "\n| Kitchen | " + states('sensor.ac_kitchen_inside_temperature')        + ' | ' + states('input_number.ac_temp_kitchen') + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.kitchen_door_sensor_access_control_kitchen') + ', ' + states("input_text.kitchen_override")
                + "\n| Library | " + states('sensor.ac_library_inside_temperature')        + ' | ' + states('input_number.ac_temp_library') + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.library_door_sensor_window_door_is_open') + ', ' + states("input_text.library_override")
                + "\n| Parents | " + states('sensor.ac_parent_bedroom_inside_temperature') + ' | ' + states('input_number.ac_temp_parents') + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.parent_bedroom_door_sensor_access_control_window_door_is_open') + ', ' + states("input_text.parents_override")
                + "\n| kid   | " + states('sensor.kid_bedroom_inside_temperature')       + ' | ' + states('input_number.ac_temp_kid')   + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.kid_bedroom_window_sensor_window_door_is_open') + ', ' + states("input_text.kid_override")
                + "\n| Gaming  | " + states('sensor.gaming_room_inside_temperature')         + ' | ' + states('input_number.ac_temp_gaming')  + ' | ' + states('input_text.hvac_mode') + ', off, ' + states('input_text.gaming_override')
                + "\n| Living  | " + states('sensor.living_room_1_inside_temperature')       + ' | ' + states('input_number.ac_temp_living')  + ' | ' + states('input_text.hvac_mode') + ', ' + states('binary_sensor.living_room_door_sensor_window_door_is_open_3') + ', ' + states('input_text.living_override')
                + "\n| Office  | " + states('sensor.office_inside_temperature')            + ' | ' + states('input_number.ac_temp_office')  + ' | ' + states('input_text.hvac_mode') + ', off, ' + states('input_text.office_override')
                + "\n------------------------------------"}}
  #---- Manually reset all overrides --------------------------------------------------------------
  ac_reset_overrides:
    alias: Turn off all airco overrides
    description: Turn off all airco overrides
    sequence:
      - action: system_log.write
        data:
          logger: "AC"
          level: warning
          message: "Resetting all overrides"
      - action: input_text.set_value
        target:
          entity_id:
            - input_text.kid_override
            - input_text.office_override
            - input_text.kitchen_override
            - input_text.library_override
            - input_text.parents_override
            - input_text.gaming_override
            - input_text.living_override
        data:
          value: "off"
      - repeat:
          for_each:
            - hvac: climate.kitchen
              temp: input_number.ac_temp_kitchen
              override: input_text.kitchen_override
            - hvac: climate.library
              temp: input_number.ac_temp_library
              override: input_text.library_override
            - hvac: climate.parent_bedroom
              temp: input_number.ac_temp_parents
              override: input_text.parents_override
            - hvac: climate.kid_bedroom
              temp: input_number.ac_temp_kid
              override: input_text.kid_override
            - hvac: climate.gaming_room
              temp: input_number.ac_temp_gaming
              override: input_text.gaming_override
            - hvac: climate.living_room_1
              temp: input_number.ac_temp_living
              override: input_text.living_override
            - hvac: climate.living_room_2
              temp: input_number.ac_temp_living
              override: input_text.living_override
            - hvac: climate.office
              temp: input_number.ac_temp_office
              override: input_text.office_override
          sequence:
            - action: climate.set_hvac_mode
              data_template:
                hvac_mode: "{{ states('input_text.hvac_mode') }}"
              target:
                entity_id: "{{ repeat.item.hvac }}"
            - action: climate.set_temperature
              data_template:
                temperature: "{{ states(repeat.item.temp) | float }}"
              target:
                entity_id: "{{ repeat.item.hvac }}"
            - action: input_text.set_value
              data_template:
                value: "off"
              target:
                entity_id: "{{ repeat.item.override }}"
            - action: system_log.write
              data:
                logger: "AC-Override"
                level: warning
                message: "{{ 'Manual reset of all overrides' }}"
  #---- Set fan speed/modes -----------------------------------------------------------------------
  set_fans:
    alias: Set fan speeds
    description: Fan speed contextual adjustment
    sequence:
      # quiet rooms
      - action: climate.set_fan_mode
        data:
          fan_mode: >
            {%- if now().hour in (23,0,1,2,3,4,5,6,7,8) and is_state('sensor.tempo_period_isred','True') -%}
              {{ "Silence" }}
            {%- else -%}
              {{ "1" }}
            {%- endif -%}
        target:
          entity_id:
            - climate.library
            - climate.parent_bedroom
            - climate.kid_bedroom
            - climate.office
      # other rooms
      - action: climate.set_fan_mode
        data:
          fan_mode: >
            {%- if now().hour in (1,2,3,4,5,6,7,8,9) and is_state('sensor.tempo_period_isred','False') -%}
              {{ "2" }}
            {%- elif now().hour in (19,20,21,22,23,0) and is_state('sensor.tempo_period_isred','False') -%}
              {{ "Silence" }}
            {%- else -%}
              {{ "1" }}
            {%- endif -%}
        target:
          entity_id:
            - climate.kitchen
            - climate.gaming_room
            - climate.living_room_1
            - climate.living_room_2
  #---- Simplified logs ---------------------------------------------------------------------------
  ac_logs:
    alias: Log AC states
    description: Centralized reporting of AC status & automations
    sequence:
      - if: "{{is_state('input_boolean.debug_flag', 'on')}}"
        then:
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: "{{ 'TARGET TEMP SET - Offset (' + states('input_number.temp_offset') + 
                          '), office (' + states('input_number.ac_temp_office')  + 
                          '), living (' + states('input_number.ac_temp_living')  + 
                          '), kitchen ('+ states('input_number.ac_temp_kitchen') + 
                          '), parents ('+ states('input_number.ac_temp_parents') + 
                          '), library ('+ states('input_number.ac_temp_library') + 
                          '), kid ('  + states('input_number.ac_temp_kid')   +
                          '), gaming (' + states('input_number.ac_temp_gaming')  +')' }}"
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: "{{ 'AC MODE - office (' + states('input_text.hvac_mode')  + 
                          '), living (' + states('input_text.hvac_mode')  + 
                          '), kitchen ('+ states('input_text.hvac_mode') + 
                          '), parents ('+ states('input_text.hvac_mode')  +  
                          '), library ('+ states('input_text.hvac_mode') + 
                          '), kid ('  + states('input_text.hvac_mode')   +
                          '), gaming (' + states('input_text.hvac_mode')  +')' }}"
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: "{{ 'AC OVERRIDE - office (' + states('input_text.kitchen_override') + 
                          '), living (' + states('input_text.kitchen_override') + 
                          '), kitchen ('+ states('input_text.kitchen_override') + 
                          '), parents ('+ states('input_text.kitchen_override') + 
                          '), library ('+ states('input_text.kitchen_override') + 
                          '), kid ('  + states('input_text.kitchen_override') +
                          '), gaming (' + states('input_text.kitchen_override') +')' }}"
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: "{{ 'ROOM TEMP READ (Daikin) - Office (' + state_attr('climate.office', 'current_temperature')|string() + 
                    '), living 1 (' + state_attr('climate.living_room_1', 'current_temperature')  |string() + 
                    '), living 2 (' + state_attr('climate.living_room_2', 'current_temperature')  |string() + 
                    '), kitchen ('  + state_attr('climate.kitchen', 'current_temperature')        |string() + 
                    '), parents ('  + state_attr('climate.parent_bedroom', 'current_temperature') |string() + 
                    '), library ('  + state_attr('climate.library', 'current_temperature')        |string() + 
                    '), kid ('    + state_attr('climate.kid_bedroom', 'current_temperature')  |string() +
                    '), gaming ('   + state_attr('climate.gaming_room', 'current_temperature')    |string() +')' }}"
          - delay: "00:00:31"
          - action: system_log.write
            data:
              logger: "AC"
              level: warning
              message: "{{ 'CURRENT TEMP SET - Office (' + state_attr('climate.office', 'temperature')|string() + 
                    '), living 1 (' + state_attr('climate.living_room_1', 'temperature')  |string() + 
                    '), living 2 (' + state_attr('climate.living_room_2', 'temperature')  |string() + 
                    '), kitchen ('  + state_attr('climate.kitchen', 'temperature')        |string() + 
                    '), parents ('  + state_attr('climate.parent_bedroom', 'temperature') |string() + 
                    '), library ('  + state_attr('climate.library', 'temperature')        |string() + 
                    '), kid ('    + state_attr('climate.kid_bedroom', 'temperature')  |string() +
                    '), gaming ('   + state_attr('climate.gaming_room', 'temperature')    |string() +')' }}"
      - delay: "00:00:31"
  #------ Set all A/C unit to cool ----------------------------------------------------------------
  all_ac_to_cool:
    alias: All AC units to cool
    description: All AC units to Cool
    sequence:
      - action: input_text.set_value
        data_template:
          value: "heat"
        target:
          entity_id: 
            - input_text.hvac_mode
      - action: input_number.set_value
        target:
          entity_id: 
            - input_number.ac_temp_office
            - input_number.ac_temp_kitchen
            - input_number.ac_temp_living
            - input_number.ac_temp_gaming
            - input_number.ac_temp_parents
            - input_number.ac_temp_library
            - input_number.ac_temp_kid
        data_template:
          value: 23
      - action: climate.set_temperature
        data:
          temperature: 23
          hvac_mode: "cool"
        target:
          entity_id: 
            - climate.kitchen
            - climate.library
            - climate.parent_bedroom
            - climate.kid_bedroom
            - climate.gaming_room
            - climate.living_room_1
            - climate.living_room_2
            - climate.office
      - action: input_text.set_value
        data_template:
          value: >
            {% if now().hour in (23,0,1,2,3,4,5,6,7,8) %}
              {% set duration=(8 - now().hour) %}
            {% else %}
              {% set duration=3 %}
            {% endif %}
            {{ now() + timedelta(hours=duration) }}
        target:
          entity_id: 
            - input_text.gaming_override 
            - input_text.living_override
            - input_text.office_override
            - input_text.kitchen_override
            - input_text.library_override
            - input_text.parents_override
            - input_text.kid_override
      - action: system_log.write
        data:
          logger: "AC"
          level: warning
          message: "All A/C have been set manually to Cool"
  #------ Set Day A/C unit to cool ----------------------------------------------------------------
  day_ac_to_cool:
    alias: All AC units to cool
    description: All AC units to Cool
    sequence:
      - action: input_text.set_value
        data_template:
          value: "heat"
        target:
          entity_id: 
            - input_text.hvac_mode
      - action: input_number.set_value
        target:
          entity_id: 
            - input_number.ac_temp_office
            - input_number.ac_temp_kitchen
            - input_number.ac_temp_living
            - input_number.ac_temp_library
        data_template:
          value: 23
      - action: climate.set_temperature
        data:
          temperature: 23
          hvac_mode: "cool"
        target:
          entity_id: 
            - climate.kitchen
            - climate.library
            - climate.living_room_1
            - climate.living_room_2
            - climate.office
      - action: input_text.set_value
        data_template:
          value: >
            {% if now().hour in (23,0,1,2,3,4,5,6,7,8) %}
              {% set duration=(8 - now().hour) %}
            {% else %}
              {% set duration=3 %}
            {% endif %}
            {{ now() + timedelta(hours=duration) }}
        target:
          entity_id: 
            - input_text.living_override
            - input_text.office_override
            - input_text.kitchen_override
            - input_text.library_override
      - action: system_log.write
        data:
          logger: "AC"
          level: warning
          message: "All A/C have been set manually to Cool"
  #------ Set Night A/C unit to cool --------------------------------------------------------------
  night_ac_to_cool:
    alias: All AC units to cool
    description: All AC units to Cool
    sequence:
      - action: input_text.set_value
        data_template:
          value: "heat"
        target:
          entity_id: 
            - input_text.hvac_mode
      - action: input_number.set_value
        target:
          entity_id: 
            - input_number.ac_temp_parents
            - input_number.ac_temp_kid
        data_template:
          value: 23
      - action: climate.set_temperature
        data:
          temperature: 23
          hvac_mode: "cool"
        target:
          entity_id: 
            - climate.parent_bedroom
            - climate.kid_bedroom
      - action: input_text.set_value
        data_template:
          value: >
            {% if now().hour in (23,0,1,2,3,4,5,6,7,8) %}
              {% set duration=(8 - now().hour) %}
            {% else %}
              {% set duration=3 %}
            {% endif %}
            {{ now() + timedelta(hours=duration) }}
        target:
          entity_id: 
            - input_text.parents_override
            - input_text.kid_override
      - action: system_log.write
        data:
          logger: "AC"
          level: warning
          message: "All A/C have been set manually to Cool"
  #------ Set all A/C to heat ---------------------------------------------------------------------
  all_ac_to_heat:
    alias: All AC units to heat
    description: All AC units to Heat
    sequence:
      - action: input_text.set_value
        data_template:
          value: "heat"
        target:
          entity_id: 
            - input_text.hvac_mode
      - action: input_number.set_value
        target:
          entity_id: 
            - input_number.ac_temp_office
            - input_number.ac_temp_kitchen
            - input_number.ac_temp_living
            - input_number.ac_temp_gaming
            - input_number.ac_temp_parents
            - input_number.ac_temp_library
            - input_number.ac_temp_kid
        data_template:
          value: 19
      - action: climate.set_temperature
        data:
          temperature: 19
          hvac_mode: "heat"
        target:
          entity_id: 
            - climate.kitchen
            - climate.library
            - climate.parent_bedroom
            - climate.kid_bedroom
            - climate.gaming_room
            - climate.living_room_1
            - climate.living_room_2
            - climate.office
      - action: input_text.set_value
        data_template:
          value: >
            {% if now().hour in (23,0,1,2,3,4,5,6,7,8) %}
              {% set duration=(8 - now().hour) %}
            {% else %}
              {% set duration=3 %}
            {% endif %}
            {{ now() + timedelta(hours=duration) }}
        target:
          entity_id: 
            - input_text.gaming_override 
            - input_text.living_override
            - input_text.office_override
            - input_text.kitchen_override
            - input_text.library_override
            - input_text.parents_override
            - input_text.kid_override
      - action: system_log.write
        data:
          logger: "AC"
          level: warning
          message: "All A/C have been set manually to Cool"
2 Likes