Skip to content

fix(on-air): find the real LEDMatrix root when loading the sign font - #386

Merged
ChuckBuilds merged 1 commit into
mainfrom
docs/on-air-readme
Sep 3, 2026
Merged

fix(on-air): find the real LEDMatrix root when loading the sign font#386
ChuckBuilds merged 1 commit into
mainfrom
docs/on-air-readme

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Sep 3, 2026

Copy link
Copy Markdown
Owner

The bug

The sign picks a TTF out of the LEDMatrix assets/fonts folder and draws at 80% of panel height. It located that folder as two levels above the plugin file:

plugin_dir   = Path(__file__).parent
project_root = plugin_dir.parent.parent

That holds when the plugin sits at <LEDMatrix>/plugin-repos/<id>. The plugins directory is configurable, though, and anywhere outside the core tree the search finds nothing, falls through to a built-in font, and the sign renders a fraction of its intended size — with a debug log line and nothing else.

It now asks the imported core where it lives (Path(src.__file__).parent.parent), keeping the old guess and the working directory as fallbacks.

This is also what made the plugin impossible to document honestly: rendered from this repo, every screenshot came out in the small fallback font rather than what a real install shows.

README

Real rendered screenshots throughout, config.json keys for all 16 settings, and three corrections found by reading the code against the prose.

1. The label claims are not implemented. The README said:

The label (max 16 chars) replaces "ON AIR" on the display. On panels 128 px wide or larger, "ON AIR" appears as the header with your label as a subtitle below it.

There is no length bound anywhere in manager.py (the schema caps the configured Sign Text at 32; a payload label is unbounded) and no header/subtitle layout — display() draws the label alone, centred, scaled to fit.

2. Five automation examples set the wrong field. They send:

{"state": "on", "label": "IN MEETING", "color": [255, 140, 0]}

In a payload, color is the text colour and bg is the background:

raw_tc = data.get('color') or data.get('text_color')
raw_bg = data.get('bg')    or data.get('background_color')

So those examples produce orange lettering on the default red, not an amber sign. The Payload Reference section already documented this correctly; the Advanced examples contradicted it. Both are now bg, and the README shows the difference:

color versus bg

3. The off state. Documented for the first time — it draws a black frame rather than dropping out of the rotation, deliberately, so the display controller does not flash its "Initializing" screen while the stop request is processed.

Tooling

Shots can now declare attrs: runtime state applied to the plugin instance after the core's loader builds it, via a wrapped loader in the docs shim. An event-driven plugin holds its interesting state in memory — put there by an MQTT message — so no configuration reaches it and a render would otherwise only ever show the idle frame. The same seam will cover mqtt-notifications and the other event-driven plugins.

Checks

  • check_plugin.py: 8/8 pass (the harness renders the idle frame, so the font change does not move it)
  • 16/16 schema properties documented by key; config-token audit drops nothing
  • render_docs_assets.py --check: all five images reproduce

1.2.5 → 1.2.6.

🤖 Generated with Claude Code

The sign looks for a TTF under the LEDMatrix assets folder, resolving the root
as two levels above the plugin file. That is right when the plugin sits at
<LEDMatrix>/plugin-repos/<id>, and wrong whenever the plugins directory is
configured somewhere else -- the search then finds nothing and the sign drops
to a built-in font a fraction of the intended size, with no error. It now asks
the imported core where it lives and keeps the old guess and the working
directory as fallbacks.

That is also what made this plugin impossible to document honestly: rendered
from this repo, the sign came out in tiny default lettering rather than the
80%-of-panel-height face a real install shows.

README: real rendered screenshots throughout, the config.json key for all 16
settings, and three corrections.

- "The label (max 16 chars) replaces ON AIR ... on panels 128px wide or larger
  ON AIR appears as the header with your label as a subtitle below it" -- there
  is no length bound and no header/subtitle layout in the code. The label is
  drawn on its own, centred, scaled to fit.
- Five automation examples sent {"color": [255, 140, 0]} where they meant the
  background. In a payload, "color" is the text colour and "bg" is the
  background, so those examples produce orange lettering on the default red
  rather than an amber sign. The payload reference already said this; the
  examples contradicted it.

Tooling: shots can now declare "attrs", runtime state applied to the plugin
instance after the core's loader builds it. An event-driven plugin like this
one holds its interesting state in memory, put there by an MQTT message, so no
configuration reaches it and a documentation render would only ever show the
idle black frame.

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

coderabbitai Bot commented Sep 3, 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: 8bd90b1c-015b-43f3-9a22-0c8887391476


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 25 complexity

Metric Results
Complexity 25

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
ChuckBuilds merged commit f40214a into main Sep 3, 2026
4 checks passed
ChuckBuilds added a commit that referenced this pull request Sep 3, 2026
The README described the display with three hand-drawn text sketches:

      03/15 2:30pm

      Team Meeting

They are now real plugin output at the true panel size -- a timed event, an
all-day event, a long title being ellipsized, and the empty state -- plus a
panel-size sheet.

Also documents google_auth, the one setting the reference did not mention. It
is not a value you type: it is the web UI's "Connect Your Google Account"
button (x-widget: google-oauth). The two combined customization rows are split
so each key appears on its own, which takes the plugin to 13 of 13 settings
documented by key.

Rendering these needed the shots "attrs" seam from #386, which is not on main
yet; the identical change to sitecustomize.py and render_docs_assets.py is
included here so this branch can render on its own.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request Sep 3, 2026
…390)

The "Key options" table listed 7 of the 25 settings and pointed at
config_schema.json for the rest, which meant the two thirds that shape what
actually appears on a card -- show_dashboard, show_delivered,
show_delivery_images, include_delivered, the scroll settings, the timezone
override -- were undocumented. All 25 are now covered, grouped by connection,
card contents, and rotation, with the advanced ones marked.

The plugin had no images. It ships a mock provider precisely so the layout can
be previewed without credentials, so every screenshot here comes from that:
the three card types, four panel sizes, and before/after pairs for
show_dashboard, highlight_today, show_carrier_logo and show_delivered. Each
pair was hash-compared to confirm the setting actually changes the render.

Adds the plugin to the root README, where it was missing (#385). That creates
a Productivity section, which #384 also adds for pomodoro-timer -- whichever
lands second needs the two rows merged into one section.

Rendering used the shots "attrs" seam from #386, not yet on main; the
identical tooling change is included so this branch renders on its own.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request Sep 3, 2026
Both sides added an independent option to the documentation renderer, so the
resolution keeps both: attrs (from #386, runtime state for event-driven
plugins) and hostname (this branch, pinning socket.gethostname so a panel that
prints the device name renders the same on any machine).

Verified after resolving: --check reproduces this branch's four hostname-pinned
images and on-air's four attrs-driven ones byte-identically, so neither seam
was lost in the merge. Also added both options to the shot-key docstring, which
listed neither.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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