Lovelace: Xiaomi - Mi air purifier 3H card

Something is wrong with automations (Purifier 3H). When I change mode from eg. Auto to favourite it changes for a second and then changes to fan. This is what I see in log:

Mi Air Purifier 3H mode change init has been triggered by state of input_select.mi_air_purifier_3h_mode
10:32:19 - 3 minuty temu
Mi Air Purifier 3H mode change update has been triggered by state of fan.mi_air_purifier_3h
10:32:19 - 3 minuty temu
Mi Air Purifier 3H fan level change init has been triggered by state of input_number.mi_air_purifier_3h_fan_level
10:32:18 - 3 minuty temu
Mi Air Purifier 3H fan level change update has been triggered by state of fan.mi_air_purifier_3h
10:32:18 - 3 minuty temu

Look like both automations fire one by one…

Changing mode is changing fan level and we have chain reaction…

Hello,

Work like a charm but really difficult for my first integration … But i’m happy now. Thanks a lot !

@check I can see the only difference are the conditions, like the one bellow, you added on 3 automations. I will give them a try and let you know my thoughts.

@majkers I think this is how Purifier implements changes in Favorite level. When going through different favorite levels it changes also the fan level. The lowest ones are with FAN level 1 and the highest ones with FAN level 3. So the automations see also a FAN changes and reacts accordingly. I have also to think about on how not to run automation when Favorite level changed.

I just updated my initial post where I added checks for 2S in automations. It works flawlessly without bloating the log when device sends status update.

I’ve had lot’s of issues with purifiers this year and finally I can say it’s working right.

1 Like

Thanks all for sharing your job. Was very helpful to me.

I have been reworking this system until I got something more compact. It also solves all problems with state ping-pong I had due to 3H connectivity issues.

Added automation:

  • Recognizes open door and window and pauses/resumes purifier (wont turn on purifier while they are open)
  • Recognizes presence, stops purifier after leaving and wont start until returning home
  • Clean before returning home, based on time of a day and presence
  • Follows day and night hours
  • Faster reaction to PM changes, and even faster with external PM sensor

You may remove all time_pattern triggers if your purifier wifi is reliable.

image
image
image

/config/integrations/airpurifier.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_number:
    mi_air_purifier_favorite_level:
      name: "Favorite level"
      initial: 0
      min: 0
      max: 14
      step: 1
      icon: mdi:weather-windy

input_boolean:
    mi_air_purifier_paused:
      name: Air Purifier Paused
      initial: off
      icon: mdi:air-filter

binary_sensor:
  - platform: template
    sensors:
      mi_air_purifier_mode_off:
        friendly_name: "Purifier Off"
        value_template: "{{ is_state('fan.mi_air_purifier_3h', 'off') }}"
      mi_air_purifier_mode_auto:
        friendly_name: "Purifier Auto mode"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'mode', 0) and is_state('fan.mi_air_purifier_3h', 'on') }}"
      mi_air_purifier_mode_silent:
        friendly_name: "Purifier Silent mode"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'mode', 1) and is_state('fan.mi_air_purifier_3h', 'on') }}"
      mi_air_purifier_mode_favorite:
        friendly_name: "Purifier Favorite mode"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'mode', 2) and is_state('fan.mi_air_purifier_3h', 'on') }}"
      mi_air_purifier_mode_fan:
        friendly_name: "Purifier Fan mode"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'mode', 3) and is_state('fan.mi_air_purifier_3h', 'on') }}"
      mi_air_purifier_mode_fan_1:
        friendly_name: "Purifier Fan mode 1"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'mode', 3) and is_state_attr('fan.mi_air_purifier_3h', 'fan_level', 1) and is_state('fan.mi_air_purifier_3h', 'on') }}"
      mi_air_purifier_mode_fan_2:
        friendly_name: "Purifier Fan mode 2"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'mode', 3) and is_state_attr('fan.mi_air_purifier_3h', 'fan_level', 2) and is_state('fan.mi_air_purifier_3h', 'on') }}"
      mi_air_purifier_mode_fan_3:
        friendly_name: "Purifier Fan mode 3"
        value_template: "{{ is_state_attr('fan.mi_air_purifier_3h', 'mode', 3) and is_state_attr('fan.mi_air_purifier_3h', 'fan_level', 3) and is_state('fan.mi_air_purifier_3h', 'on') }}"


