Lovelace Custom Fan Card Example

I guess it’s almost time to take the v88.x plunge. :worried:

EDIT: I guess I won’t update yet…There’s no V88.1 for docker yet.

I updated to 0.88.1 and didn’t work, so I cleared the Safari cache and refreshed. Now its working!

The text and boxes are pretty off. I wish I could help fix it, but I don’t know how to code.

Thanks for all help.

Can you please share your final working card code. I tried some of the other codes on this page and I’m getting same error from before Custom Element doesn’t exist .

Struggling to get the layout right

image

This seems to have fixed it. Changed the max-width

            .speed {
                min-width: 20px;
                max-width: 85px;
                margin-left: 2px;
                margin-right: 2px;
                float: right !important;

image

if you shrink the window or view it on a mobile device does it stay like that. I found if I did that the buttons wouldn’t resize.

I just posted another thread about it here and you can see what I mean:

Unfortunately they don’t resize and end up overlapping. I haven’t figured out how to make them smaller

Yeah, did you see the other thread?

this really sucks. it was really looking pretty good before and now we’re back to no good options for a compact four button fan control. :rage:

Can you post all your styles, or the entire file, whatever is easier?

I know you didn’t ask me but here is the code I’m using right now. I’m really close to getting it back to useful. the only things I can’t change are the font color to match the theme and the text size and alignment to stay within the boundaries of the button. The way it is right now it will resize properly and the buttons all function.

ex1

ex2

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

there are a few of the style options I’ve tried still in the code but they won’t affect the button style at all.

@slipx06 Can you share your 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: 20px;
                max-width: 85px;
                margin-left: 2px;
                margin-right: 2px;
                float: right !important;
            }
        </style>
        <hui-generic-entity-row hass="[[hass]]" config="[[_config]]">
            <div class='horizontal justified layout' on-click="stopPropagation">
                <mwc-button
                    class='speed'
                    raised noink name="off"
                    on-click='setSpeed'
                    disabled='[[_isOff]]'>Off</mwc-button>
                <mwc-button
                    class='speed'
                    raised noink name="low"
                    on-click='setSpeed'
                    disabled='[[_isLowSpeed]]'>Low</mwc-button>
                <mwc-button
                    class='speed'
                    raised noink name="medium"
                    on-click='setSpeed'
                    disabled='[[_isMedSpeed]]'>Med</mwc-button>
                <mwc-button
                    class='speed'
                    raised noink name="high"
                    on-click='setSpeed'
                    disabled='[[_isHghSpeed]]'>High</mwc-button>
            </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);

Made some more changes to the style using a flex container and now the buttons do not overlap when resizing. Still need to figure out how to make them smaller

class CustomFanCard extends Polymer.Element {

    static get template() {
        return Polymer.html`
            <style>
            .flex-container {
              display: flex;
              justify-content: center;
              align-items: center;
            }

            .flex-container > div {
              margin: 0px;
              padding: 1px;
            }
            </style>

            <hui-generic-entity-row hass="[[hass]]" config="[[_config]]">
                <div class='flex-container' on-click="stopPropagation">
                   <div><mwc-button
                        raised noink name="off"
                        on-click='setSpeed'
                        disabled='[[_isOff]]'>Off</mwc-button></div> 
                    <div><mwc-button
                        raised noink name="low"
                        on-click='setSpeed'
                        disabled='[[_isLowSpeed]]'>Lo</mwc-button></div>
                    <div><mwc-button
                        raised noink name="medium"
                        on-click='setSpeed'
                        disabled='[[_isMedSpeed]]'>Med</mwc-button></div>
                    <div><mwc-button
                        raised noink name="high"
                        on-click='setSpeed'
                        disabled='[[_isHghSpeed]]'>Hi</mwc-button></div>
                </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);

image

image

Can make them thinner using dense=""
image

Thanks for sharing. Came out like that on mine.

Well, after many, many hours trying to figure out why none of the regular styling options work on the new mwc-button I think (maybe…) I figured out what is happening. I’m not a web developer so take this for the value in what you paid for it…:wink:

It looks to me like when the new mwc-button was implemented it created a thing called a “shadowDOM” that locks up the available style options as soon as the “mwc-button” is called in the code. Looking thru the dev-console the values for things like text size, text color, and background colors (along with a bunch of other stuff) are fixed by the shadowDOM that gets imported when the mdc-button class is used. From what I have learned there is no way to turn it off either.

But, I learned in my digging around there might be a way to override those values by creating a new shadowDOM later in the code but I don’t understand web development enough to figure out how to do that.

Unless someone smarter than me can figure out how to override the imposed style then I think we’re stuck with the way it looks above. Or I’ll have to move to a different method of button. That really sucks because I never like any of the other button options available. To me they all looked way too “blocky”. Kind of like these do now. :slightly_frowning_face:

Drat!!

I might put in a bug report to see if the restrictions could be removed but I may be in the dog house with the developers right now so maybe someone else might get better traction doing it.

That looks right. I’m using a theme so my colours are a little different.

What did you change in the theme to get the button colors to be different?

I’m using the clear theme.

https://community.home-assistant.io/t/clear-theme/100464

Thanks.

It looks like it’s using the primary-color for the dark (not-disabled) button color. I can’t even figure how to split that out and make it individually customizable. there is a reference to “mdc-theme-primary” in the home-assistant-polymer github style page which I thought should have selected the color for the buttons but I tried that and it didn’t work either.

Maybe this can help