Sports Standings and Scores

@bburwell … that question is asked too many times. I would think we should do a test in code if red-zone sensor does not exist, do nothing.

I thought I had that in mine, but maybe I didn’t share.

Sorry - on travel this week just getting back.

I agree Red Zone seems to be the sticking point but that is typically/always has been that NFL wasn’t added to sensors - which makes sense. JC had NFL sensors added which seems that it would pass the test - assuming that because mine didn’t have the conditional test and it worked.

@jcnooo do you still have your test sandbox setup? If so I wonder if you could test this change and see if you still can see the cards.

The only change I made was this:

{%- if team in state_attr('sensor.nfl_red_zone','teams') -%}

is now this:

{%- if states('sensor.nfl_red_zone') is defined and team in state_attr('sensor.nfl_red_zone','teams') -%}

Here is the whole test dashboard code:

decluttering_templates:
  game_stats:
    card:
      type: custom:auto-entities
      unique: true
      show_empty: false
      card:
        type: custom:layout-card
        layout_type: masonry
        width: 200px
        max-columns: 5
      card_param: cards
      filter:
        template: |
          {%- for team in integration_entities("teamtracker") -%}
            {%- if state_attr(team, "league") == "[[sport]]" -%}
              {%- if states(team) == "[[status]]" -%}
                {%- if state_attr(team, "team_homeaway") == "home" -%}
                  {%- if states('sensor.nfl_red_zone') is defined and team in state_attr('sensor.nfl_red_zone','teams') -%}
                    {{{"type": "custom:teamtracker-card",
                      "entity": team,
                      "card_mod": {"style": "ha-card {\n\n    color:  black; \n    background-color:  #ffcccc; \n    box-shadow: 0 0 10px 5px red;\n}\n"},
                      "home_side": "right"}}},
                  {%- else -%}
                    {{{"type": "custom:teamtracker-card",
                      "entity": team, 
                      "home_side": "right"}}},
                  {%- endif -%}
                {%- endif -%}
              {%- endif -%}
            {%- endif -%}
          {%- endfor -%}
        exclude:
          - entity_id: '*team_tracker*'
      sort:
        method: attribute
        attribute: date
views:
  - title: Home
    cards:
      - type: custom:layout-card
        layout_type: custom:grid-layout
        layout:
          grid-template-columns: repeat(4, 1fr)
          grid-gap: 10px
        cards:
          - type: vertical-stack
            cards:
              - type: custom:teamtracker-card
                entity: sensor.chicago_blackhawks
                outline: true
                outline_color: deeppink
              - type: custom:teamtracker-card
                entity: sensor.kansas_city_chiefs
                outline: true
                outline_color: deeppink
          - type: vertical-stack
            cards:
              - type: markdown
                content: |
                  <h1>NHL Games Live</h1>
              - type: custom:decluttering-card
                template: game_stats
                variables:
                  - sport: NHL
                  - status: IN
          - type: vertical-stack
            cards:
              - type: markdown
                content: |
                  <h1>NHL Games Coming-up</h1>
              - type: custom:decluttering-card
                template: game_stats
                variables:
                  - sport: NHL
                  - status: PRE
          - type: vertical-stack
            cards:
              - type: markdown
                content: |
                  <h1>NHL Games Completed</h1>
              - type: custom:decluttering-card
                template: game_stats
                variables:
                  - sport: NHL
                  - status: POST
        column_span: 4
    header:
      layout: center
      badges_position: top
    type: custom:grid-layout

and this is what I am seeing with the new code right now:

Posted final March Madness Dashboard to my github- https://github.com/bburwell/HA-Sports-Scores/. Probably won’t change anything this year but hope for more of a true bracket layout next year.

Figured since I had the code already, just needed to adjust for Women’s, I would add it as well to my Github - Template and Dashboard updated code. Also Added Odds & O/U if available.

I also played around with NFL and MLB brackets. More on my Github but this is a quick view of both:

@bburwell Any plans to create a playoff view for the NBA/NHL playoffs?

