Skip to content

Repo improvements#88

Merged
FaserF merged 15 commits intomainfrom
repo-improvements
Jan 29, 2026
Merged

Repo improvements#88
FaserF merged 15 commits intomainfrom
repo-improvements

Conversation

@FaserF
Copy link
Copy Markdown
Owner

@FaserF FaserF commented Jan 29, 2026

Summary by CodeRabbit

  • New Features

    • Option to exclude cancelled trains from displays
    • Tracked backend API version via a dedicated version file
  • Improvements

    • Robust departure time parsing and local-time handling
    • More reliable async data fetching and error handling
  • Documentation

    • Launched comprehensive docs site and added many guides; README updated
  • Tests

    • Expanded coverage for coordinator, sensor, options flow, stability, and translations
  • Chores

    • CI workflows for docs deployment and backend API update checks; Renovate configuration added

✏️ Tip: You can customize this high-level summary in your review settings.

@FaserF FaserF self-assigned this Jan 29, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

Adds .backend_version, Renovate rules, two GitHub Actions, MkDocs documentation and requirements, README updates, many tests, and an "exclude_cancelled" option wired through coordinator, sensor, and config flow; replaces synchronous HTTP/time handling with aiohttp async context managers and Home Assistant dt_util utilities.

Changes

Cohort / File(s) Summary
Backend version & Renovate
\.backend_version, .github/renovate.json
Adds .backend_version containing 4.37.20 and Renovate config: custom manager for derf/db-fakedisplay, packageRules (labels/pr notes), pip_requirements handling, and enables GitHub Actions automation.
GitHub Actions
.github/workflows/backend-api-update.yml, .github/workflows/deploy_docs.yml
Adds workflow to run tests when .backend_version is modified and post version comments for renovate/* branches; adds docs deploy workflow to publish MkDocs to gh-pages.
Coordinator & Constants
custom_components/db_infoscreen/__init__.py, custom_components/db_infoscreen/const.py
Introduces CONF_EXCLUDE_CANCELLED, exposes exclude_cancelled on coordinator, moves to aiohttp async context usage with response.raise_for_status()/JSON parsing, improves station/via encoding, robust error and timeout handling, and unified dt_util-based time handling.
Config flow / Options
custom_components/db_infoscreen/config_flow.py
Adds _get_config_value() helper to resolve defaults from options/data, uses regex splitting for via_stations, surfaces exclude_cancelled in options schema, and centralizes option defaults.
Sensor time parsing & formatting
custom_components/db_infoscreen/sensor.py
Migrates parsing/formatting to Home Assistant dt_util, supports epoch/ISO/HH:MM inputs, normalizes to local time, applies next-day grace windows, and preserves last valid values on parse failures.
Translations / Strings
custom_components/db_infoscreen/strings.json, custom_components/db_infoscreen/translations/en.json, custom_components/db_infoscreen/translations/de.json
Adds exclude_cancelled key and corresponding English/German translations for filter options.
Documentation (MkDocs + README)
mkdocs.yml, docs/*, docs/requirements.txt, README.md
Adds full MkDocs site config and multiple docs (index, installation, configuration, advanced, development, limitations, troubleshooting), docs requirements, and updates README image paths and docs link.
Tests
tests/*
tests/test_config_flow.py, tests/test_coordinator.py, tests/test_options_flow_comprehensive.py, tests/test_sensor.py, tests/test_stability.py, tests/test_translations.py
Adds numerous tests covering options-flow persistence and delimiter handling, coordinator URL/encoding and exclude-cancelled filtering, sensor formatting and text view, stability/error handling and large-response protection, and translations consistency. Many aiohttp/time mocks and async tests added.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I nibble bytes and backend tags,

Docs unfurl and workflows play,
Tests hop in to guard the tracks,
Cancelled trains skip on their way,
Hoppity-hop — the repo's bright!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Repo improvements' is vague and generic, using non-descriptive language that does not convey specific information about the changeset's main objectives. Consider using a more descriptive title that captures the primary changes, such as 'Add comprehensive documentation, backend versioning, and testing infrastructure' or focus on the most significant change.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 94.74% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

@github-actions github-actions bot added documentation Improvements or additions to documentation backend frontend labels Jan 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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() from homeassistant.util instead, and cache a single today value 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_setup is 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_version doesn'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 using isinstance() and handling list values.

Two observations:

  1. Line 38 uses type(x) is dict which doesn't account for dict subclasses. Prefer isinstance(x, dict).
  2. 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

Comment thread .github/renovate.json
Comment thread docs/limitations.md
Comment thread mkdocs.yml Outdated
Comment thread tests/test_config_flow.py
Comment thread tests/test_sensor.py Outdated
Comment thread tests/test_stability.py
Comment thread tests/test_stability.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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‑aware dt_util.now() with naive datetimes in different timezone contexts.

now is HA‑localized (aware), but parsed departures use naive datetime.fromtimestamp() and datetime.strptime(), which interpret times as system local—not HA local. If HA's configured timezone differs from the host system's timezone, comparisons like time_candidate < now_naive (line 326) and departure_seconds calculations (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 and dt_util.as_local() for conversions, then compare with the aware now object directly instead of now_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: Consider dt_util.parse_datetime for string timestamps to handle offsets.

Manual strptime won’t parse strings with timezone offsets (e.g., ...Z, +01:00) and produces naive datetimes. Using dt_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:
+                ...

Comment thread .github/renovate.json
Comment thread docs/limitations.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 with datetime.fromtimestamp().

The format_departure_time method was updated to use dt_util helpers for timezone-aware handling, but extra_state_attributes still uses bare datetime.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 %20 for proper URL encoding of spaces, but via_stations uses quote_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 using dt_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,
+                                        )

Comment thread custom_components/db_infoscreen/sensor.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 Exception provides resilience, but catching specific exceptions (e.g., aiohttp.ClientError, asyncio.TimeoutError, json.JSONDecodeError) would give more informative error messages.

Comment on lines 1 to +10
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
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.

⚠️ Potential issue | 🟡 Minor

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.

Comment thread custom_components/db_infoscreen/__init__.py
Comment thread custom_components/db_infoscreen/__init__.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread custom_components/db_infoscreen/__init__.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread custom_components/db_infoscreen/sensor.py Outdated
@FaserF FaserF merged commit 6c62423 into main Jan 29, 2026
9 of 10 checks passed
@FaserF FaserF deleted the repo-improvements branch January 29, 2026 11:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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_str is a float timestamp (e.g., 1700000000.0), it skips the int branch and dt_util.parse_datetime() returns None. The code then calls datetime.strptime(float, "%H:%M"), which raises TypeError: strptime() argument 1 must be str, not float. Since the exception handler only catches ValueError, the TypeError propagates 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend documentation Improvements or additions to documentation frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant