Custom UI with Buttons - Fan Control

@travis
apparently a bug

@travis @juan11perez I fixed this by replacing on-tap with on-click

1 Like

great
many thanks John

Its gone!..Thanks

@travis
thanks for you script but I dont know much about the custom state card :frowning:
can you help me edit a bit??

I have a RF fan with 1 command to change from Low => Medium => High => Low … so I make a script to switch a fan speed something like

If current is low, change to high =>> send 2 command.
If current is Medium, change to high =>> send 1 command.

How can I map those script to the html card? :thinking:
Really thanks and feel free if you can help.

Good day, anyone can advise on how to insert a picture onto the paper-button

<paper-button ###image### on-click=“btntap1”>Ø

thank you

i’ve got the same question, i have a remote which has one button to go from low-med-high on successive hits. Any solution to this is greatly appreciated. thanks in advance.

Hi, i have added a picture like this:

<paper-button style='background-color:#ff0000;background-image: url("/local/your-image.png");' on-tap="btntst">Test button!</paper-button>

The *.png file must be placed in ~/.homeassistant/www

thank you for your help

Did you ever get the custom state card fan speed control working?

If so could you share your steps and configuration?

Actually couldn’t follow this long thread and get something working so i came up with my own. This is working and super easy to get setup. Let me know how easy my instructions are to setup.

@undertoe I used your code as a template to make a custom card for alarm_control_panel. I want to add a second line under the entity name like @jamesdawson3 alarm_control_panel above to show current status. Can either of you guys, or @andrey, give me any advice?

image

<dom-module id="state-card-custom-alarm_control_panel">
  <template>
    <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
    <style>
      :host {
        line-height: 1.5;
      }
      paper-button {
        float: left !important;
        font-size: 12px !important;
        height: 30px;
        margin: 3px;
        min-width: 75px;
      }
    </style>
    <div class='horizontal justified layout'>
      <state-info state-obj="[[stateObj]]"></state-info>
      <paper-button-group>
        <paper-button raised on-click='handleDisarmTap' hidden$='[[!disarmBtnVisible]]'>Disarm</paper-button>
        <paper-button raised on-click='handleAwayTap' hidden$='[[!awayBtnVisible]]'>Away</paper-button>
        <paper-button raised on-click='handleHomeTap' hidden$='[[!homeBtnVisible]]'>Home</paper-button>
        <paper-button raised on-click='handleNightTap' hidden$='[[!nightBtnVisible]]'>Night</paper-button>
      </paper-button-group>
    </div>
  </template>
</dom-module>
<script>
  Polymer({
    is: 'state-card-custom-alarm_control_panel',
    properties: {
      hass: {
        type: Object,
      },
      stateObj: {
        type: Object,
        observer: 'stateObjChanged',
      },
      statusValue: {
        type: String,
        value: 'Unknown',
      },
      disarmBtnVisible: {
        type: Boolean,
        value: false,
      },
      awayBtnVisible: {
        type: Boolean,
        value: false,
      },
      homeBtnVisible: {
        type: Boolean,
        value: false,
      },
      nightBtnVisible: {
        type: Boolean,
        value: false,
      },
    },
    stateObjChanged: function (newVal) {
      if (newVal) {
        if (newVal.state == 'disarmed') {
          this.statusValue = 'Disarmed';
          this.disarmBtnVisible = false;
          this.awayBtnVisible = true;
          this.homeBtnVisible = true;
          this.nightBtnVisible = true;
          this.updateStyles({'--status-text-color': 'gray'});
        } else if (newVal.state == 'armed_away') {
          this.statusValue = 'Armed Away';
          this.disarmBtnVisible = true;
          this.awayBtnVisible = false;
          this.homeBtnVisible = false;
          this.nightBtnVisible = false;
          this.updateStyles({'--status-text-color': 'gray'});
        } else if (newVal.state == 'armed_home') {
          this.statusValue = 'Armed Home';
          this.disarmBtnVisible = true;
          this.awayBtnVisible = false;
          this.homeBtnVisible = false;
          this.nightBtnVisible = false;
          this.updateStyles({'--status-text-color': 'gray'});
        } else if (newVal.state == 'armed_night') {
          this.statusValue = 'Armed Night';
          this.disarmBtnVisible = true;
          this.awayBtnVisible = false;
          this.homeBtnVisible = false;
          this.nightBtnVisible = false;
          this.updateStyles({'--status-text-color': 'gray'});
        }              
      }
    },
    handleDisarmTap: function (ev) {
      this.setState(ev, 'alarm_disarm');
    },
    handleAwayTap: function (ev) {
      this.setState(ev, 'alarm_arm_away');
    },
    handleHomeTap: function (ev) {
      this.setState(ev, 'alarm_arm_home');
    },
    handleNightTap: function (ev) {
      this.setState(ev, 'alarm_arm_night');
    },
    setState: function (ev, service) {
      var serviceData = {entity_id: this.stateObj.entity_id};
      this.hass.callService('alarm_control_panel', service, serviceData);
      ev.stopPropagation();
    },
  });
