diff --git a/CLAUDE.md b/CLAUDE.md index 4f1c081..5ccb06d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,8 @@ dashboard tab. Status: the overlay platform works; the Home Assistant parts are **Start every session by reading `docs/PROGRESS.md`** (current state, next steps, log). User-facing docs: `README.md` (quick start, development, CLI), `docs/REQUIREMENTS.md` -(requirements, open questions, decisions), `docs/PROGRESS.md`, `docs/ARCHITECTURE.md`, `docs/STEAM-FRAME.md`. +(requirements, open questions, decisions), `docs/DESIGN.md` (design of the HA parts and its +decisions), `docs/PROGRESS.md`, `docs/ARCHITECTURE.md`, `docs/STEAM-FRAME.md`. Keep them in sync with changes. This file holds what a developer needs on top of that. Platform: a SteamVR **dashboard overlay**, built from the author's vrdashboard template, diff --git a/README.md b/README.md index 6cb9709..afde8a5 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ the headset to Home Assistant in both directions: - **Dashboard:** a SteamVR dashboard tab to view and control Home Assistant from inside VR. > **Status:** early development. The overlay platform works on the Frame; the Home Assistant -> parts are being planned; see [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md) -> and [docs/PROGRESS.md](docs/PROGRESS.md). +> parts are being planned; see [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md), +> [docs/DESIGN.md](docs/DESIGN.md) and [docs/PROGRESS.md](docs/PROGRESS.md). ### Platform diff --git a/docs/DESIGN.md b/docs/DESIGN.md new file mode 100644 index 0000000..9dbc56d --- /dev/null +++ b/docs/DESIGN.md @@ -0,0 +1,63 @@ +# Design + +Status: **in progress**, iteration 1, phase 2 (planning and design). High-level architecture +first, then the details for the MVP. Requirements and their decisions (D1–D15): +[REQUIREMENTS.md](REQUIREMENTS.md). Design decisions start at D16. + +## Process and threads + +- **One process** started by SteamVR does everything: HA connection, sensors, services, all + overlays (D16). +- Several threads inside it for concurrent work. Which work runs in which thread is still open. + +## Layers + +Three layers; dependencies only go downward (D17). + +``` + Presentation QML (view and controller) + ▼ + Business logic entities, services, managers + ▼ + I/O HA connection (MQTT), SteamVR, system (network, audio) +``` + +- Qt Quick already enforces separating presentation from logic. +- The overlay platform (`vrdashboard_core`) spans two layers: overlays, rendering and input + are presentation infrastructure; SteamVR access for sensors and actions is I/O. + +## Object classes + +The app is HA-centric, so its building blocks follow HA's concepts (D18). Objects talk to each +other through Qt signals and slots (Qt's observer pattern). Senders don't know their receivers, +and signals across threads are delivered through the receiver's event loop. + +| Class | Role | MVP | +|---|---|---| +| **HA entity** | One HA entity, both directions. Entity → HA: state and JSON attributes. HA → entity: basic commands. Observes value signals of other objects and publishes them; turns MQTT commands into signals other objects act on. Covers sensors, simple actions (`button`), settings (`switch`, `number`), feedback (`event`). | ✓ | +| **HA service** | An action HA calls with parameters: a JSON command on an MQTT command topic (HA side: the standard `mqtt.publish` action). May be more complex than an entity command. Results go back asynchronously, e.g. through an `event` entity. How the user wraps it in HA (scripts, automations, pyscript) is their choice; FrameAssistant only requires stock HA with MQTT (NFR-6). | ✓ (notification) | +| **HA device** | Owns the MQTT connection, discovery and availability, and owns all entity and service objects. One per headset (CON-5). | ✓ | +| **I/O wrappers** | Data sources and actuators; know nothing about HA. One wrapper object per OpenVR interface used (e.g. `IVRSystem`, `IVRApplications`, `IVRChaperone`, `IVRSettings`). System wrappers (network, audio) later. | OpenVR ✓, system ✗ | +| **Overlays** | View and controller of MVC (QML), as dashboard tabs or world overlays. Get their data (the model) from other objects. | ✓ | +| **Managers** | One object per stateful feature that manages and coordinates it, using entities, services and overlays. Notification manager (MVP); status card manager (SRV-6/7) later. | notifications ✓ | +| **HA web UI** | Encapsulates Qt WebEngine for the dashboard (Q2). | ✗ | +| **Configuration** | Settings from the JSON file, loaded once by the app as an immutable value. Each component gets the part it needs through its constructor (D19). | ✓ | +| **App** | The managing object: owns all of the above as components and wires them together. | ✓ | + +Notes for later: +- OpenVR delivers all events through one queue; one dispatcher (today `VrRuntime`) hands them + to the wrappers. +- OpenVR is not documented as thread-safe (relevant for the thread decision). + +## Open + +1. High-level points 3–7 (see [PROGRESS.md](PROGRESS.md), "Next steps"). + +## Decisions + +| # | Date | Decision | Reason | +|---|------|----------|--------| +| D16 | 2026-10-05 | **One process** started by SteamVR does everything; several threads inside it for concurrent work | Meets NFR-2 without extra work, no IPC; the platform already supports several surfaces per process | +| D17 | 2026-10-05 | **Three layers:** presentation (QML), business logic, I/O. The overlay platform spans presentation (overlays, rendering, input) and I/O (SteamVR access) | Simple and enough for the start; Qt Quick already enforces separating presentation from logic | +| D18 | 2026-10-05 | **HA-centric object classes:** HA entity, HA service, HA device, I/O wrappers, overlays, managers, HA web UI, configuration, app; communication through signals and slots | The app is HA-centric; signals/slots decouple objects and work across threads | +| D19 | 2026-10-05 | **Configuration is injected:** the app loads it once as an immutable value and passes each component the part it needs; no singleton | Explicit dependencies, trivial tests with own configs, thread-safe; the app creates all components anyway | diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 2acc16d..cd2de16 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -10,21 +10,20 @@ CLAUDE.md ("Working rules"). ## Next steps -Phase 2 for the MVP scope (D8: MQTT connection + discovery + availability, the MVP sensors, a -basic notification). Design topics, proposed order: +Phase 2: first the high-level architecture, then the details for the MVP (D8). High-level +questions, in this order: -1. ~~Q4: MQTT client library~~: done, libmosquitto (D14). Remaining for the design: Qt - event-loop integration, shipping the library in the package (build + `install.sh`). -2. Component structure: configuration, MQTT/discovery layer, sensor sources, notification - service; how they fit into `Dashboard` (the app class). -3. HA entity model: topics, discovery payloads, availability, device grouping (CON-2/3/5, - SEN-15). -4. Basic notification (NOT-1): needs an overlay outside the dashboard, i.e. the planned - `WorldOverlay` (CLAUDE.md, "Planned extension"); MVP placement to be decided. -5. Test strategy for the MVP and the measurement method for NFR-3. +1. ~~Process model~~: one process, several threads (D16). +2. ~~Layers~~: presentation, business logic, I/O (D17). +3. How features plug in: object classes (D18) and configuration injection (D19) settled; + open: how the classes are wired. +4. HA channels: MQTT only, or prepare for a second one for the dashboard (Q2/Q3)? +5. Overlay kinds: dashboard tabs and world overlays (platform extension). +6. Runtime model: which work runs in which thread (D16: several threads). +7. Test strategy at a high level. -Later, not needed for the MVP: Q1 (several notifications), Q2/Q3 (dashboard); check only -whether they constrain the MVP architecture. +Details afterwards (MQTT library done: D14, D15): topics and payloads, basic notification, +configuration, packaging of libmosquitto. ## Log @@ -41,6 +40,11 @@ whether they constrain the MVP architecture. present (relevant for Q2/Q3). - libmosquitto ABI check: Debian's 2.0.21 needs at most `OPENSSL_3.0.0`/`GLIBC_2.34`; the Frame has OpenSSL 3.2.1 and glibc 2.39. No libcjson dependency. D14 holds. +- Started docs/DESIGN.md: one process (D16), three layers (D17), HA-centric object classes + (D18). HA side: FrameAssistant needs only stock HA; how the user wraps commands (scripts, + pyscript) is up to them (NFR-6 unchanged). +- Agreed to settle the high-level architecture first (list under "Next steps"), before + component details. - Stay with the Debian build container: an Arch container wouldn't match SteamOS's own package versions (private repos, rolling Arch) and has no arm64 multiarch for cross builds. diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index ab4ad1b..6c68905 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -155,6 +155,8 @@ FrameAssistant. | D14 | 2026-10-05 | MQTT client: **libmosquitto** from Debian's package in the build container, shipped in the release package like `libopenvr_api.so` | Not on the Frame; Qt MQTT would need the exact Qt version (Appendix B) | | D15 | 2026-10-05 | HA MQTT discovery: **own small layer on Qt's JSON classes**, using device-based discovery (one `/device//config` topic with all entities under `cmps`) | No suitable library (Appendix B); HA recommends device-based discovery for multi-entity devices (NFR-7) | +Design decisions (D16 onward) are in [DESIGN.md](DESIGN.md). + ## Appendix A: data sources What the headset can report, by source. Checked against `openvr.h` (OpenVR 2.15.6). **None of