Sports Standings and Scores

So I was curious how Tennis works with TeamTracker because they are individuals and not teams. It would be cool if you could just have teamtracker do events instead of individuals or some other method to simplify the code but I am not sure how that would work.

Anyway for fun I looked at https://site.web.api.espn.com/apis/site/v2/sports/tennis/atp/scoreboard to see what players were either in pre/in/post mode which is one of the items TT keys off of. I saw a couple and put them into my tennis.yaml sensor file. Here are the 4 I grabbed (I am not doing all 150 and would love to have someone post it if they get an itch to create them and I would put them on my github site).

- platform: teamtracker
  league_id: "ATP"
  team_id: "Jannik Sinner"
  name: "Jannik Sinner"
  
- platform: teamtracker
  league_id: "ATP"
  team_id: "Luca Nardi"
  name: "Luca Nardi"  

- platform: teamtracker
  league_id: "ATP"
  team_id: "Benjamin Bonzi"
  name: "Benjamin Bonzi" 

- platform: teamtracker
  league_id: "ATP"
  team_id: "Manuel Guinard"
  name: "Manuel Guinard"  
  

I changed up my Sandbox - Tennis dashboard to use post the TT cards if available. I like to keep things compact so here is what it looks like now.

Here is what the TT card tabs look like.

Here is the Sandbox dashboard code for any interested:

decluttering_templates:
  bpl_settings:
    card:
      type: custom:flex-table-card
      title: '[[title]]'
      css:
        table+: 'padding: 0px; width: 400px;'
        tbody tr td:first-child: 'width: 1%;'
        tbody tr td:nth-child(2): 'width: 1%;'
        tbody tr td:nth-child(3): 'width: 20%;'
        tbody tr:hover: 'background-color: green!important; color:white!important;'
      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]]'
      sort_by: ranks
      columns:
        - name: Rank
          data: ranks
          modify: x.current
        - name: Previous
          data: ranks
          modify: x.previous
        - name: Athlete
          data: ranks
          modify: >-
            '<div
            style="display:flex;justify-content:space-between;align-items:center;">'
            + '<div style="display:flex;align-items:center;">' + (typeof
            x.athlete.headshot !== 'undefined' ? '<img src="' +
            x.athlete.headshot + '"
            style="width:40px;height:40px;margin-right:5px;">' : '') +
            x.athlete.displayName + '</div>' + '<div style="background-image:
            url(' + (typeof x.athlete.flag !== 'undefined' ? x.athlete.flag :
            '') + '); background-size: cover; background-repeat: no-repeat;
            height: 30px; width: 50px; display: flex; align-items: center;
            justify-content: center;">' + (typeof x.athlete.flag !== 'undefined'
            ? x.athlete.flagAltText : '') + '</div>' + '</div>'
        - name: Pts
          data: ranks
          modify: x.points
  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]]" -%}
                    {{{"type": "custom:teamtracker-card",
                      "entity": team, 
                      "home_side": "right"}}},
              {%- endif -%} 
            {%- endif -%}
          {%- endfor -%}
        exclude:
          - entity_id: '*team_tracker*'
      sort:
        method: attribute
        attribute: date
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: black;
                color: silver;
                border-color: var(--ha-card-border-color, var(--divider-color, #e0e0e0));
                border-width: 2px;
                border-style: solid;
                overflow: hidden;
                width: 20%;
              }
              mwc-tab[active] {
                background: white !important;
                color: black !important;
              }
        card:
          type: custom:tabbed-card
          styles:
            '--mdc-theme-primary': black
            '--mdc-tab-text-label-color-default': silver
            '--mdc-typography-button-font-size': 12px
          tabs:
            - attributes:
                label: Tennis
                icon: mdi:tennis
              card:
                type: custom:mod-card
                card_mod:
                  style:
                    tabbed-card $: |
                      mwc-tab {
                        background: black;
                        border-color: silver;
                        border-width: 2px;
                        border-top-left-radius: 20px;
                        border-top-right-radius: 20px;
                        border-style: solid;
                        overflow: hidden;
                        width: 15%;
                      }
                      mwc-tab[active] {
                        background: white !important;
                        color: black !important
                      }
                card:
                  type: custom:tabbed-card
                  card_mod:
                    style:
                      tabbed-card $: |
                        mwc-tab {
                          background: black;
                          border-color: silver;
                          border-width: 2px;
                          border-top-left-radius: 20px;
                          border-top-right-radius: 20px;
                          border-style: solid;
                          overflow: hidden;
                          width: 10%;
                        }
                        mwc-tab[active] {
                          background: white !important;
                          color: black !important
                        }
                  tabs:
                    - attributes:
                        label: Men's Tennis
                        icon: mdi:account-group
                      card:
                        type: custom:mod-card
                        card_mod:
                          style:
                            tabbed-card $: |
                              mwc-tab {
                               background: black;
                               border-color: silver;
                               border-width: 2px;
                               border-top-left-radius: 20px;
                               border-top-right-radius: 20px;
                               border-style: solid;
                               overflow: hidden;
                               width: 15%;
                              }
                              mwc-tab[active] {
                                background: white !important;
                                color: black !important
                              }
                        card:
                          type: custom:tabbed-card
                          styles:
                            '--mdc-theme-primary': blue
                            '--mdc-tab-text-label-color-default': white
                            '--mdc-typography-button-font-size': 12px
                          tabs:
                            - attributes:
                                label: Standings
                                icon: mdi:tennis-ball-outline
                              card:
                                type: custom:stack-in-card
                                mode: vertical
                                cards:
                                  - type: markdown
                                    content: |
                                      <h1>ATP Rankings</h1>
                                  - type: custom:decluttering-card
                                    template: bpl_settings
                                    variables:
                                      - title: ATP
                                      - entity: sensor.tennis_atp_ranking
                            - attributes:
                                label: Pre, Post, Live
                              card:
                                type: horizontal-stack
                                cards:
                                  - type: vertical-stack
                                    cards:
                                      - type: markdown
                                        content: |
                                          <h1>ATP Games Pre</h1>
                                      - type: custom:decluttering-card
                                        template: game_stats
                                        variables:
                                          - sport: ATP
                                          - status: PRE
                                      - type: markdown
                                        content: |
                                          <h1>ATP Games Live</h1>
                                      - type: custom:decluttering-card
                                        template: game_stats
                                        variables:
                                          - sport: ATP
                                          - status: IN
                                      - type: markdown
                                        content: |
                                          <h1>ATP Games Completed</h1>
                                      - type: custom:decluttering-card
                                        template: game_stats
                                        variables:
                                          - sport: ATP
                                          - status: POST
                    - attributes:
                        label: Women's Tennis
                        icon: mdi:account-group
                      card:
                        type: horizontal-stack
                        cards:
                          - type: vertical-stack
                            cards:
                              - type: markdown
                                content: |
                                  <h1>WTA Rankings</h1>
                              - type: custom:decluttering-card
                                template: bpl_settings
                                variables:
                                  - title: WTA
                                  - entity: sensor.tennis_wta_ranking

More for fun than anything else

For tennis tracking, I’ve gotten rankings, upcoming tournament schedule and scores all pulled together for ATP and WTA. Here are some screenshots:



I have created player specific teamtracker sensors for those that I want to follow – since there isn’t a ā€˜team’ type list, players coming in/out of the top 25 or so will always be a moving target – I don’t have a solution for that.

Outside of the teamtracker sensors for individual players, I created four sensors to pull in the rankings and tournaments, as follows. The rankings only update once per week (Monday mornings) and I’ve got another automation to update them at that time.

##
## ATP Rankings
## 
- platform: rest
  scan_interval: 604800
  name: Tennis ATP Ranking
  unique_id: sensor.tennis_atp_ranking
  resource: https://site.web.api.espn.com/apis/site/v2/sports/tennis/atp/rankings?region=us&lang=en
  value_template: "{{ now() }}"
  json_attributes_path: "$.['rankings'][0]"
  json_attributes:
    - ranks
##
## WTA Rankings
## 
- platform: rest
  scan_interval: 604800
  name: Tennis WTA Ranking
  unique_id: sensor.tennis_wta_ranking
  resource: https://site.web.api.espn.com/apis/site/v2/sports/tennis/wta/rankings?region=us&lang=en
  value_template: "{{ now() }}"
  json_attributes_path: "$.['rankings'][0]"
  json_attributes:
    - ranks
##
## WTA Schedule
##
- platform: rest
  scan_interval: 86400
  name: Tennis WTA Schedule
  unique_id: sensor.tennis_wta_schedule
  resource_template: >-
    https://site.web.api.espn.com/apis/site/v2/sports/tennis/wta/scoreboard?region=us&lang=en&dates={{ now().strftime('%Y%m%d') }}-{{ now().replace(month=12, day=31).strftime('%Y%m%d') }}
  value_template: "{{ value_json.events | default([]) | count }}"
  json_attributes:
    - events

##
## ATP Schedule
##
- platform: rest
  scan_interval: 86400
  name: Tennis ATP Schedule
  unique_id: sensor.tennis_atp_schedule
  resource_template: >-
    https://site.web.api.espn.com/apis/site/v2/sports/tennis/atp/scoreboard?region=us&lang=en&dates={{ now().strftime('%Y%m%d') }}-{{ now().replace(month=12, day=31).strftime('%Y%m%d') }}
  value_template: "{{ value_json.events | default([]) | count }}"
  json_attributes:
    - events

For the schedule sensors, I added the dates variable to the api url and it brings back each tournament from today through the end of the year.

Here is the yaml:

decluttering_templates:
  ranking_settings:
    card:
      type: custom:flex-table-card
      title: '[[title]]'
      css:
        table+: >-
          padding: 0px; width: 100%; max-width: 1600px; display: block;
          overflow-x: auto;
        tbody tr td:first-child: 'width: 0.1%; text-align: center;'
        tbody tr td:nth-child(2): 'width: 0.1%; text-align: center;'
        tbody tr td:nth-child(3): 'width: auto; text-align: center;'
        tbody tr td:nth-child(n+4): 'width: auto;'
        tbody tr:hover: 'background-color: green!important; color: white!important;'
        '@media (max-width: 600px)': |
          tbody tr td {
            font-size: 12px;
            padding: 2px;
          }
          tbody tr td img {
            width: 15px;
            height: 15px;
          }
      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]]'
      sort_by: ranks
      columns:
        - name: Rk
          data: ranks
          modify: x.current
        - name: +-
          data: ranks
          modify: >-
            '<div style="color:' + (x.trend > 0 ? 'green' : x.trend < 0 ? 'red'
            : 'white') + ';">' + x.trend + '</div>'
        - name: Flag
          data: ranks
          modify: >-
            '<div style="white-space: nowrap; display: flex; align-items:
            center;">' + (typeof x.athlete.flag !== 'undefined' ? '<img src="' +
            x.athlete.flag + '"
            style="width:20px;height:20px;margin-right:5px;">' : '') + '<span>'
            + x.athlete.citizenshipCountry + '</span></div>'
        - name: Name
          data: ranks
          modify: >-
            '<div style="white-space: nowrap; overflow: auto; max-width: 150px;
            display: inline-block;">' + (typeof x.athlete.headshot !==
            'undefined' ? '<img src="' + x.athlete.headshot + '"
            style="width:20px;height:20px;margin-right:5px;">' : '') +
            (x.athlete.links && x.athlete.links.find(l =>
            l.rel.includes("playercard")) ? '<a href="' + x.athlete.links.find(l
            => l.rel.includes("playercard")).href + '" target="_blank"
            style="text-decoration: none; color: inherit;">' +
            x.athlete.displayName + '</a>' : x.athlete.displayName) + '</div>'
        - name: Pts
          data: ranks
          modify: x.points
  schedule_settings:
    card:
      type: custom:flex-table-card
      title: '[[title]]'
      css:
        table+: >-
          padding: 10px; width: 100%; max-width: 1600px; display: block;
          overflow-x: auto;
        tbody tr td:first-child: 'width: 20%; text-align: left;'
        tbody tr td:nth-child(2): 'width: auto; text-align: left;'
        tbody tr td:nth-child(3): 'width: auto; text-align: left;'
        tbody tr:hover: 'background-color: green!important; color: white!important;'
        '@media (max-width: 600px)': |
          tbody tr td {
            font-size: 12px;
            padding: 2px;
          }
          tbody tr td img {
            width: 15px;
            height: 15px;
          }
      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]]'
      columns:
        - name: Dates
          data: events
          modify: >-
            new Date(x.date).toLocaleDateString('en-US', { month: 'short', day:
            'numeric' })  + ' - ' + new
            Date(x.endDate).toLocaleDateString('en-US', { month: 'short', day:
            'numeric' })
        - name: Tournament
          data: events
          modify: >-
            '<div><a href="' + (x.links && x.links.length > 0 ? x.links[0].href
            : '#')  + '" target="_blank" style="text-decoration: none; color:
            inherit;">' + x.name + '</a><br> <span style="font-size: smaller;
            color: gray;">' + (x.venue ? x.venue.displayName : '') +
            '</span></div>'
        - name: Previous Winner
          data: events
          modify: >-
            (x.previousWinners && x.previousWinners.find(w => w.type.slug ===
            '[[type]]')) ?   (x.previousWinners.find(w => w.type.slug ===
            '[[type]]').headshot ?   '<img src="' + x.previousWinners.find(w =>
            w.type.slug === '[[type]]').headshot +   '" style="width: 20px;
            height: 20px; margin-right: 5px; vertical-align: middle;">' : '')
            +   '<a href="' + x.previousWinners.find(w => w.type.slug ===
            '[[type]]').links[0].href +   '" target="_blank"
            style="text-decoration: none; color: inherit;">' +  
            x.previousWinners.find(w => w.type.slug === '[[type]]').displayName
            + '</a>' : ''
  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]]" -%}
                    {{{"type": "custom:teamtracker-card",
                      "entity": team, 
                      "home_side": "right",
                      "outline": true,
                      "outline_color": 'white'}}},
              {%- endif -%} 
            {%- endif -%}
          {%- endfor -%}
      sort:
        method: attribute
        attribute: date
