Lovelace: Xiaomi - Mi air purifier 3H card

! Update April 26th @bjorn.sivertsen minimalistic card Lovelace: Xiaomi - Mi air purifier 3H card - #344 by bjorn.sivertsen

! Update December 4th 2020 - new interface, configuration on the post below:

fav on off

! Update December 1st 2020 - nice graphical interface from @Tomaae, with new automation for nigh hours, leaving home or open window, but some of the functions removed like attributes and coverage area dependency to favorite.

! Update June 10th 2020 - simplified card below:

Hi guys! I have used the previous work on 2S and other purifiers to create an exhaustive card for 3H. Next step is to translate in something more compact.

6 Likes

Can you write how you got it all? What configuration?

configuration.yaml

#======================#
#  Mi Air Purifier 3H  #
#======================#
fan:
  - platform: xiaomi_miio
    name: "Mi Air Purifier 3H"
    host: !secret mi_air_host
    token: !secret mi_air_token
    model: zhimi.airpurifier.mb3
input_select:
    mi_air_purifier_mode:
      name: Mode
      options:
        - Auto
        - Silent
        - Favorite
        - Fan
      initial: Auto
      icon: mdi:animation-outline
    mi_air_purifier_coverage:
      name: Coverage
      options:
        - 2-4 mĀ²
        - 5-9 mĀ²
        - 7-12 mĀ²
        - 12-22 mĀ²
        - 14-24 mĀ²
        - 14-25 mĀ²
        - 16-27 mĀ²
        - 17-30 mĀ²
        - 18-32 mĀ²
        - 19-33 mĀ²
        - 20-34 mĀ²
        - 22-38 mĀ²
        - 23-40 mĀ²
        - 26-45 mĀ²
        - 28-49 mĀ²
      initial: 2-4 mĀ²
      icon: mdi:texture-box
input_number:
    mi_air_purifier_favorite_level:
      name: "Favorite level"
      initial: 0
      min: 0
      max: 14
      step: 1
      icon: mdi:weather-windy
    mi_air_purifier_fan_level:
      name: "Fan level"
      initial: 1
      min: 1
      max: 3
      step: 1
      icon: mdi:jquery
input_boolean:
    mi_air_purifier_attributes:
      name: Attributes
      icon: mdi:pencil

switch:
  - platform: template
    switches:
      mi_air_purifier_led:
        friendly_name: "Display"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'led', true) }}"
        turn_on:
          service: xiaomi_miio.fan_set_led_on
          data:
            entity_id: fan.mi_air_purifier_3h
        turn_off:
          service: xiaomi_miio.fan_set_led_off
          data:
            entity_id: fan.mi_air_purifier_3h
        icon_template: mdi:lightbulb-outline
      mi_air_purifier_child_lock:
        friendly_name: "Child lock"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'child_lock', true) }}"
        turn_on:
          service: xiaomi_miio.fan_set_child_lock_on
          data:
            entity_id: fan.mi_air_purifier_3h
        turn_off:
          service: xiaomi_miio.fan_set_child_lock_off
          data:
            entity_id: fan.mi_air_purifier_3h
        icon_template: "mdi:lock-outline"        
      mi_air_purifier_buzzer:
        friendly_name: "Buzzer"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'buzzer', true) }}"
        turn_on:
          service: xiaomi_miio.fan_set_buzzer_on
          data:
            entity_id: fan.mi_air_purifier_3h
        turn_off:
          service: xiaomi_miio.fan_set_buzzer_off
          data:
            entity_id: fan.mi_air_purifier_3h
        icon_template: "mdi:volume-high"

