Automating air purifiers

Hi,

I’m trying to automate my Xiaomi Air Purifiers. What I want to achieve:

  1. Turn off air purifier if AQI is below defined value for 3 minutes and set favourite level to default value (defined in automation rule)
  2. Turn on air purifier when aqi is above certain value and set mode to auto or favourite according to defined values
  3. Change air purifier favourite level if:
    • Air purifier is on
    • AQI is above defined value for 10 minutes

I have it partially working but can’t get it where I want to, for instance “for” does not work with template condition (I’m most likely doing something wrong here).

I also had to use time trigger for “on” part as I can’t figure out how to use aqi attribute state changes. When I used it it always ended in triggering the auto mode and never switched to favourite. I think this is due how numeric_state works. Ideally I don’t want to use timer at all.

Here’s the code I have:

# Office

- alias: Turn off air purifier office
 trigger:
   platform: numeric_state
   entity_id: fan.xiaomi_air_purifier_office
   value_template: "{{ states.fan.xiaomi_air_purifier_office.attributes.aqi | int }}"
   below: 15
   for:
     hours: 0
     minutes: 3
     seconds: 0
 action:
   service: fan.turn_off
   entity_id: fan.xiaomi_air_purifier_office

- alias: Turn on air purifier office
 trigger:
   platform: time
   minutes: '/1'
   seconds: 00
 condition:
   condition: template
   value_template: "{{ states.fan.xiaomi_air_purifier_office.attributes.aqi | int > 15 }}"
   # for:
   #   hours: 0
   #   minutes: 3
   #   seconds: 0
 action:
   - service: fan.turn_on
     entity_id: fan.xiaomi_air_purifier_office
   - service: fan.set_speed
     entity_id: fan.xiaomi_air_purifier_office
     data_template:
       speed: >
           {% if states.fan.xiaomi_air_purifier_office.attributes.aqi | int > 30 %}
             favorite
           {% elif states.fan.xiaomi_air_purifier_office.attributes.aqi | int < 20 %}
             auto
           {% else %}
             auto
           {% endif %}
   - service: input_select.select_option
     data_template:
       entity_id: input_select.xiaomi_air_purifier_mode_office
       option: '{{ states.fan.xiaomi_air_purifier_office.attributes.mode }}'
2 Likes

I think something like this might work for you:

- alias: Turn off air purifier office
  trigger:
    platform: numeric_state
    entity_id: fan.xiaomi_air_purifier_office
    value_template: "{{ state.attributes.aqi }}"
    below: 15
    for:
      minutes: 3
  action:
    - service: fan.turn_off
      entity_id: fan.xiaomi_air_purifier_office
    - service: fan.xiaomi_miio_set_favorite_level
      entity_id: fan.xiaomi_air_purifier_office
      data:
        level: X
- alias: Turn on air purifier office
  trigger:
    platform: numeric_state
    entity_id: fan.xiaomi_air_purifier_office
    value_template: "{{ state.attributes.aqi }}"
    above: 15
    for:
      minutes: 3
  action:
    - service: fan.turn_on
      entity_id: fan.xiaomi_air_purifier_office
    - service: fan.set_speed
      entity_id: fan.xiaomi_air_purifier_office
      data_template:
        speed: >
          {% if trigger.to_state.attributes.aqi | int > 30 %}
            favorite
          {% else %}
            auto
          {% endif %}
- alias: Change favourite level air purifier office
  trigger:
    platform: template
    value_template: >
      {{ is_state('fan.xiaomi_air_purifier_office', 'on') and
         state_attr('fan.xiaomi_air_purifier_office', 'aqi')|int > NN }}
  action:
    - wait_template: >
        {{ is_state('fan.xiaomi_air_purifier_office', 'off') or
           state_attr('fan.xiaomi_air_purifier_office', 'aqi')|int <= NN }}
      timeout: '00:10:00'
      continue_on_timeout: 'true'
    - condition: template
      value_template: >
        {{ is_state('fan.xiaomi_air_purifier_office', 'on') and
           state_attr('fan.xiaomi_air_purifier_office', 'aqi')|int > NN }}
    - service: fan.xiaomi_miio_set_favorite_level
      entity_id: fan.xiaomi_air_purifier_office
      data:
        level: Y

Let me know if you have any questions.

Thank You, will try it out and report back.

@pnbruckner Your solution works (did not test setting favourite level yet) but will the “on” part with numeric state trigger once? Or will evaluate AQI constantly? For example, AQI is 16, so automation set purifier to “on” and mode to “auto”, what if in the meantime AQI jumps to 35? Will the automation change the mode to “favourite”? Or will it stay at “auto”?

A numeric_state trigger will trigger when the condition (e.g., above 15) becomes true, and will not trigger again until that condition becomes false (e.g., 15 or below) and then becomes true again (back above 15.)

But you also have “for 3 minutes.” That means the “above 15” condition needs to stay true for that long before it actually triggers.

So, let’s say it raises to 16. Then let’s say it stays above 15 for 3 minutes. At that time the action will run, and depending on what the value is at that time (above 30 or not) it will set the speed accordingly. Let’s say it’s 30 or below at that time (so it sets the speed to auto) and then, without going to 15 or below, eventually goes to above 30. Since it hasn’t gone to 15 or below first it won’t trigger again, so the speed will stay at auto.

If you want it to trigger again if it goes above 30, then you need to add another trigger:

- alias: Turn on air purifier office
  trigger:
    - platform: numeric_state
      entity_id: fan.xiaomi_air_purifier_office
      value_template: "{{ state.attributes.aqi }}"
      above: 15
      for:
        minutes: 3
    - platform: numeric_state
      entity_id: fan.xiaomi_air_purifier_office
      value_template: "{{ state.attributes.aqi }}"
      above: 30
  action:
    - service: fan.turn_on
      entity_id: fan.xiaomi_air_purifier_office
    - service: fan.set_speed
      entity_id: fan.xiaomi_air_purifier_office
      data_template:
        speed: >
          {% if trigger.to_state.attributes.aqi | int > 30 %}
            favorite
          {% else %}
            auto
          {% endif %}

Now once it goes above 30 it will trigger again and set the speed to favorite. You can add the “for” clause to this second trigger, too, if you want.

HI artkrz. I have a problem , my xiaomi air purifiers used xiaomi_miio compose setting fan. In homekit just a switch,can not change speed mode.please help me

Not using homekit but here’s my current setup for controlling the Air Purifier 2s:

################################################################################
# Set input states when HA starts or states change
################################################################################

- alias: Set state for air purifier office mode input
  trigger:
    - platform: homeassistant
      event: start
    - platform: state
      entity_id: sensor.xiaomi_air_purifier_mode_office
  action:
    service: input_select.select_option
    data_template:
      entity_id: input_select.air_purifier_mode_office
      option: '{{ states.fan.xiaomi_air_purifier_office.attributes.mode }}'

- alias: Set state for air purifier office favorite level input
  trigger:
    - platform: homeassistant
      event: start
    - platform: state
      entity_id: sensor.xiaomi_air_purifier_favorite_level_office
  action:
    service: input_number.set_value
    data_template:
      entity_id: input_number.air_purifier_favorite_level_office
      value: '{{ states.fan.xiaomi_air_purifier_office.attributes.favorite_level | int }}'

################################################################################
# Inputs
################################################################################

- alias: Air purifier mode office
  trigger:
    platform: state
    entity_id: input_select.xiaomi_air_purifier_mode_office
  action:
    - service: fan.set_speed
      data_template:
        entity_id: fan.xiaomi_air_purifier_office
        speed: '{{ states.input_select.xiaomi_air_purifier_mode_office.state }}'

- alias: Air purifier favorite level office
  trigger:
    platform: state
    entity_id: input_number.xiaomi_air_purifier_favorite_level_office
  action:
    - service: fan.xiaomi_miio_set_favorite_level
      data_template:
        entity_id: fan.xiaomi_air_purifier_office
        level: '{{ states.input_number.xiaomi_air_purifier_favorite_level_office.state | int }}'


################################################################################
# Automatic control
################################################################################

- alias: Turn off air purifier office
  trigger:
    platform: numeric_state
    entity_id: fan.xiaomi_air_purifier_office
    value_template: "{{ states.fan.xiaomi_air_purifier_office.attributes.aqi | int }}"
    below: 10
    for:
      minutes: 10
  condition:
    - condition: state
      entity_id: 'fan.xiaomi_air_purifier_office'
      state: 'on'
  action:
    service: fan.turn_off
    entity_id: fan.xiaomi_air_purifier_office

- alias: Turn on air purifier office
  trigger:
    platform: numeric_state
    entity_id: fan.xiaomi_air_purifier_office
    value_template: "{{ states.fan.xiaomi_air_purifier_office.attributes.aqi | int }}"
    above: 10
    for:
      minutes: 10
  condition:
    - condition: state
      entity_id: 'fan.xiaomi_air_purifier_office'
      state: 'off'
  action:
    - service: fan.turn_on
      entity_id: fan.xiaomi_air_purifier_office
    - service: fan.speed
      entity_id: fan.xiaomi_air_purifier_office
      data:
        speed: auto

- alias: Set favorite fan speed for air purifier office
  trigger:
    platform: time
    minutes: '/5'
    seconds: 00
  condition:
    - condition: state
      entity_id: 'fan.xiaomi_air_purifier_office'
      state: 'on'
  action:
    - service: fan.set_speed
      entity_id: fan.xiaomi_air_purifier_office
      data:
        speed: favorite
    - service: fan.xiaomi_miio_set_favorite_level
      data_template:
        entity_id: fan.xiaomi_air_purifier_office
        level: >
            {% if states.fan.xiaomi_air_purifier_office.attributes.aqi | int > 30 %}
              15
            {% elif states.fan.xiaomi_air_purifier_office.attributes.aqi | int > 25 %}
              10
            {% elif states.fan.xiaomi_air_purifier_office.attributes.aqi | int > 20 %}
              5
            {% elif states.fan.xiaomi_air_purifier_office.attributes.aqi | int > 15 %}
              3
            {% elif states.fan.xiaomi_air_purifier_office.attributes.aqi | int > 10 %}
              1
            {% else %}
              0
            {% endif %}
    - service: input_select.select_option
      data_template:
        entity_id: input_select.xiaomi_air_purifier_mode_office
        option: '{{ states.fan.xiaomi_air_purifier_office.attributes.mode }}'
    - service: input_number.set_value
      data_template:
        entity_id: input_number.xiaomi_air_purifier_favorite_level_office
        value: '{{ states.fan.xiaomi_air_purifier_office.attributes.favorite_level }}'

4 Likes

@artkrz thank you for sharing this… I’ve just used your code and it works perfect. thanks for sharing this and other for help having it working.

Do I need to change the name instead of office?

Since I want to be able to force the purifier in ON or OFF state without touching Home Assistant, I defined the automations to work only when I leave the purifier (either on or off) in Auto mode. If I set it to Silent or Favorite the automations won’t do anything.

My goal was to have the Mi Air Purifier PRO turn on at a certain AQI value, turn off when the value drops below a threshold for at least some time, and to turn on the fan every two hours to move the air and hopefully get a more reliable AQI reading. I used Silent mode to avoid other automations to interfere. After that 10 minutes, the fan is switched back to Auto so that the other automations take over.

Checking the speed (Auto, silent, favorite) works only when the device is on, therefore I used the “mode” attribute that is retained and readable also when off.

Setting any speed automatically turns on the fan.

I used “state_attr()” and similar everywhere because this is the correct way to do it, not using “states.attributes.xxx”. See https://www.home-assistant.io/docs/configuration/templating/

Apparently using “numerical_state” avoids the need of casting into “int” the value of the attribute.

In configuration.yaml:

fan:
  - platform: xiaomi_miio
    name: "Air Purifier Living room"
    host: 192.168.yyy.xxx
    token: xxx
    model: zhimi.airpurifier.v6

In automations.yaml:

# turn off only if below AQI value and in AUTO mode, not in silent or manual (favourite)
- alias: 'Auto off air purifier living room'
  trigger:
    platform: numeric_state
    entity_id: 'fan.air_purifier_living_room'
    value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') }}"
    below: 10
    for:
      minutes: 5
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: 'fan.air_purifier_living_room'
        state: 'on'
      - condition: template
        value_template: "{{ is_state_attr('fan.air_purifier_living_room', 'mode', 'auto') }}"
  action:
    - service: fan.turn_off
      entity_id: 'fan.air_purifier_living_room'

# turn on if AQI above specified value and in AUTO mode last time
- alias: 'Auto on air purifier living room'
  trigger:
    platform: numeric_state
    entity_id: 'fan.air_purifier_living_room'
    value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') }}"
    above: 16
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: 'fan.air_purifier_living_room'
        state: 'off'
      - condition: template
        value_template: "{{ is_state_attr('fan.air_purifier_living_room', 'mode', 'auto') }}"
  action:
    - service: fan.turn_on
      entity_id: 'fan.air_purifier_living_room'

# turn on every two hours for some minutes to move air around, keep silent for that
- alias: 'Silent on air purifier living room'
  trigger:
    platform: time_pattern
    hours: "/2"
    minutes: 0
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: 'fan.air_purifier_living_room'
        state: 'off'
      - condition: template
        value_template: "{{ is_state_attr('fan.air_purifier_living_room', 'mode', 'auto') }}"
  action:
# turns on automatically when speed is changed
#    - service: fan.turn_on
#      entity_id: 'fan.air_purifier_living_room'
    - service: fan.set_speed
      entity_id: 'fan.air_purifier_living_room'
      data:
        speed: 'Silent'
    - delay: 0:10
    - service: fan.set_speed
      entity_id: 'fan.air_purifier_living_room'
      data:
        speed: 'Auto'

Actually the Purifier-auto-off automation is not reliable, sometimes it fires, usually not.
If I disable the “for 5 minutes” then it works fine.

Any idea? Since it’s a more generic question, I’m opening a different thread to keep this one about the automations and about debugging generic features of the automation.

I have no idea. I’m happy with my approach apart of the crap readings from the built in sensor. I’m adding SDS011 as triggers.

