Dyson AM06 (Dumb IR Fan Made Smart with Power Monitoring Plug and IR Transmittor)

I finally was able to integrate my Dyson AM06 fan into Home Assistant. It kind of combines a bunch of other fan template configs I’ve seen so I figure I do a write up about it. The Dyson AM06 fan is one of Dyson’s bladeless fans but it only has one power button and then an IR remote to can the speed, oscillation, and sleep timer.

I picked one up for $85 used on eBay a few years ago but it came without a remote so I couldn’t do much to make it smart. I revisited this recently by buying a replacement remote for it and using my Tamsotized IR Transmitter to learn the IR codes of each button. I then tested different configs until I came up with the following. This is the only config I’ve seen that uses MQTT to send the IR commands which I prefer over Broadlink because it is cheaper and faster.

I used a Tasmotized power monitoring plug with PowerDelta 102 set to update the power usage every time the consumption changes 2 watts. This works pretty well to keep the state of the fan synced up with Home Assistant. I’ve noticed it’s not 100% accurate at lower speeds because the watts are so close but way better than nothing. The speed also takes a little bit to register if you change it a couple times in a row to different speeds.

fan.yaml

fan:
  - platform: template
    fans:
      dyson_am06:
        friendly_name: "Dyson AM06"
        speed_count: 10
        value_template: >-
          {% if 5 < states('sensor.fan_plug_energy_apparentpower') | float %}
            on
          {% else %}
            off
          {% endif %}
        percentage_template:  >-
          {% set fan_power = states('sensor.fan_plug_energy_apparentpower') | float %}
          {% if is_state('input_boolean.dyson_am06_osc', 'off') and 10 < fan_power < 22.5 %}
            10
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 22.5 <= fan_power < 28 %}
            20
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 28 <= fan_power < 34.5 %}
            30
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 34.5 <= fan_power < 40.5 %}
            40
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 40.5 <= fan_power < 47 %}
            50
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 47 <= fan_power < 56.5 %}
            60
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 56.5 <= fan_power < 70 %}
            70
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 70 <= fan_power < 83.5 %}
            80
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 83.5 <= fan_power < 97 %}
            90
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and fan_power >= 97 %}
            100
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 10 < fan_power < 30 %}
            10
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 30 <= fan_power < 34.5 %}
            20
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 34.5 <= fan_power < 41 %}
            30
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 41 <= fan_power < 48 %}
            40
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 48 <= fan_power < 54 %}
            50
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 54 <= fan_power < 64 %}
            60
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 64 <= fan_power < 76 %}
            70
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 76 <= fan_power < 91 %}
            80
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 91 <= fan_power < 103 %}
            90
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and fan_power >= 103 %}
            100
          {% else %}
            0
          {% endif %}
        oscillating_template: "{{ is_state('input_boolean.dyson_am06_osc', 'on') }}"
        availability_template: "{{ is_state('switch.fan_plug', 'on') }}"
        turn_on:
          - condition: state
            entity_id: fan.dyson_am06
            state: 'off'
          - service: mqtt.publish
            data:
              payload: '0,2210,842,737,1605,737,842,737,842,737,1605,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737'
              topic: cmnd/bedroom_bridge/IrSend
              qos: 0
              retain: 0
        turn_off:
          - condition: state
            entity_id: fan.dyson_am06
            state: 'on'
          - service: mqtt.publish
            data:
              payload: '0,2210,842,737,1605,737,842,737,842,737,1605,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737,842,737'
              topic: cmnd/bedroom_bridge/IrSend
              qos: 0
              retain: 0
        set_percentage:
          - service: python_script.fan_speed_control
            data_template:
              fan_speed: "{{ percentage }}"
              fan_speed_entity_id: 'sensor.dyson_am06_speed'
              fan_entity_id: 'fan.dyson_am06'
              fan_speed_count: 10
              service_domain: 'mqtt'
              service: 'publish'
              service_data_increase:
                payload: '0,2206,872,712,1608,736,862,716,856,714,1614,716,844,714,840,714,838,712,898,710,1632,716,858,712,1624,714,852,712,1610,712,1582,740,814,736'
                topic: cmnd/bedroom_bridge/IrSend
                qos: 0
                retain: 0
              service_data_decrease:
                payload: '0,2210,842,744,1602,744,832,742,828,740,1588,742,820,738,816,742,808,740,1632,740,1604,740,1600,740,1594,744,1586,742,1580,742,816,744,1570,716'
                topic: cmnd/bedroom_bridge/IrSend
                qos: 0
                retain: 0
        set_oscillating:
          - service: mqtt.publish
            data:
                payload: '0,2210,842,737,1605,737,842,737,842,737,1605,737,842,737,842,737,842,737,1605,737,842,737,1605,737,842,737,1605,737,842,737,842,737,842,737'
                topic: cmnd/bedroom_bridge/IrSend
                qos: 0
                retain: 0
          - service: input_boolean.toggle
            entity_id: input_boolean.dyson_am06_osc

input_boolean:
  dyson_am06_osc:
    name: Dyson AM06 Oscillating Status
    icon: mdi:fan

sensor:
  - platform: template
    sensors:
      dyson_am06_speed:
        friendly_name: "Dyson AM06 Speed"
        value_template:  >-
          {% set fan_power = states('sensor.fan_plug_energy_apparentpower') | float %}
          {% if is_state('input_boolean.dyson_am06_osc', 'off') and 10 < fan_power < 22.5 %}
            10
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 22.5 <= fan_power < 28 %}
            20
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 28 <= fan_power < 34.5 %}
            30
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 34.5 <= fan_power < 40.5 %}
            40
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 40.5 <= fan_power < 47 %}
            50
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 47 <= fan_power < 56.5 %}
            60
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 56.5 <= fan_power < 70 %}
            70
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 70 <= fan_power < 83.5 %}
            80
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and 83.5 <= fan_power < 97 %}
            90
          {% elif is_state('input_boolean.dyson_am06_osc', 'off') and fan_power >= 97 %}
            100
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 10 < fan_power < 30 %}
            10
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 30 <= fan_power < 34.5 %}
            20
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 34.5 <= fan_power < 41 %}
            30
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 41 <= fan_power < 48 %}
            40
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 48 <= fan_power < 54 %}
            50
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 54 <= fan_power < 64 %}
            60
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 64 <= fan_power < 76 %}
            70
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 76 <= fan_power < 91 %}
            80
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and 91 <= fan_power < 103 %}
            90
          {% elif is_state('input_boolean.dyson_am06_osc', 'on') and fan_power >= 103 %}
            100
          {% else %}
            0
          {% endif %}

fan_speed_control.py

#### Init
service_domain = data.get('service_domain')
service = data.get('service')
service_data_increase = data.get('service_data_increase')
service_data_decrease = data.get('service_data_decrease')
# fan speed data
speed = data.get('fan_speed')
speed_count = data.get('fan_speed_count')
fan_speed_entity = hass.states.get(data.get('fan_speed_entity_id'))
fan_entity = hass.states.get(data.get('fan_entity_id'))

logger.debug('<fan_speed_control> fan state ({})'.format(fan_entity.state))
logger.debug('<fan_speed_control> Received fan speed from ({}) to ({})'.format(fan_speed_entity.state, speed))

### def
def check_speed(logger, speed):
  if speed is None:
    logger.warning('<fan_speed_control> Received fan speed is invalid (None)')
    return False

  if fan_entity.state is 'off':
    logger.warning('<fan_speed_control> can not change speed when fan is off')
    return False

  return True


### Run
if check_speed(logger, speed):
  speed_step = 100 // speed_count
  target_speed = int(speed) // speed_step
  last_speed = int(fan_speed_entity.state) // speed_step if fan_speed_entity.state else 1
  speed_max = speed_count

  if target_speed > last_speed:
    increase_loop = target_speed - last_speed
    decrease_loop = 11
  else:
    increase_loop = 11
    decrease_loop = last_speed - target_speed

  # check use increase or decrease
  if decrease_loop < increase_loop:
    loop = decrease_loop
    service_data = service_data_decrease
  else:
    loop = increase_loop
    service_data = service_data_increase

  # Call service
  if data.get('support_num_repeats', False):
    service_data['num_repeats'] = loop
    logger.debug('<fan_speed_control> call service ({}.{}) {}'.format(service_domain, service, service_data))
    hass.services.call(service_domain, service, service_data)
  else:
    for i in range(loop):
      logger.debug('<fan_speed_control> call service ({}.{}) {}'.format(service_domain, service, service_data))
      result = hass.services.call(service_domain, service, service_data)
      time.sleep(0.75)


elif fan_entity.state is not 'off' and speed == 'off':
  logger.debug('<fan_speed_control> call fan off')
1 Like