Fan Control / Set speed - Working Quick setup

make sure that you have custom-ui installed and working.


follow the installing and activating directions

in your configuration.yaml add or append this to your homeassistant:

  customize_glob: 
    fan.*:
      custom_ui_state_card: state-card-custom-fanspeed

in your configuration.yaml add or append the new state card under frontend: you should have the lines above this for custom_ui so don’t be surprised if it looks different.

frontend:
  extra_html_url:
    - /local/custom_ui/state-card-custom-fanspeed.html
  extra_html_url_es5:
    - /local/custom_ui/state-card-custom-fanspeed.html

save this as state-card-custom-fanspeed.html in www/custom_ui

<dom-module id="state-card-custom-fanspeed">
  <template>
    <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
    <style>
      :host {
        line-height: 1.5;
      }
      paper-button {
		    min-width: 30px;
		    height: 30px;
		    margin: 2px;  
		    background-color:#f3f3f3;
		    border: 1px solid lightgrey; 
		    font-size: 10px !important;
		    float: right !important;   	
      }
    </style>
		<div class='horizontal justified layout'>
		  <state-info state-obj="[[stateObj]]"></state-info>
		  <paper-button-group>
		      <paper-button style="[[btnOffstyle]]" on-click='handleOffTap'>Off</paper-button>
		      <paper-button style="[[btnLowstyle]]" on-click='handleLowTap'>Low</paper-button>
		      <paper-button style="[[btnMedstyle]]" on-click='handleMedTap'>Med</paper-button>
		      <paper-button style="[[btnHighstyle]]" on-click='handleHighTap'>High</paper-button>
		  </paper-button-group>
		</div>
</template>
</dom-module>
<script>
        Polymer({
          is: 'state-card-custom-fanspeed',
          properties: {
            hass: {
              type: Object,
            },
            stateObj: {
              type: Object,
              observer: 'stateObjChanged',
            },
            statusValue: {
              type: String,
              value: 'Unset',
            },
            btnOffstyle: {
              type: String,
              value: '',
            },
            btnHighstyle: {
              type: String,
              value: '',
            },
            btnMedstyle: {
              type: String,
              value: '',
            },
            btnLowstyle: {
              type: String,
              value: '',
            },
          },
          stateObjChanged: function (newVal) {
            if (newVal) {
              if (newVal.attributes.speed == 'off') {
                this.statusValue = 'off';
                this.btnOffstyle = 'background-color: #42d9f4;';
                this.btnLowstyle = '';
                this.btnMedstyle = '';
                this.btnHighstyle = '';
                this.updateStyles({'--status-text-color': 'gray'});
              } else if (newVal.attributes.speed == 'low') {
                this.statusValue = 'low';
                this.btnOffstyle = '';
                this.btnLowstyle = 'background-color: #42d9f4;';
                this.btnMedstyle = '';
                this.btnHighstyle = '';    
                this.updateStyles({'--status-text-color': 'blue'});       
              } else if (newVal.attributes.speed == 'medium') {
                this.statusValue = 'medium';
                this.btnOffstyle = '';
                this.btnLowstyle = '';
                this.btnMedstyle = 'background-color: #42d9f4;';
                this.btnHighstyle = '';
                this.updateStyles({'--status-text-color': 'yellow'});
              } else if (newVal.attributes.speed == 'high') {
                this.statusValue = 'high';
                this.btnOffstyle = '';
                this.btnLowstyle = '';
                this.btnMedstyle = '';
                this.btnHighstyle = 'background-color: #42d9f4;';
                this.updateStyles({'--status-text-color': 'red'});
              }              
            }
          },
        	handleHighTap: function (ev) {
						this.setSpeed(ev, 'high');
          },
        	handleMedTap: function (ev) {
						this.setSpeed(ev, 'medium');
          },
        	handleLowTap: function (ev) {
						this.setSpeed(ev, 'low');
          },
        	handleOffTap: function (ev) {
            this.setSpeed(ev, 'off');
          },
          setSpeed: function (ev, setspeed ) {
          	if( setspeed == 'off' ){
          		//this.updateStyles({'--status-text-color': '#F44336'});
							//this.updateStyles({'--state-label-badge': '#F44336'});
							var serviceData = {entity_id: this.stateObj.entity_id};
            	this.hass.callService('fan', 'turn_off', serviceData);          		
          		
          	} else {
          		if (this.stateObj.state != 'on'){
								var serviceData = {entity_id: this.stateObj.entity_id, speed: setspeed };
            		this.hass.callService('fan', 'turn_on', serviceData );          			
          		}else{
          			var serviceData = {entity_id: this.stateObj.entity_id, speed: setspeed };
          			this.hass.callService('fan', 'set_speed', serviceData );
          		}
          	}
          	 ev.stopPropagation();
          },
         
});
</script>

