NFL game sensor (scores, possession, etc)

Yeah I was trying to think of a way to add possession and available timeouts. One thing is certain, the ESPN API has way more info available than the NFL API.

Ok, so this is a lot. Here are all the sensors I have pulled for various purposes from the ESPN api. Some of these I ended up not using, but didn’t delete them in case I want them for future use:

######################################
############SAINTS SENSORS############
######################################

########ESPN.COM JSON RETRIEVAL########

########SAINTS SCORE SENSOR########
rest:
- resource: http://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard
  scan_interval: 86400
  sensor:
    - name: Saints Score
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% if competition.status.type.state != 'pre' or competition.status.type.state == 'post' %}
                  {% for competitor in competition["competitors"] %}
                    {% if  competitor.team.abbreviation == 'NO' %}
                      {{ competitor.score }}
                    {% endif %}
                  {% endfor %}
                {% else %}
                  0
                {% endif %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS DISPLAY NAME SENSOR########  
    - name: Saints Display Name
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'NO' %}
                    {{ competitor.team.displayName }} ({{ competitor.records.0.summary }})
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS NAME ABBR SENSOR########  
    - name: Saints Name Abbr
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'NO' %}
                    {{ competitor.team.abbreviation }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS NAME ID SENSOR########  
    - name: Saints Name ID
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'NO' %}
                    {{ competitor.team.id }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS DISPLAY LOGO SENSOR########  
    - name: Saints Display Logo
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'NO' %}
                    {{ competitor.team.logo }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS OPPONENT SCORE SENSOR########
    - name: Saints Opponent Score
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% if competition.status.type.state != 'pre' or competition.status.type.state == 'post' %}
                  {% for competitor in competition["competitors"] %}
                    {% if  competitor.team.abbreviation != 'NO' %}
                      {{ competitor.score }}
                    {% endif %}
                  {% endfor %}
                {% else %}
                  0
                {% endif %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS OPPONENT NAME SENSOR########  
    - name: Saints Opponent Name
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'NO' %}
                    {{ competitor.team.displayName }} ({{ competitor.records.0.summary }})
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS OPPONENT NAME ABBR SENSOR########  
    - name: Saints Opponent Name Abbr
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'NO' %}
                    {{ competitor.team.abbreviation }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS OPPONENT NAME ID SENSOR########  
    - name: Saints Opponent Name ID
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'NO' %}
                    {{ competitor.team.id }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS OPPONENT LOGO SENSOR########  
    - name: Saints Opponent Logo
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'NO' %}
                    {{ competitor.team.logo }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS GAME STATUS SENSORS########
    - name: Saints Game Status
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% if competition.status.type.state == 'pre' %}
                    Pregame
                {% elif competition.status.type.state == 'post' %}
                    Final
                {% else %}
                    {{ competition.status.type.shortDetail }}
                {% endif %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS GAME CLOCK SENSOR########
    - name: Saints Game Clock
      value_template: >
        {% for event in value_json["events"] %}
          {% if 'NO' in event.shortName %}
            {% for competition in event["competitions"] %}
                {{ competition.status.displayClock }}
            {% endfor %}
          {% endif %}
        {% endfor %}

########SAINTS GAME TIME SENSOR########
    - name: Saints Game Time
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.date }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS GAME CITY SENSOR########
    - name: Saints Game City
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.venue.address.city }}, {{ competition.venue.address.state }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS GAME VENUE SENSOR########  
    - name: Saints Game Venue
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.venue.fullName }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS GAME BROADCAST SENSOR########  
    - name: Saints Game Broadcast
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for broadcast in competition["broadcasts"] %}
                    {% for name in broadcast["names"] %}
                        {{ name }}
                    {% endfor %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS GAME LAST PLAY SENSOR########  
    - name: Saints Game Last Play
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.lastPlay.text }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS GAME DOWN DISTANCE SENSOR########  
    - name: Saints Game Down Distance
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.downDistanceText }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS GAME POSSESSION SENSOR########  
    - name: Saints Game Possession
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.possession }}
              {% endfor %}
            {% endif %}
          {% endfor %}
          
