Sitelet https://github.com/mrjrask/MMM-Scores
Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

MMM-Scores

A MagicMirror² module that cycles through MLB, NHL, NFL, NBA, World Cup soccer, and Olympic Ice Hockey scoreboards. Scores are fetched automatically from public APIs with sensible fallbacks.


Table of Contents


Features

  • Eight-league scoreboards: MLB (R/H/E linescore), WBC, NHL (goals & shots), NFL (quarter-by-quarter totals plus bye list), NBA (quarter/OT breakdown), World Cup soccer (half/extra-time clocks, stoppage time, and shootout indicators), Men's Olympic Hockey, and Women's Olympic Hockey.
  • MLB/NHL/NBA Playoff Bracket screens: A full postseason bracket (Wild Card/Division Series/LCS/World Series for MLB; First Round through the Finals for NHL/NBA) plus a scrollable current-round series list with live status, next-game times, and series scores. See Playoff Bracket Screens.
  • Automatic league rotation: Show a single league, a custom sequence, or all supported leagues with timed page flips.
  • Flexible layout: Control columns, rows, or total games per page per league and scale everything with a single layoutScale value.
  • Favorite team highlighting: Per-league highlight lists add a subtle accent to matching teams on scoreboards.
  • Times Square-inspired font option: Apply the included font to scoreboard content while keeping the default MagicMirror header font.
  • Width cap for MagicMirror regions: Keep headers and content aligned inside middle_center or other constrained regions.

Requirements

  • MagicMirror² v2.20.0 or newer.
  • Node.js 18+ on the MagicMirror host (uses the built-in fetch).
  • Optional: Team logo PNGs and the TimesSquare-m105.ttf font (see Assets & Styling).

Installation

cd ~/MagicMirror/modules
git clone https://github.com/yourname/MMM-Scores.git
cd MMM-Scores
# No npm install required; the helper uses Node 18's global fetch.

Place any custom logos or font files as described below, then add the module to your config/config.js.


API Connectivity Check

Run the offline unit tests for shared league/date logic:

npm test

Use this script to verify that every external API endpoint used by the helper is reachable from your host:

npm run test:api

The command checks MLB, NHL (all fallback feeds), NFL, NBA, World Cup soccer, and both Olympic hockey ESPN endpoints, then exits non-zero if any connection fails.

Use this Olympic-focused diagnostics script to check provider reachability and print normalized men's/women's Olympic games for a target date:

npm run test:olympic -- 2026-02-11

Validate bundled font/logo assets without hitting network APIs:

npm run check:assets

Quick Start

Add this entry to config/config.js:

{
  module: "MMM-Scores",
  position: "middle_center",
  config: {
    league: "all",                 // "mlb", "nhl", "nfl", "nba", "worldcup", "olympic_mhockey", "olympic_whockey", array, or "all"
    updateIntervalScores: 60 * 1000, // helper refresh frequency
    rotateIntervalScores: 15 * 1000, // front-end page flip interval
    layoutScale: 0.95,               // scale everything uniformly
    highlightedTeams_mlb: ["CUBS"],
    highlightedTeams_worldcup: ["USA"],
    highlightedTeams_olympic_mhockey: ["USA"],
    highlightedTeams_oly_whockey: ["CAN"], // short alias also supported
    maxWidth: "720px"
  }
}

By default the module cycles through every supported league. Supply a string, array, or comma-separated list to league/leagues to control the order.


Configuration

Every option may be declared globally, as an object keyed by league ({ mlb: value, nhl: value, ... }), or with a per-league suffix (gamesPerColumn_nhl). When both exist, per-league values win.

