Fun with custom:button-card

Ah. I was lacking the id:
Thanks one more time.

The id is important when merging state settings because they are an array. If you don’t use the id then they are added to the array as new items. For example, if your template defines value: 'on' and your button also defines value: 'on' you will have 2 entries in your array for ‘on’ instead of one combined entry, and only one of them will be used (probably the one defined in the template because it is first in the final array). Merging states depends on the id because merging by value has significant challenges: the value could be javascript, or even if value is a literal the operator can be different and change the context what what the value represents. A nice benefit of using the id to merge state configs is that you don’t have to re-define the value or operator. This is especially useful when that value is javascript in the template.

Thanks for the explanation. I was trying to leverage that a little further. My presence device (person.russell_smith) has several states (home, not_home, Work, Church (as defined by my zones)). I cast this to binary_sensor.russ_sensor to toggle home/away status. I thought it would be interesting to use the multi-status “person” device in my indicator. At first I thought I could do something like

                - entity: person.russell_smith
                  name: Russ
                  template: standard-button
                  type: 'custom:button-card'
                  aspect_ratio: 1/1.07
                  styles:
                    name:
                      - font-size: 0.47em
                  state:
                    - value: home
                      id: value_on
                      name: Russ home
                      icon: 'mdi:home'
                    - value: not_home
                      id: value_off
                      name: Russ away
                      icon: 'mdi:home-outline'
                      variables:
                        text_color_off: yellow
                    - value: Work
                      id: value_off
                      name: Russ work
                      icon: 'mdi:office-building'
                      variables:
                        text_color_off: powderblue
                    - value: Church
                      id: value_off
                      name: Russ church
                      icon: 'mdi:church'
                      variables:
                        text_color_off: plum

but that didn’t work. So I added two more states to the standard_button:

    state:
      - id: value_on
        value: '[[[ return variables.value_on ]]]'
        styles:
          card:
            - background-color: '[[[ return variables.background_color_on ]]]'
          name:
            - color: '[[[ return variables.text_color_on ]]]'
          icon:
            - color: '[[[ return variables.text_color_on ]]]'
            - opacity: 1
      - id: value_off
        value: '[[[ return variables.value_off ]]]'
        styles:
          card:
            - background-color: '[[[ return variables.background_color_off ]]]'
          name:
            - color: '[[[ return variables.text_color_off ]]]'
          icon:
            - color: '[[[ return variables.text_color_off ]]]'
            - opacity: 0.5
      - id: value_2
        value: '[[[ return variables.value_2 ]]]'
        styles:
          card:
            - background-color: '[[[ return variables.background_color_2 ]]]'
          name:
            - color: '[[[ return variables.text_color_2 ]]]'
          icon:
            - color: '[[[ return variables.text_color_2 ]]]'
            - opacity: 0.5
      - id: value_3
        value: '[[[ return variables.value_3 ]]]'
        styles:
          card:
            - background-color: '[[[ return variables.background_color_3 ]]]'
          name:
            - color: '[[[ return variables.text_color_3 ]]]'
          icon:
            - color: '[[[ return variables.text_color_3 ]]]'
            - opacity: 0.5

and then coded my indicator like this:

                - entity: person.russell_smith
                  name: Russ
                  template: standard-button
                  type: 'custom:button-card'
                  aspect_ratio: 1/1.07
                  styles:
                    name:
                      - font-size: 0.47em
                  state:
                    - value: home
                      id: value_on
                      name: Russ home
                      icon: 'mdi:home'
                    - value: not_home
                      id: value_off
                      name: Russ away
                      icon: 'mdi:home-outline'
                      variables:
                        text_color_off: yellow
                    - value: Work
                      id: value_2
                      name: Russ work
                      icon: 'mdi:office-building'
                    - value: Church
                      id: value_3
                      name: Russ church
                      icon: 'mdi:church' 

It works, but I’m not sure why I could just load into the value_off id array with the Work and Church values.

I think in your first yaml you just needed to remove the id for Work and Church. You have id: value_off for not_home, Work and Church…effectively merging them together and clobbering the first 2 with the last one. You just need Work and Church to be added to the array as separate states. You only need the id’s if needing to configure the same states in multiple templates and/or the top level button. If you want to keep the id’s on them, make them unique.

1 Like

I see… and now I understand just a tad more of the way this works.
Thank you.