########SAINTS GAME ODDS SENSOR########  
    - name: Saints Game Odds
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                ({{ competition.odds.0.details }})
              {% endfor %}
            {% endif %}
          {% endfor %}

Here are the non-rest sensors that I used, mainly to populate my display card. (Based on @D34DC3N73R’s custom card above).

#####################
###### Sensors ######
#####################

sensor:
########SAINTS SCOREBOARD DISPLAY SENSORS########
- platform: template
  sensors:
    saints_sb_opponent_score:
      value_template: >
        {{ states('sensor.saints_opponent_score') }} 
      friendly_name_template: >
        {{ states('sensor.saints_opponent_name') }}
      entity_picture_template: >-
        {{ states('sensor.saints_opponent_logo') }}
    saints_sb_score:
      value_template: >
        {{ states('sensor.saints_score') }} 
      friendly_name_template: >
        {{ states('sensor.saints_display_name') }}
      entity_picture_template: >-
        {{ states('sensor.saints_display_logo') }}
    sb_possession_display_saints:
      value_template: >
        {% if states('sensor.saints_game_possession') == states('sensor.saints_name_id') %}
            🏈
        {% else %}
            
        {% endif %}
      friendly_name_template: >
        {{ states('sensor.saints_name_abbr') }}
    sb_possession_display_saints_opponent:
      value_template: >
        {% if states('sensor.saints_game_possession') == states('sensor.saints_opponent_name_id') %}
            🏈
        {% else %}
            
        {% endif %}
      friendly_name_template: >
        {{ states('sensor.saints_opponent_name_abbr') }}
    sb_venue_status:
      value_template: >
        {{ states('sensor.saints_game_status') }}
      friendly_name_template: >
        {{ states('sensor.saints_game_venue') }}
    sb_city_odds_down: 
      value_template: >
        {% if states('sensor.saints_game_status') == 'Pregame' %}
            {{ states('sensor.saints_game_odds') }}
        {% elif states('sensor.saints_game_status') == 'Final' %}
            
        {% else %}
            {{ states('sensor.saints_game_down_distance') }}
        {% endif %}
      friendly_name_template: >
        {{ states('sensor.saints_game_city') }}

Here’s what my card looks like in action (tested on tonight’s game):

And here’s what it looks like pre-game:

And post-game:

Relevant Lovelace card code: (requires button-card and lovelace-card-mod)

type: vertical-stack
cards:
  - type: markdown
    content: >
      # <center> {{ as_timestamp(states('sensor.saints_game_time')) |
      timestamp_custom("%A, %B %-d") }} 

      ## <center>{{ as_timestamp(states('sensor.saints_game_time')) |
      timestamp_custom(" %-I:%M %p") }} ({{
      states('sensor.saints_game_broadcast') }})
  - type: entities
    entities:
      - entity: sensor.saints_sb_score
        card_mod:
          style: |
            hui-generic-entity-row {
              font-size: 1.2em;
              font-weight: 350;
            }
      - entity: sensor.saints_sb_opponent_score
        card_mod:
          style: |
            hui-generic-entity-row {
              font-size: 1.2em;
              font-weight: 350;
            }
      - type: divider
      - entity: sensor.sb_venue_status
        card_mod:
          style:
            hui-generic-entity-row:
              $: |
                state-badge {display:none;}
                .text-content {
                  margin-left: 0px !important;
                }
      - entity: sensor.sb_city_odds_down
        card_mod:
          style:
            hui-generic-entity-row:
              .: |
                .text-content {
                  font-weight: 100;
                }
              $: |
                state-badge {display:none;}
                .text-content {
                  margin-left: 0px !important;
                  font-weight: 100;
                      }
  - type: conditional
    conditions:
      - entity: sensor.saints_game_status
        state_not: Pregame
      - entity: sensor.saints_game_status
        state_not: Final
    card:
      type: custom:button-card
      name: |
        [[[return `<div style='display:flex'>
           <marquee>
           ${states['sensor.saints_game_last_play'].state}
           </marquee>`]]]
      styles:
        card:
          - padding: 8px
          - font-size: 15px

