Repository navigation
Fix/audit 2026 10 - #6
Merged
Merged
Conversation
…tions
The dial showed the thermostat presets, but everything else had to live
around it: times pinned to one corner, badges pinned to the other, two
unlabelled dropdowns underneath. Adding the covers-left-open warning made
the crowding obvious — its list of covers only fitted in a hover tooltip,
which does not exist on a touch screen.
The presets become a segmented control, one colour per mode rather than red
for all of them, and everything else becomes a labelled row. Nothing floats
against the card edge any more, and the covers left open are written out.
The two duration settings were "Surcharge" and "Anticipation", which say
nothing about what they do. They are now "Garder mon choix manuel" and
"Anticiper les événements", collapsed to one line showing their value, and
folded open on click to a minus/plus stepper over the same presets as
before. Under the stepper, a sentence states the consequence rather than the
setting: "Télétravail commencera à 20:17 au lieu de 21:17." One opens at a
time and folds back after six seconds.
When nothing is wrong the card says nothing: the alert row exists only while
heat protection is on or covers were left up. The "Thermostat" header goes
too, with the name and show_title options and the editor switch that drove
them — the card sits under its own dashboard heading and repeated the word
for nothing.
Preset colours are read from the select's option_map keys, and the card no
longer depends on that attribute being there: the active preset comes from
current_key when present, and the colour class falls back to the display
label ("Eteint", "Clim", "Fan" and their neighbours map back to a known
mode) rather than producing a class no rule matches. The simulated selects
in the test Home Assistant now publish option_map, option_keys and
current_key, the same three the integration publishes.
Also fixes a trap this uncovered: localize() returns the key when a string
is missing, so every `localize(...) || fallback` in the card was dead and
"Éteint" rendered as "thermostat.off". A t() helper treats an echoed key as
missing, and the missing thermostat.off string is added to both languages.
The dial is removed with its component, and with it _formatRelativeTime and
the two select handlers the steppers replace. It is in the history if the
layout is ever wanted back.
Verified in a browser against the built bundle: presets localised with the
active one marked and each colour rule shipping, settings collapsed by
default and one-at-a-time, the consequence sentences correct, the steppers
calling number.set_value with the neighbouring preset, and no alert markup
at all in the calm state.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
package.json already declares "license": "MIT" and the README says MIT, but the repository shipped no licence text, which leaves reuse legally unclear. Same holder and wording as the companion integration. Audit: T-1 (stack note: missing LICENSE) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
custom-card-helpers was declared as a runtime dependency but never imported, so it only added install weight and audit noise. The bundle referenced homeshift-card.js.map while dist/*.map is git-ignored: HACS users got a 404 in devtools for a map that is never shipped. The bundle is minified, reproducible from the tagged sources, and small, so sourcemaps are disabled (rollup and tsconfig) instead of versioning a .map that would double the dist diff on every change. Audit: T-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The repository had no lint, no test and no type check beyond the build, which is how the editor key typo and the dead localize fallbacks went unnoticed. Adds: - vitest + happy-dom (Lit components render in a DOM without a browser), - ESLint flat config with typescript-eslint, - "typecheck" (tsc --noEmit over src and tests), "lint" and "test" scripts, used by the upcoming CI workflow. Audit: T-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The visual editor read and wrote "hermostat_mode_tentity" while the card reads "thermostat_mode_entity": picking a thermostat in the editor saved a key the card ignored, and an existing YAML config showed an empty picker. The editor also only covered 8 of the 13 options and kept its own copy of the config interface, which is how the two drifted apart. HomeShiftCardConfig and a key/domain/default table now live in src/types.ts; the card derives its defaults and watched entities from the table and the editor generates one picker per entry, so a new option cannot be forgotten in one of them. Missing editor labels are added in en/fr. Tests assert that every key the editor writes is a key the card reads, that every entity option has a picker and a label. Audit: B-1, A-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
localize() returned the key itself when a translation was missing, so every `localize(...) || "default"` in the card was dead code, a key missing from fr.json showed up raw instead of in English, and the preview stub displayed "preview.day_mode_state" with no option or arc segment highlighted (the preview section existed in no dictionary). Lookup is now: user language, English, optional fallback argument, key. The dead `||` fallbacks are removed; thermostat modes unknown to the card fall back to the integration's display value; the preview stub is built from new preview.* strings and the existing thermostat labels so its state matches an option. Tests cover the lookup chain, parameters and en/fr key parity. Audit: A-3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Missing entities (B-3): the stub day/thermostat data was used on every
dashboard, so a typo in day_mode_entity produced a card that looked
functional. The stub is now reserved for the card picker / editor
preview (HA's `preview` property); elsewhere the card shows the
existing, previously unused card.entity_not_found message.
- Next mode (B-4): unavailable was not filtered and an "unknown"
next_mode_at was truthy, giving an invalid Date and "unknown" in the
dial centre. A shared isUsable() now filters both sensors and the
cover times; an unparsable timestamp renders nothing.
- Number dropdowns (B-5): Number("unavailable") gave NaN, highlighted
the dropdown and set value "NaN"; a value outside the presets (25 min)
had no option. Values are read with Number.isFinite, an ad hoc option
is injected for off-preset values, the dropdown is disabled while the
entity is unavailable, and both dropdowns share one renderer (the
override dropdown is now also hidden when its entity is not set).
Audit: B-3, B-4, B-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… code The card re-rendered every 30 seconds although entity changes already trigger renders and the only time-dependent output is the Today / Tomorrow prefix, which changes at midnight. A single timeout now fires just after midnight and reschedules itself; it is still cleared in disconnectedCallback. Removed, never called or never rendered: _formatRelativeTime, onOffButtonClick, the .off-row / .center-button / hui-card styles, the slider's _onSvgClick / _getPercentageFromEvent, its angle constants, @query svg and its unused hass / entityId properties, and the card.next_scan / card.in / card.past strings. noUnusedLocals and noUnusedParameters are enabled so dead private members fail the build. Audit: P-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
toLocaleTimeString([], ...) used the browser locale and ignored hass.locale.time_format, so a user who chose 24 h in their Home Assistant profile could still see "02:05 PM" (and the date used the browser language). Time and date now follow hass.locale: forced 12/24 h, "system" = browser locale, default = HA language convention. Audit: A-4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Dial: the SVG had no role, no tab stop and no keyboard handling. Each mode is now a role="radio" group (accessible name = localized label, aria-checked = active) inside a labelled radiogroup, with a roving tabindex; arrows/Home/End move the focus, Enter/Space switch the mode. Arrows deliberately do not switch, so browsing the modes does not fire a thermostat change per key press. - Badges: the covers-left-open list only lived in a title tooltip (cursor: help), unreachable on touch screens, and the heat protection tooltip was dead because of pointer-events: none (B-6). Both badges are now buttons with an aria-label that toggle the detail text inline. - The three dropdowns get localized aria-labels. The clickable cover-time rows are turned into buttons by the covers feature commit that follows. Audit: A-2, B-6 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getStubConfig() wrote cover_entity: cover.homeshift_daily_covers, but the integration has no cover platform. Every card added from the picker got clickable time rows that called cover.open_cover on a missing entity. The key is removed from the stub (and from the devcontainer dashboard), and the rows only act when the configured cover exists and is not unavailable. Audit: B-2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The integration now exposes button.homeshift_open_covers and button.homeshift_close_covers (when daily covers are configured). The opening / closing time rows become real <button>s that press them: keyboard-focusable, localized aria-label including the scheduled time, active only when the button entity exists and is not unavailable (a button's "unknown" state just means never pressed). - New optional keys open_covers_entity / close_covers_entity, defaulting to the integration buttons, in the config type, the visual editor (button domain) and the README. - Legacy cover_entity keeps priority when set (cover.open_cover / close_cover, previous behaviour); documented in README and editor. - While the call is in flight the row shows "Sending…" (minimum 600 ms, aria-busy, both rows disabled to avoid double presses); a rejected call is reported through hass-notification instead of an unhandled promise rejection. - No confirmation dialog: the action is reversible with the other row, is explicitly labelled, and HA's own button rows do not confirm. - en/fr strings; devcontainer gets template buttons simulating the integration's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README: "homershift-card" in the YAML type and the three resource paths gave "Custom element doesn't exist" and a 404 on copy-paste. Five options were missing from the table (heat_protection_entity, cover_open_time_entity, cover_close_time_entity, covers_left_open_entity, besides the cover options documented with the feature). The arc is ~250°, not 270°; the promised relative "in 1h30" display never existed (and the 30 s refresh is gone). Adds the badges, keyboard use, the "Entity not found" behaviour and a Development section. - .github/copilot-instructions.md described a different card (homershift tag names, input_select service, 250° constants, the misspelled thermostat key); rewritten from the current code. - Devcontainer: setup.sh copied from /workspaces/homershift_card (does not exist), the Dockerfile WORKDIR matched it, dist/ was mounted on /config/www/workspaces/homeshift_card while the resource pointed to /local/homershift_card/. Everything now uses homeshift_card: dist/ is mounted on /config/www/homeshift_card, served at /local/homeshift_card/homeshift-card.js, and the redundant copy is dropped. Removes a stray "Xiaomi Smart Pet Fountain Card" hint. Audit: D-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing guarded the repository: the editor key typo and the dead localize fallbacks would have been caught by a trivial test. On every push to main and every pull request the workflow now runs npm ci, typecheck, lint, the Vitest suite and the build, fails if the committed dist/ differs from the fresh build, and runs hacs/action for a plugin. Audit: T-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebuilt from the sources of this branch (no sourcemap reference any more), so the CI "dist matches the sources" check passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HACS validation reads the license from the default branch through the GitHub API, so it must exist on main for pull requests to pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(card): rebuild the card as a labelled list with collapsible dura…
PR #5 rebuilt the card as a labelled list and removed the circular slider. The list layout is kept as the visual reference and the audit fixes of this branch are carried over onto it: - circular-slider.ts deleted as on main; the thermostat modes are the segmented native buttons of the list, labelled as a group, with aria-pressed, disabled while the select is unavailable (A-2). - Missing day / thermostat entity: entity_not_found outside the card picker preview, localized stub data inside it (B-3, A-3). - isUsable() for the next-mode and cover-time sensors (B-4); a duration setting whose number is unknown/unavailable reads "Unavailable", is not highlighted and cannot be opened, so no NaN (B-5); a value outside the presets is shown as is. - Times follow the HA profile (A-4); the 30 s re-render of PR #5 is replaced by the midnight timer (P-1). - localize() with fallback instead of the card-local t() helper (A-3). - ENTITY_FIELDS drives defaults, watched entities and the editor; the editor drops show_title like main and keeps thermostat_mode_entity (B-1, A-1); no cover entity in the stub (B-2). - Manual open/close: cover times are <button>s pressing the HomeShift buttons (cover_entity first when set), with "Sending..." feedback and a hass-notification on failure. - Covers-left-open chip: past three covers it shows a count and becomes a button revealing the names on tap (A-2, B-6). - Translations: union of both sides, minus the keys neither side reads any more (card.title, card.off, card.next_scan, override_duration, early_switch, editor.show_title). Tests check parity and unused keys. - Dial tests replaced by tests on the preset buttons, settings, chips. dist/ is left at this branch's bundle and rebuilt in a later commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The list layout's stepper looked up the first preset >= the current
value and moved one index from there. From a value between two presets
(25 min set on the entity page) "+" therefore skipped 30 and jumped to
1h. "+" now goes to the first preset above the value and "-" to the
last one below it.
The presets were also carried as { label, value } pairs whose labels
the list never displays; they are plain minute lists now.
Audit: B-5 (values outside the presets), P-1 (dead code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The list layout labelled its stepper buttons "-" and "+": a screen reader announced "minus, button" with no hint of which setting it changes. They are now "Decrease: <setting>" / "Increase: <setting>" (en/fr), and the value between them is a polite live region so the new duration is read out after each step. Audit: A-2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After the merge of the list layout (PR #5) the README, the Copilot instructions and the devcontainer dashboard still described the circular selector, the badges beside it, the minutes dropdowns and the show_title option, all gone. They now describe the rows (segmented thermostat modes, day mode, next mode, collapsible steppers, cover times as open/close-now buttons, alert chips), document that `name` is accepted but no longer displayed, and the devcontainer card uses the cover button and covers-left-open entities. Audit: D-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebuilt from the merged sources: list layout of PR #5 with the audit fixes, the manual cover buttons and the stepper fixes. The circular slider is no longer bundled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A "Cover automation" row lists every cover HomeShift drives (read from sensor.homeshift_covers_inhibited). Pick a duration — 4 h to 7 days, or until resumed — then pause covers one by one; each paused cover shows when it comes back and can be resumed, or all at once. While covers are paused, a calm chip names them and reopens the row. A refused call is shown as Home Assistant's notification toast. Calls homeshift.inhibit_covers / homeshift.resume_covers. The row is hidden when the sensor is missing, so older integrations are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016uVhrB9KRnmbFZRPHNHUev
Gamso
force-pushed
the
fix/audit-2026-10
branch
from
October 10, 2026 06:12
52b77cd to
64652fd
Compare
A Schedules bar at the foot of the card slides a panel up over it: one tab per day mode with the 24 h timelines of the schedules tagged with it, grouped by room, and a pencil that swaps them for the scheduler-card list of the tab. The panel is as tall as its content, anchored to the foot of the card, and scrolls when the schedules are taller than it. The bar appears once the Scheduler integration holds a schedule tagged with a day mode; show_schedules: false hides it and schedule_entities narrows it. Also aligns the gap under the thermostat modes with the card padding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t data Home Assistant was started in the background from postCreateCommand, which VS Code kills: start.sh is now the container's main process and restarts it. onboard.py creates the admin / admin account on first launch; setup.sh installs Scheduler and scheduler-card v4.0.19 (v4.0.11 froze the browser when opening a schedule), seeds sample schedules tagged with the day modes, simulated thermostats and a HomeShift dashboard. Covers use the modern template syntax, *.sh keep LF endings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gamso
force-pushed
the
fix/audit-2026-10
branch
from
October 14, 2026 06:43
2166c16 to
6904865
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.