Custom Features for Home Assistant Cards - Buttons, Dropdowns, Selectors, Sliders, Spinboxes, and Toggles

timestamp_custom uses local time by default, but if you set it’s third (first is to the left of the pipe in a filter) argument to false it’ll use UTC time and return the value you’re expecting. This is true for both ha-nunjucks and the Home Assistant default backend templating system. You can also delete the last set seconds line and omit seconds from the final time constructor.

{{ dt.time(hours, minutes) | timestamp_custom('%H:%M', false) }}

You can also use the time class instance method isoformat to get the same result.

{{ dt.time(hours, minutes).isoformat('minutes') }}
1 Like

Thanks for the quick response.

With the suggested adjustments, I was able to implement the desired functionality.

By the way…

Great respect for your work. Your project offers a fantastic and relatively simple way to customize your system. It’s also a great problem solver when other cards simply don’t fit or don’t offer the necessary functionality.

Keep up the good work!

1 Like

I’m hitting a problem when trying to pass variables to scripts from a Custom Features Button using templated values. The nunjucks templating engine seems to be converting certain characters to html encoding, which nullifies my attempts to match specific strings in the called script.

For example supposeselect.spotify_albums has the value Yann Tiersen - The Lighthouse - Everything's Calm, comprised of a string in the form %artist% - %album%.

I pass a value for album to my script as follows:

  {%- set artist = states('select.spotify_albums').split(' - ')[0] %}
  {{ states('select.spotify_albums').split(artist ~ " - ")[1] }}

In Developer Tools > Template (which I recognise is using HA’s templating engine) this produces The Lighthouse - Everything's Calm as expected. But looking at the traces for the script when called from a Custom Features Button, it shows the album variable as:

The Lighthouse - Everything's Calm

which causes it to fail in a matching routine used in the script.

Is there a way I can code round this? Or should I not be expecting this to work?

Use the safe filter to prevent characters from being HTML escaped.

{%- set artist = states('select.spotify_albums').split(' - ')[0] | safe %}
{{ states('select.spotify_albums').split(artist ~ " - ")[1] | safe }}
3 Likes

So simple! - thanks so much.

1 Like

I noticed a potential bug with the time format.

Setting, 24 hour clock:

Tile card (marked green) shows 24 hour clock, custom features card (marked pink) shows 12 hour clock for the same entity:

That’s expected behavior with templates for both the HA default jinja backend and this card’s template system. ha-nunjucks has it’s own methods to format states, attribute names and values, numbers, dates, times, and datetimes using user settings as asked for in this issue and seen here in the ha-nunjucks README. But in testing while writing this I realized that I wasn’t taking these localization settings into account when doing so, so I’ve created a bug to fix later.

Fixed the ha-nunjucks number, date, time, and datetime translated template functions to respect user locale settings and released version 4.8.4 of this project and return the same results as Home Assistant’s default frontend logic. Although after I implemented it all I realized that you can also use state_translated to get the same result.

{{ config.entity | state_translated }}
{{ states(config.entity) | date_translated }}
{{ states(config.entity) | time_translated }}
{{ states(config.entity) | datetime_translated }}

Version 4.9.0 adds many new Material Design 3 button and dropdown type variants.

  • FAB (floating action buttons) buttons.
  • Standard and vibrant menus.
  • FAB menus.

It also adds some fixes to toggle checkbox and input features to use newer Home Assistant theme style tokens, and makes it so that Material Design 3 buttons and sliders will now scale with --feature-height in their font size and border radii.

2 Likes

Dear @Nerwyn :

First of all, thank you for the great work and for continuing to update this card. It’s very useful.

I’m seeing two styling issues with custom:service-call input rows after recent updates:

  1. The --feature-button-spacing variable I used before to control the horizontal space between the button/icon area and the input field no longer seems to have any effect. I specifically want to adjust the gap between the button and the input value horizontally.

  2. In the dropdown styling section, when the dropdown is collapsed/minimized, it still leaves a visible white horizontal bar instead of hiding completely.

This used to work before, so I’m wondering if there was a layout/CSS selector change or if this is a regression. Is there a new recommended way to style the button/input spacing and fully hide the dropdown content when closed?
I'm on version 4.9.4 and have cleared cache.

  1. The --feature-button-spacing variable I used before to control the horizontal space between the button/icon area and the input field no longer seems to have any effect. I specifically want to adjust the gap between the button and the input value horizontally.

--feature-button-spacing still works for me, none of the recent changes should have affected that. Where are you setting it? At the card or row level? Using the built in style options or card-mod? If set with card-mod at the card level the Home Assistant card may be overwriting it with it's own value for --feature-button-spacing. You can also try setting .row { gap: 16px } at the row level styles.

In the dropdown styling section, when the dropdown is collapsed/minimized, it still leaves a visible white horizontal bar instead of hiding completely.

The dropdown styles were heavily modified in the last release so this could be a bug, but I haven't been able to reproduce it on any browser or device. What device/browser/app are you seeing this on? Do you have any custom styles applied to it?