***Edited to clean up some of the sensors/names and to change button-card (for scrolling live “last play” to conditional.

1 Like

I’m starting to think that the NFL dropping public access to its API is the best thing that ever happened for HA sports cards/automations.

1 Like

By the way, I know you talked about timeouts, but they appear to be organized only by home team and away team, so I didn’t get a chance to set that up because it requires defining home and away. (I haven’t done that since I am pulling venue instead).

**EDITED - I went ahead and took a stab at time outs by team below, but I can’t test it until a game is active. Also, and I can’t emphasize this enough, I have no clue what I’m doing.

########SAINTS TOL SENSOR########  
    - name: Saints TOL
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'NO' %}
                    {% if competition.status.type.state == 'in' %}
                      {% if  competitor.homeAway == 'home' %}
                        {{ competition.situation.homeTimeouts }}
                      {% else %}
                        {{ competition.situation.awayTimeouts }}
                      {% endif %}
                    {% else %}
                      -
                    {% endif %}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########SAINTS OPPONENT TOL SENSOR########  
    - name: Saints Opponent TOL
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'NO' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'NO' %}
                    {% if competition.status.type.state == 'in' %}
                      {% if  competitor.homeAway == 'home' %}
                        {{ competition.situation.homeTimeouts }}
                      {% else %}
                        {{ competition.situation.awayTimeouts }}
                      {% endif %}
                    {% else %}
                      -
                    {% endif %}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

I’d also suggest making the last_play button card conditional based on the game status sensor. Conditional Card - Home Assistant

Oh, nice. Yeah, I’ll definitely do that.

Thanks all for sharing your ESPN sensors and lovelace cards!

Question: For the REST sensors, I would like to add an automation to update frequently (every 5 seconds) during games. Will I need to include every sensor in the automation that falls under the single ESPN REST resource url, or will I be able to get away with just updating any single one of them and then the rest will ‘come along for the ride’ when the url gets pulled down for the one? I’m really hoping the REST sensor component is smart enough to update everything :crossed_fingers: :slight_smile:, otherwise that’s a LOT of sensors to add in, as I’m going to be tracking my wife’s, sister’s, and brother-in-law’s survivor pool teams each week.

I think one is sufficient.

1 Like

Can confirm, everything is updating wonderfully!

1 Like

How does your touchdown detector work?

Ok, I think I’ve settled on what I’m going with for my setup. I added timeouts, so here’s a screenshot of what my live score card currently looks like:

Rest Sensors:

Here’s the rest sensors that are pulling everything from ESPN. (This is set up for when I was watching Seattle, so you could just find and replace ‘SEA’ with whatever the short name of the team you want to track is.):

######################################
############NFL SENSORS############
######################################

########ESPN.COM JSON RETRIEVAL########

rest:
- resource: http://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard
  scan_interval: 86400
  sensor:
########NFL SCORE SENSOR########
    - name: NFL Score
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% if competition.status.type.state != 'pre' or competition.status.type.state == 'post' %}
                  {% for competitor in competition["competitors"] %}
                    {% if  competitor.team.abbreviation == 'SEA' %}
                      {{ competitor.score }}
                    {% endif %}
                  {% endfor %}
                {% else %}
                  0
                {% endif %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL TOL SENSOR########  
    - name: NFL TOL
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'SEA' %}
                    {% if competition.status.type.state == 'in' %}
                      {% if  competitor.homeAway == 'home' %}
                        {{ competition.situation.homeTimeouts }}
                      {% else %}
                        {{ competition.situation.awayTimeouts }}
                      {% endif %}
                    {% else %}
                      -
                    {% endif %}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL DISPLAY NAME SENSOR########  
    - name: NFL Display Name
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'SEA' %}
                    {{ competitor.team.displayName }} ({{ competitor.records.0.summary }})
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL NAME ABBR SENSOR########  
    - name: NFL Name Abbr
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'SEA' %}
                    {{ competitor.team.abbreviation }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL NAME ID SENSOR########  
    - name: NFL Name ID
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'SEA' %}
                    {{ competitor.team.id }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL DISPLAY LOGO SENSOR########  
    - name: NFL Display Logo
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'SEA' %}
                    {{ competitor.team.logo }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL HOME AWAY SENSOR########  
    - name: NFL Home Away
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'SEA' %}
                      {% if  competitor.homeAway == 'home' %}
                        Home
                      {% else %}
                        Away
                      {% endif %}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL OPPONENT SCORE SENSOR########
    - name: NFL Opponent Score
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% if competition.status.type.state != 'pre' or competition.status.type.state == 'post' %}
                  {% for competitor in competition["competitors"] %}
                    {% if  competitor.team.abbreviation != 'SEA' %}
                      {{ competitor.score }}
                    {% endif %}
                  {% endfor %}
                {% else %}
                  0
                {% endif %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL OPPONENT TOL SENSOR########  
    - name: NFL Opponent TOL
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'SEA' %}
                    {% if competition.status.type.state == 'in' %}
                      {% if  competitor.homeAway == 'home' %}
                        {{ competition.situation.homeTimeouts }}
                      {% else %}
                        {{ competition.situation.awayTimeouts }}
                      {% endif %}
                    {% else %}
                      -
                    {% endif %}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL OPPONENT NAME SENSOR########  
    - name: NFL Opponent Name
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'SEA' %}
                    {{ competitor.team.displayName }} ({{ competitor.records.0.summary }})
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL OPPONENT NAME ABBR SENSOR########  
    - name: NFL Opponent Name Abbr
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'SEA' %}
                    {{ competitor.team.abbreviation }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL OPPONENT NAME ID SENSOR########  
    - name: NFL Opponent Name ID
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'SEA' %}
                    {{ competitor.team.id }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL OPPONENT LOGO SENSOR########  
    - name: NFL Opponent Logo
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'SEA' %}
                    {{ competitor.team.logo }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME STATUS SENSORS########
    - name: NFL Game Status
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% if competition.status.type.state == 'pre' %}
                    Pregame
                {% elif competition.status.type.state == 'post' %}
                    Final
                {% else %}
                    {{ competition.status.type.shortDetail }}
                {% endif %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME CLOCK SENSOR########
    - name: NFL Game Clock
      value_template: >
        {% for event in value_json["events"] %}
          {% if 'SEA' in event.shortName %}
            {% for competition in event["competitions"] %}
                {{ competition.status.displayClock }}
            {% endfor %}
          {% endif %}
        {% endfor %}

########NFL GAME TIME SENSOR########
    - name: NFL Game Time
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.date }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME CITY SENSOR########
    - name: NFL Game City
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.venue.address.city }}, {{ competition.venue.address.state }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME VENUE SENSOR########  
    - name: NFL Game Venue
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.venue.fullName }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME BROADCAST SENSOR########  
    - name: NFL Game Broadcast
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for broadcast in competition["broadcasts"] %}
                    {% for name in broadcast["names"] %}
                        {{ name }}
                    {% endfor %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME LAST PLAY SENSOR########  
    - name: NFL Game Last Play
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.lastPlay.text }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME DOWN DISTANCE SENSOR########  
    - name: NFL Game Down Distance
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.downDistanceText }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME POSSESSION SENSOR########  
    - name: NFL Game Possession
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.possession }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME REDZONE SENSOR########  
    - name: NFL Game Redzone
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.isRedZone }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########NFL GAME ODDS SENSOR########  
    - name: NFL Game Odds
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'SEA' in event.shortName %}
              {% for competition in event["competitions"] %}
                ({{ competition.odds.0.details }})
              {% endfor %}
            {% endif %}
          {% endfor %}

Template Sensors:

And here are template sensors to process that data for my lovelace card:

#####################
###### Sensors ######
#####################

sensor:
########NFL SCOREBOARD DISPLAY SENSORS########
- platform: template
  sensors:
    nfl_sb_score:
      value_template: >
        {{ states('sensor.sb_nfl_tol') }} {{ states('sensor.nfl_score') }} 
      friendly_name_template: >
        {{ states('sensor.nfl_display_name') }} {{ states('sensor.sb_possession_display_nfl') }}
      entity_picture_template: >-
        {{ states('sensor.nfl_display_logo') }}
    nfl_sb_opponent_score:
      value_template: >
        {{ states('sensor.sb_nfl_opponent_tol') }} {{ states('sensor.nfl_opponent_score') }} 
      friendly_name_template: >
        {{ states('sensor.nfl_opponent_name') }} {{ states('sensor.sb_possession_display_nfl_opponent') }}
      entity_picture_template: >-
        {{ states('sensor.nfl_opponent_logo') }}
    sb_nfl_tol:
      value_template: >
        {% if states('sensor.nfl_tol') == '3' %}
            •••
        {% elif states('sensor.nfl_tol') == '2' %}
            ••
        {% elif states('sensor.nfl_tol') == '1' %}
            •
        {% else %}
            
        {% endif %}
      friendly_name_template: >
        {{ states('sensor.nfl_name_abbr') }} TO
    sb_nfl_opponent_tol:
      value_template: >
        {% if states('sensor.nfl_opponent_tol') == '3' %}
            •••
        {% elif states('sensor.nfl_opponent_tol') == '2' %}
            ••
        {% elif states('sensor.nfl_opponent_tol') == '1' %}
            •
        {% else %}
            
        {% endif %}
      friendly_name_template: >
        {{ states('sensor.nfl_name_abbr') }}
    sb_possession_display_nfl:
      value_template: >
        {% if states('sensor.nfl_game_possession') == states('sensor.nfl_name_id') %}
            🏈
        {% else %}
            
        {% endif %}
      friendly_name_template: >
        {{ states('sensor.nfl_name_abbr') }}
    sb_possession_display_nfl_opponent:
      value_template: >
        {% if states('sensor.nfl_game_possession') == states('sensor.nfl_opponent_name_id') %}
            🏈
        {% else %}
            
        {% endif %}
      friendly_name_template: >
        {{ states('sensor.nfl_opponent_name_abbr') }}
    sb_venue_status_nfl:
      value_template: >
        {{ states('sensor.nfl_game_status') }}
      friendly_name_template: >
        {{ states('sensor.nfl_game_venue') }}
    sb_city_odds_down_nfl: 
      value_template: >
        {% if states('sensor.nfl_game_status') == 'Pregame' %}
            {{ states('sensor.nfl_game_odds') }}
        {% elif states('sensor.nfl_game_status') == 'Final' %}
            
        {% else %}
            {{ states('sensor.nfl_game_down_distance') }}
        {% endif %}
      friendly_name_template: >
        {{ states('sensor.nfl_game_city') }}
    nfl_redzone:
      value_template: >
        {% if states('sensor.nfl_game_redzone') == 'True' %}
          {% if states('sensor.nfl_game_possession') == states('sensor.nfl_name_id') %}
            True
          {% else %}
            False    
          {% endif %}
        {% else %}
          False
        {% endif %}

Lovelace Card:

Here’s the updated code for the relevant parts of the card: (requires button-card and lovelace-card-mod )

