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:
:right_arrow: This automation is made to be used only by EXPERIMENTED Home Assistant users.
:right_arrow: Don’t tinker with it if you don’t fully understand all lines of the code and underlying concepts.
:right_arrow: If you botch it, it can degrade your HA performance and worth even, your A/C units.
:right_arrow: The provided code works for me, but it WILL require adjustments to your context
:right_arrow: This is educational material. If you put it in production, just be mindful of potential issues
:right_arrow: 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)

6 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"
    conditions:
      - "{{ states('input_boolean.ac_pause') == 'off' }}"
    actions:
      #----- Global abritrations ----------
      - if: "{{ now().minute == 0 and states('switch.faikin_kitchen_powerful') == 'on' }}"
        then:
          - action: script.ac_powerful
            data:
              mode: "off"
      - action: script.calculate_ac_override
      - action: script.calculate_ac_mode
      - action: script.calculate_ac_temp
      - action: script.detect_conflicts
      - action: script.set_fans
      - action: script.ac_logs
      #----- Mode is set globally ---------
      - if: "{{ states('input_text.hvac_mode') != states('input_text.hvac_mode_backup') }}"
        then:
          - action: climate.set_hvac_mode
            data:
              hvac_mode: "{{ states('input_text.hvac_mode') }}"
            target:
              entity_id: group.ac_splits
          - action: script.uns
            data:
              log_facility: airco
              message: SET - A/C Mode was {{ states('input_text.hvac_mode_backup') }} and should now be {{ states('input_text.hvac_mode') }}
      - delay: "00:00:30"
      #----- Temp is set individually -----
      - repeat:
          for_each:
            - hvac: climate.faikin_kitchen_mqtt_hvac
              ip: "192.168.0.119"
              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.faikin_library_mqtt_hvac
              ip: "192.168.0.120"
              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.faikin_parents_mqtt_hvac
              ip: "192.168.0.116"
              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.faikin_gaming_mqtt_hvac
              ip: "192.168.0.117"
              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.faikin_living1_mqtt_hvac
              ip: "192.168.0.115"
              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.faikin_living2_mqtt_hvac
              ip: "192.168.0.121"
              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
              ip: "192.168.0.101"
              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 A/C unit isn't disabled or under an override
            - if: "{{ states(repeat.item.override) == 'off' and states(repeat.item.disabled) == 'off' }}"
              then:
                # If A/C unit isn't unavailable
                - if: "{{ states(repeat.item.hvac) | lower not in ['none', 'unavailable', 'unknown'] }}"
                  then:
                    - action: climate.set_temperature
                      target:
                        entity_id: "{{ repeat.item.hvac }}"
                      data:
                        temperature: "{{ states(repeat.item.temp) | float }}"
                    - delay: "00:00:30"
                    - if: "{{ states(repeat.item.hvac) != states('input_text.hvac_mode') or state_attr(repeat.item.hvac, 'temperature') | float != states(repeat.item.temp) | float }}"
                      then:
                        - action: script.ac_recovery
                          data:
                            hvac: "{{ repeat.item.hvac }}"
                            ip: "{{ repeat.item.ip }}"
                            mode: "{{ states('input_text.hvac_mode') }}"
                            temperature: "{{ repeat.item.temp }}"
                      else:
                        - action: script.uns
                          data:
                            log_facility: airco
                            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: script.ac_recovery
                      data:
                        hvac: "{{ repeat.item.hvac }}"
                        ip: "{{ repeat.item.ip }}"
                        mode: "{{ states('input_text.hvac_mode') }}"
                        temperature: "{{ repeat.item.temp }}"
              else:
                - if: "{{ states(repeat.item.disabled) == 'off' }}"
                  then:
                    - action: script.uns
                      data:
                        log_facility: airco
                        message: WARNING - {{ repeat.item.hvac.split('.')[1] + ' override mode is on, skipping.' }}
                  else:
                    - action: script.uns
                      data:
                        log_facility: airco
                        message: WARNING - {{ repeat.item.hvac.split('.')[1] + ' is disabled, skipping.' }}

#---- 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.faikin_kitchen_mqtt_hvac
              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.faikin_library_mqtt_hvac
              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.faikin_parents_mqtt_hvac
              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.faikin_gaming_mqtt_hvac
              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.faikin_living1_mqtt_hvac
              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.faikin_living2_mqtt_hvac
              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:
            #-------- Is A/C unit disabled or on override already -----------
            - if: "{{ is_state(repeat.item.override, 'off') and is_state(repeat.item.disabled, 'off') }}"
              then:
                #-------- Is A/C unit temp or state readable -----------
                - if: "{{ (not state_attr(repeat.item.hvac, 'temperature') | is_number) or (states(repeat.item.hvac) | lower in ['none', 'unavailable', 'unknown']) }}"
                  then:
                    - action: script.uns
                      data:
                        log_facility: airco
                        notified_users: philippe
                        logbook: true
                        message: >
                          {% if not state_attr(repeat.item.hvac, 'temperature') | is_number %}
                            {{ 'WARNING - ' + repeat.item.hvac.split('.')[1] + ' ble-temp is probably down' }}
                          {% else %}
                            {{ 'WARNING - ' + repeat.item.hvac.split('.')[1] + ' faikin is probably down' }}
                          {% endif %}
                  else:
                    #-------- Check if Mode isn't the one expected -----------
                    - if: "{{ states(repeat.item.hvac) | lower != states('input_text.hvac_mode') }}"
                      then:
                        - action: script.uns
                          data:
                            log_facility: airco
                            logbook: true
                            message: WARNING - 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:
                            value: >
                              {% if now().hour in (23,0,1,2,3,4,5,6,7,8) %}
                                {% set target = today_at("06:00") %}
                                {% if now() >= target %}
                                  {% set target = target + timedelta(days=1) %}
                                {% endif %}
                                {{ target }}
                              {% else %}
                                {{ now() + timedelta(hours=4) }}
                              {% endif %}
                          target:
                            entity_id: "{{ repeat.item.override }}"
                      else:
                        #-------- Mode clear, now xheck if Temp isn't the one expected -----------
                        - if: "{{ states('input_text.hvac_mode') != 'off' and state_attr(repeat.item.hvac, 'temperature') | float != states(repeat.item.temp) | float }}"
                          then:
                            - action: script.uns
                              data:
                                log_facility: airco
                                logbook: true
                                message: WARNING - Override set because TEMP for {{ repeat.item.hvac.split('.')[1] + ' is set to ' + state_attr(repeat.item.hvac,'temperature') | string + ' / ' + ' instead of ' + states(repeat.item.temp) | string }}
                            - action: input_text.set_value
                              data:
                                value: >
                                  {% if now().hour in (23,0,1,2,3,4,5,6,7,8) %}
                                    {% set target = today_at("06:00") %}
                                    {% if now() >= target %}
                                      {% set target = target + timedelta(days=1) %}
                                    {% endif %}
                                    {{ target }}
                                  {% else %}
                                    {{ now() + timedelta(hours=4) }}
                                  {% endif %}
                              target:
                                entity_id: "{{ repeat.item.override }}"
              else:
                #-------- Override exists -----------------------------------
                - if: "{{ states(repeat.item.override) != 'off' }}"
                  then:
                    #-------- Abnormal override length ----------------------
                    - if: "{{ now() - timedelta(hours=12) > states(repeat.item.override) | as_datetime }}"
                      then:
                        - action: script.uns
                          data:
                            log_facility: airco
                            notified_users: philippe
                            logbook: true
                            message: WARNING - Abnormal override duration
                      #-------- Normal override length expirancy ----------
                      else:
                        - if: "{{ now() > states(repeat.item.override) | as_datetime }}"
                          then:
                            - action: script.uns
                              data:
                                log_facility: airco
                                logbook: true
                                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: script.uns
                              data:
                                log_facility: airco
                                message: "{{ 'Override for ' + repeat.item.hvac.split('.')[1] + ' has not expired yet.' }}"
                #-------- Automation of the A/C unit is disabled ------------
                - if: "{{ states(repeat.item.disabled) != 'off' }}"
                  then:
                    - action: script.uns
                      data:
                        log_facility: airco
                        notified_users: philippe
                        logbook: true
                        message: >
                          {% if not is_state(repeat.item.disabled, 'off') %}
                            {{ 'WARNING - ' + repeat.item.hvac.split('.')[1] + ' unit is disabled, skipping' }}
                          {% endif %}
            - delay: "00:00:02"
  #-------------------------------------------------------------------------------
  ac_logs:
    alias: Log AC states
    description: Centralized reporting of AC status & automations
    sequence:
      - action: script.uns
        data:
          log_facility: airco
          message: >
            {{"\n\n--------------------------------------------------------------------------------------------------------"
            + "\n| Room    | Probe temp | Calculated/set temp | Calculated/set Mode | Door state | Is disabled | Override"
            + "\n--------------------------------------------------------------------------------------------------------"
            + "\n| Library | " + states('sensor.faikin_library_ble_temp')        
            + '      | ' + states('input_number.ac_temp_library') + ' / ' + state_attr('climate.faikin_library_mqtt_hvac', 'current_temperature')|string() 
            + '         | ' + states('input_text.hvac_mode') + ' / ' + states('climate.faikin_library_mqtt_hvac')
            + '    | ' + states('binary_sensor.library_door_sensor_window_door_is_open') 
            + ' | ' + states("input_boolean.library_ac_disable")
            + ' | ' + states("input_text.library_override")
            + "\n--------------------------------------------------------------------------------------------------------"
            + "\n| Kitchen | " + states('sensor.faikin_kitchen_ble_temp')
            +'       | ' + states('input_number.ac_temp_kitchen') + ' / ' + state_attr('climate.faikin_kitchen_mqtt_hvac', 'current_temperature')|string() 
            +'         | ' + states('input_text.hvac_mode') + ' / ' + states('climate.faikin_kitchen_mqtt_hvac')
            +'    | ' + states('binary_sensor.kitchen_door_sensor_access_control_kitchen') 
            +' | ' + states("input_boolean.kitchen_ac_disable")
            +' | ' + states("input_text.kitchen_override")
            + "\n--------------------------------------------------------------------------------------------------------"
            + "\n| Parents | " + states('sensor.faikin_parents_ble_temp')  
            + '       | ' + states('input_number.ac_temp_parents') + ' / ' + state_attr('climate.faikin_parents_mqtt_hvac', 'current_temperature')|string() 
            + '         | ' + states('input_text.hvac_mode') + ' / ' + states('climate.faikin_parents_mqtt_hvac')
            + '    | ' + states('binary_sensor.parent_bedroom_door_sensor_access_control_window_door_is_open') 
            + ' | ' + states("input_boolean.parents_ac_disable")
            + ' | ' + states("input_text.parents_override")
            + "\n--------------------------------------------------------------------------------------------------------"
            + "\n| Jules   | " + states('sensor.faikin_jules_ble_temp')         
            + '       | ' + states('input_number.ac_temp_jules') + ' / ' + state_attr('climate.faikin_jules_mqtt_hvac', 'current_temperature')|string()
            + '         | ' + states('input_text.hvac_mode') + ' / ' + states('climate.faikin_jules_mqtt_hvac')
            + ' | ' + states('binary_sensor.jules_bedroom_window_sensor_window_door_is_open') 
            + ' | ' + states("input_boolean.jules_ac_disable")
            + ' | ' + states("input_text.jules_override")
            + "\n--------------------------------------------------------------------------------------------------------"
            + "\n| Gaming  | " + states('sensor.faikin_gaming_ble_temp')           
            + '       | ' + states('input_number.ac_temp_gaming') + ' / ' + state_attr('climate.faikin_gaming_mqtt_hvac', 'current_temperature')|string()
            + '         | ' + states('input_text.hvac_mode') + ' / ' + states('climate.faikin_gaming_mqtt_hvac') 
            + '    | off' 
            + ' | ' + states("input_boolean.gaming_ac_disable")
            + ' | ' + states('input_text.gaming_override')
            + "\n--------------------------------------------------------------------------------------------------------"
            + "\n| Living  | " + states('sensor.faikin_living1_ble_temp')        
            + '       | ' + states('input_number.ac_temp_living')  + ' / ' + state_attr('climate.faikin_living1_mqtt_hvac', 'current_temperature')|string()
            + '         | ' + states('input_text.hvac_mode') + ' / ' + states('climate.faikin_living1_mqtt_hvac')  
            + '    | ' + states('binary_sensor.living_room_door_sensor_window_door_is_open_3') 
            + ' | ' + states("input_boolean.living_ac_disable")
            + ' | ' + states('input_text.living_override')
            + "\n--------------------------------------------------------------------------------------------------------"
            + "\n| Office  | " + states('sensor.office_inside_temperature')            
            + '       | ' + states('input_number.ac_temp_office') + ' / ' + state_attr('climate.office', 'current_temperature')|string()  
            + '         | ' + states('input_text.hvac_mode') + ' / ' + states('climate.office')  
            + '    | off' 
            + ' | ' + states("input_boolean.office_ac_disable")
            + ' | ' + states('input_text.office_override')
            + "\n--------------------------------------------------------------------------------------------------------"}}

2 Likes

This is great work, thank you.
I installed 3 Daikin units in January and quickly realised their own temperature control is utter nonsense. I’m genuinely surprised they are still considered top of the game.

I’ve been trying to get HA to control it and have had partial success.

What temperature probes have you found to be reliable? If any.
I’m just about to go down the tasmota rabbit hole for cheap sonoff ZigBee probes

1 Like

Hi Vicdavery, go the Faikin route.
Efficient, simple, reliable. I added some bluetooth thermometers probed by and called it a day.

You’ll need to flash the temp probes with a custom firmware, and to physical’y add the faikin to the daikin socket but heck, this all worth every second of your time.

Probes

Faikout

I kept the above mentioned automation but simplified the code accordingly.

Do you happen to have any instructions or guide on how to put the Faikin in? I’m tempted to get them for mine due to how bad the built in temp sensor is but I’m unsure of where to start with taking it apart to get it in. I believe my unit - Daikin Vogue, is a very similar model to yours. Thanks

1 Like

Sure. The github of Revk (faikout’s author) has it all. Compatibility list, cables, links to order, parts references. https://codeberg.org/RevK/ESP32-Faikout

Regarding your model yes it totally looks like my daikin stylish. It’s fairly simple to open. With current cut from the power:

A good video here.

Two little plastic tabs are hiding the screw on the lower part of the external plastic case. Remove them, unclip gently all around specificity on the top+back side you hear a little click.

Once done, locate on the right side of the appliance some metal box. Unscrew and there should be the control board inside.

At the top of it, there is a port named s21. It’s where the faiking goes. I ordered cables heads from aliexpress to make my own cables. (Sometimes there are inverted, it didn’t fry my faikin, just turn the plug upside down):
This in 4S and this if memory serves.

Just join them 1:1 pin-wise.


S21 is the white port at the top close to the blue one.

You can unplug the onboard pre installed wifi chipset from daikin if you don’t use the app. Cleaner wifi, better security. It’s the large square with a shiny center at the bottom of the picture.

Be gentle in plugin / unplugging your cable to the s21, it’s a bit fragile and can be ripped off if you force or forget their is a little lock at the bottom of your cable plug. Don’t ask me how I know.

If all goes well your faikin will blink blue and offer a wifi hotspot.

If you use my automation remember to disable the faikin auto mode switch (cool/heat) in its settings.

2 Likes

Hello to you all, prety new around here, but just wondering what do i do If my FTXf Ver E dosn’t have any S21 port only the bigger one S403 and the S430 for the BRP069C45 newer adapter ( but without LAN descovery option). Can i plug all the 5 pins of a BRP069B45 adapter that I have to the one with C present on my unit?
do you got the Pinout for s430.
I did conect the first 4 pins, it was working I have put it in AP mode, but when registering to onecta App it giving me an error befor finishing the process. i guess I miss the handshake between the AC unit and the Wifi because I miss the 5th pin.

If you have an S21 port, there’s also the esphome-daikin-s21 project for direct control. I maintain a fork and stuff like a setpoint offset is built in (Daikin likes to overshoot the target to make up for not knowing what the living space temperature is).