# Panama Home accessories customization design

## Summary

Panama will turn the existing Home Assistant card into the approved
**A · Accessory shelf**: a two-column grid of larger light controls with an
always-visible brightness slider on every tile. Panama Settings will gain a
first-class **Home & Phone** page where the user can discover all Home
Assistant lights, choose which ones appear, reorder them, and assign concise
Panama-only aliases.

The Phone card will add **Messages**, which opens the installed BlueBubbles
desktop application. Messages remains available when KDE Connect is asleep
because it is a local application handoff rather than a KDE Connect action.

## Goals

- Discover every Home Assistant light instead of limiting Panama to the old
  GNOME extension selection.
- Let the user choose any number of lights, order them, and give them readable
  local names without changing Home Assistant itself.
- Show the first four selected lights in Control Center's resting state and
  all selected lights in its expanded state.
- Make brightness directly adjustable on every visible light tile.
- Preserve quick on/off control, stale-state handling, and per-light failure
  isolation.
- Add a Messages action that opens BlueBubbles from the Phone card.
- Keep credentials, Home Assistant response bodies, and private preferences
  out of Git and logs.

## Non-goals

- Panama will not rename Home Assistant entities or friendly names globally.
- This release will not add color, color-temperature, scenes, automations,
  rooms, dashboards, or non-light domains.
- Panama will not send or read messages itself. BlueBubbles remains the
  complete messaging client.
- Panama will not make BlueBubbles availability depend on KDE Connect or the
  iPhone's local-network reachability.
- Panama will not continuously send brightness updates while the pointer is
  moving.

## Verified system state

The live Home Assistant instance currently exposes 22 light entities. All 22
advertise a brightness-capable color mode. The current compatibility selection
contains seven lights and omits the available Living Room group. Several source
friendly names begin with `Generic Zigbee Coordinator (EZSP)`, which is why
Panama needs local aliases rather than additional truncation rules in the
Control Center.

BlueBubbles is installed as the Flatpak desktop application
`app.bluebubbles.BlueBubbles`.

## Control Center interaction

### Accessory shelf

The resting Home section uses a two-column, two-row grid. Each selected light
tile contains:

- a bulb power control;
- the Panama alias, falling back to the Home Assistant friendly name;
- `On`, `Off`, `Unavailable`, or a concise busy/error state;
- the confirmed brightness percentage; and
- a full-width amber brightness slider.

The first four selected lights appear in the resting grid. The existing
`N accessories` action expands the Home section to a scrollable two-column grid
of every selected light in preference order. A **Manage in Settings** row at
the end opens Panama Settings directly to Home & Phone.

The tile body and bulb toggle power, except for the slider's own hit area.
Dragging a slider updates only its visual preview. Releasing it sends one Home
Assistant request. A value from 1–100 calls `light.turn_on` with
`brightness_pct`; zero calls `light.turn_off`. Dragging an off light above zero
turns it on at the chosen brightness. This avoids network chatter and makes the
final value deterministic.

Brightness displayed in Panama is derived from Home Assistant's 0–255 value
and rounded to a percentage. An off light whose current state has no brightness
attribute displays zero. A normal power toggle lets Home Assistant restore its
own previous brightness.

Only the affected light becomes busy. A successful action refreshes the
catalog and remains globally quiet. A failed action restores the last confirmed
value and shows a concise inline error on that tile; it does not make unrelated
lights stale or emit a global toast.

### Phone Messages action

The Phone action grid becomes four equal controls: Send File, Clipboard, Ring,
and Messages. The first three retain their capability and reachability checks.
Messages is enabled whenever BlueBubbles is installed, even if the phone is not
nearby, and launches the allow-listed desktop application without a shell.

If BlueBubbles is unavailable, the Messages action is visibly disabled and the
Phone detail section offers a plain `BlueBubbles is not installed` explanation.
Opening BlueBubbles is globally quiet.

## Panama Settings

### Home & Phone page

Panama Settings gains a **Home & Phone** sidebar destination between Network &
Devices and Desktop & Dock. Its page contains three cards.

1. **Home Assistant** shows connection health, discovered-light count, Refresh,
   and Open Home Assistant.
2. **Control Center favorites** shows selected accessories as a two-column
   card shelf. Each item exposes a drag handle, editable alias, source friendly
   name, and Remove action. Dragging reorders the list; the first four are
   explicitly marked as the resting Control Center shelf.
3. **Available lights** is a searchable list of the remaining discovered
   lights. Add appends a light to the selected list. Source names are always
   visible here so similarly named physical devices remain distinguishable.

A short **Phone continuity** row reports that Messages opens BlueBubbles and
provides an Open action. There is no redundant enable switch: if BlueBubbles is
installed, the button is useful.

Changes save automatically after a short debounce. Alias input is trimmed;
an empty alias falls back to the source friendly name. Duplicate aliases are
allowed because households can legitimately contain similarly named lights.
Selection may be empty, in which case Control Center shows a setup row that
opens this page.

## Persistence and migration

Add `config/HomePreferences.qml` as the durable owner of selected entity IDs,
order, aliases, and an initialization marker. It uses an atomically written
JSON adapter under Quickshell's per-shell state directory, beside the existing
Panama Settings preferences. The file contains no token, URL, or Home Assistant
response data.

