# Luma Home — UX mechanics The behaviour contract for the Home Assistant tab. Every rule here is meant to be implemented exactly; where a number is given (timings, steps, sizes) it is the spec, not an example. Visual reference for every card and panel: `screens/`. Measurements and colours: `components.md` and `tokens.json`. Input is the LG remote only: **◀ ▶ ▲ ▼, OK, Back**. Viewing distance 5–6 m on a 65" TV, logical layout 1920 × 1080. --- ## Part A — Global rules ### A1. Tap and hold on OK | | Rule | |---|---| | Tap | OK pressed and released in **under 0.4 s**. The action runs **on release**. | | Hold | OK still down at **0.4 s**. The hold action runs **at 0.4 s**, while the key is still down. The release that follows is ignored. | | Hold cue | From the moment OK goes down, a ring fills around the focused element's icon over 0.4 s (white stroke on a 18%-white track). Released early: the ring disappears and the tap runs. | | No hold action | If the focused element has no hold action, there is no ring and a long press behaves as a tap on release. | | Key repeat | OK auto-repeat events from the TV are ignored. Arrow auto-repeat is used where stated (sliders, volume). | Timing is measured from the key-down event to the key-up event as delivered by SDL-webOS (Wayland press/release). A tap never toggles anything on key-down, so a hold can never toggle by accident. ### A2. Screen structure and focus order ``` Top nav (Start · Apps · Home · Settings) ▼ Room tabs (Home · Living Area · Kitchen · …) ▼ Card grid (4 × 3 or 3 × 3) ``` - **▲ from the top row of the grid** → room tabs (the active room's tab). **▲ from room tabs** → top nav. - **▼ from room tabs** → the card that was focused last in that room (first card the first time). - **◀ ▶ on room tabs** switches rooms immediately (no OK needed); the grid below changes, focus stays on the tabs. - **Back from a card** → room tabs. **Back from room tabs** → top nav. Back from top nav does nothing. - Focus memory: each room remembers its last focused card for the session. ### A3. The grid - Every screen is a fixed grid: **4 columns × 3 rows** (cell 424 × 240) or, chosen per screen, **3 columns × 3 rows** (cell 572 × 240). Gap 18 px, grid area 1752 × 756 at x 84, y 206. - Cards fill whole cells only: **1×1**, or **1×2** (one column, two rows tall, 498 px) for weather and media. - No scrolling inside a screen. A room with more cards than cells shows the first 12 (or 9); more rooms or screens are added as tabs, not as scrolling. - Movement is spatial: ◀ ▶ ▲ ▼ go to the card whose cell is next in that direction. No wrap-around. - **1×2 cards:** entering from the side keeps the row you came from in memory; leaving sideways returns to that row on the other side. ▼ from a 1×2 card goes to the cell below its bottom row. - Empty cells are skipped: the move continues in the same direction to the next card, or does nothing. ### A4. Focus visuals - Focused card: **4 px white ring (96%)** with a **4 px dark gap** (`rgba(8,11,18,0.85)`) between ring and card, card **scales to 1.04**, fill brightens, shadow deepens. 120 ms ease-out. - Inside a panel or the media card the ring sits on the focused control, not on the card. - Exactly one ring on screen at any time. ### A5. Card anatomy (1×1) ``` ┌───────────────────────────────────────┐ │ (icon) Name Value │ ← top row: 48 px icon circle, name 19 px, value 20 px bold │ │ │ ───────── middle band ─────────────── │ ← graph / level bar / colour strip / window / photo │ │ │ Subtitle (context) │ ← 14 px, optional; context, never the state └───────────────────────────────────────┘ ``` - **On**: icon circle and value take the card's accent colour, plus a soft radial glow top-left. - **Off**: grey icon circle (white 10%), value white 60%, no glow. - **Unavailable** (HA `unavailable`/`unknown`): card at 50% opacity, value "Offline", OK does nothing, hint bar shows "Offline". Hold OK still opens the panel if the card has one (read-only). ### A6. The hint bar Bottom-left of every screen, 14 px. It always describes the **focused** element: `OK · Hold OK · Back `. - Labels are verbs for the actual next result: "Turn on" when off, "Turn off" when on. - If there is no hold action, the "Hold OK" item is **not shown** (no "Details", no placeholder). - Media and panels add their arrow hints (e.g. "▲▼ Volume"). ### A7. Panels (Hold OK) - A panel opens on the **right side**, 620 × 1080, on top of the grid. The grid stays visible and dimmed behind it. - Focus moves into the panel on its **first section**. The card keeps a static (non-focused) highlight so you can see which card the panel belongs to. - Inside a panel: **▲ ▼ move between sections (rows)**, **◀ ▶ adjust or choose within the row**, **OK selects / presses**, **Back closes** the panel and returns focus to the card. - Changes apply **live** while the panel is open — there is no Save. Slider changes are sent **200 ms after the last press** (one call per burst); buttons and presets are sent at once. - If the entity becomes unavailable while the panel is open, the panel stays, controls go disabled, and the header subtitle shows "Offline". - Sections that the entity doesn't support are **hidden**, not disabled (e.g. no airflow rows on a radiator, no colour on a dimmable light). ### A8. Sliders inside panels - ◀ ▶ change by the stated step. Holding an arrow repeats every **150 ms** after an initial **400 ms**; after 2 s of holding the step doubles. - The value label updates on every step; HA receives the value 200 ms after the last step. - Values clamp at the ends; no wrap-around. ### A9. State updates and errors - **Optimistic:** the card shows the new state the moment the action is sent. - **Confirmation:** the real HA state replaces it when it arrives. If no matching state arrives within **5 s**, the card reverts and the hint bar shows "Couldn't reach " for 4 s. - HA state changes from elsewhere (app, automation) update cards and open panels live, except a control currently being adjusted (it keeps the local value until 1.5 s after the last press). --- ## Part B — Cards Each card: what it shows, every key, the panel, states, hint bar and Home Assistant calls. ### B1. Light — on/off For lights that only switch (no brightness). | | | |---|---| | Size | 1×1 | | Shows | Name, value **On / Off**, optional subtitle. No middle band. Accent gold `#F5B85A`. | | OK (tap) | Toggle. | | Hold OK | Nothing (no panel, no ring). | | Hint bar | `OK Turn on` / `OK Turn off` | | HA | `light.toggle` (or `light.turn_on` / `light.turn_off`) | ### B2. Light — dimmable (no colour) For lights with brightness only (e.g. Ceiling Lights). | | | |---|---| | Size | 1×1 | | Shows | Value = brightness % or **Off**. Middle band: brightness bar (12 px) in the accent. | | OK (tap) | On / off. Turning on restores the **last brightness**. | | Hold OK | Brightness panel. | | Hint bar | `OK Turn on · Hold OK Brightness & presets` | **Panel — sections top to bottom** 1. **Brightness** — slider 1–100 %, ◀ ▶ **10 %** steps (A8). Going below 10 % with ◀ stops at 1 %; ◀ again turns off. 2. **Presets** — Off · 25 % · 50 % · 100 %. ◀ ▶ choose, OK applies. OK on the Brightness row toggles on/off. Back closes. HA: `light.turn_on` with `brightness_pct`; `light.turn_off`. ### B3. Light — colour (white + RGB) | | | |---|---| | Size | 1×1 | | Shows | Value = brightness %. Middle band: brightness bar **in the light's current colour**. Accent, glow and bar follow the live colour (warm white → gold). Subtitle: "White · 2700 K" or "Colour". | | OK (tap) | On / off at the last colour and brightness. | | Hold OK | Brightness & colour panel. | | Hint bar | `OK Turn on · Hold OK Brightness & colour` | **Panel** 1. **Brightness** — as B2. 2. **Light type** — two buttons **White · Colour**. ◀ ▶ + OK switches. White restores the last colour temperature, Colour restores the last hue. 3. **White temperature** (White mode) — slider, ◀ ▶ **250 K** steps within the light's min–max (typically 2700–6500 K). Label "2700 K · warm" / "4000 K · neutral" / "6500 K · cool". **or Hue** (Colour mode) — ◀ ▶ **15°** steps around the colour wheel, full saturation. 4. **Swatches** — White mode: 2700 · 3000 · 4000 · 5000 · 6500 K. Colour mode: 8 colours (red, orange, yellow, green, blue, violet, pink, white). OK applies. HA: `light.turn_on` with `brightness_pct`, `color_temp_kelvin` or `hs_color`. ### B4. LED strips and TV lights (with effects) | | | |---|---| | Size | 1×1 | | Shows | Value = brightness % or Off. Middle band: 34 px strip showing the current colour, or the effect's gradient. Subtitle: "Effect: Aurora", "White · 3000 K" or "Colour". | | OK (tap) | On / off (restores last colour or effect). | | Hold OK | Colours & effects panel. | | Hint bar | `OK Turn on · Hold OK Colours & effects` | **Panel** 1. **Brightness** — as B2. 2. **Light** — White · Colour (as B3). Switching modes swaps the presets in row 4. 3. **Temperature** (White) or **Hue** (Colour) — as B3. 4. **White presets** (2700 · 3000 · 4000 · 5000 · 6500 K) **or Colour presets** (8 swatches). 5. **Effects** — up to **6 pinned effects** as buttons, then **"All effects · N ›"**. OK on an effect plays it (the button shows "Playing"). OK on "All effects" opens the list. **All effects list** (replaces the panel content, same position) - Two groups: **Pinned** (in pin order, numbered) then **All effects A–Z**. - ▲ ▼ move, the list scrolls to keep focus visible. **OK plays** the effect. **Hold OK pins / unpins** it (star fills gold). - Pins are unlimited. **The panel shows the first 6 pins.** A 7th or later pin stays in the Pinned group at the top of this list, tagged "Not on panel"; nothing changes on the panel. Unpinning one of the first 6 moves the next pin up onto the panel. - Back returns to the panel with focus on the Effects row. - Pins are stored per light in local preferences. HA: `light.turn_on` with `effect` (list from the entity's `effect_list`), `brightness_pct`, `hs_color`, `color_temp_kelvin`. ### B5. Air conditioner (Gree AC) | | | |---|---| | Size | 1×1 | | Shows | Value = target temperature ("25°"). Middle band: 24 h room-temperature graph (grey when off). Subtitle: "Heat · 26° inside". Accent by mode: heat `#F06A4A`, cool blue. | | OK (tap) | On / off; turning on resumes the **last mode**. | | Hold OK | AC panel. | | Hint bar | `OK Turn on · Hold OK Mode, fan & airflow` | **Panel — sections top to bottom** 1. **Target** — ◀ ▶ by the entity's step (Gree: 1°), within its min–max. 2. **Mode** — icon buttons: Auto · Cool · Dry · Fan · Heat. OK selects. 3. **Fan speed · auto** — preset pills **Auto · Quiet · Turbo**. OK selects. 4. **Fan speed · manual** — 5-step slider Low · Medium-low · Medium · Medium-high · High. ◀ ▶ moves one step and selects manual. Selecting a step clears the auto preset; selecting an auto preset greys the slider (still focusable). 5. **Up–down airflow** — icon-only: **Swing** + 5 fixed positions. 6. **Left–right airflow** — icon-only: **Swing** + 5 fixed positions. Row label shows the current value ("Fixed · position 2", "Swing"). Radiators and wall thermostats have their own card (B5b). Gree extra toggles (Light, X-Fan, Health, Sleep, SE) are **not** included. HA: `climate.turn_on/turn_off`, `climate.set_temperature`, `climate.set_hvac_mode`, `climate.set_fan_mode`, `climate.set_swing_mode` (and the Gree integration's swing-position entities for fixed positions). ### B5b. Heating — radiator valve (TRV) and wall thermostat One card type with two versions, picked by the device. Both are `climate` entities with modes **off / heat** only. | | | |---|---| | Size | 1×1 | | Shows | Value = target temperature, or **Off**. Middle band: 24 h room-temperature graph. Subtitle: what it is doing + room temperature — "Heating · 22.5° inside", "Idle · 23° inside" (target reached), or "22.5° inside" when off. Accent heat `#F06A4A`. | | Warnings | Shown in the subtitle in **amber** `#F2B84B`, 600 weight, replacing the normal text: "Window open · paused" (value shows the setback temperature), "Battery low". | | Unavailable | Card at 50 %, value "Offline", subtitle "Last seen