Restart home assistant and when its loaded in your browser make sure you do a full reload of the HA interface. Shift + refresh.

End result

patio_Fan_2patio_Fan_1

10 Likes

:laughing:

1 Like

Any idea how to prevent this condition when a window is resized?

image

Changed some of the styling

This is really cool; any way to combine it with the last update time from Andrey’s custom UI?

Hmmm i never used Andrey’s custom UI, but i don’t see why not. Probably just needs the HTML portion changed.

I think your changes need to cascade into Andrey’s as you can only specify one custom card per device.

@undertoe I’d like to add an extra line under the entity name to show an attribute (similar to the extra_data_template feature in CustomUI) but I’m not so good with HTML. Any advice?

Should work, try this

fan.*:
  custom_ui_state_card: state-card-custom-fanspeed
    extra_data_template: ${ attributes.speed }

Didn’t seem to work for me…does it work for you?

Yea your right, looks like i will have to learn a little more on how custom ui did it. I am not really in the need for it so not sure how quickly i will be able to get it in there.

have you tried using mine as a secondary ui under custom ui?

Haven’t tried but just thinking

Yeah, I tried the state_card_custom_ui_secondary but no luck.

@undertoe

I’ve installed your state-card-custom-fanspeed and most of it seems to be working.

The only thing I can’t get to work is when I click on the “off” button that the display actually shows the “off” button highlighted. The fan does actually turn off and the icon for the fan goes from yellow (on) to blue (off) but the buttons stay at whatever the last fan speed was before I clicked on the “off” button.

Fan running on ‘high’:

ex

Fan ‘off’:

ex2

I’ve tried working out the code to see where the failure might be but I just don’t understand it enough to figure it out.

And in case it helps here is the fan code:

- platform: mqtt  
  name: "Master Bedroom Fan Sonoff"  
  state_topic: "stat/sonoff_MBR_fan/RESULT"
  speed_state_topic: "stat/sonoff_MBR_fan/RESULT"
  state_value_template: "{% if value_json.FanSpeed == 0 -%}0{%- elif value_json.FanSpeed > 0 -%}4{%- endif %}" 
  speed_value_template: "{{ value_json.FanSpeed }}"
  availability_topic: tele/sonoff_MBR_fan/LWT
  payload_available: Online
  payload_not_available: Offline
  speed_command_topic: "cmnd/sonoff_MBR_fan/FanSpeed"
  payload_low_speed: "1"
  payload_medium_speed: "2"
  payload_high_speed: "3"
  command_topic: "cmnd/sonoff_MBR_fan/FanSpeed"
  payload_off: "0"
  payload_on: "4"
  speeds:
    - "off"
    - low
    - medium
    - high

I had the same issue but then I turned the on/off to a toggle switch by making the following edit:

<paper-button-group>
    <!--<paper-button style="[[btnOffstyle]]" on-click='handleOffTap'>Off</paper-button>-->
    <paper-button style="[[btnLowstyle]]" on-click='handleLowTap'>Lo</paper-button>
    <paper-button style="[[btnMedstyle]]" on-click='handleMedTap'>Med</paper-button>
    <paper-button style="[[btnHighstyle]]" on-click='handleHighTap'>Hi</paper-button>
</paper-button-group>
<ha-entity-toggle state-obj='[[stateObj]]' hass='[[hass]]' in-dialog='[[inDialog]]'></ha-entity-toggle>

Thanks for that. It’s an improvement but I wish there was a way to not have the button for the last speed stay on after you click the off button.

It’s a little confusing what the actual state is unless you really look at at it & think about it.

No problem, I figure that out as well. Change newVal.attributes.speed to newVal.state in this condition block (about line 68):

> stateObjChanged: function (newVal) {
>   if (newVal) {
>     if (newVal.state == 'off') {

I made this edit as well, much better to recognize state of the fan. Great idea!

1 Like

I finally got a chance to try your last code change and it worked to fix the speed status display.

Then I reverted your first change you posted above back to the original and after that it works exactly the way I wanted it to work.

Now it shows four ‘boxes’ (instead of three boxes & a toggle) and when I click on the ‘off’ box it clears the other speed displays and the ‘off’ speed box is turned on.

thank you!

1 Like

anyway to get this to work in hass.io?

Hello,
I am new to Home Assistant, Hassio, and yaml in general.
I have managed to get your custom Fan controller working on the frontend, for the most part.
I did JonMayers fix to get the fan state working, but it only shows off and high, no matter the state.
The buttons work, and control fan speed, but the state is not shown on the buttons properly.
If it is on low, it shows high, if it is on med, it shows high, if it is on high, it shows high, and if it is off, it shows off.
Here is my state-card-custom-fanspeed.html

<dom-module id="state-card-custom-fanspeed">
  <template>
    <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
    <style>
      :host {
        line-height: 1.5;
      }
      paper-button {
		    min-width: 30px;
		    height: 30px;
		    margin: 2px;  
		    background-color:#f3f3f3;
		    border: 1px solid lightgrey; 
		    font-size: 10px !important;
		    float: right !important;   	
      }
    </style>
		<div class='horizontal justified layout'>
		  <state-info state-obj="[[stateObj]]"></state-info>
		  <paper-button-group>
		      <paper-button style="[[btnOffstyle]]" on-click='handleOffTap'>Off</paper-button>
		      <paper-button style="[[btnLowstyle]]" on-click='handleLowTap'>Low</paper-button>
		      <paper-button style="[[btnMedstyle]]" on-click='handleMedTap'>Med</paper-button>
		      <paper-button style="[[btnHighstyle]]" on-click='handleHighTap'>High</paper-button>
		  </paper-button-group>
		</div>
</template>
</dom-module>
<script>
        Polymer({
          is: 'state-card-custom-fanspeed',
          properties: {
            hass: {
              type: Object,
            },
            stateObj: {
              type: Object,
              observer: 'stateObjChanged',
            },
            statusValue: {
              type: String,
              value: 'Unset',
            },
            btnOffstyle: {
              type: String,
              value: '',
            },
            btnHighstyle: {
              type: String,
              value: '',
            },
            btnMedstyle: {
              type: String,
              value: '',
            },
            btnLowstyle: {
              type: String,
              value: '',
            },
          },
          stateObjChanged: function (newVal) {
            if (newVal) {
              if (newVal.state == 'off') {
                this.statusValue = 'off';
                this.btnOffstyle = 'background-color: #42d9f4;';
                this.btnLowstyle = '';
                this.btnMedstyle = '';
                this.btnHighstyle = '';
                this.updateStyles({'--status-text-color': 'gray'});
              } else if (newVal.attributes.speed == 'low') {
                this.statusValue = 'low';
                this.btnOffstyle = '';
                this.btnLowstyle = 'background-color: #42d9f4;';
                this.btnMedstyle = '';
                this.btnHighstyle = '';    
                this.updateStyles({'--status-text-color': 'blue'});       
              } else if (newVal.attributes.speed == 'medium') {
                this.statusValue = 'medium';
                this.btnOffstyle = '';
                this.btnLowstyle = '';
                this.btnMedstyle = 'background-color: #42d9f4;';
                this.btnHighstyle = '';
                this.updateStyles({'--status-text-color': 'yellow'});
              } else if (newVal.attributes.speed == 'high') {
                this.statusValue = 'high';
                this.btnOffstyle = '';
                this.btnLowstyle = '';
                this.btnMedstyle = '';
                this.btnHighstyle = 'background-color: #42d9f4;';
                this.updateStyles({'--status-text-color': 'red'});
              }              
            }
          },
        	handleHighTap: function (ev) {
						this.setSpeed(ev, 'high');
          },
        	handleMedTap: function (ev) {
						this.setSpeed(ev, 'medium');
          },
        	handleLowTap: function (ev) {
						this.setSpeed(ev, 'low');
          },
        	handleOffTap: function (ev) {
            this.setSpeed(ev, 'off');
          },
          setSpeed: function (ev, setspeed ) {
          	if( setspeed == 'off' ){
          		//this.updateStyles({'--status-text-color': '#F44336'});
							//this.updateStyles({'--state-label-badge': '#F44336'});
							var serviceData = {entity_id: this.stateObj.entity_id};
            	this.hass.callService('fan', 'turn_off', serviceData);          		
          		
          	} else {
          		if (this.stateObj.state != 'on'){
								var serviceData = {entity_id: this.stateObj.entity_id, speed: setspeed };
            		this.hass.callService('fan', 'turn_on', serviceData );          			
          		}else{
          			var serviceData = {entity_id: this.stateObj.entity_id, speed: setspeed };
          			this.hass.callService('fan', 'set_speed', serviceData );
          		}
          	}
          	 ev.stopPropagation();
          },
         
});
</script>

and here is my configuration. yaml section pertaining to device

fan:
  - platform: mqtt  
    name: "Katies Ceiling Fan"  
    state_topic: "stat/ifan02_1/RESULT"
    speed_state_topic: "stat/ifan02_1/RESULT"
    state_value_template: >
        {% if value_json.FanSpeed is defined %}
          {% if value_json.FanSpeed == 0 -%}0{%- elif value_json.FanSpeed > 0 -%}2{%- endif %}
        {% else %}
          {% if states.fan.katies_ceiling_fan.state == 'off' -%}0{%- elif states.fan.katies_ceiling_fan.state == 'on' -%}2{%- endif %}
        {% endif %}
    speed_value_template: "{{ value_json.FanSpeed }}"
    availability_topic: tele/ifan02_1/LWT
    payload_available: Online
    payload_not_available: Offline
    speed_command_topic: "cmnd/ifan02_1/FanSpeed"
    payload_low_speed: "1"
    payload_medium_speed: "2"
    payload_high_speed: "3"
    command_topic: "cmnd/ifan02_1/FanSpeed"
    payload_off: "0"
    payload_on: "2"
    qos: 1
    retain: false
    speeds:
      - low
      - medium
      - high
light:
  - platform: mqtt
    name: "Katies Ceiling Fan Light"
    command_topic: "cmnd/ifan02_1/power1"
    state_topic: "stat/ifan02_1/POWER1"
    availability_topic: tele/ifan02_1/LWT
    payload_available: Online
    payload_not_available: Offline
    qos: 1
    payload_off: "OFF"
    payload_on: "ON"
    retain: false

Perhaps I’ve got conflicting instructions in the two places? I am stumbling my way through this, and learning something new every day.

Here is my working iFan02 code:

  - platform: mqtt  
    name: "Master Bedroom Fan"
    command_topic: "cmnd/sonoff_MBR_fan/FanSpeed"
    speed_command_topic: "cmnd/sonoff_MBR_fan/FanSpeed"    
    state_topic: "stat/sonoff_MBR_fan/RESULT"
    speed_state_topic: "stat/sonoff_MBR_fan/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.fan.master_bedroom_fan.state == 'off' -%}0{%- elif states.fan.master_bedroom_fan.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/sonoff_MBR_fan/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

I haven’t looked at all of the custom-ui code you posted so I’m assuming you copied the latest code and then only made the two latest changes talked about between myself & @jonmayer just above this post?