type: vertical-stack
cards:
  - type: markdown
    content: >
      # <center> {{ as_timestamp(states('sensor.nfl_game_time')) |
      timestamp_custom("%A, %B %-d") }} 

      ## <center>{{ as_timestamp(states('sensor.nfl_game_time')) |
      timestamp_custom(" %-I:%M %p") }} ({{ states('sensor.nfl_game_broadcast')
      }})
  - type: entities
    entities:
      - entity: sensor.nfl_sb_score
        card_mod:
          style: |
            hui-generic-entity-row {
              font-size: 1.2em;
              font-weight: 350;
            }
      - entity: sensor.nfl_sb_opponent_score
        card_mod:
          style: |
            hui-generic-entity-row {
              font-size: 1.2em;
              font-weight: 350;
            }
      - type: divider
      - entity: sensor.sb_venue_status_nfl
        card_mod:
          style:
            hui-generic-entity-row:
              $: |
                state-badge {display:none;}
                .text-content {
                  margin-left: 0px !important;
                }
      - entity: sensor.sb_city_odds_down_nfl
        card_mod:
          style:
            hui-generic-entity-row:
              .: |
                .text-content {
                  font-weight: 100;
                }
              $: |
                state-badge {display:none;}
                .text-content {
                  margin-left: 0px !important;
                  font-weight: 100;
                      }
  - type: conditional
    conditions:
      - entity: sensor.nfl_game_status
        state_not: Pregame
      - entity: sensor.nfl_game_status
        state_not: Final
    card:
      type: custom:button-card
      name: |
        [[[return `<div style='display:flex'>
           <marquee>
           ${states['sensor.nfl_game_last_play'].state}
           </marquee>`]]]
      styles:
        card:
          - padding: 8px
          - font-size: 15px
  - type: horizontal-stack
    cards:
      - type: button
        tap_action:
          action: call-service
          service: homeassistant.update_entity
          service_data: {}
          target:
            entity_id:
              - sensor.nfl_game_status
        icon: mdi:football-helmet
        icon_height: 50px
        name: Update Sensors

*I also added a red zone sensor in there, but Saints played miserably today, so didn’t get to test it until a later game. I also didn’t put the red zone status in the card.

2 Likes

Finally we’re getting back to the original purpose of this whole thread, lol.

Touchdown Detector:

This is definitely not the only way to do this. (I noticed there is a ‘scoreValue’ variable in the ESPN api, which identifies how much the score has just changed.) The method I use below is basically a holdover from how we in this thread were doing it when using the NFL.com api.

First, I created a helper input boolean in the UI and called it “NFL Touchdown Detector”

Here’s the automation that turns on the boolean when a touchdown is scored by my team:

alias: NFL - Input Boolean Control - Turn On/Off Touchdown Tracker Input Boolean
description: Turns on input boolean for one minute if team score changes by 6+ points
trigger:
  - platform: state
    entity_id: sensor.nfl_score
condition:
  - condition: template
    value_template: |-
      {{ trigger.to_state is not none and trigger.from_state is not none and
               trigger.to_state.state|int > (trigger.from_state.state|int + 5)
            }}
action:
  - service: input_boolean.turn_on
    data: {}
    entity_id: input_boolean.nfl_touchdown_detector
  - delay: '00:03:00'
  - service: input_boolean.turn_off
    data: {}
    entity_id: input_boolean.nfl_touchdown_detector
mode: single

Win Detector:

I also have a “win detector,” which uses another helper input boolean called “NFL Win Detector”:

alias: >-
  NFL - Input Boolean Control - Turn On/Off Win Input Boolean
description: Turns on input boolean for five minutes when team wins
trigger:
  - platform: state
    entity_id: sensor.nfl_game_status
    to: Final
condition:
  - condition: template
    value_template: >-
      {{ states("sensor.nfl_score")|int >
      states("sensor.nfl_opponent_score")|int }}
action:
  - service: input_boolean.turn_on
    data: {}
    entity_id: input_boolean.nfl_win_detector
  - delay: '00:05:00'
  - service: input_boolean.turn_off
    data: {}
    entity_id: input_boolean.nfl_win_detector
mode: single

I use the on/off state of these input boolean helpers to trigger any automations.

Input Boolean Reset Automation:

**It doesn’t hurt to also go ahead and make a built-in reset function to make sure your input booleans are off when they should be. This one below just turns them all off when HA starts.

alias: NFL - Input Boolean Control - All NFL Booleans Reset on HA Restart
description: ''
trigger:
  - platform: homeassistant
    event: start