views:
  - title: Tennis
    icon: mdi:tennis
    type: panel
    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: 50%;
              }
              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: ATP
                icon: mdi:tennis
              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: Rankings
                      icon: mdi:format-list-bulleted
                    card:
                      type: custom:decluttering-card
                      template: ranking_settings
                      variables:
                        - title: ATP Rankings
                        - entity: sensor.tennis_atp_ranking
                  - attributes:
                      label: Schedule
                      icon: mdi:calendar
                    card:
                      type: custom:decluttering-card
                      template: schedule_settings
                      variables:
                        - title: ATP Schedule
                        - entity: sensor.tennis_atp_schedule
                        - type: mens-singles
                  - attributes:
                      label: Scores
                      icon: mdi:scoreboard
                    card:
                      type: horizontal-stack
                      cards:
                        - type: vertical-stack
                          cards:
                            - type: markdown
                              content: |
                                <h3>ATP Live Matches</h3>
                            - type: custom:decluttering-card
                              template: game_stats
                              variables:
                                - sport: ATP
                                - status: IN
                            - type: markdown
                              content: |
                                <h3>ATP Upcoming Matches</h3>
                            - type: custom:decluttering-card
                              template: game_stats
                              variables:
                                - sport: ATP
                                - status: PRE
                            - type: markdown
                              content: |
                                <h3>ATP Completed Matches</h3>
                            - type: custom:decluttering-card
                              template: game_stats
                              variables:
                                - sport: ATP
                                - status: POST
            - attributes:
                label: WTA
                icon: mdi:tennis
              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: Rankings
                      icon: mdi:format-list-bulleted
                    card:
                      type: custom:decluttering-card
                      template: ranking_settings
                      variables:
                        - title: WTA Rankings
                        - entity: sensor.tennis_wta_ranking_2
                  - attributes:
                      label: Schedule
                      icon: mdi:calendar
                    card:
                      type: custom:decluttering-card
                      template: schedule_settings
                      variables:
                        - title: WTA Schedule
                        - entity: sensor.tennis_wta_schedule_2
                        - type: womens-singles
                  - attributes:
                      label: Scores
                      icon: mdi:scoreboard
                    card:
                      type: horizontal-stack
                      cards:
                        - type: vertical-stack
                          cards:
                            - type: markdown
                              content: |
                                <h3>WTA Live Matches</h3>
                            - type: custom:decluttering-card
                              template: game_stats
                              variables:
                                - sport: WTA
                                - status: IN
                            - type: markdown
                              content: |
                                <h3>WTA Upcoming Matches</h3>
                            - type: custom:decluttering-card
                              template: game_stats
                              variables:
                                - sport: WTA
                                - status: PRE
                            - type: markdown
                              content: |
                                <h3>WTA Completed Matches</h3>
                            - type: custom:decluttering-card
                              template: game_stats
                              variables:
                                - sport: WTA
                                - status: POST

