Happy note. Am heading out to Sharks v Golden Nights so I checked the “Starting Goalies” tab to see whose in net and it is working! Maybe the grabbing of the ever-changing key works?
What’s the latest for NBA? I have scores showing but no standings in Divisional, conference, overall etc…
NBA is preseason. The current setup from @kbrown01 is for regular season.
With no changes to anything, they should start showing up when the season starts.
ah yes of course it’s pre-season! duh…my bad! Got excited when I saw games coming up!
You can change the REST query string for preseason. I am not in front of my computer to know which parameter it is.
Of course you would need to reload or create a set of sensors for that.
@kbrown01 not sure what happened but only the NHL standings are not showing for me. NFL, MLB, and NBA are working fine.
EDIT: Here’s the relevant card code
decluttering_templates:
nhl_settings:
card:
type: custom:flex-table-card
title: '[[title]]'
css:
table+: 'padding: 0px;'
tbody tr td:first-child: 'width: 2%;'
tbody tr td:nth-child(2): 'width: 15%;'
tbody tr td:nth-child(n+3): 'width: 5%;'
tbody tr:hover: 'background-color: green!important; color:white!important;'
tbody tr td:nth-child(7): 'background-color: green; color: white;'
card_mod:
style:
.: |
ha-card {
overflow: auto;
}
$: |
.card-header {
padding-top: 6px!important;
padding-bottom: 4px!important;
font-size: 14px!important;
line-height: 14px!important;
font-weight: bold!important;
}
entities:
include: '[[entity]]'
exclude: '[[excluded_entities]]'
sort_by: entries-
columns:
- hidden: true
data: '[[attribute]]'
modify: '[[sort]]'
- name: >-
<div title="Clinch Indicator: * : Clinched Presidents' Trophy
(Best Regular-Season Record) x : Clinched Playoff Berth -
: In The Hunt e : Eliminated from Playoff Contention">C</div>
data: '[[attribute]]'
modify: >-
if(typeof x.stats.find(y=>y.abbreviation == 'CLINCH') !==
'undefined' ){x.stats.find(y=>y.abbreviation ==
'CLINCH').displayValue}else{'-'}
- name: Team
data: '[[attribute]]'
modify: >-
'<div><a href="' + x.team.links[0].href + '" target="_blank"><img
src="' + x.team.logos[0].href + '" style="height:
20px;vertical-align:middle;"></a> ' + x.team.displayName +
'</div>'
- name: <div title="Games Played">GP</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'GP').displayValue
- name: <div title="Wins (worth two points)">W</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'W').displayValue
- name: <div title="Losses (worth zero points)">L</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'L').displayValue
- name: <div title="OT/Shootout losses (worth one point)">OTL</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'OTL').displayValue
- name: <div title="Points">PTS</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'PTS').displayValue
- name: <div title="Regulation Wins">RW</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'RW').displayValue
- name: <div title="Regulation and Overtime Wins">ROW</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'ROW').displayValue
- name: <div title="Shootout Wins">SOW</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'SOW').displayValue
- name: <div title="Shootout Losses">SOL</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'SOL').displayValue
- name: <div title="Home Record">HOME</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'HOME').displayValue
- name: <div title="Away Record">AWAY</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'AWAY').displayValue
- name: <div title="Goals For">GF</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'GF').displayValue
- name: <div title="Goals Against">GA</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'GA').displayValue
- name: <div title="Goal Differential">DIFF</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.type == 'pointsdiff').displayValue
- name: <div title="Last 10 Games">L10</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'L10').summary
- name: <div title="Current Streak">STRK</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'STRK').displayValue
views:
- theme: Backend-selected
title: Sports
type: panel
icon: mdi:strategy
badges: []
cards:
- type: custom:mod-card
card_mod:
style:
tabbed-card $: |
mwc-tab {
background: var(--ha-card-background, var(--card-background-color, white) );
border-color: var(--ha-card-border-color, var(--divider-color, #e0e0e0) );
border-width: 2px;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
border-style: solid;
overflow: hidden;
width: 25%;
}
mwc-tab[active] {
background: #EBFFD8 !important;
}
card:
type: custom:tabbed-card
styles:
'--mdc-theme-primary': green
'--mdc-tab-text-label-color-default': silver
'--mdc-typography-button-font-size': 12px
tabs:
- attributes:
label: NHL
icon: mdi:hockey-puck
card:
type: custom:mod-card
card_mod:
style:
tabbed-card $: |
mwc-tab {
background: var(--ha-card-background, var(--card-background-color, white) );
border-color: var(--ha-card-border-color, var(--divider-color, #e0e0e0) );
border-width: 2px;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
border-style: solid;
overflow: hidden;
width: 25%;
}
mwc-tab[active] {
background: #EBFFD8 !important;
}
card:
type: custom:tabbed-card
styles:
'--mdc-theme-primary': green
'--mdc-tab-text-label-color-default': silver
'--mdc-typography-button-font-size': 12px
tabs:
- attributes:
label: Standings
icon: mdi:ballot
card:
type: custom:mod-card
card_mod:
style:
tabbed-card $: |
mwc-tab {
background: var(--ha-card-background, var(--card-background-color, white) );
border-color: var(--ha-card-border-color, var(--divider-color, #e0e0e0) );
border-width: 2px;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
border-style: solid;
overflow: hidden;
width: 25%;
}
mwc-tab[active] {
background: #EBFFD8 !important;
}
card:
type: custom:tabbed-card
styles:
'--mdc-theme-primary': green
'--mdc-tab-text-label-color-default': silver
'--mdc-typography-button-font-size': 12px
tabs:
- attributes:
label: Divisional
card:
type: custom:stack-in-card
mode: vertical
cards:
- type: markdown
content: |
<h2>Eastern Conference</h2>
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Atlantic
- entity: sensor.nhl_east_atlantic
- attribute: entries
- excluded_entities:
- sensor.nhl_starting_goalies
- sensor.nhl_wildcard
- sensor.nhl_wildcard_standings
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Metropolitan
- entity: sensor.nhl_east_metropolitan
- attribute: entries
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: markdown
content: |
<h2>Western Conference</h2>
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Central
- entity: sensor.nhl_west_central
- attribute: entries
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Pacific
- entity: sensor.nhl_west_pacific
- attribute: entries
- excluded_entities:
- sensor.nhl_starting_goalies
- sensor.nhl_wildcard
- sensor.nhl_wildcard_standings
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- attributes:
label: Conference
card:
type: custom:stack-in-card
mode: vertical
cards:
- type: markdown
content: |
<h2>Eastern Conference</h2>
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: null
- entity: sensor.nhl_east_*
- attribute: entries
- excluded_entities:
- sensor.nhl_starting_goalies
- sensor.nhl_wildcard
- sensor.nhl_wildcard_standings
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: markdown
content: |
<h2>Western Conference</h2>
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: null
- entity: sensor.nhl_west_*
- attribute: entries
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- attributes:
label: Overall
card:
type: custom:decluttering-card
template: nhl_settings
variables:
- title: null
- entity: sensor.nhl_*_*
- attribute: entries
- excluded_entities:
- sensor.nhl_starting_goalies
- sensor.nhl_wildcard
- sensor.nhl_wildcard_standings
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- attributes:
label: Playoffs
card:
type: custom:stack-in-card
mode: vertical
cards:
- type: markdown
content: |
<h2>Eastern Conference</h2>
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Atlantic
- entity: sensor.nhl_wildcard_standings
- attribute: east_atlantic_top
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Metropolitan
- entity: sensor.nhl_wildcard_standings
- attribute: east_metropolitan_top
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Wildcard
- entity: sensor.nhl_wildcard_standings
- attribute: east_wildcard
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: In The Hunt
- entity: sensor.nhl_wildcard_standings
- attribute: east_hunt
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Eliminated
- entity: sensor.nhl_wildcard_standings
- attribute: east_eliminated
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: markdown
content: |
<h2>Western Conference</h2>
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Central
- entity: sensor.nhl_wildcard_standings
- attribute: west_central_top
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Pacific
- entity: sensor.nhl_wildcard_standings
- attribute: west_pacific_top
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Wildcard
- entity: sensor.nhl_wildcard_standings
- attribute: west_wildcard
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: In The Hunt
- entity: sensor.nhl_wildcard_standings
- attribute: west_hunt
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
- type: custom:decluttering-card
template: nhl_settings
variables:
- title: Eliminated
- entity: sensor.nhl_wildcard_standings
- attribute: west_eliminated
- excluded_entities:
- sensor.nhl_starting_goalies
- sort: >-
x.stats.find(y=>y.shortDisplayName ==
'PTS').value
NHL is broken here as well
I have the same, flex-table card was updated and it breaks NHL because I have the “title” attribute. I removed the “title” attribute from all the headings and it works again.
nhl_settings:
card:
type: custom:flex-table-card
title: '[[title]]'
css:
table+: 'padding: 0px; width: 1600px;'
tbody tr td:first-child: 'width: 2%;'
tbody tr td:nth-child(2): 'width: 20%;'
tbody tr td:nth-child(n+3): 'width: 5%;'
tbody tr:hover: 'background-color: green!important; color:white!important;'
tbody tr td:nth-child(7): 'background-color: green; color: white;'
card_mod:
style:
.: |
ha-card {
overflow: auto;
}
$: |
.card-header {
padding-top: 6px!important;
padding-bottom: 4px!important;
font-size: 14px!important;
line-height: 14px!important;
font-weight: bold!important;
}
entities:
include: '[[entity]]'
exclude: '[[excluded_entities]]'
sort_by: entries-
columns:
- hidden: true
data: '[[attribute]]'
modify: '[[sort]]'
- name: <div>C</div>
data: '[[attribute]]'
modify: >-
if(typeof x.stats.find(y=>y.abbreviation == 'CLINCH') !==
'undefined' ){x.stats.find(y=>y.abbreviation ==
'CLINCH').displayValue}else{'-'}
- name: Team
data: '[[attribute]]'
modify: >-
'<div><a href="' + x.team.links[0].href + '" target="_blank"><img
src="' + x.team.logos[0].href + '" style="height:
20px;vertical-align:middle;"></a> ' + x.team.displayName +
'</div>'
- name: <div>GP</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'GP').displayValue
- name: <div>W</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'W').displayValue
- name: <div>L</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'L').displayValue
- name: <div>OTL</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'OTL').displayValue
- name: <div>PTS</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'PTS').displayValue
- name: <div>RW</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'RW').displayValue
- name: <div>ROW</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'ROW').displayValue
- name: <div>SOW</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'SOW').displayValue
- name: <div>SOL</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'SOL').displayValue
- name: <div>HOME</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'HOME').displayValue
- name: <div>AWAY</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'AWAY').displayValue
- name: <div>GF</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'GF').displayValue
- name: <div>GA</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'GA').displayValue
- name: <div>DIFF</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'DIFF').displayValue
- name: <div>L10</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'L10').summary
- name: <div>STRK</div>
data: '[[attribute]]'
modify: x.stats.find(y=>y.abbreviation == 'STRK').displayValue
It has been reported in GITHUB.
Update: Dev says it is fixed and will support the title attribute again, you can replace the JS or wait for the update.
Would like to attempt to install this but it seems a little unclear to me. Is it just a matter of downloading the code and placing everything in my config/ folder? I’ve installed all the required cards. Just not sure where to go from there.
Have you looked here? GitHub - kbrown01/SportStandingsScores: Sports Standings and Scores Sensors and Dashboard for Home Assistant
Do you have the sensors created? This is not an integration. It relies on many sensors that are REST calls to the ESPN api
Yes I created them through the teamtracker integration by adding all the teams one by one. It’s just unclear to me where to place everything else from the github page. Sorry mostly use integrations but this looks like something i’ve been looking for inside of HA and wanted to try it out
Ah, there are more than teamtracker. You need the sensors that gets the stats. Let’s start with what you are most interested in … NFL? NBA? NHL? Let me know and I will post the sensors for that sport and then you will be closer
I would want to see NHL, NFL, MLB
OK, so lets start with NFL. Now … I use includes for most all my sensors. How do you do your YAML? Are they just one large configuration.yaml or includes?
If you use includes, then in configuration.yaml you would have:
template: !include template.yaml
sensor: !include sensor.yaml
and in sensor.yaml you need:
##
## NFL Standings
##
- platform: rest
scan_interval: 36000
name: NFL Standings
unique_id: sensor.nfl_standings
resource: https://site.web.api.espn.com/apis/v2/sports/football/nfl/standings?seasontype=2&type=0&level=3
value_template: "{{ now() }}"
json_attributes:
- children
and in template.yaml you need:
sensor:
###
### NFL Divisions
###
- name: NFL AFC East
unique_id: sensor.nfl_afc_east
state: "{{ now() }}"
attributes:
entries: "{{ state_attr('sensor.nfl_standings','children')[0]['children'][0]['standings']['entries'] }}"
- name: NFL AFC North
unique_id: sensor.nfl_afc_north
state: "{{ now() }}"
attributes:
entries: "{{ state_attr('sensor.nfl_standings','children')[0]['children'][1]['standings']['entries'] }}"
- name: NFL AFC South
unique_id: sensor.nfl_afc_south
state: "{{ now() }}"
attributes:
entries: "{{ state_attr('sensor.nfl_standings','children')[0]['children'][2]['standings']['entries'] }}"
- name: NFL AFC West
unique_id: sensor.nfl_afc_west
state: "{{ now() }}"
attributes:
entries: "{{ state_attr('sensor.nfl_standings','children')[0]['children'][3]['standings']['entries'] }}"
- name: NFL NFC East
unique_id: sensor.nfl_nfc_east
state: "{{ now() }}"
attributes:
entries: "{{ state_attr('sensor.nfl_standings','children')[1]['children'][0]['standings']['entries'] }}"
- name: NFL NFC North
unique_id: sensor.nfl_nfc_north
state: "{{ now() }}"
attributes:
entries: "{{ state_attr('sensor.nfl_standings','children')[1]['children'][1]['standings']['entries'] }}"
- name: NFL NFC South
unique_id: sensor.nfl_nfc_south
state: "{{ now() }}"
attributes:
entries: "{{ state_attr('sensor.nfl_standings','children')[1]['children'][2]['standings']['entries'] }}"
- name: NFL NFC West
unique_id: sensor.nfl_nfc_west
state: "{{ now() }}"
attributes:
entries: "{{ state_attr('sensor.nfl_standings','children')[1]['children'][3]['standings']['entries'] }}"
- name: NFL Red Zone
unique_id: sensor.nfl_red_zone
state: |
{% set redzone = namespace(teams=[]) %}
{%- for team in integration_entities("teamtracker") -%}
{%- if states(team) == "IN" and state_attr(team, "league") == "NFL" and state_attr(team, "team_homeaway") == "home" and state_attr(team,'possession') is not none -%}
{% set possession = state_attr(team,'possession') %}
{% set team_id = state_attr(team,'team_id') %}
{% set opponent_id = state_attr(team,'opponent_id') %}
{% set team_abbr = state_attr(team,'team_abbr') %}
{% set opponent_abbr = state_attr(team,'opponent_abbr') %}
{% if state_attr(team,'down_distance_text') is not none %}
{% set down_distance_text = state_attr(team,'down_distance_text') %}
{% set down_distance_endzone = state_attr(team,'down_distance_text').split(' ')[4] %}
{% set down_distance_yardline = state_attr(team,'down_distance_text').split(' ')[5] %}
{% set possession_abbr = opponent_abbr if opponent_id == possession else team_abbr %}
{% set non_possession_abbr = opponent_abbr if opponent_id != possession else team_abbr %}
{%- if (possession_abbr != down_distance_endzone) and (down_distance_yardline | int) <= 20 %}
{% set redzone.teams = redzone.teams + [team] %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{{ redzone.teams | count }}
attributes:
teams: |
{% set redzone = namespace(teams=[]) %}
{%- for team in integration_entities("teamtracker") -%}
{%- if states(team) == "IN" and state_attr(team, "league") == "NFL" and state_attr(team,'possession') is not none -%}
{% set possession = state_attr(team,'possession') %}
{% set team_id = state_attr(team,'team_id') %}
{% set opponent_id = state_attr(team,'opponent_id') %}
{% set team_abbr = state_attr(team,'team_abbr') %}
{% set opponent_abbr = state_attr(team,'opponent_abbr') %}
{% if state_attr(team,'down_distance_text') is not none %}
{% set down_distance_text = state_attr(team,'down_distance_text') %}
{% set down_distance_endzone = state_attr(team,'down_distance_text').split(' ')[4] %}
{% set down_distance_yardline = state_attr(team,'down_distance_text').split(' ')[5] %}
{% set possession_abbr = opponent_abbr if opponent_id == possession else team_abbr %}
{% set non_possession_abbr = opponent_abbr if opponent_id != possession else team_abbr %}
{%- if (possession_abbr != down_distance_endzone) and (down_distance_yardline | int) <= 20 %}
{% set redzone.teams = redzone.teams + [team] %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{{ redzone.teams }}
These will create a REST sensor called sensor.nfl_standings
and the template sensors then will break that into the AFC and NFC divisions.
If you don’t have any includes yet, you create those files at the same level as the configuration.yaml (although you can place them elsewhere and organize differently). This is just my way of doing it.
The last template is a specialized one that maintains a sensor with teams in the red zone that is used to colorize the teamtracker cards.
Of course, once you have this setup you can reload the REST and then the Template Entities or just restart HA.
Once you get this, circle back and we’ll set up the flex-table cards for standings
OK. I setup the includes and just copied your template.yaml and sensor.yaml and all the standings sensors were created
Awesome. So sounds like you are semi-experienced. So you want this in it’s own dashboard? Possibly with the tabbed view like is shown or?
Ha! Yeah I sort of know what I’m doing lol. I understand how things work within HA. I just wasn’t sure how to implement everything. Yeah separate dashboard is fine with the tabbed view is great.
Well grab this (too long to post here)
You may need to do some editing at first to remove some things you do not have, the goal is to get you on NFL. You would only have NFL sensors now but that should not error the whole card (it might).
Well I used your sensor and template files so every sport was created. Place the dashboard.yaml in same place as the sensor.yaml and template.yaml location? Do I need an include for it as well?