Custom UI with Buttons - Fan Control

I’ve figured this out so I will post my code here for anyone else who may find it useful.

First, I have a template sensor which has 4 states, disarmed, disarmed_stay, armed_away, and armed_stay. This template sensor is based off a combination of input_booleans that change with various automatons.

I followed the approach of @Airpal and put the following code in a single file in /www/custom_ui:

<dom-module id="state-card-custom_alarm">
  <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: 0px 0px 0px 0px;
        padding: 5 5 5 5;
      }
      .info {
        font-family: var(--paper-font-body1_-_font-family); -webkit-font-smoothing: var(--paper-font-body1_-_-webkit-font-smoothing); font-size: var(--paper-font-body1_-_font-size); font-weight: var(--paper-font-body1_-_font-weight); line-height: var(--paper-font-body1_-_line-height);min-width:10px;white-space:nowrap;float:left
      }
      .info {
        margin-left:16px
      }
      .name {
        white-space: var(--paper-font-common-nowrap_-_white-space); overflow: var(--paper-font-common-nowrap_-_overflow); text-overflow: var(--paper-font-common-nowrap_-_text-overflow);color:var(--primary-text-color);line-height:40px
      }
      .name[in-dialog],:host([secondary-line]) .name {
        line-height:20px
      }
      .status,::content> * {
        white-space: var(--paper-font-common-nowrap_-_white-space); overflow: var(--paper-font-common-nowrap_-_overflow); text-overflow: var(--paper-font-common-nowrap_-_text-overflow);color:var(--status-text-color);
      }
      .badge {
        position:relative;display:inline-block;width:40px;color:var(--status-text-color);border-radius:50%;height:40px;text-align:center;background-size:cover;line-height:40px
      }
    </style>
<div class='horizontal justified layout'>
    <div class='horizontal start-justified layout'>
      <div class="badge">
        <state-badge class="badge" state-obj="[[stateObj]]"></state-badge>
      </div>
      <div class="info">
        <div class="name" in-dialog>Status:</div>
        <div class="status">[[statusValue]]</div>
      </div>
    </div>
  <paper-button-group>
        <paper-button
        style="background-color:#4CAF50; color: #FFFFFF;"
        on-tap='handleDisarmTap'
        hidden$='[[!disarmButtonVisible]]'>Disarm</paper-button>
      <paper-button
      style="background-color:#673AB7; color: #FFFFFF;"
        on-tap='handleStayTap'
        hidden$='[[!armStayButtonVisible]]'> Arm Stay </paper-button>
      <paper-button
      style="background-color:#F44336; color: #FFFFFF;"
        on-tap='handleAwayTap'
        hidden$='[[!armAwayButtonVisible]]'>Arm Away</paper-button>
  </paper-button-group>
</div>
</template>
</dom-module>
<script>
        Polymer({
          is: 'state-card-custom_alarm',
          properties: {
            hass: {
              type: Object,
            },
            stateObj: {
              type: Object,
              observer: 'stateObjChanged',
            },
            disarmButtonVisible: {
              type: Boolean,
              value: false,
            },
            armStayButtonVisible: {
              type: Boolean,
              value: false,
            },
            armAwayButtonVisible: {
              type: Boolean,
              value: false,
            },
            statusValue: {
              type: String,
              value: 'unknown',
            },
          },
          stateObjChanged: function (newVal) {
            if (newVal) {
              if (newVal.state == 'disarmed_stay') {
                this.disarmButtonVisible = false;
                this.armStayButtonVisible = true;
                this.armAwayButtonVisible = false;
                this.statusValue = 'disarmed';
                this.updateStyles({'--status-text-color': '#4CAF50'});
              } else if (newVal.state == 'disarmed') {
                 this.disarmButtonVisible = false;
                 this.armStayButtonVisible = true;
                 this.armAwayButtonVisible = true;
                 this.statusValue = 'disarmed';     
                this.updateStyles({'--status-text-color': '#4CAF50'});       
              } else if (newVal.state == 'armed_stay') {
                this.disarmButtonVisible = true;
                this.armStayButtonVisible = false;
                this.armAwayButtonVisible = false;
                this.statusValue = 'armed stay';
                this.updateStyles({'--status-text-color': '#673AB7'});
              } else if (newVal.state == 'armed_away') {
                this.disarmButtonVisible = true;
                this.armStayButtonVisible = false;
                this.armAwayButtonVisible = false;
                this.statusValue = 'armed away';
                this.updateStyles({'--status-text-color': '#F44336'});
              }              
            }
          },
          handleDisarmTap: function (ev) {
            var serviceData = {entity_id: "input_boolean.home_switch"}
            this.hass.callService('input_boolean', 'turn_on', serviceData);
            this.stopProp(ev);
          },
          handleStayTap: function (ev) {
            var serviceData = {entity_id: "input_boolean.stay_switch"}
            this.hass.callService('input_boolean', 'turn_on', serviceData);
            this.stopProp(ev);
          },
          handleAwayTap: function (ev) {
            var serviceData = {entity_id: "input_boolean.away_switch"}
            this.hass.callService('input_boolean', 'turn_on', serviceData);
            this.stopProp(ev);
          },
          stopProp(ev) {
            ev.stopPropagation();
          },
});
</script>

So far this is working great in both Chrome and Safari. Next, I’m considering extending this idea to lights and other switches to replace the toggle with a button.

Here is my complete alarm panel:

7 Likes