Fan control with set_preset_mode

I wrote this for my Philips air purifier.

It uses set_preset_mode instead of set_speed.

My devices has 6 preset:

  • 1
  • 2
  • auto
  • sleep
  • turbo
  • off

Result

Entities to create

this will add the entities need for the graph

  • air quality
  • voc
  • pm 25
  • allergen

configuration.yaml

sensor: !include sensor.yaml

sensor.yaml

#sensors:
- platform: template
  sensors:
    ac3033_rob_air_quality:
      friendly_name: "Qualità dell'aria"
      value_template: "{{ state_attr('fan.philips_airpurifier', 'air_quality_index') }}"
    ac3033_rob_allergen:
      friendly_name: "Indice allergeni"
      value_template: "{{ state_attr('fan.philips_airpurifier', 'indoor_allergen_index') }}"
    ac3033_rob_pm25:
      friendly_name: "Indice pm25"
      value_template: "{{ state_attr('fan.philips_airpurifier', 'pm25') }}"
    ac3033_rob_volatile_organic:
      friendly_name: "Materiale organico volatile"
      value_template: "{{ state_attr('fan.philips_airpurifier', 'total_volatile_organic_compounds') }}"

Files needed

mini-graph-card-bundle.js
can be installed via HACS or downloaded from here placed in /config/www/mini-graph-card

air-purifier-card.js
(code below) to be placed in /config/www/air-purifier-card/

class AirPurifierCard extends Polymer.Element {

    static get template() {
        return Polymer.html`
            <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
            <style>
                :host {
                    line-height: 1.5;
                }
                .grp_cmd_preset {
                    padding: 0 10px;
                    margin: 10px 0;
                }
                .cmd_preset {
                    float: right;
                    margin: 15px 0 0 10px;                    
                }

                .cmd_preset_icon {
                    padding: 8px 4px;
                    width: auto;
                    height: auto;
                }
        
                ha-entity-toggle {
                    margin-left: 10px;
                }
            </style>

             <div class='grid justified layout grp_cmd_preset' on-click="stopPropagation">

                <mwc-button
                    class='cmd_preset'
                    raised noink name="sleep"
                    on-click='setPreset'
                    disabled='[[_is_Sleep]]'><ha-icon icon="mdi:power-sleep" class="cmd_preset_icon"></ha-icon></mwc-button>

                <mwc-button
                    class='cmd_preset'
                    raised noink name="auto"
                    on-click='setPreset'
                    disabled='[[_is_Auto]]'><ha-icon icon="mdi:fan-auto" class="cmd_preset_icon"></ha-icon></mwc-button>       

                <mwc-button
                    class='cmd_preset'
                    raised noink name="turbo"
                    on-click='setPreset'
                    disabled='[[_is_Turbo]]'><ha-icon icon="mdi:fan-speed-3" class="cmd_preset_icon"></ha-icon></mwc-button>

                <mwc-button
                    class='cmd_preset'
                    raised noink name="2"
                    on-click='setPreset'
                    disabled='[[_is_Set2]]'><ha-icon icon="mdi:fan-speed-2" class="cmd_preset_icon"></ha-icon></mwc-button>                    

                <mwc-button
                    class='cmd_preset'
                    raised noink name="1"
                    on-click='setPreset'
                    disabled='[[_is_Set1]]'><ha-icon icon="mdi:fan-speed-1" class="cmd_preset_icon"></ha-icon></mwc-button>

                <mwc-button
                    class='cmd_preset'
                    raised noink name="off"
                    on-click='setOff'
                    disabled='[[_is_OFF]]'><ha-icon icon="mdi:fan-off" class="cmd_preset_icon"></ha-icon></mwc-button>    


            </div>

        `;
    }

    static get properties() {
        return {
            hass: {
                type: Object,
                observer: 'hassChanged'
            },
            _config: Object,
            _stateObj: Object,
            _is_OFF: Boolean,
            _is_Set1: Boolean,
            _is_Set2: Boolean,
            _is_Auto: Boolean,
            _is_Sleep: Boolean,
            _is_Turbo: Boolean
        }
    }

    setConfig(config) {
        this._config = config;
    }


    hassChanged(hass) {

        const config = this._config;
        const stateObj = hass.states[config.entity];

        let mode;
        if (stateObj && stateObj.attributes) {
            mode = stateObj.attributes.preset_mode;
        }

        var off = stateObj.state != 'on';
        

        this.setProperties({
            _stateObj: stateObj,
            _is_Set1: mode === '1',
            _is_Set2: mode === '2',
            _is_Auto: mode === 'auto',
            _is_Sleep: mode === 'sleep',
            _is_Turbo: mode === 'turbo',
            _is_OFF: mode == null
        });

        //console.log(_is_OFF);
        //console.log(off);
    }

    stopPropagation(e) {
        e.stopPropagation();
    }

    setOff(e) {
        this.hass.callService('fan', 'turn_off', {
            entity_id: this._config.entity
         });
    }


    setPreset(e) {

        const valore = e.currentTarget.getAttribute('name');
        //console.log(valore);
        this.hass.callService('fan', 'set_preset_mode', {
           entity_id: this._config.entity, preset_mode: valore
        });
    }

}

customElements.define('air-purifier-card', AirPurifierCard);

both files must been added into lovelace => settings => Dashboards => Resources

url: /local/air-purifier-card/air-purifier-card.js
type: Javascript module

url: /local/mini-graph-card/mini-graph-card-bundle.js?v=0.10.0
type: Javascript module

Custom card for lovelace

type: vertical-stack
title: Purificatore
cards:
  - type: custom:air-purifier-card
    entity: fan.philips_airpurifier
    name: Purificatore
  - type: grid
    columns: 2
    title: Aria
    cards:
      - type: custom:mini-graph-card
        entities:
          - entity: sensor.ac3033_rob_air_quality
            name: Qualità
        icon: mdi:air-filter
        line_color: blue
        line_width: 6
        lower_bound: 0
        show:
          average: true
      - type: custom:mini-graph-card
        entities:
          - entity: sensor.ac3033_rob_allergen
            name: Allergeni
        icon: mdi:air-filter
        line_color: '#e74c3c'
        line_width: 6
        lower_bound: 0
        show:
          average: true
      - type: custom:mini-graph-card
        entities:
          - entity: sensor.ac3033_rob_pm25
            name: PM 25
        icon: mdi:air-filter
        line_color: orange
        line_width: 6
        lower_bound: 0
        show:
          average: true
      - type: custom:mini-graph-card
        entities:
          - entity: sensor.ac3033_rob_volatile_organic
            name: V.O.C.
        icon: mdi:air-filter
        line_color: green
        line_width: 6
        lower_bound: 0
        show:
          average: true

Hope can help

1 Like

Hello @crc-error79

Thanks for this card control !

I used it for my Philips Air Purifier AC1715 and the kongo09 / philips-airpurifier-coap Github project.

I needed to modify two preset, the AC1715 use “speed 1” and “speed 2” instead of “1” and “2” in air-purifier-card.js (off / sleep / turbo are ok).

Also, I didn’t understand why you use specific sensor.yaml so I didn’t use it (and the AC1715 has only 2 sensors, PM and AI).

Thanks for this :slight_smile:

Due to change in Home Assistant since 2023.5 with Deprecating Polymer the project no longer work.

(With ChatGPT and some docs) I made some changes to make it work again (with the Lit library).

Here the code :

import {
  LitElement,
  html,
  css,
} from "https://unpkg.com/[email protected]/lit-element.js?module";

class AirPurifierCardv2 extends LitElement {

  static get properties() {
    return {
      hass: {
          type: Object,
          observer: 'hassChanged'
      },
      _config: Object,
      _stateObj: Object,
      _is_OFF: Boolean,
      _is_Set1: Boolean,
      _is_Set2: Boolean,
      _is_Auto: Boolean,
      _is_Sleep: Boolean,
      _is_Turbo: Boolean
    };
  }

  static styles = css`
    :host {
      line-height: 1.5;
      display: block;
    }

    .cmd_preset {
      margin: 1px;
    }

    .grp_cmd_preset {
      text-align: center;
    }

    .cmd_preset_icon {
      padding: 8px 4px;
      width: auto;
      height: auto;
    }

    ha-entity-toggle {
      margin-left: 10px;
    }
  `;

  render() {
    return html`
      <div class="grid justified layout grp_cmd_preset" @click="${this.stopPropagation}">
        <mwc-button
          class="cmd_preset"
          raised
          noink
          name="off"
          @click="${this.setOff}"
          ?disabled="${this._is_OFF}"
        >
          <ha-icon icon="mdi:fan-off" class="cmd_preset_icon"></ha-icon>
        </mwc-button>

        <mwc-button
          class="cmd_preset"
          raised
          noink
          name="speed 1"
          @click="${this.setPreset}"
          ?disabled="${this._is_Set1}"
        >
          <ha-icon icon="mdi:fan-speed-1" class="cmd_preset_icon"></ha-icon>
        </mwc-button>

        <mwc-button
          class="cmd_preset"
          raised
          noink
          name="speed 2"
          @click="${this.setPreset}"
          ?disabled="${this._is_Set2}"
        >
          <ha-icon icon="mdi:fan-speed-2" class="cmd_preset_icon"></ha-icon>
        </mwc-button>

        <mwc-button
          class="cmd_preset"
          raised
          noink
          name="turbo"
          @click="${this.setPreset}"
          ?disabled="${this._is_Turbo}"
        >
          <ha-icon icon="mdi:fan-speed-3" class="cmd_preset_icon"></ha-icon>
        </mwc-button>

        <mwc-button
          class="cmd_preset"
          raised
          noink
          name="sleep"
          @click="${this.setPreset}"
          ?disabled="${this._is_Sleep}"
        >
          <ha-icon icon="mdi:power-sleep" class="cmd_preset_icon"></ha-icon>
        </mwc-button>

      </div>
    `;
  }

  setConfig(config) {
    this._config = config;
  }

  updated(changedProperties) {
    if (changedProperties.has('hass')) {
      this.hassChanged(this.hass);
    }
  }

  hassChanged(hass) {
    const config = this._config;
    const stateObj = hass.states[config.entity];

    let mode;
    if (stateObj && stateObj.attributes) {
      mode = stateObj.attributes.preset_mode;
    }

    const off = stateObj.state !== 'on';

    this._stateObj = stateObj;
    this._is_Set1 = mode === 'speed 1';
    this._is_Set2 = mode === 'speed 2';
    this._is_Auto = mode === 'auto';
    this._is_Sleep = mode === 'sleep';
    this._is_Turbo = mode === 'turbo';
    this._is_OFF = mode == null;
  }

  stopPropagation(e) {
    e.stopPropagation();
  }

  setOff(e) {
    this.hass.callService('fan', 'turn_off', {
      entity_id: this._config.entity,
    });
  }

  setPreset(e) {
    const valore = e.currentTarget.getAttribute('name');
    this.hass.callService('fan', 'set_preset_mode', {
      entity_id: this._config.entity,
      preset_mode: valore,
    });
  }
}

customElements.define('air-purifier-cardv2', AirPurifierCardv2);

And for the card, now I have to use “fan.purificateur” instead of “fan.philips_airpurifier”, I don’t know why but this works like that.