sensor:
  - platform: template
    sensors:
      mi_air_purifier_temp:
        friendly_name: "Temperature"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'temperature') }}"
        unit_of_measurement: "Ā°C"
        device_class: "temperature"
      mi_air_purifier_humidity:
        friendly_name: "Humidity"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'humidity') }}"
        unit_of_measurement: "%"
        device_class: "humidity"
      mi_air_purifier_air_quality_pm25:
        friendly_name: "Air quality (AQI) PM2.5"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'aqi') }}"
        unit_of_measurement: "Ī¼g/mĀ³"
        icon_template: "mdi:weather-fog"
      mi_air_purifier_avg_air_quality_pm25:
        friendly_name: "Average air quality (AvgAQI) PM2.5"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'average_aqi') }}"
        unit_of_measurement: "Ī¼g/mĀ³"
        icon_template: "mdi:weather-hazy"
      mi_air_purifier_use_time:
        friendly_name: "Time used"
        value_template: >
            {% macro phrase(value, name) %}
            {%- set value = value | int %}
            {%- set end = 's' if value > 1 else '' %}
            {{- '{} {}{}'.format(value, name, end) if value | int > 0 else '' }}
            {%- endmacro %}
            
            {% set weeks = (state_attr('fan.mi_air_purifier_3h', 'use_time') | int / 604800) | int %}
            {% set days = ((state_attr('fan.mi_air_purifier_3h', 'use_time') | int - (weeks * 604800)) / 86400) | int %}
            {% set hours = ((state_attr('fan.mi_air_purifier_3h', 'use_time') | int - (weeks * 604800) - (days * 86400)) / 3600) | int %}
            {% set minutes = ((state_attr('fan.mi_air_purifier_3h', 'use_time') | int - (weeks * 604800) - (days * 86400) - (hours * 3600)) / 60) | int %}
            {% set seconds = (state_attr('fan.mi_air_purifier_3h', 'use_time') | int - (weeks * 604800) - (days * 86400) - (hours * 3600) - (minutes*60)) | int %}
            {{ [ phrase(weeks, 'week'), phrase(days, 'day'), phrase(hours, 'hr'), phrase(minutes, 'min'), phrase(seconds, 'sec') ] | select('!=','') | list | join(', ') }}
        icon_template: "mdi:heart-pulse"
      mi_air_purifier_filter_used:
        friendly_name: "Filter used"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'filter_hours_used') }}"
        unit_of_measurement: "hrs"
        icon_template: "mdi:heart-off"
      mi_air_purifier_filter_remaining:
        friendly_name: "Filter remaining"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'filter_life_remaining') }}"
        unit_of_measurement: "%"
        icon_template: "mdi:heart-outline"
      mi_air_purifier_purify_volume:
        friendly_name: "Purified volume"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'purify_volume') }}"
        unit_of_measurement: "mĀ³"
        icon_template: "mdi:warehouse"
      mi_air_purifier_speed:
        friendly_name: "Motor speed"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'motor_speed') }}"
        unit_of_measurement: "rpm"
        icon_template: "mdi:speedometer"
      mi_air_purifier_led_brightness:
        friendly_name: "Led brightness"
        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'led_brightness') }}"
        unit_of_measurement: "lx"
        icon_template: "mdi:brightness-5"

automation.yaml

- id: 'xxxxxxxxxxxxx'
  alias: Air Purifier mode change
  description: ''
  trigger:
  - entity_id: input_select.mi_air_purifier_mode
    platform: state
  condition: []
  action:
  - service: fan.set_speed
    entity_id: fan.mi_air_purifier_3h
    data_template:
      speed: '{{ states(''input_select.mi_air_purifier_mode'') }}'
- id: 'xxxxxxxxxxxxx'
  alias: Air Purifier mode update
  description: ''
  trigger:
  - entity_id: fan.mi_air_purifier_3h
    platform: state
  condition: []
  action:
  - service: input_select.select_option
    entity_id: input_select.mi_air_purifier_mode
    data_template:
      option: '{{ state_attr(''fan.mi_air_purifier_3h'', ''speed'') }}'
- id: 'xxxxxxxxxxxxx'
  alias: Air Purifier favorite level change
  description: ''
  trigger:
  - entity_id: input_number.mi_air_purifier_favorite_level
    platform: state
  condition: []
  action:
  - data_template:
      level: '{{ states(''input_number.mi_air_purifier_favorite_level'') | int }}'
    entity_id: fan.mi_air_purifier_3h
    service: xiaomi_miio.fan_set_favorite_level
