Person Cards - Show Off Yours

Here is the template to add to the top of your dashboard yaml when using the raw configuration editor. Make sure you have installed the custom-button-card from HACS first. Other integrations are proximity, companion app (for phone sensors) a configured home zone, and spotify. Most information is optional. Add the variable but leave it blank - it should still render without throwing an error.

Template code for dashboard
button_card_templates:
  person_card:
    variables:
      var_person_entity: Person Entity
      var_device_tracker: Location tracking device
      var_phone_device_type: Enter iphone or android
      var_iphone_wifi_ssid: (iPhone only) phone SSID sensor
      var_phone_battery_state: Phone battery state sensor
      var_phone_battery_level: Phone battery level sensor
      var_phone_ringer_mode: (Android Only) Phone ringer mode sensor
      var_phone_ringer_volume: (Android Only) Phone ringer volume level
      var_geocoded_location: Entity to provide exact addresses
      var_spotify: spotify entity. Leave blank if not used
      var_wifi_connection: (Android Only) Phone wifi connection name sensor
      var_step_counter: Step counter entity
      var_home_proximity: Proximity to home distance sensor
      var_activity: Activity detection sensor
      var_travel_direction: Proximity to home travel direction sensor
      var_map_api_key: geoapify.com map api key
    name: null
    show_name: false
    show_state: false
    show_icon: false
    show_entity_picture: false
    entity: '[[[ return variables.var_person_entity ]]]'
    aspect_ratio: 1/1
    triggers_update:
      - '[[[ return variables.var_activity ]]]'
      - '[[[ return variables.var_spotify ]]]'
    tap_action:
      action: none
    styles:
      card:
        - padding: 0% 0% 0% 0%
      custom_fields:
        photo:
          - position: absolute
          - top: 8%
          - left: 4%
          - width: 38%
          - height: 38%
          - border: 7px solid var(--primary-color);
          - border-radius: 50%
        map_area:
          - position: absolute
          - background-color: var(--primary-color)
          - background: |
              [[[
                
                if ( states[variables.var_device_tracker]) {
                  return `url('https://maps.geoapify.com/v1/staticmap?style=osm-bright&width=600&height=230&center=lonlat:${states[variables.var_device_tracker].attributes.longitude},${states[variables.var_device_tracker].attributes.latitude}&zoom=14.0&marker=lonlat:${states[variables.var_device_tracker].attributes.longitude},${states[variables.var_device_tracker].attributes.latitude};type:material;color:%23ff3421;icontype:awesome&scaleFactor=2&apiKey=${variables.var_map_api_key}')`;
                }
              ]]]
          - background-repeat: no-repeat
          - background-size: cover;
          - width: 100%
          - height: 35%
          - top: 0%
          - left: 0%
          - font-size: 8px
        zone:
          - position: absolute
          - justify-self: start
          - font-weight: bold
          - font-size: 100%
          - top: 27%
          - left: 53%
          - width: 60%
          - height: 100%
          - text-align: left
          - color: black
          - font-size: 100%
        geolocation:
          - position: absolute
          - justify-self: start
          - font-size: 80%
          - top: 36%
          - left: 53%
          - width: 50%
          - text-align: left
          - color: var(--primary-text-color)
        travel:
          - position: absolute
          - justify-self: start
          - font-size: 80%
          - top: 58%
          - left: 60%
          - width: 50%
          - text-align: left
          - color: var(--accent-color)
        proximity:
          - position: absolute
          - justify-self: start
          - font-size: 90%
          - top: 53%
          - left: 53%
          - width: 50%
          - text-align: left
          - color: var(--primary-text-color)
        battery:
          - position: absolute
          - justify-self: start
          - font-size: 90%
          - top: 53%
          - left: 10%
          - width: 50%
          - text-align: left
          - color: |
              [[[                 
                if ( states[variables.var_phone_battery_level] )  {
                  if (states[variables.var_phone_battery_level].state <= 15 )
                    return `red`;
                  if (states[variables.var_phone_battery_level].state >= 16 && states[variables.var_phone_battery_level].state < 45)
                    return `orange`;
                  else
                    return `green`;
                }
              ]]]
        ringer:
          - position: absolute
          - justify-self: start
          - font-size: 90%
          - top: 80%
          - left: 10%
          - width: 50%
          - text-align: left
          - color: |
              [[[
                  if ( states[variables.var_phone_ringer_mode] ) {
                    if (states[variables.var_phone_ringer_mode].state == "silent")
                      return `red`;
                    else  
                      return `var(--primary-text-color)`;
                  }
              ]]]
        steps:
          - position: absolute
          - justify-self: start
          - font-size: 90%
          - top: 66%
          - left: 53%
          - width: 50%
          - text-align: left
          - color: var(--primary-text-color)
        wifi:
          - position: absolute
          - justify-self: start
          - font-size: 90%
          - top: 66%
          - left: 10%
          - width: 50%
          - text-align: left
          - color: |
              [[[
                if (variables.var_phone_device_type == 'iphone') {
                  if (states[variables.var_iphone_wifi_ssid]) {
                    if (states[variables.var_iphone_wifi_ssid].state == 'Not Connected')  
                      return `lightgrey`;
                    else 
                      return `var(--primary-text-color)`;
                  }
                } else {
                  if (states[variables.var_wifi_connection]) {
                    if (states[variables.var_wifi_connection].state == '<not connected>')  
                      return `lightgrey`;
                    else 
                      return `var(--primary-text-color)`;
                  }
                }
              ]]]
        media_playing:
          - position: absolute
          - display: flex
          - align-items: center
          - font-size: 80%
          - top: 89%
          - left: 0%
          - width: 95%
          - height: 8%
          - color: |
              [[[ 
                return `var(--primary-text-color)`;
              ]]]
          - visibility: |
              [[[
                if ( (states[variables.var_spotify] )   && ( states[variables.var_spotify].state == "playing" ) )
                  return `visible`;
                else
                  return  `hidden`;
              ]]]
          - background-color: |
              [[[
                  return `var(--primary-color)`;
              ]]]
        media_image:
          - position: absolute
          - justify-self: start
          - top: 82%
          - left: 80%
          - width: 17%
          - text-align: left
          - color: |
              [[[
                return `var(--primary-text-color)`;
              ]]]
          - visibility: |
              [[[
                if ( ( states[variables.var_spotify] )   && (states[variables.var_spotify].state == "playing" ) )
                  return `visible`;
                else
                  return  `hidden`;
              ]]]
        activity:
          - position: absolute
          - font-size: 80%
          - background: white
          - display: flex
          - align-items: center
          - top: 38%
          - left: 34%
          - width: 10%
          - height: 10%
          - text-align: center
          - color: black
          - visibility: |
              [[[
                if ( states[variables.var_activity] ) {
                  let var_allowed = 'in_vehicle automotive on_bicycle cycling running still stationary walking on_foot';
                  let var_activity_state = states[variables.var_activity].state;
                  
                  if ( var_allowed.toLowerCase().includes(var_activity_state.toLowerCase()) )
                    return `visible`;
                  else
                    return `hidden`;
                }
              ]]]
          - border: 4px solid var(--accent-color);
          - border-radius: 50%
    custom_fields:
      map_area: ''
      photo: |
        [[[
          return `<img style="width: 100%;height: 100%; object-fit: contain;" src='${entity.attributes.entity_picture}' >`
        ]]]  
      zone: |
        [[[
          if (`${entity.state}` == 'not_home') { 
          return `<ha-icon icon="mdi:home-export-outline"
            style="width: 10%; height: 10%";>
            </ha-icon><span> Away</span>`;
          } 
          if (`${entity.state}` =='home') { 
          return `<ha-icon 
            icon="mdi:home"
            style="width: 10%; height: 10%;">
            </ha-icon><span> ${entity.state}</span>`;
          } else {
          return `<ha-icon 
            icon="mdi:map-marker-radius"
            style="width: 10%; height: 10%;">
            </ha-icon><span> ${entity.state}</span>`;
          }
        ]]]
      geolocation: |
        [[[
          
          if ( states[variables.var_geocoded_location] ) {
            if ( variables.var_phone_device_type == 'iphone')
              return `
                <span> 
                  ${states[variables.var_geocoded_location].attributes['Sub Thoroughfare']}
                  ${states[variables.var_geocoded_location].attributes.Thoroughfare} <br />
                  ${states[variables.var_geocoded_location].attributes.Locality}, 
                  ${states[variables.var_geocoded_location].attributes['Administrative Area']}
                  ${states[variables.var_geocoded_location].attributes['Postal Code']}
                </span>`;
            else
              return `
                <span> 
                  ${states[variables.var_geocoded_location].attributes.sub_thoroughfare}
                  ${states[variables.var_geocoded_location].attributes.thoroughfare} <br />
                  ${states[variables.var_geocoded_location].attributes.locality}, 
                  ${states[variables.var_geocoded_location].attributes.administrative_area}
                  ${states[variables.var_geocoded_location].attributes.postal_code}
                </span>`;
          }
        ]]]
      travel: |
        [[[
          if ( states[variables.var_travel_direction] && states[variables.var_travel_direction].state == 'towards' ) {
           return `
              <span> 
                Heading Home
              </span>`;
          } else {
            return ` `
          }
           
        ]]]
      battery: |
        [[[
          if (states[variables.var_phone_battery_state]) {
            if (states[variables.var_phone_battery_state].state.toLowerCase() =='charging') { 
              return `<ha-icon icon="mdi:battery-charging" style="width: 12%; height: 12%; ">
              </ha-icon> 
              <span style="color: var(--text-color-sensor);">
               ${states[variables.var_phone_battery_level].state}%
                <span style="font-size: 80%;" >
                  phone charging
                </span>
              </span>`;
            } else {
              if (states[variables.var_phone_battery_level].state < 11) {
                return `<ha-icon icon="mdi:battery-10" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;}
              if (states[variables.var_phone_battery_level].state >=10 && states[variables.var_phone_battery_level].state < 20)
                return `<ha-icon icon="mdi:battery-20" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
              if (states[variables.var_phone_battery_level].state >= 20 && states[variables.var_phone_battery_level].state < 30)
                return `<ha-icon icon="mdi:battery-30" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
              if (states[variables.var_phone_battery_level].state >= 30 && states[variables.var_phone_battery_level].state < 40)
                return `<ha-icon icon="mdi:battery-40" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                    ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
              if (states[variables.var_phone_battery_level].state >= 40 && states[variables.var_phone_battery_level].state < 50)
                return `<ha-icon icon="mdi:battery-50" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
              if (states[variables.var_phone_battery_level].state >= 50 && states[variables.var_phone_battery_level].state < 60)
                return `<ha-icon icon="mdi:battery-60" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
              if (states[variables.var_phone_battery_level].state >= 60 && states[variables.var_phone_battery_level].state < 70)
                return `<ha-icon icon="mdi:battery-70" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
                if (states[variables.var_phone_battery_level].state >= 70 && states[variables.var_phone_battery_level].state < 80)
                return `<ha-icon icon="mdi:battery-80" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
                if (states[variables.var_phone_battery_level].state >= 80 && states[variables.var_phone_battery_level].state < 90)
                return `<ha-icon icon="mdi:battery-90" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
              if (states[variables.var_phone_battery_level].state >= 90 && states[variables.var_phone_battery_level].state <= 100)
                return `<ha-icon icon="mdi:battery" style="width: 12%; height: 12%;"> </ha-icon>
                  <span style="color: var(--text-color-sensor);">
                  ${states[variables.var_phone_battery_level].state}%
                    <span style="font-size: 80%;" >
                      phone battery
                    </span>
                  </span>`;
            }
          }
        ]]]
      ringer: |
        [[[

          if (variables.var_phone_device_type == 'iphone') {
            return ` `
          } else { 
            if ( states[variables.var_phone_ringer_mode] ) {
              if (states[variables.var_phone_ringer_mode].state == "silent")
                return `<ha-icon
                  icon="mdi:volume-off"
                  style="width: 12%; height: 12%;">
                  </ha-icon>                
                  <span style="font-size: 80%;" >
                    ringer silent
                  </span>`
              if (states[variables.var_phone_ringer_mode].state == "vibrate") 
                return `<ha-icon icon="mdi:volume-vibrate"
                  style="width: 12%; height: 12%;">
                  </ha-icon>
                  <span style="font-size: 80%;" >
                    ringer vibrate
                  </span>`
              else 
                return `<ha-icon
                  icon="mdi:volume-high"
                  style="width: 12%; height: 12%;">
                  </ha-icon>
                  <span> ${states[variables.var_phone_ringer_volume].state}
                    <span style="font-size: 80%;" >
                      ringer level
                    </span>
                  </span>`
            }
          }
            
        ]]]
      steps: |
        [[[
          if ( states[variables.var_step_counter] ) {
            return `<ha-icon
              icon="mdi:walk"
              style="width: 12%; height: 12%;">
              </ha-icon> 
              <span>${Math.round(states[variables.var_step_counter].state).toFixed(0)} <span style="font-size: 80%;" >steps today</span></span>`
          }
        ]]]
      wifi: |
        [[[

          if (variables.var_phone_device_type == 'iphone') {
            if ( states[variables.var_iphone_wifi_ssid] ) {
              if ( states[variables.var_iphone_wifi_ssid].state == 'Not Connected') 
                 return `<ha-icon
                  icon="mdi:wifi-off"
                  style="width: 12%; height: 12%;">
                  </ha-icon>
                    <span style="font-size: 80%;" >Disconnected</span>`;
              else
                 return `<ha-icon
                  icon="mdi:wifi"
                  style="width: 12%; height: 12%;">
                  </ha-icon>
                    <span style="font-size: 80%;" > ${states[variables.var_iphone_wifi_ssid].state} </span>`;
          }
          } else {
            if ( states[variables.var_wifi_connection] ) {
              if ( states[variables.var_wifi_connection].state == '<not connected>' ) 
                return `<ha-icon
                icon="mdi:wifi-off"
                style="width: 12%; height: 12%;">
                </ha-icon>
                <span style="font-size: 80%;" >Disconnected</span>`;
              else 
                return `<ha-icon
                icon="mdi:wifi"
                style="width: 12%; height: 12%;">
                </ha-icon>
                <span style="font-size: 80%;" >${states[variables.var_wifi_connection].state}</span>`;
            }
          }
        ]]]
      media_playing: |
        [[[
            if ( (states[variables.var_spotify])  && (states[variables.var_spotify].state == "playing" )) {
              return `<marquee> <ha-icon
              icon="mdi:music"
              style="width: 20px; height: 20px;"></ha-icon
              <span> ${states[variables.var_spotify].attributes.media_title}
              - ${states[variables.var_spotify].attributes.media_artist} </marquee>`;
            } else {
              return ` `;
            }
        ]]]
      media_image: |
        [[[
            if ((states[variables.var_spotify])   && (states[variables.var_spotify].state == "playing" )) {
              return `<img style="width: 100%;height: 100%; object-fit: contain;" src='${states[variables.var_spotify].attributes.entity_picture}' >`;
            } else {
              return ` `;
            }
        ]]]
      proximity: |
        [[[
          if ( states[variables.var_home_proximity] ) {
          return `<ha-icon
            icon="mdi:map-marker-distance"
            style="width: 12%; height: 12%;">
            </ha-icon> 
            <span> 
               ${Math.round(states[variables.var_home_proximity].state).toFixed(0)}
            <span style="font-size: 80%;" >miles from home</span></span>`
          }
        ]]]
      activity: |
        [[[
          if ( states[variables.var_activity] ) {
            let var_activity_state = states[variables.var_activity].state.toLowerCase();

            if ( var_activity_state == 'in_vehicle' || var_activity_state == 'automotive')
              return `<ha-icon icon="mdi:car" style="width: 70%; height: 70%;"></ha-icon>`;
            else if ( var_activity_state == 'on_bicycle' || var_activity_state == 'cycling')
                return `<ha-icon icon="mdi:bicycle" style="width: 70%; height: 70%;"></ha-icon>`;
            else if ( var_activity_state.toLowerCase() == 'running')
                return `<ha-icon icon="mdi:run-fast" style="width: 70%; height: 70%;"></ha-icon>`;
            else if ( var_activity_state == 'still' || var_activity_state == 'stationary')
                return `<ha-icon icon="mdi:sofa" style="width: 70%; height: 70%;"></ha-icon>`;
            else if ( var_activity_state.toLowerCase() == 'walking' || var_activity_state == 'on_foot') {
                return `<ha-icon icon="mdi:shoe-print" style="width: 70%; height: 70%;"></ha-icon>`;
            } else {      
                return `<ha-icon icon="mdi:map-marker-question-outline" style="width: 70%; height: 70%;"></ha-icon>`;
            }
          }
        ]]]

Here is the yaml to create the card. Replace the variables with your entities that match the required settings. If using an iPhone, make sure to set variable var_device_type: to iphone…

Card Yaml
type: custom:button-card
template: person_card
   var_person_entity: Person Entity
   var_device_tracker: Location tracking device
   var_phone_device_type: Enter iphone or android
   var_iphone_wifi_ssid: (iPhone only) phone SSID sensor
   var_phone_battery_state: Phone battery state sensor
   var_phone_battery_level: Phone battery level sensor
   var_phone_ringer_mode: (Android Only) Phone ringer mode sensor
   var_phone_ringer_volume: (Android Only) Phone ringer volume level
   var_geocoded_location: Entity to provide exact addresses
   var_spotify: spotify entity. Leave blank if not used
   var_wifi_connection: (Android Only) Phone wifi connection name sensor
   var_step_counter: Step counter entity
   var_home_proximity: Proximity to home distance sensor
   var_activity: Activity detection sensor
   var_travel_direction: Proximity to home travel direction sensor
   var_map_api_key: api key from geoapify.Register to first, its free 

Sorry took a while to answer. I was working on one function to show if the person is headed towards home.

Also here is an example card configuration that give my current card

Arcade Bliss Card Config Example
type: custom:button-card
template: person_card
variables:
  var_person_entity: person.arcadebliss
  var_device_tracker: device_tracker.arcadeblisss_talkie_talkie
  var_phone_battery_state: sensor.pixel_8_pro_battery_state
  var_phone_battery_level: sensor.pixel_8_pro_battery_level
  var_phone_ringer_mode: sensor.pixel_8_pro_ringer_mode
  var_phone_ringer_volume: sensor.pixel_8_pro_volume_level_ringer
  var_geocoded_location: sensor.pixel_8_pro_geocoded_location
  var_spotify: media_player.spotify_arcadebliss
  var_wifi_state: binary_sensor.pixel_8_pro_wifi_state
  var_wifi_connection: sensor.pixel_8_pro_wifi_connection
  var_step_counter: sensor.pixel_8_pro_daily_steps
  var_home_proximity: sensor.proximity_home_arcadebliss_distance
  var_activity: sensor.pixel_8_pro_detected_activity
  var_travel_direction: sensor.proximity_home_arcadebliss_direction_of_travel
  var_map_api_key: 1a6a9cbce0fa3fc8ab8537971284caea


4 Likes