split out
This commit is contained in:
+220
@@ -0,0 +1,220 @@
|
||||
import requests
|
||||
import utils.logos as logos
|
||||
from rgbmatrix import graphics
|
||||
from utils.colors import Colors
|
||||
from utils.fonts import font, font_small
|
||||
from time import time
|
||||
|
||||
games = []
|
||||
last_fetch = 0
|
||||
current_page = 0
|
||||
page_display_time = 8
|
||||
last_switch = time()
|
||||
show_preferred = True
|
||||
|
||||
current_preferred_game = 0
|
||||
preferred_games = []
|
||||
preferred_teams = [
|
||||
("BUF", "nfl"),
|
||||
("BUF", "nhl"),
|
||||
("TOR", "mlb"),
|
||||
("LAL", "nba"),
|
||||
]
|
||||
|
||||
# --- 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 = logos.load_logo(league, game["away"])
|
||||
home_logo = logos.load_logo(league, game["home"])
|
||||
|
||||
logos.draw_logo(canvas, away_logo, offset + 0, 0)
|
||||
logos.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 = logos.load_logo(league, game["home"])
|
||||
away_logo = logos.load_logo(league, game["away"])
|
||||
|
||||
logos.draw_logo(canvas, away_logo, 0, 0)
|
||||
logos.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"])
|
||||
)
|
||||
|
||||
def draw_frame(canvas):
|
||||
now = time()
|
||||
|
||||
if now - last_fetch > 30 or len(games) <= 0:
|
||||
games = get_all_scores()
|
||||
last_fetch = now
|
||||
|
||||
if games:
|
||||
canvas.Clear()
|
||||
|
||||
# clear finished preferred games
|
||||
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)
|
||||
|
||||
# collect 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')
|
||||
graphics.DrawText(canvas, font, 10, 22, graphics.Color(*Colors.RED.value), "No games today")
|
||||
|
||||
# canvas = matrix.SwapOnVSync(canvas)
|
||||
# sleep(10)
|
||||
return canvas
|
||||
+5
-270
@@ -1,25 +1,17 @@
|
||||
import requests
|
||||
import os
|
||||
import govee
|
||||
import math
|
||||
import pygame
|
||||
from enum import Enum
|
||||
from time import sleep, time
|
||||
from pathlib import Path
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from rgbmatrix import RGBMatrix, RGBMatrixOptions, graphics
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# --- Current file path for file resolution
|
||||
SCRIPT_DIR = Path(__file__).parent.resolve()
|
||||
from utils.data import LOGO_DIR, ASSET_DIR
|
||||
from utils.colors import Colors
|
||||
import modes.score as score_mode
|
||||
|
||||
# --- Load environment vars ---
|
||||
load_dotenv()
|
||||
|
||||
# --- Default vars ---
|
||||
ASSET_DIR = os.path.join(SCRIPT_DIR, "assets")
|
||||
LOGO_DIR = os.path.join(ASSET_DIR, "logos")
|
||||
|
||||
# --- Matrix config ---
|
||||
options = RGBMatrixOptions()
|
||||
options.rows = 32
|
||||
@@ -34,25 +26,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)
|
||||
YELLOW = (255, 200, 0)
|
||||
RED = (255, 50, 50)
|
||||
SABRES_BLUE = (0, 135, 48)
|
||||
SABRES_GOLD = (252, 20, 210)
|
||||
|
||||
# --- Govee API ---
|
||||
if os.environ.get('GOVEE_API_KEY'):
|
||||
govee_api = govee.GoveeApi(key=os.environ["GOVEE_API_KEY"])
|
||||
@@ -192,251 +165,13 @@ def draw_pil_image(canvas, img):
|
||||
r, g, b = img.getpixel((x, y))
|
||||
canvas.SetPixel(x, y, b, g, r) # bgr panels
|
||||
|
||||
def load_logo(league, abbr):
|
||||
key = f"{league}_{abbr}"
|
||||
if key in logo_cache:
|
||||
return logo_cache[key]
|
||||
|
||||
path = os.path.join(LOGO_DIR, f"{key}.png")
|
||||
if not os.path.exists(path):
|
||||
print(f"Logo not found: {path}")
|
||||
logo_cache[key] = None
|
||||
return None
|
||||
|
||||
try:
|
||||
# FIX: convert to RGB here so draw_logo always gets 3-channel pixels
|
||||
img = Image.open(path).convert("RGB")
|
||||
logo_cache[key] = img
|
||||
return img
|
||||
except Exception as e:
|
||||
print(f"Error loading logo {key}: {e}")
|
||||
logo_cache[key] = None
|
||||
return None
|
||||
|
||||
def draw_logo(canvas, img, x, y):
|
||||
if img is None:
|
||||
return
|
||||
for px in range(img.width):
|
||||
for py in range(img.height):
|
||||
# FIX: unpack as RGB (load_logo guarantees RGB now)
|
||||
r, g, b = img.getpixel((px, py))
|
||||
canvas.SetPixel(x + px, y + py, r, b, g) # bgr panels
|
||||
|
||||
# --- 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 = load_logo(league, game["away"])
|
||||
home_logo = load_logo(league, game["home"])
|
||||
|
||||
draw_logo(canvas, away_logo, offset + 0, 0)
|
||||
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 = load_logo(league, game["home"])
|
||||
away_logo = load_logo(league, game["away"])
|
||||
|
||||
draw_logo(canvas, away_logo, 0, 0)
|
||||
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
|
||||
|
||||
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 finished preferred games
|
||||
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)
|
||||
|
||||
# collect 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')
|
||||
graphics.DrawText(canvas, font, 10, 22, graphics.Color(*Colors.RED.value), "No games today")
|
||||
|
||||
canvas = matrix.SwapOnVSync(canvas)
|
||||
sleep(10)
|
||||
canvas_ref = score_mode.draw_frame(canvas)
|
||||
canvas = matrix.SwapOnVSync(canvas_ref)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -0,0 +1,9 @@
|
||||
from enum import Enum
|
||||
|
||||
# --- Pre-built colors ---
|
||||
class Colors(Enum):
|
||||
WHITE = (255, 255, 255)
|
||||
YELLOW = (255, 200, 0)
|
||||
RED = (255, 50, 50)
|
||||
SABRES_BLUE = (0, 135, 48)
|
||||
SABRES_GOLD = (252, 20, 210)
|
||||
@@ -0,0 +1,6 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).parent.resolve()
|
||||
ASSET_DIR = os.path.join(SCRIPT_DIR, "assets")
|
||||
LOGO_DIR = os.path.join(ASSET_DIR, "logos")
|
||||
@@ -0,0 +1,10 @@
|
||||
from rgbmatrix import graphics
|
||||
from utils.data import ASSET_DIR
|
||||
import os
|
||||
|
||||
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"))
|
||||
@@ -0,0 +1,35 @@
|
||||
import os
|
||||
from utils.data import LOGO_DIR
|
||||
from PIL import Image
|
||||
|
||||
logo_cache = {}
|
||||
|
||||
def load_logo(league, abbr):
|
||||
key = f"{league}_{abbr}"
|
||||
if key in logo_cache:
|
||||
return logo_cache[key]
|
||||
|
||||
path = os.path.join(LOGO_DIR, f"{key}.png")
|
||||
if not os.path.exists(path):
|
||||
print(f"Logo not found: {path}")
|
||||
logo_cache[key] = None
|
||||
return None
|
||||
|
||||
try:
|
||||
# FIX: convert to RGB here so draw_logo always gets 3-channel pixels
|
||||
img = Image.open(path).convert("RGB")
|
||||
logo_cache[key] = img
|
||||
return img
|
||||
except Exception as e:
|
||||
print(f"Error loading logo {key}: {e}")
|
||||
logo_cache[key] = None
|
||||
return None
|
||||
|
||||
def draw_logo(canvas, img, x, y):
|
||||
if img is None:
|
||||
return
|
||||
for px in range(img.width):
|
||||
for py in range(img.height):
|
||||
# FIX: unpack as RGB (load_logo guarantees RGB now)
|
||||
r, g, b = img.getpixel((px, py))
|
||||
canvas.SetPixel(x + px, y + py, r, b, g) # bgr panels
|
||||
Reference in New Issue
Block a user