- id: 'xxxxxxxxxxxxx'
  alias: Air Purifier favorite level update
  description: ''
  trigger:
  - entity_id: fan.mi_air_purifier_3h
    platform: state
  condition: []
  action:
  - data_template:
      value: '{{ state_attr(''fan.mi_air_purifier_3h'', ''favorite_level'') | int
        }}'
    entity_id: input_number.mi_air_purifier_favorite_level
    service: input_number.set_value
- id: 'xxxxxxxxxxxxx'
  alias: Air Purifier fan level change
  description: ''
  trigger:
  - entity_id: input_number.mi_air_purifier_fan_level
    platform: state
  condition: []
  action:
  - service: xiaomi_miio.fan_set_fan_level
    entity_id: fan.mi_air_purifier_3h
    data_template:
      level: '{{ states(''input_number.mi_air_purifier_fan_level'') | int }}'
- id: 'xxxxxxxxxxxxx'
  alias: Air Purifier fan level update
  description: ''
  trigger:
  - entity_id: fan.mi_air_purifier_3h
    platform: state
  condition: []
  action:
  - service: input_number.set_value
    entity_id: input_number.mi_air_purifier_fan_level
    data_template:
      value: '{{ state_attr(''fan.mi_air_purifier_3h'', ''fan_level'') | int }}'
- id: 'xxxxxxxxxxxxx'
  alias: Air Purifier translate favorite level to coverage
  description: ''
  trigger:
  - entity_id: input_number.mi_air_purifier_favorite_level
    platform: state
  condition: []
  action:
  - data_template:
      option: >
        {% if is_state("input_number.mi_air_purifier_favorite_level", "0.0") -%}
        2-4 mĀ²
        {%- elif is_state("input_number.mi_air_purifier_favorite_level", "1.0") -%}
        5-9 mĀ²
        {%- elif is_state("input_number.mi_air_purifier_favorite_level", "2.0") -%}
        7-12 mĀ²
        {%- elif is_state("input_number.mi_air_purifier_favorite_level", "3.0") -%}
        12-22 mĀ²
        {%- elif is_state("input_number.mi_air_purifier_favorite_level", "4.0") -%}
         14-24 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "5.0") -%}
         14-25 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "6.0") -%}
         16-27 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "7.0") -%}
         17-30 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "8.0") -%}
         18-32 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "9.0") -%}
         19-33 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "10.0") -%}
         20-34 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "11.0") -%}
         22-38 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "12.0") -%}
         23-40 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "13.0") -%}
         26-45 mĀ²
         {%- elif is_state("input_number.mi_air_purifier_favorite_level", "14.0") -%}
         28-49 mĀ²
         {%- else -%}
         {%- endif %}
    entity_id: input_select.mi_air_purifier_coverage
    service: input_select.select_option
- id: 'xxxxxxxxxxxxx'
  alias: 'Air Purifier translate coverage to favorite level '
  description: ''
  trigger:
  - entity_id: input_select.mi_air_purifier_coverage
    platform: state
  condition: []
  action:
  - data_template:
      value: >
        {% if is_state("input_select.mi_air_purifier_coverage", "2-4 mĀ²") -%}
        0 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "5-9 mĀ²") -%}
        1 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "7-12 mĀ²") -%}
        2 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "12-22 mĀ²") -%}
        3 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "14-24 mĀ²") -%}
        4 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "14-25 mĀ²") -%}
        5 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "16-27 mĀ²") -%}
        6 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "17-30 mĀ²") -%}
        7 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "18-32 mĀ²") -%}
        8 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "19-33 mĀ²") -%}
        9 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "20-34 mĀ²") -%}
        10 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "22-38 mĀ²") -%}
        11 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "23-40 mĀ²") -%}
        12 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "26-45 mĀ²") -%}
        13 
        {%- elif is_state("input_select.mi_air_purifier_coverage", "28-49 mĀ²") -%}
        14 
        {%- else -%}
        {%- endif %}
    entity_id: input_number.mi_air_purifier_favorite_level
    service: input_number.set_value
- id: 'XXXXXXXXXXXXX'
  alias: Air Purifier turn off
  description: ''
  trigger:
  - below: '5'
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    for: 0:05:00
    platform: numeric_state
  condition:
  - condition: state
    entity_id: fan.mi_air_purifier_3h
    state: 'on'
  action:
  - data: {}
    entity_id: fan.mi_air_purifier_3h
    service: fan.turn_off
