Skip to content
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
817384e
feat(thermostat): add the weekly schedule tables
William-De71 Aug 23, 2026
01e6ec8
feat(thermostat): add the schedule matching and validation helpers
William-De71 Aug 23, 2026
c5c7523
feat(thermostat): add the thermostat integration service
William-De71 Aug 23, 2026
7341711
feat(thermostat): declare the dashboard box type
William-De71 Aug 23, 2026
43356de
feat(thermostat): add the dashboard widget
William-De71 Aug 23, 2026
10a7825
feat(thermostat): add the integration pages
William-De71 Aug 23, 2026
0b3ad67
feat(thermostat): add the English, French and German translations
William-De71 Aug 23, 2026
bac0b94
docs(thermostat): add the living specs for the integration
William-De71 Aug 23, 2026
88bd71d
fix(thermostat): harden the regulation loop against out-of-range input
William-De71 Aug 23, 2026
9df66ba
fix(thermostat): defer the preset write to the end of the drag
William-De71 Aug 23, 2026
eb4aa93
chore(thermostat): drop dead styles and stop the services the tests s…
William-De71 Aug 23, 2026
5643c9c
fix(thermostat): address the review feedback on units, manual holds a…
William-De71 Aug 24, 2026
567f215
fix(thermostat): stop the return to the schedule from re-arming manua…
William-De71 Aug 24, 2026
2c0c748
test(thermostat): cover the remaining defensive branches
William-De71 Aug 24, 2026
693f6c9
fix(thermostat): expire a permanent manual hold once a schedule is at…
William-De71 Aug 24, 2026
90a11ff
perf(thermostat): cache the runtime feature keys behind the ownership…
William-De71 Aug 24, 2026
4de1637
fix(thermostat): keep overnight slots when copying a day onto other days
William-De71 Aug 24, 2026
bde9801
fix(thermostat): make the schedule editor readable and stop it blocki…
William-De71 Aug 24, 2026
cc68c71
feat(thermostat): report the suspended state, and summarise the devic…
William-De71 Aug 24, 2026
aa8d566
fix(thermostat): make the widget reachable by touch, keyboard and scr…
William-De71 Aug 24, 2026
f372a9b
fix(thermostat): make the schedule editor operable without a pointer
William-De71 Aug 24, 2026
18c1770
fix(thermostat): make the dial focusable, and restore the back link
William-De71 Aug 25, 2026
bb18477
fix(thermostat): show the widget for a thermostat that was never driven
William-De71 Aug 26, 2026
f63009e
refactor(thermostat): move the integration menu to the Horizon top tabs
William-De71 Aug 27, 2026
61e440d
fix(thermostat): stop the widget starting or faking heating it never …
William-De71 Aug 27, 2026
58497ad
docs(thermostat): correct the stretch claim on the thermostat box spec
William-De71 Aug 27, 2026
6ce8a27
feat(thermostat): drive a real thermostat with the weekly schedules
William-De71 Aug 28, 2026
b82f643
fix(thermostat): really stop an external thermostat that has a mode
William-De71 Aug 29, 2026
198dded
test(thermostat): cover the mode writes, and format the device list
William-De71 Aug 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/specs/dashboard-flexible-layout-and-widgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,22 @@ The signature widget: an illustration (typically the user's house) with live dev
- Assets are deleted with their dashboard — explicitly by `dashboard.destroy` (production never sets `PRAGMA foreign_keys`, so the declared FK cascade alone would not fire on SQLite). An asset orphaned by replacing a box image lives until then; a cleanup pass can come later if it ever matters.
- Optional **night variant**: a second image auto-swapped by sun state — **deferred**, one additive field when it comes, no schema impact.

## E2. New box type: `thermostat`

A circular-gauge widget for the thermostat integration (`docs/specs/thermostat.md`): current temperature and humidity, target setpoint, a preset bar and a drag-to-set dial.

- New `DASHBOARD_BOX_TYPE.THERMOSTAT = 'thermostat'` in `server/utils/constants.js`. It is **not** a stretching box (absent from `TILE_STRETCH_BOX_TYPES` in `front/src/utils/dashboardSections.js`): unlike a value tile, the card is a stack of a dial, a preset bar and a status banner, so absorbing a column's leftover height would only pad the gauge with empty glass rather than make anything more readable.
- Box config — deliberately **one device-referencing key and nothing else**:

| Key | Meaning |
|---|---|
| `thermostat_feature` | selector of the `thermostat` / `target-temperature` feature to display |
| `name` | optional card title override (shared with every other box type) |

- **Why the config is this small.** Regulation settings — the sensor, the switch, the active schedule, the six preset temperatures, hysteresis, TPI cycle and band — are **device params**, never box fields. Putting them in `t_dashboard.boxes` would make a per-user dashboard document the source of truth of a control loop that turns real heaters on and off: the loop would have to read every dashboard on each tick, including the **private** dashboards of other users, and the same thermostat displayed on two dashboards with different settings would resolve non-deterministically. The widget chooses *which* thermostat to display; it never owns the regulation.
- `thermostat_feature` is a device-referencing field: it is listed in `FEATURE_STRING_FIELDS` (`server/lib/device/device.migrate.js`) and in `docs/specs/device-migration.md` B.3, so migrating the thermostat device rewrites the widget's selector.
- Values update live over the device websocket plus the `THERMOSTAT.*` messages (`PRESET_UPDATED`, `MANUAL_MODE_UPDATED`, `CONFIG_UPDATED`) the service broadcasts.

## F. AI illustration generation through Gladys Plus

Generating the `house-view` illustration is the one step that cannot be beautiful-by-default from a form alone. Gladys Plus already proxies AI calls; illustration generation follows the exact same pattern.
Expand Down
2 changes: 1 addition & 1 deletion docs/specs/device-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Two replacement maps are built once: `featureReplacements` (mapped source featur
Fields rewritten — this list is **exhaustive and must stay in sync with the Joi schemas** of `server/models/scene.js` and `server/models/dashboard.js` (both reject unknown keys, so any new device-referencing field lands here in the same diff):
- **Scene actions** (`t_scene.actions`, array of arrays, recursing into `condition.if-then-else`'s `if` / `then` / `else`): `device_feature` (feature), `device_features[]` (features), `device` (device), `devices[]` (devices), `camera` (device).
- **Scene triggers** (`t_scene.triggers`, flat array): `device_feature` (feature), `device_features[]` (features), `device` (device — schema-declared legacy field, rewritten for safety).
- **Dashboard boxes** (`t_dashboard.boxes`, array of *sections* `{ columns: [[box]] }` since the flexible layout — legacy arrays of arrays are still walked): `device_feature` (feature), `device_features[]` (features), `device` (device), `camera` (device); plus the nested selector holders introduced by the wall-panel widgets — `chips[].device_feature` (feature), `pins[].device_feature` (feature), `actions[].device_feature` (feature, quick-actions box), and the **values** of `scene_status_features` (scene selector → feature selector map; keys are scene selectors and are not rewritten). Values are replaced **in place**; array length and order never change, keeping `device_feature_names` / `units` / `colors` index-aligned.
- **Dashboard boxes** (`t_dashboard.boxes`, array of *sections* `{ columns: [[box]] }` since the flexible layout — legacy arrays of arrays are still walked): `device_feature` (feature), `device_features[]` (features), `device` (device), `camera` (device); plus the nested selector holders introduced by the wall-panel widgets — `chips[].device_feature` (feature), `pins[].device_feature` (feature), `actions[].device_feature` (feature, quick-actions box), `thermostat_feature` (feature, thermostat box — the only device-referencing key that box carries, every regulation setting living on the device instead), and the **values** of `scene_status_features` (scene selector → feature selector map; keys are scene selectors and are not rewritten). Values are replaced **in place**; array length and order never change, keeping `device_feature_names` / `units` / `colors` index-aligned.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Only scenes/dashboards that actually changed are saved. Rewritten scenes go through `SceneManager.addScene` so the RAM copy (`this.scenes`, the one `checkTrigger` iterates) and its scheduled triggers are replaced atomically with the DB copy — the same path as `scene.update`. Dashboards have no RAM cache. References to **unmapped** source features are intentionally left dangling (existing deletion semantics; the UI warned).

Expand Down
Loading
Loading