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
9 changes: 6 additions & 3 deletions docs/plugin-development/03-advanced-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ endlessly-scrolling strip. A plugin opts in by implementing:
| `get_vegas_content` | `(self) -> Optional[list[Image]]` | The PIL image(s) to splice into the strip, or `None` |
| `get_vegas_content_type` | `(self) -> str` | `'single'` or `'multi'` (multiple scrollable items, e.g. games) |
| `get_vegas_display_mode` | `(self) -> VegasDisplayMode` | How this plugin behaves in the strip |
| `get_supported_vegas_modes` | `(self) -> list[VegasDisplayMode]` | (Optional) modes the plugin supports |

Import the enum defensively — older cores don't ship it:

Expand Down Expand Up @@ -185,8 +184,12 @@ See the Vegas section of
and the `vegas_mode` config declarations in
[`plugins/nfl-draft/config_schema.json`](../../plugins/nfl-draft/config_schema.json)
and [`plugins/olympics/config_schema.json`](../../plugins/olympics/config_schema.json).
[`plugins/calendar/manager.py`](../../plugins/calendar/manager.py) shows
`get_supported_vegas_modes` returning multiple modes.
Core 3.8.0 adds `vegas_participation` (`"scroll"`, `"pause"` or `"exclude"`)
in the manifest as the way to declare a fixed answer; the hooks above still
decide it when the manifest and the user's config say nothing, and only
`STATIC` (pauses) and content type `'none'` (excluded) change anything. Do not
implement `get_supported_vegas_modes`: core never read it and removes it in
3.9.0. See the core's `docs/PLUGIN_API_REFERENCE.md`, "Vegas participation".

---