Hope this helps someone!

Fantastic Job Jason! Keep Going!

I see that you have a _2 with the wta schedule - sensor.tennis_wta_schedule_2

My guess is you might have played around with one first and then made changes or maybe not.

If you do want it to just read sensor.tennis_wta_schedule you can go into settings->Devices&Services->entities search for wta - delete the sensor sensor.tennis_wta_schedule and then go into sensor.tennis_wta_schedule_2 and rename it by deleting the _2. You will need to update your dashboard as well.

Great Job on what you have done so far!

1 Like

@bburwell that’s exactly what happened…couldn’t figure it out. but resolved now – thanks for all your help!

Are there any other workarounds for getting duplicate cards?

Tennis isn’t populating a value for the ā€˜team_homeaway’ attribute so I’ve removed that filter from the game stats tab which is allowing duplicates to appear.

I’m not showing duplicate cards on mine today, but I just put Amanda in as a quick test.

Do you have both tennis players names in there - teamtracker sensor?

If so you may need to create a check to see if the friendly_name has already been used. Similar to something I did with NCAAF/NCAAM to filter out conference only game.

Yes, it occurs when I have both players tracked in team tracker.

I looked at your filter and came up with something that will remove the duplicates (not as easy since the home/away isn’t an option for tennis) – which does filter out the duplicates for IN games.

However, it creates a ā€˜no card type configured’ error for the PRE and POST games. Not certain I understand what’s causing it to only work on the IN games.

This is what I’m using the game_stats section:

  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: |
          {%- set matches = namespace(seen=[]) -%}

          {%- for team in integration_entities("teamtracker") -%}
            {%- set team_id = state_attr(team, "team_id") %}
            {%- set opponent_id = state_attr(team, "opponent_id") %}

            {%- if state_attr(team, "league") == "[[sport]]" -%}
              {%- if states(team) == "[[status]]" -%}
                {%- if team_id is not none and opponent_id is not none %}
                  {%- set match = [team_id, opponent_id] | sort | join('-') %}
                    {%- if match not in matches.seen %}
                      {%- set matches.seen = matches.seen + [match] %}
                        {{{"type": "custom:teamtracker-card",
                          "entity": team, 
                          "home_side": "right",
                          "outline": true,
                          "outline_color": 'white'
                        }}},
                    {%- endif %}
                {%- endif %}
              {%- endif %}
            {%- endif %}
          {%- endfor %}
      sort:
        method: attribute
        attribute: date

So Jinja2 drives me crazy when you are trying to use namespaces. That’s why filtering on home works well because you don’t have to create, but in this case there isn’t a home.

For my NCAAF/M conferences I created a separate sensor that included the team names and then sorted on that (you can see it on my github ncaaf.yaml and template.yaml files but I don’t think you really need that here.

Maybe this is an alternative, haven’t really checked it for other than a couple players.

I see that team_id has the player id that ESPN should keep constant. For example Benjamin Bonzi is 2355 which equates to his page on ESPN - Benjamin Bonzi Stats, News, Pictures, Bio, Videos - ESPN

Since we know players competing against each other will be in each others card, maybe key off their id’s and since we don’t care if they are on the right or left only list the lower value id.

Try this instead (btw I always sandbox changes in the decluttering which is why you will see it called duplicate_game_stats and then I change dashboard calls until I verify that it works.

  duplicate_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") -%}
            {%- set team_id = state_attr(team, "team_id") %}
            {%- set opponent_id = state_attr(team, "opponent_id") %}

            {%- if state_attr(team, "league") == "[[sport]]" and states(team) == "[[status]]" -%}
              {%- if team_id is not none and opponent_id is not none and team_id < opponent_id -%}
                {{{"type": "custom:teamtracker-card",
                  "entity": team, 
                  "home_side": "right",
                  "outline": true,
                  "outline_color": "white"}}},
              {%- endif -%}
            {%- endif -%}
          {%- endfor -%}
      sort:
        method: attribute
        attribute: date

This is what I get:

and I have these in the tennis.yaml file

- platform: teamtracker
  league_id: "ATP"
  team_id: "Jannik Sinner"
  name: "Jannik Sinner"
  
- platform: teamtracker
  league_id: "ATP"
  team_id: "Luca Nardi"
  name: "Luca Nardi"  

- platform: teamtracker
  league_id: "ATP"
  team_id: "Daniel Altmaier"
  name: "Daniel Altmaier"  

- platform: teamtracker
  league_id: "ATP"
  team_id: "Benjamin Bonzi"
  name: "Benjamin Bonzi" 

- platform: teamtracker
  league_id: "ATP"
  team_id: "Luca Van Assche"
  name: "Luca Van Assche" 

- platform: teamtracker
  league_id: "ATP"
  team_id: "Manuel Guinard"
  name: "Manuel Guinard"  

- platform: teamtracker
  league_id: "ATP"
  team_id: "Jan-Lennard Struff"
  name: "Jan-Lennard Struff"  


- platform: teamtracker
  league_id: "WTA"
  team_id: "Amanda Anisimova"
  name: "Amanda Anisimova"    

@jasonkjennings here is one maybe you can figure out on tennis for me.

It seems to me that you would want to see who won the tournaments (at least for this year) on the page as well as the upcoming ones.

That is what I did for golf and was wondering if it is possible for tennis. Tennis API’s from what I have seen (at least for ATP) are a little goofy because sometimes it includes WTA data. Haven’t really looked at but that was what a cursory glance showed.

So here is what I have for golf (code is on github). Now I am having a heck of time finding golf rankings that I don’t have to scrape but eventually that will get worked out. Alsoneed to flush out pre/live/post but I’m getting off track, Golf is a work in progress

It would be interesting to add this to tennis.

Here is the golf schedule page:

And here is the code change (I liked your value_template so I added it) to give me the tournaments for this year - completed and future.


## PGA Schedule
##
- platform: rest
  scan_interval: 86400
  name: Golf PGA Schedule
  unique_id: sensor.golf_pga_schedule
  resource_template: >-
    https://site.web.api.espn.com/apis/site/v2/sports/golf/pga/scoreboard?region=us&lang=en&dates={{ now().replace(month=1, day=1).strftime('%Y%m%d') }}-{{ now().replace(month=12, day=31).strftime('%Y%m%d') }}
  value_template: "{{ value_json.events | default([]) | count }}"
  json_attributes:
    - events

I am having problems with your card… can you show me how you set the teamtracker integration for every athlete?
My sensors, for example, are named ā€œsensor.matteo_berrettiniā€ but they are not shown in the view. Maybe i must use a different name for those athlete sensors?

Depending on your setup you will need to create a sensor for Matteo Berrentttini. In my setup I put the players in a sensor yaml called tennis.yaml.

I don’t really follow tennis, but to check on Matteo I added this a minute ago to my tennis.yaml and since he plays on the ATP that is the league_id.

- platform: teamtracker
  league_id: "ATP"
  team_id: "Matteo Berrettini"
  name: "Matteo Berrettini"  

It yields this:

You would need to do that for every player you want to see in teamtracker.

If you are using code similar to what I did you can also click on the player in player rankings and it will take you to the ESPN website for that player and you can confirm the spelling of the name.

1 Like

here are the WTA/ATP top 50 – keep in mind that this is a static list and won’t update over time:

ATP:

- platform: teamtracker
  league_id: "ATP"
  team_id: "Jannik Sinner"
  name: "Jannik Sinner"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Alexander Zverev"
  name: "Alexander Zverev"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Carlos Alcaraz"
  name: "Carlos Alcaraz"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Taylor Fritz"
  name: "Taylor Fritz"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Casper Ruud"
  name: "Casper Ruud"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Novak Djokovic"
  name: "Novak Djokovic"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Daniil Medvedev"
  name: "Daniil Medvedev"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Alex de Minaur"
  name: "Alex de Minaur"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Tommy Paul"
  name: "Tommy Paul"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Andrey Rublev"
  name: "Andrey Rublev"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Grigor Dimitrov"
  name: "Grigor Dimitrov"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Stefanos Tsitsipas"
  name: "Stefanos Tsitsipas"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Holger Rune"
  name: "Holger Rune"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Felix Auger-Aliassime"
  name: "Felix Auger-Aliassime"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Frances Tiafoe"
  name: "Frances Tiafoe"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Karen Khachanov"
  name: "Karen Khachanov"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Hubert Hurkacz"
  name: "Hubert Hurkacz"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Pablo Carreno Busta"
  name: "Pablo Carreno Busta"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Denis Shapovalov"
  name: "Denis Shapovalov"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Nick Kyrgios"
  name: "Nick Kyrgios"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Roberto Bautista Agut"
  name: "Roberto Bautista Agut"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Diego Schwartzman"
  name: "Diego Schwartzman"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Lorenzo Musetti"
  name: "Lorenzo Musetti"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Borna Coric"
  name: "Borna Coric"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Sebastian Korda"
  name: "Sebastian Korda"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Daniel Evans"
  name: "Daniel Evans"
- platform: teamtracker
  league_id: "ATP"
  team_id: "John Isner"
  name: "John Isner"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Miomir Kecmanovic"
  name: "Miomir Kecmanovic"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Alejandro Davidovich Fokina"
  name: "Alejandro Davidovich Fokina"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Jenson Brooksby"
  name: "Jenson Brooksby"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Adrian Mannarino"
  name: "Adrian Mannarino"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Albert Ramos-Vinolas"
  name: "Albert Ramos-Vinolas"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Lorenzo Sonego"
  name: "Lorenzo Sonego"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Filip Krajinovic"
  name: "Filip Krajinovic"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Alexander Bublik"
  name: "Alexander Bublik"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Marton Fucsovics"
  name: "Marton Fucsovics"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Emil Ruusuvuori"
  name: "Emil Ruusuvuori"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Brandon Nakashima"
  name: "Brandon Nakashima"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Aslan Karatsev"
  name: "Aslan Karatsev"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Pedro Martinez"
  name: "Pedro Martinez"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Sebastian Baez"
  name: "Sebastian Baez"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Benjamin Bonzi"
  name: "Benjamin Bonzi"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Ugo Humbert"
  name: "Ugo Humbert"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Francisco Cerundolo"
  name: "Francisco Cerundolo"
- platform: teamtracker
  league_id: "ATP"
  team_id: "Maxime Cressy"
  name: "Maxime Cressy"

WTA:

- platform: teamtracker
  league_id: "WTA"
  team_id: "Aryna Sabalenka"
  name: "Aryna Sabalenka"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Iga Swiatek"
  name: "Iga Swiatek"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Coco Gauff"
  name: "Coco Gauff"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Jasmine Paolini"
  name: "Jasmine Paolini"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Jessica Pegula"
  name: "Jessica Pegula"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Madison Keys"
  name: "Madison Keys"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Elena Rybakina"
  name: "Elena Rybakina"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Qinwen Zheng"
  name: "Qinwen Zheng"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Emma Navarro"
  name: "Emma Navarro"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Paula Badosa"
  name: "Paula Badosa"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Danielle Collins"
  name: "Danielle Collins"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Daria Kasatkina"
  name: "Daria Kasatkina"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Diana Shnaider"
  name: "Diana Shnaider"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Anhelina Kalinina"
  name: "Anhelina Kalinina"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Mirra Andreeva"
  name: "Mirra Andreeva"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Magda Linette"
  name: "Magda Linette"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Veronika Kudermetova"
  name: "Veronika Kudermetova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Liudmila Samsonova"
  name: "Liudmila Samsonova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Anna Kalinskaya"
  name: "Anna Kalinskaya"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Jelena Ostapenko"
  name: "Jelena Ostapenko"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Petra Kvitova"
  name: "Petra Kvitova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Ekaterina Alexandrova"
  name: "Ekaterina Alexandrova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Barbora Krejcikova"
  name: "Barbora Krejcikova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Karolina Pliskova"
  name: "Karolina Pliskova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Karolina Muchova"
  name: "Karolina Muchova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Zhang Shuai"
  name: "Zhang Shuai"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Elise Mertens"
  name: "Elise Mertens"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Victoria Azarenka"
  name: "Victoria Azarenka"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Sloane Stephens"
  name: "Sloane Stephens"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Fiona Ferro"
  name: "Fiona Ferro"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Kaia Kanepi"
  name: "Kaia Kanepi"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Jule Niemeier"
  name: "Jule Niemeier"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Marie Bouzkova"
  name: "Marie Bouzkova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Clara Tauson"
  name: "Clara Tauson"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Claire Liu"
  name: "Claire Liu"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Elina Svitolina"
  name: "Elina Svitolina"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Marta Kostyuk"
  name: "Marta Kostyuk"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Anastasia Potapova"
  name: "Anastasia Potapova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Bianca Andreescu"
  name: "Bianca Andreescu"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Leylah Fernandez"
  name: "Leylah Fernandez"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Amanda Anisimova"
  name: "Amanda Anisimova"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Camila Giorgi"
  name: "Camila Giorgi"
- platform: teamtracker
  league_id: "WTA"
  team_id: "Alize Cornet"
  name: "Alize Cornet"

2 Likes

@jasonkjennings Just a question, using the config flow in the integration. am i forced to insert all those one by one, or there is another way simplier?

Absolutely…I dropped these into my sensors.yaml file (i actually created a folder which the config yaml points to and then and dropped these two files in that folder). Then just reload your config and all the sensors are there.

Ok, but in this way i need first to delete the entries inside the ui integration?

EDIT: Ok solved… i see sensors added manually are automatically added to the ones created in the UI.

Thanks…

1 Like

For s&g’s I am toying around with UFL. I’ve added the ufl_sensors.yaml to my github.

There seems to be less data in UFL so needed to create a new decluttering template (ufl_settings). Season doesn’t start until March 28th and ends June 14th.

I’m sure I will need to adjust (red zone/additional data points/schedule/etc.) but code is here for those that want it: https://github.com/bburwell/HA-Sports-Scores/tree/main

Is there a way to exclude from recorder and history the sensors from this integration without putting them one by one in the entities?

You should be able to add the sensors to your configuration.yaml. I would need to double check but I believe all of the sensors in all of my yaml’s start with the sport.

So all sports sensors start with their name NBA = sensor.nba_, NFL = sensor.nfl_, Golf = sensor.golf_*, etc.

Here is my configuration.yaml recorder section but honestly I don’t pay attention to the what I am guessing you are going after is db size.

recorder:
  exclude:
    domains:
      - teamtracker
    entity_globs:
      - sensor.mlb_*
      - sensor.nfl_*
      - sensor.nhl_*
      - sensor.nba_*
      - sensor.wnba_*
      - sensor.ncaaf_*
      - sensor.f1_*
      - sensor.nascar_*
      - sensor.tennis_*
      - sensor.golf_*
      - sensor.ufl_*

You probably could write an automation to purge more frequently but would need to test. Something like this:

alias: Purge Sports Sensor Data
description: ""
triggers:
  - at: "02:00:00"
    trigger: time
actions:
  - data:
      keep_days: 5
    target:
      entity_id:
        - sensor.mlb_*
        - sensor.nfl_*
        - sensor.nhl_*
        - sensor.nba_*
        - sensor.wnba_*
        - sensor.ncaaf_*
        - sensor.f1_*
        - sensor.nascar_*
        - sensor.tennis_*
        - sensor.golf_*
        - sensor.ufl_*
    action: recorder.purge_entities
mode: single

Might check this link as well
https://community.home-assistant.io/t/how-to-keep-your-recorder-database-size-under-control/295795

If it is a performance thing when you are looking at states in developer tools and it is taking a long time - make sure to uncheck the attributes box (@ehcah showed me that) and it really helps when youre looking at sensors.

Yes, i was thinking about it, but for my fault i forgot to name the sensors with the _tennis name. So now i am forced to rename all of them. Or put in the automation the sensors one by one… :frowning:

Hmm… this is very interesting… i never thought about it… Thanks.

So I started looking at ESPN Endpoints. I feel that there are a couple good sites that get into the endpoints and discuss syntax, usage etc. but there are a lot of different sites you need to go to get the overall endpoints that we use in HA.

So I put together a CSV file that has a bunch of endpoints that I will continue to update from time to time.

Absolutely not complete, but figured it might help some of us on this thread and those that are new that may be looking for a different soccer league as an example and needs help in getting the link.

Feel free to comment and if you have additional links let me know and I will add them to the file.

Here is the link: https://github.com/bburwell/HA-Sports-Scores. The file is in the ESPN API endpoints.

Here is just a snippet of what is in the file.

Update (3/3/2025) - trying to add additional paths for other sports but haven’t added sensors or dashboards for these. Some of the sports added include:

  • Basketball (NBL)
  • College (Hockey,lacrosse,women’s field hockey,volleyball,water-polo(?))
  • MMA (UFC)
  • Lacrosse (PLL/NLL)
1 Like