- id: 'XXXXXXXXXXXXX'
  alias: Air Purifier turn on
  description: ''
  trigger:
  - above: '15'
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    for: 0:05:00
    platform: numeric_state
  condition:
  - condition: state
    entity_id: fan.mi_air_purifier_3h
    state: 'off'
  action:
  - data: {}
    entity_id: fan.mi_air_purifier_3h
    service: fan.turn_on
  - data_template:
      speed: Auto
    entity_id: fan.mi_air_purifier_3h
    service: fan.set_speed
- id: 'XXXXXXXXXXXXX'
  alias: Air Purifier set favorite level based on PM2.5
  description: ''
  trigger:
  - minutes: '5'
    platform: time_pattern
  condition:
  - condition: state
    entity_id: fan.mi_air_purifier_3h
    state: 'on'
  action:
  - data_template:
      speed: Favorite
    entity_id: fan.mi_air_purifier_3h
    service: fan.set_speed
  - data_template:
      level: >
        {% if states("sensor.mi_air_purifier_air_quality_pm25") | int > 50 -%}
        14
        {%- elif states("sensor.mi_air_purifier_air_quality_pm25") | int > 40 -%}
        12 
        {%- elif states("sensor.mi_air_purifier_air_quality_pm25") | int > 30 -%}
        9
        {%- elif states("sensor.mi_air_purifier_air_quality_pm25") | int > 20 -%}
        6
        {%- else -%}
        4
        {%- endif %}
    entity_id: fan.mi_air_purifier_3h
    service: xiaomi_miio.fan_set_favorite_level

lovelace.yaml

      - cards:
          - cards:
              - entity: sensor.mi_air_purifier_air_quality_pm25
                max: 200
                min: 0
                name: AQI PM2.5
                severity:
                  green: 0
                  red: 150
                  yellow: 75
                type: gauge
            type: horizontal-stack
          - entities:
              - entity: fan.mi_air_purifier_3h
              - entity: input_select.mi_air_purifier_mode
              - entity: input_number.mi_air_purifier_favorite_level
              - entity: input_select.mi_air_purifier_coverage
              - entity: input_number.mi_air_purifier_fan_level
              - type: divider
              - entity: sensor.mi_air_purifier_temp
              - entity: sensor.mi_air_purifier_humidity
              - entity: sensor.mi_air_purifier_air_quality_pm25
              - entity: sensor.mi_air_purifier_avg_air_quality_pm25
              - type: divider
              - entity: sensor.mi_air_purifier_use_time
              - entity: sensor.mi_air_purifier_filter_used
              - entity: sensor.mi_air_purifier_filter_remaining
              - entity: sensor.mi_air_purifier_purify_volume
              - type: divider
              - entity: sensor.mi_air_purifier_speed
              - entity: sensor.mi_air_purifier_led_brightness
              - type: divider
              - entity: switch.mi_air_purifier_led
              - entity: switch.mi_air_purifier_child_lock
              - entity: switch.mi_air_purifier_buzzer
            show_header_toggle: false
            type: entities
        type: vertical-stack
8 Likes

In automations what I have to enter instead of xxxxxxxx ? Ip or something else ?
Thank You :slight_smile:

some random ID of 13 digits ex ā€˜1234567891011ā€™. All of mine starts with 158. What you can do is to start a new automation in Configuration, save it and then edit it in File Editor.

Adding 3 more automations in the script related to operation besides the existing 8 related to settings.

Turn off if PM2.5 < 5 for 5 minutes
Turn on mode auto if PM2.5 > 15 for 5 minutes
Switch to favorite and select different levels according to PM2.5 level.

1 Like

Just wanted to drop a quick thanks, @schilea. Copied and edited it to my liking. If only HA would finally allow displaying cards or defining actions based on attributes.

Next up for me are door/window sensor automations so that the purifier doesnā€™t run when Iā€™m refreshing the flat.

