Sports Standings and Scores

Thank you so much :blush:

A nother question. Do you make the something with playoff seeds?
Like you write in this post:

Not yet. I was working on extracting full NHL hockey fantasy stats for leagues. Or possibly like you wanted … a player tracker that just gives me the stats for players.

I am reviewing Yahoo Sports as their APIs are well documented. I might b able to just get the PLayoff seeding from there (or a scrape). Probably a few weeks before I can get to those tasks.

I should note that the ESPN data does have SEED in it, but it is not correct. It is merely done by conference and does not account for NHL-style (top 3 teams in each Division and then the two best of the remaining in the Conference).

UPDATE: Just you caused me to look, I found out the method to get wildcard team data. So now it is a matter of creating a wildcard sensor and a few other things. I will try to do this later today. This is the link that would deliver just wildcard teams (top two are the current ones, the remainder are currently out of contention). This would need to be combined with top three in each division to give the current picture for Wildcard.

https://site.web.api.espn.com/apis/v2/sports/hockey/nhl/standings?region=us&lang=en&contentorigin=espn&type=3&level=2&sort=playoffseed%3Aasc%2Cpoints%3Adesc%2Cgamesplayed%3Aasc%2Crotwins%3Adesc&seasontype=2

that sounds really good.

I would like to take this opportunity to thank you for your great work here.

Thank you!

I have successfully created a sensor that yields all the Wildcard information for the NHL. I am going to put the sensors in here. I am going to be out on a trip and not sure I will have time to work on this for a week. But you could learn by the examples of how the whole solution works and attempt to plug it in. I just may not be responsive to questions for a bit.

IMPORTANT NOTE: I chose to call it nhl_wildcard so if you use this, you will need to eliminate that entity in the same way as nhl_starting_goalies was done. Otherwise, you can select a different name.

as in:

      entities:
        include: '[[entity]]'
        exclude: 
            - sensor.nhl_starting_goalies
            - sensor.nhl_wildcard
            - sensor.nhl_wildcard_standings

When I get back after a few weeks, I am going to standardize this better and use variables to pass in both the includes and excludes.

The sensor that gets data from the ESPN web API is as follows. It is a REST sensor that is just like the other REST sensors except it grabs two attributes. One contains the top three teams in each Division, the other has all the remainder teams in Wildcard order.

##
## NHL Wildcard
##
- platform: rest
  scan_interval: 36000
  name: NHL Wildcard
  unique_id: sensor.nhl_wildcard
  resource: https://site.web.api.espn.com/apis/v2/sports/hockey/nhl/standings?region=us&lang=en&contentorigin=espn&type=3&level=2&sort=playoffseed%3Aasc%2Cpoints%3Adesc%2Cgamesplayed%3Aasc%2Crotwins%3Adesc&seasontype=2
  value_template: "{{ now() }}"
  json_attributes:
      - children
      - overall

Now the second sensor is a template sensor that beaks these out into 6 attributes. Top three for each division plus the wildcard for each conference.

###
### NHL Wildcard
###
  - name: NHL Wildcard Standings
    unique_id: sensor.nhl_wildcard_standings
    state: "{{ now() }}"
    attributes:
      east_atlantic_top: "{{ state_attr('sensor.nhl_wildcard','overall')[0]['children'][0]['standings']['entries'] }}"
      east_metropolitan_top: "{{ state_attr('sensor.nhl_wildcard','overall')[0]['children'][1]['standings']['entries'] }}"
      east_wildcard: "{{ state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'] }}"
      west_central_top: "{{ state_attr('sensor.nhl_wildcard','overall')[1]['children'][0]['standings']['entries'] }}"
      west_pacific_top: "{{ state_attr('sensor.nhl_wildcard','overall')[1]['children'][1]['standings']['entries'] }}"
      west_wildcard: "{{ state_attr('sensor.nhl_wildcard','children')[1]['standings']['entries'] }}"

This second sensor can be used not to create flex tables in the exact same manner as the others as the fields inside are the same. The only changes would be getting the right attribute for the right table but the names are obvious.

The second sensor has all the information to be able to draw this as done in ESPN web site:

That looks good. I’m still curious about the code for the output

if s.o is interested in German Bundesliga Standing here my config:

config configuration.yaml

  - platform: rest
    scan_interval: 36000
    name: Bundesliga Standing
    unique_id: sensor.bundesliga_standing
    resource: https://site.web.api.espn.com/apis/v2/sports/soccer/ger.1/standings
    value_template: "{{ now() }}"
    json_attributes:
        - children
template:
  - sensor:
      - name: soccer_german_bundesliga
        state: 'standing'
        attributes:
          standings: >-
            {{ state_attr('sensor.bundesliga_standing','children')[0].standings.entries }}   

card config:

custom:flex-table-card needed!

type: custom:flex-table-card
title: Standings
css:
  table+: 'padding: 0px; width: 100%;'
card_mod:
  style: |
	ha-card {
		overflow: auto;
	}
	$: |
		.card-header {
		  padding: 12px 0px 8px 4px!important;
		  font-size: 16px!important;
		  line-height: 18px!important;
		  font-weight: bold!important;
		}
sort_by: entries-
entities:
  include: sensor.soccer_german_bundesliga
columns:
  - name: P.
	data: standings
	modify: |
	  x.stats[10].displayValue
  - name: Team
	data: standings
	modify: "'<div><img src=\"' + x.team.logos[0].href + '\" style=\"height: 20px;vertical-align:middle;\">&nbsp;' + x.team.displayName + '</div>'"
  - name: G
	data: standings
	modify: |
	  x.stats[0].displayValue
  - name: DIFF
	data: standings
	modify: |
	  x.stats[8].displayValue   
  - name: Pts.
	data: standings
	modify: |
	  x.stats[2].displayValue 

Result:

1 Like

I completed a overhaul of the solution and will post tonight an update to GITHUB. This includes handling NHL Wildcard:

I would note that ESPN also slightly modified the data. They now have two fields – “Average Point Differential” and “Goal Differential” that use the same field name. The GUI shows the average point differential as the goal differential is easy since it is “GF - GA”

UPDATE

Changed files are posted to GITHUB. This adds the Wildcard sensors for NHL and also starting goalies. However, as stated in previous thread the starting goalies will need to change as the data source URL now changes daily.

NOTE: There are changes also in the decluttering template for NHL to pass in a list of ignored entities because of naming conventions. You should replace all the entire NHL tab or examine how variables are passed to ensure a functional panel.

By the way, the website changed the link and it broke starting goalies. I may need to think through how best to get the data such that it does not break. You can change the sensor for the new link for now:

- platform: rest
  name: nhl_starting_goalies
  scan_interval: 3600
  resource: https://www.dailyfaceoff.com/_next/data/hIErXNiLxFhcIi3lzX7SH/starting-goalies.json
  value_template: "{{ value_json.pageProps.date }}"
  json_attributes_path: "$.pageProps"
  json_attributes:
    - data

It appears as if this now changes daily. I will need to examine alternate methods to do this or understand how that key can be found.

Is it possible to clean up the Wildcard tab and sensor even more by separating or removing the teams that have already been eliminated (denoted by an “e” in the Clinch column)? That way only the teams actually in the wildcard hunt are shown. But then take that a step further and show the current wildcard spot holders and another section labeled “In The Hunt” which shows all the remaining teams not yet eliminated? Not sure if it’s possible with how the data comes from ESPN, or if it’s just display and styling adjustments.

Currently if you look at the sensor, I organized the data as:

  1. Top three in each division
  2. Rest in order for each conference

I was trying to get the 1,2,3 numbering scheme of each one, but time was limited and I wanted to get this out to folks.

I think that it is totally possible to eliminate the “e” 's and changing the labels is all in the code for the dashboard YAML. Let me see what I can do on that one.

How good are you at modifying things? Comfortable with or ?

In looking at the sensor as it is, one would need to bust the Wildcard into 3 groups.

  1. Top 2 = Current Wildcard
  2. Next ones without an “e” = In the Hunt
  3. “e” 's = Golfing

Splitting 1 to 2+3 is easy, the attribute URL would be:

{{ state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'][:2] }}

and

{{ state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'][3:] }}

Which is give me the top 2 (by adding [:2] at the end. And 3 to end (by adding [3:])
I will need to think through the “e’s” now

In other words, this (without getting rid of e’s) gives you a start:

###
### NHL Wildcard
###
  - name: NHL Wildcard Standings
    unique_id: sensor.nhl_wildcard_standings
    state: "{{ now() }}"
    attributes:
      east_atlantic_top: "{{ state_attr('sensor.nhl_wildcard','overall')[0]['children'][0]['standings']['entries'] }}"
      east_metropolitan_top: "{{ state_attr('sensor.nhl_wildcard','overall')[0]['children'][1]['standings']['entries'] }}"
      east_wildcard: "{{ state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'][:2] }}"
      east_hunt: "{{ state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'][3:] }}"
      west_central_top: "{{ state_attr('sensor.nhl_wildcard','overall')[1]['children'][0]['standings']['entries'] }}"
      west_pacific_top: "{{ state_attr('sensor.nhl_wildcard','overall')[1]['children'][1]['standings']['entries'] }}"
      west_wildcard: "{{ state_attr('sensor.nhl_wildcard','children')[1]['standings']['entries'][:2] }}"
      west_hunt: "{{ state_attr('sensor.nhl_wildcard','children')[1]['standings']['entries'][3:] }}"

Of course, if you want “In the Hunt” you would need to add those to the dashboard.yaml. I will look in a bit and see if I can separate that into two – hunt and eliminated.

So I now have this:

I’ll play around with this a bit. I’m fairly comfortable modifying things or running with an idea, but it’s the getting started that can be tough for me. Will play around with this some tonight.

I’m glad you got it done so quickly. I have now adopted everything and the TAB wildcard also works in the adopted dashboard.

But I have distributed everything in individual views. But with wildcard something doesn’t work for me and it looks like this:

This is my code:

type: custom:stack-in-card
mode: vertical
cards:
  - type: custom:decluttering-card
    template: nhl_settings
    variables:
      - title: Eastern 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: Eastern 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: Eastern 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: Western 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: Western 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: Western Wildcard
      - entity: sensor.nhl_wildcard_standings
      - attribute: west_wildcard
      - excluded_entities:
          - sensor.nhl_starting_goalies
      - sort: x.stats.find(y=>y.shortDisplayName == 'PTS').value

What make i wrong?

There are two sensors. A REST sensor.nhl_wiidcard and a template sensor.nhl_wildcard_standings.

Make sure you grabbed the code for both. I will guess you missed the REST one a which gets the raw data and then the template one has no data because it’s data comes from the REST one.

Check on developer tab and make sure both sensors exist and have data/attributes

This should be [2:] otherwise the first team in the hunt (ex. Calgary in the west) gets removed.

I have this working:

For removing eliminated teams, can we look at the clinch column value and if there’s an “e” for a value, don’t display that row of data? Or look at the value and if it does contain an “e” place those in a separate section at the bottom under “Out Of The Playoffs” or just “Eliminated”

The other thing I was thinking of which might help the other sports too is have a secondary sort. For NHL points are first, but if two teams are tied in points, tiebreaker goes to the team with fewer games played (higher points percentage), then the most Regulation wins (RW column), then the most Regulation and Overtime wins excluding shootouts (ROW column). EDIT: This I think is actually handled by the ESPN data, so disregard this request.

I’d love to be able to click on a column header and sort by that, but I think that’s beyond YAML capability and more HTML or CSS.

Great pickup, thanks.

On sorting … you can do that by sorting in flex-table using multiple columns. i.e:

sort_by: [battery+, name-]

would pass in a list (guessing here, untested and not complete):

- sort: [x.stats.find(y=>y.shortDisplayName == 'PTS').value, x.stats.find(y=>y.abbreviation == 'RW').displayValue]

I would note that the data is already sorted that way from ESPN … however … you may want to implemented multi-sort in other ones like “Overall”.

I am looking today at how to do the “e” .

UPDATE:

I am close to being able to split the hunt into two, those eliminated and those “still in the hunt”. I will add attributes for these as hunt and eliminated. You can then chose if you want just the hunt or whether you wish a full view including the eliminated.

It is like this and based on the fact that clincher only exists in the “e” teams. It does not in the “hunt” teams.

{% set eteams = namespace(eteam=[]) %}
{% for team in state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'][2:] %}
  {% for stat in team['stats'] %}
    {% if stat.name == 'clincher' %}
        {% set eteams.eteam = eteams.eteam + [team] %}
    {% endif %}
  {% endfor %}
{% endfor %}
{{ eteams.eteam }}

UPDATE 2

This is nearly done. I will implement the changes in the full dashboard but those experienced with the implementation could use this to get all the categories – top 3, two current wildcards, those still in the hunt, and those eliminated.

###
### NHL Wildcard
###
  - name: NHL Wildcard Standings
    unique_id: sensor.nhl_wildcard_standings
    state: "{{ now() }}"
    attributes:
      east_atlantic_top: "{{ state_attr('sensor.nhl_wildcard','overall')[0]['children'][0]['standings']['entries'] }}"
      east_metropolitan_top: "{{ state_attr('sensor.nhl_wildcard','overall')[0]['children'][1]['standings']['entries'] }}"
      east_wildcard: "{{ state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'][:2] }}"
      east_hunt: >
          {% set hteams = namespace(hteam=[]) %}
          {% for team in state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'][2:] %}
            {% for stat in team['stats'] | selectattr('name','eq','clincher') %}
            {% else %}
                  {% set hteams.hteam = hteams.hteam + [team] %}
            {% endfor %}
          {% endfor %}
          {{ hteams.hteam }}
      east_eliminated: >
        {% set eteams = namespace(eteam=[]) %}
        {% for team in state_attr('sensor.nhl_wildcard','children')[0]['standings']['entries'][2:] %}
          {% for stat in team['stats'] %}
            {% if stat.name == 'clincher' %}
                {% set eteams.eteam = eteams.eteam + [team] %}
            {% endif %}
          {% endfor %}
        {% endfor %}
        {{ eteams.eteam }}
      west_central_top: "{{ state_attr('sensor.nhl_wildcard','overall')[1]['children'][0]['standings']['entries'] }}"
      west_pacific_top: "{{ state_attr('sensor.nhl_wildcard','overall')[1]['children'][1]['standings']['entries'] }}"
      west_wildcard: "{{ state_attr('sensor.nhl_wildcard','children')[1]['standings']['entries'][:2] }}"
      west_hunt: >
          {% set hteams = namespace(hteam=[]) %}
          {% for team in state_attr('sensor.nhl_wildcard','children')[1]['standings']['entries'][2:] %}
            {% for stat in team['stats'] | selectattr('name','eq','clincher') %}
            {% else %}
                  {% set hteams.hteam = hteams.hteam + [team] %}
            {% endfor %}
          {% endfor %}
          {{ hteams.hteam }}
      west_eliminated: >
        {% set eteams = namespace(eteam=[]) %}
        {% for team in state_attr('sensor.nhl_wildcard','children')[1]['standings']['entries'][2:] %}
          {% for stat in team['stats'] %}
            {% if stat.name == 'clincher' %}
                {% set eteams.eteam = eteams.eteam + [team] %}
            {% endif %}
          {% endfor %}
        {% endfor %}
        {{ eteams.eteam }}

The other thing I saw mentioned previously was a symbol/text key or legend. I created a markdown card to add it, but now working on formatting so it is similar to how the NHL displays it (see the bottom of this website NHL Hockey Standings | NHL.com):

- type: markdown
  title: Legend
  content: >
    x - Clinched Playoff spot
    p - Presidents' Trophy
    GP - Games Played
    W - Wins (worth two points)
    L - Losses (worth zero points)
    OT - OT/Shootout losses (worth one point)
    PTS - Points
    P% - Points Percentage
    RW - Regulation Wins
    ROW - Regulation plus Overtime Wins
    GF - Goals For
    GA - Goals Against
    DIFF - Goal Differential
    HOME - Home record
    AWAY - Away Record
    S/O - Record in games decided by Shootout
    L10 - Record in last ten games
    STRK - Streak