Expand Down
53 changes: 26 additions & 27 deletions plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,8 @@
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"latest_version": "1.56.0",
"ledmatrix_min_version": "3.8.0",
"commit": "637a6aef9c1af31186445eff47be41b167f87b4e"
"latest_version": "1.56.1",
"ledmatrix_min_version": "3.8.0"
},
{
"id": "basketball-scoreboard",
Expand All @@ -104,12 +103,12 @@
"plugin_path": "plugins/basketball-scoreboard",
"stars": 0,
"downloads": 0,
"last_updated": "2026-10-01",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"latest_version": "1.41.1",
"latest_version": "1.41.2",
"ledmatrix_min_version": "3.8.0",
"commit": "56e3f7a0070a26bd7b3fcd80b0214445ded480ac"
"commit": "7824542bdc40ae5783b33997a234f61f2838bf3b"
},
{
"id": "calendar",
Expand All @@ -129,12 +128,12 @@
"plugin_path": "plugins/calendar",
"stars": 0,
"downloads": 0,
"last_updated": "2026-09-28",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"latest_version": "1.2.9",
"latest_version": "1.2.10",
"ledmatrix_min_version": "3.3.0",
"commit": "53e42a489c235fc99246c309a44980cdf50a5b9e"
"commit": "36c19cb8d8ba85f15c1d77b543c6946d2c056034"
},
{
"id": "christmas-countdown",
Expand Down Expand Up @@ -280,12 +279,12 @@
"plugin_path": "plugins/football-scoreboard",
"stars": 0,
"downloads": 0,
"last_updated": "2026-10-01",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"latest_version": "3.18.3",
"latest_version": "3.18.4",
"ledmatrix_min_version": "3.8.0",
"commit": "e5dd1c3dc87dcd0dea00c9209feab6e9138471c4"
"commit": "7824542bdc40ae5783b33997a234f61f2838bf3b"
},
{
"id": "geochron",
Expand Down Expand Up @@ -382,13 +381,13 @@
"plugin_path": "plugins/hockey-scoreboard",
"stars": 0,
"downloads": 0,
"last_updated": "2026-10-01",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"latest_version": "1.42.0",
"latest_version": "1.42.1",
"icon": "fas fa-hockey-puck",
"ledmatrix_min_version": "3.8.0",
"commit": "27785a3eea0e6e40bc456c57bd2a0bae2a873376"
"commit": "7824542bdc40ae5783b33997a234f61f2838bf3b"
},
{
"id": "lacrosse-scoreboard",
Expand All @@ -408,13 +407,13 @@
"plugin_path": "plugins/lacrosse-scoreboard",
"stars": 0,
"downloads": 0,
"last_updated": "2026-10-01",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"latest_version": "1.36.1",
"latest_version": "1.36.2",
"icon": "fas fa-baseball-ball",
"ledmatrix_min_version": "3.8.0",
"commit": "56e3f7a0070a26bd7b3fcd80b0214445ded480ac"
"commit": "7824542bdc40ae5783b33997a234f61f2838bf3b"
},
{
"id": "birdnet-go",
Expand Down Expand Up @@ -762,12 +761,12 @@
"plugin_path": "plugins/olympics",
"stars": 0,
"downloads": 0,
"last_updated": "2026-09-28",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"latest_version": "3.1.3",
"latest_version": "3.1.4",
"ledmatrix_min_version": "2.0.0",
"commit": "62fa95853476715b833abfe0b207d4f50876b6b4"
"commit": "36c19cb8d8ba85f15c1d77b543c6946d2c056034"
},
{
"id": "pga-tour-leaderboard",
Expand Down Expand Up @@ -1004,13 +1003,13 @@
"plugin_path": "plugins/ufc-scoreboard",
"stars": 0,
"downloads": 0,
"last_updated": "2026-10-01",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"latest_version": "1.19.1",
"latest_version": "1.19.2",
"icon": "fas fa-fist-raised",
"ledmatrix_min_version": "3.8.0",
"commit": "56e3f7a0070a26bd7b3fcd80b0214445ded480ac"
"commit": "7824542bdc40ae5783b33997a234f61f2838bf3b"
},
{
"id": "weather",
Expand Down Expand Up @@ -1340,13 +1339,13 @@
"plugin_path": "plugins/blackjack",
"stars": 0,
"downloads": 0,
"last_updated": "2026-09-16",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "https://raw.githubusercontent.com/ChuckBuilds/ledmatrix-plugins/main/plugins/blackjack/assets/hero.png",
"latest_version": "1.3.0",
"latest_version": "1.3.1",
"icon": "fa-diamond",
"ledmatrix_min_version": "3.2.0",
"commit": "912724bccbf6c56985be2b1fcdf16b79a33dd0c2"
"commit": "36c19cb8d8ba85f15c1d77b543c6946d2c056034"
},
{
"id": "ledmatrix-nascar",
Expand Down
36 changes: 36 additions & 0 deletions plugins/baseball-scoreboard/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [1.56.1] - 2026-10-02

### Changed
- The `has_live_content() returning ...` summary is logged at INFO only
when the answer changes. The once-a-minute re-log of an unchanged answer
is now DEBUG: at INFO it was a persistent-journal line a minute, each one
an SD-card write, for as long as the device ran.

## [1.56.0] - 2026-10-02

### Added
Expand Down Expand Up @@ -72,6 +80,34 @@
No behaviour change: the code is the same, and every safety-harness render,
scroll card and celebration golden is pixel-identical.

## [1.54.2] - 2026-10-01

### Fixed
- The series line on the full-screen Recent card now fits vertically as
well as horizontally (#593). The width fallback in 1.54.1 never stepped
a large `period_text` font down on a wide panel, so "Final/N" and the
series row together still covered the score. With a series line shown,
the card is drawn in the largest face that keeps the status row, score
and series row apart: the configured time face, stepped down a pixel at
a time to 8 px, then the 4x6 record face. Cards without a series line,
and the default font, draw as before.

## [1.54.1] - 2026-10-01

### Fixed
- `display_options.show_series_summary` no longer prints the series line
("Series tied 1-1") over the score and "Final/N" on the full-screen
Recent card (#588). The line now shares the card's bottom row with the
date, taking the first of these that fits between the record corners:
date and series, the series alone, a compact spelling (`Tied 1-1`,
`NYY 2-1`, `NYY W 4-2`), then the same in the small 4x6 font with the
date row cleared. The line is never dropped. Off by default; Recent card
only.

### Known issues
- On a 64-wide panel with both team records on there is no room between
the record corners, so the series line is drawn and can touch them.

## [1.54.0] - 2026-09-30

### Added
Expand Down
6 changes: 5 additions & 1 deletion plugins/baseball-scoreboard/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2658,7 +2658,11 @@ def has_live_content(self) -> bool:
self._last_live_content_state = state
self._last_live_content_log = current_time
counts = ", ".join(f"{league}={n}" for league, n in sorted(league_counts.items())) or "none"
self.logger.info(
# INFO only when the answer changes. The periodic re-log of an
# unchanged answer goes to DEBUG: at INFO it was a journal line a
# minute for as long as the device ran, each one an SD write.
log = self.logger.info if changed else self.logger.debug
log(
f"has_live_content() returning {result}: "
f"mlb_live={mlb_live}, milb_live={milb_live}, ncaa_live={ncaa_live} "
f"(live games: {counts})"
Expand Down
8 changes: 7 additions & 1 deletion plugins/baseball-scoreboard/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "baseball-scoreboard",
"name": "Baseball Scoreboard",
"version": "1.56.0",
"version": "1.56.1",
"update_interval": 60,
"author": "ChuckBuilds",
"description": "Live, recent, and upcoming baseball games across MLB, MiLB, and NCAA Baseball with real-time scores and schedules",
Expand Down Expand Up @@ -31,6 +31,12 @@
"branch": "main",
"plugin_path": "plugins/baseball-scoreboard",
"versions": [
{
"version": "1.56.1",
"released": "2026-10-02",
"ledmatrix_min_version": "3.8.0",
"notes": "The has_live_content() summary is logged at INFO only when the answer changes; the once-a-minute re-log of an unchanged answer is now DEBUG. At INFO it wrote a persistent-journal line a minute to the SD card for as long as the device ran. No change in behaviour."
},
{
"version": "1.56.0",
"released": "2026-10-02",
Expand Down
16 changes: 12 additions & 4 deletions plugins/baseball-scoreboard/test_live_content_log_throttle.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Covers:
1. An unchanged live answer logs once, not once per call.
2. A changed answer (count or boolean) logs immediately.
3. An unchanged answer is re-logged after the throttle interval.
3. An unchanged answer is re-logged after the throttle interval, at DEBUG.
4. False results are throttled the same way (the old behavior, preserved).

Run: <core-venv>/bin/python plugins/baseball-scoreboard/test_live_content_log_throttle.py
Expand All @@ -30,14 +30,19 @@
class _RecordingLogger:
def __init__(self):
self.info_lines = []
self.relog_lines = [] # the has_live_content() summary, at DEBUG

def info(self, msg, *a, **k):
self.info_lines.append(msg)

def debug(self, msg, *a, **k):
if str(msg).startswith("has_live_content() returning"):
self.relog_lines.append(msg)

def warning(self, msg, *a, **k):
pass

warning = error = debug
error = warning


class _LiveSource:
Expand Down Expand Up @@ -125,10 +130,13 @@ def test_unchanged_answer_relogs_after_interval():
stub.has_live_content()
assert len(stub.logger.info_lines) == 1

# Pretend the interval elapsed: a steady state stays visible in the log.
# Pretend the interval elapsed: a steady state is re-logged, but at DEBUG.
# At INFO it was a persistent-journal line a minute, every minute.
stub._last_live_content_log -= stub._live_content_log_interval + 1
stub.has_live_content()
assert len(stub.logger.info_lines) == 2, stub.logger.info_lines
assert len(stub.logger.info_lines) == 1, stub.logger.info_lines
assert len(stub.logger.relog_lines) == 1, stub.logger.relog_lines
assert "returning True" in stub.logger.relog_lines[0], stub.logger.relog_lines[0]


def test_false_results_are_throttled():
Expand Down
8 changes: 8 additions & 0 deletions plugins/basketball-scoreboard/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [1.41.2] - 2026-10-02

### Changed
- The `has_live_content() returning ...` summary is logged at INFO only
when the answer changes. The once-a-minute re-log of an unchanged answer
is now DEBUG: at INFO it was a persistent-journal line a minute, each one
an SD-card write, for as long as the device ran.

## [1.41.1] - 2026-10-01

### Changed
Expand Down
6 changes: 5 additions & 1 deletion plugins/basketball-scoreboard/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1630,7 +1630,11 @@ def has_live_content(self) -> bool:
if changed or due:
self._last_live_content_state = state
self._last_live_content_log = current_time
self.logger.info(
# INFO only when the answer changes. The periodic re-log of an
# unchanged answer goes to DEBUG: at INFO it was a journal line a
# minute for as long as the device ran, each one an SD write.
log = self.logger.info if changed else self.logger.debug
log(
f"has_live_content() returning {result}: "
f"nba_live={nba_live}, wnba_live={wnba_live}, "
f"ncaam_live={ncaam_live}, ncaaw_live={ncaaw_live}"
Expand Down
10 changes: 8 additions & 2 deletions plugins/basketball-scoreboard/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "basketball-scoreboard",
"name": "Basketball Scoreboard",
"version": "1.41.1",
"version": "1.41.2",
"update_interval": 60,
"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",
Expand All @@ -19,6 +19,12 @@
"branch": "main",
"plugin_path": "plugins/basketball-scoreboard",
"versions": [
{
"version": "1.41.2",
"released": "2026-10-02",
"ledmatrix_min_version": "3.8.0",
"notes": "The has_live_content() summary is logged at INFO only when the answer changes; the once-a-minute re-log of an unchanged answer is now DEBUG. At INFO it wrote a persistent-journal line a minute to the SD card for as long as the device ran. No change in behaviour."
},
{
"version": "1.41.1",
"released": "2026-10-01",
Expand Down Expand Up @@ -605,7 +611,7 @@
],
"stars": 0,
"downloads": 0,
"last_updated": "2026-09-30",
"last_updated": "2026-10-02",
"verified": true,
"screenshot": "",
"display_modes": [
Expand Down
18 changes: 11 additions & 7 deletions plugins/blackjack/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@
#: and calls display() straight after, so the real gap is microseconds.
_STATIC_PAUSE_WINDOW = 0.5

#: The ``vegas_mode`` values this plugin honours. SCROLL is for multi-item
#: plugins and a hand is one thing, so ``"scroll"`` falls back to fixed. This
#: was ``get_supported_vegas_modes()``, which core deprecated (removed in 3.9.0)
#: and never read; nothing else consulted it.
#:
#: There is deliberately no manifest ``vegas_participation``: Vegas derives
#: ours from get_vegas_display_mode() (STATIC pauses, otherwise scroll), and
#: that call is what tells display() a STATIC pause is starting.
_VEGAS_MODES_OFFERED = ("fixed", "static")


class BlackjackPlugin(BasePlugin):
"""Deals one hand of blackjack per rotation and plays it out on the panel."""
Expand Down Expand Up @@ -692,7 +702,7 @@ def _resolve_vegas_mode(self):
except ValueError:
self.logger.warning("Invalid vegas_mode %r, using fixed", requested)
else:
if mode in self.get_supported_vegas_modes():
if mode.value in _VEGAS_MODES_OFFERED:
return mode
self.logger.warning(
"vegas_mode %r is not supported by this plugin, using fixed",
Expand Down Expand Up @@ -729,12 +739,6 @@ def _show_static_still(self) -> bool:
self.display_manager.update_display()
return True

def get_supported_vegas_modes(self):
"""The two that work. SCROLL is for multi-item plugins; a hand is one."""
if VegasDisplayMode is None:
return []
return [VegasDisplayMode.FIXED_SEGMENT, VegasDisplayMode.STATIC]

# -- lifecycle / web UI ----------------------------------------------

def on_config_change(self, new_config) -> None:
Expand Down
Loading
Loading