Thanks @iophobia. I am having already 15 window/door sensors but never thought to link them with purifier. :(. They are linked with the boiler as heating is stopped when opening the window for more than 30 seconds and start again when closed more than 1 minute

Get a bunch of errors with your config:

2020-06-02 16:11:59 ERROR (MainThread) [homeassistant.components.automation] Air Purifier favorite level update: Error executing script. Invalid data for call_service at pos 1: expected float for dictionary value @ data['value']
2020-06-02 16:11:59 ERROR (MainThread) [homeassistant.components.automation] Air Purifier fan level update: Error executing script. Invalid data for call_service at pos 1: expected float for dictionary value @ data['value']
2020-06-02 16:11:59 WARNING (MainThread) [homeassistant.components.input_select] Invalid option: {{ state_attr('fan.mi_air_purifier_3h', 'speed') }} (possible options: Auto, Silent, Favorite, Fan)
2020-06-02 16:12:30 ERROR (MainThread) [homeassistant.components.automation] Air Purifier favorite level update: Error executing script. Invalid data for call_service at pos 1: expected float for dictionary value @ data['value']
2020-06-02 16:12:30 ERROR (MainThread) [homeassistant.components.automation] Air Purifier fan level update: Error executing script. Invalid data for call_service at pos 1: expected float for dictionary value @ data['value']
2020-06-02 16:12:30 WARNING (MainThread) [homeassistant.components.input_select] Invalid option: {{ state_attr('fan.mi_air_purifier_3h', 'speed') }} (possible options: Auto, Silent, Favorite, Fan)
2020-06-02 16:13:32 ERROR (MainThread) [homeassistant.components.automation] Air Purifier favorite level update: Error executing script. Invalid data for call_service at pos 1: expected float for dictionary value @ data['value']
2020-06-02 16:13:32 ERROR (MainThread) [homeassistant.components.automation] Air Purifier fan level update: Error executing script. Invalid data for call_service at pos 1: expected float for dictionary value @ data['value']
2020-06-02 16:13:32 WARNING (MainThread) [homeassistant.components.input_select] Invalid option: {{ state_attr('fan.mi_air_purifier_3h', 'speed') }} (possible options: Auto, Silent, Favorite, Fan)

Seems the automation is not working properly in your case. Share your configuration.

Seems like i found the solution.
The Automations get everytime buggy if i reload the automations from file editor or make a new automation with the editor :frowning: !

In all the automations you should use data_template instead of data.

I still get these errors after i start it manually! I copied everything one by one and compared it!

2020-06-04 18:45:21 WARNING (MainThread) [homeassistant.components.input_select] Invalid option: None (possible options: Auto, Silent, Favorite, Fan)

What does the option ā€œFanā€ (besides Auto, Favorite, Silent) do?

Fan actualy is Manual mode from the app. The 3 levels.

Ok! But i dont understand whats wrong! Everything is copied 1:1 from your config and i get errors!

I think you can ignore this error, it is just an warning that input select it is not initialized with a value. Once starting the purifier and selecting something it will update automatically.
I think in configuration the initial value is missing for input select like for example: initial: Auto.
There are still other errors?

Hi Mike, today, after upgrading the operation system and supervisor, I experienced a lot of issues with purifier. The purifier unavailable or connect and then lost connectivity. Besides this a very long time to load the configuration and major disk usage increase on my Pi. Seems that the HA guys totally screwed up with this release.

It seems like changing coverage area doesnā€™t have any bearing on favourite level/Mode.

Also just played around with switching up and down the Favourite level on Pi 3B+ and the thing went ballistic with the favourite level constantly switching up and down, eventually crashing the Pi

Hi @emmpii the coverage area should work. Just tested now. There is an automation that updates the favorite level accordingly but this is very sensitive to alignment. Trial on my own. What you can do is to compare exactly your automation with the one from above.
Regarding switching up and down the Favorite level, I also notice this after the upgrade (I have a Pi4) but not with Pi crushing, rather have to restart the HA. My guess is that there is a time for HA to sync with the purifier and in the same time there are some automation that sync purifier with the HA entities. If you change them too fast the purifier is not keeping up with the values change and tries back to update the HA entities to its values. Simply it enters in a loop.

Iā€™ve literally copied everything youā€™ve got. I can see the favourite level updates when you change the coverage but the mode doesnā€™t get set to favourite so the command never gets sent to the purifier