fix(docs-tooling): let shot attrs reach nested plugin state - #423
Merged
Conversation
The attrs seam could only set attributes on the plugin instance itself. The sports scoreboards keep their per-mode state on sub-managers rather than on the plugin -- self._managers["live"].current_game -- so a shot could not reach the game it wanted to draw, and every render came out blank. That architecture is shared by the six scoreboards still to be documented. An attrs key may now be a dotted path. Each segment is tried as a mapping key and as an attribute, so both self._managers["live"] and self.live_manager resolve, and the final assignment uses __setitem__ when the owner is a mapping that does not already carry the name. A path that does not exist is skipped rather than raised: a shots file shared across panel sizes should not fail because one plugin shapes its state differently. Verified in isolation -- a nested path sets through a dict of objects, a plain top-level name still sets, and a missing path is a no-op. The on-air and calendar renders, which both use flat attrs, still reproduce byte-identically under --check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 18 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
A shots file can only carry JSON, but plugin state is not all strings and lists. The sports renderers call logo_path.parent, so a logo field seeded as a string raises AttributeError inside _load_and_resize_logo; the exception is caught per-team, the card silently fails to draw, and the panel comes out blank with nothing in the harness output to say why. Values whose key ends in _path now become pathlib.Path, alongside the existing rule that a three-integer list becomes a tuple. Both now recurse into nested dicts and lists, so a seeded game record is converted throughout rather than only at the top level. Verified against nrl-scoreboard: seeding its live sub-manager and calling that manager's display() directly renders 1892 lit pixels, where the same seed with string paths rendered none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pairs with LEDMatrix#522, which adds --display-mode to the core's
render_plugin.py. A shot may now name which screen to draw:
{"name": "live", "display_mode": "nrl_live"}
Without it, a plugin declaring several modes renders whichever its
no-argument display() path picks -- which for the sports scoreboards is
nothing at all, since their state lives on per-mode sub-managers.
The key is omitted from the command line unless set, so every existing shot
list behaves exactly as before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 4, 2026
ChuckBuilds
added a commit
that referenced
this pull request
Sep 4, 2026
…renders (#442) The README explained the MQTT side well -- payload format, topic wildcards, five Home Assistant examples -- but never listed the plugin's own settings, and had no images. All 20 schema leaves are now documented, verified by a token audit, with three renders. The five that were missing: mqtt.client_id, mqtt.keepalive, text.scroll_gap_width, update_interval and display_duration. display_duration is worth its own note. The core reads it as a plugin's screen time (base_plugin.py:406), but this plugin overrides get_display_duration() and returns display.default_duration instead, so setting the root key alone changes nothing. The two defaults agree at 10s, which hides it until someone sets one and not the other. The README now says which one to set. update_interval, by contrast, is live: the core schedules update() from it (plugin_manager.py:809). Both looked dead to a grep of the plugin alone -- neither name appears in manager.py -- and grepping the core is what separated the two. Also documented: the two font routes. text.font_path takes any path, customization.message_text.font offers the bundled faces as a dropdown, and the customization block wins when its font loads. Colours come from text either way, since customization carries no colour keys. check_plugin.py passes 8/8 with no FAIL. Carries the docs-tooling changes from #423 and the frame-runner --display-mode change from #441, which these renders depend on. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds
added a commit
that referenced
this pull request
Sep 4, 2026
…ers (#439) The README covered the five top-level settings well but said nothing about the nine customization keys -- three text elements, each with a font, size and colour -- which is the whole of the plugin's appearance control. All 14 schema leaves are now documented, verified by a token audit, with three renders. The renders also make a practical point the README could not: on a 128-wide panel the default PressStart2P at 8px runs past the edge once the view count reaches seven digits. Dropping the three lines to 4x6-font.ttf at 6 fits, and the side-by-side shows both. Also corrected: the logo lookup is described as it actually works -- assets/youtube_logo.png resolved against the process working directory first, then the LEDMatrix install root -- rather than "in project root". And the API-key failure has a visible symptom worth naming, the panel showing "YT: Update API Key", which the old troubleshooting section did not mention. Kept the ChuckBuilds links block, the API-quota arithmetic, and the secrets-file split for the key. check_plugin.py passes with no FAIL. Carries the docs-tooling changes from #423, which this render depends on. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds
added a commit
that referenced
this pull request
Sep 4, 2026
…#436) The README documented six of the ten leagues by pasting a near-identical config block for each, covered a fraction of the 636 settings, and had no images. It now documents every schema leaf -- verified by a token audit -- with six renders, and keeps the ChuckBuilds links block, the supported-league table, the custom-league codes, the World Cup section, the TEAMS.md pointer and the non-favorite dwell table. Ten league blocks, 53 leaves each, identical except enabled. One <league> section collapses 477 of the 636 leaves. leagues.<slug>.display_options does nothing. Nothing in this plugin or in the core reads it; _adapt_config_for_manager takes show_records, show_ranking and show_odds from the plugin config root instead. Proved by render in both directions -- toggling the per-league copy gives byte-identical panels, toggling the root key draws and removes the records. That is 30 settings, and it is the opposite precedence to every other scoreboard here, so a config pattern carried across sets the wrong one. Filed as #435; the README documents the behaviour that exists. scroll_settings in the same blocks looks equally unreferenced but is live -- scroll_display.py delegates to the core base class, which reads it. Grepping the core is what separated the two. Soccer has the richest status model of the set, and it was undocumented beyond a list in the World Cup section: 1H, 2H, HALF, ET1, ET2, ETH and PEN while live, then Final, F/ET or F/Pen. Both now have renders. check_plugin.py passes 24/24 with no FAIL. Carries the docs-tooling changes from #423, which this render depends on. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds
added a commit
that referenced
this pull request
Sep 4, 2026
…ders (#434) The README covered a fraction of the 274 settings and had no images. It now documents every schema leaf -- verified by a token audit -- with five renders, and keeps the ChuckBuilds links block, the four team abbreviation lists, the background-service notes, the NCAA season-data caveat, the game-limits behaviour and the non-favorite dwell table. Four leagues. Only two defaults differ across them -- enabled, and show_ranking, which is on for the two college leagues -- so that is a short table plus one <league> section rather than 200 duplicated rows. The two NCAA blocks carry a march_madness section the NBA and WNBA do not have: seeds in place of AP rankings, the round abbreviation, the bracket region, a tournament-game cap, and a tournament_mode override that is otherwise decided from the calendar. It now has its own section and a three-way render. Also newly documented: - The mode toggles are show_live / show_recent / show_upcoming here, as in football, not live / recent / upcoming as in hockey and lacrosse. A display_modes block copied across silently sets nothing. - mode_durations has no live_mode_duration in this plugin, unlike the football, hockey and lacrosse scoreboards. - other_games_min_quality is live for the college leagues, which have a poll, and inert for the NBA and WNBA, which do not. check_plugin.py passes 24/24 with no FAIL. Carries the docs-tooling changes from #423, which this render depends on. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds
added a commit
that referenced
this pull request
Sep 4, 2026
…#433) The README ran to 894 lines and still covered a fraction of the 226 settings, with no images. It now documents every schema leaf -- verified by a token audit -- with five renders, and keeps the ChuckBuilds links block, the rotation and resume semantics, the non-favorite dwell table, the exclude-teams note and all three example configurations. Three leagues, 53 leaves each. The two NCAA blocks are identical; the NHL block differs in nine defaults, so that is a table rather than 106 duplicated rows. Two settings do not work, and the README now says so rather than describing them as features: - show_powerplay is resolved into the manager config and power_play is extracted onto the game dict, but nothing in this plugin or in the core draws either. No value changes the render. Filed as #431. - Scroll and Vegas cards gate the shot line on a flat show_shots key at the league root, which the schema never declares, so it reads False whatever show_shots_on_goal is set to. Switch mode is unaffected. Filed as #432. Also documented: the per-league display_options copy wins over the defaults copy and several of its defaults differ -- show_shots_on_goal is false under defaults and true under nhl -- which presents as a setting that does nothing. check_plugin.py passes with no FAIL. Carries the docs-tooling changes from #423, which this render depends on. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds
added a commit
that referenced
this pull request
Sep 4, 2026
…rs (#430) * docs(football-scoreboard): document all 177 settings, with real renders The README was long but incomplete: it documented perhaps half the settings, carried a "Version History" section still claiming v2.0.7 against a manifest at 3.4.1, and had no images. It now covers every schema leaf -- verified by a token audit -- with seven renders. Kept in full: dynamic team resolution and the AP_TOP_* warning, the rotation and resume semantics, the measured turnover example, the ranked/divisions reasoning including why broadcast was retired, the worked example, and the celebration banner table. Three things it did not say: - The mode toggles here are show_live / show_recent / show_upcoming, not live / recent / upcoming as in every other scoreboard. A display_modes block copied across silently sets nothing. - Five defaults differ between nfl and ncaa_fb -- enabled, live_game_duration, upcoming_games_to_show, other_upcoming_games_to_show and show_ranking. Documented as a table rather than duplicating 52 rows per league. - The live card's down-and-distance, red-zone colour, possession icon and timeout pips were undocumented, and are the plugin's most distinctive output. layout_mode: adaptive now has a side-by-side render against classic on a 128x64 panel, which is where the difference actually shows. Dropped the stale version-history section rather than correcting it; the manifest versions array is the real record. check_plugin.py passes 24/24 with no FAIL. Carries the docs-tooling changes from #423, which this render depends on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(football-scoreboard): keep the ChuckBuilds links block The rewrite collapsed the YouTube, Instagram, Discord and sponsorship links into a shields badge. They are the project owner support links, not decoration, so restore them verbatim at the top. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds
added a commit
that referenced
this pull request
Sep 4, 2026
) The README covered roughly 55 of the plugin's 131 settings and had no images at all. It now documents every schema leaf -- verified by a token audit against config_schema.json -- and shows each of the three display modes, the show_records toggle, and the card at four panel sizes. Three things the old README got wrong or left out: - The seven selection limits (recent_games_to_show and friends) are declared twice, at the config root and under game_limits, and both are read. game_limits wins where present. Same for show_records / show_ranking / show_odds under display_options, and for show_favorite_teams_only under filtering. Setting the root copy while the nested one exists looks like a setting that does nothing. - other_games_min_quality's documented choices included "broadcast", which the enum does not offer. - The scroll_settings block is unreachable (#422); the README now says so rather than describing six knobs that do nothing. mode_durations and customization.favorite_result_colors are read by the core, not by this plugin, so they look dead to a grep of this tree. The README now says where they are read. Renders come from docs/assets/nrl-scoreboard/shots.json. The fixture seeds games onto the per-mode sub-managers rather than the plugin -- every mode's display() returns early unless games_list is populated, and the live path also needs live_games -- and passes display_mode, without which the plugin's no-argument path selects nothing and draws a blank panel. The bundled PEN/MEL logo files are grey placeholders, so the crests render as blocks; the README says so. check_plugin.py passes on all eight panel sizes for all three modes. Carries the docs-tooling changes from #423 (display_mode shot key, dotted-path attrs, *_path coercion), which this render depends on. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds
added a commit
that referenced
this pull request
Sep 4, 2026
…rs (#428) * docs(lacrosse-scoreboard): document all 170 settings, with real renders The README documented a fraction of the plugin's 170 settings and had no images. It now covers every schema leaf -- verified by a token audit -- and shows the three men's display modes, both leagues side by side, the show_records toggle, and the card at four panel sizes. The two league blocks are identical in every setting except one: ncaa_mens defaults to enabled and ncaa_womens does not. Documenting one <league> table rather than two identical sets keeps 104 of the 170 leaves readable instead of duplicated. Two things the old README did not say: - display_options.show_records and show_ranking default to true per league, while defaults.show_records and defaults.show_ranking default to false, and the per-league copy wins. Because the UI writes schema defaults on save, changing the defaults copy appears to do nothing. Both the settings reference and troubleshooting now say so. - The favorites key here is teams.favorite_teams_only, not show_favorite_teams_only as in the single-league scoreboards, and it defaults to off rather than on. other_games_min_quality is live in this plugin -- NCAA lacrosse has a national poll to rank against -- unlike in nrl, where it is inert. other_games_divisions stays inert; it is a college football taxonomy. Renders come from docs/assets/lacrosse-scoreboard/shots.json. Lacrosse keeps one manager per league and mode as a plain attribute rather than in a dict, and only builds them when that league's enabled flag is set, so the fixture turns both leagues on. Real ESPN crests, not placeholders. check_plugin.py passes with no FAIL. Carries the docs-tooling changes from #423, which this render depends on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(lacrosse-scoreboard): keep the lax_ prefix upgrade notice The rewrite dropped the breaking-change notice from 1.1.0, when the display modes gained a lax_ prefix to stop colliding with the NCAA hockey modes. Anyone still holding an old pin in display_durations or rotation_order needs it, and the CHANGELOG mapping is the only other place it is written down. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(lacrosse-scoreboard): repair the manifest after the merge The merge from main left the 1.24.2 entry holding both sides bodies -- two notes keys, two changelog keys, and a missing comma between them -- so manifest.json was not valid JSON and the version gate could not read it. Both sides had added a 1.24.2: main from the font fix in #409, this branch from the README rewrite. Rebuilt from main manifest, which is intact, with the docs change moved to a new 1.24.3 entry above it. That keeps main 1.24.2 exactly as merged and follows the shape the other docs PRs used on main -- the new entry carries the previous entry notes and changelog plus its own changes array. plugins.json regenerated: latest_version 1.24.3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The
attrsseam (added in #386) could only set attributes on the plugin instance itself:The sports scoreboards keep their per-mode state on sub-managers, not on the plugin:
so a shot could not reach the game it wanted to draw, and the render came out blank. That architecture is shared by the six scoreboards still to be documented (nrl, lacrosse, football, hockey, basketball, soccer), so this blocks all of them.
What changed
An attrs key may now be a dotted path:
Each segment is tried as a mapping key and as an attribute, so both
self._managers["live"]andself.live_managerresolve. The final assignment uses__setitem__when the owner is a mapping that does not already carry the name.A path that does not exist is skipped rather than raised — a shots file shared across panel sizes should not fail because one plugin shapes its state differently.
Verification
Exercised the resolver directly against a stand-in shaped like the scoreboards:
And for regression, the two merged plugins that use flat attrs still reproduce byte-identically:
Scope
Tooling only — no plugin, schema or manifest touched. This does not by itself make
nrl-scoreboardrender: its sub-manager needs more seeded state thancurrent_gamealone, which is the next thing to work out.🤖 Generated with Claude Code