Option Type Default Description
league / leagues string | string[] "mlb" League(s) to display. Accepts "mlb", "wbc", "nhl", "nfl", "nba", "worldcup", "olympic_mhockey", "olympic_whockey", "mlb_playoffs", "nhl_playoffs", "nba_playoffs", or "all". Arrays define the rotation order. The three *_playoffs screens are not included in "all" — add them explicitly (see Playoff Bracket Screens).
updateIntervalScores number 60000 Milliseconds between helper fetches. Minimum enforced interval is 10 seconds.
rotateIntervalScores number 15000 Milliseconds between scoreboard page rotations.
timeZone string "America/Chicago" Time zone used to decide the scoreboard date. Before the configured daily update cutoff (09:30 local for most leagues, 03:00 for Olympic hockey), scoreboards show previous-day final scores and then rotate to a current-day schedule screen; after the cutoff they show the current day's scoreboard.
providerCacheMs number 20000 Per-provider/per-date Olympic provider cache TTL in milliseconds (minimum 15000).
requestTimeoutMs number 15000 Maximum time in milliseconds for each helper HTTP request before it is aborted.
lastGoodCacheMs number 21600000 How long a last successful response can be reused if a provider fails; stale payloads are flagged for the UI.
seasonalFiltering boolean true Enables automatic hiding for known seasonal windows such as the 2026 NHL Olympic break and Olympic scoreboard retirement.
hideNhlDuringOlympics boolean true Hide NHL while the configured NHL break window is active.
hideNhlFrom / hideNhlUntil string "2026-02-06" / "2026-02-24" Inclusive ISO dates for the NHL Olympic-break visibility window.
hideOlympicsAfterEnd boolean true Hide Olympic hockey scoreboards after the configured Olympic end date.
hideOlympicsFrom string "2026-02-24" ISO date when Olympic hockey scoreboards are hidden unless seasonal filtering is disabled.
showProviderStatus boolean false Shows a compact source/updated/stale-data line above the scoreboards; stale fallback data is always indicated.
scoreboardColumns number auto Columns per page. Defaults to 2 for MLB (capped at 2) and 4 for NHL/NFL/NBA/World Cup/Olympic hockey.
gamesPerColumn (scoreboardRows) number auto Games stacked in each column (4 for all leagues unless overridden).
gamesPerPage number derived Override the total games per page; rows adjust automatically per league.
layoutScale number 1 Scales the entire module (clamped between 0.6 and 1.4).
highlightedTeams_mlb string | string[] [] Team abbreviations to highlight. Also available as _nhl, _nfl, _nba, _worldcup, _olympic_mhockey/_oly_mhockey, _olympic_whockey/_oly_whockey.
showTitle boolean true Toggles the module header (MLB Scoreboard, etc.).
useTimesSquareFont boolean true Applies the Times Square font to scoreboard cards.
maxWidth string | number "800px" Caps the module width and header alignment. Numbers are treated as pixels.

Layout controls

  • Per-league overrides: Append the league suffix (_nhl, _nfl, _nba, _mlb, _worldcup, _olympic_mhockey, _olympic_whockey) to scoreboardColumns, gamesPerColumn, or gamesPerPage to change a single league's layout.
  • Object form: For layoutScale or highlight lists, you can pass an object with default and per-league keys.

League rotation

The module keeps an internal rotation list derived from league/leagues. It fetches games for every configured league on each helper poll and flips the front-end page every rotateIntervalScores milliseconds. Helper fetches run independently so one slow provider does not block the rest of the rotation.

Provider resilience and seasonal visibility

All helper HTTP requests use requestTimeoutMs and validated HTTP status handling. When a provider fails, the helper reuses the most recent successful payload for that league until lastGoodCacheMs expires and marks the data as stale. Set showProviderStatus: true to show source/update metadata even when data is fresh; stale fallback data is shown automatically.

Seasonal filtering is configurable. For example, keep NHL and Olympic boards visible regardless of the built-in 2026 windows:

config: {
  league: "all",
  seasonalFiltering: false
}

Or customize the dates:

config: {
  hideNhlFrom: "2026-02-06",
  hideNhlUntil: "2026-02-24",
  hideOlympicsFrom: "2026-02-25"
}

