Fun with custom:button-card

There is one “problem” with the active stroke, it is already full around 85%
I think that is because the start of the active border is not in the middle (but on the left) of the rect?

image

custom_fields:
  RectP: |
    [[[ 
      const state = states['sensor.afzuigkap_programma_voortgang'].state;
      let bgActiveColor;
        if (state >= 90) {
          bgActiveColor = '#00ae5b';
        } else if (state >= 50) {
          bgActiveColor = '#ffbe3e';
        } else if (state >= 25) {
          bgActiveColor = '#ff9532';
        } else if (state >= 0) {
          bgActiveColor = '#ff3e3e';
        } else {
          bgActiveColor = 'rgba(217,217,217,0.1)';
        }
      const value = Math.abs(states['sensor.afzuigkap_programma_voortgang'].state);
      const fill = 'rgba(255,255,255,0.7)';
      const bgColor = '#d9d9d9';
      const width = 60;
      const height = 26;
      const dashArray = 2 * (width + height);
      const dashOffset = dashArray * (1 - value / 100); 

      return `
      <svg viewBox="0 0 75 75">
        <rect x="8" y="8" width="${width}" height="${height}"
          rx="12" ry="12"
          stroke="${bgColor}" stroke-width="1" fill="${fill}" />
        <rect x="8" y="8" width="${width}" height="${height}"
          rx="12" ry="12"
          stroke="${bgActiveColor}" stroke-width="2" fill="none"
          stroke-dasharray="${dashArray}" stroke-dashoffset="${dashOffset}" />
        <text x="38" y="23" fill="rgba(62, 62, 62, 1.0);" 
          font-weight="normal" font-size="16" font-family="Sans-serif" 
          text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle">
          ${value}%
        </text>
      </svg>`;
    ]]]

no, not at all. you can use that same entity in the javascript .

seems to me you are way over your head here, creating difficult configs, while not understanding things.
simply c&p stuff we make for you wont help you in the long run

please educate yourself step by step, and make progress doing so.

playing with entities across the board in the various configurations is basic and required knowledge you should grasp first.

1 Like

Because you manipulate the border radius, the total stroke length is cut off…

const dashArray = 2 * ((width - 6) + (height - 5))
1 Like

:man_facepalming: yes of course haha
Thanks again! have it all working now :smiley:

care to have a look at New badges and card-mod customisation - #67 by Mariusthvdb please?

Seeking the border percentage on a badge is just a but more involved…

I did encounter a problem that if the entity used in the custom field is removed and no longer exists the whole card will not load anymore.

I tried the following but no success:

custom_fields:
  TemperatureC: |
    [[[
      const state = states['sensor.panasonic_wandmodel_woonkamer_inside_temperature'].state;
      if (state is defined) {
      return `
        <ha-card style="width: 55px; height: 25px; padding: 0px 2px 0px 2px;
          color: rgba(62, 62, 62, 1.0); background-color: rgba(255,255,255,0.8); border-color: #d9d9d9; border-radius: 12px;
          font-size: 14px; text-transform: capitalize; font-family: Sans-serif;">
          <span>${states['sensor.panasonic_wandmodel_woonkamer_inside_temperature'].state} °C</span>
        </ha-card> `
      }
    ]]]

Is there any other way to check if the entity exists?

I wanted to share the swipable (over the next 8hrs) weather template that i’ve been trying to refine and optimize for some time now. I use it as a weather preview for the coming hours taking into account if its day or nighttime including current conditions at the top.
The times are full hours past the current time and the weather shows only the temps and conditions.
Now the only thing that’s missing is to inject the time when the sun rises and sets with hours AND minutes. Haven’t figured that out yet.
Any suggestions?

here’s a code snippet from my template:

        - type: custom:button-card
          entity: '[[[ return entity.entity_id ]]]'
          label: >-
            [[[ 
              var index = 6;
              var temperature = parseFloat(states['sensor.weather_temp_h' + index].state);
              return Math.round(temperature) + "<span style>°C</span>"; 
            ]]]
          name: >
            [[[ 
              var index = 6;
              var now = new Date();
              var currentHour = now.getHours();
              var hourOffset = index + 1;
              var nextHour = (currentHour + hourOffset) % 24;
              var period = nextHour >= 12 ? "PM" : "AM";
              var formattedHour = nextHour % 12 === 0 ? 12 : nextHour % 12;
              return formattedHour + '<span style="font-size: 0.8rem;">' + period + '</span>';
            ]]]
          show_label: true
          show_icon: false
          custom_fields:
            icon: >
              [[[ 
                var index = 6;
                var weather = states['sensor.weather_condition_h' + index].state;
                
                var forecastTime = new Date();
                forecastTime.setHours(forecastTime.getHours() + (index + 1));

                var sunriseTime = new Date(states['sensor.home_sun_dawn'].state);
                var sunsetTime = new Date(states['sensor.home_sun_dusk'].state);

                sunriseTime.setFullYear(forecastTime.getFullYear());
                sunriseTime.setMonth(forecastTime.getMonth());
                sunriseTime.setDate(forecastTime.getDate());

                sunsetTime.setFullYear(forecastTime.getFullYear());
                sunsetTime.setMonth(forecastTime.getMonth());
                sunsetTime.setDate(forecastTime.getDate());

                var isNight = forecastTime < sunriseTime || forecastTime >= sunsetTime;

                var iconSuffix = (isNight && !weather.startsWith('clear')) ? '-night' : '';

                return '<img src="/local/weather-icons/' + weather + iconSuffix + '.svg"/>';
              ]]]
          styles: *item_styles


So this is my weather dashboard using the CB-lacars buttons CB=Custom Button
it is animated you can see that here

1 Like

I need your help :pray: I created and modified a beautiful sticky navigation bar based on this (even my wife loves it :grin:). While trying to further evolve the bar, I came across an issue with the custom:button-card:

Is there a way to trigger the refresh of the button based on the return of a javascript? Goal: I would like to highlight the background of the button on the navigation bar based on the currently open view. However, the button does not refresh on a change and “triggers_update: all” only updates the button with an ugly delay.

Currently, the button configuration looks as follows:

            - type: custom:button-card
              icon: mdi:cog
              tap_action:
                action: navigate
                navigation_path: |
                  [[[
                    let popup_name = '#pop-up-system';
                    let current_path = window.location.href;
                    let popup_path_position = current_path.lastIndexOf(popup_name);
                    if (popup_path_position != -1) {
                      popup_name = current_path.substring(0, popup_path_position);
                    };
                    return popup_name;
                  ]]]     
              triggers_update: all
              styles:
                icon:
                  - width: 24px
                  - color: white
                img_cell:
                  - width: 50px
                  - height: 50px
                card:
                  - background: |
                      [[[
                        let popup = window.location.hash;
                        if (popup == '#pop-up-system') return 'grey';
                        else return 'none';
                      ]]]
                  - border: none
                  - padding: 0

You’ll have to use a helper such as an input select or input boolean

Hmm, but that would affect the navigation bar on all client devices at the same time, correct? Is there a way to have client-specific helpers?

What comes to mind is to make an automation that sets the helper based on the user.

Im working on my floorplan dashboard at the moment.

For additional person cards i want to use custom:button-card in a hexagon style.
Grid_Button-Card_Person
I want to set the round entity picture in the middle of the upper half.
On the lower half i want to set the name and the state of my person entity.
But as u see it didnt get to work.

