New lovelace & info cards#133
Conversation
📝 WalkthroughWalkthroughAdds a Lovelace integration for a DB Infoscreen: integration setup that conditionally serves and injects a frontend card JS, two Lovelace strategy classes to generate dashboard/views from sensors, and a new LitElement-based custom card served from Changes
Sequence DiagramsequenceDiagram
participant HA as Home Assistant
participant Setup as Integration Setup
participant Lovelace as Lovelace Backend
participant FS as Static File Server
participant Frontend as Frontend UI
participant Card as Custom Card
HA->>Setup: async_setup(hass, config)
Setup->>Setup: init hass.data[DOMAIN], setup_lock
Setup->>Setup: try import lovelace strategies
Setup->>FS: check for .../www/db-infoscreen-card.js
alt file exists
Setup->>FS: register /db_infoscreen/card.js (no-cache)
Setup->>Lovelace: add_extra_js_url('/db_infoscreen/card.js') (once)
end
HA->>Lovelace: request generated dashboard/view
Lovelace->>HA: enumerate sensor.* states, filter departures/dbf
Lovelace->>HA: build views with `custom:db-infoscreen-card` entries
Frontend->>FS: fetch /db_infoscreen/card.js
Frontend->>Card: register custom element & editor
Card->>HA: read entity state(s) and attributes
Card->>Card: compute sentiment/clock/render rows
Frontend->>Card: user interactions (expand, action)
Card->>HA: call services / TTS / persistent notifications
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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: 5
🧹 Nitpick comments (2)
custom_components/db_infoscreen/www/db-infoscreen-card.js (1)
237-245: Placeholderalert()in_runActionand silent no-op in_sharedegrade the card's advertised actions.
_runAction('exit_sync')and_runAction('coffee')simply raise a browseralert()— buttons in_renderDetailstherefore don't perform anything meaningful. Similarly,_shareis a no-op whenevernavigator.shareis unavailable (desktop browsers, insecure contexts) with no user feedback. Given the card is labeled "FINAL" in the footer, consider either:
- wiring these to real HA service calls (e.g.,
this.hass.callService("notify", ...),script.turn_on, etc.) configurable viaconfig.actions, or- hiding the buttons entirely until implemented, to avoid shipping dead UI.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@custom_components/db_infoscreen/www/db-infoscreen-card.js` around lines 237 - 245, Replace the placeholder alert in _runAction and the silent no-op in _share with real, user-visible behavior: update _runAction(type) to look up an action mapping in the component config (e.g., config.actions[type]) and invoke Home Assistant services via this.hass.callService (for example notify, script.turn_on, or other service and data from the mapping) or hide/disable the corresponding buttons in _renderDetails when no action is configured; for _share(dep) keep navigator.share when available but add a fallback that copies the text to the clipboard (navigator.clipboard.writeText) and surfaces feedback by calling a HA notification service (e.g., this.hass.callService('persistent_notification', 'create', ... ) or notify) so users know the share/copy succeeded or failed. Ensure you reference and modify _runAction, _share and _renderDetails to check config.actions and provide the described fallbacks.custom_components/db_infoscreen/__init__.py (1)
103-109: Narrow the import-only try/except toImportError.The
try/excepthere guards a plainfrom .lovelace import .... That can raiseImportError(module missing / transitive import failures), butAttributeError/KeyErrorcannot be raised by the import statement itself — including them is misleading. If the intent is to keep the block resilient to anything the imported module's top-level code could do, that's an argument for keeping the module trivial instead.- except (ImportError, AttributeError, KeyError): + except ImportError: _LOGGER.exception("Could not import Lovelace strategies")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@custom_components/db_infoscreen/__init__.py` around lines 103 - 109, The except block around the import of DBInfoscreenDashboardStrategy and DBInfoscreenViewStrategy is too broad; restrict it to only catch ImportError. Update the try/except that imports from .lovelace (the block referencing DBInfoscreenDashboardStrategy and DBInfoscreenViewStrategy) to catch only ImportError instead of (ImportError, AttributeError, KeyError), leaving the import statement unchanged and keeping the _LOGGER.exception call for failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@custom_components/db_infoscreen/__init__.py`:
- Around line 114-127: The static path registration is executed on every
async_setup_entry and causes RuntimeError on duplicate registration; guard the
call to hass.http.async_register_static_paths with the same global flag used for
add_extra_js_url (hass.data[DOMAIN]["extra_js_registered"]) so the static path
for card_path is only registered once per Home Assistant process. Specifically,
around the call to hass.http.async_register_static_paths (and the
StaticPathConfig creation), check hass.data[DOMAIN].get("extra_js_registered")
first and only register the static path when that flag is not set, then set
hass.data[DOMAIN]["extra_js_registered"] = True after successful registration
(keep the existing add_extra_js_url usage intact).
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js`:
- Around line 278-280: The code attempts to push to window.customCards without
ensuring it exists, causing a TypeError; fix by guarding initialization before
pushing: check if window.customCards is falsy and assign it an empty array, then
push the card metadata for "db-infoscreen-card"; leave the customElements.define
calls for DBInfoscreenCard and DBInfoscreenCardEditor unchanged so registration
still occurs even when window.customCards was undefined.
- Around line 167-193: The rendering crashes when dep.train is missing because
_renderRow calls dep.train.toLowerCase() unguarded; update _renderRow to safely
derive a train label (e.g., const trainLabel = dep.train || dep.line || '') and
use trainLabel.toString() or an empty string before calling toLowerCase(), then
use that trainLabel for both the line-badge class check and the displayed text
(replace dep.train usages with the safe trainLabel and ensure the badge class
check uses trainLabel.toLowerCase().includes('ice') only after the fallback).
- Around line 259-276: DBInfoscreenCardEditor currently only declares _config as
reactive and accesses this.hass in _localize and render before hass may be
assigned, causing a TypeError and showing "undefined" values; update static get
properties to include hass as a reactive property, make _localize defensively
handle missing this.hass (e.g., default to 'en' when this.hass or
this.hass.language is falsy), and ensure render uses safe defaults for values
(use empty string when this._config or fields like entity/weather_entity are
undefined) so the editor can render immediately; touch the class' static get
properties, _localize, and render methods to implement these changes.
- Around line 184-189: The time cell currently uses dep.time which is often
undefined; update the rendering in db-infoscreen-card.js to use
dep.departure_current as the primary display value, falling back to
dep.scheduledDeparture if departure_current is missing, and keep the existing
delay logic that reads dep.delay; specifically replace the content inside the
<div class="time-v"> (where dep.time is used) to render departure_current ||
scheduledDeparture, preserving formatting and the surrounding delay check that
parses dep.delay.
---
Nitpick comments:
In `@custom_components/db_infoscreen/__init__.py`:
- Around line 103-109: The except block around the import of
DBInfoscreenDashboardStrategy and DBInfoscreenViewStrategy is too broad;
restrict it to only catch ImportError. Update the try/except that imports from
.lovelace (the block referencing DBInfoscreenDashboardStrategy and
DBInfoscreenViewStrategy) to catch only ImportError instead of (ImportError,
AttributeError, KeyError), leaving the import statement unchanged and keeping
the _LOGGER.exception call for failures.
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js`:
- Around line 237-245: Replace the placeholder alert in _runAction and the
silent no-op in _share with real, user-visible behavior: update _runAction(type)
to look up an action mapping in the component config (e.g.,
config.actions[type]) and invoke Home Assistant services via
this.hass.callService (for example notify, script.turn_on, or other service and
data from the mapping) or hide/disable the corresponding buttons in
_renderDetails when no action is configured; for _share(dep) keep
navigator.share when available but add a fallback that copies the text to the
clipboard (navigator.clipboard.writeText) and surfaces feedback by calling a HA
notification service (e.g., this.hass.callService('persistent_notification',
'create', ... ) or notify) so users know the share/copy succeeded or failed.
Ensure you reference and modify _runAction, _share and _renderDetails to check
config.actions and provide the described fallbacks.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 05634a2e-6ffa-4b71-bb73-aa3250258911
📒 Files selected for processing (3)
custom_components/db_infoscreen/__init__.pycustom_components/db_infoscreen/lovelace.pycustom_components/db_infoscreen/www/db-infoscreen-card.js
🚧 Files skipped from review as they are similar to previous changes (1)
- custom_components/db_infoscreen/lovelace.py
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
custom_components/db_infoscreen/www/db-infoscreen-card.js (2)
277-278:setConfigshould validate required fields;getCardSizemay under-reserve space.Two small concerns:
setConfigcurrently accepts anything. Per the HA custom-card contract, it should throw a descriptiveErrorwhen the requiredentityis missing so the Lovelace editor can surface the problem instead of the card silently showing "Initializing system…" forever.getCardSize()changed tocount + 1. Given the heavy row styling (padding: 25px 45px,platform-v60px, 1.3rem destination), each row visually occupies more than one masonry unit, so 6 departures return size7while the rendered height is much larger. The previous* 1.5factor was closer to reality.♻️ Suggested refactor
- setConfig(config) { this.config = config; } - getCardSize() { return (this.config.count || DEFAULT_COUNT) + 1; } + setConfig(config) { + if (!config || !config.entity) { + throw new Error("You need to define an `entity` in the card configuration"); + } + this.config = config; + } + getCardSize() { return Math.ceil((this.config.count || DEFAULT_COUNT) * 1.5) + 1; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@custom_components/db_infoscreen/www/db-infoscreen-card.js` around lines 277 - 278, Update setConfig to validate required fields: check that config and config.entity (or whichever required field is used) exist and throw a descriptive Error (e.g., "Missing required 'entity' in card configuration") instead of silently accepting any input; then assign this.config = config. Update getCardSize to better approximate rendered height by reverting to a multiplier (use Math.ceil((this.config.count || DEFAULT_COUNT) * 1.5) + 1) so rows reserve more vertical space (reference getCardSize, DEFAULT_COUNT and config.count), ensuring the result is an integer and still adds the header row.
219-225:_renderParticlesassumesweatheris a string.
weathercomes fromhass.states[...]?.state || 'sunny'at line 143, which is almost always a string, but a degenerate entity could expose a non-string state (or'').weather.includes(...)throws onundefined/numbers. The|| 'sunny'defaultsundefined/null, but an empty string passes through and.includeson a number would throw. A cheap guard keeps render() robust:🛡️ Proposed fix
_renderParticles(weather) { - if (!['rainy', 'snowy', 'pouring'].some(s => weather.includes(s))) return html``; - const isSnow = weather.includes('snow'); + const w = String(weather || ''); + if (!['rainy', 'snowy', 'pouring'].some(s => w.includes(s))) return html``; + const isSnow = w.includes('snow');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@custom_components/db_infoscreen/www/db-infoscreen-card.js` around lines 219 - 225, The _renderParticles function currently calls weather.includes(...) assuming weather is a string; add a defensive guard at the top of _renderParticles to ensure weather is a string (or coerce/replace non-strings/empty values with a safe default like 'sunny') before using .includes, then use that sanitized value (e.g., weatherStr) for both the includes checks and computing isSnow; update references to weather in _renderParticles accordingly so unexpected non-string states or empty strings won't throw.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js`:
- Around line 240-248: The _runAction method currently assumes action.service is
a valid "domain.service" string; validate that this.config.actions[type] exists
and that action.service is a non-empty string containing a '.' before calling
split — e.g., check typeof action.service === 'string' &&
action.service.includes('.') — then split into domain and service and only call
this.hass.callService(domain, service, action.data || {}) when both parts are
truthy; otherwise log a clear warning referencing _runAction and the malformed
action.service and return early to avoid passing undefined to hass.callService.
- Around line 250-254: The _announce function currently assumes
SpeechSynthesisUtterance and window.speechSynthesis exist; update _announce to
guard against missing APIs by checking typeof SpeechSynthesisUtterance !==
'undefined' and window.speechSynthesis before creating/using a
SpeechSynthesisUtterance, and wrap the speak call in a try/catch to avoid
throwing on unsupported platforms; also normalize the language by converting
this.hass.language to a BCP-47-like tag (e.g., use this.hass.language or append
a region fallback) before assigning to utterance.lang, and silently fail or log
a warning if speech is not available so the button handler doesn’t propagate
errors.
- Around line 256-268: The _share method uses hardcoded English strings and
leaves navigator.clipboard.writeText unhandled; add share-specific translation
keys (e.g. "share.train", "share.destination", "share.minutes_delay",
"share.clipboard_copied", etc.) to TRANSLATIONS and replace the literal
fragments in _share with calls to this._localize(...) so the title/text use
localized strings (reference _share and TRANSLATIONS/_localize). Also handle
clipboard rejection by adding a .catch on navigator.clipboard.writeText(...)
that calls this.hass.callService('persistent_notification', 'create', { title:
'DB Infoscreen', message: this._localize('share.clipboard_failed') }) or similar
localized error, and optionally console.error the caught error to aid debugging.
---
Nitpick comments:
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js`:
- Around line 277-278: Update setConfig to validate required fields: check that
config and config.entity (or whichever required field is used) exist and throw a
descriptive Error (e.g., "Missing required 'entity' in card configuration")
instead of silently accepting any input; then assign this.config = config.
Update getCardSize to better approximate rendered height by reverting to a
multiplier (use Math.ceil((this.config.count || DEFAULT_COUNT) * 1.5) + 1) so
rows reserve more vertical space (reference getCardSize, DEFAULT_COUNT and
config.count), ensuring the result is an integer and still adds the header row.
- Around line 219-225: The _renderParticles function currently calls
weather.includes(...) assuming weather is a string; add a defensive guard at the
top of _renderParticles to ensure weather is a string (or coerce/replace
non-strings/empty values with a safe default like 'sunny') before using
.includes, then use that sanitized value (e.g., weatherStr) for both the
includes checks and computing isSnow; update references to weather in
_renderParticles accordingly so unexpected non-string states or empty strings
won't throw.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a0ce8f2f-e353-4578-a63e-812a37167661
📒 Files selected for processing (2)
custom_components/db_infoscreen/__init__.pycustom_components/db_infoscreen/www/db-infoscreen-card.js
🚧 Files skipped from review as they are similar to previous changes (1)
- custom_components/db_infoscreen/init.py
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
custom_components/db_infoscreen/www/db-infoscreen-card.js (1)
5-6:⚠️ Potential issue | 🟠 MajorStop acquiring Lit from Home Assistant internals.
This still evaluates
Object.getPrototypeOf(customElements.get("ha-panel-lovelace"))at module load and readshtml/cssfrom the prototype. If the Lovelace panel is not registered yet, the module throws beforedb-infoscreen-cardis defined. Home Assistant’s Lit migration note calls this pattern “not recommended”, and current custom-card examples importLitElement,html, andcssas module bindings instead: HA Lit migration, HA custom-card docs, Lit docs.Proposed direction
-const LitElement = window.LitElement || Object.getPrototypeOf(customElements.get("ha-panel-lovelace")); -const { html, css } = LitElement.prototype; +import { LitElement, html, css } from "https://unpkg.com/lit@3/index.js?module";Prefer bundling
litinto the served card instead of relying on a CDN if this integration should work offline.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@custom_components/db_infoscreen/www/db-infoscreen-card.js` around lines 5 - 6, The module currently reads Lit internals at import time via Object.getPrototypeOf(customElements.get("ha-panel-lovelace")) and destructures html/css from LitElement.prototype, which can throw if the Lovelace panel isn't registered; update db-infoscreen-card.js to stop acquiring Lit from Home Assistant internals and instead import/bundle Lit directly so you get LitElement, html, and css as module bindings (or bundle lit into the served card) rather than using LitElement = window.LitElement || Object.getPrototypeOf(customElements.get("ha-panel-lovelace")) and reading html/css from the prototype.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js`:
- Around line 353-356: Guard against duplicate registration by checking for
existing registrations and picker entries before defining/pushing: use
customElements.get("db-infoscreen-card-editor") and
customElements.get("db-infoscreen-card") to only call customElements.define for
DBInfoscreenCardEditor and DBInfoscreenCard if they are not already registered,
and check window.customCards (if present) for an existing entry with type ===
"db-infoscreen-card" before pushing the object to avoid duplicate picker
entries.
- Around line 242-244: _calcScore currently treats cancelled departures as
having zero delay and counts them as stable; update the filter in _calcScore to
skip cancelled rows using the same cancellation flag logic used in _renderRow
(e.g., check !d.cancelled or the property used there) so only non-cancelled
departures with (d.delay || 0) < 5 are counted when computing the stability
percentage.
- Around line 213-214: The exit_sync and coffee buttons currently render even
when no service action is configured, causing dead controls that just log
warnings; update the template in db-infoscreen-card.js to only render the
exit_sync button when the corresponding action exists (e.g., this.actions &&
this.actions.exit_sync) and only render the coffee button when isLate is true
AND the corresponding action exists (e.g., this.actions && this.actions.coffee);
keep the click handlers calling _runAction('exit_sync') and _runAction('coffee')
but guard the buttons' rendering on the presence of those action keys so buttons
are omitted when lovelace.py does not supply an actions object.
- Around line 287-288: The call to navigator.share in db-infoscreen-card.js can
reject (e.g., user cancels) and is currently unhandled; update the share branch
where navigator.share({ title: dep.train || 'DB', text: text }) is invoked to
handle promise rejection by either awaiting it inside a try/catch or appending a
.catch handler that silently ignores cancellation and logs unexpected errors via
the existing logger/console; ensure you reference navigator.share and keep the
same payload (title: dep.train || 'DB', text: text).
---
Duplicate comments:
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js`:
- Around line 5-6: The module currently reads Lit internals at import time via
Object.getPrototypeOf(customElements.get("ha-panel-lovelace")) and destructures
html/css from LitElement.prototype, which can throw if the Lovelace panel isn't
registered; update db-infoscreen-card.js to stop acquiring Lit from Home
Assistant internals and instead import/bundle Lit directly so you get
LitElement, html, and css as module bindings (or bundle lit into the served
card) rather than using LitElement = window.LitElement ||
Object.getPrototypeOf(customElements.get("ha-panel-lovelace")) and reading
html/css from the prototype.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 68d337cb-77b4-48c3-b420-e416c097b0d4
📒 Files selected for processing (2)
custom_components/db_infoscreen/__init__.pycustom_components/db_infoscreen/www/db-infoscreen-card.js
🚧 Files skipped from review as they are similar to previous changes (1)
- custom_components/db_infoscreen/init.py
| <button class="action-btn" @click="${() => this._runAction('exit_sync')}"><ha-icon icon="mdi:sync"></ha-icon> ${this._localize('action_sync')}</button> | ||
| ${isLate ? html`<button class="action-btn" @click="${() => this._runAction('coffee')}"><ha-icon icon="mdi:coffee"></ha-icon> ${this._localize('action_coffee')}</button>` : ''} |
There was a problem hiding this comment.
Hide service-backed buttons when no action is configured.
The generated Lovelace config in custom_components/db_infoscreen/lovelace.py:105-110 does not pass an actions object, so these buttons render as dead controls and only log warnings when clicked. Render exit_sync/coffee only when the corresponding action exists.
Proposed fix
_renderDetails(dep) {
const isLate = (dep.delay || 0) > 5;
+ const hasAction = (type) => Boolean(this.config?.actions?.[type]?.service);
return html`
<tr>
<td colspan="3">
<div class="details-area">
<div style="font-size:0.75rem; text-transform:uppercase; color:rgba(255,255,255,0.4); margin-bottom:10px">${this._localize('insight_title')}</div>
<div class="context-actions">
- <button class="action-btn" `@click`="${() => this._runAction('exit_sync')}"><ha-icon icon="mdi:sync"></ha-icon> ${this._localize('action_sync')}</button>
- ${isLate ? html`<button class="action-btn" `@click`="${() => this._runAction('coffee')}"><ha-icon icon="mdi:coffee"></ha-icon> ${this._localize('action_coffee')}</button>` : ''}
+ ${hasAction('exit_sync') ? html`<button class="action-btn" `@click`="${() => this._runAction('exit_sync')}"><ha-icon icon="mdi:sync"></ha-icon> ${this._localize('action_sync')}</button>` : ''}
+ ${isLate && hasAction('coffee') ? html`<button class="action-btn" `@click`="${() => this._runAction('coffee')}"><ha-icon icon="mdi:coffee"></ha-icon> ${this._localize('action_coffee')}</button>` : ''}
<button class="action-btn" `@click`="${() => this._announce(dep)}"><ha-icon icon="mdi:bullhorn"></ha-icon> ${this._localize('action_announce')}</button>
<button class="action-btn" `@click`="${() => this._share(dep)}"><ha-icon icon="mdi:share-variant"></ha-icon> ${this._localize('action_share')}</button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js` around lines 213 -
214, The exit_sync and coffee buttons currently render even when no service
action is configured, causing dead controls that just log warnings; update the
template in db-infoscreen-card.js to only render the exit_sync button when the
corresponding action exists (e.g., this.actions && this.actions.exit_sync) and
only render the coffee button when isLate is true AND the corresponding action
exists (e.g., this.actions && this.actions.coffee); keep the click handlers
calling _runAction('exit_sync') and _runAction('coffee') but guard the buttons'
rendering on the presence of those action keys so buttons are omitted when
lovelace.py does not supply an actions object.
| _calcScore(deps) { | ||
| if (!deps.length) return 100; | ||
| return Math.round((deps.filter(d => (d.delay || 0) < 5).length / deps.length) * 100); |
There was a problem hiding this comment.
Do not count cancelled departures as stable.
A cancelled row with no delay currently satisfies (d.delay || 0) < 5, so a board full of cancellations can still show a high stability score. Use the cancellation flags already handled in _renderRow.
Proposed fix
_calcScore(deps) {
if (!deps.length) return 100;
- return Math.round((deps.filter(d => (d.delay || 0) < 5).length / deps.length) * 100);
+ const stableCount = deps.filter((d) => {
+ if (d.is_cancelled || d.isCancelled) return false;
+ const delay = parseInt(d.delay ?? 0, 10);
+ return (Number.isFinite(delay) ? delay : 0) < 5;
+ }).length;
+ return Math.round((stableCount / deps.length) * 100);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js` around lines 242 -
244, _calcScore currently treats cancelled departures as having zero delay and
counts them as stable; update the filter in _calcScore to skip cancelled rows
using the same cancellation flag logic used in _renderRow (e.g., check
!d.cancelled or the property used there) so only non-cancelled departures with
(d.delay || 0) < 5 are counted when computing the stability percentage.
| if (navigator.share) { | ||
| navigator.share({ title: dep.train || 'DB', text: text }); |
There was a problem hiding this comment.
Handle navigator.share() rejection too.
The clipboard branch is guarded now, but the Web Share branch can still reject and produce an unhandled promise when the user cancels or the browser denies sharing.
Proposed fix
if (navigator.share) {
- navigator.share({ title: dep.train || 'DB', text: text });
+ navigator.share({ title: dep.train || 'DB', text })
+ .catch((err) => {
+ if (err?.name !== 'AbortError') {
+ console.warn("[DB Infoscreen] Share failed", err);
+ }
+ });
} else if (navigator.clipboard) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (navigator.share) { | |
| navigator.share({ title: dep.train || 'DB', text: text }); | |
| if (navigator.share) { | |
| navigator.share({ title: dep.train || 'DB', text }) | |
| .catch((err) => { | |
| if (err?.name !== 'AbortError') { | |
| console.warn("[DB Infoscreen] Share failed", err); | |
| } | |
| }); | |
| } else if (navigator.clipboard) { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js` around lines 287 -
288, The call to navigator.share in db-infoscreen-card.js can reject (e.g., user
cancels) and is currently unhandled; update the share branch where
navigator.share({ title: dep.train || 'DB', text: text }) is invoked to handle
promise rejection by either awaiting it inside a try/catch or appending a .catch
handler that silently ignores cancellation and logs unexpected errors via the
existing logger/console; ensure you reference navigator.share and keep the same
payload (title: dep.train || 'DB', text: text).
| customElements.define("db-infoscreen-card-editor", DBInfoscreenCardEditor); | ||
| customElements.define("db-infoscreen-card", DBInfoscreenCard); | ||
| window.customCards = window.customCards || []; | ||
| window.customCards.push({ type: "db-infoscreen-card", name: "DB Infoscreen I18N EDITION", preview: true }); |
There was a problem hiding this comment.
Guard duplicate card registration.
If users already added this card manually and the integration also injects /db_infoscreen/card.js, the second load can throw on customElements.define() and/or duplicate the picker entry.
Proposed fix
-customElements.define("db-infoscreen-card-editor", DBInfoscreenCardEditor);
-customElements.define("db-infoscreen-card", DBInfoscreenCard);
+if (!customElements.get("db-infoscreen-card-editor")) {
+ customElements.define("db-infoscreen-card-editor", DBInfoscreenCardEditor);
+}
+if (!customElements.get("db-infoscreen-card")) {
+ customElements.define("db-infoscreen-card", DBInfoscreenCard);
+}
window.customCards = window.customCards || [];
-window.customCards.push({ type: "db-infoscreen-card", name: "DB Infoscreen I18N EDITION", preview: true });
+if (!window.customCards.some((card) => card.type === "db-infoscreen-card")) {
+ window.customCards.push({ type: "db-infoscreen-card", name: "DB Infoscreen I18N EDITION", preview: true });
+}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@custom_components/db_infoscreen/www/db-infoscreen-card.js` around lines 353 -
356, Guard against duplicate registration by checking for existing registrations
and picker entries before defining/pushing: use
customElements.get("db-infoscreen-card-editor") and
customElements.get("db-infoscreen-card") to only call customElements.define for
DBInfoscreenCardEditor and DBInfoscreenCard if they are not already registered,
and check window.customCards (if present) for an existing entry with type ===
"db-infoscreen-card" before pushing the object to avoid duplicate picker
entries.
Summary by CodeRabbit
New Features
Bug Fixes / Improvements