feat(calendar): dynamic Google Calendar picker for multi-calendar selection#45
feat(calendar): dynamic Google Calendar picker for multi-calendar selection#45ChuckBuilds merged 9 commits intomainfrom
Conversation
… display - All sports plugins: use America/New_York (with DST) instead of naive datetime.now() for ESPN API date queries, plus 1-day lookback to catch late-night games still in progress — matching the fix already in LEDMatrix core (commit cd0c43fb). ESPN anchors its schedule calendar to Eastern US time regardless of where the user is located. - Baseball: inject resolved timezone (plugin config → global config → UTC) into self.config early in manager __init__ so game_renderer.py and scroll_display.py both get the correct timezone. Fix game_renderer.py fallback from 'US/Eastern' to 'UTC' (fallback is now never reached for users with a global config timezone set). - F1: inject resolved timezone into config before constructing F1Renderer. Add _to_local_dt() helper to F1Renderer and use it in render_upcoming_race() and render_calendar_entry() so session/race dates display in local time instead of UTC. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- baseball/manager.py: use getattr + targeted AttributeError/TypeError instead of broad Exception; log debug on unavailable global timezone - baseball/game_renderer.py: catch pytz.UnknownTimeZoneError around pytz.timezone() and log a warning with the bad tz name before falling back to UTC - basketball/sports.py: update debug log to show full date range (formatted_date_yesterday-formatted_date) instead of only today - f1/requirements.txt: add pytz>=2024.1 (used in f1_renderer.py) - f1/f1_renderer.py: narrow except Exception → pytz.exceptions.UnknownTimeZoneError in _to_local_dt so unexpected errors still surface - f1/manager.py: extract _resolve_timezone() helper and call it from both __init__ and on_config_change() so the renderer always receives the correct timezone when config is hot-reloaded Versions: baseball 1.4.2, basketball 1.4.2, f1 1.1.2 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… support Adds a get_calendars() method to the calendar plugin that calls the Google calendarList API, enabling users to select calendars from a dynamic checklist instead of manually typing IDs. Improves error logging for failed per-calendar fetches. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughUpdates across multiple plugins: manifest version bumps, sports APIs now query an Eastern Time two-day window, added/expanded timezone error logging, calendar plugin gains a get_calendars() method and schema picker, F1 rendering gets timezone conversion, and countdown plugin receives a large normalization and caching refactor. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/hockey-scoreboard/sports.py (1)
856-858:⚠️ Potential issue | 🟡 MinorStale log message after date-range change.
The fetch now covers a 2-day range (yesterday → today) but the log still reads
"todays games", which will mislead debugging.🛠️ Proposed fix
- self.logger.info( - f"Fetched {len(events)} todays games for {self.sport} - {self.league}" - ) + self.logger.info( + f"Fetched {len(events)} games ({formatted_date_yesterday}-{formatted_date}) " + f"for {self.sport} - {self.league}" + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/hockey-scoreboard/sports.py` around lines 856 - 858, Update the stale log message that still says "todays games": change the self.logger.info call that uses f"Fetched {len(events)} todays games for {self.sport} - {self.league}" to accurately describe the new 2-day range (e.g., "fetched {len(events)} games for yesterday and today" or "fetched {len(events)} games for the last 2 days") so logs reflect the actual fetch window; locate the self.logger.info line in the method that populates events and replace the wording while keeping the same variables ({len(events)}, self.sport, self.league).
🧹 Nitpick comments (2)
plugins/baseball-scoreboard/sports.py (1)
851-865: Update logging to reflect the actualdatesrange.This block now queries
yesterday-today, but the debug log still says “on date …” (single day).Proposed tweak
formatted_date = now.strftime("%Y%m%d") formatted_date_yesterday = yesterday.strftime("%Y%m%d") + date_range = f"{formatted_date_yesterday}-{formatted_date}" # Fetch todays games only url = f"https://site.api.espn.com/apis/site/v2/sports/{self.sport}/{self.league}/scoreboard" - self.logger.debug(f"Fetching today's games for {self.sport}/{self.league} on date {formatted_date}") + self.logger.debug( + f"Fetching games for {self.sport}/{self.league} on ESPN date range {date_range} (ET)" + ) response = self.session.get( url, - params={"dates": f"{formatted_date_yesterday}-{formatted_date}", "limit": 1000}, + params={"dates": date_range, "limit": 1000}, headers=self.headers, timeout=10, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/baseball-scoreboard/sports.py` around lines 851 - 865, The debug log in the ESPN schedule fetch incorrectly states a single date while the request uses a date range; update the logger.debug call in the fetch block (where tz, now, yesterday, formatted_date, formatted_date_yesterday and url are defined) to include the full dates range (e.g., formatted_date_yesterday and formatted_date or the actual "dates" param) so the message accurately reflects the params sent to self.session.get.plugins/football-scoreboard/sports.py (1)
851-865: Align the debug message with the new date-range request.The request now sends a
yesterday-todayrange, but the debug log still reports a single date. This makes troubleshooting harder.Proposed tweak
formatted_date = now.strftime("%Y%m%d") formatted_date_yesterday = yesterday.strftime("%Y%m%d") + date_range = f"{formatted_date_yesterday}-{formatted_date}" # Fetch todays games only url = f"https://site.api.espn.com/apis/site/v2/sports/{self.sport}/{self.league}/scoreboard" - self.logger.debug(f"Fetching today's games for {self.sport}/{self.league} on date {formatted_date}") + self.logger.debug( + f"Fetching games for {self.sport}/{self.league} on ESPN date range {date_range} (ET)" + ) response = self.session.get( url, - params={"dates": f"{formatted_date_yesterday}-{formatted_date}", "limit": 1000}, + params={"dates": date_range, "limit": 1000}, headers=self.headers, timeout=10, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/football-scoreboard/sports.py` around lines 851 - 865, The debug message incorrectly states "Fetching today's games..." while the request uses a date range; update the logger.debug in the method that builds the ESPN scoreboard request to reflect the actual params by including both formatted_date_yesterday and formatted_date (or the combined range string used in params) so the log shows the full "{formatted_date_yesterday}-{formatted_date}" range for the GET to url (referencing tz, now, yesterday, formatted_date, formatted_date_yesterday, and self.logger.debug).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@plugins.json`:
- Around line 175-178: Update the calendar plugin's version to 1.1.0 to reflect
the new get_calendars() feature and google-calendar-picker schema change: change
the "version" field in the calendar plugin manifest (the manifest's version
entry for the calendar plugin) to "1.1.0" and update the "latest_version" key
currently set to "1.0.2" in the plugins registry (the plugins.json entry for the
calendar plugin) to "1.1.0"; also search for other occurrences of "1.0.2"
related to the calendar plugin and replace them with "1.1.0".
In `@plugins/baseball-scoreboard/manager.py`:
- Around line 96-108: The timezone lookup currently only catches AttributeError
and TypeError when calling config_manager.get_timezone(), which can allow other
exceptions to crash init; modify the try/except around the get_timezone() call
(the block that references cache_manager, config_manager.get_timezone(), and
self.config) to catch a broader Exception, log the exception details via
self.logger.debug or self.logger.exception (including the error message), and
then continue to set self.config["timezone"] = global_tz or "UTC" so
initialization remains fail-safe.
In `@plugins/baseball-scoreboard/manifest.json`:
- Line 4: Update manifest.json so the "versions" array includes the new "1.5.4"
entry as the first (most recent) element and ensure the top entry matches the
"version" field; prepend "1.5.4" to the "versions" array (the array named
versions in manifest.json) so the array's first item and the "version" property
are in sync.
In `@plugins/basketball-scoreboard/manifest.json`:
- Line 4: The manifest.json's top-level "version" value was changed to "1.5.4"
but the "versions" array still lists "1.5.3" as the most recent; update the
"versions" array so its first entry is "1.5.4" (prepend the new version at the
top) to keep the "version" field and the "versions" array in sync, ensuring the
newest release appears first.
In `@plugins/calendar/config_schema.json`:
- Around line 31-32: The plugin added a new feature ("x-widget":
"google-calendar-picker" in config_schema.json) so update the plugin manifest to
a MINOR bump: open the plugin's manifest.json and change the current patch
release to the next minor (e.g., 1.1.0) and prepend that new version string to
the "versions" array so the release history reflects a minor-version addition
rather than a patch.
In `@plugins/calendar/manager.py`:
- Around line 373-385: New feature get_calendars was added and config schema
changes were included, so bump the MINOR version (increment the middle digit and
set patch to 0) for this plugin; locate the plugin's config_schema.json and any
version metadata associated with the manager (reference: get_calendars in
manager.py) and update the "version" field from X.Y.Z to X.(Y+1).0 (and update
any release/changelog entries if present) to reflect a MINOR release.
- Around line 373-385: Wrap the Google API call inside get_calendars in a
try/except so API failures don't bubble up: surround the call to
self.service.calendarList().list().execute() (and subsequent processing) with a
try block, catch Exception as e, log the error (using an existing logger on the
class, e.g. self.logger.exception or module logging) with context like "Failed
to fetch calendar list", and return an empty list [] to degrade gracefully while
keeping the existing calendar item mapping logic unchanged.
- Line 433: Replace the self.logger.error call in the except block that
currently logs "Error fetching events from calendar '{calendar_id}': {e} —
verify this calendar ID is correct and accessible under your Google account"
with self.logger.exception so the traceback is preserved; locate the call to
self.logger.error in plugins/calendar/manager.py (the one referencing
calendar_id and variable e, likely inside the calendar event fetch method) and
change it to self.logger.exception with the same descriptive message so
exception context is included in the logs.
In `@plugins/calendar/manifest.json`:
- Line 4: Update the plugin manifest version to a MINOR release: change the
"version" value in plugins/calendar/manifest.json from "1.0.2" to "1.1.0" (and
update any other identical occurrences around the same file referenced in the
review, e.g., the repeated entries at lines 36-40) to reflect this
feature/schema addition; ensure the version string is exactly "1.1.0" and no
other metadata is modified.
In `@plugins/f1-scoreboard/f1_renderer.py`:
- Around line 81-89: The _to_local_dt function must defensively handle naive
datetimes returned by datetime.fromisoformat: after parsing
(datetime.fromisoformat) check if dt.tzinfo is None and, if so, explicitly
assign/normalize it to UTC (use pytz.UTC or an equivalent UTC tz) before calling
dt.astimezone(local_tz); keep the existing logic that resolves local_tz via
self.config.get and falls back to pytz.UTC on UnknownTimeZoneError
(pytz.timezone and pytz.UTC), then call astimezone(local_tz) on the now-aware
datetime.
In `@plugins/f1-scoreboard/manager.py`:
- Around line 109-121: The _resolve_timezone method can crash if
config_manager.get_timezone() raises exceptions other than
AttributeError/TypeError; update the try/except around
config_manager.get_timezone() in _resolve_timezone to catch Exception as e (or a
broader set that includes runtime errors), log the caught exception (e.g.,
self.logger.debug or self.logger.exception with the error message and context)
and then fall back to returning "UTC"; this change should reference the
_resolve_timezone function, the config_manager variable obtained from
cache_manager, and the get_timezone() call so you adjust the correct block.
In `@plugins/f1-scoreboard/manifest.json`:
- Line 4: The manifest's top-level "version" value is out of sync with the
"versions" array; add a new entry for "1.2.2" as the first element of the
"versions" array (including its release metadata and updated "last_updated"
timestamp), ensure that the new top entry matches the top-level "version" field
(set to "1.2.2"), and remove or update any stale references to "1.2.1" so the
array and the "version" field are consistent (update release notes/date in the
new versions[0] entry accordingly).
In `@plugins/football-scoreboard/manifest.json`:
- Line 4: The manifest.json's "version" field was bumped to "2.3.4" but the
"versions" array still has "2.3.3" as its top entry; update the "versions" array
to prepend the new entry "2.3.4" (so the most recent version is first) and
ensure the top element matches the "version" field; edit the "versions" array in
manifest.json to add "2.3.4" above "2.3.3" so both the "version" property and
the first item of "versions" are in sync.
In `@plugins/hockey-scoreboard/manifest.json`:
- Line 4: Prepend a new entry to the versions array so its top element matches
the bumped "version" field: add an object with released (ISO date or current
release date), version set to "1.2.4", and ledmatrix_min (minimum supported
ledmatrix version) as the first array element in versions, ensuring the
versions[0].version equals the manifest's "version" value; keep existing older
entries intact below it.
In `@plugins/soccer-scoreboard/manifest.json`:
- Line 4: The manifest's "version" field was bumped to "1.4.4" but the
"versions" array wasn't updated; update the top of the "versions" array in
plugins/soccer-scoreboard/manifest.json by prepending a new entry for "1.4.4" so
the first element in "versions" matches the "version" field, ensuring the newest
version appears first and both values stay in sync.
In `@plugins/ufc-scoreboard/manifest.json`:
- Line 4: The manifest's "version" field was updated to "1.2.3" but the
"versions" array still lists "1.2.2" as the latest; update the top of the
"versions" array to prepend a new entry for "1.2.3" so the first element matches
the "version" field, ensuring the "version" property and the first item in the
"versions" array (in manifest.json) remain in sync.
---
Outside diff comments:
In `@plugins/hockey-scoreboard/sports.py`:
- Around line 856-858: Update the stale log message that still says "todays
games": change the self.logger.info call that uses f"Fetched {len(events)}
todays games for {self.sport} - {self.league}" to accurately describe the new
2-day range (e.g., "fetched {len(events)} games for yesterday and today" or
"fetched {len(events)} games for the last 2 days") so logs reflect the actual
fetch window; locate the self.logger.info line in the method that populates
events and replace the wording while keeping the same variables ({len(events)},
self.sport, self.league).
---
Nitpick comments:
In `@plugins/baseball-scoreboard/sports.py`:
- Around line 851-865: The debug log in the ESPN schedule fetch incorrectly
states a single date while the request uses a date range; update the
logger.debug call in the fetch block (where tz, now, yesterday, formatted_date,
formatted_date_yesterday and url are defined) to include the full dates range
(e.g., formatted_date_yesterday and formatted_date or the actual "dates" param)
so the message accurately reflects the params sent to self.session.get.
In `@plugins/football-scoreboard/sports.py`:
- Around line 851-865: The debug message incorrectly states "Fetching today's
games..." while the request uses a date range; update the logger.debug in the
method that builds the ESPN scoreboard request to reflect the actual params by
including both formatted_date_yesterday and formatted_date (or the combined
range string used in params) so the log shows the full
"{formatted_date_yesterday}-{formatted_date}" range for the GET to url
(referencing tz, now, yesterday, formatted_date, formatted_date_yesterday, and
self.logger.debug).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
plugins.jsonplugins/baseball-scoreboard/game_renderer.pyplugins/baseball-scoreboard/manager.pyplugins/baseball-scoreboard/manifest.jsonplugins/baseball-scoreboard/sports.pyplugins/basketball-scoreboard/manifest.jsonplugins/basketball-scoreboard/sports.pyplugins/calendar/config_schema.jsonplugins/calendar/manager.pyplugins/calendar/manifest.jsonplugins/f1-scoreboard/f1_renderer.pyplugins/f1-scoreboard/manager.pyplugins/f1-scoreboard/manifest.jsonplugins/f1-scoreboard/requirements.txtplugins/football-scoreboard/manifest.jsonplugins/football-scoreboard/sports.pyplugins/hockey-scoreboard/manifest.jsonplugins/hockey-scoreboard/sports.pyplugins/soccer-scoreboard/manifest.jsonplugins/soccer-scoreboard/sports.pyplugins/ufc-scoreboard/manifest.jsonplugins/ufc-scoreboard/sports.py
- Calendar: bump to 1.1.0, add get_calendars(), google-calendar-picker schema, wrap calendarList in try/except, use logger.exception for event fetch errors - Registry: plugins.json calendar latest_version 1.1.0 - Manifests: prepend version entries for baseball, basketball, f1, football, hockey, soccer, ufc so version matches versions[0] - Baseball/F1: catch broad Exception in get_timezone, log and fallback to UTC - F1 renderer: treat naive datetimes as UTC in _to_local_dt - Sports: debug logs show date range; hockey info log says 'last 2 days' Made-with: Cursor
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
plugins/baseball-scoreboard/manager.py (1)
96-111: Timezone fallback logic looks solid; minor logging cleanup suggested.The broad exception handling and fallback to UTC is well-implemented, addressing the previous review concern. However, the static analysis hint is valid:
logger.exception()automatically includes the full exception traceback in the log output, making the%splaceholder witheredundant.🧹 Proposed fix to remove redundant exception argument
try: global_tz = config_manager.get_timezone() except Exception as e: self.logger.exception( - "Failed to read global timezone from config_manager.get_timezone(): %s. Falling back to UTC.", - e, + "Failed to read global timezone from config_manager.get_timezone(). Falling back to UTC." ) self.config["timezone"] = global_tz or "UTC"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/baseball-scoreboard/manager.py` around lines 96 - 111, The logger.exception call in the timezone resolution should not pass the exception object as a formatting argument because logger.exception already logs the traceback; update the exception handling in the block that calls config_manager.get_timezone() (references: cache_manager, config_manager, config_manager.get_timezone(), self.logger.exception, self.config) to call self.logger.exception with a plain descriptive message (e.g., "Failed to read global timezone from config_manager.get_timezone(); falling back to UTC.") and remove the redundant "%s" and e argument, then leave the existing fallback assignment self.config["timezone"] = global_tz or "UTC" unchanged.plugins/football-scoreboard/manifest.json (1)
4-4: Alignlast_updatedwith the new release date.Line 4 and Lines 27-31 indicate a
2026-03-02release, but Line 243 still showslast_updated: "2026-02-24". Updating it keeps registry metadata consistent.Proposed tweak
- "last_updated": "2026-02-24", + "last_updated": "2026-03-02",Also applies to: 27-31
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/football-scoreboard/manifest.json` at line 4, Update the manifest's last_updated field to match the new release date shown by the version/release metadata (change last_updated from "2026-02-24" to "2026-03-02"); locate the "last_updated" property in plugins/football-scoreboard/manifest.json and make the date consistent with the release entries around the "version": "2.3.4" and the release block that lists "2026-03-02".plugins/basketball-scoreboard/manifest.json (1)
4-4: Updatelast_updatedto match the new release record.Line 4 and Lines 21-25 now reflect a
2026-03-02release, but Line 74 still haslast_updated: "2026-02-24". Consider syncing this metadata.Proposed tweak
- "last_updated": "2026-02-24", + "last_updated": "2026-03-02",Also applies to: 21-25
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/basketball-scoreboard/manifest.json` at line 4, The manifest's metadata is inconsistent: the "version" is 1.5.4 and several release fields show 2026-03-02, but the "last_updated" field still reads "2026-02-24"; update the last_updated value to "2026-03-02" so it matches the new release record and ensure any other occurrences of last_updated in this manifest are synchronized; verify the "version" and release date blocks (references: "version" and "last_updated") are consistent across the file.plugins/f1-scoreboard/manager.py (1)
109-123: Remove redundant exception object fromlogging.exception()call.
logger.exception()automatically captures and logs the current exception's traceback viasys.exc_info(). Passingeas an argument is redundant and flagged by static analysis (TRY401).♻️ Proposed fix
try: tz = config_manager.get_timezone() except Exception as e: - self.logger.exception( - "Failed to read global timezone from config_manager.get_timezone(): %s. Falling back to UTC.", - e, - ) + self.logger.exception( + "Failed to read global timezone from config_manager.get_timezone(). Falling back to UTC." + ) return tz or "UTC"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/f1-scoreboard/manager.py` around lines 109 - 123, The logger.exception call in _resolve_timezone currently passes the caught exception object (e) as a formatting argument to self.logger.exception, which is redundant and causes static analysis warnings; update the except block around config_manager.get_timezone() so it calls self.logger.exception with a single message (e.g. "Failed to read global timezone from config_manager.get_timezone(): Falling back to UTC.") and remove the extra exception argument so the traceback is still captured automatically by logger.exception.plugins/calendar/manager.py (1)
440-444: Drop redundant exception object fromlogger.exception.At Line 441,
self.logger.exception(...)already records traceback/exception context. Passingeas%sduplicates noise in logs.🧹 Proposed cleanup
self.logger.exception( - "Error fetching events from calendar '%s': %s - verify this calendar ID is correct and accessible under your Google account", - calendar_id, - e, + "Error fetching events from calendar '%s' - verify this calendar ID is correct and accessible under your Google account", + calendar_id, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/calendar/manager.py` around lines 440 - 444, Remove the redundant exception object passed into the logger.exception call: in the block where self.logger.exception is invoked with calendar_id and the exception variable e, drop the extra "e" argument and keep a single formatted message referencing calendar_id (logger.exception already logs traceback/exception context). Update the self.logger.exception invocation so it only formats the message with calendar_id and relies on logger.exception to record the exception details.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@plugins/calendar/config_schema.json`:
- Around line 26-33: The calendars schema object contains a duplicated
"x-widget" key; remove the redundant "x-widget" entry so the object only has a
single "x-widget" property (keep the first occurrence) in the calendars schema
definition to resolve the ambiguity and lint error—locate the calendars schema
in config_schema.json and delete the second "x-widget" declaration.
In `@plugins/calendar/manager.py`:
- Around line 378-389: The calendar list call only fetches the first page;
update the logic in the method that calls
self.service.calendarList().list().execute() to iterate pages using
nextPageToken: initialize page_token = None, call .list(pageToken=page_token)
(or omit when None), merge each response's "items" into a single calendars list,
set page_token = response.get("nextPageToken") and loop until it's falsy, then
map each calendar to the existing dict shape (id, summary, primary) and return
the combined list; reference the existing usage of
self.service.calendarList().list(), "items", and "nextPageToken" when
implementing the loop.
---
Nitpick comments:
In `@plugins/baseball-scoreboard/manager.py`:
- Around line 96-111: The logger.exception call in the timezone resolution
should not pass the exception object as a formatting argument because
logger.exception already logs the traceback; update the exception handling in
the block that calls config_manager.get_timezone() (references: cache_manager,
config_manager, config_manager.get_timezone(), self.logger.exception,
self.config) to call self.logger.exception with a plain descriptive message
(e.g., "Failed to read global timezone from config_manager.get_timezone();
falling back to UTC.") and remove the redundant "%s" and e argument, then leave
the existing fallback assignment self.config["timezone"] = global_tz or "UTC"
unchanged.
In `@plugins/basketball-scoreboard/manifest.json`:
- Line 4: The manifest's metadata is inconsistent: the "version" is 1.5.4 and
several release fields show 2026-03-02, but the "last_updated" field still reads
"2026-02-24"; update the last_updated value to "2026-03-02" so it matches the
new release record and ensure any other occurrences of last_updated in this
manifest are synchronized; verify the "version" and release date blocks
(references: "version" and "last_updated") are consistent across the file.
In `@plugins/calendar/manager.py`:
- Around line 440-444: Remove the redundant exception object passed into the
logger.exception call: in the block where self.logger.exception is invoked with
calendar_id and the exception variable e, drop the extra "e" argument and keep a
single formatted message referencing calendar_id (logger.exception already logs
traceback/exception context). Update the self.logger.exception invocation so it
only formats the message with calendar_id and relies on logger.exception to
record the exception details.
In `@plugins/f1-scoreboard/manager.py`:
- Around line 109-123: The logger.exception call in _resolve_timezone currently
passes the caught exception object (e) as a formatting argument to
self.logger.exception, which is redundant and causes static analysis warnings;
update the except block around config_manager.get_timezone() so it calls
self.logger.exception with a single message (e.g. "Failed to read global
timezone from config_manager.get_timezone(): Falling back to UTC.") and remove
the extra exception argument so the traceback is still captured automatically by
logger.exception.
In `@plugins/football-scoreboard/manifest.json`:
- Line 4: Update the manifest's last_updated field to match the new release date
shown by the version/release metadata (change last_updated from "2026-02-24" to
"2026-03-02"); locate the "last_updated" property in
plugins/football-scoreboard/manifest.json and make the date consistent with the
release entries around the "version": "2.3.4" and the release block that lists
"2026-03-02".
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
plugins.jsonplugins/baseball-scoreboard/manager.pyplugins/baseball-scoreboard/manifest.jsonplugins/baseball-scoreboard/sports.pyplugins/basketball-scoreboard/manifest.jsonplugins/calendar/config_schema.jsonplugins/calendar/manager.pyplugins/calendar/manifest.jsonplugins/f1-scoreboard/f1_renderer.pyplugins/f1-scoreboard/manager.pyplugins/f1-scoreboard/manifest.jsonplugins/football-scoreboard/manifest.jsonplugins/football-scoreboard/sports.pyplugins/hockey-scoreboard/manifest.jsonplugins/hockey-scoreboard/sports.pyplugins/soccer-scoreboard/manifest.jsonplugins/ufc-scoreboard/manifest.json
🚧 Files skipped from review as they are similar to previous changes (8)
- plugins/hockey-scoreboard/manifest.json
- plugins/hockey-scoreboard/sports.py
- plugins.json
- plugins/f1-scoreboard/manifest.json
- plugins/baseball-scoreboard/manifest.json
- plugins/soccer-scoreboard/manifest.json
- plugins/calendar/manifest.json
- plugins/ufc-scoreboard/manifest.json
- Calendar: fix duplicate x-widget, add paginated get_calendars, simplify logger.exception usage - Baseball/F1: logger.exception no longer passes exception arg for timezone fallback - Basketball/Football: last_updated dates match latest release versions Made-with: Cursor
Preserve timezone fallback fixes while incorporating upstream registry and plugin version updates so fix/timezone-display can merge cleanly. Made-with: Cursor
Parse string booleans safely, enforce unique countdown IDs, and broaden cache invalidation to include image-affecting settings; also clarify README wording for explicit image_path configuration. Made-with: Cursor
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@plugins/countdown/manager.py`:
- Around line 167-171: The runtime cache seeding (used_ids populated from
cached_images and countdown_values) is causing unchanged incoming IDs to be
treated as collisions and renamed; modify the dedupe logic so existing runtime
IDs are not reserved against incoming IDs. When building used_ids from
cached_images and countdown_values, exclude any IDs that are present in the
current incoming IDs set (or, alternatively, when assigning/renaming in the
dedupe routine, first check if the incoming id is already in incoming_ids and
allow it to remain). Update the code paths that reference used_ids and the
renaming/generation logic so preserved incoming IDs (from the incoming
list/variable used for import) are not considered collisions by used_ids.
- Around line 121-127: The string-to-boolean parsing branch (the
isinstance(value, str) block) currently returns True for any unrecognized
string; change it to return the provided default instead of True for
unknown/typod inputs. Keep the existing normalized checks for
("false","0","off","no","") -> False and ("true","1","on","yes") -> True, but
replace the final unconditional "return True" with "return default" so unknown
normalized values (e.g. "flase") do not invert the setting.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b4d04d35-ddc7-4603-884f-184b3109f8a3
📒 Files selected for processing (2)
plugins/countdown/README.mdplugins/countdown/manager.py
✅ Files skipped from review due to trivial changes (1)
- plugins/countdown/README.md
| self.countdowns = self._normalize_countdowns(config.get('countdowns', [])) | ||
| self._countdown_signature = self._build_countdown_signature(self.countdowns) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
manifest_path="$(fd -p 'manifest.json' plugins/countdown | head -n1 || true)"
if [[ -z "${manifest_path}" ]]; then
echo "No countdown manifest found under plugins/countdown"
exit 1
fi
echo "Manifest file: ${manifest_path}"
echo "=== PR diff for manifest ==="
git diff -- "${manifest_path}" | sed -n '1,220p'
echo "=== Current version fields ==="
python - "${manifest_path}" <<'PY'
import json, re, sys, pathlib
p = pathlib.Path(sys.argv[1])
data = json.loads(p.read_text())
version = data.get("version")
versions = data.get("versions") or []
top = versions[0] if versions else None
print("version:", version)
print("top versions entry:", top)
semver = re.compile(r"^\d+\.\d+\.\d+$")
print("version_is_semver:", bool(isinstance(version, str) and semver.match(version)))
if isinstance(top, dict):
top_version = top.get("version")
print("top_entry_matches_version:", top_version == version)
else:
print("top_entry_matches_version: false (missing/invalid top versions entry)")
PYRepository: ChuckBuilds/ledmatrix-plugins
Length of output: 313
🏁 Script executed:
# Check the current manifest.json content
cat plugins/countdown/manifest.jsonRepository: ChuckBuilds/ledmatrix-plugins
Length of output: 805
🏁 Script executed:
# Check what changed in manager.py
git diff plugins/countdown/manager.pyRepository: ChuckBuilds/ledmatrix-plugins
Length of output: 55
🏁 Script executed:
# Check plugins/countdown directory contents
ls -la plugins/countdown/
# Get file size and line count for manager.py
wc -l plugins/countdown/manager.py
# Read lines 70-80 from manager.py to verify the mentioned code
sed -n '70,80p' plugins/countdown/manager.pyRepository: ChuckBuilds/ledmatrix-plugins
Length of output: 1170
🏁 Script executed:
# Check git status to understand branch state
git status | head -30Repository: ChuckBuilds/ledmatrix-plugins
Length of output: 307
Manifest structure and versioning incomplete. The plugins/countdown/manifest.json requires two critical updates:
-
Bump version to 1.1.0 — Lines 74-75 in manager.py introduce feature-level functionality (countdown normalization and signature building), which requires a MINOR version bump per coding guidelines.
-
Add missing
versionsarray — The manifest is missing the requiredversionsarray. Add it with the new version entry at the top, following the structure:{ "released": "YYYY-MM-DD", "version": "1.1.0", "ledmatrix_min": "<required_version>" }. Ensure theversionfield in the root of the manifest stays synchronized with the top entry inversions.
…ming IDs in dedupe Made-with: Cursor
…icts Made-with: Cursor
Summary
get_calendars()method to the calendar plugin usingcalendarList().list()— returns all calendars accessible via the authenticated Google accountconfig_schema.jsonto usex-widget: google-calendar-pickeron thecalendarsfield, triggering the new dynamic UI in the LEDMatrix web interface1.0.2Why
Users with multiple Google Calendars had no way to know their correct calendar IDs, and errors fetching non-primary calendars were silently swallowed. This makes selection self-service and failures visible.
Pairs with LEDMatrix core PR: ChuckBuilds/LEDMatrix (fix/timezonefinder-tmp-space branch)
Test plan
journalctl -u ledmatrix | grep calendarshows fetches from each selected calendar🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Updates