Heres my code:

          - square: true
            type: grid
            card_mod:
              style: |
                :host {
                  position: absolute;
                  left: 21%;
                  top: 7%;
                  opacity: 0.7;
                  mix-blend-mode: normal;
                  }
            cards:
              - type: custom:button-card
                card_mod:
                  style: |
                    :host {
                      aspect-ratio: 1/cos(30deg);
                      clip-path: polygon(50% -50%,100% 50%,50% 150%,0 50%);
                      background: #3B8686;
                    }
                entity: person.micha
                name: Micha
                aspect_ratio: 2/1
                show_entity_picture: false
                state:
                  - value: home
                    styles:
                      custom_fields:
                        icon:
                          - border-color: '#77c66e'
                  - value: not_home
                    styles:
                      custom_fields:
                        icon:
                          - border-color: deepskyblue
                  - value: Dossmann
                    styles:
                      custom_fields:
                        icon:
                          - border-color: '#B83838'
                styles:
                  grid:
                    - grid-template-areas: >-
                        "icon"  
                        "name"
                        "state"
                    - grid-template-columns: 1fr
                    - grid-template-rows: 1fr 
                  custom_fields:
                    icon:
                      - clip-path: circle()
                      - width: 50%
                      - pointer-events: none
                      - display: grid
                      - border: 3px solid
                      - border-color: '#808080'
                      - border-radius: 500px
                      - margin: 0 0 0 0
                      - opacity: 1
                      - align-self: start
                      - justify-self: end
                    name:
                      - align-self: end
                      - justify-self: end
                      - background: none
                      - padding: 5px
                    state:
                      - align-self: start
                      - justify-self: middle
                      - background: none
                      - padding: 5px
                      - margin-top: 5px
                custom_fields:
                  icon: >
                    [[[ return entity === undefined ? null : `<img
                    src="${states[entity.entity_id].attributes.entity_picture}"
                    width="100%">`; ]]]
                  name:
                    card:
                      type: custom:button-card
                      name: Micha
                      styles:
                        card:
                          - color: var(--contrast20)
                          - font-size: 14px
                          - font-weight: 600
                          - background: none
                  state:
                    card:
                      type: custom:button-card
                      entity: person.micha
                      show_icon: false
                      name: |
                        [[[ 
                          return states['person.micha'].state;
                        ]]]
                      state:
                        - value: home
                          name: Daheim
                        - value: not_home
                          name: Unterwegs
                      styles:
                        card:
                          - color: var(--contrast20)
                          - font-size: 13px
                          - background: none
                          - opacity: '0.7'
              - type: custom:button-card
                entity: person.isa
                show_state: true
                show_entity_picture: false
                card_mod:
                  style: |
                    :host {
                      top: 45%;
                      left: -32%;
                      aspect-ratio: 1/cos(30deg);
                      clip-path: polygon(50% -50%,100% 50%,50% 150%,0 50%);
                      background: #3B8686;
                      border: #434589;
                    }
              - type: custom:button-card
                entity: input_boolean.hund_daheim
                name: Willow
                show_entity_picture: false
                styles:
                  entity_picture:
                    - size: 20%
                entity_picture: /local/willow.jpg
                card_mod:
                  style: |
                    :host {
                      left: -63%;
                      aspect-ratio: 1/cos(30deg);
                      clip-path: polygon(50% -50%,100% 50%,50% 150%,0 50%);
                      background: #3B8686;
                    }
            columns: 3

I only use a grid, because i didnt know how i can set a absolute position for a custom-button card. :sweat_smile:

Maybe someone could help me? :slight_smile:

This code renders correctly:

type: custom:button-card
entity: person.tom
show_state: true
show_name: false
show_label: true
label: "[[[ return helpers.relativeTime(states['device_tracker.tom_phone'].last_changed); ]]]"

Now I would like it to instead say “Last Updated: X time ago”. But this seems to fail, rendering as [object Object]:

label: "[[[ return 'Last Updated: ' + helpers.relativeTime(states['device_tracker.tom_phone'].last_changed); ]]]"

Trying as well to copy the example from the documentation here also gives the same result:

label: >
  [[[
    var lastupdated = helpers.relativeTime(states['device_tracker.tom_phone'].last_changed);
    return 'Last Updated: ' + (lastupdated);
  ]]]

Can anyone help with how to achieve this?

This is apparently a known issue, and a workaround is shown here:

I’m trying to pull some latency values from sensors in The Unifi integration into a custom-button-card. The value, in milliseconds, will show up correct in a stadard entities card, however in the custom-card it shows as seconds. See screenshots and code bellow.
How do I format or template the value to show up correct, in ms in the custom-button-card?

