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')