condition: []
action:
  - service: input_boolean.turn_off
    data: {}
    entity_id: input_boolean.nfl_touchdown_detector
  - service: input_boolean.turn_off
    data: {}
    entity_id: input_boolean.nfl_win_detector
  - delay: '00:00:30'
  - service: homeassistant.update_entity
    data: {}
    entity_id: sensor.nfl_game_status
mode: single

Update Automation for During Game

Just a reminder, in order for these to actually be live, you need to be forcing the rest sensors to update. I have mine set to update every 3 seconds during the game, which is determined by the status of the google calendar @D34DC3N73R and @djbrooks022 mentioned above. You don’t want the rest sensors taking up resources by updating all of the time while games aren’t active.

Here’s how I have set up my automation:

alias: Saints - NFL Scores - Update JSON/XML Every 3 Seconds During Game Only
description: ''
trigger:
  - platform: state
    entity_id: sensor.nfl_game_status
    from: Pregame
  - platform: state
    entity_id: calendar.new_orleans_saints
    to: 'on'
    from: 'off'
  - platform: template
    value_template: >-
      {{ (state_attr('calendar.new_orleans_saints', 'start_time') | as_timestamp
      - now() | as_timestamp) < 300 and
      (state_attr('calendar.new_orleans_saints', 'start_time') | as_timestamp -
      now() | as_timestamp) > 0 }}
condition:
  - condition: or
    conditions:
      - condition: state
        entity_id: calendar.new_orleans_saints
        state: 'on'
      - condition: not
        conditions:
          - condition: state
            entity_id: sensor.nfl_game_status
            state: Final
      - condition: template
        value_template: >-
          {{ (state_attr('calendar.new_orleans_saints', 'start_time') |
          as_timestamp - now() | as_timestamp) < 300 and
          (state_attr('calendar.new_orleans_saints', 'start_time') |
          as_timestamp - now() | as_timestamp) > 0 }}
action:
  - repeat:
      until:
        - condition: or
          conditions:
            - condition: state
              entity_id: sensor.nfl_game_status
              state: Final
            - condition: state
              entity_id: calendar.new_orleans_saints
              state: 'off'
              for: '01:00:00'
      sequence:
        - service: homeassistant.update_entity
          data: {}
          target:
            entity_id:
              - sensor.nfl_game_status
              - sensor.nfl_score
              - sensor.nfl_opponent_score
        - delay:
            hours: 0
            minutes: 0
            seconds: 3
            milliseconds: 0
mode: single

Hope this helps!

2 Likes

It does! Thank you.

I just can’t get that damn possession football to show up on the Lovelace card. I was trying to find the issues while being pissed off at the Chiefs defense and drinking beer. So instead of breaking my entire HA setup, I waited until today.

I added a win probability conditional card to my setup. Still trying to work out some of the kinks, but I have it set up to use team colors, and switching to alternate team colors when my dark mode is active.

light mode / team colors

dark mode / alt team colors

If anyone is interested I can post the sensors / lovelace card.

2 Likes

Good work! I would love to see the code for that.

I still have to work out some logic for when the value isn’t available during game time, but this is what I have so far.

The card uses mini graph card. If you want team colors it also requires lovelace card templater, which requires lovelace card tools.

rest sensors

########PACKERS HOME/AWAY SENSOR########
    - name: Packers Home/Away
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'GB' %}
                    {% if competitor.homeAway == 'home' %}
                      Home
                    {% else %}
                      Away
                    {% endif %}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

########PACKERS WIN PROBABILITY SENSOR########
    - name: Probability Home
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.lastPlay.probability.homeWinPercentage }}
              {% endfor %}
            {% endif %}
          {% endfor %}

    - name: Probability Away
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.situation.lastPlay.probability.awayWinPercentage }}
              {% endfor %}
            {% endif %}
          {% endfor %}

########PACKERS TEAM COLORS########
    - name: Packers Color
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'GB' %}
                    #{{ competitor.team.color }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

    - name: Packers Alt Color
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation == 'GB' %}
                    #{{ competitor.team.alternateColor }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

    - name: Packers Opponent Color
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'GB' %}
                    #{{ competitor.team.color }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

    - name: Packers Opponent Alt Color
      value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for competitor in competition["competitors"] %}
                  {% if  competitor.team.abbreviation != 'GB' %}
                    #{{ competitor.team.alternateColor }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

template sensors

sensor:
########PACKERS SCOREBOARD DISPLAY SENSORS########
  - platform: template
    sensors:
      [...]
      packers_win_probability:
        value_template: >
          {% if states('sensor.packers_home_away') == 'Home' %}
            {{ states('sensor.probability_home') | float * 100 }}
          {% else %}
            {{ states('sensor.probability_away') | float * 100 }}
          {% endif %}
        friendly_name_template: >
          {{ states('sensor.packers_display_name') }}
      opponent_win_probability:
        value_template: >
          {% if states('sensor.packers_home_away') == 'Home' %}
            {{ states('sensor.probability_away') | float * 100 }}
          {% else %}
            {{ states('sensor.probability_home') | float * 100 }}
          {% endif %}
        friendly_name_template: >
          {{ states('sensor.packers_opponent_name') }}

lovelace card

- type: conditional
  conditions:
    - entity: sensor.packers_game_status
      state_not: Pregame
    - entity: sensor.packers_game_status
      state_not: Final
  card:
    type: 'custom:card-templater'
    card:
      type: 'custom:mini-graph-card'
      name: WIN PROBABILITY
      icon: 'mdi:percent'
      hours_to_show: 3
      points_per_hour: 120
      aggregate_func: last
      animate: true
      entities:
        - entity: sensor.packers_win_probability
          color_template: >
            {% if states('input_boolean.dark_mode') == 'on' %}
             {{ states('sensor.packers_alt_color') }}
            {% else %}
             {{ states('sensor.packers_color') }}
            {% endif %}
        - entity: sensor.opponent_win_probability
          color_template: >
            {% if states('input_boolean.dark_mode') == 'on' %}
             {{ states('sensor.packers_opponent_alt_color') }}
            {% else %}
             {{ states('sensor.packers_opponent_color') }}
            {% endif %}
1 Like

oh and btw, if you want your last play button card to display the full card width, even if the info is shorter, you can add this:

card_mod:
  style: 
    div#container: |
      div#name {
        width: 100%;
      }
2 Likes

Awesome, thanks. I definitely have no idea what I’m doing!

LOL, I think you and D34DC3N73R have both done awesome work! My wife was very excited by the possession and timeout indicators last night during the Packers/Lions game.

I noticed a lot of log errors (many thousands) concerning the ‘situation’ key being missing in the json while I was setting up the newest sensors yesterday. As a temporary workaround I disabled logging for that component for anything but a fatal error in my configuration.yaml:

logger:
  #default: info
  default: warn
  logs:
    homeassistant.helpers.template: fatal

But long term I’d like to keep template error logging at its default level and try to figure out how to prevent these templates from causing the error. I haven’t looked into it much yet, but. I’m pretty sure the situation key isn’t available except during live games, so we’d need to catch that condition and handle creating those state differently.

Yes, those are only available during the game. With a scan interval of 86400 (24 hours), I get 5 of these errors per day with all the sensors.

 ERROR (MainThread) [homeassistant.helpers.template] Template variable error: 'dict object' has no attribute 'situation' when rendering '{% for event in value_json["events"] %}

I guess you’d get many more if you’re tracking more than one team, and one of the games was live.

Off the top of my head, it would be possible to include each of the problem sensors in a txt file for each team and inject them into the configuration.yaml at game time. But, that would require restarting HA for them to take effect.

Another option (which I haven’t tested at all) would be to set up separate resource blocks in rest: for each team. I think that would stop HA from trying to pull stats so frequently for games that are not happening. But, you’d still get 5 errors per day per team.