finity69x2 / fan-control-entity-row

Provides a means to show a compact graphical control row for 2 or 3 speed fans in Home Assistant
68 stars 27 forks source link

Using fan-control-entity-row with a 6 speed fan #26

Closed stavrosso closed 3 years ago

stavrosso commented 4 years ago

Hi, thank you for an amazing component

Is there a way I can use this with a 6 speed fan with speeds named Speed 1, Speed 2 , Speed 3 etc?

Thank you

dethpickle commented 4 years ago

I'll second this.

I've got a Haiku-L fan, integrated with the SenseMe add-on through HACS. The fan has 7 speeds. I had though when I started using the fan control several months ago with other fans that there was a way to associate a specific data payload to low, med, high. Looking now, I don't see it. I could be imagining things though. But something like low=1, med=3 and high=7 or something.

@stavrosso, is there a specific type of fan or existing integration you're trying to match up with so it might be more obvious how to do this for @finity69x2?

finity69x2 commented 4 years ago

You could possibly create a template fan using the speeds of your existing fan and selecting which ones you want to use for low, med & high in the template.

I think...

stavrosso commented 4 years ago

Hi @finity69x2 and @dethpickle ,

Thanks for replying

I have 3 fans which are using sonoff i-fan02 and Tasmota. (these work fine with the code by finity69x2 , Thank you)

I have a dumb 6 speed fan which I control using broadlink RF codes.. The brand of the fan is Sunlight and the model is Breeze.

I found a way to put 6 buttons interface on lovelace to control this fan using a combination of components. Here is what I did.

  1. I created a fan using this https://github.com/smartHomeHub/SmartIR and a custom remote code I called 1050.json . I used the remote.learn_command in developer options services to learn the codes of my fan's remote buttons.
  2. I used a modified version of custom-fan-card.js component so it can accept the speed commands of my fan which are speed1, speed2, speed3 etc
  3. The buttons didn't work at first . Then I realized that I had to click on the fan entity icon where the smartir component creates a forward and reverse button. If I clicked the forward button , the buttons operate normally and I can control the fan.

I will attempt to correctly post my codes , maybe they will be helpful to someone else. I hope I manage to do this correctly because it is the first time I am posting codes.

This is the fan entity using smartir

