Skip to content

fix(baseball-scoreboard): stop rendering game start times in UTC - #228

Open
ChuckBuilds wants to merge 2 commits into
mainfrom
claude/baseball-plugin-timezone-nx7cka
Open

fix(baseball-scoreboard): stop rendering game start times in UTC#228
ChuckBuilds wants to merge 2 commits into
mainfrom
claude/baseball-plugin-timezone-nx7cka

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Pull Request

Summary

The baseball scoreboard rendered every game start time in UTC (a 6:45pm Central first pitch showed as 11:45PM) even on setups where the clock plugin displayed the correct local time. The plugin looked for the LEDMatrix global timezone only on cache_manager.config_manager; on cores that expose config_manager via the plugin manager instead, that lookup came back empty and resolution fell through to UTC. This centralizes timezone resolution, adds the missing fallbacks, and makes the timezone setting an actual, documented config option.

Type of change

  • Bug fix in an existing plugin
  • New plugin (also fill out the SUBMISSION.md checklist below)
  • New feature for an existing plugin
  • Documentation only
  • Repo-wide change (registry script, hook, top-level docs)

Plugin(s) affected

baseball-scoreboard (1.19.3 → 1.20.0)

Related issues

N/A — reported directly.

What changed

New baseball_timezone.py — single resolver shared by the switch-mode scorebug (sports.py), the scroll-mode game card (game_renderer.py) and the plugin manager. First valid value wins:

  1. timezone in the plugin's own config (explicit override)
  2. plugin_manager.config_managerthis is the lookup that was missing; clock-simple and geochron check it first, which is why they were correct on the same device
  3. cache_manager.config_manager (what the plugin used to check exclusively)
  4. Host system zone — TZ, /etc/timezone, /etc/localtime
  5. UTC

Also supports older cores that expose load_config()/get_config() rather than get_timezone(), skips blank/invalid zone names with a warning instead of adopting them, and never propagates an exception out of a core config manager.

Two related fixes make an explicit override actually usable:

  • config_schema.json never declared timezone, and the schema sets additionalProperties: false — so hand-editing the key in the saved config was discarded. It is now a string property under Advanced Settings, defaulting to "" (follow the global timezone).
  • The manager mutated the config dict the core handed it (self.config["timezone"] = ...), which persisted a bogus "timezone": "UTC" into the user's saved plugin config. The resolved value now lives on the instance and reaches sub-components through a copy.

Test plan

  • Loaded the plugin in LEDMatrix on real hardware
  • Loaded the plugin in LEDMatrix emulator mode (EMULATOR=true python3 run.py)
  • Rendered the plugin in the dev preview server (scripts/dev_server.py)
  • Verified the web UI configuration form against the schema
  • Other — see below

New test_timezone_resolution.py (10 cases, all passing) covers the resolution order, the plugin-level override winning, the plugin_manager lookup that was missing, the legacy load_config() shape, a get_timezone() that raises, blank/invalid values being skipped, the system-zone backstop, the UTC last resort, and an end-to-end conversion of the reported 23:45Z → 6:45PM case.

