Lovelace Custom Fan Card Example

Awesome card.

3 Likes

Please share your code. This on lovelace?

Sure. I used @rhodges’s code with some modify. This is my custom:

class CustomFanCard 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;
                }
                .speed {
                    min-width: 35px;
                    max-width: 35px;
                    margin-left: 5px;
                    margin-right: 0px;
                }
                ha-entity-toggle {
                    margin-left: 16px;
                }
            </style>
            <hui-generic-entity-row hass="[[hass]]" config="[[_config]]">
                <div class='horizontal justified layout' on-click="stopPropagation">
                    <paper-button
                        class='speed'
                        raised noink name="low"
                        on-tap='setSpeed'
                        disabled='[[_isLowSpeed]]'><ha-icon icon="mdi:numeric-1-box"></ha-icon></paper-button>
                    <paper-button
                        class='speed'
                        raised noink name="medium"
                        on-tap='setSpeed'
                        disabled='[[_isMedSpeed]]'><ha-icon icon="mdi:numeric-2-box"></ha-icon></paper-button>
                    <paper-button
                        class='speed'
                        raised noink name="high"
                        on-tap='setSpeed'
                        disabled='[[_isHghSpeed]]'><ha-icon icon="mdi:numeric-3-box"></ha-icon></paper-button>
                    <ha-entity-toggle hass="[[hass]]" state-obj="[[_stateObj]]"></ha-entity-toggle>
                </div>
            </hui-generic-entity-row>
        `;
    }

    static get properties() {
        return {
            hass: {
                type: Object,
                observer: 'hassChanged'
            },
            _config: Object,
            _stateObj: Object,
            _isLowSpeed: Boolean,
            _isMedSpeed: Boolean,
            _isHghSpeed: Boolean
        }
    }

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

    hassChanged(hass) {

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

        let speed;
        if (stateObj && stateObj.attributes) {
            speed = stateObj.attributes.speed || 'off';
        }

        this.setProperties({
            _stateObj: stateObj,
            _isLowSpeed: speed === 'low',
            _isMedSpeed: speed === 'medium',
            _isHghSpeed: speed == 'high'
        });
    }

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

    setSpeed(e) {
        const speed = e.currentTarget.getAttribute('name');
        this.hass.callService('fan', 'set_speed', {
            entity_id: this._config.entity, speed: speed
        });
    }

}

customElements.define('custom-fan-card', CustomFanCard);
3 Likes

Big thanks for posting this custom card code, I use lovelace for a control panel and even thought it’s a 10inch tablet the icons are to small, this is perfect to make my icons bigger but keep the interface compact.

class CustomFanCard 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;
            }
            .speed {
                min-width: 35px;
                max-width: 85px;
                margin-left: 5px;
                margin-right: 0px;
                padding: 0!important;
            }
            ha-entity-toggle {
                margin-left: 16px;
            }
            .large-icon{
                width:65px!important;
                height:65px!important;
            }
        </style>
        <hui-generic-entity-row hass="[[hass]]" config="[[_config]]">
            <div class='horizontal justified layout' on-click="stopPropagation">
                <paper-button
                    class='speed'
                    raised noink name="up"
                    on-tap='setSpeed'
                    disabled='[[_isLowSpeed]]'><ha-icon class="large-icon" icon="mdi:arrow-up-thick"></ha-icon></paper-button>
                <paper-button
                    class='speed'
                    raised noink name="stop"
                    on-tap='setSpeed'
                    disabled='[[_isMedSpeed]]'><ha-icon class="large-icon" icon="mdi:stop"></ha-icon></paper-button>
                <paper-button
                    class='speed'
                    raised noink name="down"
                    on-tap='setSpeed'
                    disabled='[[_isHghSpeed]]'><ha-icon class="large-icon" icon="mdi:arrow-down-thick"></ha-icon></paper-button>
            </div>
        </hui-generic-entity-row>
    `;
}

static get properties() {
    return {
        hass: {
            type: Object,
            observer: 'hassChanged'
        },
        _config: Object,
        _stateObj: Object,
        _isLowSpeed: Boolean,
        _isMedSpeed: Boolean,
        _isHghSpeed: Boolean
    }
}

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

hassChanged(hass) {

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

    let speed;
    if (stateObj && stateObj.attributes) {
        speed = stateObj.attributes.speed || 'off';
    }

    this.setProperties({
        _stateObj: stateObj,
        _isLowSpeed: speed === 'up',
        _isMedSpeed: speed === 'stop',
        _isHghSpeed: speed === 'down'
    });
}

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

setSpeed(e) {
    const speed = e.currentTarget.getAttribute('name');
    if (speed === 'up'){
        this.hass.callService('script', 'blind_lr_sheer_up', {
            entity_id: this._config.entity
        });
    }
    if (speed === 'stop'){
        this.hass.callService('script', 'blind_lr_sheer_stop', {
            entity_id: this._config.entity
        });
    }
    if (speed === 'down'){
        this.hass.callService('script', 'blind_lr_sheer_down', {
            entity_id: this._config.entity
        });
    }
}

}
customElements.define(‘custom-fan-card’, CustomFanCard);

love lace config

- type: custom:custom-fan-card
  entity: cover.blind_b1_block
  name: Sheer

and I get this, show comparison to normal entitiy garage cover icons

Big thanks again, I how need to modify code to handle all the different shutters, which I’ll have a go at and let you know/ask for help.

What error/issue are you getting? using type: js worked for me

1 Like

This works well but the only problem is the last fan speed stays selected when you turn the fan off . Seems like the card does not reset the state when the fan is off. You have to choose another fan speed to start it again.

A similar problem was fixed in the old interface --> Fan Control. Has anyone got this working in lovelace?

Hello, I add the “off” button to the card, but seems not working. I click the “Off” button is only can trigger the on-tap event, but cannot disable the button. Anything i doing wrong. Please help!!!

image

class CustomFanCard 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;
                }
                .speed {
                    min-width: 40px;
                    max-width: 40px;
                    margin-left: 5px;
                    margin-right: 2px;
                }
                ha-entity-toggle {
                    margin-left: 16px;
                }
            </style>
            <hui-generic-entity-row hass="[[hass]]" config="[[_config]]">
                <div class='horizontal justified layout' on-click="stopPropagation">
                
                    <paper-button
                        class='speed'
                        raised noink name="low"
                        on-tap='setSpeed'
                        disabled='[[_isLowSpeed]]'>Lo</paper-button>
                    <paper-button
                        class='speed'
                        raised noink name="medium"
                        on-tap='setSpeed'
                        disabled='[[_isMedSpeed]]'>Med</paper-button>
                    <paper-button
                        class='speed'
                        raised noink name="high"
                        on-tap='setSpeed'
                        disabled='[[_isHghSpeed]]'>Hi</paper-button>
                    <paper-button
                        class='speed'
                        raised noink name="off"
                        on-tap='turnOff'
                        disabled='[[_isOff]]'>Off</paper-button>
                        
                    <ha-entity-toggle hass="[[hass]]" state-obj="[[_stateObj]]"></ha-entity-toggle>
                    
                </div>
            </hui-generic-entity-row>
        `;
    }

    static get properties() {
        return {
            hass: {
                type: Object,
                observer: 'hassChanged'
            },
            _config: Object,
            _stateObj: Object,
            _isOff: Boolean,
            _isLowSpeed: Boolean,
            _isMedSpeed: Boolean,
            _isHghSpeed: Boolean,
        }
    }

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

    hassChanged(hass) {

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

        let speed;
        if (stateObj && stateObj.attributes) {
            speed = stateObj.attributes.speed || 'off';
        }

        this.setProperties({
            _stateObj: stateObj,
            _isOff: speed === 'off',
            _isLowSpeed: speed === 'low',
            _isMedSpeed: speed === 'medium',
            _isHghSpeed: speed === 'high',
        });
    }

    stopPropagation(e) {
        e.stopPropagation();
    }
    
    turnOff(e) {
        const speed = e.currentTarget.getAttribute('name');    
        this.hass.callService('script', 'fan_control', {
		    mqtt_topic: 'ha/dining room fan/cmd/switch',
		    command: speed
        });
        
    }

    setSpeed(e) {
        const speed = e.currentTarget.getAttribute('name');
        this.hass.callService('script', 'fan_control', {
		    mqtt_topic: 'ha/dining room fan/cmd/level',
		    command: speed
        });
        
    }

}

customElements.define('custom-fan-card', CustomFanCard);

Managed to fix it.

class CustomFanCard 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;
                }
                .speed {
                    min-width: 35px;
                    max-width: 35px;
                    margin-left: 5px;
                    margin-right: 0px;
                }
                ha-entity-toggle {
                    margin-left: 16px;
                }
            </style>
            <hui-generic-entity-row hass="[[hass]]" config="[[_config]]">
                <div class='horizontal justified layout' on-click="stopPropagation">
                    <paper-button
                        class='speed'
                        raised noink name="off"
                        on-tap='setSpeed'
                        disabled='[[_isOff]]'><ha-icon icon="mdi:numeric-0-box"></ha-icon></paper-button>
                    <paper-button
                        class='speed'
                        raised noink name="low"
                        on-tap='setSpeed'
                        disabled='[[_isLowSpeed]]'><ha-icon icon="mdi:numeric-1-box"></ha-icon></paper-button>
                    <paper-button
                        class='speed'
                        raised noink name="medium"
                        on-tap='setSpeed'
                        disabled='[[_isMedSpeed]]'><ha-icon icon="mdi:numeric-2-box"></ha-icon></paper-button>
                    <paper-button
                        class='speed'
                        raised noink name="high"
                        on-tap='setSpeed'
                        disabled='[[_isHghSpeed]]'><ha-icon icon="mdi:numeric-3-box"></ha-icon></paper-button>
                    <ha-entity-toggle hass="[[hass]]" state-obj="[[_stateObj]]"></ha-entity-toggle>
                </div>
            </hui-generic-entity-row>
        `;
    }

    static get properties() {
        return {
            hass: {
                type: Object,
                observer: 'hassChanged'
            },
            _config: Object,
            _stateObj: Object,
            _isOff: Boolean,
            _isLowSpeed: Boolean,
            _isMedSpeed: Boolean,
            _isHghSpeed: Boolean
        }
    }

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

    hassChanged(hass) {

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

        let speed;
        if (stateObj && stateObj.attributes) {
            speed = stateObj.attributes.speed || 'off';
        }

        this.setProperties({
            _stateObj: stateObj,
            _isOff: stateObj.state  === 'off',
            _isLowSpeed: speed === 'low' && stateObj.state === 'on',
            _isMedSpeed: speed === 'medium' && stateObj.state === 'on',
            _isHghSpeed: speed == 'high' && stateObj.state === 'on'
        });

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

    setSpeed(e) {
        const speed = e.currentTarget.getAttribute('name');
        this.hass.callService('fan', 'set_speed', {
            entity_id: this._config.entity, speed: speed
        });
    }

}