`

This is the modified version of the custom-fan-card.js

` class CustomFanCard extends Polymer.Element {

static get template() {
    return Polymer.html`
        <style>
            .flex-container {
                display: flex;
                justify-content: center;
                align-items: center;
            }
            @keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-ripple-surface--test-edge-var-bug{--mdc-ripple-surface-test-edge-var: 1px solid #000;visibility:hidden}.mdc-ripple-surface--test-edge-var-bug::before{border:var(--mdc-ripple-surface-test-edge-var)}.mdc-button{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.875rem;line-height:2.25rem;font-weight:500;letter-spacing:.0892857143em;text-decoration:none;text-transform:uppercase;--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity;padding:0 8px 0 8px;display:inline-flex;position:relative;align-items:center;justify-content:center;box-sizing:border-box;min-width:64px;height:36px;border:none;outline:none;line-height:inherit;user-select:none;-webkit-appearance:none;overflow:hidden;vertical-align:middle;border-radius:4px}.mdc-button::before,.mdc-button::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-button::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1}.mdc-button.mdc-ripple-upgraded::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-button.mdc-ripple-upgraded::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-button.mdc-ripple-upgraded--unbounded::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-button.mdc-ripple-upgraded--foreground-activation::after{animation:225ms mdc-ripple-fg-radius-in forwards,75ms mdc-ripple-fg-opacity-in forwards}.mdc-button.mdc-ripple-upgraded--foreground-deactivation::after{animation:150ms mdc-ripple-fg-opacity-out;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-button::before,.mdc-button::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-button.mdc-ripple-upgraded::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-button::-moz-focus-inner{padding:0;border:0}.mdc-button:active{outline:none}.mdc-button:hover{cursor:pointer}.mdc-button:disabled{background-color:transparent;color:rgba(0,0,0,.37);cursor:default;pointer-events:none}.mdc-button.mdc-button--dense{border-radius:4px}.mdc-button:not(:disabled){background-color:transparent}.mdc-button:not(:disabled){color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}.mdc-button::before,.mdc-button::after{background-color:#6200ee}@supports not (-ms-ime-align: auto){.mdc-button::before,.mdc-button::after{background-color:var(--mdc-theme-primary, #6200ee)}}.mdc-button:hover::before{opacity:.04}.mdc-button:not(.mdc-ripple-upgraded):focus::before,.mdc-button.mdc-ripple-upgraded--background-focused::before{transition-duration:75ms;opacity:.12}.mdc-button:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-button:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:.16}.mdc-button.mdc-ripple-upgraded{--mdc-ripple-fg-opacity: 0.16}.mdc-button .mdc-button__icon{margin-left:0;margin-right:8px;display:inline-block;width:18px;height:18px;font-size:18px;vertical-align:top}[dir=rtl] .mdc-button .mdc-button__icon,.mdc-button .mdc-button__icon[dir=rtl]{margin-left:8px;margin-right:0}.mdc-button svg.mdc-button__icon{fill:currentColor}.mdc-button--raised .mdc-button__icon,.mdc-button--unelevated .mdc-button__icon,.mdc-button--outlined .mdc-button__icon{margin-left:-4px;margin-right:8px}[dir=rtl] .mdc-button--raised .mdc-button__icon,.mdc-button--raised .mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button--unelevated .mdc-button__icon,.mdc-button--unelevated .mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button--outlined .mdc-button__icon,.mdc-button--outlined .mdc-button__icon[dir=rtl]{margin-left:8px;margin-right:-4px}.mdc-button--raised,.mdc-button--unelevated{padding:0 16px 0 16px}.mdc-button--raised:disabled,.mdc-button--unelevated:disabled{background-color:rgba(0,0,0,.12);color:rgba(0,0,0,.37)}.mdc-button--raised:not(:disabled),.mdc-button--unelevated:not(:disabled){background-color:#6200ee}@supports not (-ms-ime-align: auto){.mdc-button--raised:not(:disabled),.mdc-button--unelevated:not(:disabled){background-color:var(--mdc-theme-primary, #6200ee)}}.mdc-button--raised:not(:disabled),.mdc-button--unelevated:not(:disabled){color:#fff;color:var(--mdc-theme-on-primary, #fff)}.mdc-button--raised::before,.mdc-button--raised::after,.mdc-button--unelevated::before,.mdc-button--unelevated::after{background-color:#fff}@supports not (-ms-ime-align: auto){.mdc-button--raised::before,.mdc-button--raised::after,.mdc-button--unelevated::before,.mdc-button--unelevated::after{background-color:var(--mdc-theme-on-primary, #fff)}}.mdc-button--raised:hover::before,.mdc-button--unelevated:hover::before{opacity:.08}.mdc-button--raised:not(.mdc-ripple-upgraded):focus::before,.mdc-button--raised.mdc-ripple-upgraded--background-focused::before,.mdc-button--unelevated:not(.mdc-ripple-upgraded):focus::before,.mdc-button--unelevated.mdc-ripple-upgraded--background-focused::before{transition-duration:75ms;opacity:.24}.mdc-button--raised:not(.mdc-ripple-upgraded)::after,.mdc-button--unelevated:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-button--raised:not(.mdc-ripple-upgraded):active::after,.mdc-button--unelevated:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:.32}.mdc-button--raised.mdc-ripple-upgraded,.mdc-button--unelevated.mdc-ripple-upgraded{--mdc-ripple-fg-opacity: 0.32}.mdc-button--raised{box-shadow:0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0,0,0,.12);transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-button--raised:hover,.mdc-button--raised:focus{box-shadow:0px 2px 4px -1px rgba(0, 0, 0, 0.2),0px 4px 5px 0px rgba(0, 0, 0, 0.14),0px 1px 10px 0px rgba(0,0,0,.12)}.mdc-button--raised:active{box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2),0px 8px 10px 1px rgba(0, 0, 0, 0.14),0px 3px 14px 2px rgba(0,0,0,.12)}.mdc-button--raised:disabled{box-shadow:0px 0px 0px 0px rgba(0, 0, 0, 0.2),0px 0px 0px 0px rgba(0, 0, 0, 0.14),0px 0px 0px 0px rgba(0,0,0,.12)}.mdc-button--outlined{border-style:solid;padding:0 14px 0 14px;border-width:2px}.mdc-button--outlined:disabled{border-color:rgba(0,0,0,.37)}.mdc-button--outlined:not(:disabled){border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-button--dense{height:32px;font-size:.8125rem}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:"liga";-webkit-font-smoothing:antialiased}:host{display:inline-flex;outline:none}.mdc-button{flex:1}
            :host {
                display: inherit;
            }
            .speeds button:host {
                display: inline-flex;
                outline: none;
            }
            .speeds button {
                min-width: 34px !important;
                width: 34px;
                font-size: 11px !important;
            }
        </style>

        <hui-generic-entity-row hass="[[hass]]" config="[[_config]]">
            <div class='flex-container' on-click="stopPropagation">
                <div class='speeds'>
                    <button
                        class='mdc-button mdc-button--raised mdc-ripple-upgraded'
                        toggles name="off"
                        on-click='setSpeed'
                        disabled='[[_isOff]]'>
                        <span class="mdc-button__label">O</span>
                    </button>
                    <button
                        class='mdc-button mdc-button--raised mdc-ripple-upgraded'
                        toggles name="speed1"
                        on-click='setSpeed'
                        disabled='[[_isOnSpeed1]]'>
                        <span class="mdc-button__label">1</span>
                    </button>
                    <button
                        class='mdc-button mdc-button--raised mdc-ripple-upgraded'
                        toggles name="speed2"
                        on-click='setSpeed'
                        disabled='[[_isOnSpeed2]]'>
                        <span class="mdc-button__label">2</span>
                    </button>
                    <button
                        class='mdc-button mdc-button--raised mdc-ripple-upgraded'
                        toggles name="speed3"
                        on-click='setSpeed'
                        disabled='[[_isOnSpeed3]]'>
                        <span class="mdc-button__label">3</span>
                    </button>
                    <button
                        class='mdc-button mdc-button--raised mdc-ripple-upgraded'
                        toggles name="speed4"
                        on-click='setSpeed'
                        disabled='[[_isOnSpeed4]]'>
                        <span class="mdc-button__label">4</span>
                    </button>
                    <button
                        class='mdc-button mdc-button--raised mdc-ripple-upgraded'
                        toggles name="speed5"
                        on-click='setSpeed'
                        disabled='[[_isOnSpeed5]]'>
                        <span class="mdc-button__label">5</span>
                    </button>
                    <button
                        class='mdc-button mdc-button--raised mdc-ripple-upgraded'
                        toggles name="speed6"
                        on-click='setSpeed'
                        disabled='[[_isOnSpeed6]]'>
                        <span class="mdc-button__label">6</span>
                    </button>
                </div>
            </div>
        </hui-generic-entity-row>
    `;
}

static get properties() {
    return {
        hass: {
            type: Object,
            observer: 'hassChanged'
        },
        _config: Object,
        _stateObj: Object,
        _isOff: Boolean,
        _isOnSpeed1: Boolean,
        _isOnSpeed2: Boolean,
        _isOnSpeed3: Boolean,
        _isOnSpeed4: Boolean,
        _isOnSpeed5: Boolean,
        _isOnSpeed6: 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',
        _isOnSpeed1: speed === 'speed1' && stateObj.state === 'on',
        _isOnSpeed2: speed === 'speed2' && stateObj.state === 'on',
        _isOnSpeed3: speed === 'speed3' && stateObj.state === 'on',
        _isOnSpeed4: speed === 'speed4' && stateObj.state === 'on',
        _isOnSpeed5: speed === 'speed5' && stateObj.state === 'on',
        _isOnSpeed6: speed === 'speed6' && 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);

` The card code is as follows

`- platform: smartir name: Breeze unique_id: breeze_fan device_code: 1050 controller_data: 192.168.1.80

power_sensor: binary_sensor.fan_power`

and here is how it looks on lovelace

breeze fan