NFL game sensor (scores, possession, etc)

You should definitely do a write-up on how you did that scoreboard. That is phenomenal!

This is amazing work! im a bit lost looking for the bears… i see you use ss.xml but im not sniffing anything related to the Chicago Bears. - Tom mentioned he set up for buffalo… how did you find buffalo?

@jamesking210 CHI would be for the Bears.

Is anyone else having issues with their sensors updating in home assistant? It looks like the ss.xml link is still reporting Week 17’s scores from last year. When I try ss.json the sensors return an unavailable state. Thoughts?

Apparently, the NFL is no longer updating the xml link. Last year it would update on Wednesday for the week’s games. From what I could find, you now have to create an account (and pay) to get live data. Yeah, it sucks.

I’m not good enough with json / rest sensors to figure this out, but would it be possible to use the ESPN API? http://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard

With some help, I was able to get the ESPN API working. I have this sensor for GB score:

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

My “score” automation triggers when the sensor updates with a condition that the score is not 0. I also have a google calendar automation that updates the scan interval from 86400 to 3 when the Packers are playing, and changes it back to 86400 when the game is over.

edit: damn… really wish I would have had more opportunities to test it out this week. :cry:

3 Likes

I feel guilty as a Saints fan telling you this, but I can assure you it worked really well!

Seriously, thank you.

Do you mind sharing how you set up your scan interval automation?

Sure, I can wipe away the tears and stop drinking for a few moments.

First, my score sensor is the only sensor with scan_interval values of 86400 / 3. So, I made two shell_commands. (game_on & game_over).

  game_on: sed -i 's/scan_interval:\ 86400/scan_interval:\ 3/' /config/configuration.yaml
  game_over: sed -i 's/scan_interval:\ 3/scan_interval:\ 86400/' /config/configuration.yaml

I’m also using docker, so you might have to edit the file location of configuration.yaml if you’re not.

Next, and add the calendar for your team in google calendar.
image

image

image

Back in HA, set up the google calendar integration. You should now have /config/google_calendars.yaml and you should have an entry similar to this in that file:

- cal_id: nfl_9_%47reen+%42ay+%50ackers#[email protected]
  entities:
  - device_id: green_bay_packers
    ignore_availability: true
    name: Green Bay Packers
    track: false

Change track: false to track: true and restart HA. You should now see the game dates & times in your HA calendar.

So, the automations go something like this:

- alias: Game On
  trigger:
  - platform: state
    entity_id: calendar.green_bay_packers
    to: 'on'
    from: 'off'
  action:
  - service: shell_command.game_on
  - service: homeassistant.restart
  mode: single
- alias: Game Over
  trigger:
  - platform: state
    entity_id: calendar.green_bay_packers
    from: 'on'
    to: 'off'
  action:
  - service: shell_command.game_over
  - service: homeassistant.restart
  mode: single

And that should do it. At game time, the scan_interval will be updated to 3 or whatever you want, and HA will automatically restart to apply the changes. When the game is over, it’ll go back to 86400 (24 hours) and again, automatically restart to apply the changes.

Good luck with the rest of the season. I hope we can see you guys in the playoffs if we get our act together.

1 Like

Thank you so much again to @D34DC3N73R for getting this going again. I was totally lost until you posted your sensor. Here are all the sensors I have made for the Saints using ESPN’s api:

***Edited my config below to follow @Jefe’s suggestion and make all sensors one API call. I told y’all I didn’t know what I was doing!

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