I started but I haven’t finished yet.

I am keying off the notes section in the API to figure out rounds. In the other sports I don’t run into the dreaded ‘Template output exceeded maximum size of 262144 characters’ error but in NHL and NBA I am running into these. So first round isn’t there.

I am going to need to filter out first round data or find a way to filter on something else. This is the reason I moved to rest mostly because the NCAAF was running into this on almost every team. You don’t have this limitation with REST in Home Assistant.

For anyone who wants to help, I have updated the 6 individual sports sensors NCAAM/NCAAW/NFL/MLB/NHL/NBA, the Template.yaml and the sandbox dashboard NCAA Men’s & Women’s March Madness, NFL, MLB, NBA, NHL on my github.

The code is incredibly lazy and I need to clean it up, but hopefully it will help someone. All of the playoffs are in their own dashboard but I will eventually move them to where they should go.

You can see that I am not showing the first round with NHL and NBA and it is because of template error mentioned above. Everything else is there.

Also remember these are for last season. So I will need to update dates for this season on all teams.

Quick Edit:
Looks like NBA is starting to populate so I have updated both NBA and NHL Dates (see below). NHL should work but will check in a couple days.

NHL 2025: ?dates=20250419-20250623
NBA 2025: ?dates=20250415-20250622

Here are a couple screenshots:


Edit:
Here is what I am seeing for NBA right now (added play-in)

Edit 2:
Couldn’t figure out how to Fix NBA/NHL with filtering/templates, so created a couple python files to create json files and then pull them into REST sensors in HA. I put all of the changes up and tried to show how to use them in the readme. NHL isn’t populating yet so can’t check this years data yet but expect that in a couple days. I do have NBA First round showing up now and I have added broadcast station next to time. All of the code is up on github if anyone wants it.

Edit 3(4/17/2025):
Looks like NHL is getting populated and seems to be working. Remember you will need the Python Script for NHL. I have also added a new dashboard (I consolidated the decluttering into a single template and added WNBA to that dashboard. That is the primary one I will be working on moving forward but will also try to keep the other one updated as well.

This is what I am showing currently for NHL.

1 Like

Thats really looks great.

Can you maybe share the code and explain how to integrate it?

Take a look at my github. All of the code (Sensors/Templates/Dashboard) are up there and the readme covers most of it. Here is the link: GitHub - bburwell/HA-Sports-Scores: Home Assistant Sports Scores, Standings, Dashboards and Yaml's for Home Assistant

Sorry, I didn’t know it was already there. Thank you.

But now I have a small problem. I have the complete code from here

github.com/bburwell/HA-Sports-Scores

inserted into a new dashboard.

then i include the following into my sensor.yaml:

## Post Season Playoffs 
  - platform: rest
    scan_interval: 90
    name: NHL Playoffs Stanleycup
    unique_id: sensor.nhl_playoffs_stanleycup
    #2024 -- resource: https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard?dates=20240420-20240624
    resource: https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard?dates=20250419-20250623
    value_template: "{{ now() }}"
    json_attributes:
        - leagues
        - events
      
  - platform: rest
    name: NHL Playoff West 1st Round
    unique_id: sensor.nhl_playoff_west_1st_round
    resource:  http://192.168.xxx.xxx:8123/local/nhl_west_1st_round.json
    value_template: "{{ now()}}"  
    json_attributes: 
     - events
      
  - platform: rest
    name: NHL Playoff East 1st Round
    unique_id: sensor.nhl_playoff_east_1st_round
    resource:  http://192.168.xxx.xxx:8123/local/nhl_east_1st_round.json
    value_template: "{{ now()}}"    
    json_attributes: 
     - events

But now it looks like this:

What am I doing wrong?

So First round pulls from the python files because if I use templates HA barks. I explain it in the readme a little bit.

You will need to put the python files in config/www directory and then change the configuration.yaml like I call out and then create an automation that calls the json files.

  • The python file needs to be called from HA because of the last couple of lines:

    • save_to_file(“/config/www/nhl_west_1st_round.json”, west_1st_round)
    • save_to_file(“/config/www/nhl_east_1st_round.json”, east_1st_round)
  • if you wanted to test this just remove the /config/www/ from the last lines and then do python nhl-1st.py from the www directory and it should create 2 json files. Just make sure you put it back so HA puts the data in the right directory.

For it to fill out the rest of the NHL that you are looking at you also need to make sure that you have the playoff section of the nhl_sensors.yaml loaded as well as the NHL section of the template.yaml

You also need to change the ip in those sensors to point to your ip. For example if your HA IP is 192.168.1.10 - these lines should say - http://192.168.1.10:8123/local/nhl_west_1st_round.json

If you wanted to check to see if the dashboard is working you could grab the playoff section out of Nfl_sensors.yaml and the NFL section in the template.yaml. NFL doesn’t have enough data to exceed the HA template.

Ok for fun I spoke with GROK today ;). I didn’t like the way 1st Round NBA & NHL rolled out. The layout was confusing so I had some fun with GROK.

