Dual-entity flashing button template for custom:button-card

Thanks for replying. I ended up making something myself to do it.

@spry-salt, what did you end up doing to achieve it?

Broadly, I created a button with three states - on, off, and changing.

For example:
If the button state is On and the button is then pressed, it changes state to Changing and stays like that until either the action succeeds or fails, then it either changes to Off or reverts to On.

It was fiddly to do but it works and it’s not as confusing as buttons that flick back and forth confusingly between states while the switching and polling is happening.

An example of that might be useful for others, if you don’t mind sharing it.

I use this technique to create a tri-state switch that lets me switch on/off internet access for a device on my home network.

When it’s switching between on and off states, to account for the delay the switch shows an intermediate state. This makes it less confusing for the user as they can see something’s actually in the process of happening.

It uses a Helper:

input_boolean.blocking_my_device)

It also uses two automations, a switch to turn internet access on/off (provided by the ControlD integration in HACS), and a toggle on a card.

      cards:
        - type: custom:button-card
          entity: input_boolean.blocking_my_device
          show_name: true
          name: "My Device"
          show_state: false
          show_label: true
          state:
            - operator: template
              value: |
                [[[
                  var e = entity.state;
                  var x = states['switch.device_my_device_blocking'].state;
                  if (e == 'on' && x == 'on') return 'on'; 
                  if (e == 'on' && x == 'off') return 'changing'; 
                  if (e == 'off' && x == 'on') return 'changing'; 
                  if (e == 'off' && x == 'off') return 'off'; 
                  else return 'Unknown';
                ]]]
              icon: |
                [[[
                  var e = entity.state;
                  var x = states['switch.device_my_device_blocking'].state;
                  if (e == 'on' && x == 'on') return 'mdi:close-network'; 
                  if (e == 'on' && x == 'off') return 'mdi:help-network'; 
                  if (e == 'off' && x == 'on') return 'mdi:help-network'; 
                  if (e == 'off' && x == 'off') return 'mdi:check-network'; 
                  else return 'Unknown';
                ]]]
          label: |
            [[[
              var e = entity.state;
              var x = states['switch.device_my_device_blocking'].state;
              if (e == 'on' && x == 'on') return 'Blocked'; 
              if (e == 'on' && x == 'off') return 'Changing'; 
              if (e == 'off' && x == 'on') return 'Changing'; 
              if (e == 'off' && x == 'off') return 'Unblocked'; 
              else return 'Unknown';
            ]]]
          styles:
            icon:
              - color: |
                  [[[
                    var e = entity.state;
                    var x = states['switch.device_my_device_blocking'].state;
                    if (e == 'on' && x == 'on') return 'red'; 
                    if (e == 'on' && x == 'off') return 'orange'; 
                    if (e == 'off' && x == 'on') return 'orange'; 
                    if (e == 'off' && x == 'off') return 'green'; 
                    else return 'lightgrey';
                  ]]]
          tap_action:
            action: call-service
            service: automation.trigger
            data: {}
            target:
              entity_id: automation.blocking_my_device_change_by_user

Automation 1 - Blocking My Device change by user - this runs when I press the button to switch internet access on/off for my device, and toggles the Helper.

alias: Blocking My_Device change by user
description: ""
trigger: []
condition: []
action:
  - if:
      - condition: template
        value_template: >-
          {{ states('switch.device_My_Device_blocking') ==
          states('input_boolean.blocking_My_Device') }} 
    then:
      - service: input_boolean.toggle
        data: {}
        target:
          entity_id: input_boolean.blocking_My_Device
      - service: switch.toggle
        data: {}
        target:
          entity_id: switch.device_My_Device_blocking
      - service: homeassistant.update_entity
        data: {}
        target:
          entity_id:
            - switch.device_My_Device_blocking
            - sensor.listalldevices_json
        enabled: false
      - if:
          - condition: template
            value_template: >-
              {{ states('switch.device_My_Device_blocking') !=
              states('input_boolean.blocking_My_Device') }} 
        then:
          - wait_for_trigger:
              - platform: template
                value_template: >-
                  {{ states('switch.device_My_Device_blocking') ==
                  states('input_boolean.blocking_My_Device') }} 
                for:
                  hours: 0
                  minutes: 0
                  seconds: 2
            continue_on_timeout: true
            timeout:
              hours: 0
              minutes: 1
              seconds: 0
              milliseconds: 0
  - if:
      - condition: template
        value_template: >-
          {{ states('switch.device_My_Device_blocking') !=
          states('input_boolean.blocking_My_Device') }} 
    then:
      - service: input_boolean.toggle
        data: {}
        target:
          entity_id: input_boolean.blocking_My_Device
mode: single

Automation 2 - Blocking My Device change by REST - this runs when the internet blocking setting for my device actually changes on my DNS provider.

alias: Blocking My_Device change by REST
description: ""
trigger:
  - platform: state
    entity_id:
      - switch.device_My_Device_blocking
condition:
  - condition: template
    value_template: >-
      {{ states('switch.device_My_Device_blocking') !=
      states('input_boolean.blocking_My_Device') }}
action:
  - service: input_boolean.toggle
    data: {}
    target:
      entity_id: input_boolean.blocking_My_Device
mode: single

Sensors and switch:

- platform: rest
  resource: https://api.controld.com/devices
  name: listalldevices_json
  headers:
    content-type: "application/json"
    Authorization: "Bearer api.MY_API_HERE"
  json_attributes_path: "$.body"
  json_attributes:
    - "devices"
  value_template: "{{ value_json.success }}"

- platform: rest
  resource: https://api.controld.com/profiles
  name: listallprofiles_json
  headers:
    content-type: "application/json"
    Authorization: "Bearer api.MY_API_HERE"
  json_attributes_path: "$.body"
  json_attributes:
    - "profiles"
  value_template: "{{ value_json.success }}"



- platform: rest
  resource: https://api.controld.com/devices/MY_DEVICE_ID_HERE
  name: device_my_device_blocking
  method: put
  body_on: '{ "profile_id": "MY_PROFILE1_ID_HERE", "profile_id2": "-1" }'
  body_off: '{ "profile_id": "MY_PROFILE2_ID_HERE", "profile_id2": "-1" }'
  is_on_template: "{{ state_attr('sensor.device_my_device','profile') == 'Block access' }}"
  headers:
    content-type: "application/json"
    Authorization: "Bearer api.MY_API_HERE"


- sensor:
    - name: DeviceNames
      state: >
        {{ states('sensor.listalldevices_json') }}
      availability: >
        {{ states('sensor.listalldevices_json') }}
      attributes:
        device_names: >
          {% set theDeviceList = state_attr('sensor.listalldevices_json','devices') %}
          {{ theDeviceList | map(attribute='name') | list | default }}

    - name: ProfileNames
      state: >
        {{ states('sensor.listallprofiles_json') }}
      availability: >
        {{ states('sensor.listallprofiles_json') }}
      attributes:
        profile_names: >
          {% set theDeviceList = state_attr('sensor.listallprofiles_json','profiles') %}
          {{ theDeviceList | map(attribute='name') | list | default }}

    - name: Device_My_Device
      state: >
        {{ 'My_Device' in state_attr('sensor.DeviceNames','device_names') }}
      availability: >
        {{ (expand( 'sensor.DeviceNames','device_names' ) | rejectattr('state', 'in', ['unknown','unavailable']) | list | count > 0) 
          and ('My_Device' in state_attr('sensor.DeviceNames','device_names')) }}
      attributes:
        profile: >
          {% set theProfile = "empty" %}
          {% set theDeviceList = state_attr('sensor.listalldevices_json','devices') %}
          {% if theDeviceList is not none %}
            {% set theDevice = theDeviceList | selectattr('name', 'eq', 'My_Device') | list %}
            {% if theDevice is not none %}
              {% if 'profile' in theDevice[0] %}
                {% set theProfile = theDevice[0].profile.name %}
              {% endif %}
            {% endif %}
          {% endif %}
          {{ theProfile }}
        profile2: >
          {% set theProfile = "empty" %}
          {% set theDeviceList = state_attr('sensor.listalldevices_json','devices') %}
          {% if theDeviceList is not none %}
            {% set theDevice = theDeviceList | selectattr('name', 'eq', 'My_Device') | list %}
            {% if theDevice is not none %}
              {% if 'profile2' in theDevice[0] %}
                {% set theProfile = theDevice[0].profile2.name %}
              {% endif %}
            {% endif %}
          {% endif %}
          {{ theProfile }}
1 Like

Love this, but struggling a little bit to understand how best to setup. I want to have this active for multiple sensors I have running eg motion, and water.

Any guidance on what to do / copy etc is appreciated.

@CJF077, it was designed for a sensor and a switch but it might work for two sensors. If it doesn’t work as-is, it might need some small adjustments to react correctly to sensor states if they aren’t “off” and “on”. I recommend experimenting with it and reading documentation for the custom button card. The more you learn about how it works the more powerful it will be for you :slight_smile:

1 Like