sensor:
  - platform: template
    sensors:
      mi_air_purifier_air_quality_pm25:
        friendly_name: "Air quality (AQI) PM2.5"
        value_template: >
           {% set value = state_attr('fan.mi_air_purifier_3h', 'aqi') %}
           {% if state_attr('fan.mi_air_purifier_3h', 'aqi') == None %}
            {{ 0 }}
           {%- else -%}
            {{ value }}
           {% endif %}           
        unit_of_measurement: "μg/m³"
        icon_template: "mdi:weather-fog"

/config/automations/airpurifier.yaml

#==============================================#
#  Air Purifier favorite level change
#==============================================#
- id: air_purifier_favorite_level_change
  alias: Air Purifier favorite level change
  description: ''
  trigger:
  - entity_id: input_number.mi_air_purifier_favorite_level
    platform: state
  condition:
    - condition: state
      entity_id: binary_sensor.mi_air_purifier_mode_favorite
      state: 'on'
  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
#==============================================#
#  Air Purifier favorite level change
#==============================================#
- id: air_purifier_favorite_level_update
  alias: Air Purifier favorite level change
  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
#==============================================#
#  Air Purifier set favorite level based on PM2.5
#==============================================#
- id: air_purifier_set_favorite_level_based_on_PM2.5
  alias: Air Purifier set favorite level based on PM2.5
  description: ''
  trigger:
  - platform: numeric_state
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    above: '50'
  - platform: numeric_state
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    below: '50'
  - platform: numeric_state
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    below: '40'
  - platform: numeric_state
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    below: '30'
  - platform: numeric_state
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    below: '20'
  - platform: numeric_state
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    below: '10'
  - minutes: '/2'
    platform: time_pattern
  condition:
    - condition: state
      entity_id: binary_sensor.mi_air_purifier_mode_favorite
      state: 'on'
  action:
  - data_template:
      level: >
        {% if states("sensor.mi_air_purifier_air_quality_pm25") | int > 50 -%}
        11
        {%- elif states("sensor.mi_air_purifier_air_quality_pm25") | int > 40 -%}
        9
        {%- elif states("sensor.mi_air_purifier_air_quality_pm25") | int > 30 -%}
        8
        {%- elif states("sensor.mi_air_purifier_air_quality_pm25") | int > 20 -%}
        6
        {%- elif states("sensor.mi_air_purifier_air_quality_pm25") | int > 10 -%}
        4
        {%- else -%}
        2
        {%- endif %}
    entity_id: fan.mi_air_purifier_3h
    service: xiaomi_miio.fan_set_favorite_level
#==============================================#
#  PAUSE
#==============================================#
- id: air_purifier_pause
  alias: Air Purifier pause
  description: ''
  trigger:
  - platform: state
    entity_id: binary_sensor.door_contact_sensor_contact
    from: 'off'
    to: 'on'
  - platform: state
    entity_id: binary_sensor.window_contact_sensor_contact
    from: 'off'
    to: 'on'
  condition:
  - condition: and
    conditions:
    - condition: state
      entity_id: input_boolean.mi_air_purifier_paused
      state: 'off'
    - condition: state
      entity_id: fan.mi_air_purifier_3h
      state: 'on'
  action:
  - service: input_boolean.turn_on
    data: {}
    entity_id: input_boolean.mi_air_purifier_paused
  - service: fan.turn_off
    data: {}
    entity_id: fan.mi_air_purifier_3h
  mode: single