Also run:

  • python scripts/check_module_collisions.py — OK across 41 plugins (new module is plugin-prefixed per the deferred-import rule)
  • Full plugins/baseball-scoreboard/test_*.py suite — every test that passes on main in this environment still passes. test_baseball_plugin.py, test_config_reload.py, test_score_antialiasing.py and test_test_mode_live_games.py fail identically before and after this change (they need the core src package and font assets that aren't present here).

Not verified on hardware or in the emulator — worth a sanity check on a real panel before release.

Required for plugin changes

  • Bumped version in plugins/<id>/manifest.json (1.19.3 → 1.20.0)
  • class_name in manifest.json matches the actual class in manager.py exactly (unchanged)
  • entry_point matches the real file (unchanged)
  • Updated the plugin's README.md if config keys changed — documents timezone plus a troubleshooting entry for the UTC symptom
  • config_schema.json is the source of truth for the web UI form — timezone added with default, description and x-advanced
  • Pre-commit hook ran successfully (auto-synced plugins.json)

Checklist

  • My commits follow the message convention in CONTRIBUTING.md
  • I read CONTRIBUTING.md and CODE_OF_CONDUCT.md
  • I've not committed any secrets

Notes for reviewer

Existing installs need one manual step. Anyone who ran an affected version already has "timezone": "UTC" written into their saved baseball config by the old write-back bug. Since an explicit plugin-level value wins, that stale "UTC" will keep overriding the fix until it's cleared or set to the right zone. It's now editable in the web UI under Advanced Settings, and the README troubleshooting entry calls it out. I deliberately avoided auto-healing a stored "UTC" — there's no way to tell it apart from a deliberate choice, and silently overriding it would break users who genuinely want UTC.

sports.py calls _get_timezone() once per game during extraction. In the normal path the resolver returns on the first candidate (the manager already injected the resolved name into the manager config), so no filesystem probing happens per game; pytz.timezone() is internally memoized.


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added an Advanced Settings option to specify an IANA timezone for game start times.
    • Game times now follow the configured timezone, global/system timezone, or UTC fallback.
    • Updated the Baseball Scoreboard plugin to version 1.20.0.
  • Bug Fixes

    • Fixed game times incorrectly displaying in UTC.
    • Prevented resolved timezone values from being written back to saved configuration.
  • Documentation

    • Added timezone configuration guidance and troubleshooting information.

The plugin looked for the LEDMatrix global timezone only on
cache_manager.config_manager. On cores that expose config_manager via the
plugin manager instead, that lookup came back empty and every start time was
drawn in UTC -- a 6:45pm Central first pitch rendered as 11:45PM -- while
clock-simple and geochron, which check plugin_manager first, showed the correct
local time on the same device.

Timezone resolution now lives in one place (baseball_timezone.py) shared by the
switch-mode scorebug, the scroll-mode game card and the plugin manager. Order:
plugin override, plugin_manager.config_manager, cache_manager.config_manager,
host system zone (TZ, /etc/timezone, /etc/localtime), then UTC.

Two related fixes make an explicit override actually usable:

- config_schema.json never declared `timezone` and sets
  additionalProperties: false, so hand-editing the key in the saved config had
  no effect. It is now a documented string property under Advanced Settings.
- The manager assigned self.config["timezone"], mutating the dict the core
  handed it and persisting a bogus "timezone": "UTC" into the user's saved
  plugin config. The resolved value is kept on the instance and passed to
  sub-components via a copy instead.

Adds test_timezone_resolution.py covering the resolution order, invalid and
blank values, a raising config_manager, and the system-zone backstop.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QSnspNZceRCdpUtJh2Co6e
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ChuckBuilds, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bbfdf3d9-c011-4299-a528-927f229f70e7

📥 Commits

Reviewing files that changed from the base of the PR and between 92329f9 and 9068874.

📒 Files selected for processing (2)
  • plugins/baseball-scoreboard/baseball_timezone.py
  • plugins/baseball-scoreboard/test_timezone_resolution.py
📝 Walkthrough

Walkthrough

Baseball Scoreboard 1.20.0 centralizes timezone resolution, adds an advanced timezone configuration property, updates manager and rendering paths to use resolved timezones without mutating saved configuration, adds regression tests, and documents the release.

Changes

Baseball Scoreboard timezone handling

Layer / File(s) Summary
Centralized timezone resolution and validation
plugins/baseball-scoreboard/baseball_timezone.py, plugins/baseball-scoreboard/test_timezone_resolution.py
Adds ordered timezone resolution across plugin configuration, config managers, host sources, and UTC, with validation, legacy config support, exception handling, and regression tests.
Runtime configuration and rendering integration
plugins/baseball-scoreboard/config_schema.json, plugins/baseball-scoreboard/manager.py, plugins/baseball-scoreboard/game_renderer.py, plugins/baseball-scoreboard/sports.py
Adds the advanced timezone schema property, stores resolved values separately from persisted configuration, passes them to subcomponents, and uses shared resolution for game-time rendering.
Version metadata and configuration documentation
plugins.json, plugins/baseball-scoreboard/CHANGELOG.md, plugins/baseball-scoreboard/README.md, plugins/baseball-scoreboard/manifest.json
Updates version metadata to 1.20.0 and documents timezone settings, fallback behavior, troubleshooting, and release changes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Manager
  participant TimezoneResolver
  participant ConfigManagers
  participant Renderer
  Manager->>TimezoneResolver: Resolve effective timezone
  TimezoneResolver->>ConfigManagers: Check plugin and core configuration
  ConfigManagers-->>TimezoneResolver: Return candidate timezone
  TimezoneResolver-->>Manager: Return validated timezone
  Manager->>Renderer: Pass resolved timezone configuration
  Renderer->>TimezoneResolver: Resolve timezone for game start
  TimezoneResolver-->>Renderer: Return tzinfo
  Renderer->>Renderer: Convert UTC start time to local time
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: fixing baseball-scoreboard game start times rendering in UTC.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/baseball-plugin-timezone-nx7cka

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

codacy-production Bot commented Jul 28, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 48 complexity

Metric Results
Complexity 48

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/baseball-scoreboard/baseball_timezone.py`:
- Around line 107-118: Update the timezone candidate resolution around the
candidates collection and its consuming loop to evaluate sources lazily,
yielding each candidate only when reached. Preserve the existing priority
order—plugin config, plugin-manager config, cache-manager config, then system
timezone—and stop evaluating lower-priority sources once a valid timezone is
found.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a6255e8-da3a-4f12-b821-8e77eee6962a

📥 Commits

Reviewing files that changed from the base of the PR and between 3f7c90e and 92329f9.

📒 Files selected for processing (10)
  • plugins.json
  • plugins/baseball-scoreboard/CHANGELOG.md
  • plugins/baseball-scoreboard/README.md
  • plugins/baseball-scoreboard/baseball_timezone.py
  • plugins/baseball-scoreboard/config_schema.json
  • plugins/baseball-scoreboard/game_renderer.py
  • plugins/baseball-scoreboard/manager.py
  • plugins/baseball-scoreboard/manifest.json
  • plugins/baseball-scoreboard/sports.py
  • plugins/baseball-scoreboard/test_timezone_resolution.py

Comment thread plugins/baseball-scoreboard/baseball_timezone.py Outdated
resolve_timezone_name() built its candidate list eagerly, so every call
queried both config managers and stat'd the host timezone files even when the
plugin config already supplied a valid zone. SportsCore._get_timezone() runs
once per game during extraction, making that a per-game filesystem probe.

Yield candidates from a generator instead, preserving the priority order, and
assert in the tests that lower-priority sources are left untouched once a
candidate resolves.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QSnspNZceRCdpUtJh2Co6e
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.

2 participants