I decided to do a similar thing with the garage “cover” as follows:

                    - entity: cover.garage_door_opener
                      template: standard-button
                      type: 'custom:button-card'
                      aspect_ratio: 1/1.3
                      styles:
                        name:
                          - font-size: 0.47em
                      state:
                        - value: open
                          id: value_on
                          name: Overhead open
                        - value: closed
                          id: value_off
                          name: Overhead closed
                        - value: opening
                          name: Overhead opening
                          icon: 'mdi:arrow-up-bold-box-outline'
                        - value: closing
                          name: Overhead closing
                          icon: 'mdi:arrow-down-bold-box-outline'

That all works wonderfully, but I also tried changing the background color for opening and closing by adding the lines

                      variables:
                        text_color_off: DarkCoral

right under the icon setting. That didn’t work. Any ideas?

I also added the code

                      tap_action:
                        action: call-service
                        service: cover.toggle
                        service_data:
                          entity_id: cover.garage_door_opener

So now, instead of calling up the “more information” window (which will actually let me open or close the door, but it needs one more tap to open and one more to close the window), a tap will toggle the door open or closed depending on its current state.

I don’t believe variables can be set in the state config. If you want to change the background color in a state config you need to use styles:

                        - value: opening
                          name: Overhead opening
                          icon: 'mdi:arrow-up-bold-box-outline'
                          styles:
                            card:
                              - background-color: DarkCoral
1 Like

Oops. Right. Thanks again.

Is there some simple way to combine two binary items into a tri-state item?
To be specific, I already have a group.family that groups the people into one entity. It’s set in the default way so when anyone is home it’s state is home.
Here’s my indicator code:

        - entity: group.family
          name: Family
          template: standard-button
          type: 'custom:button-card'
          aspect_ratio: 1/1.1
          styles:
            name:
              - font-size: 0.47em
          variables:
            background_color_on: darkgrey
            background_color_off: darkgrey
            text_color_on: yellow
          state:
            - value: home
              id: value_on
              name: Some Home
              icon: 'mdi:home-circle'
            - value: not_home
              id: value_off
              name: All Away
              icon: 'mdi:home-circle-outline'

All that works flawlessly and it’s all I need for automations because they only care whether no one is at home or anyone is home. For display purposes, though, it would be nice to have it change to “all home” when everyone is present. I can set up another group with the same entities, but using the “all” option so that it’s only home when everyone’s here, but is there a way to combine both entities into one button which would display as above in all situations except when everyone’s here and then it would display “all home” and possibly use a brighter color than yellow to call attention to the fact?
The other possibility is to throw out the current scheme and create a virtual sensor device with three states: 0-home, 1-some away, 2-all away, and then throw some code to count presences. (Either count everyone at home each time any presence changes – absolutely reliable, but processing intense, or count up and down based on when any of the presences changes from home to not or vice-versa – less processing intense, but could there be an error in the count when two presence changes occur nearly simultaneously (say in the likely situation when we all leave in the same vehicle?))

I actually figure this one out myself :slight_smile:
What I did was set up an input_select with values of “none”,“some”, and “all”. In node-RED I created a flow which get triggered any time anyone comes home or leaves home. It then uses an entities node to get all my people (any entity_id that starts with person) as an array. Then I use a function that passes both the total number of people and a count of the filtered people whose state is “home”. I then set up text which is “none” (count=0), “all” (count=total), or “some” (whatever’s left) and set the option on my input_select using that. The input_select that is thus automated goes into a custom:button card as follows:

        - entity: input_select.family_presence
          name: Family
          template: standard-button
          type: 'custom:button-card'
          aspect_ratio: 1/1.1
          styles:
            name:
              - font-size: 0.47em
          variables:
            background_color_on: darkgrey
            background_color_off: darkgrey
            text_color_on: yellow
          state:
            - value: none
              id: value_off
              name: none Home
              icon: 'mdi:home-circle-outline'
            - value: some
              id: value_on
              name: some Home
              icon: 'mdi:home-circle'
            - value: all
              name: all Home
              icon: 'mdi:home-circle'
              styles:
                card:
                  - background-color: darkgrey
                name:
                  - color: greenyellow
                icon:
                  - color: greenyellow

Viola! I have an indicator that glows soft white when no one is home, yellow when someone is home, and green when everyone is home with icons and text to match the state.
I’m really enjoying being able to have such control over the interface.

Your logic to compare total and filtered counts is exactly what I was going to suggest. I don’t use node-red, but that sounds like a workable solution. An alternative to consider is a template sensor, which would avoid needing an input_select. The logic of finding entities starting with ‘person’ and doing the counting would be the same as what you did in node-red. Both approaches are equally effective.

A “template sensor”… I’m imagining something like:

sensor:
  - platform: template
    sensors:
      family_sensor:
        value_template: "{{ "none", "some", "all" }}"
        icon_template: >-
          {% if is_state('family_sensor', 'none') %}
            mdi:home-outline
          {% else %}
            mdi:home
          {% endif %}