</script>
1 Like
<div class='horizontal justified layout'>
      <state-info state-obj="[[stateObj]]"></state-info>
      <paper-button-group>
        <paper-button raised on-click='handleDisarmTap' hidden$='[[!disarmBtnVisible]]'>Disarm</paper-button>
        <paper-button raised on-click='handleAwayTap' hidden$='[[!awayBtnVisible]]'>Away</paper-button>
        <paper-button raised on-click='handleHomeTap' hidden$='[[!homeBtnVisible]]'>Home</paper-button>
        <paper-button raised on-click='handleNightTap' hidden$='[[!nightBtnVisible]]'>Night</paper-button>
      </paper-button-group>
</div>
<div class='horizontal justified layout'>
 <strong>ANY HTML YOU WANT</strong>
</div>

Then just update your Polymer js code to include what you want. console.log(); is your friend :slight_smile:

Gotcha…I’m not familiar with Polymer or HTML so it’s a bit of a learning curve…

Hi Kylerw,
I like the fan control you have with the four buttons, off, low, med & high. I am trying to get the same thing. I am using a Broadlink rm pro to send commands to existing remote ceiling fans with success. Home assistant does’t know the state of the fan and I’m fine with this, as I can turn all fans off by executing fan off command. I would like to collapse all the control entries to one line. Do you or anyone else know how I might achieve this?

This is what I currently see with works perfectly, just doesn’t look the way I would like it to.
I will eventually move light to sonoff switch so Homeassistant knows the state of the light.
23%20pm

config below

switch:

  • platform: broadlink
    host: 192.168.0.241
    mac: ‘##:##:##:##:##:##’
    timeout: 15
    type: rm2_pro_plus
    switches:
    wakeup_the_broadlink:
    friendly_name: “Wakeup the Broadlink”
    command_on: ‘================’
    command_off: ‘================’

Lounge

lounge_light:
  friendly_name: "Light"
  command_on: 'shAcAAgTBwsQEwgSCBIIEggSCBIIEggKEAoQEggAASUAAAAAAAAAAAAAAAA='
lounge_fan_off:
  friendly_name: "Fan Off"
  command_on: 'shQcAAkSCRIJEQkRCRIJCRERCRIJEgkJEQkREggAASQAAAAAAAAAAAAAAAA='
lounge_fan_low:
  friendly_name: "Fan Low"
  command_on: 'shMcAAgJERIIEggSCBIIEggSCRIJEgkJEQoQEggAASQAAAAAAAAAAAAAAAA='
lounge_fan_medium:
  friendly_name: "Fan Medium"
  command_on: 'shYcAAgTCBIIEggKEBIIEggSCBIIEggKEAoQEggAASQAAAAAAAAAAAAAAAA='
lounge_fan_high:
  friendly_name: "Fan High"
  command_on: 'shEcAAkSCRIJEgkSCQkREQkRCRIJEQkJEgkSEgkAASQAAAAAAAAAAAAAAAA='

script:

Lounge

lounge_light:
sequence:
- service: switch.turn_on
entity_id: switch.lounge_light
lounge_fan_off:
sequence:
- service: switch.turn_on
entity_id: switch.lounge_fan_off
lounge_fan_low:
sequence:
- service: switch.turn_on
entity_id: switch.lounge_fan_low
lounge_fan_medium:
sequence:
- service: switch.turn_on
entity_id: switch.lounge_fan_medium
lounge_fan_high:
sequence:
- service: switch.turn_on
entity_id: switch.lounge_fan_high

group:
lounge_fan:
name: Lounge Room
view: no
control: hidden
entities:
- script.lounge_light
- script.lounge_fan_off
- group.lounge_fan

I spent a lot of time to make it work because I didn’t find detailed instructions, so I show my example.

It’s interface for TV control (esp8266 and MQTT).

Unfortunately, I have not been able to understand how to get rid of the pop-up window when tap on the button, event.stopPropagation() and in-dialog=“false” don’t help :frowning:

My version Home Assistant 0.74.0.

configuration.yaml

homeassistant:
  customize: !include customize.yaml
  
frontend:
  extra_html_url:
    - /local/custom_ui/state-card-custom-ui.html
    - /local/custom_ui/state-card-custom-tv.html
  extra_html_url_es5:
    - /local/custom_ui/state-card-custom-ui-es5.html

customize.yaml

switch.kitchen_tv:
  icon: mdi:television
  friendly_name: "TV"
  mqtt_topic: "kitchen/tv/command"
  custom_ui_state_card: state-card-custom-tv

scripts.yaml

control_tv:
  alias: Control TV
  sequence:
  - data_template:
      payload: "{{ command }}"
      topic: "{{ mqtt_topic }}"
    service: mqtt.publish

switches.yaml

- platform: mqtt
  command_topic: "-"
  name: kitchen_tv