Hi @Nerwyn :
Thank you for your reply! Maybe something special with my setup, but the dropdown issue is most likely caused by the below styles:

              :host {
                .dropdown {
                  top: 0%;
                  left: -117%;
                  width: 80px;
                  transform: none;
                  overflow-y: hidden;
                  color: var(--idle-color);
                  border-radius: var(--tile-border-radius);
                  background-color: var(--dropdown-bg-color);
                  backdrop-filter: blur(10px);
                }
                ....

When I removed this part it works as expected, but I'd like to fix the location of the dropdown hence the workaround I found now is to add:

              :host(:not([open])) .dropdown {
                opacity: 0 !important;
              }

That did solve the problem, please let me know if you see better ways of styling it.
Regarding the --feature-button-spacing, the code below used to work:

      - type: custom:service-call
        entries:
          - type: input
            entity_id: input_datetime.aquarium_last_maintenance
            icon: mdi:fishbowl-outline
            label: Last Maintenance
            tap_action:
              action: perform-action
              target:
                entity_id: input_datetime.aquarium_last_maintenance
              perform_action: input_datetime.set_datetime
              data:
                date: "{{ value }}"
            thumb: date
            step: 1
            range:
              - '{{ states("input_datetime.aquarium_last_maintenance")}}'
              - '{{ (now() | as_datetime).strftime("%Y-%m-%d") }}'
            styles: |-
                  .......
              }
              :host {
                --feature-border-radius: var(--tile-border-radius);
              }
        styles: |-
          :host {
            --feature-border-radius: var(--tile-border-radius);
            **--feature-button-spacing: 5px;**
          }
          @media (max-width: 750px) {
            :host {
              --feature-height: 34px !important;
            }

Now the workaround I found is to add below:

      - type: custom:service-call
        entries:
          - type: input
            entity_id: input_datetime.aquarium_last_maintenance
            icon: mdi:fishbowl-outline
            label: Last Maintenance
            tap_action:
              action: perform-action
              target:
                entity_id: input_datetime.aquarium_last_maintenance
              perform_action: input_datetime.set_datetime
              data:
                date: "{{ value }}"
            thumb: date
            step: 1
            range:
              - '{{ states("input_datetime.aquarium_last_maintenance")}}'
              - '{{ (now() | as_datetime).strftime("%Y-%m-%d") }}'

            styles: |-
              :host {
                gap: 4px;
              }
.......

Sorry for the messy styling, no expert in css so had to try and error for hours to find a workaround.

Hi,

Could you please share the config of the card shown in the release note, in order to recreate a tile card with the tile icon buttons to make it look similar to a regular one using only custom features card ? I am struggling to achieve a one to one copy, ie. with the name and state on 2 lines with different fonts (among other things).

Thank you very much !

Yup! Here it is in a tile card so you can see it side by side with the original tile icon and labels.

type: tile
grid_options:
  columns: 12
  rows: 2
entity: light.desk_lights
vertical: false
features:
  - type: custom:service-call
    entries:
      - type: button
        entity_id: light.desk_lights
        icon: mdi:desk
        tap_action:
          action: toggle
          target:
            entity_id: light.desk_lights
          data: {}
        thumb: tile-icon
        styles: |-
          :host {
            left: -2px;
          }
      - type: button
        entity_id: light.desk_lights
        icon: ""
        unit_of_measurement: "%"
        autofill_entity_id: false
        value_attribute: brightness
        thumb: transparent
        label: Desk Lights
        styles: |-
          :host {
            justify-content: flex-start;
            flex-direction: row;
          }
          .label {
            display: flex;
            flex-direction: column;
            
            width: fit-content;
            font-size: 14px;
            font-weight: 500;
          }
          .label::after {
            content: '{{ value or 'Off' }}{{ unit if value or '' }}';
            align-self: flex-start;
            font-size: 12px;
            font-weight: 400;
          }
        tap_action:
          action: more-info
    styles: |-
      :host {
        --feature-button-spacing: 8px;
      }
features_position: bottom

Thank you so much !

Version 4.10 adds dynamic dropdown and selector options, and templatable feature values.

You can now define dropdown and selector options using either an entity attribute or a template that resolves to a list. The config of each option is defined using an option template object, which you can modify as needed.

If you do not define an entity for a feature, you can now instead define it's value using a template string or static value.

Thanks to @kristofferR for implementing dynamic options.

1 Like

Edit: You can ignore this. I’ve figured it out. I had to add an alternate checked value for the toggle.

Thank you so much for the work you’ve put into this, as well as the help you’ve been giving the community! I have a question about the toggle, if you have a moment.

I’m trying to use a toggle to control a simple door lock … and by all appearances, it looks right. When I swipe the toggle to lock the door, it does indeed lock, but after about 3 seconds, the toggle flips back to its original state, rather than staying on the “locked” position. The door lock itself is uneffected by this flip. I’ve included the YAML for the toggle below:

type: custom:custom-features-card
features:
  - type: custom:service-call
    entries:
      - type: toggle
        entity_id: lock.front_door
        tap_action:
          action: toggle
          target:
            entity_id: lock.front_door
          data: {}
        swipe_only: true
        thumb: default
        label: Front Door
        unchecked_icon: mdi:lock-open
        checked_icon: mdi:lock
        autofill_entity_id: true
        check_numeric: true
        allow_list: true
        value_attribute: lock_state
        haptics: false
transparent: true

Any insight you have would be greatly appreciated!