As promised @nebulight. Yaml below
Sensors/entities used:
image.zwift_<user_name>_profile_picture
sensor.zwift_online_<uniqueID_number>
sensor.zwift_<user_name>_power_zone_name
sensor.zwift_activity_color
sensor.zwift_distance_<uniqueID_number>
sensor.zwift_altitude_<uniqueID_number>
sensor.zwift_gradient_<uniqueID_number>
sensor.zwift_power_<uniqueID_number>
sensor.zwift_cadence_<uniqueID_number>
sensor.zwift_speed_<uniqueID_number>
sensor.zwift_heart_rate_<uniqueID_number>
sensor.zwift_level_<uniqueID_number>
sensor.zwift_cycle_progress_<uniqueID_number>
sensor.zwift_run_level_<uniqueID_number>
sensor.zwift_run_progress_<uniqueID_number>
sensor.distance_earth_circumference
sensor.elevation_mountain_everest
Cards used:
custom:layout-card
custom:button-card
conditional
markdown
entities
custom:bar-card
Main Last/Live Sessions (conditional)
cards:
- type: custom:button-card
entity: image.zwift_<user_name>_profile_picture
show_entity_picture: true
show_icon: false
show_name: true
show_label: true
name: Zwift <user_name>
label: >-
[[[ return states['sensor.zwift_online_<uniqueID_number>']?.state === 'True'
? 'Live on Zwift' : 'Last Zwift session'; ]]]
entity_picture: >-
[[[ return entity?.attributes?.entity_picture ||
entity?.attributes?.url || ''; ]]]
tap_action:
action: more-info
styles:
card:
- padding: 12px
- border-radius: 8px
- display: grid
- grid-template-columns: 72px 1fr
- align-items: center
- column-gap: 14px
grid:
- grid-template-areas: "'i n' 'i l'"
- grid-template-columns: 72px 1fr
- grid-template-rows: 1fr 1fr
- align-items: center
img_cell:
- width: 72px
- height: 72px
- border-radius: 50%
- overflow: hidden
entity_picture:
- width: 72px
- height: 72px
- object-fit: cover
name:
- justify-self: start
- font-size: 18px
- font-weight: "600"
label:
- justify-self: start
- font-size: 13px
- opacity: "0.75"
- card:
content: >-
{% set last_act =
state_attr("sensor.zwift_online_<uniqueID_number>",
'latest_activity') %}
{% set t = last_act['movingTimeInMs'] %}
{% set d = last_act['distanceInMeters'] %}
{% set pace_in_seconds = 0 %}
{% if d and t %}
{% set pace_in_seconds = t / d %}
{% endif %}
Last session: {{
states.sensor.zwift_online_<uniqueID_number>.attributes.latest_activity.name
}}

The {% if last_act['sport'] == "CYCLING" -%}
ride
{%- else -%}
run
{%- endif %} started on {{
as_timestamp(
states.sensor.zwift_online_<uniqueID_number>.attributes.latest_activity.startDate
) | timestamp_custom(' %d %B %Y at %H:%M ') | replace(" 0", "")
}}
The {% if last_act['sport'] == "CYCLING" -%}
ride
{%- else -%}
run
{%- endif %} took {{
states.sensor.zwift_online_<uniqueID_number>.attributes.latest_activity.duration
}} minutes
The {% if last_act['sport'] == "CYCLING" -%}
ride
{%- else -%}
run
{%- endif %} ended on {{
as_timestamp(
states.sensor.zwift_online_<uniqueID_number>.attributes.latest_activity.endDate
) | timestamp_custom(' %d %B %Y at %H:%M ') | replace(" 0", "")
}}
{% if last_act['sport'] == "CYCLING" -%}
Ride
{%- else -%}
Run
{%- endif %} stats:
Distance: {{
states.sensor.zwift_online_<uniqueID_number>.attributes.latest_activity.distanceInMeters
| round / 1000
}} km
{% if last_act['sport'] == "CYCLING" -%}
Climbing: {{
states.sensor.zwift_online_<uniqueID_number>.attributes.latest_activity.totalElevation
}} m
{%- else -%}
Pace: {{ pace_in_seconds | round // 60 }}:{{
'%02d' | format(pace_in_seconds | round % 60)
}} /km
{%- endif %}
Power: {{
states.sensor.zwift_online_<uniqueID_number>.attributes.latest_activity.avgWatts
}} W
Calories: {{
states.sensor.zwift_online_<uniqueID_number>.attributes.latest_activity.calories
}} kcal
title: Last Zwift Session
type: markdown
conditions:
- entity: sensor.zwift_online_<uniqueID_number>
state: "False"
type: conditional
- type: conditional
conditions:
- entity: sensor.zwift_online_<uniqueID_number>
state: "True"
card:
type: custom:button-card
entity: sensor.zwift_<user_name>_power_zone_name
show_icon: false
show_state: false
show_label: false
name: >-
[[[ const zone = entity?.state || 'unknown';
return zone.replace(/_/g, ' ').replace(/\b\w/g,
c => c.toUpperCase()); ]]]
tap_action:
action: more-info
styles:
card:
- height: 54px
- border-radius: 8px
- padding: 8px 14px
- background: >-
[[[ const raw =
states['sensor.zwift_activity_color']?.state ||
'[128, 128, 128]';
const nums = (raw.match(/\d+/g) ||
['128', '128', '128']).slice(0, 3).map(Number);
return `rgb(${nums[0]}, ${nums[1]}, ${nums[2]})`; ]]]
- color: >-
[[[ const raw =
states['sensor.zwift_activity_color']?.state ||
'[128, 128, 128]';
const nums = (raw.match(/\d+/g) ||
['128', '128', '128']).slice(0, 3).map(Number);
const lum =
0.299 * nums[0] + 0.587 * nums[1] + 0.114 * nums[2];
return lum > 150 ? '#111111' : '#ffffff'; ]]]
- display: flex
- align-items: center
- justify-content: center
grid:
- grid-template-areas: "'n'"
- grid-template-rows: 1fr
- align-items: center
- justify-items: center
name:
- justify-self: center
- align-self: center
- text-align: center
- font-size: 22px
- font-weight: "700"
- line-height: "1"
- card:
entities:
- entity: sensor.zwift_<user_name>_power_zone_name
name: Power Zone
- entity: sensor.zwift_activity_color
name: Zone RGB
- entity: sensor.zwift_distance_<uniqueID_number>
name: Distance
- entity: sensor.zwift_altitude_<uniqueID_number>
name: Altitude
- entity: sensor.zwift_gradient_<uniqueID_number>
name: Gradient
- entity: sensor.zwift_power_<uniqueID_number>
name: Power
- entity: sensor.zwift_cadence_<uniqueID_number>
name: Cadence
- entity: sensor.zwift_speed_<uniqueID_number>
name: Speed
- entity: sensor.zwift_heart_rate_<uniqueID_number>
name: Heart Rate
show_header_toggle: false
title: Zwift Live Session
type: entities
conditions:
- entity: sensor.zwift_online_<uniqueID_number>
state: "True"
type: conditional
layout: vertical
type: custom:layout-card
Bard cards (warning:possible not available on HACS anymore?)
cards:
- entities:
- entity: sensor.zwift_level_<uniqueID_number>
name: Cycling Level
- color: darkorange
entity: sensor.zwift_cycle_progress_<uniqueID_number>
entity_row: true
height: 40px
name: Cycle Progress
positions:
indicator: "off"
name: inside
value: "off"
style: |-
bar-card-currentbar,
bar-card-contentbar,
bar-card-backgroundbar {
margin-top: 10px;
}
type: custom:bar-card
- entity: sensor.zwift_run_level_<uniqueID_number>
name: Run Level
- color: deeppink
entity: sensor.zwift_run_progress_<uniqueID_number>
entity_row: true
height: 40px
name: Run Progress
positions:
indicator: "off"
name: inside
value: "off"
style: |-
bar-card-currentbar,
bar-card-contentbar,
bar-card-backgroundbar {
margin-top: 10px;
}
type: custom:bar-card
show_header_toggle: false
title: <user_name>'s Zwift Profile
type: entities
layout: vertical
type: custom:layout-card
and finally the global stats cards
type: custom:layout-card
layout_type: vertical
cards:
- type: markdown
title: Statistics (all-time)
content: >-
FTP: {{
states.sensor.zwift_online_<uniqueID_number>.attributes.ftp
}} Watts
Max Streak: {{
states.sensor.zwift_online_<uniqueID_number>.attributes.streaksMaxLength
}} weeks
Total Hours: {{
(
states.sensor.zwift_online_<uniqueID_number>.attributes.totalTimeInMinutes
/ 60
) | round(2)
}} hours
Power Generated: {{
(
states.sensor.zwift_online_<uniqueID_number>.attributes.totalWattHours
/ 1000
) | round(2)
}} kWh
Total Distance: {{
(
states.sensor.zwift_online_<uniqueID_number>.attributes.totalDistance
/ 1000
) | round(2)
}} km
Distance Perspective: {{
states.sensor.distance_earth_circumference.state
}} times around the Earth
Total Elevation: {{
(
states.sensor.zwift_online_<uniqueID_number>.attributes.totalDistanceClimbed
/ 1000
) | round(2)
}} km
Elevation Perspective: {{
states.sensor.elevation_mountain_everest.state
}} times up Everest
associated template sensors (two of) for above:
- sensor:
# Various sensors for the Zwift dashboard
- name: "Distance Earth Circumference"
default_entity_id: sensor.distance_earth_circumference
unit_of_measurement: "times around Earth"
state: >-
{% set total_distance_travelled_km =
states.sensor.zwift_online_<uniqueID_number>.attributes.totalDistance
| float(0) / 1000 %}
{% set earth_circumference_km = 40075 %}
{{
(total_distance_travelled_km / earth_circumference_km)
| round(2)
}}
- name: "Elevation Mount Everest"
default_entity_id: sensor.elevation_mountain_everest
unit_of_measurement: "times up Everest"
state: >-
{% set total_distance_climbed_meters =
states.sensor.zwift_online_<uniqueID_number>.attributes.totalDistanceClimbed
| float(0) %}
{% set everest_height_meters = 8849 %}
{{
(total_distance_climbed_meters / everest_height_meters)
| round(2)
}}