Appearance
Mobile App
The Haptique mobile app (apps/mobile/) is an Expo/React Native application for controlling devices, managing rooms, running scenes, and interacting with the HOS server. It connects to the server via REST API for commands, SSE for live state updates, and optionally Socket.IO for designer features.
Navigation Structure
The app uses a root stack navigator with an unauthenticated entry flow and a bottom tab navigator for authenticated users.
Root Stack
| Screen | Component | Purpose |
|---|---|---|
Discovery | DiscoveryScreen | User enters the server base URL (e.g., http://192.168.1.x:8080) |
Login | LoginScreen | Username and password entry |
Authenticating | (loading screen) | Awaits login response |
Main | AuthenticatedAppEntry | Bottom tab navigator (authenticated shell) |
Bottom Tabs
Inside AuthenticatedAppEntry:
| Tab | Screen | Purpose |
|---|---|---|
| Home | HomeNavigator (nested stack) | Rooms, device control |
| Media | MediaScreen | Media playback |
| Zones | ZonesScreen | Zone management |
| Scenes | ScenesScreen | Scene execution |
| Profile | ProfileScreen | User settings |
| Custom | CustomTabScreen | Designer-configurable tab |
Home Stack
HomeNavigator lives at src/features/navigation/HomeNavigator.tsx and manages the primary device interaction flow:
| Screen | Component | Purpose |
|---|---|---|
RoomsHome | RoomsHomeScreen | Room list entry point |
RoomRemoteHome | RoomieRoomsHomeScreen | Roomie rooms view |
Rooms | RoomsScreen | Room browser |
RoomDashboard | RoomDashboardScreen | Single room dashboard |
DeviceControlResolver | DeviceControlResolverScreen | Routes to device-type-specific control screen |
LightControl | LightControlScreen | Light device control (brightness, color, power) |
AvControl | AvControlScreen | AV device control (input, volume, transport) |
MediaControl | MediaControlScreen | Media device control |
HvacControl | HvacControlScreen | HVAC device control (temperature, mode, fan) |
GenericControl | GenericCommandScreen | Generic fallback for unmapped device types |
Key Data Contexts
Three React contexts carry the app's global state. They are initialized in App.tsx and consumed throughout the screen hierarchy.
| Context | File | Responsibilities |
|---|---|---|
HaptiqueContext | src/contexts/HaptiqueContext.tsx | Controller session (base URL), auth token, bootstrap data (devices, zones, spaces, programs, actionMaps), SSE connection, command dispatch functions |
ThemeContext | src/contexts/ThemeContext.tsx | Dark/light theme mode, color palette, typography tokens |
RoomsDataContext | src/features/rooms/RoomsDataContext.tsx | Room list, per-room device lists, favorite devices, room-level refresh |
HaptiqueContext is the central data hub — nearly every screen reads from it to access devices, rooms, and dispatch commands. It owns the SSE connection to /app/os/state/stream and updates device state slices in real time as state events arrive.
ThemeContext is consumed by all visual components for consistent color and typography. It reads the system preference on first launch and persists the user's override in local storage.
RoomsDataContext supplements HaptiqueContext with room-scoped caching. Room screens subscribe to it to get pre-filtered device lists per room without triggering full re-renders of the global context.
Feature Directories
The app is organized into feature slices under src/features/:
| Feature | Path | Purpose |
|---|---|---|
controls | src/features/controls/ | Device control screens: light, av, media, hvac, generic |
core | src/features/core/ | osApi.ts — typed HTTP client for all server communication |
designer | src/features/designer/ | Tab config, designer preview, custom screen |
devices | src/features/devices/ | Device type resolution, screen routing via DeviceControlResolver |
media | src/features/media/ | Media playback, hardware volume bridge |
navigation | src/features/navigation/ | HomeNavigator, gesture hooks, BottomBarVisibility |
rooms | src/features/rooms/ | RoomsDataContext, adapters, room screens |
scenes | src/features/scenes/ | Scene execution |
shared | src/features/shared/ | Theme helpers, error boundary, common components |
How the App Connects to the Server
The connection lifecycle runs in six steps. Steps 1-5 are the standard authenticated flow. Step 6 is designer-only.
User App (HaptiqueContext) HOS Server
| | |
| Enter base URL | |
+-------------------------> | |
| (DiscoveryScreen) | |
| | |
| Submit credentials | |
+-------------------------> | |
| (LoginScreen) | POST /app/os/auth/login |
| +--------------------------> |
| | <-- bearer token ----------+
| | (stored in react-native- |
| | keychain) |
| | |
| | GET /bootstrap |
| | GET /media-services/status | (parallel)
| | GET /programs |
| | GET /spaces |
| | GET /action-maps |
| +--------------------------> |
| | <-- bootstrap data --------+
| | |
| | GET /app/os/state/stream |
| (authenticated shell) | ?accessToken=<token> |
| <----------------------- +--------------------------> |
| | |
| | <-- SSE: ready -------------+
| | <-- SSE: snapshot ---------+
| | <-- SSE: state (ongoing) --+
| | <-- SSE: ping (keepalive) -+Step-by-step:
Discovery —
DiscoveryScreencaptures the server base URL and stores it in context (e.g.,http://192.168.1.x:8080). No auth required at this step.Login —
POST /app/os/auth/loginwith username and password. On success, the bearer token is stored securely inreact-native-keychain. The token is attached to all subsequent requests via theAuthorization: Bearer <token>header.Bootstrap (parallel) —
HaptiqueContextfires five parallel requests:/bootstrap— core device registry, zones, spaces/media-services/status— streaming service availability/programs— automation programs/spaces— space configurations/action-maps— command mappings
Live updates (SSE) — A persistent SSE connection opens on
GET /app/os/state/stream?accessToken=<token>usingreact-native-sse. Event types:ready— server confirms stream is opensnapshot— full device state dump on connectstate— incremental state change for a single deviceevent— protocol event from a driverping— keepalive
Commands — Dispatched from control screens via
HaptiqueContexthelper functions:POST /devices/:id/command— device action dispatchPOST /programs/:id/run— trigger automation programPOST /action-maps/:id/trigger— trigger action map
Designer only (optional) —
Socket.IOconnection at/socket/designerfor live design push (used only when the designer feature is active).
See Also
- Server Internals — how the server processes requests dispatched by the app
- API Reference — full endpoint inventory, SSE event shapes, Socket.IO events
- Dev Setup — running the mobile app locally against a live HOS server