On first launch only, Panama seeds the preference order from the existing
GNOME-extension-compatible selection so the current Control Center does not
reset. After initialization, an intentionally empty selection remains empty and
is never reseeded. New Home Assistant entities appear only in Available lights;
Panama does not silently add them to Control Center.

Aliases affect Panama only. Home Assistant's current friendly name is retained
as `sourceName` and shown in Settings. If a selected entity disappears from the
catalog, its preference remains in place as unavailable until the user removes
it, preventing temporary Home Assistant outages from destroying configuration.

## Home Assistant boundary

Extend `panama-home-assistant` with these normalized interfaces:

- `catalog` returns every `light.*` entity with `id`, `sourceName`, `state`,
  `available`, `active`, `dimmable`, and `brightnessPct`;
- `brightness ENTITY_ID PERCENT` validates a 0–100 integer and calls the
  appropriate light service; and
- the existing `toggle ENTITY_ID` remains the on/off action.

The helper authorizes actions against the current discovered light catalog,
not against arbitrary entity IDs supplied by QML. It never returns unrelated
Home Assistant domains or arbitrary attributes. Action requests use
`homeassistant.toggle`, `light.turn_on`, and `light.turn_off` with an exact
entity ID and, when relevant, `brightness_pct`.

The existing URL/token precedence remains unchanged: private Panama
environment values first, then the GNOME extension URL and Secret Service
token. The legacy entity list is used only for first-run preference migration.

`services/HomeAssistant.qml` owns the live catalog, resolves it against
`HomePreferences`, exposes ordered selected entities and the first four shelf
items, and tracks per-entity pending values and errors. Settings and Control
Center consume this one service rather than maintaining separate copies.

## BlueBubbles boundary

Extend `SystemSettings.openApplication()` with an allow-listed `bluebubbles`
entry that launches `flatpak run app.bluebubbles.BlueBubbles` as separate
arguments. Add a read-only `bluebubblesAvailable` property based on the
installed desktop application or Flatpak metadata. PhoneControls and the
Settings page consume those interfaces; neither constructs a command string.

## Failure behavior

- Home Assistant unreachable: retain the last catalog and selected states,
  mark them stale, and offer Retry.
- Authentication rejected: show `Authentication required` without exposing the
  response body.
- Catalog contains a malformed entity: omit that entity and keep healthy lights.
- Selected entity temporarily missing: keep its alias/order and show it as
  unavailable.
- Preference write fails: retain the in-memory edit, show an inline Settings
  error, and allow Retry; do not overwrite the previous valid file.
- Brightness request fails: snap the tile to its last confirmed value and show
  the error on that tile only.
- BlueBubbles missing: disable Messages without affecting KDE Connect actions.

## Files and components

- `config/dot/quickshell/config/HomePreferences.qml` — private selected-light
  order and aliases.
- `config/dot/quickshell/config/qmldir` — register the preference singleton.
- `config/dot/quickshell/scripts/panama-home-assistant` — catalog and brightness
  contracts.
- `config/dot/quickshell/services/HomeAssistant.qml` — catalog/preference/action
  composition.
- `config/dot/quickshell/services/SystemSettings.qml` — BlueBubbles detection
  and allow-listed launch.
- `config/dot/quickshell/modules/quicksettings/HomeControls.qml` — two-column
  accessory shelf and expanded selected grid.
- `config/dot/quickshell/modules/quicksettings/HomeTile.qml` — power,
  percentage, busy/error state, and slider.
- `config/dot/quickshell/modules/quicksettings/PhoneControls.qml` — Messages
  action and independent enablement.
- `config/dot/quickshell/modules/settings/HomePhonePage.qml` — discovery,
  selection, aliases, ordering, and phone handoff.
- `config/dot/quickshell/modules/settings/SettingsSidebar.qml` and
  `SettingsShell.qml` — page registration and routing.
- `config/dot/quickshell/services/ShellState.qml` and `shell.qml` — allow the
  Control Center and tests to open the Home & Phone page directly.

## Testing and verification

Focused automated coverage will prove:

1. Catalog filtering returns all valid lights, excludes other domains and raw
   attributes, normalizes 0–255 brightness, and tolerates malformed entities.
2. Brightness validates entity membership and 0–100 bounds, uses
   `light.turn_on` for 1–100, and uses `light.turn_off` for zero.
3. First-run migration seeds the legacy order once; selection, aliases,
   reordering, removal, and intentionally empty state survive a shell restart.
4. The first four selected lights form the resting shelf and the expanded grid
   contains every selected light in order.
5. Slider preview does not issue an action until release; success refreshes;
   failure restores the confirmed value and remains local to one tile.
6. Settings routes to Home & Phone, searches the complete catalog, and exposes
   source names separately from aliases.
7. Messages maps only to the allow-listed BlueBubbles command and remains
   independent of KDE Connect reachability.
8. Existing Control Center, Settings, Home Assistant, KDE Connect, Ongoing,
   notification, and shell contracts remain green.
9. Fresh Quickshell construction has no QML errors, Hyprland configuration is
   valid, and visual captures cover resting, expanded, off-light drag,
   unavailable light, and Settings states.

Automated tests will not toggle or dim a real light and will not open
BlueBubbles. Those final checks require explicit user actions in the finished
interfaces.
