From cc759bca59891a524dd716af151abbb104b6f92d Mon Sep 17 00:00:00 2001 From: Alex Frantz Date: Wed, 17 Jun 2026 23:27:36 -0400 Subject: [PATCH] more splitting --- modes/score_mode.py | 221 +++++++++++++++++++++++++++++++++++++++ scoreboard.py | 245 ++++---------------------------------------- 2 files changed, 239 insertions(+), 227 deletions(-) create mode 100644 modes/score_mode.py diff --git a/modes/score_mode.py b/modes/score_mode.py new file mode 100644 index 0000000..2d5ab44 --- /dev/null +++ b/modes/score_mode.py @@ -0,0 +1,221 @@ +import utils.image_utils as image_utils +from rgbmatrix import graphics +from scoreboard import Colors, data +from time import time, sleep + +def get_scores(sport, league): + url = f"https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/scoreboard" + try: + resp = requests.get(url, timeout=5) + resp.raise_for_status() + games = [] + for event in resp.json().get("events", []): + comp = event["competitions"][0] + teams = comp["competitors"] + home = next(t for t in teams if t["homeAway"] == "home") + away = next(t for t in teams if t["homeAway"] == "away") + status = event["status"]["type"]["shortDetail"] + games.append( + { + "league": league, + "away": away["team"]["abbreviation"].upper(), + "away_score": away["score"], + "home": home["team"]["abbreviation"].upper(), + "home_score": home["score"], + "status": status, + "id": event["id"], + } + ) + return games + except Exception as e: + print(f"Fetch error ({league}): {e}") + return [] + +def get_all_scores(): + print('fetching game scores from espn') + games = [] + games += get_scores("hockey", "nhl") + games += get_scores("football", "nfl") + games += get_scores("basketball", "nba") + games += get_scores("baseball", "mlb") + return games + +def draw_all_games(canvas, games, start_index): + for i in range(4): + game_index = (start_index + i) % len(games) + game = games[game_index] + offset = i * 64 + + league = game["league"] + away_logo = image_utils.load_logo(league, game["away"]) + home_logo = image_utils.load_logo(league, game["home"]) + + image_utils.draw_logo(canvas, away_logo, offset + 0, 0) + image_utils.draw_logo(canvas, home_logo, offset + 0, 16) + + graphics.DrawText( + canvas, + data['fonts']['font_small'], + offset + 18, + 11, + graphics.Color(*Colors.RED.value), + game["away"], + ) + graphics.DrawText( + canvas, + data['fonts']['font_small'], + offset + 18, + 27, + graphics.Color(*Colors.WHITE.value), + game["home"], + ) + + graphics.DrawText( + canvas, + data['fonts']['font'], + offset + 40, + 13, + graphics.Color(*Colors.WHITE.value), + str(game["away_score"]), + ) + graphics.DrawText( + canvas, + data['fonts']['font'], + offset + 40, + 29, + graphics.Color(*Colors.WHITE.value), + str(game["home_score"]), + ) + + if i < 3: + for row in range(32): + canvas.SetPixel(offset + 63, row, 40, 40, 40) + +def draw_single_game(canvas, game): + league = game["league"] + home_logo = image_utils.load_logo(league, game["home"]) + away_logo = image_utils.load_logo(league, game["away"]) + + image_utils.draw_logo(canvas, away_logo, 0, 0) + image_utils.draw_logo(canvas, home_logo, 0, 16) + + graphics.DrawText( + canvas, + data['fonts']['font_small'], + 18, + 11, + graphics.Color(*Colors.WHITE.value), + game["away"], + ) + graphics.DrawText( + canvas, + data['fonts']['font_small'], + 18, + 27, + graphics.Color(*Colors.WHITE.value), + game["home"], + ) + + graphics.DrawText( + canvas, + data['fonts']['font'], + 40, + 13, + graphics.Color(*Colors.WHITE.value), + str(game["away_score"]), + ) + graphics.DrawText( + canvas, + data['fonts']['font'], + 40, + 29, + graphics.Color(*Colors.WHITE.value), + str(game["home_score"]), + ) + graphics.DrawText( + canvas, + data['fonts']['font'], + 55, + 22, + graphics.Color(*Colors.WHITE.value), + str(game["status"]) + ) + +def run_board(canvas, matrix): + + # preferred games + current_preferred_game = 0 + preferred_games = [] + preferred_teams = [ + ("BUF", "nfl"), + ("BUF", "nhl"), + ("TOR", "mlb"), + ("LAL", "nba"), + ] + + # data + games = [] + last_fetch = 0 + current_page = 0 + page_display_time = 8 + last_switch = time() + show_preferred = True + + while true: + now = time() + + if now - last_fetch > 30 or len(games) <= 0: + games = get_all_scores() + last_fetch = now + + if games: + canvas.Clear() + # clear bad preferred games out + if len(preferred_games) > 0: + for preferred_game in preferred_games: + shown_game = [g for g in games if preferred_game == g['id']] + if len(shown_game) <= 0 or "Final" in shown_game[0]['status']: + preferred_games.remove(preferred_game) + + # get new preferred games + for game in games: + if (game['away'], game['league']) in preferred_teams or (game['home'], game['league']) in preferred_teams: + if 'Final' not in game['status'] and game['id'] not in preferred_games: + preferred_games.append(game['id']) + + print(preferred_games) + if now - last_switch > page_display_time: + last_switch = now + + if show_preferred and len(preferred_games) > 0: + single_preferred_game = next( + (g for g in games if g['id'] == preferred_games[current_preferred_game]), None + ) + if single_preferred_game: + print(f'Showing preferred game {single_preferred_game["home"]} vs {single_preferred_game["away"]}') + draw_single_game(canvas, single_preferred_game) + + current_preferred_game += 1 + if current_preferred_game >= len(preferred_games): + show_preferred = False + current_preferred_game = 0 + current_page = 0 + else: + print(f'Showing all games page {current_page} / {len(games)}') + draw_all_games(canvas, games, current_page) + current_page += 4 + if current_page >= len(games): + current_page = 0 + if len(preferred_games) > 0: + show_preferred = True + else: + canvas.Clear() + + print('No games available') + + # no games available, just draw placeholder + graphics.DrawText(canvas, data["fonts"]["font"], 10, 22, graphics.Color(*Colors.RED.value), "No games today") + + canvas = matrix.SwapOnVSync(canvas) + sleep(10) + \ No newline at end of file diff --git a/scoreboard.py b/scoreboard.py index ec39ad1..d7d1bf5 100644 --- a/scoreboard.py +++ b/scoreboard.py @@ -1,13 +1,13 @@ -import requests import os import utils.govee as govee -import utils.image_utils as image_utils from enum import Enum -from time import sleep, time from pathlib import Path from rgbmatrix import RGBMatrix, RGBMatrixOptions, graphics from dotenv import load_dotenv +# modes +import modes.score_mode as score_mode + # --- Load environment vars --- load_dotenv() @@ -16,6 +16,20 @@ SCRIPT_DIR = Path(__file__).parent.resolve() ASSET_DIR = os.path.join(SCRIPT_DIR, "assets") LOGO_DIR = os.path.join(ASSET_DIR, "logos") +# --- importable data dict --- +data = { + "paths": { + "SCRIPT_DIR": SCRIPT_DIR, + "ASSET_DIR": ASSET_DIR, + "LOGO_DIR": LOGO_DIR + }, + "fonts": { + "font": graphics.Font().LoadFont(os.path.join(ASSET_DIR, "fonts/7x13.bdf")), + "font_small": graphics.Font().LoadFont(os.path.join(ASSET_DIR, "fonts/5x7.bdf")), + "font_big": graphics.Font().LoadFont(os.path.join(ASSET_DIR, "fonts/9x18.bdf")) + } +} + # --- Matrix config --- options = RGBMatrixOptions() options.rows = 32 @@ -30,17 +44,6 @@ options.brightness = 80 matrix = RGBMatrix(options=options) canvas = matrix.CreateFrameCanvas() -# --- Font initialization --- -font = graphics.Font() -font_small = graphics.Font() -font_big = graphics.Font() -font.LoadFont(os.path.join(ASSET_DIR, "fonts/7x13.bdf")) -font_small.LoadFont(os.path.join(ASSET_DIR, "fonts/5x7.bdf")) -font_big.LoadFont(os.path.join(ASSET_DIR, "fonts/9x18.bdf")) - -# --- Logo cache --- -logo_cache = {} - # --- Pre-built colors --- class Colors(Enum): WHITE = (255, 255, 255) @@ -59,223 +62,11 @@ if os.environ['GOVEE_API_KEY']: # pygame.mixer.music.load(os.path.join(ASSET_DIR, filename)) # pygame.mixer.music.play() -# --- Fetch scores --- -def get_scores(sport, league): - url = f"https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/scoreboard" - try: - resp = requests.get(url, timeout=5) - resp.raise_for_status() - games = [] - for event in resp.json().get("events", []): - comp = event["competitions"][0] - teams = comp["competitors"] - home = next(t for t in teams if t["homeAway"] == "home") - away = next(t for t in teams if t["homeAway"] == "away") - status = event["status"]["type"]["shortDetail"] - games.append( - { - "league": league, - "away": away["team"]["abbreviation"].upper(), - "away_score": away["score"], - "home": home["team"]["abbreviation"].upper(), - "home_score": home["score"], - "status": status, - "id": event["id"], - } - ) - return games - except Exception as e: - print(f"Fetch error ({league}): {e}") - return [] - -def get_all_scores(): - print('fetching game scores from espn') - games = [] - games += get_scores("hockey", "nhl") - games += get_scores("football", "nfl") - games += get_scores("basketball", "nba") - games += get_scores("baseball", "mlb") - return games - -# --- Game drawing --- -def draw_all_games(canvas, games, start_index): - for i in range(4): - game_index = (start_index + i) % len(games) - game = games[game_index] - offset = i * 64 - - league = game["league"] - away_logo = image_utils.load_logo(league, game["away"]) - home_logo = image_utils.load_logo(league, game["home"]) - - image_utils.draw_logo(canvas, away_logo, offset + 0, 0) - image_utils.draw_logo(canvas, home_logo, offset + 0, 16) - - graphics.DrawText( - canvas, - font_small, - offset + 18, - 11, - graphics.Color(*Colors.RED.value), - game["away"], - ) - graphics.DrawText( - canvas, - font_small, - offset + 18, - 27, - graphics.Color(*Colors.WHITE.value), - game["home"], - ) - - graphics.DrawText( - canvas, - font, - offset + 40, - 13, - graphics.Color(*Colors.WHITE.value), - str(game["away_score"]), - ) - graphics.DrawText( - canvas, - font, - offset + 40, - 29, - graphics.Color(*Colors.WHITE.value), - str(game["home_score"]), - ) - - if i < 3: - for row in range(32): - canvas.SetPixel(offset + 63, row, 40, 40, 40) - -def draw_single_game(canvas, game): - league = game["league"] - home_logo = image_utils.load_logo(league, game["home"]) - away_logo = image_utils.load_logo(league, game["away"]) - - image_utils.draw_logo(canvas, away_logo, 0, 0) - image_utils.draw_logo(canvas, home_logo, 0, 16) - - graphics.DrawText( - canvas, - font_small, - 18, - 11, - graphics.Color(*Colors.WHITE.value), - game["away"], - ) - graphics.DrawText( - canvas, - font_small, - 18, - 27, - graphics.Color(*Colors.WHITE.value), - game["home"], - ) - - graphics.DrawText( - canvas, - font, - 40, - 13, - graphics.Color(*Colors.WHITE.value), - str(game["away_score"]), - ) - graphics.DrawText( - canvas, - font, - 40, - 29, - graphics.Color(*Colors.WHITE.value), - str(game["home_score"]), - ) - graphics.DrawText( - canvas, - font, - 55, - 22, - graphics.Color(*Colors.WHITE.value), - str(game["status"]) - ) - # --- Main loop --- def run(): global canvas - games = [] - last_fetch = 0 - current_page = 0 - page_display_time = 8 - last_switch = time() - show_preferred = True - # preferred games - current_preferred_game = 0 - preferred_games = [] - preferred_teams = [ - ("BUF", "nfl"), - ("BUF", "nhl"), - ("TOR", "mlb"), - ("LAL", "nba"), - ] - - while True: - now = time() - - if now - last_fetch > 30 or len(games) <= 0: - games = get_all_scores() - last_fetch = now - - if games: - canvas.Clear() - # clear bad preferred games out - if len(preferred_games) > 0: - for preferred_game in preferred_games: - shown_game = [g for g in games if preferred_game == g['id']] - if len(shown_game) <= 0 or "Final" in shown_game[0]['status']: - preferred_games.remove(preferred_game) - - # get new preferred games - for game in games: - if (game['away'], game['league']) in preferred_teams or (game['home'], game['league']) in preferred_teams: - if 'Final' not in game['status'] and game['id'] not in preferred_games: - preferred_games.append(game['id']) - - print(preferred_games) - if now - last_switch > page_display_time: - last_switch = now - - if show_preferred and len(preferred_games) > 0: - single_preferred_game = next( - (g for g in games if g['id'] == preferred_games[current_preferred_game]), None - ) - if single_preferred_game: - print(f'Showing preferred game {single_preferred_game["home"]} vs {single_preferred_game["away"]}') - draw_single_game(canvas, single_preferred_game) - - current_preferred_game += 1 - if current_preferred_game >= len(preferred_games): - show_preferred = False - current_preferred_game = 0 - current_page = 0 - else: - print(f'Showing all games page {current_page} / {len(games)}') - draw_all_games(canvas, games, current_page) - current_page += 4 - if current_page >= len(games): - current_page = 0 - if len(preferred_games) > 0: - show_preferred = True - else: - canvas.Clear() - - print('No games available') - - # no games available, just draw placeholder - graphics.DrawText(canvas, font, 10, 22, graphics.Color(*Colors.RED.value), "No games today") - - canvas = matrix.SwapOnVSync(canvas) - sleep(10) + score_mode(canvas, matrix) if __name__ == "__main__": run()