Is that even close. (I’m still working my way through template lore.)

Along those lines, yes. The logic for value_template can be the full logic of what you are doing in Node-Red instead of doing node-red and input_select at all.

Wow. I could benefit from a partial example of that kind of code within the template. (I’m not fluent in YAML yet. Node-RED lets me code in javascript.)
I have three person entities (person.russ, person.janette, and person.katie). I made use of a filter function that returns a subset of an array matching a criteria (item.state===‘home’). I also used .length (to return the number of items in the array). I would have used .substring(), but the entities node allows you to extract only those whose entity_id starts with “person” so I used that instead.

I hadn’t done this particular kind of template before, so I gave it a shot using the Template tab in the Developer Tools page of Home Assistant. It’s a great way to try out different templates and see what you get for results.

{% set items = states.person %}
{% set all = items|length %}
{% set home = items|map(attribute="state")|select("equalto","home")|list|length %}
{{ "nobody home" if home == 0 else "all home" if home == all else "some home" }}

The first line is getting all entities that are in the person domain (i.e., the part of the entity_id before the dot). The second line just puts the length of that list into a variable to make the final logic easier to read. The third line maps the entities in the list to just their state values, then filters the list by value “home”, then re-casts to a list and counts it. The last line determines the state based on the value comparisons. I wasn’t expecting to have to re-cast the filter result to a list, but whatever object type the filter returns doesn’t have a length property so that seems necessary.

There are probably other good references out there, but my go-to references for jinja scripting are the Home Assistant Templating page and the Jinja Documentation. It admittedly took me a while of using templates to get a handle on them, and I still have to research the syntax every time I’m making a new template.

Thanks. I’ll be gleaning over the links you listed and working with the Template tab a lot to see what else I can do with templating as opposed to simple automation. In every Home Automation system I’ve worked with before there was always a hard line between what you could do and what you couldn’t do because you weren’t in charge of the interface. The thing I like about HA is that this isn’t the case. The only caveat is the learning curve. As long as I stick to the line that I’ll do things when I can figure out how instead of artificially wanting it done now, I’ll be OK. (It’s already miles ahead of my old system.)

1 Like

Here’s what I ended up with:

sensor:
  - platform: template
    sensors:
      family_sensor:
        value_template: >- 
          {% set items = states.person %}
          {% set all = items|length %}
          {% set home = items|map(attribute="state")|select("equalto","home")|list|length %}
          {{ "none home" if home == 0 else "all home" if home == all else "some home" }}
        icon_template: >-
          {% if is_state('family_sensor', 'none') %}
            mdi:home-outline
          {% else %}
            mdi:home
          {% endif %}

I’m really liking the power of templating. The one downside is that it requires a reboot to reload configuration.yaml when I change something.
I haven’t moved the coloration yet. I’m thinking it’s better to have that in the Card Configuration anyway.
I’m thinking I might move the device templates to a template.yaml file to keep my configuration.yaml cleaner and I see myself adding a lot more of those clever templates over time.

1 Like

No need to reboot if you’re on core 0.117.x (or maybe 0.116.x; I forget when they added it) you can use the “Reload Template Entities” link on the Configuration => Server Controls page.

I have my configuration.yaml split into multiple files. It’s nice to have smaller files to work with and easier to find what I’m looking for. I have enough entity configurations that I have a folder for each type (sensors, switches, etc.) and a file for each device (some files have more than one entity, but they are usually coming from same device source). I got some inspiration of how to organize my configs from frenck’s config repo, which is really comprehensive. This video that he did with Dr. ZZS is worth watching for great yaml organization tips.

I also recently started managing my dashboards as yaml files too, so that I can share content across different dashboards. My custom button card templates in total are over 1000 lines and it was getting to be annoying making changes in one dashboard and then having to copy-paste the whole set of templates to another dashboard via the raw configuration editor. Plus I was having to copy-paste a bunch of my dashboard cards between views and dashboards any time I changed one of them. Maintenance headache for sure. By taking manual control of the dashboards as files I am able to organize everything as separate files and use !include to insert them into any dashboard. The tradeoff is not being able to edit them in the UI, but I can edit the files and choose the “Refresh” menu option to reload the changes. The “Refresh” option appears in the same place as the “Edit” option in the UI, in the triple-dot menu.

Regarding my previous comment about not being able to edit yaml file dashboards in the UI, I found a workaround for at least experimenting in the UI on a yaml file dashboard. You can’t save changes in the UI, but you can experiment with different settings and then copy the revised yaml to your appropriate file.