Conversation
📝 WalkthroughWalkthroughAdds Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Renovate as Renovate Bot
participant Repo as Repository
participant CI as "GitHub Actions (backend-api-update)"
participant Tests as "Test Suite"
participant Commenter as "Workflow Commenter"
Renovate->>Repo: Open/Update PR modifying `.backend_version`
Repo->>CI: PR event triggers backend-api-update.yml
CI->>Repo: Checkout + setup Python
CI->>Tests: Run pytest (stability, coordinator, sensor, etc.)
Tests-->>CI: Results (pass/fail)
alt tests pass and branch matches renovate/*
CI->>Commenter: Read `.backend_version`
Commenter->>Repo: Post PR comment with backend version
else tests fail
CI->>Repo: Report failing checks
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
custom_components/db_infoscreen/sensor.py (1)
103-123: Use Home Assistant timezone utilities for date comparisons.The code uses
datetime.now().date()at lines 103 and 120, which returns naive/local time unaware of Home Assistant's configured timezone. When HA is configured to a different timezone than the system, this causes incorrect date comparisons: same-day departures incorrectly include the date in the display, or different-day times show only the hour without the date.Use
dt_util.now()fromhomeassistant.utilinstead, and cache a singletodayvalue to avoid multiple function calls.Proposed fix
from homeassistant.components.sensor import SensorEntity +from homeassistant.util import dt as dt_util from .const import DOMAIN, CONF_ENABLE_TEXT_VIEW import logging from datetime import datetime @@ format_departure_time method @@ try: departure_time = datetime.strptime( - f"{datetime.now().date()} {departure_time}", + f"{dt_util.now().date()} {departure_time}", "%Y-%m-%d %H:%M", ) @@ if isinstance(departure_time, datetime): _LOGGER.debug("Checking departure time date: %s", departure_time.date()) - _LOGGER.debug("Today's date: %s", datetime.now().date()) - if departure_time.date() != datetime.now().date(): + today = dt_util.now().date() + _LOGGER.debug("Today's date: %s", today) + if departure_time.date() != today:
🤖 Fix all issues with AI agents
In @.github/renovate.json:
- Around line 44-49: Remove "manifest.json" from the pip_requirements.fileMatch
array so the pip_requirements manager only matches text-based requirement files
(leave entries like "^requirements.*\\.txt$" and "^docs/requirements\\.txt$");
if you intend to manage manifest.json with Renovate, add a separate manager
configuration (e.g., a custom json or regex manager) instead of including
"manifest.json" in pip_requirements.fileMatch.
In `@docs/limitations.md`:
- Around line 50-52: Under the "Routing vs. Departures" section update the
sentence that reads “It will not tell you which train to take to reach a
specific destination if that involve changes.” to correct the verb agreement to
“involves” (i.e., replace “if that involve changes” with “if that involves
changes”) so the disclaimer reads correctly; locate this text under the "Routing
vs. Departures" header and make the single-word change.
In `@mkdocs.yml`:
- Around line 84-86: Update the pymdownx.emoji extension configuration to use
the built-in mkdocs-material paths instead of the deprecated package: replace
references to materialx.emoji.twemoji and materialx.emoji.to_svg with
material.extensions.emoji.twemoji and material.extensions.emoji.to_svg in the
pymdownx.emoji block so the config uses the built-in material extensions (i.e.,
change the emoji_index and emoji_generator values accordingly).
In `@tests/test_config_flow.py`:
- Around line 193-226: The test test_options_flow_persistence calls
hass.config_entries.async_update_entry before registering the entry; move the
call to config_entry.add_to_hass(hass) so the entry is added to hass prior to
invoking hass.config_entries.async_update_entry, ensuring the update operates on
a registered entry (adjust the order of config_entry.add_to_hass and
hass.config_entries.async_update_entry accordingly).
In `@tests/test_sensor.py`:
- Around line 87-102: The test is flaky because it uses dt_util.now() while the
sensor code calls datetime.now(), so freeze time for both to a fixed datetime
(e.g., use dt_util.parse_datetime to create fixed_now) by patching
homeassistant.util.dt.now to return fixed_now and patching the sensor module's
datetime.now (the datetime used in the sensor implementation) to return the same
fixed_now; then compute dep_time = fixed_now + timedelta(minutes=10), set
mock_coordinator.data accordingly, and assert sensor.native_value to avoid
date-boundary/timezone differences.
In `@tests/test_stability.py`:
- Around line 28-48: The async test functions (e.g.,
test_coordinator_handles_api_errors, test_coordinator_handles_malformed_json,
test_input_sanitization, test_large_response_handling) are missing the
`@pytest.mark.asyncio` decorator causing coroutines like
coordinator._async_update_data() not to be awaited properly; add
`@pytest.mark.asyncio` above each async def test_* to ensure pytest runs them as
asyncio tests and that DBInfoScreenCoordinator._async_update_data() is executed
and awaited during the tests.
- Around line 96-102: The assertion uses len(data) but
coordinator._async_update_data() can return None, causing TypeError; update the
check around data from coordinator._async_update_data() (variable data) to
handle None safely—either assert data is not None first or change the assertion
to allow None (e.g., assert data is None or len(data) <= 100) so the test won't
raise when data is None.
🧹 Nitpick comments (5)
README.md (1)
15-17: Documentation link is a good addition.Prominently placing the full documentation link near the top improves discoverability.
Minor nit: There appear to be two consecutive blank lines (16 and 17) before the next content block. Consider removing one for consistency with the rest of the file's formatting.
✨ Suggested fix
📚 **[View the Full Documentation](https://faserf.github.io/ha-db_infoscreen/)** - <div align="center">custom_components/db_infoscreen/config_flow.py (1)
62-65: Consider accepting both “,” and “|” for via-station input.Line 62 and Line 215 now split only on commas; users accustomed to
|will end up with a single entry. Supporting both separators preserves backward compatibility.🔧 Possible tweak
- user_input[CONF_VIA_STATIONS] = [ - s.strip() for s in via_raw.split(",") if s.strip() - ] + user_input[CONF_VIA_STATIONS] = [ + s.strip() for s in re.split(r"[|,]", via_raw) if s.strip() + ] @@ - user_input[CONF_VIA_STATIONS] = [ - s.strip() for s in via_raw.split(",") if s.strip() - ] + user_input[CONF_VIA_STATIONS] = [ + s.strip() for s in re.split(r"[|,]", via_raw) if s.strip() + ]Also applies to: 215-217
tests/test_sensor.py (1)
41-63:test_sensor_setupis a no-op right now.Lines 41–63 end with
pass; consider skipping or removing to avoid misleading coverage.🧹 Optional cleanup
async def test_sensor_setup(hass: HomeAssistant): """Test setting up the sensor.""" + pytest.skip("Setup flow is covered by integration tests") @@ - pass.github/workflows/backend-api-update.yml (1)
41-50: Consider adding error handling for the comment action.If
.backend_versiondoesn't exist or is empty, the script will post a comment with an undefined/empty version. Consider adding a guard.🛡️ Suggested improvement
script: | const fs = require('fs'); const version = fs.readFileSync('.backend_version', 'utf8').trim(); + + if (!version) { + core.warning('Backend version is empty'); + return; + } github.rest.issues.createComment({tests/test_translations.py (1)
33-45: Consider usingisinstance()and handling list values.Two observations:
- Line 38 uses
type(x) is dictwhich doesn't account for dict subclasses. Preferisinstance(x, dict).- The function silently ignores list values in the JSON structure. If any translation values are arrays, they won't be flattened or compared.
♻️ Suggested improvement
def flatten_json(y): """Flatten a nested json dict.""" out = {} def flatten(x, name=""): - if type(x) is dict: + if isinstance(x, dict): for a in x: flatten(x[a], name + a + ".") + elif isinstance(x, list): + for i, item in enumerate(x): + flatten(item, name + str(i) + ".") else: out[name[:-1]] = x flatten(y) return out
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
custom_components/db_infoscreen/__init__.py (1)
276-330: Avoid mixing HA‑awaredt_util.now()with naive datetimes in different timezone contexts.
nowis HA‑localized (aware), but parsed departures use naivedatetime.fromtimestamp()anddatetime.strptime(), which interpret times as system local—not HA local. If HA's configured timezone differs from the host system's timezone, comparisons liketime_candidate < now_naive(line 326) anddeparture_secondscalculations (line 678) will be semantically incorrect, causing filtering/formatting errors off by the timezone difference.Normalize all parsed times to HA's local timezone using
dt_util.utc_from_timestamp()for Unix timestamps anddt_util.as_local()for conversions, then compare with the awarenowobject directly instead ofnow_naive.🛠️ Example direction (normalize parsed times to HA TZ)
- now = dt_util.now() - now_naive = now.replace(tzinfo=None) - today = now.date() + now = dt_util.now() + today = now.date() ... - if isinstance(departure_time_str, int): - departure_time_obj = datetime.fromtimestamp( - departure_time_str - ) + if isinstance(departure_time_str, int): + departure_time_obj = dt_util.as_local( + dt_util.utc_from_timestamp(departure_time_str) + ) ... - time_candidate = datetime.strptime( + time_candidate = datetime.strptime( departure_time_str, "%H:%M" ).replace( year=now.year, month=now.month, day=now.day ) - if time_candidate < now_naive - timedelta( + time_candidate = time_candidate.replace( + tzinfo=dt_util.DEFAULT_TIME_ZONE + ) + if time_candidate < now - timedelta( minutes=5 ): ... - departure_seconds = ( - effective_departure_time - now_naive - ).total_seconds() + departure_seconds = ( + effective_departure_time - now + ).total_seconds()
🤖 Fix all issues with AI agents
In @.github/renovate.json:
- Around line 6-52: The Renovate config's customManagers block uses the
deprecated key "fileMatch"; update the customManagers entry by replacing
"fileMatch" with "managerFilePatterns" (preserving the existing pattern value),
ensuring the rest of the block (customType, matchStrings, datasourceTemplate,
depNameTemplate, versioningTemplate) remains unchanged so Renovate recognizes
the regex manager; verify the "customManagers" object and its
"managerFilePatterns" key are valid JSON.
In `@docs/limitations.md`:
- Around line 29-31: The nested list under the "**Recommendation**" bullet has
incorrect indentation for MD007; fix the two nested bullet lines ("Only enable
\"Keep Route\" if you are actively using that data in a custom card." and
"Consider excluding these sensors from your Recorder configuration...") so they
use a 2-space indent relative to the parent list marker (make them aligned as
second-level list items under the "**Recommendation**" heading) to satisfy the
2-space expectation.
🧹 Nitpick comments (1)
custom_components/db_infoscreen/sensor.py (1)
69-132: Considerdt_util.parse_datetimefor string timestamps to handle offsets.Manual
strptimewon’t parse strings with timezone offsets (e.g.,...Z,+01:00) and produces naive datetimes. Usingdt_util.parse_datetime(and localizing if tzinfo is missing) would make parsing more robust and cut down the format cascade.♻️ Illustrative refactor for the string branch
- elif isinstance(departure_time, str): - try: - departure_time = datetime.strptime(departure_time, "%Y-%m-%d %H:%M:%S") - _LOGGER.debug( - "Converted departure time from string: %s", departure_time - ) - except ValueError: - try: - departure_time = datetime.strptime( - departure_time, "%Y-%m-%dT%H:%M:%S" - ) - _LOGGER.debug( - "Converted departure time from ISO string: %s", departure_time - ) - except ValueError: - try: - departure_time = datetime.strptime( - departure_time, "%Y-%m-%dT%H:%M" - ) - _LOGGER.debug( - "Converted departure time from ISO short string: %s", - departure_time, - ) - except ValueError: - ... + elif isinstance(departure_time, str): + parsed = dt_util.parse_datetime(departure_time) + if parsed is not None: + if parsed.tzinfo is None: + parsed = parsed.replace(tzinfo=dt_util.DEFAULT_TIME_ZONE) + departure_time = dt_util.as_local(parsed) + _LOGGER.debug("Converted departure time from string: %s", departure_time) + else: + ...
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@custom_components/db_infoscreen/sensor.py`:
- Around line 1-5: Run black formatting on this file to satisfy CI: run `black
--write` (or apply the same Black rules in your editor) for
custom_components/db_infoscreen/sensor.py so imports and spacing match Black's
style; ensure the module-level imports (SensorEntity, DOMAIN,
CONF_ENABLE_TEXT_VIEW, logging, dt_util, datetime) are correctly spaced and any
trailing unused imports removed so the file passes the Black check.
🧹 Nitpick comments (3)
custom_components/db_infoscreen/sensor.py (1)
220-229: Inconsistent timezone handling withdatetime.fromtimestamp().The
format_departure_timemethod was updated to usedt_utilhelpers for timezone-aware handling, butextra_state_attributesstill uses baredatetime.fromtimestamp()which produces naive datetimes in the local system timezone. For consistency and robustness, consider using the same approach here.♻️ Suggested refactor for consistency
if "scheduledTime" in dep_copy and isinstance( dep_copy["scheduledTime"], int ): - dep_copy["scheduledTime"] = datetime.fromtimestamp( - dep_copy["scheduledTime"] - ).strftime("%Y-%m-%d %H:%M:%S") + dep_copy["scheduledTime"] = dt_util.as_local( + dt_util.utc_from_timestamp(dep_copy["scheduledTime"]) + ).strftime("%Y-%m-%d %H:%M:%S") if "time" in dep_copy and isinstance(dep_copy["time"], int): - dep_copy["time"] = datetime.fromtimestamp(dep_copy["time"]).strftime( - "%Y-%m-%d %H:%M:%S" - ) + dep_copy["time"] = dt_util.as_local( + dt_util.utc_from_timestamp(dep_copy["time"]) + ).strftime("%Y-%m-%d %H:%M:%S")custom_components/db_infoscreen/__init__.py (2)
211-216: Inconsistent URL encoding for via_stations.The main station encoding (lines 125-129) converts
+to%20for proper URL encoding of spaces, butvia_stationsusesquote_plus()directly which encodes spaces as+. This inconsistency could cause issues if via station names contain spaces and the API expects%20.♻️ Suggested fix for consistent encoding
if self.via_stations: encoded_via_stations = [ - quote_plus(station.strip()) for station in self.via_stations + quote_plus(station.strip()).replace("+", "%20") for station in self.via_stations ]
570-593: Consider usingdt_util.parse_datetime()for arrival time parsing.The arrival time string parsing uses manual format attempts, while departure time parsing uses
dt_util.parse_datetime()first. Using the same approach would be more consistent and handle additional formats automatically (including timezone-aware strings).♻️ Suggested refactor for consistency
elif isinstance(scheduled_arrival, str): - try: - arrival_time = dt_util.as_local( - datetime.strptime( - scheduled_arrival, "%Y-%m-%dT%H:%M:%S" - ) - ) - except ValueError: + parsed_arr = dt_util.parse_datetime(scheduled_arrival) + if parsed_arr: + arrival_time = dt_util.as_local(parsed_arr) + else: + # Fallback for HH:MM format try: arrival_time = dt_util.as_local( datetime.strptime( - scheduled_arrival, "%Y-%m-%dT%H:%M" - ) - ) - except ValueError: - arrival_time = dt_util.as_local( - datetime.strptime( scheduled_arrival, "%H:%M" ).replace( year=today.year, month=today.month, day=today.day, ) ) + except ValueError: + _LOGGER.warning( + "Unable to parse scheduledArrival: %s", + scheduled_arrival, + )
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@custom_components/db_infoscreen/__init__.py`:
- Around line 1-10: Run Black to auto-format this file (e.g., run `black .`) so
the import block and overall file comply with the project's style; specifically
ensure the import statements shown (from homeassistant import config_entries,
from homeassistant.core import HomeAssistant, from
homeassistant.helpers.update_coordinator import DataUpdateCoordinator, from
homeassistant.util import dt as dt_util, from datetime import timedelta,
datetime, import aiohttp, import async_timeout, import logging, import json,
from urllib.parse import quote, urlencode) are formatted according to Black (one
per line, proper spacing and grouping) and commit the resulting changes.
- Around line 310-329: The code incorrectly calls dt_util.as_local on a naive
datetime (departure_time_str) which treats it as UTC; instead build an aware
datetime in Home Assistant's timezone using the existing aware now value: parse
hours/minutes from departure_time_str and create departure_time_obj by taking
now (an aware datetime) and replacing hour/minute/seconds/microseconds (e.g.,
departure_time_obj = now.replace(hour=H, minute=M, second=0, microsecond=0)),
then apply the same "if departure_time_obj < now - timedelta(minutes=5)"
adjustment; update references to dt_util.as_local and the
datetime.replace(year=..., month=..., day=...) block accordingly so the created
datetime uses the HA configured timezone and avoids incorrect UTC-to-local
conversion.
- Around line 573-582: The arrival time parsing uses dt_util.as_local on a naive
datetime (scheduled_arrival via datetime.strptime) which incorrectly assumes
UTC; instead construct an aware datetime in the local timezone before using it —
for example, after creating the datetime from datetime.strptime and
replace(year=..., month=..., day=...), attach the local tzinfo from
dt_util.now().tzinfo (or otherwise make it timezone-aware) and then use that
aware datetime in place of the current dt_util.as_local(...) call (refer to
scheduled_arrival, datetime.strptime, and dt_util.as_local in the snippet).
🧹 Nitpick comments (1)
custom_components/db_infoscreen/__init__.py (1)
700-703: Consider more specific exception handling.The broad
except Exceptionprovides resilience, but catching specific exceptions (e.g.,aiohttp.ClientError,asyncio.TimeoutError,json.JSONDecodeError) would give more informative error messages.
| from homeassistant import config_entries | ||
| from homeassistant.core import HomeAssistant | ||
| from homeassistant.helpers.update_coordinator import DataUpdateCoordinator | ||
| from homeassistant.util import dt as dt_util | ||
| from datetime import timedelta, datetime | ||
| import aiohttp | ||
| import async_timeout | ||
| import logging | ||
| import json | ||
| from urllib.parse import quote_plus, urlencode | ||
| from urllib.parse import quote, urlencode |
There was a problem hiding this comment.
Pipeline failure: Black formatting check required.
The CI reports that Black would reformat this file. Please run black . locally to fix formatting before merging.
🧰 Tools
🪛 GitHub Actions: CI Orchestrator
[error] 1-1: Black formatting check failed. 1 file would be reformatted by 'black --check .'.
🤖 Prompt for AI Agents
In `@custom_components/db_infoscreen/__init__.py` around lines 1 - 10, Run Black
to auto-format this file (e.g., run `black .`) so the import block and overall
file comply with the project's style; specifically ensure the import statements
shown (from homeassistant import config_entries, from homeassistant.core import
HomeAssistant, from homeassistant.helpers.update_coordinator import
DataUpdateCoordinator, from homeassistant.util import dt as dt_util, from
datetime import timedelta, datetime, import aiohttp, import async_timeout,
import logging, import json, from urllib.parse import quote, urlencode) are
formatted according to Black (one per line, proper spacing and grouping) and
commit the resulting changes.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@custom_components/db_infoscreen/__init__.py`:
- Around line 583-591: The arrival time construction using temp_dt and
arrival_time = now.replace(...) needs the same day-rollover logic used for
departure times: after building arrival_time from scheduled_arrival and now,
detect if arrival_time <= now (meaning the listed HH:MM is for the next day) and
if so add one day (arrival_time += timedelta(days=1)); ensure timedelta is
imported and use the existing variable names temp_dt, scheduled_arrival,
arrival_time and now so the behavior matches the departure parsing logic.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@custom_components/db_infoscreen/sensor.py`:
- Around line 4-5: The HH:MM-only fallback currently builds a datetime using
today’s date and can incorrectly remain on today when the intended departure is
tomorrow; update the parsing logic that constructs the fallback datetime (the
block(s) that set the time-only fallback into e.g. fallback_dt or departure_dt)
to compare it against the current time using dt_util.now() and a small grace
window (e.g. 1–5 minutes) and, if fallback_dt is before now minus the grace
window, add one day (use datetime.timedelta(days=1)) to roll it over to the next
day; apply the same change to both places mentioned (the HH:MM fallback blocks
around the ranges noted) so next‑day times are represented correctly.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
custom_components/db_infoscreen/__init__.py (1)
273-346: Handle non-string numeric timestamps (floats) before the HH:MM fallback.If
departure_time_stris a float timestamp (e.g.,1700000000.0), it skips theintbranch anddt_util.parse_datetime()returnsNone. The code then callsdatetime.strptime(float, "%H:%M"), which raisesTypeError: strptime() argument 1 must be str, not float. Since the exception handler only catchesValueError, theTypeErrorpropagates uncaught, breaking the entire update cycle.✅ Proposed fix
- if isinstance(departure_time_str, int): + if isinstance(departure_time_str, (int, float)): departure_time_obj = dt_util.utc_from_timestamp( - departure_time_str + int(departure_time_str) ).astimezone(now.tzinfo) @@ - except ValueError: + except (TypeError, ValueError): _LOGGER.error( "Invalid time format, skipping departure: %s", departure_time_str, ) continue
Summary by CodeRabbit
New Features
Improvements
Documentation
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.