From the custom-button-card:

From the stock entity card:

My code:

entity: sensor.hytta_hh_cloudflare_wan_latency
name: Latency Starlink
show_icon: true
show_name: true
show_state: true
styles:
  card:
    - background: var(--card-background-color)
    - height: 70px
    - border-radius: 20px
    - padding: 0px
  name:
    - position: absolute
    - bottom: 45%
    - left: 40%
    - font-size: 15px
    - font-weight: 600
    - color: var(--primary-color)
  state:
    - position: absolute
    - bottom: 20%
    - left: 40%
    - font-size: 15px
    - font-weight: 550
    - color: rgba(96,114,116,0.7)
  icon:
    - position: absolute
    - bottom: 11%
    - left: 2%
    - height: 55px
    - color: var(--bedroom-blue)
type: custom:button-card
entity: sensor.hytta_hh_cloudflare_wan_latency
name: Latency Starlink
show_icon: true
show_name: true
show_state: true
styles:
  card:
    - background: var(--card-background-color)
    - height: 70px
    - border-radius: 20px
    - padding: 0px
  name:
    - position: absolute
    - bottom: 45%
    - left: 40%
    - font-size: 15px
    - font-weight: 600
    - color: var(--primary-color)
  state:
    - position: absolute
    - bottom: 20%
    - left: 40%
    - font-size: 15px
    - font-weight: 550
    - color: rgba(96,114,116,0.7)
  icon:
    - position: absolute
    - bottom: 11%
    - left: 2%
    - height: 55px
    - color: var(--bedroom-blue)
type: custom:button-card
entity: sensor.hytta_hh_cloudflare_wan_latency
name: Latency Starlink
show_icon: true
show_name: true
show_state: true
styles:
  card:
    - background: var(--card-background-color)
    - height: 70px
    - border-radius: 20px
    - padding: 0px
  name:
    - position: absolute
    - bottom: 45%
    - left: 40%
    - font-size: 15px
    - font-weight: 600
    - color: var(--primary-color)
  state:
    - position: absolute
    - bottom: 20%
    - left: 40%
    - font-size: 15px
    - font-weight: 550
    - color: rgba(96,114,116,0.7)
  icon:
    - position: absolute
    - bottom: 11%
    - left: 2%
    - height: 55px
    - color: var(--bedroom-blue)

I’m trying to get a slider-entity-row card to work inside of a button-card. But I’m messing the grid up. I got it working right on desktop but the formatting is all messed up on mobile. See below:

Screenshot 2025-02-07 094059

Can anyone assist in correcting the code so the mobile and desktop formatting are identical?

type: custom:button-card
name: Living Room
entity: light.living_room_lights
icon: mdi:light-recessed
tap_action:
  action: toggle
hold_action: []
show_icon: true
show_state: true
styles:
  card:
    - height: 60px
    - border-radius: 15px
    - font-size: 10px
    - padding-bottom: 0px
    - padding-top: 0px
    - position: relative
  img_cell:
    - align-self: center
    - justify-content: center
  grid:
    - grid-template-areas: "\"i n n\" \" i slider s\""
    - grid-template-columns: 50px auto
    - grid-template-rows: auto auto
  name:
    - justify-self: left
    - font-weight: 600
    - font-size: 14px
    - padding-top: 20px
    - padding-left: 6px
    - padding-bottom: 0px
  state:
    - justify-self: left
    - font-size: 12px
    - font-weight: 400
    - padding-bottom: 30px
    - padding-left: 0px
  icon:
    - justify-self: center
    - padding-top: 0px
    - padding-left: 10px
    - padding-bottom: 30px
    - width: 25px
  custom_fields:
    slider:
      - width: 90%
      - margin-left: 0%
      - justify-self: left
      - font-size: 16px
      - padding-bottom: 30px
      - padding-top: 0px
      - padding-left: 0px