Now this has so many moving parts it might not be for everyone but I’ll try to keep it simple.

I created a Dashboard that lays out the 7 games and keeps the teams together. Here is how the data is created:

  • First I have 2 Python Files they’re called NHL & NBA -1st.grok.py (youll also see GPT called out but in my mind GPT is like Kleenex) but this was done with GROK. GROK seems to be smarter than GPT, feistier, and loves to hear itself ramble about everything you ask it to do. But it was good on this project.
  • The python files create multiple json files based on 1st Round, 2nd Round, Finals and Championship or Stanley Cup. They need to be placed in the /config/www/ directory.
  • You need to update the configuration.yaml as well and then create an automation - mostly laid out in my github readme.
  • Within the NBA and NHL sensor files I am using rest to populate sensors based on these files. These sensors have all of the data from ESPN API for that round and then there is a series section that lumps all of teams that are playing each other together but just pulls some of the data.
  • I then have a separate Sandbox Dashboard call GROK Dashboard II
  • The Files are in Python Files, Sensors, Dashboards Respectively and are in the usual places on my github.
  • I also have markdown cards in dashboard to keep things from being unruly but I need the real estate to show 7 games.
  • At some point I am going to do the same for MLB Playoffs. Other sports are basically 1 and done so I am leaving them with the old code.
    Here is what it looks like and the files are up on my github. Play, change, and contribute.


ok thanks.

I’m now seeing data, but there’s still something wrong with the layout. What else might I be doing wrong?

And I also tried the second version Grok.
All json files were created.
but the data is not displayed to me:

Great you’re almost there. Nicely done.

So your 2 separate asks:
1st Ask - I am actually cheating with the Flex-Table Card and just using 1 column. An idea I stole from @ehcah. Data is sent to the decluttering template and it builds the code inside the single cell. I am wrapping a border around it with card-mod. The color I am picking is in the variables in the dashboard. For example with NHL Eastern Conference Playoffs the color is ‘#D50032’. So you need to make sure that these variables are in there if you want the border and color of your choosing.

                                        variables:
                                          - title: Eastern Conference 1st Round
                                          - color: '#D50032'
                                          - entity: sensor.nhl_playoff_east_1st_round
                                          - events: events
                                          - show_rankings: false
                                          - show_series: true
                                          - show_broadcast: true

2nd Ask - So right now only the 1st round json files are being populated by ESPN and they haven’t put data into the next rounds yet and I based the structure on last years data. Hoping I wont have to tweak it but yet to be seen.

  • First step is make sure the files in the /config/www/*.json files have data in them. Specifically look at nhl_west_1st_round_gpt.json or nba_west_1st_round_gpt.json.
  • The First few lines in that file should look like this:
{
  "sport": "NHL",
  "events": [
    {
      "id": "401768100",
      "uid": "s:70~l:90~e:401768100",
      "date": "2025-04-19T22:00Z",
      "name": "St. Louis Blues at Winnipeg Jets",
      "shortName": "STL @ WPG",
      "season": {
        "year": 2025,
        "type": 3,
        "slug": "post-season"
      },
      "competitions": [
        {
          "id": "401768100",
          "uid": "s:70~l:90~e:401768100~c:401768100",
          "date": "2025-04-19T22:00Z",
          "attendance": 15225,
  • As long as that looks correct, I am guessing you haven’t added the sections in the nhl_sensors.yaml or nba_sensors.yaml to pull the code into the sensors.
  • Make sure that you update the IP with your own HA IP
  • So confirm that you have the sections in nhl/nba_sensors.yaml (cleaned up the github to show the code between the ############################ gpt #################### comments.
  • The other step is you need to create an automation that updates the sensors. I have updated my github readme to show NHL automation calling the python files as well. This automation only runs durning the playoffs. I have one for NBA as well.

Flow is:

  • Automation calls Python files
  • json files are created/updated
  • sensors in nhl or nba monitor the json files and update their respective sensors (1st Round/2nd Round/etc.)
  • Dashboard updates based on sensor data.
  • Customization can be done with variables or just going into the code and tweaking.

Hope this helps.

1 ask.

this was in my desclutting:

cards:
                      - type: custom:decluttering-card
                        template: sports_playoffs
                        variables:
                          - title: Eastern Conference 1st Round
                          - color: '#D50032'
                          - entity: sensor.nhl_playoff_east_1st_round
                          - events: events
                          - show_rankings: false
                          - show_series: true
                          - show_broadcast: true

but don´t work.

Ask 2:
in the nhl_west_1st_round_gpt.json are datas
Sorry I overlooked the sensor part for gpt but I’ve added the part now and restarted HA.
My sensor.yaml currently looks like this regarding NHL:

##
## NHL Standings
##        
  - platform: rest
    scan_interval: 36000
    name: NHL Standings
    unique_id: sensor.nhl_standings
    resource: https://site.web.api.espn.com/apis/v2/sports/hockey/nhl/standings?seasontype=2&type=0&level=3
    value_template: "{{ now() }}"
    json_attributes:
        - children

##
## NHL Starting Goalies
##
  - platform: rest
    name: nhl_starting_goalies
    unique_id: sensor.nhl_starting_goalies
    scan_interval: 3600
    resource_template: >
      https://www.dailyfaceoff.com/_next/data/{{states('sensor.starting_goalie_buildid')}}/starting-goalies.json
    value_template: "{{ value_json.pageProps.date }}"
    json_attributes_path: "$.pageProps"
    json_attributes:
      - data    
##
## 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

######################### Post Season Playoffs 
  - platform: rest
    scan_interval: 90
    name: NHL Playoffs Stanleycup
    unique_id: sensor.nhl_playoffs_stanleycup
    #2024 -- resource: https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard?dates=20240420-20240624
    resource: https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard?dates=20250419-20250623
    value_template: "{{ now() }}"
    json_attributes:
      - leagues
      - events
      
  - platform: rest
    name: NHL Playoff West 1st Round
    unique_id: sensor.nhl_playoff_west_1st_round
    resource:  http://192.168.178.88:8123/local/nhl_west_1st_round.json
    value_template: "{{ now()}}"  
    json_attributes: 
     - events
      
  - platform: rest
    name: NHL Playoff East 1st Round
    unique_id: sensor.nhl_playoff_east_1st_round
    resource:  http://192.168.178.88:8123/local/nhl_east_1st_round.json
    value_template: "{{ now()}}"    
    json_attributes: 
     - events

###  2nd Round GPT
  - platform: rest
    name: NHL Playoff West 2nd Round gpt
    unique_id: sensor.nhl_playoff_west_2nd_round_gpt
    resource:  http://192.168.178.88:8123/local/nhl_west_2nd_round_gpt.json
    value_template: "{{ now()}}"  
    json_attributes: 
     - events
     - series
      
  - platform: rest
    name: NHL Playoff East 2nd Round gpt
    unique_id: sensor.nhl_playoff_east_2nd_round_gpt
    resource:  http://192.168.178.88:8123/local/nhl_east_2nd_round_gpt.json
    value_template: "{{ now()}}"    
    json_attributes: 
     - events
     - series
     
### finals
  - platform: rest
    name: NHL Playoff West Final gpt
    unique_id: sensor.nhl_playoff_west_final_gpt
    resource:  http://192.168.178.88:8123/local/nhl_west_final_gpt.json
    value_template: "{{ now()}}"  
    json_attributes: 
     - events
     - series
      
  - platform: rest
    name: NHL Playoff East Final gpt
    unique_id: sensor.nhl_playoff_east_final_gpt
    resource:  http://192.168.178.88:8123/local/nhl_east_final_gpt.json
    value_template: "{{ now()}}"    
    json_attributes: 
     - events
     - series

### Stanley Cup
  - platform: rest
    name: NHL Stanley Cup Final gpt
    unique_id: sensor.nhl_stanley_cup_final_gpt
    resource:  http://192.168.178.88:8123/local/nhl_stanley_cup_final_gpt.json
    value_template: "{{ now()}}"  
    json_attributes: 
     - events
     - series

############################ gpt ####################
  - platform: rest
    name: NHL Playoff East 1st Round
    unique_id: sensor.nhl_playoff_east_1st_round
    resource:  http://192.168.178.88:8123/local/nhl_east_1st_round.json
    value_template: "{{ now()}}"    
    json_attributes: 
     - events

###  2nd Round GPT
  - platform: rest
    name: NHL Playoff West 2nd Round gpt
    unique_id: sensor.nhl_playoff_west_2nd_round_gpt
    resource:  http://192.168.178.88:8123/local/nhl_west_2nd_round_gpt.json
    value_template: "{{ now()}}"  
    json_attributes: 
     - events
     - series
      
  - platform: rest
    name: NHL Playoff East 2nd Round gpt
    unique_id: sensor.nhl_playoff_east_2nd_round_gpt
    resource:  http://192.168.178.88:8123/local/nhl_east_2nd_round_gpt.json
    value_template: "{{ now()}}"    
    json_attributes: 
     - events
     - series
### finals
  - platform: rest
    name: NHL Playoff West Final gpt
    unique_id: sensor.nhl_playoff_west_final_gpt
    resource:  http://192.168.178.88:8123/local/nhl_west_final_gpt.json
    value_template: "{{ now()}}"  
    json_attributes: 
     - events
     - series
      
  - platform: rest
    name: NHL Playoff East Final gpt
    unique_id: sensor.nhl_playoff_east_final_gpt
    resource:  http://192.168.178.88:8123/local/nhl_east_final_gpt.json
    value_template: "{{ now()}}"    
    json_attributes: 
     - events
     - series

### Stanley Cup
  - platform: rest
    name: NHL Stanley Cup Final gpt
    unique_id: sensor.nhl_stanley_cup_final_gpt
    resource:  http://192.168.178.88:8123/local/nhl_stanley_cup_final_gpt.json
    value_template: "{{ now()}}"  
    json_attributes: 
     - events
     - series

Unfortunately, there’s still no display.

But I see this in my Watchman:

event.competitions [missing] template.yaml

event.id [missing] template.yaml

Compound I apologize that one I think is on me. When I was updating the sensors I think I overwrote/deleted 1st round GPT.

I’ve updated NHL and reviewed NBA.

Try this code in the NHL sensors and reboot.

You need to put in your HA IP.

############################ gpt ####################

###  1st Round GPT
- platform: rest
  name: NHL Playoff West 1st Round gpt
  unique_id: sensor.nhl_playoff_west_1st_round_gpt
  resource:  http://192.168.xxx.xxx:8123/local/nhl_west_1st_round_gpt.json
  value_template: "{{ now()}}"  
  json_attributes: 
   - events
   - series
      
- platform: rest
  name: NHL Playoff East 1st Round gpt
  unique_id: sensor.nhl_playoff_east_1st_round_gpt
  resource:  http://192.168.xxx.xxx:8123/local/nhl_east_1st_round_gpt.json
  value_template: "{{ now()}}"    
  json_attributes: 
   - events
   - series


###  2nd Round GPT
- platform: rest
  name: NHL Playoff West 2nd Round gpt
  unique_id: sensor.nhl_playoff_west_2nd_round_gpt
  resource:  http://192.168.xxx.xxx:8123/local/nhl_west_2nd_round_gpt.json
  value_template: "{{ now()}}"  
  json_attributes: 
   - events
   - series
      
- platform: rest
  name: NHL Playoff East 2nd Round gpt
  unique_id: sensor.nhl_playoff_east_2nd_round_gpt
  resource:  http://192.168.xxx.xxx:8123/local/nhl_east_2nd_round_gpt.json
  value_template: "{{ now()}}"    
  json_attributes: 
   - events
   - series
### finals
- platform: rest
  name: NHL Playoff West Final gpt
  unique_id: sensor.nhl_playoff_west_final_gpt
  resource:  http://192.168.xxx.xxx:8123/local/nhl_west_final_gpt.json
  value_template: "{{ now()}}"  
  json_attributes: 
   - events
   - series
      
- platform: rest
  name: NHL Playoff East Final gpt
  unique_id: sensor.nhl_playoff_east_final_gpt
  resource:  http://192.168.xxx.xxx:8123/local/nhl_east_final_gpt.json
  value_template: "{{ now()}}"    
  json_attributes: 
   - events
   - series

### Stanley Cup
- platform: rest
  name: NHL Stanley Cup Final gpt
  unique_id: sensor.nhl_stanley_cup_final_gpt
  resource:  http://192.168.xxx.xxx:8123/local/nhl_stanley_cup_final_gpt.json
  value_template: "{{ now()}}"  
  json_attributes: 
   - events
   - series
############################ gpt ####################

For the color challenge maybe try clearing your browser cache. I can’t see any reason why the border wouldn’t be changing.

Maybe also make sure you are running the latest card_mod.

If that doesn’t work you could replace the variable in the decluttering template card_mod section and see if that works:

Replace this:
border: 2px solid [[color]];
with this:
border: 2px solid #D50032;

ok now I get data.

but it simply cuts off on the right at the Western conference:

Regarding the color in the other case, I entered this code:
border: 2px solid #D50032;

I have already cleared the cache

but it still shows it like this:!

I just re-uploaded the Playoff Updated - March Madness NCAAM,NCAAW,MLB,NFL,NHL,NBA,WNBA dashboard. Try pasting that over your current one and see if there was something askew.

From a formatting perspective on the GROK daskboard, I put everything on a large screen, since I primarily am at my PC vs on a phone. So it is not optimized. It shouldn’t split like that though. If I shrink my browser it looks like this:

. Ugly but uniform split

If you wanted to make everything vertical then under cards change

  • expanded: false
    cards:
    - type: horizontal-stack
    to
  • expanded: false
    cards:
    - type: vertical-stack
  card:
                                type: vertical-stack
                                cards:
                                  - type: custom:expander-card
                                    title: NBA PLayoffs 1st Round
                                    icon: mdi:basketball
                                    expanded: false
                                    cards:
                                      - type: horizontal-stack
                                        cards:
                                          - type: custom:decluttering-card
                                            template: sports_playoffs_series_gpt
                                            variables:
                                              - title: Eastern Conference 1st Round

What happens if you change the card_mod in the decluttering_template to this?

 card_mod:
        style:
          .: |
            ha-card {
              overflow: auto;
              border: 2px solid [[color]];
              border-radius: 8px;
            }
          $: |
            .card-header {
              background-color: pink;
              color: white;
              padding: 8px!important;
              font-size: 14px!important;
              font-weight: bold!important;
            }

This is what I get in the GROK dashboard

So when I make the browser window as big as possible on the PC it looks like this:

But if I make it a little smaller it looks like this:


It simply cuts off on the right. Like yours, especially with the colors, it doesn’t look right for me.