#==============================================#
#  RESUME
#==============================================#
- id: air_purifier_resume
  alias: Air Purifier resume
  description: ''
  trigger:
  - platform: state
    entity_id: binary_sensor.door_contact_sensor_contact
    from: 'on'
    to: 'off'
  - platform: state
    entity_id: binary_sensor.window_contact_sensor_contact
    from: 'on'
    to: 'off'
  condition:
  - condition: and
    conditions:
    - condition: state
      entity_id: binary_sensor.window_contact_sensor_contact
      state: 'off'
    - condition: state
      entity_id: binary_sensor.door_contact_sensor_contact
      state: 'off'
    - condition: state
      entity_id: input_boolean.mi_air_purifier_paused
      state: 'on'
  action:
  - service: input_boolean.turn_off
    data: {}
    entity_id: input_boolean.mi_air_purifier_paused
  - service: fan.turn_on
    data: {}
    entity_id: fan.mi_air_purifier_3h
  mode: single
#==============================================#
#  LEAVE HOME
#==============================================#
- id: air_purifier_leave_home
  alias: Air Purifier leave home
  description: ''
  trigger:
  - platform: state
    from: home
    to: not_home
    entity_id: device_tracker.shiis_iphone
  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
  mode: single
#==============================================#
#  RETURN HOME
#==============================================#
# - id: air_purifier_return_home
#   alias: Air Purifier return home
#   description: ''
#   trigger:
#   - platform: state
#     from: not_home
#     to: home
#     entity_id: device_tracker.shiis_iphone
#   condition:
#   - condition: state
#     entity_id: fan.mi_air_purifier_3h
#     state: 'off'
#   action:
#   - service: fan.set_speed
#     entity_id: fan.mi_air_purifier_3h
#     data_template:
#       speed: 'Silent'
#   mode: single
#==============================================#
#  PRE-CLEAN BEFORE RETURNING HOME
#==============================================#
- id: air_purifier_pre-clean
  alias: Air Purifier pre-clean
  description: ''
  trigger:
  - platform: time
    at: '15:00'
  condition:
  - condition: and
    conditions:
    - condition: state
      entity_id: device_tracker.shiis_iphone
      state: not_home
    - condition: state
      entity_id: input_boolean.mi_air_purifier_paused
      state: 'off'
  action:
  - service: fan.set_speed
    entity_id: fan.mi_air_purifier_3h
    data_template:
      speed: 'Favorite'
  mode: single
#==============================================#
#  CLEAN AFTER CLOSING WINDOWS
#==============================================#
# - id: air_purifier_auto-clean_window
#   alias: Air Purifier auto-clean window
#   description: ''
#   trigger:
#   - platform: state
#     entity_id: binary_sensor.window_contact_sensor_contact
#     from: 'on'
#     to: 'off'
#   condition:
#   - condition: and
#     conditions:
#     - condition: time
#       after: '8:00'
#       before: '23:00'
#     - condition: state
#       entity_id: input_boolean.mi_air_purifier_paused
#       state: 'off'
#     - condition: state
#       entity_id: fan.mi_air_purifier_3h
#       state: 'off'
#   action:
#   - service: fan.set_speed
#     entity_id: fan.mi_air_purifier_3h
#     data_template:
#       speed: 'Favorite'
#   mode: single
#==============================================#
#  CLEAN ABOVE 50 PM25
#==============================================#
- id: air_purifier_auto-clean
  alias: Air Purifier auto-clean
  description: ''
  trigger:
  - platform: numeric_state
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    above: '50'
    for: 00:01:00
  - platform: time_pattern
    minutes: '/5'
  condition:
  - condition: and
    conditions:
    - condition: time
      after: '8:00'
      before: '23:00'
    - condition: state
      entity_id: device_tracker.shiis_iphone
      state: home
    - condition: state
      entity_id: input_boolean.mi_air_purifier_paused
      state: 'off'
    - condition: state
      entity_id: binary_sensor.window_contact_sensor_contact
      state: 'off'
    - condition: state
      entity_id: binary_sensor.door_contact_sensor_contact
      state: 'off'
    - condition: state
      entity_id: binary_sensor.headset_presence
      state: 'on'
    - condition: state
      entity_id: binary_sensor.mi_air_purifier_mode_fan
      state: 'off'
    - condition: numeric_state
      entity_id: sensor.mi_air_purifier_air_quality_pm25
      above: 50
  action:
  - service: fan.set_speed
    entity_id: fan.mi_air_purifier_3h
    data_template:
      speed: 'Favorite'
  - service: xiaomi_miio.fan_set_favorite_level
    entity_id: fan.mi_air_purifier_3h
    data_template:
      level: 11
  mode: single
#==============================================#
#  SWITCH TO SILENT AT 10 PM25
#==============================================#
- id: air_purifier_auto-clean_silent
  alias: Air Purifier auto-clean silent
  description: ''
  trigger:
  - platform: numeric_state
    entity_id: sensor.mi_air_purifier_air_quality_pm25
    below: '10'
    for: 00:01:00
  - platform: time_pattern
    minutes: '/5'
  condition:
  - condition: and
    conditions:
    - condition: state
      entity_id: fan.mi_air_purifier_3h
      state: 'on'
    - condition: numeric_state
      entity_id: sensor.mi_air_purifier_air_quality_pm25
      below: '10'
    - condition: state
      entity_id: binary_sensor.mi_air_purifier_mode_favorite
      state: 'on'
  action:
#   - service: fan.set_speed
#     entity_id: fan.mi_air_purifier_3h
#     data_template:
#       speed: 'Silent'
  - service: fan.turn_off
    data: {}
    entity_id: fan.mi_air_purifier_3h
  mode: single
#==============================================#
#  SWITCH TO SILENT FOR SLEEP
#==============================================#
- id: air_purifier_sleep
  alias: Air Purifier sleep
  description: ''
  trigger:
  - platform: numeric_state
    entity_id: sensor.pc_power
    below: '30'
  condition:
  - condition: and
    conditions:
    - condition: time
      after: '23:00'
      before: '08:00'
    - condition: state
      entity_id: input_boolean.mi_air_purifier_paused
      state: 'off'
    - condition: state
      entity_id: fan.mi_air_purifier_3h
      state: 'on'
  action:
#   - service: fan.set_speed
#     entity_id: fan.mi_air_purifier_3h
#     data_template:
#       speed: 'Silent'
  - service: fan.turn_off
    data: {}
    entity_id: fan.mi_air_purifier_3h
  mode: single

Dashboard