########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 }}
                  {% 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 }}
                  {% 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.displayClock }}, Q{{ competition.status.period }}
                {% endif %}
              {% 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 INFO DISPLAY SENSOR########
sensor:
- platform: template
  sensors:
    saints_game_status_display:
        value_template: >
            {% if state_attr('calendar.new_orleans_saints', 'start_time') | as_timestamp - now() | as_timestamp < 259200 and state_attr('calendar.new_orleans_saints', 'start_time') | as_timestamp - now() | as_timestamp > -10800 %}
                    {{ states('sensor.saints_game_venue') }} - {{ states('sensor.saints_game_city') }} ({{ states('sensor.saints_game_broadcast') }})
            {% else %}
                    {{ states('sensor.saints_game_venue') }} - {{ states('sensor.saints_game_city') }}
            {% endif %}

Anyone should be able to change the sensors to their team by just replacing “NO” with whatever the abbreviation for their team is at: http://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard

I am using all of those sensors to populate a custom Lovelace card:

I ended up making an automation in the UI that forces a refresh of the score/time sensors every 3 seconds during the game. Although I am using the google calendar setup like @D34DC3N73R does, I didn’t end up using their script because I didn’t want to have to restart HA. Here is that automation:

alias: Saints - NFL Scores - Update JSON/XML Every 3 Seconds During Game Only
description: ''
trigger:
  - platform: state
    entity_id: sensor.saints_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.saints_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.saints_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.saints_game_status
              - sensor.saints_score
              - sensor.saints_opponent_score
        - delay:
            hours: 0
            minutes: 0
            seconds: 3
            milliseconds: 0
mode: single

Everything seems to be working well. (I REALLY don’t know what I’m doing.) Hopefully this is helpful for someone. I can confirm it worked in real time for yesterday’s game. (My touchdown celebration automations triggered every time before the extra point was kicked.)

1 Like

using what i read here from ESPN’s i got what i needed with scoring… on the API i see isRedZone = “true” or “false” - is there a way to make a new sensor with just that true/false info from across the league? i plan on making an automation of when true, my lights will go red

Nice another Saints fan WHO DAT!!!

1 Like

I just looked and it seems like the red zone sensor category is only visible while a game is active. I’m also not quite understanding, are you wanting a red zone sensor for just your team’s game, or are you trying to have your lights turn red when any team in any active game is in the red zone?

Either way, you’ll probably have to wait until Sunday to try to set up or test any of it, since right now the API is not showing any red zone categories.

I took a little bit different of a route to achieve this using Google Calendar. Rather than using the NFL api, I can create cards for any sport.

And if you want to be notified before a game starts, you can setup notifications X minutes before: https://smarthomepursuits.com/how-to-setup-google-calendar-event-notifications/

Looks great. Are the scores in the google calendar entry description updated in real time? That’s where you’re pulling those scores from, right?

Yep, they show up in parenthesis for both the calendar event as well as the Lovelace card:

1 Like

Thanks to @D34DC3N73R and @davearneson for your contributions!

I converted the sensors to only call the API once per update (using the Steelers). This should be more efficient. I have the scan_interval set to 300 while testing. Code should be placed in the config file (or a package).

rest:
  - resource: http://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard
    scan_interval: 300
    sensor:
      - name: Steelers Score
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' 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 == 'PIT' %}
                        {{ competitor.score }}
                      {% endif %}
                    {% endfor %}
                  {% else %}
                    0
                  {% endif %}
                {% endfor %}
              {% endif %}
            {% endfor %}

      - name: Steelers Display Name
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' in event.shortName %}
                {% for competition in event["competitions"] %}
                  {% for competitor in competition["competitors"] %}
                    {% if  competitor.team.abbreviation == 'PIT' %}
                      {{ competitor.team.displayName }}
                    {% endif %}
                  {% endfor %}
                {% endfor %}
              {% endif %}
            {% endfor %}

      - name: Steelers Opponent Score
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' 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 != 'PIT' %}
                        {{ competitor.score }}
                      {% endif %}
                    {% endfor %}
                  {% else %}
                    0
                  {% endif %}
                {% endfor %}
              {% endif %}
            {% endfor %}

      - name: Steelers Opponent Name
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' in event.shortName %}
                {% for competition in event["competitions"] %}
                  {% for competitor in competition["competitors"] %}
                    {% if  competitor.team.abbreviation != 'PIT' %}
                      {{ competitor.team.displayName }}
                    {% endif %}
                  {% endfor %}
                {% endfor %}
              {% endif %}
            {% endfor %}

      - name: Steelers Game Status
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' 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.displayClock }}, Q{{ competition.status.period }}
                  {% endif %}
                {% endfor %}
              {% endif %}
            {% endfor %}

      - name: Steelers Game Time
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' in event.shortName %}
                {% for competition in event["competitions"] %}
                  {{ competition.date }}
                {% endfor %}
              {% endif %}
            {% endfor %}

      - name: Steelers Game City
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' in event.shortName %}
                {% for competition in event["competitions"] %}
                  {{ competition.venue.address.city }}, {{ competition.venue.address.state }}
                {% endfor %}
              {% endif %}
            {% endfor %}

      - name: Steelers Game Venue
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' in event.shortName %}
                {% for competition in event["competitions"] %}
                  {{ competition.venue.fullName }}
                {% endfor %}
              {% endif %}
            {% endfor %}

      - name: Steelers Game Broadcast
        value_template: >
            {% for event in value_json["events"] %}
              {% if 'PIT' in event.shortName %}
                {% for competition in event["competitions"] %}
                  {% for broadcast in competition["broadcasts"] %}
                      {% for name in broadcast["names"] %}
                          {{ name }}
                      {% endfor %}
                  {% endfor %}
                {% endfor %}
              {% endif %}
            {% endfor %}

2 Likes

Yeah, that makes a lot more sense. I edited my config (and my previous post) to follow your suggestion.

Your scorecard inspired me to create something similar.

image

I use card-mod a lot but figured I’d post the code for anyone wanting something similar.

sensors + template sensors

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

      - name: Packers Display Name
        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.shortDisplayName }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

      - name: Packers Display Logo
        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.logo }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

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

      - name: Packers Opponent Name
        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.shortDisplayName }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

      - name: Packers Opponent Logo
        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.logo }}
                  {% endif %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

      - name: Packers Game Status
        value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% if competition.status.type.state == 'pre' %}
                    Pregame
                {% elif competition.status.type.state == 'post' %}
                    Final
                {% else %}
                    Q{{ competition.status.period }}
                {% endif %}
              {% endfor %}
            {% endif %}
          {% endfor %}

      - name: Packers Game Clock
        value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                  {{ competition.status.displayClock }}
              {% endfor %}
            {% endif %}
          {% endfor %}

      - name: Packers Game Time
        value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.date }}
              {% endfor %}
            {% endif %}
          {% endfor %}

      - name: Packers Game City
        value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.venue.address.city }}, {{ competition.venue.address.state }}
              {% endfor %}
            {% endif %}
          {% endfor %}

      - name: Packers Game Venue
        value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {{ competition.venue.fullName }}
              {% endfor %}
            {% endif %}
          {% endfor %}

      - name: Packers Game Broadcast
        value_template: >
          {% for event in value_json["events"] %}
            {% if 'GB' in event.shortName %}
              {% for competition in event["competitions"] %}
                {% for broadcast in competition["broadcasts"] %}
                    {% for name in broadcast["names"] %}
                        {{ name }}
                    {% endfor %}
                {% endfor %}
              {% endfor %}
            {% endif %}
          {% endfor %}

sensor:
  - platform: template
    sensors:
      packers_sb_score:
        value_template: >
          {{ states('sensor.packers_score') }} 
        friendly_name_template: >
          {{ states('sensor.packers_display_name') }}
        entity_picture_template: >-
          {{ states('sensor.packers_display_logo') }}
      packers_sb_opponent_score:
        value_template: >
          {{ states('sensor.packers_opponent_score') }} 
        friendly_name_template: >
          {{ states('sensor.packers_opponent_name') }}
        entity_picture_template: >-
          {{ states('sensor.packers_opponent_logo') }}
      sb_state_clock:
        value_template: >
          {{ states('sensor.packers_game_clock') }}
        friendly_name_template: >
          {{ states('sensor.packers_game_status') }}
      sb_venue_date:
        value_template: >
          {{ as_timestamp(strptime(as_datetime(states.sensor.packers_game_time.state), '%Y-%m-%d %H:%M:%S%z')) | timestamp_custom("%A @ %-I:%M %p") }}
        friendly_name_template: >
          {{ states('sensor.packers_game_venue') }}
      sb_city_broadcast: 
        value_template: >
          {{ states('sensor.packers_game_broadcast') }}
        friendly_name_template: >
          {{ states('sensor.packers_game_city') }}

and the lovelace card

      - type: entities
        title: Scoreboard
        entities:
          - entity: sensor.packers_sb_score
            style: |
              hui-generic-entity-row {
                font-size: 1.8em;
                font-weight: 500;
                text-transform: uppercase;
              }
          - entity: sensor.packers_sb_opponent_score
            style: |
              hui-generic-entity-row {
                font-size: 1.8em;
                font-weight: 500;
                text-transform: uppercase;
              }
          - entity: sensor.sb_state_clock
            style:
              hui-generic-entity-row:
                .: |
                  .text-content {
                    font-size: 1.2em;
                  }
                $: |
                  state-badge {display:none;}
                  .text-content {
                    margin-left: 0px !important;
                    font-size: 1.2em;
                  }
          - type: divider
          - entity: sensor.sb_venue_date
            style:
              hui-generic-entity-row:
                $: |
                  state-badge {display:none;}
                  .text-content {
                    margin-left: 0px !important;
                  }
          - entity: sensor.sb_city_broadcast
            style:
              hui-generic-entity-row:
                .: |
                  .text-content {
                    font-weight: 100;
                  }
                $: |
                  state-badge {display:none;}
                  .text-content {
                    margin-left: 0px !important;
                    font-weight: 100;
                  }
3 Likes

Nice, that looks really great! I’m planning on keeping an eye on the game tonight so I can figure out what other information they have active during the game. (Possession, down and distance, last play, etc.)

I’ll post if I find anything interesting.