Lovelace Custom Fan Card Example

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ā€.

Thank you. Sorry for the bad code entry. I was excited that I got it to work. I knowā€¦lame excuse, but hey I learned something today!!! :slight_smile:

Ok, so you are saying that it is working for you?

ok finity thanks for the help.
Now I get to work this to.
But I only copy the code in to \hassio\config\www\custom_ui in costum-fan-card.js file
add this to fans.yaml

  - platform: mqtt  
    name: "Delavnica Ventilator"
    command_topic: "cmnd/delavnica_ventilator/FanSpeed"
    speed_command_topic: "cmnd/delavnica_ventilator/FanSpeed"    
    state_topic: "stat/delavnica_ventilator/RESULT"
    speed_state_topic: "stat/delavnica_ventilator/RESULT"
    #state_value_template: "{% if value_json.FanSpeed == 0 -%}0{%- elif value_json.FanSpeed > 0 -%}4{%- endif %}"
    state_value_template: >
      {% if value_json.FanSpeed is defined %}
        {% if value_json.FanSpeed == 0 -%}0{%- elif value_json.FanSpeed > 0 -%}4{%- endif %}
      {% else %}
        {% if states.delavnica_ventilator.state == 'off' -%}0{%- elif states.delavnica_ventilator.state == 'on' -%}4{%- endif %}
      {% endif %}
    speed_value_template: "{{ value_json.FanSpeed }}"
    #speed_value_template: "{% if value_json.FanSpeed == 0 -%}off{%- elif value_json.FanSpeed > 0 -%}{{ value_json.FanSpeed }}{%- endif %}"
    availability_topic: tele/delavnica_ventilator/LWT
    payload_off: "0"
    payload_on: "4"
    payload_low_speed: "1"
    payload_medium_speed: "2"
    payload_high_speed: "3"
    payload_available: Online
    payload_not_available: Offline
    speeds:
      - off
      - low
      - medium
      - high

and this in to lovelace entity card

  - entity: fan.delavnica_ventilator
    name: Ventilator
    type: 'custom:custom-fan-card'

and it works great.
I hope that can help some one in the future.

This needs to be updated to support the mwc-button if upgrading to 0.88

Note for Lovelace custom card developers: if you relied on the availability of <paper-button> in your code, you will have to update it to <mwc-button> to get a similar component.

1 Like

Is that just a search/replace or more involved?

Mostly. Also replace any on-tap attribute of the button with on-click. More changes might be needed.

Replace paper-button with mwc-bottom and on-tap with on-click will get you working. The style will be different, so more changes will need to be made to get it looking exactly like it was before, if that is what you want.

1 Like

Iā€™m not sure what Iā€™m doing wrong. I keep getting Custom Element doesnā€™t exist.

Iā€™m running 0.88 have the custom-fan-card.js file in the www folder. Also, tried changing <paper-button> to <mwc-button> in the code and on-tap to on-click.

Have this on my raw config editor:

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

Have this on my card configuration:

entities:
  - entity: fan.family_room
    type: 'custom:custom-fan-card'
title: Fan
type: entities

And custom-fan-card 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">
                    <mwc-button
                        class='speed'
						style='[[_lowOnColor]]'
                        toggles name="low"
                        on-click='setSpeed'
                        disabled='[[_isOnLow]]'>LOW</mwc-button>
                    <mwc-button
                        class='speed'
						style='[[_medOnColor]]'
                        toggles name="medium"
                        on-click='setSpeed'
                        disabled='[[_isOnMed]]'>MED</mwc-button>
                    <mwc-button
                        class='speed'
						style='[[_highOnColor]]'
                        toggles name="high"
                        on-click='setSpeed'
                        disabled='[[_isOnHigh]]'>HIGH</mwc-button>
				    <mwc-button
                        class='speed'
						style='[[_offColor]]'
                        toggles name="off"
                        on-click='setSpeed'
                        disabled='[[_isOffState]]'>OFF</mwc-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);

I had it working fine before on the Custom-UI. Iā€™m new to Lovelace. Appreciate the help. Thanks!

This is working for me; but the text on the buttons is off. But it does work and update.

The only difference is mine are like:

  - type: custom:fold-entity-row
    head:
      entity: light.office_lamp_level
      secondary_info: last-changed
    group_config:
        secondary_info: last-changed
    items:
      - light.office_lamp_level
      - light.office_ceiling_fan_light
      - entity: fan.office
        type: custom:custom-fan-card
      - sensor.office_temperature
      - lightingsm.motion_office
      - binary_sensor.office_occupancy
      - binary_sensor.office_pir_sensor

Change the type to module

1 Like

Woww!! Itā€™s a progress! Now itā€™s showing the buttons. A little ugly though. The buttons are not functional, if I turn on the fan I seen the buttons getting green but doesnā€™t work if I click on it.