type: vertical-stack
cards:
  - type: picture-elements
    image: local/images/mi-air-purifier-3h.png
    elements:
      - type: state-label
        entity: fan.mi_air_purifier_3h
        attribute: aqi
        style:
          top: 21%
          left: 73.3%
          min-width: 40px
          min-height: 40px
          text-align: center
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(0, 2, 0)'
          border-radius: 20px
      - type: image
        entity: fan.mi_air_purifier_3h
        state_image:
          'on': local/images/mi-air-purifier-3h_on.png
          'off': local/images/mi-air-purifier-3h_off.png
        style:
          top: 38%
          left: 73.3%
      - type: state-label
        entity: fan.mi_air_purifier_3h
        attribute: average_aqi
        prefix: 'Average PM2.5 '
        suffix: ' μg/m³'
        style:
          top: 12%
          left: 60%
          transform: 'translate(-100%, 0)'
          text-shadow: '1px 1px 2px black, 0 0 25px rgb(41, 41, 41)'
      - type: state-label
        entity: fan.mi_air_purifier_3h
        attribute: filter_hours_used
        prefix: 'Filter used '
        suffix: ' hrs'
        style:
          top: 25%
          left: 60%
          transform: 'translate(-100%, 0)'
          text-shadow: '1px 1px 2px black, 0 0 25px rgb(41, 41, 41)'
      - type: state-label
        entity: fan.mi_air_purifier_3h
        attribute: filter_life_remaining
        prefix: 'Filter remaining '
        suffix: '%'
        style:
          top: 38%
          left: 60%
          transform: 'translate(-100%, 0)'
          text-shadow: '1px 1px 2px black, 0 0 25px rgb(41, 41, 41)'
      - type: state-label
        entity: fan.mi_air_purifier_3h
        attribute: purify_volume
        prefix: 'Purified volume '
        suffix: ' m³'
        style:
          top: 51%
          left: 60%
          transform: 'translate(-100%, 0)'
          text-shadow: '1px 1px 2px black, 0 0 25px rgb(41, 41, 41)'
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_auto
        title: Auto
        icon: 'mdi:calendar-sync'
        tap_action:
          action: call-service
          service: fan.set_speed
          service_data:
            entity_id: fan.mi_air_purifier_3h
            speed: Auto
        style:
          top: 75%
          left: 6%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_silent
        title: Silent
        icon: 'mdi:fan-minus'
        tap_action:
          action: call-service
          service: fan.set_speed
          service_data:
            entity_id: fan.mi_air_purifier_3h
            speed: Silent
        style:
          top: 75%
          left: 16%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_favorite
        title: Favorite
        icon: 'mdi:fan-chevron-up'
        tap_action:
          action: call-service
          service: fan.set_speed
          service_data:
            entity_id: fan.mi_air_purifier_3h
            speed: Favorite
        style:
          top: 75%
          left: 26%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_fan_1
        title: Fan level 1
        icon: 'mdi:fan-speed-1'
        tap_action:
          action: call-service
          service: xiaomi_miio.fan_set_fan_level
          service_data:
            entity_id: fan.mi_air_purifier_3h
            level: 1
        style:
          top: 75%
          left: 36%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_fan_2
        title: Fan level 2
        icon: 'mdi:fan-speed-2'
        tap_action:
          action: call-service
          service: xiaomi_miio.fan_set_fan_level
          service_data:
            entity_id: fan.mi_air_purifier_3h
            level: 2
        style:
          top: 75%
          left: 46%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_fan_3
        title: Fan level 3
        icon: 'mdi:fan-speed-3'
        tap_action:
          action: call-service
          service: xiaomi_miio.fan_set_fan_level
          service_data:
            entity_id: fan.mi_air_purifier_3h
            level: 3
        style:
          top: 75%
          left: 56%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_off
        title: 'Off'
        icon: 'mdi:fan-off'
        tap_action:
          action: call-service
          service: fan.turn_off
          service_data:
            entity_id: fan.mi_air_purifier_3h
        style:
          top: 5%
          left: 6%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
  - card:
      entities:
        - entity: input_number.mi_air_purifier_favorite_level
      type: entities
    conditions:
      - entity: binary_sensor.mi_air_purifier_mode_favorite
        state: 'on'
    type: conditional

/config/www/images/

mi-air-purifier-3h_off mi-air-purifier-3h_on

Optional PM sensor
Hardware used:

  • wemos d1 mini
  • pms7003

Replace all instances of sensor.mi_air_purifier_air_quality_pm25 with sensor.bedroom_pm_pm_2_5um in automation file.

ESPHOME config:

substitutions:
  devicename: esp_env02
  human_devicename: Bedroom PM

esphome:
  name: $devicename
  platform: ESP8266
  board: d1_mini
  on_boot:
      - switch.turn_on: pms_set
      - delay: 3s
      - switch.turn_off: pms_set

#<<: !include common/common.yaml
<<: !include common/time.yaml
<<: !include common/wifi.yaml
<<: !include common/api.yaml
<<: !include common/ota.yaml
<<: !include common/web_server.yaml
<<: !include common/text_sensors.yaml
logger:
  level: DEBUG
  baud_rate: 0
  esp8266_store_log_strings_in_flash: false

interval:
  - interval: 60s
    then:
      - if:
          condition:
            - lambda: 'return id(purifier_favorite).state == false;'
          then:
            - switch.turn_on: pms_set
            - delay: 3s
            - switch.turn_off: pms_set

  - interval: 10s
    then:
      - if:
          condition:
            - lambda: 'return id(purifier_favorite).state == true;'
          then:
            - switch.turn_on: pms_set
            - delay: 3s
            - switch.turn_off: pms_set

sensor:
  - !include common/sensor/wifi-signal.yaml
  - !include common/sensor/uptime.yaml

  - platform: pmsx003
    type: PMSX003
    pm_1_0:
      name: ${human_devicename} PM 1.0µm
      icon: mdi:air-filter
      accuracy_decimals: 0
      filters:
        - throttle: 3s
        - delta: 1
    pm_2_5:
      name: ${human_devicename} PM 2.5µm
      icon: mdi:air-filter
      accuracy_decimals: 0
      filters:
        - throttle: 3s
        - delta: 1
    pm_10_0:
      name: ${human_devicename} PM 10.0µm
      icon: mdi:air-filter
      accuracy_decimals: 0
      filters:
        - throttle: 3s
        - delta: 1

binary_sensor:
  - !include common/binary_sensor/status.yaml
  
  - platform: homeassistant
    internal: true
    id: purifier_favorite
    entity_id: binary_sensor.mi_air_purifier_mode_favorite

switch:
  - !include common/switch/restart.yaml
  - platform: gpio
    internal: true
    pin: GPIO0
    id: pms_set

uart:
  tx_pin: GPIO1
  rx_pin: GPIO3
  baud_rate: 9600
2 Likes

Hi @Tomaae. Nice work and I have added it on the top of this topic. Indeed, calling a service instead of “double” automation (input to and from the purifier) could be very useful to avoid ping pong. Some of the automations are really interesting like silence during the night hours and off/on when a window is open. Also the work on graphical interface.
I am having some additional ideas and will try to add my input on your work on the next days. Could you also post a picture with favorite level selection?

Oh yeah, I forgot to add notes about extra automation I have.
I want to expand on them too. Like changing favorite level when enabling fav mode, instead of just time based and maybe reworking whole time based model to “above/bellow” triggers.
I’m also integrating separate PM sensor based on ESPHOME and pms7003 which will be located further away from my purifier so I can have it work more effectively. It is also far more responsive compared to build in one.

Favorite level selection is the same as yours, I have not yet found a way to integrate it into picture elements.

I have integrated a simplified version of yours to test functionalities. Indeed calling the service is better than “double” automation.
image
but I am now thinking on something a little bit different to present also the aqi colored. I have pictures ready but let see when I am going to have time to integrate them

MI-g MI-y MI-r MI

Oh yes, using services works reliably. Before I switched to services, I had previous settings return back about 80% of the time. But that could be issue with 3H and its unreliable wifi.
Using color for AQI is nice, I just used it to indicate on/off status. Its really simple to implement tho, if you want to use it in that way.
But if you use image so large, there may not be space for status text and control buttons without having it feel crowded.

Hi, I figured out how to add favorite and fun as conditional.
This is still working progress to add aqi colors, humidity and temperature like on purifier display.
Below is the work so far based on your template:
off
fan favorite silent auto

new sensor in configuration.yaml

sensor:
      mi_air_purifier_favorite_level:
        friendly_name: "Favorite Level"
#        value_template: "{{ state_attr('fan.mi_air_purifier_3h', 'favorite_level') }}"
        value_template: >
           {% if state_attr('fan.mi_air_purifier_3h', 'favorite_level') == None %}
            {{ 0 }}
           {%- else -%}
            {{ state_attr('fan.mi_air_purifier_3h', 'favorite_level') }}
           {% endif %}
        icon_template: "mdi:weather-windy"

Dashboard:

type: picture-elements
image: local/images/mi-air-purifier-3h.jpg
elements:
  - type: image
    entity: fan.mi_air_purifier_3h
    state_image:
      'on': local/images/mi-air-purifier-3h_on.jpg
      'off': local/images/mi-air-purifier-3h_off.jpg
    style:
      top: 32%
      left: 73.3%
  - type: state-icon
    entity: binary_sensor.mi_air_purifier_mode_off
    title: 'Off'
    icon: 'mdi:power-standby'
    tap_action:
      action: call-service
      service: fan.turn_off
      service_data:
        entity_id: fan.mi_air_purifier_3h
    style:
      top: 5%
      left: 6%
      transform: 'translate(-50%, 0)'
      background-color: 'rgb(41, 41, 41)'
      border-radius: 20px
  - type: state-label
    entity: fan.mi_air_purifier_3h
    attribute: aqi
    style:
      top: 21%
      left: 73.3%
      min-width: 40px
      min-height: 40px
      text-align: center
      transform: 'translate(-50%, 0)'
      color: 'rgb(255,255,255)'
      background-color: 'rgb(0, 2, 0)'
      border-radius: 20px
  - type: state-icon
    entity: binary_sensor.mi_air_purifier_mode_auto
    title: Auto
    icon: 'mdi:autorenew'
    tap_action:
      action: call-service
      service: fan.set_speed
      service_data:
        entity_id: fan.mi_air_purifier_3h
        speed: Auto
    style:
      top: 40%
      left: 6%
      transform: 'translate(-50%, 0)'
      background-color: 'rgb(41, 41, 41)'
      border-radius: 20px
  - type: state-icon
    entity: binary_sensor.mi_air_purifier_mode_silent
    title: Silent
    icon: 'mdi:power-sleep'
    tap_action:
      action: call-service
      service: fan.set_speed
      service_data:
        entity_id: fan.mi_air_purifier_3h
        speed: Silent
    style:
      top: 40%
      left: 16%
      transform: 'translate(-50%, 0)'
      background-color: 'rgb(41, 41, 41)'
      border-radius: 20px
  - type: state-icon
    entity: binary_sensor.mi_air_purifier_mode_favorite
    title: Favorite
    icon: 'mdi:heart'
    tap_action:
      action: call-service
      service: fan.set_speed
      service_data:
        entity_id: fan.mi_air_purifier_3h
        speed: Favorite
    style:
      top: 40%
      left: 26%
      transform: 'translate(-50%, 0)'
      background-color: 'rgb(41, 41, 41)'
      border-radius: 20px
  - type: state-icon
    entity: binary_sensor.mi_air_purifier_mode_fan
    title: Fan
    icon: 'mdi:fan'
    tap_action:
      action: call-service
      service: fan.set_speed
      service_data:
        entity_id: fan.mi_air_purifier_3h
        speed: Fan
    style:
      top: 40%
      left: 36%
      transform: 'translate(-50%, 0)'
      background-color: 'rgb(41, 41, 41)'
      border-radius: 20px
  - type: conditional
    conditions:
      - entity: binary_sensor.mi_air_purifier_mode_fan
        state: 'on'
    elements:
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_fan_1
        title: Fan level 1
        icon: 'mdi:fan-speed-1'
        tap_action:
          action: call-service
          service: xiaomi_miio.fan_set_fan_level
          service_data:
            entity_id: fan.mi_air_purifier_3h
            level: 1
        style:
          top: 70%
          left: 16%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_fan_2
        title: Fan level 2
        icon: 'mdi:fan-speed-2'
        tap_action:
          action: call-service
          service: xiaomi_miio.fan_set_fan_level
          service_data:
            entity_id: fan.mi_air_purifier_3h
            level: 2
        style:
          top: 70%
          left: 26%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: state-icon
        entity: binary_sensor.mi_air_purifier_mode_fan_3
        title: Fan level 3
        icon: 'mdi:fan-speed-3'
        tap_action:
          action: call-service
          service: xiaomi_miio.fan_set_fan_level
          service_data:
            entity_id: fan.mi_air_purifier_3h
            level: 3
        style:
          top: 70%
          left: 36%
          transform: 'translate(-50%, 0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
  - type: conditional
    conditions:
      - entity: binary_sensor.mi_air_purifier_mode_favorite
        state: 'on'
    elements:
      - type: state-label
        entity: sensor.mi_air_purifier_favorite_level
        style:
          top: 70%
          left: 26%
          min-width: 40px
          min-height: 40px
          text-align: center
          transform: 'translate(-50%, 0)'
          color: 'rgb(255,255,0)'
          background-color: 'rgb(0, 2, 0)'
          border-radius: 20px
      - type: icon
        icon: 'mdi:plus'
        tap_action:
          action: call-service
          service: input_number.increment
          service_data:
            entity_id: input_number.mi_air_purifier_favorite_level
        style:
          top: 75%
          left: 36%
          transform: 'translate(-50%, 0)'
          color: 'rgb(255,255,0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px
      - type: icon
        icon: 'mdi:minus'
        tap_action:
          action: call-service
          service: input_number.decrement
          service_data:
            entity_id: input_number.mi_air_purifier_favorite_level
        style:
          top: 75%
          left: 16%
          transform: 'translate(-50%, 0)'
          color: 'rgb(255,255,0)'
          background-color: 'rgb(41, 41, 41)'
          border-radius: 20px

Oh, thats a great idea, I will try it out too. Hopefully 3H will play nicely with it.
I have also implemented all those automation changes I mentioned. Its really smooth now and very fast. Specially with external sensor.
I have added code for that one too, but thats WIP as I want to add more sensors there. That wont have any effect on purifier tho.

PS: is new sensor really needed? cant it be handled by input_number.mi_air_purifier_favorite_level?

Haven’t figured out how to display the favourite value using input number as integer and not decimal like 4.0, 7.0. I was tried this also initially. So that was the ideea it came to me in order to display integer values.

I have tried to implement this, but it does not work well. If you want to increase by more then one at time, it will just rubberband to increase by 1. Not sure if we will be able to fix this, its probably limited by integration itself.

It is not working to increase by more than once at the time. Called services are increment and decrement number. If you want to increment more you have to integrate the slider in picture and then call set value. I haven’t figured out how to do this that’s why decided for baby steps.

Great work @Tomaae & @schilea!

I have an idea for an automation:

Sometimes my girlfriend likes to increase the fan speed to fully clean the room before heading to bed at various hours.

Now the automatic adjustment will decrease the fan speed due to PM 2.5 after a short time.

Could it be an idea to add a condition that only changes the speed, if there hasn’t been user input for the last 10 minutes etc?

Hi,
It will only adjust in favorite mode or when turned off. If you select any other mode, it wont be adjusted at all.

That is actually why I build my own PM sensor, so I can place it on other side of the room and get more accurate readings for automation.

New card output below. I am going to do some clean up than I will post the codes here. Please let me know your thoughts.

Off - all greyed out, including numerical info, except off button
off

On mode - numerical info white bright
Auto
auto

Silent
silent

Fan
fan
fan2

Favorite - increment/decrement is one step at the time, this is how integration works
favorite

3 colors according to predefined PM2.5 severity level: clean, polluted, critical, not visible when off
mi_air_purifier_3h_clean mi_air_purifier_3h_polluted mi_air_purifier_3h_critical mi_air_purifier_3h_off

Additional info:
Open attributes - Google 3 dots:

PM2.5 history - click on PM2.5 value:
aqi

PM2.5 severity level history:
pm2.5

Temperature history:

Humidity history:
humidity

Looks really nice. Left side feels bit empty, but that easily solvable. Probably just putting text “Purifier” between off and menu buttons would do it. But that can be experimented with later.
I would also try different background, since pure white is too similar to color of the purifier itself. Thats why I picked one with bit yellowish background. It makes it “pop out” more and is easier on eyes. But thats just pure visual thing.

Hi, I am also thinking on title, but picture-entity is adding it on top and I am not really like this. so it should be added as an object.

Screenshot 2020-12-02 221310

I personally prefer bright themes, as having the default one in Assistant, but as you said, it is just a matter of preference. The baseline white can be easily replace in any picture editor app.

Regarding the open space, I reserved it for some additional switches like child lock or display brightness as there were some requests in the past.