Skip to content

fix(ledmatrix-elections): load fixtures as UTF-8, and finish the docs - #441

Merged
ChuckBuilds merged 1 commit into
mainfrom
docs/elections-readme
Sep 4, 2026
Merged

fix(ledmatrix-elections): load fixtures as UTF-8, and finish the docs#441
ChuckBuilds merged 1 commit into
mainfrom
docs/elections-readme

Conversation

@ChuckBuilds

Copy link
Copy Markdown
Owner

The bug

check_plugin.py reported 16 FAIL of 32 on Windows. manager.py loaded the bundled NYT fixture with a bare open(), so Python used the platform default encoding — cp1252 — and the read raised part-way through:

test_mode: NYT fixture load failed: 'charmap' codec can't decode byte 0x9d
in position 1744733: character maps to <undefined>

The fixture is UTF-8 and carries non-ASCII candidate names. The exception is caught and logged, test_mode then finds no races, and every panel renders blank. On Linux the default is UTF-8, so the path works and nothing is visible — which is why it survived.

Confirmed pre-existing on main by stashing: the same 16 failures. Both call sites (manager.py, and test_elections.py's load() helper) now pass encoding="utf-8". The harness goes 16 FAIL → 0 FAIL. Filed as #440.

Docs

The README's only image was a placeholder pointing at #. It now has real renders of both display modes and of four panel sizes.

display modes

Twelve settings were missing from the configuration table — the two district numbers, both scroll knobs, and the eight provider endpoints. All 35 schema leaves are now documented (token audit: 0 undocumented).

Two settings the code reads are declared nowhere in config_schema.json: test_mode (manager.py:64) and providers.ca_sos.override_nyt_votes (manager.py:80). Both work, but neither reaches the web UI, so they are hand-edit only. The old README documented both as if the form offered them; it now says otherwise. Same class as #427 and #429.

How the renders work

They replay a small NYT-shaped feed rather than seeding plugin state, because the plugin parses that JSON into Race and Candidate dataclasses and the renderer uses attribute access — a dict pushed onto self.races would not survive. So the images exercise the real provider parse, including the first_name/last_name extraction (my first attempt used name_display and the cards rendered candidate ids).

One tooling change

_docs_frame_runner.py took no --display-mode, so a multi-mode plugin could not have a scrolling shot — the ticker failed with unrecognized arguments. It now accepts one, with the same TypeError fallback render_plugin.py uses. Same shape as the earlier --skip-update fix.

Checks

  • check_plugin.py: 32/32 PASS, zero FAIL (was 16 FAIL)
  • Config-token audit: 35 schema leaves, 0 undocumented
  • Every example config checked against the schema
  • Manifest bumped 1.2.1 -> 1.2.2, plugins.json regenerated

🤖 Generated with Claude Code

check_plugin.py reported 16 FAIL of 32 on Windows. manager.py loaded the
bundled NYT fixture with a bare open(), so Python used the platform
default encoding -- cp1252 -- and the read raised part-way through:

  test_mode: NYT fixture load failed: 'charmap' codec can't decode byte
  0x9d in position 1744733

The fixture is UTF-8 and carries non-ASCII candidate names. The
exception was caught and logged, test_mode then found no races, and
every panel rendered blank. On Linux the default is UTF-8, so the path
works and nothing is visible -- which is why it survived. Confirmed
pre-existing on main by stashing. Both call sites, manager.py and
test_elections.py's load() helper, now pass encoding="utf-8". The
harness goes 16 FAIL -> 0 FAIL. Filed as #440.

The README's only image was a placeholder pointing at "#". It now
carries real renders of both display modes and of four panel sizes.
Twelve settings were undocumented or absent from the table -- the two
district numbers, both scroll knobs, and the eight provider endpoints --
so all 35 schema leaves are now covered, verified by a token audit.

Also recorded: test_mode and providers.ca_sos.override_nyt_votes are
read by the code but declared nowhere in config_schema.json, so they
never reach the web UI and can only be set by hand. Both work; the
README now says they are hand-edit only rather than implying the form
offers them.

The renders replay a small NYT-shaped feed rather than seeding plugin
state, because the plugin parses that JSON into Race and Candidate
dataclasses and the renderer uses attribute access -- a dict pushed onto
self.races would not survive. So the images exercise the real parse.

Rendering the scrolling ticker needed one tooling change: the frame
runner took no --display-mode, so a multi-mode plugin could not have a
scrolling shot. It now accepts one, with the same TypeError fallback
render_plugin.py uses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 444b4ea5-ba12-499e-9ec4-79d18ac6cf7c


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 18 complexity

Metric Results
Complexity 18

View in Codacy

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.

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
ChuckBuilds merged commit 941f8e2 into main Sep 4, 2026
4 checks passed
@ChuckBuilds
ChuckBuilds deleted the docs/elections-readme branch September 4, 2026 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant