From f2e009169177c83dd61bd1167336e87080f08892 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 12:19:28 -0400 Subject: [PATCH 1/2] feat(scoreboards): pick favourite teams from a list instead of typing codes favorite_teams was a free-text array of abbreviations with nothing validating the contents -- just a description saying "use 2-3 letter codes". A wrong or stale code saved cleanly and then matched nothing, and the result is an empty screen with no error anywhere to explain it. The roster is now an enum with display names, rendered as the same checkbox-group the odds ticker already uses for exactly this. Prompted by a user reporting that PHI did not work for the Phillies while NYY worked for the Yankees -- I could not reproduce that (ESPN returns PHI in both the teams endpoint and live game data, and the resolver passes it through unchanged), but a picker removes the whole class of mistake rather than that one instance. MLB, NFL, NBA, WNBA and NHL. The college leagues keep the text field: a checkbox grid of several hundred teams is worse than typing one. scripts/check_team_pickers.py already validates pickers against ESPN and fails when a roster drifts, so these are covered by it from now on. Its league-key discovery took a fixed trail offset, which reported hockey's nested .../nhl/properties/teams/properties/favorite_teams as league "teams" and could not resolve it; it now walks back to the nearest segment that names a league, and all nine pickers validate. Existing configurations are unaffected -- the codes were already ESPN's, so anything valid today stays valid. Verified: PHI, NYY and TB are all accepted, a plausible typo like PHL is not. Harness clean on the four plugins; the two failing tests are the pre-existing missing-src ones on main. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Udr6MfaFLUPhX5Fgo67Jf5 --- plugins.json | 8 +- plugins/afl-scoreboard/config_schema.json | 2 +- .../baseball-scoreboard/config_schema.json | 73 ++++++++++- plugins/baseball-scoreboard/manifest.json | 14 +- .../basketball-scoreboard/config_schema.json | 124 ++++++++++++++++-- plugins/basketball-scoreboard/manifest.json | 10 +- .../football-scoreboard/config_schema.json | 77 ++++++++++- plugins/football-scoreboard/manifest.json | 10 +- plugins/hockey-scoreboard/config_schema.json | 77 ++++++++++- plugins/hockey-scoreboard/manifest.json | 8 +- plugins/nrl-scoreboard/config_schema.json | 2 +- scripts/check_team_pickers.py | 10 +- 12 files changed, 383 insertions(+), 32 deletions(-) diff --git a/plugins.json b/plugins.json index f2a50b1c..0a2f1c15 100644 --- a/plugins.json +++ b/plugins.json @@ -76,7 +76,7 @@ "last_updated": "2026-08-13", "verified": true, "screenshot": "", - "latest_version": "1.27.0" + "latest_version": "1.28.0" }, { "id": "basketball-scoreboard", @@ -101,7 +101,7 @@ "last_updated": "2026-08-13", "verified": true, "screenshot": "", - "latest_version": "1.15.0" + "latest_version": "1.16.0" }, { "id": "calendar", @@ -240,7 +240,7 @@ "last_updated": "2026-08-13", "verified": true, "screenshot": "", - "latest_version": "2.17.0" + "latest_version": "2.18.0" }, { "id": "geochron", @@ -335,7 +335,7 @@ "last_updated": "2026-08-13", "verified": true, "screenshot": "", - "latest_version": "1.12.0", + "latest_version": "1.13.0", "icon": "fas fa-hockey-puck" }, { diff --git a/plugins/afl-scoreboard/config_schema.json b/plugins/afl-scoreboard/config_schema.json index a6beea3e..c02d0cfc 100644 --- a/plugins/afl-scoreboard/config_schema.json +++ b/plugins/afl-scoreboard/config_schema.json @@ -566,7 +566,7 @@ "mode_durations": { "type": "object", "title": "Mode-Level Durations", - "description": "Control total duration for each mode type. If not set, uses dynamic calculation (total_games \u00d7 per_game_duration).", + "description": "Control total duration for each mode type. If not set, uses dynamic calculation (total_games × per_game_duration).", "properties": { "recent_mode_duration": { "type": [ diff --git a/plugins/baseball-scoreboard/config_schema.json b/plugins/baseball-scoreboard/config_schema.json index cf2bea98..0aca431f 100644 --- a/plugins/baseball-scoreboard/config_schema.json +++ b/plugins/baseball-scoreboard/config_schema.json @@ -172,13 +172,80 @@ }, "favorite_teams": { "type": "array", + "title": "Favorite Teams", + "description": "Select your favorite MLB teams.", "items": { - "type": "string" + "type": "string", + "enum": [ + "ARI", + "ATH", + "ATL", + "BAL", + "BOS", + "CHC", + "CHW", + "CIN", + "CLE", + "COL", + "DET", + "HOU", + "KC", + "LAA", + "LAD", + "MIA", + "MIL", + "MIN", + "NYM", + "NYY", + "PHI", + "PIT", + "SD", + "SEA", + "SF", + "STL", + "TB", + "TEX", + "TOR", + "WSH" + ] }, "default": [], "uniqueItems": true, - "maxItems": 30, - "description": "List of favorite MLB team abbreviations (e.g., NYY, BOS, LAD). Use 2-3 letter codes." + "x-widget": "checkbox-group", + "x-options": { + "labels": { + "ARI": "Arizona Diamondbacks", + "ATH": "Athletics", + "ATL": "Atlanta Braves", + "BAL": "Baltimore Orioles", + "BOS": "Boston Red Sox", + "CHC": "Chicago Cubs", + "CHW": "Chicago White Sox", + "CIN": "Cincinnati Reds", + "CLE": "Cleveland Guardians", + "COL": "Colorado Rockies", + "DET": "Detroit Tigers", + "HOU": "Houston Astros", + "KC": "Kansas City Royals", + "LAA": "Los Angeles Angels", + "LAD": "Los Angeles Dodgers", + "MIA": "Miami Marlins", + "MIL": "Milwaukee Brewers", + "MIN": "Minnesota Twins", + "NYM": "New York Mets", + "NYY": "New York Yankees", + "PHI": "Philadelphia Phillies", + "PIT": "Pittsburgh Pirates", + "SD": "San Diego Padres", + "SEA": "Seattle Mariners", + "SF": "San Francisco Giants", + "STL": "St. Louis Cardinals", + "TB": "Tampa Bay Rays", + "TEX": "Texas Rangers", + "TOR": "Toronto Blue Jays", + "WSH": "Washington Nationals" + } + } }, "exclude_teams": { "x-advanced": true, diff --git a/plugins/baseball-scoreboard/manifest.json b/plugins/baseball-scoreboard/manifest.json index 55dfac4d..4c2964c1 100644 --- a/plugins/baseball-scoreboard/manifest.json +++ b/plugins/baseball-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "baseball-scoreboard", "name": "Baseball Scoreboard", - "version": "1.27.0", + "version": "1.28.0", "author": "ChuckBuilds", "description": "Live, recent, and upcoming baseball games across MLB, MiLB, and NCAA Baseball with real-time scores and schedules", "category": "sports", @@ -30,6 +30,12 @@ "branch": "main", "plugin_path": "plugins/baseball-scoreboard", "versions": [ + { + "version": "1.28.0", + "released": "2026-08-19", + "ledmatrix_min_version": "2.0.0", + "notes": "Turn the favorite-teams field into a checkbox picker. It was a free-text array with only a description telling you to use 2-3 letter codes and nothing validating what you typed, so a wrong or stale code saved cleanly and then matched nothing -- an empty screen with no error anywhere. The roster is now an enum with display names, rendered as the same checkbox-group the odds ticker already uses, and scripts/check_team_pickers.py keeps it in step with ESPN. Existing configurations are unaffected: the codes were already ESPN's, so anything valid today stays valid. Applies to MLB; the college leagues keep the text field, since a checkbox grid of several hundred teams is worse than typing. Codes ESPN no longer uses cannot be offered by a picker, so if one is already saved the config will not save until it is replaced: CWS (White Sox), KCR (Royals), OAK (Athletics (pre-2025 code)), SDP (Padres), SFG (Giants), TBR (Rays), WAS (Nationals). The web UI reports which value it rejected." + }, { "version": "1.27.0", "released": "2026-08-19", @@ -223,7 +229,7 @@ { "released": "2026-07-08", "version": "1.14.2", - "notes": "Shrink the Traditional Scoreboard's ball/strike/out circle indicators further (they were still a bit overpowering) and move the batting-team \u25b2/\u25bc indicator out of the At Bat column into the header row's empty team-column cell, right next to the inning numbers.", + "notes": "Shrink the Traditional Scoreboard's ball/strike/out circle indicators further (they were still a bit overpowering) and move the batting-team ▲/▼ indicator out of the At Bat column into the header row's empty team-column cell, right next to the inning numbers.", "ledmatrix_min": "2.0.0" }, { @@ -241,7 +247,7 @@ { "released": "2026-07-07", "version": "1.13.1", - "notes": "Fix the Traditional Scoreboard's At Bat side panel (added in 1.13.0) clipping its ball/strike/out dots off the right edge of the display -- the fit check compared leftover space against a flush-left grid, but the grid is actually centered, so it was eating into the panel's reserved space from the left too. Also account for the Outs row's extra batting-team \u25b2/\u25bc arrow, which wasn't factored into the width check at all.", + "notes": "Fix the Traditional Scoreboard's At Bat side panel (added in 1.13.0) clipping its ball/strike/out dots off the right edge of the display -- the fit check compared leftover space against a flush-left grid, but the grid is actually centered, so it was eating into the panel's reserved space from the left too. Also account for the Outs row's extra batting-team ▲/▼ arrow, which wasn't factored into the width check at all.", "ledmatrix_min": "2.0.0" }, { @@ -283,7 +289,7 @@ { "released": "2026-07-02", "version": "1.7.0", - "notes": "Add exclude_teams (hide specific teams from live rotation and recent/final scores \u2014 spoiler protection) and filtering.favorite_live_boost (tune how much more often your favorite's live game appears in rotation vs other live games) per league.", + "notes": "Add exclude_teams (hide specific teams from live rotation and recent/final scores — spoiler protection) and filtering.favorite_live_boost (tune how much more often your favorite's live game appears in rotation vs other live games) per league.", "ledmatrix_min": "2.0.0" }, { diff --git a/plugins/basketball-scoreboard/config_schema.json b/plugins/basketball-scoreboard/config_schema.json index 993b9595..2e389166 100644 --- a/plugins/basketball-scoreboard/config_schema.json +++ b/plugins/basketball-scoreboard/config_schema.json @@ -204,11 +204,80 @@ }, "favorite_teams": { "type": "array", + "title": "Favorite Teams", + "description": "Select your favorite NBA teams.", "items": { - "type": "string" + "type": "string", + "enum": [ + "ATL", + "BKN", + "BOS", + "CHA", + "CHI", + "CLE", + "DAL", + "DEN", + "DET", + "GS", + "HOU", + "IND", + "LAC", + "LAL", + "MEM", + "MIA", + "MIL", + "MIN", + "NO", + "NY", + "OKC", + "ORL", + "PHI", + "PHX", + "POR", + "SA", + "SAC", + "TOR", + "UTAH", + "WSH" + ] }, "default": [], - "description": "List of favorite NBA team abbreviations (e.g., LAL, BOS, GS). Use 2-3 letter codes. These are ESPN's codes, which are not always the ones you would guess: Golden State is GS, not GSW." + "uniqueItems": true, + "x-widget": "checkbox-group", + "x-options": { + "labels": { + "ATL": "Atlanta Hawks", + "BKN": "Brooklyn Nets", + "BOS": "Boston Celtics", + "CHA": "Charlotte Hornets", + "CHI": "Chicago Bulls", + "CLE": "Cleveland Cavaliers", + "DAL": "Dallas Mavericks", + "DEN": "Denver Nuggets", + "DET": "Detroit Pistons", + "GS": "Golden State Warriors", + "HOU": "Houston Rockets", + "IND": "Indiana Pacers", + "LAC": "LA Clippers", + "LAL": "Los Angeles Lakers", + "MEM": "Memphis Grizzlies", + "MIA": "Miami Heat", + "MIL": "Milwaukee Bucks", + "MIN": "Minnesota Timberwolves", + "NO": "New Orleans Pelicans", + "NY": "New York Knicks", + "OKC": "Oklahoma City Thunder", + "ORL": "Orlando Magic", + "PHI": "Philadelphia 76ers", + "PHX": "Phoenix Suns", + "POR": "Portland Trail Blazers", + "SA": "San Antonio Spurs", + "SAC": "Sacramento Kings", + "TOR": "Toronto Raptors", + "UTAH": "Utah Jazz", + "WSH": "Washington Wizards" + } + } }, "exclude_teams": { "x-advanced": true, @@ -551,7 +620,7 @@ "mode_durations": { "type": "object", "title": "Mode-Level Durations", - "description": "Control total duration for each mode type for NBA. If not set, uses dynamic calculation (total_games \u00d7 per_game_duration).", + "description": "Control total duration for each mode type for NBA. If not set, uses dynamic calculation (total_games × per_game_duration).", "properties": { "recent_mode_duration": { "x-advanced": true, @@ -591,11 +660,50 @@ }, "favorite_teams": { "type": "array", + "title": "Favorite Teams", + "description": "Select your favorite WNBA teams.", "items": { - "type": "string" + "type": "string", + "enum": [ + "ATL", + "CHI", + "CON", + "DAL", + "GS", + "IND", + "LA", + "LV", + "MIN", + "NY", + "PHX", + "POR", + "SEA", + "TOR", + "WSH" + ] }, "default": [], - "description": "List of favorite WNBA team abbreviations (e.g., NY, LA, SEA). Use 2-3 letter codes. These are ESPN's codes: New York Liberty is NY and Los Angeles Sparks is LA." + "uniqueItems": true, + "x-widget": "checkbox-group", + "x-options": { + "labels": { + "ATL": "Atlanta Dream", + "CHI": "Chicago Sky", + "CON": "Connecticut Sun", + "DAL": "Dallas Wings", + "GS": "Golden State Valkyries", + "IND": "Indiana Fever", + "LA": "Los Angeles Sparks", + "LV": "Las Vegas Aces", + "MIN": "Minnesota Lynx", + "NY": "New York Liberty", + "PHX": "Phoenix Mercury", + "POR": "Portland Fire", + "SEA": "Seattle Storm", + "TOR": "Toronto Tempo", + "WSH": "Washington Mystics" + } + } }, "exclude_teams": { "x-advanced": true, @@ -938,7 +1046,7 @@ "mode_durations": { "type": "object", "title": "Mode-Level Durations", - "description": "Control total duration for each mode type for WNBA. If not set, uses dynamic calculation (total_games \u00d7 per_game_duration).", + "description": "Control total duration for each mode type for WNBA. If not set, uses dynamic calculation (total_games × per_game_duration).", "properties": { "recent_mode_duration": { "x-advanced": true, @@ -1363,7 +1471,7 @@ "mode_durations": { "type": "object", "title": "Mode-Level Durations", - "description": "Control total duration for each mode type for NCAA Men's Basketball. If not set, uses dynamic calculation (total_games \u00d7 per_game_duration).", + "description": "Control total duration for each mode type for NCAA Men's Basketball. If not set, uses dynamic calculation (total_games × per_game_duration).", "properties": { "recent_mode_duration": { "x-advanced": true, @@ -1788,7 +1896,7 @@ "mode_durations": { "type": "object", "title": "Mode-Level Durations", - "description": "Control total duration for each mode type for NCAA Women's Basketball. If not set, uses dynamic calculation (total_games \u00d7 per_game_duration).", + "description": "Control total duration for each mode type for NCAA Women's Basketball. If not set, uses dynamic calculation (total_games × per_game_duration).", "properties": { "recent_mode_duration": { "x-advanced": true, diff --git a/plugins/basketball-scoreboard/manifest.json b/plugins/basketball-scoreboard/manifest.json index 68f775b4..5e094852 100644 --- a/plugins/basketball-scoreboard/manifest.json +++ b/plugins/basketball-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "basketball-scoreboard", "name": "Basketball Scoreboard", - "version": "1.15.0", + "version": "1.16.0", "description": "Live, recent, and upcoming basketball games across NBA, NCAA Men's, NCAA Women's, and WNBA with real-time scores, schedules, and March Madness tournament support", "author": "ChuckBuilds", "category": "sports", @@ -18,6 +18,12 @@ "branch": "main", "plugin_path": "plugins/basketball-scoreboard", "versions": [ + { + "version": "1.16.0", + "released": "2026-08-19", + "ledmatrix_min_version": "2.0.0", + "notes": "Turn the favorite-teams field into a checkbox picker. It was a free-text array with only a description telling you to use 2-3 letter codes and nothing validating what you typed, so a wrong or stale code saved cleanly and then matched nothing -- an empty screen with no error anywhere. The roster is now an enum with display names, rendered as the same checkbox-group the odds ticker already uses, and scripts/check_team_pickers.py keeps it in step with ESPN. Existing configurations are unaffected: the codes were already ESPN's, so anything valid today stays valid. Applies to NBA and WNBA; the college leagues keep the text field, since a checkbox grid of several hundred teams is worse than typing. Codes ESPN no longer uses cannot be offered by a picker, so if one is already saved the config will not save until it is replaced: GSW (Warriors), NOP (Pelicans), NYK (Knicks), PHO (Suns), SAS (Spurs), UTA (Jazz), WAS (Wizards). The web UI reports which value it rejected." + }, { "version": "1.15.0", "released": "2026-08-19", @@ -146,7 +152,7 @@ { "released": "2026-07-02", "version": "1.6.0", - "notes": "Add exclude_teams (hide specific teams from live rotation and recent/final scores \u2014 spoiler protection) and filtering.favorite_live_boost (tune how much more often your favorite's live game appears in rotation vs other live games) per league.", + "notes": "Add exclude_teams (hide specific teams from live rotation and recent/final scores — spoiler protection) and filtering.favorite_live_boost (tune how much more often your favorite's live game appears in rotation vs other live games) per league.", "ledmatrix_min": "2.0.0" }, { diff --git a/plugins/football-scoreboard/config_schema.json b/plugins/football-scoreboard/config_schema.json index de6c7b37..e5eab76d 100644 --- a/plugins/football-scoreboard/config_schema.json +++ b/plugins/football-scoreboard/config_schema.json @@ -167,11 +167,84 @@ }, "favorite_teams": { "type": "array", + "title": "Favorite Teams", + "description": "Select your favorite NFL teams.", "items": { - "type": "string" + "type": "string", + "enum": [ + "ARI", + "ATL", + "BAL", + "BUF", + "CAR", + "CHI", + "CIN", + "CLE", + "DAL", + "DEN", + "DET", + "GB", + "HOU", + "IND", + "JAX", + "KC", + "LAC", + "LAR", + "LV", + "MIA", + "MIN", + "NE", + "NO", + "NYG", + "NYJ", + "PHI", + "PIT", + "SEA", + "SF", + "TB", + "TEN", + "WSH" + ] }, "default": [], - "description": "List of favorite NFL team abbreviations (e.g., TB, DAL, GB). Use 2-3 letter codes." + "uniqueItems": true, + "x-widget": "checkbox-group", + "x-options": { + "labels": { + "ARI": "Arizona Cardinals", + "ATL": "Atlanta Falcons", + "BAL": "Baltimore Ravens", + "BUF": "Buffalo Bills", + "CAR": "Carolina Panthers", + "CHI": "Chicago Bears", + "CIN": "Cincinnati Bengals", + "CLE": "Cleveland Browns", + "DAL": "Dallas Cowboys", + "DEN": "Denver Broncos", + "DET": "Detroit Lions", + "GB": "Green Bay Packers", + "HOU": "Houston Texans", + "IND": "Indianapolis Colts", + "JAX": "Jacksonville Jaguars", + "KC": "Kansas City Chiefs", + "LAC": "Los Angeles Chargers", + "LAR": "Los Angeles Rams", + "LV": "Las Vegas Raiders", + "MIA": "Miami Dolphins", + "MIN": "Minnesota Vikings", + "NE": "New England Patriots", + "NO": "New Orleans Saints", + "NYG": "New York Giants", + "NYJ": "New York Jets", + "PHI": "Philadelphia Eagles", + "PIT": "Pittsburgh Steelers", + "SEA": "Seattle Seahawks", + "SF": "San Francisco 49ers", + "TB": "Tampa Bay Buccaneers", + "TEN": "Tennessee Titans", + "WSH": "Washington Commanders" + } + } }, "exclude_teams": { "x-advanced": true, diff --git a/plugins/football-scoreboard/manifest.json b/plugins/football-scoreboard/manifest.json index b4c681cd..956056af 100644 --- a/plugins/football-scoreboard/manifest.json +++ b/plugins/football-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "football-scoreboard", "name": "Football Scoreboard", - "version": "2.17.0", + "version": "2.18.0", "author": "ChuckBuilds", "class_name": "FootballScoreboardPlugin", "description": "Standalone plugin for live, recent, and upcoming football games across NFL and NCAA Football with real-time scores, down/distance, possession, and game status. Now with organized nested config!", @@ -24,6 +24,12 @@ "ncaa_fb_live" ], "versions": [ + { + "version": "2.18.0", + "released": "2026-08-19", + "ledmatrix_min_version": "2.0.0", + "notes": "Turn the favorite-teams field into a checkbox picker. It was a free-text array with only a description telling you to use 2-3 letter codes and nothing validating what you typed, so a wrong or stale code saved cleanly and then matched nothing -- an empty screen with no error anywhere. The roster is now an enum with display names, rendered as the same checkbox-group the odds ticker already uses, and scripts/check_team_pickers.py keeps it in step with ESPN. Existing configurations are unaffected: the codes were already ESPN's, so anything valid today stays valid. Applies to NFL; the college leagues keep the text field, since a checkbox grid of several hundred teams is worse than typing. Codes ESPN no longer uses cannot be offered by a picker, so if one is already saved the config will not save until it is replaced: JAC (Jaguars), LA (Rams), OAK (Raiders (pre-2020)), SD (Chargers (pre-2017)), STL (Rams (pre-2016)), TAM (Buccaneers), WAS (Commanders). The web UI reports which value it rejected." + }, { "version": "2.17.0", "released": "2026-08-19", @@ -164,7 +170,7 @@ "released": "2026-07-10", "version": "2.8.0", "ledmatrix_min_version": "2.0.0", - "notes": "Adaptive layout (beta, opt-in): set layout_mode: \"adaptive\" to scale fonts/logos/regions to any panel size. Default stays \"classic\" \u2014 rendering is unchanged unless you opt in; switch back to classic in config to revert without reinstalling. Adaptive mode also applies customization.layout x/y offsets in scroll mode (classic scroll never did). User-configured fonts win over adaptive sizing." + "notes": "Adaptive layout (beta, opt-in): set layout_mode: \"adaptive\" to scale fonts/logos/regions to any panel size. Default stays \"classic\" — rendering is unchanged unless you opt in; switch back to classic in config to revert without reinstalling. Adaptive mode also applies customization.layout x/y offsets in scroll mode (classic scroll never did). User-configured fonts win over adaptive sizing." }, { "released": "2026-07-08", diff --git a/plugins/hockey-scoreboard/config_schema.json b/plugins/hockey-scoreboard/config_schema.json index 6706fc8e..9d1cbd67 100644 --- a/plugins/hockey-scoreboard/config_schema.json +++ b/plugins/hockey-scoreboard/config_schema.json @@ -312,11 +312,84 @@ "properties": { "favorite_teams": { "type": "array", + "title": "Favorite Teams", + "description": "Select your favorite NHL teams.", "items": { - "type": "string" + "type": "string", + "enum": [ + "ANA", + "BOS", + "BUF", + "CAR", + "CBJ", + "CGY", + "CHI", + "COL", + "DAL", + "DET", + "EDM", + "FLA", + "LA", + "MIN", + "MTL", + "NJ", + "NSH", + "NYI", + "NYR", + "OTT", + "PHI", + "PIT", + "SEA", + "SJ", + "STL", + "TB", + "TOR", + "UTAH", + "VAN", + "VGK", + "WPG", + "WSH" + ] }, "default": [], - "description": "NHL favorite team abbreviations (e.g., ['TB', 'TOR', 'BOS'])" + "uniqueItems": true, + "x-widget": "checkbox-group", + "x-options": { + "labels": { + "ANA": "Anaheim Ducks", + "BOS": "Boston Bruins", + "BUF": "Buffalo Sabres", + "CAR": "Carolina Hurricanes", + "CBJ": "Columbus Blue Jackets", + "CGY": "Calgary Flames", + "CHI": "Chicago Blackhawks", + "COL": "Colorado Avalanche", + "DAL": "Dallas Stars", + "DET": "Detroit Red Wings", + "EDM": "Edmonton Oilers", + "FLA": "Florida Panthers", + "LA": "Los Angeles Kings", + "MIN": "Minnesota Wild", + "MTL": "Montreal Canadiens", + "NJ": "New Jersey Devils", + "NSH": "Nashville Predators", + "NYI": "New York Islanders", + "NYR": "New York Rangers", + "OTT": "Ottawa Senators", + "PHI": "Philadelphia Flyers", + "PIT": "Pittsburgh Penguins", + "SEA": "Seattle Kraken", + "SJ": "San Jose Sharks", + "STL": "St. Louis Blues", + "TB": "Tampa Bay Lightning", + "TOR": "Toronto Maple Leafs", + "UTAH": "Utah Mammoth", + "VAN": "Vancouver Canucks", + "VGK": "Vegas Golden Knights", + "WPG": "Winnipeg Jets", + "WSH": "Washington Capitals" + } + } }, "exclude_teams": { "x-advanced": true, diff --git a/plugins/hockey-scoreboard/manifest.json b/plugins/hockey-scoreboard/manifest.json index 0497a5c3..d8d59fb8 100644 --- a/plugins/hockey-scoreboard/manifest.json +++ b/plugins/hockey-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "hockey-scoreboard", "name": "Hockey Scoreboard", - "version": "1.12.0", + "version": "1.13.0", "author": "ChuckBuilds", "description": "Live, recent, and upcoming hockey games across NHL, NCAA Men's, and NCAA Women's hockey with real-time scores and schedules", "homepage": "https://github.com/ChuckBuilds/ledmatrix-plugins/tree/main/plugins/hockey-scoreboard", @@ -54,6 +54,12 @@ } ], "versions": [ + { + "version": "1.13.0", + "released": "2026-08-19", + "ledmatrix_min_version": "2.0.0", + "notes": "Turn the favorite-teams field into a checkbox picker. It was a free-text array with only a description telling you to use 2-3 letter codes and nothing validating what you typed, so a wrong or stale code saved cleanly and then matched nothing -- an empty screen with no error anywhere. The roster is now an enum with display names, rendered as the same checkbox-group the odds ticker already uses, and scripts/check_team_pickers.py keeps it in step with ESPN. Existing configurations are unaffected: the codes were already ESPN's, so anything valid today stays valid. Applies to NHL; the college leagues keep the text field, since a checkbox grid of several hundred teams is worse than typing. Codes ESPN no longer uses cannot be offered by a picker, so if one is already saved the config will not save until it is replaced: ARI (Coyotes (pre-2024)), LAK (Kings), MON (Canadiens), NJD (Devils), PHX (Coyotes (pre-2014)), SJS (Sharks), TBL (Lightning), UTA (Mammoth), WAS (Capitals). The web UI reports which value it rejected." + }, { "version": "1.12.0", "released": "2026-08-19", diff --git a/plugins/nrl-scoreboard/config_schema.json b/plugins/nrl-scoreboard/config_schema.json index f67bcd81..08af7262 100644 --- a/plugins/nrl-scoreboard/config_schema.json +++ b/plugins/nrl-scoreboard/config_schema.json @@ -562,7 +562,7 @@ "mode_durations": { "type": "object", "title": "Mode-Level Durations", - "description": "Control total duration for each mode type. If not set, uses dynamic calculation (total_games \u00d7 per_game_duration).", + "description": "Control total duration for each mode type. If not set, uses dynamic calculation (total_games × per_game_duration).", "properties": { "recent_mode_duration": { "type": [ diff --git a/scripts/check_team_pickers.py b/scripts/check_team_pickers.py index 3fdeb5bc..6720b940 100755 --- a/scripts/check_team_pickers.py +++ b/scripts/check_team_pickers.py @@ -80,8 +80,14 @@ def walk(node, trail): if isinstance(node, dict): if (node.get("x-widget") == "checkbox-group" and trail and trail[-1] == "favorite_teams"): - # .../leagues/properties//properties/favorite_teams - league = trail[-3] if len(trail) >= 3 else None + # Usually ...//properties/favorite_teams, but hockey + # and lacrosse group theirs one level deeper, under + # ...//properties/teams/properties/favorite_teams. + # Taking a fixed offset reported that as league "teams", so + # walk back to the nearest segment that names a league. + league = next( + (part for part in reversed(trail) if part in LEAGUE_PATHS), + trail[-3] if len(trail) >= 3 else None) yield league, node for key, value in node.items(): for hit in walk(value, trail + [key]): From 32b259a4583ba3205b1023d229e181ee83253edf Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Aug 2026 17:11:39 -0400 Subject: [PATCH 2/2] docs(scoreboards): record which saved codes the pickers will reject Rebase resolution plus review follow-up. The enum that makes the picker possible is also what validates the saved value, and the web UI's save endpoint returns 400 on a schema violation. A config still holding a code ESPN has retired -- OAK for the Athletics, ARI for the Coyotes, SD/STL for the Chargers and Rams -- therefore cannot be saved until that entry is replaced, even if the edit was to an unrelated field. Those codes already matched nothing at runtime, so nothing that worked stops working, but the failure moves from silent to blocking and that belongs in the release notes rather than in a support thread. Each list is derived from the plugin's own enum rather than asserted, so it cannot drift from what the schema actually rejects. Also drops the incidental afl/nrl unicode churn (identical parsed value, re-serialised escapes) so those two files match main byte-for-byte. --- plugins/afl-scoreboard/config_schema.json | 2 +- plugins/nrl-scoreboard/config_schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/afl-scoreboard/config_schema.json b/plugins/afl-scoreboard/config_schema.json index c02d0cfc..a6beea3e 100644 --- a/plugins/afl-scoreboard/config_schema.json +++ b/plugins/afl-scoreboard/config_schema.json @@ -566,7 +566,7 @@ "mode_durations": { "type": "object", "title": "Mode-Level Durations", - "description": "Control total duration for each mode type. If not set, uses dynamic calculation (total_games × per_game_duration).", + "description": "Control total duration for each mode type. If not set, uses dynamic calculation (total_games \u00d7 per_game_duration).", "properties": { "recent_mode_duration": { "type": [ diff --git a/plugins/nrl-scoreboard/config_schema.json b/plugins/nrl-scoreboard/config_schema.json index 08af7262..f67bcd81 100644 --- a/plugins/nrl-scoreboard/config_schema.json +++ b/plugins/nrl-scoreboard/config_schema.json @@ -562,7 +562,7 @@ "mode_durations": { "type": "object", "title": "Mode-Level Durations", - "description": "Control total duration for each mode type. If not set, uses dynamic calculation (total_games × per_game_duration).", + "description": "Control total duration for each mode type. If not set, uses dynamic calculation (total_games \u00d7 per_game_duration).", "properties": { "recent_mode_duration": { "type": [