state:
  - value: "off"
    styles:
      card: null
      icon: null
  - value: "on"
    styles:
      card:
        - background-color: rgba(255, 255, 255,.8)
      icon:
        - color: "#FF9500"
      name:
        - color: black
      state:
        - color: black
      custom_fields:
        slider:
          - color: black
custom_fields:
  slider:
    card:
      full_row: true
      type: custom:slider-entity-row
      entity: "[[[ return entity.entity_id ]]]"
      hide_state: true

I am hoping someone can help me because I am tired of fighting with AI to try to figure this out. I am using custom:button-card for my entire dashboard. I am using a base template as shown below.

base:
  variables:
    state_on: >
      [[[ return ['on', 'home', 'heat_cool', 'Running', 'Fresh', 'Charging', 'charging', 'Started', 'locked', 'Empty', 'Dirty', 'Auto'].indexOf(!entity || entity.state) !== -1; ]]]
    state_home: >
      [[[ return ['home'].indexOf(!entity || entity.state) !== -1; ]]]
    state_zone: >
      [[[ return ['Flinner', 'Kelly', 'Stantec', 'Friendship', 'TPA', 'SVS'].indexOf(!entity || entity.state) !== -1; ]]]
    state_away: >
      [[[ return ['not_home'].indexOf(!entity || entity.state) !== -1; ]]]
    state_empty: >
      [[[ return ['Empty'].indexOf(!entity || entity.state) !== -1; ]]]
    state_done: >
      [[[ return ['Done'].indexOf(!entity || entity.state) !== -1; ]]]
    state_auto: >
      [[[ return ['Auto'].indexOf(!entity || entity.state) !== -1; ]]]
    state_used: >
      [[[ return ['Used'].indexOf(!entity || entity.state) !== -1; ]]]
    state_clean: >
      [[[ return ['Clean'].indexOf(!entity || entity.state) !== -1; ]]]
    state_dirty: >
      [[[ return ['Dirty'].indexOf(!entity || entity.state) !== -1; ]]]
    state_charging: >
      [[[ return ['Charging', 'charging'].indexOf(!entity || entity.state) !== -1; ]]]
    state_discharging: >
      [[[ return ['Not Charging', 'not charging', 'discharging'].indexOf(!entity || entity.state) !== -1; ]]]
    state_unlocked: >
      [[[ return ['unlocked'].indexOf(!entity || entity.state) !== -1; ]]]
    state_locked: >
      [[[ return ['locked'].indexOf(!entity || entity.state) !== -1; ]]]
    state: >
      [[[ return !entity || entity.state; ]]]
    entity_id: >
      [[[ return !entity || entity.entity_id; ]]]
    entity_picture: >
      [[[ return !entity || entity.attributes.entity_picture; ]]]
    is_youtube: >
      [[[
        let is_youtube = entity?.attributes?.app_id === 'com.google.ios.youtube',
            sensor = this?._config?.triggers_update,
            media_title = entity?.attributes?.media_title,
            watching_title = states[sensor]?.attributes?.title;
        if (is_youtube && media_title === watching_title) {
            return true;
        }
      ]]]
        translate_home: Home
    translate_not_home: Away
    translate_open: Open
    translate_closed: Closed
    translate_occupied: Occupied
    translate_not_occupied: Empty
    translate_on: "On"
    translate_off: "Off"
    translate_detected: "Detected"
    translate_clear: "Clear"
    translate_playing: "Playing"
    translate_paused: "Paused"
    translate_NotCharging: "Discharging"
  triggers_update:
    - "[[[ return variables.sensor_entity; ]]]"
    - "[[[ return variables.timer_entity; ]]]"
  tap_action:
    action: more-info
  aspect_ratio: 1/1
  show_state: true
  show_icon: false
  styles:
    grid:
      - grid-template-areas: |
          "icon   circle"
          "n      n"
          "s      s"
      - grid-template-columns: repeat(2, 1fr)
      - grid-template-rows: auto repeat(3, min-content)
      - gap: 1.3%
      - align-items: start
      - will-change: transform
      - position: relative
    name:
      - justify-self: start
      - line-height: 110%
      - font-size: 15px
      - font-weight: bold
    state:
      - justify-self: start
      - line-height: 110%
      - font-size: 12px
    card:
      - background-image: >
          [[[
            if (variables.state_home) {
              return `linear-gradient(to bottom, rgba(50,168,82,1) 0%, rgba(50,168,82,1) 50%, rgba(0,0,0,0) 50%)`;
            }
            if (variables.state_away) {
              return 'linear-gradient(to bottom, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_zone) {
              return `linear-gradient(to bottom, rgba(49,130,183,1) 0%, rgba(49,130,183,1) 50%, rgba(0,0,0,0) 50%)`;
            }
            if (variables.state_empty) {
              return 'linear-gradient(to bottom, rgba(50,168,82,1) 0%, rgba(50,168,82,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_done) {
              return 'linear-gradient(to bottom, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_auto) {
              return 'linear-gradient(to bottom, rgba(50,168,82,1) 0%, rgba(50,168,82,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_used) {
              return 'linear-gradient(to bottom, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_clean) {
              return 'linear-gradient(to bottom, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_dirty) {
              return 'linear-gradient(to bottom, rgba(50,168,82,1) 0%, rgba(50,168,82,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_charging) {
              return 'linear-gradient(to bottom, rgba(50,168,82,1) 0%, rgba(50,168,82,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_discharging) {
              return 'linear-gradient(to bottom, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_unlocked) {
              return 'linear-gradient(to bottom, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 50%, rgba(0,0,0,0) 50%)';
            }
            if (variables.state_locked) {
              return 'linear-gradient(to bottom, rgba(50,168,82,1) 0%, rgba(50,168,82,1) 50%, rgba(0,0,0,0) 50%)';
            } else {
              return 'linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 50%, rgba(0,0,0,0) 50%)';
            }
          ]]]
      - overflow: hidden
      - border-radius: 10px
      - border-width: 0px
      - -webkit-tap-highlight-color: rgba(0,0,0,0)
      - transition: none
      - --mdc-ripple-color: >
          [[[
            return variables.state_on
                ? 'var(--contrast1)'
                : '#97989c';
          ]]]
      - color: >
          [[[
            return variables.state_on
                ? 'var(--contrast1)'
                : '#97989c';
          ]]]
      - background-color: >
          [[[
            return variables.state_on
                ? 'var(--contrast20)'
                : 'rgba(115, 115, 115, 0.25)';
          ]]]
  custom_fields:
    circle: >
      [[[ 
        const timerEntity = variables.timer_entity;
        if (!timerEntity) return '';
        
        const timerState = states[timerEntity]?.state;
        return timerState === 'active' ? 'circle_cd' : 'circle_info';
      ]]]

There is alot here I know (I used this thread and have gone from there), but I am fighing with the circle custom_field. I have multiple timers that I want to see on the cards when they are active. I created the circle_cd template that incorporates another custom:button-card to display the countdown. I have circle_info for other information displayed on the cards. I call the two templates as necessary. The code is below.

The issue I am having is when both are included as a template. I have a template in the base file that I was hoping would choose between the two templates based on whether timer_entity is active. If I have one or the other, the templates work as expected. When I have both called out, it does not switch to circle_cd when the timer becomes active.

circle_cd:
  styles:
    custom_fields:
      circle:
        - width: 88%
        - margin: -3% 2% 0 0
        - justify-self: end
        - opacity: 1
  custom_fields:
    circle:
      card:
        type: custom:button-card
        entity: "[[[ return variables.timer_entity; ]]]"
        aspect_ratio: 1/1
        show_icon: false
        show_name: false
        show_state: true
        styles:
          card:
            - border: none
            - background: none
            - background-color: none
            - display: >
                [[[ 
                  return states[variables.timer_entity]?.state === 'active' ? 'block' : 'none'; 
                ]]]
          state:
            - font-size: 14px
            - font-weight: 700
            - color: >
                [[[
                  return variables.state_on
                      ? 'var(--contrast1)'
                      : '#97989c';
                ]]]