I have finally build new sensors based on sds011 and esp. Here’s new automation rules including 1st attempt on using pm10 as well as pm2.5. Works really nice but probably could be improved. Here’s a package for 2s in my office (requires pm2.5 and pm10 readings in mqtt sensor:

homeassistant:
  customize:
    sensor.air_purifier_office_aqi:
      icon: mdi:weather-fog
    sensor.air_purifier_office_humidity:
      device_class: humidity
    sensor.air_purifier_office_temerature:
      device_class: temperature
    sensor.air_purifier_office_mode:
      icon: mdi:transition
    sensor.air_purifier_office_favorite_level:
      icon: mdi:weather-windy

fan:
  - platform: xiaomi_miio
    host: 10.0.40.14
    token: !secret xiaomi_air_purifier_office
    name: Xiaomi Air Purifier Office

group:
  air_purifier_office:
    name: Air purifier
    view: no
    entities:
      - fan.xiaomi_air_purifier_office
      - input_select.xiaomi_air_purifier_mode_office
      - sensor.air_purifier_office_mode
      - input_number.xiaomi_air_purifier_favorite_level_office
      - sensor.air_purifier_office_favorite_level
      - sensor.air_purifier_office_aqi
      - sensor.air_purifier_office_temperature
      - sensor.air_purifier_office_humidity
  automation_office:
    name: Office
    view: no
    icon: mdi:robot
    entities:
      - automation.set_favorite_fan_speed_for_air_purifier_office
      - automation.set_state_for_air_purifier_office_favorite_level_input
      - automation.set_state_for_air_purifier_office_mode_input
      - automation.turn_off_air_purifier_office
      - automation.turn_on_air_purifier_office

input_number:
  xiaomi_air_purifier_favorite_level_office:
    name: Favorite level
    icon: mdi:weather-windy
    initial: 0
    min: 0
    max: 14
    step: 1

input_select:
  xiaomi_air_purifier_mode_office:
    name: Mode
    icon: mdi:transition
    options:
    - auto
    - favorite
    - silent
    - idle

sensor:
  - platform: template
    sensors:
      air_purifier_office_mode:
        value_template: '{{ states.fan.xiaomi_air_purifier_office.attributes.mode }}'
  - platform: template
    sensors:
      air_purifier_office_favorite_level:
        value_template: '{{ states.fan.xiaomi_air_purifier_office.attributes.favorite_level }}'
  - platform: template
    sensors:
      air_purifier_office_aqi:
        value_template: '{{ states.fan.xiaomi_air_purifier_office.attributes.aqi }}'
        unit_of_measurement: "µg/m3"
  - platform: template
    sensors:
      air_purifier_office_temperature:
        value_template: '{{ states.fan.xiaomi_air_purifier_office.attributes.temperature }}'
        unit_of_measurement: "°C"
  - platform: template
    sensors:
      air_purifier_office_humidity:
        value_template: '{{ states.fan.xiaomi_air_purifier_office.attributes.humidity }}'
        unit_of_measurement: "%"
  - platform: template
    sensors:
      air_purifier_office_child_lock:
        value_template: '{{ states.fan.xiaomi_air_purifier_office.attributes.child_lock }}'
  - platform: template
    sensors:
      air_purifier_office_led:
        value_template: '{{ states.fan.xiaomi_air_purifier_office.attributes.led }}'
  - platform: template
    sensors:
      air_purifier_office_buzzer:
        value_template: '{{ states.fan.xiaomi_air_purifier_office.attributes.buzzer }}'

switch:
  - platform: template
    switches:
      air_purifier_child_lock_office:
        friendly_name: "Blokada"
        value_template: "{{ is_state('sensor.air_purifier_office_child_lock', 'True') }}"
        turn_on:
          service: fan.xiaomi_miio_set_child_lock_on
          data:
            entity_id: fan.xiaomi_air_purifier_office
        turn_off:
          service: fan.xiaomi_miio_set_child_lock_off
          data:
            entity_id: fan.xiaomi_air_purifier_office
        icon_template: >-
                  {% if is_state('sensor.air_purifier_office_child_lock', 'True') %}
                    mdi:lock
                  {% else %}
                    mdi:lock-open
                  {% endif %}
  - platform: template
    switches:
      air_purifier_led_office:
        friendly_name: "Led"
        value_template: "{{ is_state('sensor.air_purifier_office_led', 'True') }}"
        turn_on:
          service: fan.xiaomi_miio_set_led_on
          data:
            entity_id: fan.xiaomi_air_purifier_office
        turn_off:
          service: fan.xiaomi_miio_set_led_off
          data:
            entity_id: fan.xiaomi_air_purifier_office
        icon_template: >-
                  {% if is_state('sensor.air_purifier_office_led', 'True') %}
                    mdi:led-off
                  {% else %}
                    mdi:led-on
                  {% endif %}
  - platform: template
    switches:
      air_purifier_buzzer_office:
        friendly_name: "Dzwięk"
        value_template: "{{ is_state('sensor.air_purifier_office_buzzer', 'True') }}"
        turn_on:
          service: fan.xiaomi_miio_set_buzzer_on
          data:
            entity_id: fan.xiaomi_air_purifier_office
        turn_off:
          service: fan.xiaomi_miio_set_buzzer_off
          data:
            entity_id: fan.xiaomi_air_purifier_office
        icon_template: >-
                  {% if is_state('sensor.air_purifier_office_buzzer', 'True') %}
                    mdi:volume-high
                  {% else %}
                    mdi:volume-mute
                  {% endif %}

automation:
  # Set input states when HA starts
  - alias: Set state for air purifier office mode input
    trigger:
      - platform: homeassistant
        event: start
      - platform: state
        entity_id: sensor.xiaomi_air_purifier_mode_office
    action:
      service: input_select.select_option
      data_template:
        entity_id: input_select.air_purifier_mode_office
        option: '{{ states.fan.xiaomi_air_purifier_office.attributes.mode }}'
  - alias: Set state for air purifier office favorite level input
    trigger:
      - platform: homeassistant
        event: start
      - platform: state
        entity_id: sensor.xiaomi_air_purifier_favorite_level_office
    action:
      service: input_number.set_value
      data_template:
        entity_id: input_number.air_purifier_favorite_level_office
        value: '{{ states.fan.xiaomi_air_purifier_office.attributes.favorite_level | int }}'
  # Inputs
  - alias: Air purifier mode office
    trigger:
      platform: state
      entity_id: input_select.xiaomi_air_purifier_mode_office
    action:
      - service: fan.set_speed
        data_template:
          entity_id: fan.xiaomi_air_purifier_office
          speed: '{{ states.input_select.xiaomi_air_purifier_mode_office.state }}'
  - alias: Air purifier favorite level office
    trigger:
      platform: state
      entity_id: input_number.xiaomi_air_purifier_favorite_level_office
    action:
      - service: fan.xiaomi_miio_set_favorite_level
        data_template:
          entity_id: fan.xiaomi_air_purifier_office
          level: '{{ states.input_number.xiaomi_air_purifier_favorite_level_office.state | int }}'
  # Automatic control
  - alias: Turn off air purifier office
    trigger:
      platform: numeric_state
      entity_id: sensor.office_pm_2_5
      value_template: "{{ states('sensor.office_pm_2_5') | int }}"
      below: 5
      for:
        minutes: 10
    condition:
      - condition: state
        entity_id: 'fan.xiaomi_air_purifier_office'
        state: 'on'
    action:
      service: fan.turn_off
      entity_id: fan.xiaomi_air_purifier_office
  - alias: Turn on air purifier office
    trigger:
      platform: numeric_state
      entity_id: sensor.office_pm_2_5
      value_template: "{{ states('sensor.office_pm_2_5') | int }}"
      above: 5
      for:
        minutes: 5
    condition:
      - condition: state
        entity_id: 'fan.xiaomi_air_purifier_office'
        state: 'off'
    action:
      - service: fan.turn_on
        entity_id: fan.xiaomi_air_purifier_office
      - service: fan.speed
        entity_id: fan.xiaomi_air_purifier_office
        data:
          speed: auto
  - alias: Set favorite fan speed for air purifier office
    trigger:
      platform: time_pattern
      minutes: '/5'
      seconds: 00
    condition:
      - condition: state
        entity_id: 'fan.xiaomi_air_purifier_office'
        state: 'on'
    action:
      - service: fan.set_speed
        entity_id: fan.xiaomi_air_purifier_office
        data:
          speed: favorite
      - service: fan.xiaomi_miio_set_favorite_level
        data_template:
          entity_id: fan.xiaomi_air_purifier_office
          level: >
              {% if states('sensor.office_pm_2_5') | int > 30 or states('sensor.office_pm_10') | int > 50 %}
                10
              {% elif states('sensor.office_pm_2_5') | int > 20 or states('sensor.office_pm_10') | int > 40 %}
                5
              {% elif states('sensor.office_pm_2_5') | int > 10 or states('sensor.office_pm_10') | int > 20 %}
                3
              {% elif states('sensor.office_pm_2_5') | int > 5 or states('sensor.office_pm_10') | int > 10 %}
                1
              {% else %}
                0
              {% endif %}
      - service: input_select.select_option
        data_template:
          entity_id: input_select.xiaomi_air_purifier_mode_office
          option: '{{ states.fan.xiaomi_air_purifier_office.attributes.mode }}'
      - service: input_number.set_value
        data_template:
          entity_id: input_number.xiaomi_air_purifier_favorite_level_office
          value: '{{ states.fan.xiaomi_air_purifier_office.attributes.favorite_level }}'

For anyone that runs into:

Service not found for call_service at pos 1: Unable to find service fan/xiaomi_miio_set_favorite_level

Just rename fan.xiaomi_miio_* to xiaomi_miio.fan_* and everything should be OK.

2 Likes

I coud not get the fan available after the 0.103 update (breaking change in xiaomi_miio). I cannot understand what I have to change… Am I wrong in something?

fan:
  - platform: xiaomi_miio
    host: 192.168.0.xx
    token: "xxxxxxxxxxxxxx"
    model: zhimi.airpurifier.v2
    name: "Purificatore"

There is no need to change this. You only have to change service calls.

Appaer more errors with Buzzer, Led, and Lock Child

Did anyone came into issue that automation for setting Air Purifier favorite level (speed) does not work unless it is triggered manually?

You sure you have all automations enabled?