customElements.define('custom-fan-card', CustomFanCard);
1 Like

Yes, work now. Thanks a lot!!

1 Like

I’m fairly close to making this work just like the other state_card_custom_fanspeed interface did but I’m stuck on two things:

  1. Does anyone know how the syntax to use logical operators to set the state of a boolean in the code? I’ve tried “_boolean3: (_boolean1) && (_boolean2)” which gives errors. I tried it without the (). I’ve tried with {}. I’ve tried with {()}. etc…

  2. what is the way to set the background color if the button dynamically with the state of a boolean?

I’ve done searches for every iteration of those search terms I can think of and can’t find the answers. Everything I’ve found has been very basic or unrelated to the questions I have. Comprehensive Polymer documentation is pretty hard to find.

Is there a reason the card is shown with no border or box on my UI?

3 Likes

If anyone is interested I’ve been working on tweaking this card to make it look and act like the modifications I made to the state-card-custom-fanspeed from https://community.home-assistant.io/t/fan-control-set-speed-working-quick-setup/57520.

The only things different from that are the color for the “off” state and the button order. But if you want it the other way then it’s trivial to make the change.

Here are the screenshots with a dark theme and with the default theme:

ex

ex2

ex3

ex4

Here is the code:

class CustomFanCard 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;
                }
                .speed {
                    min-width: 30px;
                    max-width: 30px;
					margin-left: 2px;
                    margin-right: 2px;
    	            //background-color:#546E7A;
					background-color:#759aaa;
	                border: 1px solid lightgrey; 
	                font-size: 10px !important;
	                float: right !important;   
                }
               </style>
            <hui-generic-entity-row hass="[[hass]]" config="[[_config]]">
                <div class='horizontal justified layout' on-click="stopPropagation">
                    <paper-button
                        class='speed'
						style='[[_lowOnColor]]'
                        toggles name="low"
                        on-tap='setSpeed'
                        disabled='[[_isOnLow]]'>LOW</paper-button>
                    <paper-button
                        class='speed'
						style='[[_medOnColor]]'
                        toggles name="medium"
                        on-tap='setSpeed'
                        disabled='[[_isOnMed]]'>MED</paper-button>
                    <paper-button
                        class='speed'
						style='[[_highOnColor]]'
                        toggles name="high"
                        on-tap='setSpeed'
                        disabled='[[_isOnHigh]]'>HIGH</paper-button>
				    <paper-button
                        class='speed'
						style='[[_offColor]]'
                        toggles name="off"
                        on-tap='setSpeed'
                        disabled='[[_isOffState]]'>OFF</paper-button>
                </div>
            </hui-generic-entity-row>
        `;
    }

    static get properties() {
        return {
            hass: {
                type: Object,
                observer: 'hassChanged'
            },
            _config: Object,
            _stateObj: Object,
			_lowOnColor: String,
			_medOnColor: String,
			_highOnColor: String,
			_offColor: String,
            _isOffState: Boolean,
            _isOnState: Boolean,
            _isOnLow: Boolean,
            _isOnMed: Boolean,
            _isOnHigh: Boolean
        }
    }

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

    hassChanged(hass) {

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

        let speed;
        if (stateObj && stateObj.attributes) {
            speed = stateObj.attributes.speed || 'off';
        }
		
		let low;
		let med;
		let high;
		let offstate;
		
		if (stateObj && stateObj.attributes) {
		    if (stateObj.state == 'on' && stateObj.attributes.speed == 'low') {
			    low = 'on';
		    } else if (stateObj.state == 'on' && stateObj.attributes.speed == 'medium') {
			    med = 'on';
		    } else if (stateObj.state == 'on' && stateObj.attributes.speed == 'high') {
			    high = 'on';
		    } else {
				offstate = 'on';
			}
		}
		
        let lowcolor;
		let medcolor;
		let hicolor;
		let offcolor;
		
		if (low == 'on') {
			lowcolor = 'background-color: #43A047';
		} else {
			lowcolor = '';
		}
		
		if (med == 'on') {
			medcolor = 'background-color: #43A047';
		} else {
			medcolor = '';
		}
		
		if (high == 'on') {
			hicolor = 'background-color: #43A047';
		} else {
			hicolor = '';
		}
		
		if (offstate == 'on') {
			//offcolor = 'background-color: #43A047';
			offcolor = 'background-color: #f44c09';
		} else {
			offcolor = '';
		}
		
		this.setProperties({
            _stateObj: stateObj,
			_isOffState: stateObj.state == 'off',
            _isOnLow: low === 'on',
			_isOnMed: med === 'on',
			_isOnHigh: high === 'on',
			_lowOnColor: lowcolor,
			_medOnColor: medcolor,
			_highOnColor: hicolor,
			_offColor: offcolor
        });
    }

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

    setSpeed(e) {
        const speed = e.currentTarget.getAttribute('name');
        this.hass.callService('fan', 'set_speed', {
            entity_id: this._config.entity, speed: speed
        });
    }

}

customElements.define('custom-fan-card', CustomFanCard);

the configuration is the same as the original card:

- entity: fan.master_bedroom_fan
  type: custom:custom-fan-card
  name: MBR Fan

If you don’t like the colors you can simply go into the code and change the hex values for the “background-color:”.

And @rhodges since this thread has transformed into a thread specific to a custom fan card I’m changing the title of the thread to reflect that. I hope that’s OK. It will help more people find it if that’s the kind of information they need.

And thanks for getting the ball rolling on this. I’m OK at modifying cards (after digging around trying to find the info I need!) but there’s no way I would have known how to even get started without your initial work.

6 Likes

Thanks a lot finity I waited for someone to do the conversion to Lovelace.
Great Job!

Thank you! :slightly_smiling_face:

Hello
I like your fan card wery much. Great job.
But I can’t make it work
Can you please write step by step instruction how to implement in lovelace
Thanks

Thanks a bunch for this; it was the last holdback from the custom_ui days in my UI. Now Lovelace has replaced it all completely with feature parity (or better).

Thanks a bunch for this!

I may modify it to use for some lights to give logical dimming levels like 25%, 50%, 75%, 100% to make it easier on a small iPhone versus dragging a slider.

1 Like

First you need to copy the entire contents of the code above and store it in a file called “custom-fan-card.js” and locate that file in your www directory in your config directory.

then in your lovelace config you need to add the card in the “resources” section. How you do that depends on the mode of lovelace config you use. I use the yaml mode so my entry there looks like this:

resources:
  - url: /local/custom-fan-card.js
    type: js

then in your lovelace main config section you need to add the card config wherever you want to show it.

title: Home Assistant
views:
  title: Main
  cards:
    - type: entities
      title: Master Bedroom
      show_header_toggle: false
      entities:
        - entity: fan.master_bedroom_fan
          type: custom:custom-fan-card
          name: MBR Fan

You will obviously need to have a fan configured first and you will change the entity_id above to your fan id.

Does the above work with the raw conifg editor on .87?

I keep getting Entity not available config.entity Any help?

Thanks

I got it.

type: entities
title: Master Bedroom
show_header_toggle: false
entities:
  - entity: fan.sonoff_fan_1
    type: 'custom:custom-fan-card'
    name:Fan

Got message: Your config is not supported by the UI editor:
Expected a value of type {entity,name,icon} | entity-id for entities.0.type but received "custom:custom-fan-card".
Falling back to YAML editor.

you need to post your code properly formatted for the forum.

But at first glance trying to see thru the bad formatting it looks like in the “name:” section you don’t have a space after it and before “Fan”.