circle_info:
  styles:
    card:
      - --c-stroke-color-on: var(--contrast1)
      - --c-stroke-color-off: none
      - --c-stroke-color-state-off: "#97989c"
      - --c-fill-color-on: none
      - --c-fill-color-off: none
      - --c-stroke-width: 2.3
      - --c-font-color-on: var(--contrast1)
      - --c-font-color-off: none
      - --c-font-color-state-off: "#97989c"
      - --c-font-size: 14px
      - --c-unit-font-size: 10.5px
      - --c-font-weight: 700
      - --c-letter-spacing: -0.02rem
    custom_fields:
      circle:
        - width: 88%
        - margin: -3% 2% 0 0
        - justify-self: end
        - opacity: 1
  custom_fields:
    circle: >
      [[[ 
        const input = variables.circle_input;

        if (input === null) {
          return '';
        }

        let r = 22, c = r * 2 * Math.PI;
        let domain = entity.entity_id.split('.')[0],
            state = variables.state,
            unit = variables.circle_input_unit || ' ';

        const circle = (percentage, displayText, unit, isDisplay = false) => {
            let strokeColor = 'var(--c-stroke-color-on)';
            let fontColor = 'var(--c-font-color-on)';
            
            if (isDisplay && !variables.state_on) {
                strokeColor = 'var(--c-stroke-color-state-off)';
                fontColor = 'var(--c-font-color-state-off)';
            } else if (!variables.state_on && !isDisplay) {
                strokeColor = 'var(--c-stroke-color-off)';
                fontColor = 'var(--c-font-color-off)';
            }

            return `
              <svg viewBox="0 0 50 50">
                <style>
                  circle {
                    transform: rotate(-90deg);
                    transform-origin: 50% 50%;
                    stroke-dasharray: ${c};
                    stroke-dashoffset: ${c - percentage / 100 * c};
                    stroke-width: var(--c-stroke-width);
                    stroke: ${strokeColor};
                    fill: ${variables.state_on ? 'var(--c-fill-color-on)' : 'var(--c-fill-color-off)'}; 
                  }
                  text {
                    font-size: var(--c-font-size);
                    font-weight: var(--c-font-weight);
                    letter-spacing: var(--c-letter-spacing);
                    fill: ${fontColor}; 
                  }
                  tspan {
                    font-size: var(--c-unit-font-size);
                  }
                  #circle_value, tspan {
                    text-anchor: middle;
                    dominant-baseline: central;
                  }
                </style>
                <circle id="circle_stroke" cx="25" cy="25" r="${r}"/>
                <text id="circle_value" x="50%" y="52%">${displayText}${unit}</text>
              </svg>
            `;
        };

        const personIcon = (path, stateOn) => {
            let fillColor = stateOn ? 'var(--c-stroke-color-on)' : 'var(--c-stroke-color-state-off)';
            return `
              <svg viewBox="0 0 50 50">
                <style>
                  path {
                    fill: ${fillColor};
                    transform: scale(1.0);
                    transform-origin: center;
                  }
                </style>
                <path d="${path}"/>
              </svg>
            `;
        };

        if (domain === 'sensor' && entity.entity_id.includes('battery')) {
            const batteryLevel = parseFloat(input);
            return circle(batteryLevel, batteryLevel, unit, true);
        }

        if (domain === 'light' && state === 'on') {
            const brightness = entity.attributes.brightness;
            const percentage = brightness ? Math.round((brightness / 255) * 100) : 100;
            return circle(percentage, percentage, '%');
        }

        if (domain === 'fan') {
            if (state === 'on') {
                const percentage = entity.attributes.percentage;
                let displayText = '';
                if (percentage === 33) displayText = 'LOW';
                else if (percentage === 66) displayText = 'MED';
                else if (percentage === 100) displayText = 'HIGH';
                else displayText = 'ON';
                return circle(percentage, displayText, '');
            }
        }

        if (domain === 'climate') {
            let displayText = `${input}`;
            return circle(100, displayText, unit, true);
        }

        if (domain === 'binary_sensor') {
            let time = c => {
                let s = (c / 1e3),
                    m = (c / 6e4),
                    h = (c / 36e5),
                    d = (c / 864e5);
                return s < 60
                    ? parseInt(s) + 's'
                    : m < 60 ? parseInt(m) + 'm'
                    : h < 24 ? parseInt(h) + 'h'
                    : parseInt(d) + 'd';
            };
            let timeValue = states[variables.retain] === undefined || states[variables.retain].state === 'unavailable'
                    ? time(Date.now() - Date.parse(entity.last_changed))
                    : time(Date.now() - Date.parse(states[variables.retain].state));
            return circle(100, timeValue, ' ');
        }

        if (domain === 'person') {
            const paths = {
              'home': 'M22 37.75V28.75H28V37.75H35.5V25.75H40L25 12.25 10 25.75H14.5V37.75H22Z',
              'Friendship': 'M25 11.5 8.5 20.5 25 29.5 38.5 22.135V32.5H41.5V20.5M14.5 26.77V32.77L25 38.5 35.5 32.77V26.77L25 32.5 14.5 26.77Z',
              'Stantec': 'M22 10.75H28A3 3 0 0 1 31 13.75V16.75H37A3 3 0 0 1 40 19.75V36.25A3 3 0 0 1 37 39.25H13C11.335 39.25 10 37.9 10 36.25V19.75C10 18.085 11.335 16.75 13 16.75H19V13.75C19 12.085 20.335 10.75 22 10.75M28 16.75V13.75H22V16.75H28Z',
              'Kelly': 'M25 11.5 11.5 19.3V38.5H20.5L24.85 34 29.5 38.5H38.5V19.3L25 11.5M18.85 37V28L23.35 32.5 18.85 37M20.35 26.5H29.35L24.85 31 20.35 26.5M30.85 37 26.35 32.5 30.85 28V37M29.5 23.5H20.2V20.5H29.5V23.5Z',
              'Flinner': 'M25 29.5A3 3 0 0 1 22 26.5C22 24.835 23.35 23.5 25 23.5A3 3 0 0 1 28 26.5 3 3 0 0 1 25 29.5M17.5 17.5C17.5 15.835 18.835 14.5 20.5 14.5A3 3 0 0 1 23.5 17.5 3 3 0 0 1 20.5 20.5C18.835 20.5 17.5 19.15 17.5 17.5M25 10C19.645 10 14.845 12.31 11.5 16L25 40 38.5 16C35.17 12.31 30.355 10 25 10Z',
              'TPA': 'M37.855 29.29 40 27.145 37.855 25 32.5 30.355 19.645 17.5 25 12.145 22.855 10 20.71 12.145 18.565 10 15.355 13.21 13.21 11.065 11.065 13.21 13.21 15.355 10 18.565 12.145 20.71 10 22.855 12.145 25 17.5 19.645 30.355 32.5 25 37.855 27.145 40 29.29 37.855 31.435 40 34.645 36.79 36.79 38.935 38.935 36.79 36.79 34.645 40 31.435 37.855 29.29Z',
              'SVS': 'M25 11.5 8.5 20.5 25 29.5 38.5 22.135V32.5H41.5V20.5M14.5 26.77V32.77L25 38.5 35.5 32.77V26.77L25 32.5 14.5 26.77Z',
              'not_home': 'M42.25 27.25 36.25 33.25V28.75H22.75V25.75H36.25V21.25L42.25 27.25M12.25 37.75V25.75H7.75L22.75 12.25 33.25 21.7V22.75H29.935L22.75 16.285 15.25 23.035V34.75H30.25V31.75H33.25V37.75H12.25Z'
            };
            let path = paths[entity.state];
            return `
              <svg viewBox="0 0 50 50">
                ${circle(100, '', '', true)}
                ${personIcon(path, variables.state_on)}
              </svg>
            `;
        }

        if (variables.state_on) {
            return circle(100, input, unit);
        }
      ]]]

Any help would be greatly appreciated. Is what I am trying to do even achievable?