Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,10 @@
"plugin_path": "plugins/ledmatrix-leaderboard",
"stars": 0,
"downloads": 0,
"last_updated": "2026-07-31",
"last_updated": "2026-08-05",
"verified": true,
"screenshot": "",
"latest_version": "1.2.3"
"latest_version": "1.3.0"
},
{
"id": "ledmatrix-flights",
Expand Down
46 changes: 46 additions & 0 deletions plugins/ledmatrix-leaderboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,52 @@ A plugin for LEDMatrix that displays scrolling leaderboards and standings for mu
- `max_duration`: Maximum display duration (30-600 seconds, default: 300)
- `loop`: Continuously loop the leaderboard (default: true)

### Appearance (`global.appearance`)

The leaderboard renders for a 1:1 LED matrix, where a partially-lit pixel is a
visibly dim LED rather than a smooth edge. These options control that:

- `pixel_perfect_text` (default `true`): draw text with anti-aliasing off, so
every glyph pixel is fully on or fully off. Set to `false` for the older,
softer look.
- `crisp_logos` (default `true`): give logos hard edges instead of a ring of
half-lit pixels.
- `text_outline` (default `true`): black outline behind text so it stays
readable where it sits near a logo.
- `logo_scale` (default `1.0`): logo height as a fraction of the panel height.
`1.0` fits the panel exactly; anything above `1.0` crops the top and bottom of
every logo.
- `font_size` (default `0` = pick a size for the panel height): sizes are
snapped to the font's pixel grid — multiples of 8 for Press Start 2P — because
off-grid sizes are what make pixel fonts look blurry.

### How many teams are shown

Each league's `top_teams` sets how far down the standings to go. **Set it to `0`
to show every team the league returns** (all 32 NFL teams, the full AP Top 25,
and so on).

A longer list needs proportionally more time on screen. The display controller
gives the plugin `min(plugin cap, core cap)` seconds and then moves on
mid-scroll, so a list longer than that budget simply stops partway through —
which looks like the leaderboard cutting off at an arbitrary team. The relevant
settings:

| Setting | Where | Default |
|---|---|---|
| `global.dynamic_duration.max_duration_seconds` | this plugin | 600 |
| `global.dynamic_duration.controller_cap_seconds` | this plugin | 600 |
| `display.dynamic_duration.max_duration_seconds` | LEDMatrix core config | 180 |

The **lowest** of the three wins, so the core's 180s default is usually the one
that decides it. All 32 NFL teams is roughly 3,200px of ticker: about 240s at
the default 15 px/s, or about 36s at 100 px/s.

If the content will not fit the budget, the plugin logs a warning at startup
naming which cap is limiting it and roughly how much of the list will not be
reached. Raise that cap, increase the scroll speed
(`global.display.scroll_speed` / `scroll_delay`), or lower `top_teams`.

### Per-League Settings

#### NFL Configuration
Expand Down
77 changes: 58 additions & 19 deletions plugins/ledmatrix-leaderboard/config_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,45 @@
"type": "boolean",
"default": false,
"description": "Continuously loop the leaderboard"
},
"appearance": {
"type": "object",
"description": "Pixel-perfect rendering options for text and logos",
"properties": {
"pixel_perfect_text": {
"type": "boolean",
"default": true,
"description": "Render text with hard pixel edges. Disable only if you prefer the older anti-aliased (softer, blurrier) look"
},
"crisp_logos": {
"type": "boolean",
"default": true,
"description": "Give logos hard edges instead of a ring of half-lit pixels"
},
"text_outline": {
"x-advanced": true,
"type": "boolean",
"default": true,
"description": "Draw a black outline around text so it stays readable over logos"
},
"logo_scale": {
"x-advanced": true,
"type": "number",
"default": 1.0,
"minimum": 0.5,
"maximum": 1.5,
"description": "Logo height as a fraction of the panel height. 1.0 fits the panel exactly; above 1.0 crops the top and bottom of every logo"
},
"font_size": {
"x-advanced": true,
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 32,
"description": "Font size in pixels. 0 picks a size that suits the panel height. Values are snapped to the font's pixel grid (multiples of 8 for Press Start 2P) to keep text sharp"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
Expand All @@ -246,9 +285,9 @@
"top_teams": {
"type": "integer",
"default": 10,
"minimum": 5,
"minimum": 0,
"maximum": 32,
"description": "Number of top NFL teams to display"
"description": "Number of top NFL teams to display. 0 shows every team the standings return (up to 32). Long lists need a matching display duration - see the README"
}
},
"additionalProperties": false
Expand All @@ -264,9 +303,9 @@
"top_teams": {
"type": "integer",
"default": 10,
"minimum": 5,
"minimum": 0,
"maximum": 30,
"description": "Number of top NBA teams to display"
"description": "Number of top NBA teams to display. 0 shows every team the standings return (up to 30). Long lists need a matching display duration - see the README"
}
},
"additionalProperties": false
Expand All @@ -282,9 +321,9 @@
"top_teams": {
"type": "integer",
"default": 10,
"minimum": 5,
"minimum": 0,
"maximum": 30,
"description": "Number of top MLB teams to display"
"description": "Number of top MLB teams to display. 0 shows every team the standings return (up to 30). Long lists need a matching display duration - see the README"
}
},
"additionalProperties": false
Expand All @@ -300,9 +339,9 @@
"top_teams": {
"type": "integer",
"default": 25,
"minimum": 5,
"minimum": 0,
"maximum": 130,
"description": "Number of top NCAA Football teams to display"
"description": "Number of top NCAA Football teams to display. 0 shows every team the standings return (up to 130). Long lists need a matching display duration - see the README"
},
"show_ranking": {
"type": "boolean",
Expand All @@ -323,9 +362,9 @@
"top_teams": {
"type": "integer",
"default": 10,
"minimum": 5,
"minimum": 0,
"maximum": 32,
"description": "Number of top NHL teams to display"
"description": "Number of top NHL teams to display. 0 shows every team the standings return (up to 32). Long lists need a matching display duration - see the README"
}
},
"additionalProperties": false
Expand All @@ -341,9 +380,9 @@
"top_teams": {
"type": "integer",
"default": 25,
"minimum": 5,
"minimum": 0,
"maximum": 350,
"description": "Number of top NCAA Men's Basketball teams to display"
"description": "Number of top NCAA Men's Basketball teams to display. 0 shows every team the standings return (up to 350). Long lists need a matching display duration - see the README"
},
"show_ranking": {
"type": "boolean",
Expand All @@ -364,9 +403,9 @@
"top_teams": {
"type": "integer",
"default": 10,
"minimum": 5,
"minimum": 0,
"maximum": 60,
"description": "Number of top NCAA Men's Hockey teams to display"
"description": "Number of top NCAA Men's Hockey teams to display. 0 shows every team the standings return (up to 60). Long lists need a matching display duration - see the README"
},
"show_ranking": {
"type": "boolean",
Expand All @@ -387,9 +426,9 @@
"top_teams": {
"type": "integer",
"default": 25,
"minimum": 5,
"minimum": 0,
"maximum": 350,
"description": "Number of top NCAA Women's Basketball teams to display"
"description": "Number of top NCAA Women's Basketball teams to display. 0 shows every team the standings return (up to 350). Long lists need a matching display duration - see the README"
},
"show_ranking": {
"type": "boolean",
Expand All @@ -410,9 +449,9 @@
"top_teams": {
"type": "integer",
"default": 25,
"minimum": 5,
"minimum": 0,
"maximum": 350,
"description": "Number of top NCAA Baseball teams to display"
"description": "Number of top NCAA Baseball teams to display. 0 shows every team the standings return (up to 350). Long lists need a matching display duration - see the README"
},
"season": {
"x-advanced": true,
Expand Down Expand Up @@ -444,4 +483,4 @@
"required": [
"enabled"
]
}
}
19 changes: 18 additions & 1 deletion plugins/ledmatrix-leaderboard/data_fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,25 @@ def fetch_standings(self, league_config: Dict[str, Any]) -> List[Dict[str, Any]]
else:
standings = self._fetch_teams_data(league_config)

# Apply top_teams limit centrally so config changes take effect immediately
# Apply top_teams limit centrally so config changes take effect immediately.
# 0 (or any non-positive value) means "show every team the API returned".
top_teams = league_config.get('top_teams', 10)
try:
top_teams = int(top_teams)
except (TypeError, ValueError):
top_teams = 10

if top_teams <= 0:
self.logger.info(
"Showing all %d teams for %s (top_teams=0)", len(standings), league_key
)
return standings

if len(standings) < top_teams:
self.logger.info(
"Requested top %d teams for %s but only %d available",
top_teams, league_key, len(standings)
)
return standings[:top_teams]

def _fetch_ncaa_fb_rankings(self, league_config: Dict[str, Any]) -> List[Dict[str, Any]]:
Expand Down
Loading
Loading