From 36c19cb8d8ba85f15c1d77b543c6946d2c056034 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:25:00 -0400 Subject: [PATCH 1/3] blackjack, calendar, olympics: stop using get_supported_vegas_modes Core deprecated BasePlugin.get_supported_vegas_modes() for removal in 3.9.0 (core #682). Core never read it, so the overrides were dead code; blackjack also called its own override to validate vegas_mode. - blackjack 1.3.1: the honoured vegas_mode values (fixed, static) are a module constant. No manifest vegas_participation on purpose: Vegas derives it from get_vegas_display_mode(), and that call is what tells display() a STATIC pause is starting. - calendar 1.2.10, olympics 3.1.4: drop the override. Participation still comes from get_vegas_display_mode() / get_vegas_content_type(). Core floors unchanged (3.2.0, 3.3.0, 2.0.0): nothing new from core is used. The plugin-development doc no longer lists the hook and points at vegas_participation. Co-Authored-By: Claude Opus 5.5 --- .../plugin-development/03-advanced-features.md | 9 ++++++--- plugins/blackjack/manager.py | 18 +++++++++++------- plugins/blackjack/manifest.json | 10 ++++++++-- plugins/calendar/manager.py | 12 ------------ plugins/calendar/manifest.json | 10 ++++++++-- plugins/olympics/manager.py | 6 ------ plugins/olympics/manifest.json | 10 ++++++++-- 7 files changed, 41 insertions(+), 34 deletions(-) diff --git a/docs/plugin-development/03-advanced-features.md b/docs/plugin-development/03-advanced-features.md index fbe0387e..64740256 100644 --- a/docs/plugin-development/03-advanced-features.md +++ b/docs/plugin-development/03-advanced-features.md @@ -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: @@ -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". --- diff --git a/plugins/blackjack/manager.py b/plugins/blackjack/manager.py index 18b51d61..b25ef783 100644 --- a/plugins/blackjack/manager.py +++ b/plugins/blackjack/manager.py @@ -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.""" @@ -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", @@ -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: diff --git a/plugins/blackjack/manifest.json b/plugins/blackjack/manifest.json index 23a3f6de..b92543a9 100644 --- a/plugins/blackjack/manifest.json +++ b/plugins/blackjack/manifest.json @@ -1,7 +1,7 @@ { "id": "blackjack", "name": "Blackjack", - "version": "1.3.0", + "version": "1.3.1", "author": "ChuckBuilds", "description": "Deals one hand of Las Vegas blackjack per rotation. Cards slide in and flip face up a few seconds apart, the player follows basic strategy, the dealer plays the house rules, and the hand ends on a BLACKJACK! / BUST! / PUSH banner. Every hand comes off a real shuffled shoe, so no two rotations are the same.", "entry_point": "manager.py", @@ -34,6 +34,12 @@ "update_interval": 3600, "default_duration": 22, "versions": [ + { + "version": "1.3.1", + "released": "2026-10-02", + "ledmatrix_min_version": "3.2.0", + "notes": "Stops consulting get_supported_vegas_modes(), which LEDMatrix core deprecated and removes in 3.9.0 (core never read it). The two vegas_mode values the plugin honours, fixed and static, are now a module constant, so Vegas behaviour is unchanged: fixed scrolls a finished hand by, static pauses on one, and scroll still falls back to fixed. Core floor unchanged." + }, { "version": "1.3.0", "released": "2026-09-16", @@ -66,7 +72,7 @@ } ], "config_schema": "config_schema.json", - "last_updated": "2026-09-16", + "last_updated": "2026-10-02", "stars": 0, "downloads": 0, "verified": true, diff --git a/plugins/calendar/manager.py b/plugins/calendar/manager.py index 1550e840..74a698ea 100644 --- a/plugins/calendar/manager.py +++ b/plugins/calendar/manager.py @@ -872,18 +872,6 @@ def get_vegas_display_mode(self) -> VegasDisplayMode: # Default to SCROLL for multi-event content return VegasDisplayMode.SCROLL - def get_supported_vegas_modes(self) -> List[VegasDisplayMode]: - """ - Return list of Vegas display modes this plugin supports. - - Calendar supports both SCROLL (events flow with the ticker) - and FIXED_SEGMENT (calendar shows as a static block). - - Returns: - List of supported VegasDisplayMode values - """ - return [VegasDisplayMode.SCROLL, VegasDisplayMode.FIXED_SEGMENT] - # ==================== End Vegas Scroll Integration ==================== def get_display_duration(self) -> float: diff --git a/plugins/calendar/manifest.json b/plugins/calendar/manifest.json index c16a7c43..2fbe6087 100644 --- a/plugins/calendar/manifest.json +++ b/plugins/calendar/manifest.json @@ -1,7 +1,7 @@ { "id": "calendar", "name": "Google Calendar", - "version": "1.2.9", + "version": "1.2.10", "author": "ChuckBuilds", "description": "Display upcoming events from Google Calendar with date, time, and event details. Shows next 1-3 events with automatic rotation and timezone support.", "category": "productivity", @@ -35,6 +35,12 @@ } ], "versions": [ + { + "version": "1.2.10", + "released": "2026-10-02", + "ledmatrix_min_version": "3.3.0", + "notes": "Drops its get_supported_vegas_modes() override, which LEDMatrix core deprecated and removes in 3.9.0. Core never read it; Vegas still takes the calendar's participation from get_vegas_display_mode() and get_vegas_content_type(), so behaviour is unchanged. Core floor unchanged." + }, { "version": "1.2.9", "released": "2026-09-28", @@ -142,7 +148,7 @@ "ledmatrix_min_version": "2.0.0" } ], - "last_updated": "2026-09-28", + "last_updated": "2026-10-02", "stars": 0, "downloads": 0, "verified": true, diff --git a/plugins/olympics/manager.py b/plugins/olympics/manager.py index c473d989..e3267669 100644 --- a/plugins/olympics/manager.py +++ b/plugins/olympics/manager.py @@ -621,12 +621,6 @@ def get_vegas_display_mode(self) -> VegasDisplayMode: return VegasDisplayMode.SCROLL - def get_supported_vegas_modes(self) -> List[VegasDisplayMode]: - """Return list of Vegas display modes this plugin supports.""" - if VegasDisplayMode is None: - return [] - return [VegasDisplayMode.SCROLL, VegasDisplayMode.FIXED_SEGMENT, VegasDisplayMode.STATIC] - def has_live_content(self) -> bool: """ Check if there is priority live content to display. diff --git a/plugins/olympics/manifest.json b/plugins/olympics/manifest.json index 81498fe8..a8339233 100644 --- a/plugins/olympics/manifest.json +++ b/plugins/olympics/manifest.json @@ -1,7 +1,7 @@ { "id": "olympics", "name": "Olympics", - "version": "3.1.3", + "version": "3.1.4", "author": "ChuckBuilds", "description": "Enhanced Olympics plugin with live medal counts, upcoming events, results, and countdown. Supports Vegas scroll mode and regular display mode.", "category": "sports", @@ -37,6 +37,12 @@ "country_tracking": true }, "versions": [ + { + "version": "3.1.4", + "released": "2026-10-02", + "ledmatrix_min_version": "2.0.0", + "notes": "Drops its get_supported_vegas_modes() override, which LEDMatrix core deprecated and removes in 3.9.0. Core never read it; Vegas still takes the plugin's participation from get_vegas_display_mode() (pausing for live medal finals) and get_vegas_content_type(), so behaviour is unchanged. Core floor unchanged." + }, { "version": "3.1.3", "released": "2026-09-28", @@ -102,7 +108,7 @@ "ledmatrix_min_version": "2.0.0" } ], - "last_updated": "2026-09-28", + "last_updated": "2026-10-02", "stars": 0, "downloads": 0, "verified": true, From 7824542bdc40ae5783b33997a234f61f2838bf3b Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:25:00 -0400 Subject: [PATCH 2/3] scoreboards: has_live_content() re-log at DEBUG; baseball CHANGELOG backfill The six scoreboards (baseball, basketball, football, hockey, lacrosse, ufc) logged the has_live_content() summary at INFO once a minute even when nothing had changed: a persistent-journal line a minute, each an SD write. It is now INFO only when the answer changes and DEBUG for the periodic re-log. The baseball/football/ufc throttle tests now assert the re-log is DEBUG and not INFO. Stage 4 did not move has_live_content() into core (sports_plugin_host only calls it), so the change belongs here. baseball-scoreboard's CHANGELOG also gains the missing 1.54.1 (#588) and 1.54.2 (#593) entries. Bumps: baseball 1.54.4, basketball 1.41.2, football 3.18.4, hockey 1.42.1, lacrosse 1.36.2, ufc 1.19.2. Co-Authored-By: Claude Opus 5.5 --- plugins/baseball-scoreboard/CHANGELOG.md | 36 +++++++++++++++++++ plugins/baseball-scoreboard/manager.py | 6 +++- plugins/baseball-scoreboard/manifest.json | 10 ++++-- .../test_live_content_log_throttle.py | 16 ++++++--- plugins/basketball-scoreboard/CHANGELOG.md | 8 +++++ plugins/basketball-scoreboard/manager.py | 6 +++- plugins/basketball-scoreboard/manifest.json | 10 ++++-- plugins/football-scoreboard/CHANGELOG.md | 8 +++++ plugins/football-scoreboard/manager.py | 6 +++- plugins/football-scoreboard/manifest.json | 10 ++++-- .../test_live_content_log_throttle.py | 16 ++++++--- plugins/hockey-scoreboard/CHANGELOG.md | 8 +++++ plugins/hockey-scoreboard/manager.py | 6 +++- plugins/hockey-scoreboard/manifest.json | 10 ++++-- plugins/lacrosse-scoreboard/CHANGELOG.md | 8 +++++ plugins/lacrosse-scoreboard/manager.py | 6 +++- plugins/lacrosse-scoreboard/manifest.json | 10 ++++-- plugins/ufc-scoreboard/CHANGELOG.md | 8 +++++ plugins/ufc-scoreboard/manager.py | 6 +++- plugins/ufc-scoreboard/manifest.json | 10 ++++-- .../test_live_content_log_throttle.py | 16 ++++++--- 21 files changed, 190 insertions(+), 30 deletions(-) diff --git a/plugins/baseball-scoreboard/CHANGELOG.md b/plugins/baseball-scoreboard/CHANGELOG.md index 954447ea..09854c3f 100644 --- a/plugins/baseball-scoreboard/CHANGELOG.md +++ b/plugins/baseball-scoreboard/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.54.4] - 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.54.3] - 2026-10-01 ### Changed @@ -27,6 +35,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 diff --git a/plugins/baseball-scoreboard/manager.py b/plugins/baseball-scoreboard/manager.py index e08e467d..6032f9f3 100644 --- a/plugins/baseball-scoreboard/manager.py +++ b/plugins/baseball-scoreboard/manager.py @@ -2575,7 +2575,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})" diff --git a/plugins/baseball-scoreboard/manifest.json b/plugins/baseball-scoreboard/manifest.json index 2cc0a25e..63564487 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.54.3", + "version": "1.54.4", "update_interval": 60, "author": "ChuckBuilds", "description": "Live, recent, and upcoming baseball games across MLB, MiLB, and NCAA Baseball with real-time scores and schedules", @@ -31,6 +31,12 @@ "branch": "main", "plugin_path": "plugins/baseball-scoreboard", "versions": [ + { + "version": "1.54.4", + "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.54.3", "released": "2026-10-01", @@ -818,7 +824,7 @@ "ledmatrix_min_version": "2.0.0" } ], - "last_updated": "2026-09-30", + "last_updated": "2026-10-02", "stars": 0, "downloads": 0, "verified": true, diff --git a/plugins/baseball-scoreboard/test_live_content_log_throttle.py b/plugins/baseball-scoreboard/test_live_content_log_throttle.py index 775c053d..510a94a2 100644 --- a/plugins/baseball-scoreboard/test_live_content_log_throttle.py +++ b/plugins/baseball-scoreboard/test_live_content_log_throttle.py @@ -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: /bin/python plugins/baseball-scoreboard/test_live_content_log_throttle.py @@ -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: @@ -122,10 +127,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(): diff --git a/plugins/basketball-scoreboard/CHANGELOG.md b/plugins/basketball-scoreboard/CHANGELOG.md index 990cbd10..b848530a 100644 --- a/plugins/basketball-scoreboard/CHANGELOG.md +++ b/plugins/basketball-scoreboard/CHANGELOG.md @@ -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 diff --git a/plugins/basketball-scoreboard/manager.py b/plugins/basketball-scoreboard/manager.py index 243f502a..fa2e330d 100644 --- a/plugins/basketball-scoreboard/manager.py +++ b/plugins/basketball-scoreboard/manager.py @@ -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}" diff --git a/plugins/basketball-scoreboard/manifest.json b/plugins/basketball-scoreboard/manifest.json index 7b0dc646..57182547 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.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", @@ -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", @@ -605,7 +611,7 @@ ], "stars": 0, "downloads": 0, - "last_updated": "2026-09-30", + "last_updated": "2026-10-02", "verified": true, "screenshot": "", "display_modes": [ diff --git a/plugins/football-scoreboard/CHANGELOG.md b/plugins/football-scoreboard/CHANGELOG.md index 43609811..5e1a31bc 100644 --- a/plugins/football-scoreboard/CHANGELOG.md +++ b/plugins/football-scoreboard/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [3.18.4] - 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. + ## [3.18.3] - 2026-10-01 ### Changed diff --git a/plugins/football-scoreboard/manager.py b/plugins/football-scoreboard/manager.py index a86e07b2..2d97da72 100644 --- a/plugins/football-scoreboard/manager.py +++ b/plugins/football-scoreboard/manager.py @@ -2339,7 +2339,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"nfl_live={nfl_live}, ncaa_live={ncaa_live} " f"(live games: {counts})" diff --git a/plugins/football-scoreboard/manifest.json b/plugins/football-scoreboard/manifest.json index 6d1c6075..e74b4fbc 100644 --- a/plugins/football-scoreboard/manifest.json +++ b/plugins/football-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "football-scoreboard", "name": "Football Scoreboard", - "version": "3.18.3", + "version": "3.18.4", "update_interval": 60, "author": "ChuckBuilds", "class_name": "FootballScoreboardPlugin", @@ -25,6 +25,12 @@ "ncaa_fb_live" ], "versions": [ + { + "version": "3.18.4", + "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": "3.18.3", "released": "2026-10-01", @@ -929,7 +935,7 @@ "ledmatrix_min_version": "2.0.0" } ], - "last_updated": "2026-09-30", + "last_updated": "2026-10-02", "stars": 0, "downloads": 0, "verified": true, diff --git a/plugins/football-scoreboard/test_live_content_log_throttle.py b/plugins/football-scoreboard/test_live_content_log_throttle.py index 4e763bb9..e04b463b 100644 --- a/plugins/football-scoreboard/test_live_content_log_throttle.py +++ b/plugins/football-scoreboard/test_live_content_log_throttle.py @@ -13,7 +13,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: /bin/python plugins/football-scoreboard/test_live_content_log_throttle.py @@ -32,14 +32,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: @@ -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(): diff --git a/plugins/hockey-scoreboard/CHANGELOG.md b/plugins/hockey-scoreboard/CHANGELOG.md index d8db92fd..09210e0c 100644 --- a/plugins/hockey-scoreboard/CHANGELOG.md +++ b/plugins/hockey-scoreboard/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.42.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.42.0] - 2026-10-01 ### Added diff --git a/plugins/hockey-scoreboard/manager.py b/plugins/hockey-scoreboard/manager.py index aef63489..dc7028c4 100644 --- a/plugins/hockey-scoreboard/manager.py +++ b/plugins/hockey-scoreboard/manager.py @@ -2346,7 +2346,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"nhl_live={nhl_live}, ncaa_mens_live={ncaa_mens_live}, " f"ncaa_womens_live={ncaa_womens_live}" diff --git a/plugins/hockey-scoreboard/manifest.json b/plugins/hockey-scoreboard/manifest.json index b775753e..a814f355 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.42.0", + "version": "1.42.1", "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.42.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.42.0", "released": "2026-10-01", @@ -681,7 +687,7 @@ "ledmatrix_min_version": "2.0.0" } ], - "last_updated": "2026-09-30", + "last_updated": "2026-10-02", "stars": 0, "downloads": 0, "verified": true, diff --git a/plugins/lacrosse-scoreboard/CHANGELOG.md b/plugins/lacrosse-scoreboard/CHANGELOG.md index 4581f0f4..a8510e90 100644 --- a/plugins/lacrosse-scoreboard/CHANGELOG.md +++ b/plugins/lacrosse-scoreboard/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.36.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.36.1] - 2026-10-01 ### Changed diff --git a/plugins/lacrosse-scoreboard/manager.py b/plugins/lacrosse-scoreboard/manager.py index e3adcfa6..6454f949 100644 --- a/plugins/lacrosse-scoreboard/manager.py +++ b/plugins/lacrosse-scoreboard/manager.py @@ -2175,7 +2175,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"ncaa_mens_live={ncaa_mens_live}, ncaa_womens_live={ncaa_womens_live}" ) diff --git a/plugins/lacrosse-scoreboard/manifest.json b/plugins/lacrosse-scoreboard/manifest.json index 0ae11790..38213534 100644 --- a/plugins/lacrosse-scoreboard/manifest.json +++ b/plugins/lacrosse-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "lacrosse-scoreboard", "name": "Lacrosse Scoreboard", - "version": "1.36.1", + "version": "1.36.2", "author": "ChuckBuilds", "description": "Live, recent, and upcoming NCAA men's and women's lacrosse games with real-time scores and schedules", "homepage": "https://github.com/ChuckBuilds/ledmatrix-plugins/tree/main/plugins/lacrosse-scoreboard", @@ -50,6 +50,12 @@ } ], "versions": [ + { + "version": "1.36.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.36.1", "released": "2026-10-01", @@ -580,7 +586,7 @@ "ledmatrix_min_version": "2.0.0" } ], - "last_updated": "2026-09-30", + "last_updated": "2026-10-02", "stars": 0, "downloads": 0, "verified": true, diff --git a/plugins/ufc-scoreboard/CHANGELOG.md b/plugins/ufc-scoreboard/CHANGELOG.md index 3fcf4ea3..e6b4a92a 100644 --- a/plugins/ufc-scoreboard/CHANGELOG.md +++ b/plugins/ufc-scoreboard/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.19.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.19.1] - 2026-10-01 ### Changed diff --git a/plugins/ufc-scoreboard/manager.py b/plugins/ufc-scoreboard/manager.py index d67b8038..1f0e02c1 100644 --- a/plugins/ufc-scoreboard/manager.py +++ b/plugins/ufc-scoreboard/manager.py @@ -973,7 +973,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 {ufc_live}: live_games={live_count}" ) diff --git a/plugins/ufc-scoreboard/manifest.json b/plugins/ufc-scoreboard/manifest.json index f9b22f1b..9dfc8e35 100644 --- a/plugins/ufc-scoreboard/manifest.json +++ b/plugins/ufc-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "ufc-scoreboard", "name": "UFC Scoreboard", - "version": "1.19.1", + "version": "1.19.2", "author": "LegoGuy1000", "contributors": [ { @@ -32,6 +32,12 @@ "default_duration": 15, "config_schema": "config_schema.json", "versions": [ + { + "version": "1.19.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.19.1", "released": "2026-10-01", @@ -379,7 +385,7 @@ "ledmatrix_min_version": "2.0.0" } ], - "last_updated": "2026-09-30", + "last_updated": "2026-10-02", "stars": 0, "downloads": 0, "verified": true, diff --git a/plugins/ufc-scoreboard/test_live_content_log_throttle.py b/plugins/ufc-scoreboard/test_live_content_log_throttle.py index c08764b2..471b1090 100644 --- a/plugins/ufc-scoreboard/test_live_content_log_throttle.py +++ b/plugins/ufc-scoreboard/test_live_content_log_throttle.py @@ -12,7 +12,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: /bin/python plugins/ufc-scoreboard/test_live_content_log_throttle.py @@ -31,14 +31,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: @@ -114,10 +119,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(): From 8a3d5bfab03585ebb590e6b6a0c9b48ccdbd50ef Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:25:00 -0400 Subject: [PATCH 3/3] Regenerate plugins.json Co-Authored-By: Claude Opus 5.5 --- plugins.json | 63 ++++++++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/plugins.json b/plugins.json index f918e3dc..213f9684 100644 --- a/plugins.json +++ b/plugins.json @@ -77,12 +77,11 @@ "plugin_path": "plugins/baseball-scoreboard", "stars": 0, "downloads": 0, - "last_updated": "2026-10-01", + "last_updated": "2026-10-02", "verified": true, "screenshot": "", - "latest_version": "1.54.3", - "ledmatrix_min_version": "3.8.0", - "commit": "56e3f7a0070a26bd7b3fcd80b0214445ded480ac" + "latest_version": "1.54.4", + "ledmatrix_min_version": "3.8.0" }, { "id": "basketball-scoreboard", @@ -104,12 +103,11 @@ "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", - "ledmatrix_min_version": "3.8.0", - "commit": "56e3f7a0070a26bd7b3fcd80b0214445ded480ac" + "latest_version": "1.41.2", + "ledmatrix_min_version": "3.8.0" }, { "id": "calendar", @@ -129,12 +127,11 @@ "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", - "ledmatrix_min_version": "3.3.0", - "commit": "53e42a489c235fc99246c309a44980cdf50a5b9e" + "latest_version": "1.2.10", + "ledmatrix_min_version": "3.3.0" }, { "id": "christmas-countdown", @@ -280,12 +277,11 @@ "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", - "ledmatrix_min_version": "3.8.0", - "commit": "e5dd1c3dc87dcd0dea00c9209feab6e9138471c4" + "latest_version": "3.18.4", + "ledmatrix_min_version": "3.8.0" }, { "id": "geochron", @@ -382,13 +378,12 @@ "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" + "ledmatrix_min_version": "3.8.0" }, { "id": "lacrosse-scoreboard", @@ -408,13 +403,12 @@ "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" + "ledmatrix_min_version": "3.8.0" }, { "id": "birdnet-go", @@ -762,12 +756,11 @@ "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", - "ledmatrix_min_version": "2.0.0", - "commit": "62fa95853476715b833abfe0b207d4f50876b6b4" + "latest_version": "3.1.4", + "ledmatrix_min_version": "2.0.0" }, { "id": "pga-tour-leaderboard", @@ -1004,13 +997,12 @@ "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" + "ledmatrix_min_version": "3.8.0" }, { "id": "weather", @@ -1340,13 +1332,12 @@ "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" + "ledmatrix_min_version": "3.2.0" }, { "id": "ledmatrix-nascar",