Highlighting

Highlight any number of teams per league using the appropriate _mlb, _nhl, _nfl, _nba, _worldcup, _olympic_mhockey (or _oly_mhockey), or _olympic_whockey (or _oly_whockey) suffix. Highlights apply to scoreboards.

Olympic hockey country mapping uses IOC-style 3-letter codes (CAN, USA, FIN, SWE, GER, SUI, CZE, SVK, LAT, DEN, FRA, ITA, JPN).


Playoff Bracket Screens

Three additional screens show a full postseason bracket instead of a day's scoreboard: mlb_playoffs, nhl_playoffs, and nba_playoffs. Like the scoreboards, each one is titled in the module header ("MLB Playoffs", etc., hidden with showTitle: false), and draws:

  1. A seven-column bracket (WC · DS · LCS · WS · LCS · DS · WC for MLB; R1 · R2 · CF · SCF/Finals · CF · R2 · R1 for NHL/NBA), with the AL/West on the left and the NL/East on the right. Decided series dim the loser, live series turn both scores yellow, and undecided future rounds show the matchup already known from seeding with no scores ("TBD" when a team isn't known yet).
  2. A single-column list of every series in the current round (the earliest round with an unfinished series, or the last round once everything is decided), each with a status line ("Game 2 · Tonight 7 PM", "Royals win 2-0", "Series tied 1-1", etc).
  3. On very narrow displays (under 200px wide) the bracket is dropped automatically and only the series list shows.

Add one or more to your rotation like any other league:

{
  module: "MMM-Scores",
  position: "middle_center",
  config: {
    league: ["mlb_playoffs", "nhl_playoffs", "nba_playoffs"],
    rotateIntervalScores: 20 * 1000,
    maxWidth: "800px"
  }
}

Notes:

  • Before a league's postseason bracket exists, the screen shows a projected bracket built from regular-season standings/seeding (heading reads "Projected Wild Card Series" / "Projected First Round"), so the screen is never blank during the stretch run.
  • Each playoff feed is cached in the helper for 120 seconds, or 30 seconds while any series has a live game, independent of updateIntervalScores.
  • If a league has no postseason data at all (offseason with no cached standings yet), the screen shows a centered "No postseason data" message.
  • The playoff screen is 3.5 NFL scoreboard cells wide (595px at the default scale) and scales with layoutScale the same way scoreboard cards do. A fixed-length maxWidth smaller than that still caps it.

Assets & Styling

MMM-Scores/
├─ MMM-Scores.js
├─ MMM-Scores.css
├─ playoff-bracket.css
├─ playoff-bracket-shared.js
├─ playoff-data-mlb.js
├─ playoff-data-nhl.js
├─ playoff-data-nba.js
├─ node_helper.js
├─ fonts/
│  └─ TimesSquare-m105.ttf
└─ images/
   ├─ mlb/
   │  └─ ATL.png (etc.)
   ├─ nhl/
   │  └─ BOS.png (etc.)
   ├─ nfl/
   │  └─ kc.png  (lowercase filenames)
   ├─ nba/
   │  └─ ATL.png (etc.)
   └─ oly/
      └─ USA.png (Olympic/World Cup country flags, uppercase country code)
  • Logos: Place transparent PNG logos named with the abbreviations used in-game data (CUBS.png, NYR.png, kc.png, CHI.png, etc.). The module falls back to text when a logo is missing. Olympic men's/women's hockey and World Cup soccer read from images/oly/<CODE>.png (for example CAN.png, USA.png, SWE.png).
  • Font: Drop fonts/TimesSquare-m105.ttf into fonts/. The CSS registers it with @font-face.
  • Styling tweaks: Override CSS variables in MMM-Scores.css or globally (e.g., css/custom.css). Useful variables include --scoreboard-card-width-base, --scoreboard-team-font-base, --scoreboard-value-font-base, --scoreboard-gap-base, and --matrix-gap-base.
  • Asset diagnostics: Run npm run check:assets after adding logos or fonts. The command verifies required folders/files and warns about case-colliding PNG names that can behave differently across filesystems.

Example:

:root {
  --scoreboard-team-font-base: 30px;
  --scoreboard-value-font-base: 34px;
  --matrix-gap-base: 10px;
}

Data Sources

Scoreboard data comes from league-specific feeds with fallbacks where needed.

  • MLB scores: https://statsapi.mlb.com/api/v1/schedule/games?sportId=1&hydrate=linescore (date based on timeZone), filtered to MLB club-vs-club games only. International/WBC matchups are kept off the MLB scoreboard and only appear when wbc is explicitly configured.
  • NHL scores: Prefers statsapi.web.nhl.com endpoints with automatic fallbacks to the public scoreboard and REST feeds; the date adjusts for early-morning previous-day fetches.
  • NBA scores: https://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard for the selected date.
  • World Cup soccer scores: https://site.api.espn.com/apis/site/v2/sports/soccer/fifa.world/scoreboard for the selected date; live cards show 1H, 2H, ET, match minutes, stoppage time such as 45'+2', and shootout superscripts when provided by the feed.
  • NFL scores: Weekly schedules from https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard?dates=<YYYYMMDD> aggregated from Wednesday through Monday night; includes bye-week teams.
  • Men's Olympic hockey scores: Primary https://site.api.espn.com/apis/site/v2/sports/hockey/mens-olympics/scoreboard?dates=<YYYYMMDD> with resilient provider-chain hooks (olympics.com, IIHF, TheSportsDB, Wikipedia/Wikidata finals) and last-good-data fallback.
  • Women's Olympic hockey scores: Primary https://site.api.espn.com/apis/site/v2/sports/hockey/womens-olympics/scoreboard?dates=<YYYYMMDD> with the same provider-chain/fallback architecture.
  • MLB playoff bracket: statsapi.mlb.com schedule/postseason-series endpoints for the bracket, plus the regular-season standings endpoint for wild-card-era seeding (falls back to a seed-projected bracket before the postseason schedule exists).
  • NHL playoff bracket: api-web.nhle.com's playoff-bracket and schedule/now endpoints, with a standings-based first-round projection and a last-season bracket fallback in the offseason.
  • NBA playoff bracket: the NBA's live-data CDN bracket JSON (cdn.nba.com, with an S3 mirror fallback), which also keeps last season's completed bracket visible in the offseason.

Troubleshooting

  • Header font changes unexpectedly: Remove broad overrides like .module.MMM-Scores * { font-family: 'Times Square' !important; } so the MagicMirror header keeps its default font.
  • Font not loading: Confirm fonts/TimesSquare-m105.ttf exists and is readable. CSS references it with url('/sitelet?url=https%3A%2F%2Fgithub.com%2Fmrjrask%2Ffonts%2FTimesSquare-m105.ttf').
  • Logos missing: Ensure filenames exactly match the abbreviations used in game data (case-sensitive per league). Missing files fall back to text labels.
  • "Cannot find module 'node-fetch'": Upgrade to Node.js 18+; the helper relies on the built-in fetch.
  • CSS 404s for /css/custom.css: Only reference css/custom.css if the file exists to avoid MIME errors.
  • Playoff screen always shows "Projected": That's expected once regular-season standings exist but the postseason schedule hasn't been published yet (or, for NHL/NBA, once the previous postseason has fully wrapped and next year's bracket isn't up yet). It switches to the real bracket automatically once the provider publishes it.
  • Playoff screen shows "No postseason data": The helper couldn't reach any of that league's data sources (live bracket, standings-based projection, or previous-season fallback). Check npm run test:api-style connectivity to the hosts listed in Data Sources.

License

MIT License. See LICENSE for full text.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages