Person Cards - Show Off Yours

Most cards in this thread look like they’re straight from the Metaverse! Yours looks like high production value.

Well done sir and thanks for sharing your code!

1. Dynamic Border Color Based on Status

The border around the person’s picture changes color depending on their location:

  • Green: The person is at home.
  • Red: The person is away.
  • Orange: The person has another status (e.g., traveling).

This makes it easy to visually identify someone’s location by looking at the color of the border.

2. Charging Status with Dynamic Lightning Icon

When the phone is charging, a lightning icon is displayed instead of the battery percentage. This lightning icon blinks to indicate that the device is currently charging, providing a clear visual indication.

3. Dynamic Battery Colors

The battery status is dynamically displayed with different colors depending on the battery level:

  • Green: Battery level above 50%.
  • Yellow: Battery level between 30% and 50%.
  • Orange: Battery level between 10% and 30%.
  • Red: Battery level below 10%.

This color-coded display makes it easy to quickly see the battery status.

4. Dynamic Travel Time Color Indicator

For travel time (from a Waze sensor or similar source), the travel time is dynamically color-coded:

  • Green: Short travel time (under 20 minutes).
  • Yellow: Moderate travel time (between 20 and 40 minutes).
  • Red: Long travel time (over 40 minutes).

This provides a quick visual indication of how long the travel time is.

5. Dynamic Travel Time Display

The travel time is displayed in minutes, along with a circular chart that dynamically adjusts in size according to the travel time, relative to a maximum time (e.g., 60 minutes). This makes it easier to see how close the travel time is to the maximum.

12 Likes

can you share the code?

Here you go:

type: custom:button-card
styles:
  card:
    - padding: 8px
    - border-radius: 20px
  grid:
    - display: grid
    - grid-template-columns: auto 1fr auto
    - grid-template-rows: auto auto auto
    - grid-template-areas: |
        "picture entity battery"
        "picture state travel"
  custom_fields:
    picture:
      - grid-area: picture
      - justify-self: start
      - align-self: center
    entity:
      - grid-area: entity
      - justify-self: start
      - align-self: end
      - text-align: left
      - margin-left: 15px
    state:
      - grid-area: state
      - justify-self: start
      - align-self: start
      - text-align: left
      - margin-left: 15px
    battery:
      - grid-area: battery
      - justify-self: center
      - align-self: center
      - text-align: left
      - margin-left: 15px
    travel:
      - grid-area: travel
      - justify-self: center
      - align-self: center
      - text-align: left
      - margin-left: 15px
custom_fields:
  picture: |
    [[[
      let state = states['person.your_person_entity'].state;
      let borderColor = "gray";
      if (state === "home") {
        borderColor = "green";
      } else if (state === "not_home") {
        borderColor = "red";
      } else {
        borderColor = "orange";
      }

      return `<div style="position: relative; margin: 0; padding: 0; border: 3px solid ${borderColor}; border-radius: 50%; width: 40px; height: 40px; display: flex; justify-content: center; align-items: center;">
                <img src="${states['person.your_person_entity'].attributes.entity_picture}" style="width: 40px; height: 40px; border-radius: 50%; object-fit: cover; margin: 0; padding: 0;">
              </div>`;
    ]]]
  entity: |
    [[[
      return `<div style="font-size: 12px; font-weight: bold; margin: 0; padding: 0; text-transform: uppercase;">
                ${states['person.your_person_entity'].attributes.friendly_name}
              </div>`;
    ]]]
  state: |
    [[[
      let state = states['person.your_person_entity'].state;
      if (state === "home") {
        state = "Home";
      } else if (state === "not_home") {
        state = "Away";
      }
      return `<div style="font-size: 10px; color: var(--secondary-text-color); margin: 0; padding: 0;">
                ${state}
              </div>`;
    ]]]
  battery: |
    [[[
      let battery = states['sensor.your_battery_level_sensor'].state;
      let charging = states['sensor.your_battery_state_sensor'].state;
      let color = "green";
      if (battery < 10) {
        color = "red";
      } else if (battery < 30) {
        color = "orange";
      } else if (battery < 50) {
        color = "yellow";
      } else {
        color = "green";
      }

      return `<div style="position: relative; font-size: 14px; color: var(--secondary-text-color); margin: 0; padding: 0;">
                <svg width="30" height="30" viewBox="0 0 36 36" class="circular-chart">
                  <path class="circle-bg"
                    stroke="#eee"
                    stroke-width="2"
                    fill="none"
                    d="M18 2.0845
                      a 15.9155 15.9155 0 0 1 0 31.831
                      a 15.9155 15.9155 0 0 1 0 -31.831" />
                  <path class="circle"
                    stroke="${color}"
                    stroke-width="2"
                    stroke-dasharray="${battery}, 100"
                    fill="none"
                    d="M18 2.0845
                      a 15.9155 15.9155 0 0 1 0 31.831
                      a 15.9155 15.9155 0 0 1 0 -31.831" />
                  ${
                    charging === "Charging"
                      ? `<text x="18" y="24" class="percentage" style="fill:yellow; font-size: 16px; text-anchor: middle; animation: blink 2s infinite;">
                          ⚡
                        </text>`
                      : `<text x="18" y="17" class="percentage" style="fill:#fff; font-size: 10px; text-anchor: middle;">${battery}</text>
                         <text x="18" y="26" class="percentage" style="fill:#fff; font-size: 8px; text-anchor: middle;">%</text>`
                  }
                </svg>
                <style>
                  @keyframes blink {
                    0% { opacity: 1; }
                    50% { opacity: 0; }
                    100% { opacity: 1; }
                  }
                </style>
              </div>`;
    ]]]
  travel: |
    [[[
      let travel_time = states['sensor.your_travel_time_sensor'].state;
      let color = "green";
      let max_time = 60;
      let percentage = Math.min((travel_time / max_time) * 100, 100);
      if (travel_time >= 40) {
        color = "red";
      } else if (travel_time >= 20) {
        color = "yellow";
      } else {
        color = "green";
      }

      return `<div style="font-size: 14px; color: var(--secondary-text-color); margin: 0; padding: 0;">
                <svg width="30" height="30" viewBox="0 0 36 36" class="circular-chart">
                  <path class="circle-bg"
                    stroke="#eee"
                    stroke-width="2"
                    fill="none"
                    d="M18 2.0845
                      a 15.9155 15.9155 0 0 1 0 31.831
                      a 15.9155 15.9155 0 0 1 0 -31.831" />
                  <path class="circle"
                    stroke="${color}"
                    stroke-width="2"
                    stroke-dasharray="${percentage}, 100"
                    fill="none"
                    d="M18 2.0845
                      a 15.9155 15.9155 0 0 1 0 31.831
                      a 15.9155 15.9155 0 0 1 0 -31.831" />
                  <text x="18" y="17" class="percentage" style="fill:#fff; font-size: 10px; text-anchor: middle;">${travel_time}</text>
                  <text x="18" y="25" class="percentage" style="fill:#fff; font-size: 6px; text-anchor: middle;">min</text>
                </svg>
              </div>`;
    ]]]

6 Likes

Very impressive. :+1:

1 Like

apologies if I’m bringing this post back from the dead, but would you have the code for your peope cards? :smiley:

I’m particularly interested in being able to change the ringer mode remotely, as my wife is notorious for misplacing her phone with it in silent mode

I’ve had the same person card for the last few years. It works but I like to freshen up a bit. In button card, is it possible to have things like battery/distance clickable? I can with my current one, so clock distance and tells you the address a person is at

Cheers

Old but not dead yet. :smiley:

And yes, you use case was my use case aswell.

I don’t know if there’s an easier way but mine works as intended:

Here’s the code for the button:

type: custom:mushroom-template-card
primary: Ringer Mode
secondary: "{{ states('sensor.YOURMOBILE_ringer_mode') }}"
icon: "{{ state_attr('sensor.YOURMOBILE_ringer_mode', 'icon') }}"
entity: sensor.dennis_pixel_ringer_mode
icon_color: |-
  {% if is_state('sensor.YOURMOBILE_ringer_mode', 'normal') %}
    green
  {% elif is_state('sensor.YOURMOBILE_ringer_mode', 'vibrate') %}
    blue
  {% elif is_state('sensor.YOURMOBILE_ringer_mode', 'silent') %} 
    yellow 
  {% endif %}
hold_action:
  action: call-service
  service: script.toggle_ringer_mode
  target: {}
tap_action:
  action: none
double_tap_action:
  action: none
layout: horizontal
fill_container: false
multiline_secondary: false

And here’s code for the script I toggle with button:

alias: "[YOU] Toggle Ringer Mode "
sequence:
  - data:
      message: command_ringer_mode
      data:
        command: >-
          {% if is_state('sensor.YOURMOBILE_ringer_mode', 'normal')  %}
          vibrate  {% elif is_state('sensor.YOURMOBILE_ringer_mode',
          'vibrate') %} silent  {% elif
          is_state('sensor.YOURMOBILE_ringer_mode', 'silent') %} normal  {%
          endif %}
    action: notify.mobile_app_YOURMOBILE
mode: single
icon: mdi:phone-ring

And here you find the full documentation:

1 Like

Thanks mate - would you be able to share your YAML for one of the entire “people” cards as well?

Haven’t updated in a while but it’s still working.There you go:

type: horizontal-stack
cards:
  - type: custom:stack-in-card
    cards:
      - type: custom:mushroom-person-card
        entity: person.ME
        use_entity_picture: true
        layout: vertical
        hold_action:
          action: navigate
          navigation_path: ME
        hide_name: true
        hide_state: true
        fill_container: true
        card_mod:
          style: |
            ha-card {
              --ha-card-border-width: 0;
            }
      - type: custom:mushroom-chips-card
        chips:
          - type: template
            content: |-
              {% if is_state('person.ME', 'home') %}
                zuhause
              {% elif is_state('person.ME', 'not_home') %}
                unterwegs
              {% else %}
                {{ states('person.ME') }}
              {% endif %}
            card_mod:
              style: |
                ha-card {
                  --chip-box-shadow: 0px 0px;
                  --text-color: #a6a6a6;
                  --chip-font-size: 12px;
                  --chip-border-width: 0
                }
        alignment: center
  - type: custom:stack-in-card
    cards:
      - type: custom:mushroom-person-card
        entity: person.HER
        use_entity_picture: true
        layout: vertical
        hold_action:
          action: navigate
          navigation_path: HER
        hide_name: true
        hide_state: true
        fill_container: true
        card_mod:
          style: |
            ha-card {
              --ha-card-border-width: 0;
            }
      - type: custom:mushroom-chips-card
        chips:
          - type: template
            content: |-
              {% if is_state('person.HER', 'home') %}
                zuhause
              {% elif is_state('person.HER', 'not_home') %}
                unterwegs
              {% else %}
                {{ states('person.HER') }}
              {% endif %}
            card_mod:
              style: |
                ha-card {
                  --chip-box-shadow: 0px 0px;
                  --text-color: #a6a6a6;
                  --chip-font-size: 12px;
                  --chip-border-width: 0
                }
        alignment: center

And this is the target for my personal card:

  - theme: Google Theme
    title: ME
    path: ME
    icon: ''
    badges: []
    cards:
      - type: custom:mushroom-chips-card
        chips:
          - type: back
      - type: custom:mushroom-template-card
        secondary: >-
          {{ state_attr('sensor.ME_geocoded_location', 'thoroughfare')
          }}  {{ state_attr('sensor.ME_geocoded_location',
          'sub_thoroughfare') }}, {{
          state_attr('sensor.ME_geocoded_location', 'locality')
          }}     
        entity: sensor.ME_geocoded_location
        primary: Wo bin ich?
        icon: mdi:map-marker
        layout: horizontal
        multiline_secondary: false
        tap_action:
          action: more-info
        hold_action:
          action: navigate
          navigation_path: /map
      - type: custom:mushroom-title-card
        subtitle: Bedienung
      - type: gauge
        entity: sensor.ME_battery_level
        name: Akku
        theme: Google Theme
        severity:
          green: 75
          yellow: 25
          red: 0
        needle: true
      - type: horizontal-stack
        cards:
          - type: custom:mushroom-entity-card
            entity: binary_sensor.ME_is_charging
            name: Ladestatus
            icon_color: amber
      - type: custom:mushroom-template-card
        primary: Ringer Mode
        secondary: '{{ states(''sensor.ME_ringer_mode'') }}'
        icon: '{{ state_attr(''sensor.ME_ringer_mode'', ''icon'') }}'
        entity: sensor.ME_ringer_mode
        icon_color: |-
          {% if is_state('sensor.ME_ringer_mode', 'normal') %}
            green
          {% elif is_state('sensor.ME_ringer_mode', 'vibrate') %}
            blue
          {% elif is_state('sensor.ME_ringer_mode', 'silent') %} 
            yellow 
          {% endif %}
        hold_action:
          action: call-service
          service: script.toggle_ringer_mode
          target: {}
        tap_action:
          action: none
        double_tap_action:
          action: none
        layout: horizontal
        fill_container: false
        multiline_secondary: false
      - type: custom:mushroom-entity-card
        tap_action:
          action: call-service
          service: script.find_my_phone
          service_data: {}
          target: {}
        icon: mdi:cellphone-arrow-down-variant
        hide_icon: false
        entity: script.find_my_phone
        name: Wo ist mein Handy?
        icon_color: green
        layout: horizontal
        secondary_info: none
1 Like

many thanks - gives me something to work towards :smiley:

1 Like

I’ve been working on mine, with modifying some that I found here and there.

Most of it works great, but when I start spotify, I have to do a page refresh before it will start the marquee and change the picture to the headphones.

HA-Personcard-normal
HA-Personcard-headphones

type: custom:button-card
entity: person.<USERNAME>
device: <DEVICENAME>
variables:
  phone_battery_level_sensor: "[[[ return states[\"sensor.<DEVICENAME>_battery_level\"].state]]]"
  phone_battery_state_sensor: "[[[ return states[\"sensor.<DEVICENAME>_battery_state\"].state]]]"
  phone_wifi_sensor: "[[[ return states[\"sensor.<DEVICENAME>_wifi_connection\"].state]]]"
  proximity_sensor: "[[[ return states[\"sensor.<DEVICENAME>_geocoded_location\"].state]]]"
  avatar_normal: /local/<USER>-Smile.png
  avatar_headphones: /local/<USER>-Smile+headphones.png
  media_player_spotify: media_player.spotify_<SPOTIFYACCOUNT>
aspect_ratio: 2/1
name: Person
show_entity_picture: false
show_name: false
hold_action:
  action: none
state:
  - value: home
    styles:
      card:
        - background-color: "#202124"
      custom_fields:
        icon:
          - border-color: "#77c66e"
  - value: not_home
    styles:
      card:
        - background-color: "#202124"
      custom_fields:
        icon:
          - border-color: deepskyblue
  - value: Pizzeria
    styles:
      card:
        - background-color: "#202124"
      custom_fields:
        icon:
          - border-color: "#B83838"
styles:
  card:
    - background-color: "#202124"
    - border-radius: 5%
    - padding: 2% 5% 5% 5%
    - color: gray
    - font-size: 12px
    - text-shadow: 0px 0px 0px black
    - text-transform: capitalize
    - justify-self: center
    - align-content: center
  grid:
    - grid-template-areas: "\"icon status\" \"icon battery\" \"icon wifi\" \"icon proximity\""
    - grid-template-columns: 1.5fr 1fr
    - grid-template-rows: 1fr 1fr 1fr 1fr
    - align-content: center
  name:
    - font-size: 15px
    - align-self: middle
    - justify-self: start
  custom_fields:
    icon:
      - clip-path: circle()
      - width: 80%
      - pointer-events: none
      - display: grid
      - border: 5px solid
      - border-color: "#808080"
      - border-radius: 500px
      - margin: 0 0 0 0
      - opacity: 1
      - align-self: center
      - justify-self: center
    status:
      - justify-self: start
      - color: gray
    proximity:
      - align-self: middle
      - justify-self: start
      - color: gray
    wifi:
      - justify-self: start
      - color: gray
      - "--text-wifi-color-sensor": >-
          [[[ if (variables.phone_wifi_sensor == '<not connected>') return
          "#aaaaaa"; ]]]
    battery:
      - align-self: middle
      - justify-self: start
      - color: gray
      - "--text-color-sensor": >-
          [[[ if (variables.phone_battery_level_sensor < 35) return "#EF4F1A";
          ]]]
    media:
      - align-self: right
      - position: absolute
      - bottom: 4%
      - right: 4%
      - left: 4%
      - color: >-
          [[[if (states[variables.media_player_spotify].state == "playing")
          return "#000000"; else return "#ffffff00"; ]]]          
custom_fields:
  icon: >
    [[[ 
     if (states[variables.media_player_spotify].state =='playing') 
     {
    return entity === undefined ? null : `<img
    src=${variables.avatar_headphones} width="100%">`; } else { return entity
    === undefined ? null : `<img
    src="${states[entity.entity_id].attributes.entity_picture}" width="100%">`;
    } ]]]
  status: |
    [[[
      if (entity.state =='not_home') { 
      return `<ha-icon icon="mdi:home-export-outline"
        style="width: 20px; height: 20px; color: '#888888';">
        </ha-icon><span> On the way</span>`;
      } 
      if (entity.state =='home') { 
      return `<ha-icon 
        icon="mdi:home"
        style="width: 20px; height: 20px; color: 888888;">
        </ha-icon><span> ${entity.state}</span>`;
      } else { 
        if (entity.state =='[YOUR OTHER LOCATION]') { 
        return `<ha-icon icon="mdi:pizza"
          style="width: 20px; height: 20px; color: '#888888';">
          </ha-icon><span> ${entity.state}</span>`;
        }
        else{
        return `<ha-icon 
          icon="mdi:map-marker-radius"
          style="width: 20px; height: 20px; color: 888888;">
          </ha-icon><span> ${entity.state}</span>`;
        }
      }
    ]]]
  battery: |
    [[[
      if (variables.phone_battery_state_sensor =='charging') { 
        return `<ha-icon
        icon="mdi:battery-charging-medium"
        style="width: 20px; height: 20px; color: #888888;">
        </ha-icon> <span><span style="color: var(--text-color-sensor);">${variables.phone_battery_level_sensor}% Battery</span></span>`;
      } else {
        return `<ha-icon
        icon="mdi:battery-medium"
        style="width: 20px; height: 20px; color: #888888;">
        </ha-icon> <span><span style="color: var(--text-color-sensor);">${variables.phone_battery_level_sensor}% Battery</span></span>`;
      }
    ]]]
  wifi: |
    [[[
      if (variables.phone_wifi_sensor.state =='<not connected>') { 
        return `<ha-icon
        icon="mdi:wifi"
        style="width: 20px; height: 20px; color: var(--text-wifi-color-sensor);">
        </ha-icon> <span><span style="color: var(--text-wifi-color-sensor);">Disconnected</span></span>`; 
      } else {
        return `<ha-icon
        icon="mdi:wifi"
        style="width: 20px; height: 20px; color: #888888;">
        </ha-icon> <span><span style="color: var(--text-color-sensor);">${variables.phone_wifi_sensor}</span></span>`;
      }
    ]]]
  media: |
    [[[ 
     if (states[variables.media_player_spotify].state =='playing') { 
      return `<marquee> <ha-icon
        icon="mdi:music"
        style="width: 20px; height: 20px;"></ha-icon
        <span> ${states[variables.media_player_spotify].attributes.media_title}
        - ${states[variables.media_player_spotify].attributes.media_artist} </marquee>`
    } else {
      
    }
    ]]]

For some reason, when it changes the picture to the headphones one, it is a different size. Any suggestions?

probably a resolution thing, did you check the 2 pictures to be of exact same proportions?

as for the marquee

did you check the empty ‘else’ here? thats never a good thing.

you could try and set the triggers_update entity list to include that player entity explicitly

btw, marquee tag is long deprecated and support may vary among browser. advice is to move to Javascript translateX() see translateX() - CSS: Cascading Style Sheets | MDN

1 Like

@Mariusthvdb thanks for the info, I’ll take a look into that.

Hi Ben, I really love your version of the person card. Unfortunately, my HA skills are not that deep yet and I tried it now for a week, but don’t get any further.
Maybe I can ask for your assistance here and a short explanation on how this is working and where I have to put the codes.

For now, I added the “Template” you shared into the ui_lovelace.yaml on the top as 2nd template. But when I try to add the card code, it either gives me a code error or it ends up as a blank box on the dashboard.
I have installed the decluttering card (I cannot choose or see this on the add card menu though. The installation is there if I check in Studio Code Server.
I tried to use manual card, no go.
I tried to use custom button card, no go.

Thanks a lot in advance!!

Here is where i put the template in the ui_lovelace.yaml:
(short example)


button_card_templates: !include_dir_merge_named “…/…/custom_components/ui_lovelace_minimalist/ui_minimalist/ulm_templates/”
decluttering_templates:
person_card_new:
default:
- size: 60%
- color: auto
- background_color: var(–primary-background-color)
- variable: spin
- spin: false
- show_name: false
- show_state: true
- show_label: false
- show_icon: true
- show_last_changed: false
- show_entity_picture: false
- tap_action:
action: more-info
haptic: light
- aspect_ratio: 2/1
- margin-right: 20px
- label: " "
- off_icon_color: gray
- off_name_color: gray
- off_state_color: gray
card:
type: custom:button-card
battery: “[[battery]]”
home_sensor: “[[home_sensor]]”
work_sensor: “[[work_sensor]]”
work_icon: “[[work_icon]]”
name: “[[name]]”

I don’t actually use this card anymore but send me a private message with your code and I’ll see if I can recreate mine again :slight_smile:

Thank you very much, I think i cannot yet start a PM conversation. I just created a account yesterday. Would you mind sending me a PM so i can reply with my code?

Many thanks in advance :slight_smile:

A link to the code of my below person card (which shows the status of a lot of other things.

My Air-Conditioning is controlled with a flow in NodeRED, and performs the following functions.

  • Turns OFF Air-Conditioning when no one is home
  • Turns OFF Air-Conditioning when a door or window is open for 2 minutes
  • NodeRED flow adjusts the mode of the air conditioner pending multiple variables.
  • NodeRED flow makes voice announcements i.e “A door or window has been open for 2 minutes, the air-conditioner has been turned off”.

HOME MODE - AC ON

  • Someone in the household is home

AWAY MODE - AC OFF

  • Everyone is away from the household

DOOR OR WINDOW OPEN MORE THAN 2 MINS - AC OFF

  • Door and Window Status only shows on the home screen if someone is home and the air-conditioning is OFF.
  • Display reverts to HOME MODE when doors and windows are closed.

WHOLE DASHBOARD

  • Setup on a 15.6 Uperfect Vertical 1080p Monitor in the lounge room.
  • Connected to a Raspbery Pi400 running in kiosk mode.
  • Text large enough to see from the couch.

Finally I made my own good Person Card with all the samples and ideas from here, some extra work and some HACS integrations:

You need following integrations:

  • Places
  • Bubble Card
  • Waze

From Places you get the city_name, travel distance, and updated time
from waze you get the travel time, bubble card for the tap_action popUp (if needed)

This is my Card:
image

Showing different images on different places, you can define as many places as you want, every place is a HomeAssistent Zone, so I’ve added Garden as well

Showing a warning if not_home or at work and location data is outdated
image

If not home & not at work it shows the city name:
image

Extras:

  • Iphone sleep mode will show sleeping image if home or not_home
  • iphone activity will show small emojy for walking, car, cycling
  • Distance to home is shown if not home
  • Time to hme is shown if not home
  • Shows time since when the person is on this place helpful when not_home, so below city name always shown since when

Link:

  • Tap-Action opens a Bubble Card PopUp with more information like map, phone status etc.

Any ideas what could be added?

The code is:
Just replace the sensor names in Variables area, and the picture path

# Waze integration needed for Travel-Time
# Places integration needed for City names, Distance, etc.
# Bubble Card integration for Popus needed or exchange navigation path to entity page
# Save images to /local/images/persons/PERSONNAME/LocationName.png
# Exchange name Martin to your name 

type: custom:button-card
aspect_ratio: 7/5  # Dynamic aspect ratio of 7:5 for responsive scaling on different devices
variables:
  person: sensor.martin  # The main entity representing the person - from places
  name: martin  # Name of the person, used dynamically in navigation or display
  focus: binary_sensor.iphone_von_martin_focus  # Binary sensor for the device focus status
  activity: sensor.iphone_von_martin_activity  # Sensor showing the person's activity (e.g., walking, driving)
  battery_level: sensor.iphone_von_martin_battery_level  # Sensor showing the battery level of the person's device
  travel_time: sensor.fahrzeit_nach_hause_martin  # Sensor showing travel time to the home location - from waze
entity: person.martin  # Main Home Assistant entity for the person
tap_action:
  action: navigate  # Tap action triggers navigation to a specific dashboard
  navigation_path: "[[[ return \"/dashboard-templates/popups#\"+variables.name]]]"  # Dynamic navigation path using the person's name
show_entity_picture: true 
entity_picture: /local/images/persons/martin/home.png  # Default picture for the person
show_name: true 
show_last_changed: false  
triggers_update: all  # Updates the card whenever any of the referenced entities change
state:
  # When the person is at "home"
  - value: home
    name: 🏡 Zu Hause
      # Show sleep image when iPhone in SleepMode
    entity_picture: |
      [[[
            if (states[variables.focus].state == 'on')
            return `/local/images/persons/martin/sleep.png`
            else return `/local/images/persons/martin/home.png`

          ]]]
    styles:
      entity_picture:
        - margin-top: 12px
        - width: 60px
        - position: relative
      name:
        - color: "#7DDA9F"
  # When the person is at "work"
  - value: Arbeit
    name: 🏭 Arbeit
    entity_picture: /local/images/persons/martin/work.png
    styles:
      name:
        - color: "#7DDA9F"
      entity_picture:
        - width: 60px
        - position: relative
        - margin-top: 20px
  # When the person is at "Garden"
  - value: Garten
    name: 👨🏻‍🌾 Garten
    entity_picture: /local/images/persons/martin/garten.png
    styles:
      name:
        - color: "#7DDA9F"
      entity_picture:
        - width: 60px
        - position: relative
        - margin-top: 20px
  # When the person is "not_home" show City name
  - value: not_home
    name: |
      [[[
      return `🗺️ ${states[variables.person].attributes.city}`;
      ]]]

   # Show sleep image when iPhone in SleepMode
    entity_picture: |
      [[[
            if (states[variables.focus].state == 'on')
            return `/local/images/persons/martin/sleep.png`
            else return `/local/images/persons/martin/wave.png`

          ]]]
    styles:
      name:
        - color: "#93ADCB"
      entity_picture:
        - margin-top: 12px
        - opacity: 0.3
        - width: 60px
        - position: relative
   
  # When the person is "unknown"
  - value: unknown
    name: |
      [[[
        return `Unbekannt`                                                   
      ]]]
    operator: default
    entity_picture: /local/images/persons/martin/wave.png
    styles:
      entity_picture:
        - margin-top: 12px
        - opacity: 0.3
        - width: 60px
        - position: relative
      name:
        - color: gray
custom_fields:
  # Show last updated time from main Places entity show last updated if not home
  # Show last changed if state is home
  last_info: |
    [[[
      if (states[variables.person].state === 'not_home') {
        // If not_home show `last_updated` of main places entity 
        const lastUpdated = new Date(states[variables.person].attributes.last_updated);
        const now = new Date();
        const diffMs = now - lastUpdated; // Unterschied in Millisekunden
        const diffMinutes = Math.floor(diffMs / (1000 * 60)); // Minuten
        const diffHours = Math.floor(diffMinutes / 60); // Stunden
        if (diffMinutes < 60) {
          return `Vor ${diffMinutes} Minuten aktualisiert`;
        } else {
          return `Vor ${diffHours} ${diffHours === 1 ? 'Stunde' : 'Stunden'} aktualisiert`;
        }
      } else {
        // If home show `last_changed` of person entity
        const lastChanged = new Date(states[variables.person].last_changed);
        const now = new Date();
        const diffMs = now - lastChanged; // Unterschied in Millisekunden
        const diffMinutes = Math.floor(diffMs / (1000 * 60)); // Minuten
        const diffHours = Math.floor(diffMinutes / 60); // Stunden
        if (diffMinutes < 60) {
          return `Seit ${diffMinutes} Minuten`;
        } else {
          return `Seit ${diffHours} ${diffHours === 1 ? 'Stunde' : 'Stunden'}`;
        }
      }
    ]]]
  #Show warning if last update is 10 minutes ago
  warning: |
    [[[
      const lastUpdated = new Date(states[variables.person].attributes.last_updated);
      const now = new Date();
      const diffMs = now - lastUpdated;
      const diffMinutes = Math.floor(diffMs / (1000 * 60));

      // Zeige eine Warnung, wenn die letzte Aktualisierung mehr als 10 Minuten her ist
      if (diffMinutes >= 10) {
        return `⚠️ Zuletzt aktualisiert vor mehr als 10 Minuten`;
      } else {
        return '';
      }
    ]]]
  #Show small Icon based on iPhone activity
  activity: |
    [[[
      if (states[variables.activity].state == 'Walking')
      return `🚶🏻‍♂️`
      if (states[variables.activity].state == 'Running')
      return `🏃🏻‍♂️`
      if (states[variables.activity].state == 'Automotive')
      return `🚙`
      if (states[variables.activity].state == 'Cycling')
      return `🚴🏻‍♂️`                                                  
      else return ""
    ]]]
  #Show battery icon and charging stat
  battery: |
    [[[
      var i = states[variables.battery_level].attributes.icon;
      var b = states[variables.battery_level].state;
      return `📱${b}%<span style='vertical-align: 1px'></span><ha-icon icon='${i}' style='width: 8px; vertical-align:2px'></ha-icon>`
    ]]]
  #Show traveltime to home
  traveltime: |
    [[[
      if (states[variables.person].attributes.distance_from_home_km <= 0.2)
      return ""
      else return `<ha-icon
          icon="mdi:map-marker-distance"
          style="width: 16px; height: 10px; vertical-align: 2px; padding-right: 2px">
          </ha-icon><span>${
        states[variables.travel_time].state} min</span>

      `
    ]]]
  #Show distance to home
  distance: |
    [[[
      if (states[variables.person].attributes.distance_from_home_km <= 0.2)
      return ""
      else return `<ha-icon
          icon="mdi:map"
          style="width: 16px; height: 10px; vertical-align: 2px; padding-right: 1px">
          </ha-icon><span>${
        states[variables.person].attributes.distance_from_home_km} km</span>
      `
    ]]]
  ###############Styles area##############

styles:
  grid:
    - grid-template-areas: |
        "i i"
        "n n"
        "last_info last_info"
        "warning warning"
        "activity distance"
        "battery traveltime"
    - grid-template-columns: 1fr 1fr
    - grid-template-rows: auto auto auto auto auto auto
  custom_fields:
    last_info:
      - justify-self: center
      - font-size: 8px
      - font-weight: normal
      - color: white
      - text-align: center
    warning:
      - justify-self: center
      - font-size: 8px
      - font-weight: bold
      - color: "#FF0000"
      - text-align: center
    sleep:
      - position: absolute
    activity:
      - height: 30%
      - position: absolute
      - top: 50px
      - right: 4px
      - font-size: 18pt
    distance:
      - position: absolute
      - right: 4px
      - top: 20px
      - font-size: 8px
    traveltime:
      - position: absolute
      - right: 2px
      - top: 36px
      - font-size: 8px
    battery:
      - position: absolute
      - right: 10px
      - top: 4px
      - font-size: 8px
      - color: >-
          [[[ if (states[variables.battery_level].state < 30) return "#e45649";
          if (states[variables.battery_level].state < 50) return "#ffa229"; if
          (states[variables.battery_level].state < 101) return "#50A14F"; else
          return "#ffc640"]]]


1 Like