/home/homeassistant/.homeassistant/www/custom_ui/state-card-custom-tv.html

<dom-module id="state-card-custom-tv">
  <template>
    <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
    <style>
      :host {
        line-height: 1.5;
      }
      paper-button {
        min-width: 50px;
        height: 50px;
        margin: 2px 0 0;
        background-color: #252525;
      }
	  paper-button-group {
	    text-align: right;
	  }
	  state-info {
	    min-width: 80px !important;
	  }
    </style>
	
	<div class='horizontal justified layout'>

	  <state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
	  
	  <paper-button-group in-dialog='[[inDialog]]'>
	  
		  <paper-button in-dialog='[[inDialog]]' on-tap="power">
			<svg style="width:24px; height:24px" viewBox="0 0 24 24">
			<path fill="#848484" d="M13,3H11V13H13V3M17.83,5.17L16.41,6.59C18.05,7.91 19,9.9 19,12A7,7 0 0,1 12,19C8.14,19 5,15.88 5,12C5,9.91 5.95,7.91 7.58,6.58L6.17,5.17C2.38,8.39 1.92,14.07 5.14,17.86C8.36,21.64 14.04,22.1 17.83,18.88C19.85,17.17 21,14.65 21,12C21,9.37 19.84,6.87 17.83,5.17Z" /></svg>
		  </paper-button>
		  
		  <paper-button in-dialog='[[inDialog]]' on-tap="programDown">
			<svg style="width:24px; height:24px" viewBox="0 0 24 24">
			<path fill="#848484" d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M7,10L12,15L17,10H7Z" /></svg>
		  </paper-button>
		  
		  <paper-button in-dialog='[[inDialog]]' on-tap="programUp">
			<svg style="width:24px; height:24px" viewBox="0 0 24 24">
			<path fill="#848484" d="M12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M17,14L12,9L7,14H17Z" /></svg>
		  </paper-button>
		  
		  <paper-button in-dialog='[[inDialog]]' on-tap="volumeMute">
			<svg style="width:24px; height:24px" viewBox="0 0 24 24">
			<path fill="#848484" d="M12,4L9.91,6.09L12,8.18M4.27,3L3,4.27L7.73,9H3V15H7L12,20V13.27L16.25,17.53C15.58,18.04 14.83,18.46 14,18.7V20.77C15.38,20.45 16.63,19.82 17.68,18.96L19.73,21L21,19.73L12,10.73M19,12C19,12.94 18.8,13.82 18.46,14.64L19.97,16.15C20.62,14.91 21,13.5 21,12C21,7.72 18,4.14 14,3.23V5.29C16.89,6.15 19,8.83 19,12M16.5,12C16.5,10.23 15.5,8.71 14,7.97V10.18L16.45,12.63C16.5,12.43 16.5,12.21 16.5,12Z" /></svg>
		  </paper-button>
		  
		  <paper-button in-dialog='[[inDialog]]' on-tap="volumeDown">
			<svg style="width:24px; height:24px" viewBox="0 0 24 24">
			<path fill="#848484" d="M3,9H7L12,4V20L7,15H3V9M14,11H22V13H14V11Z" /></svg>
		  </paper-button>
		  
		  <paper-button in-dialog='[[inDialog]]' on-tap="volumeUp">
			<svg style="width:24px; height:24px" viewBox="0 0 24 24">
			<path fill="#848484" d="M3,9H7L12,4V20L7,15H3V9M14,11H17V8H19V11H22V13H19V16H17V13H14V11Z" /></svg>
		  </paper-button>
		  
	  </paper-button-group>
	</div>
  </template>
</dom-module>

<script>
  Polymer({
    is: 'state-card-custom-tv',
    properties: {
      hass: {
        type: Object,
      },
      inDialog: {
        type: Boolean,
        value: false,
      },
      stateObj: {
        type: Object,
      },
    },
    power: function (event) {
      this.sendCommand(event, '0');
    },
    programDown: function (event) {
      this.sendCommand(event, '5');
    },
    programUp: function (event) {
      this.sendCommand(event, '4');
    },
    volumeMute: function (event) {
      this.sendCommand(event, '3');
    },
    volumeDown: function (event) {
      this.sendCommand(event, '2');
    },
    volumeUp: function (event) {
      this.sendCommand(event, '1');
    },
    sendCommand(event, command) {
	
      this.hass.callService('script', 'control_tv', {
		mqtt_topic: this.stateObj.attributes.mqtt_topic,
		command: command
	  });
	  
      event.stopPropagation();
	},
});
</script>
4 Likes

Any thoughts on moving this to lovelace? :wink:

sorry for noob question: can your card be used in the lovelace ui?

I have no idea, I haven’t tried Lovelace yet.

Hi, I tried to config the custom fan card as your instruction but when I add an entity card in lovelace, it said "custom element does not exist: state-card-custom_fan, can you instruct more detail about its configuration? thank you.