Appearance
Driver Architecture
System Overview
HOS (Haptique OS) is a smart home operating system that runs as a server on the local network — either as an Electron desktop app or in headless Docker mode. The server exposes a REST/WebSocket API for mobile clients and a separate WebSocket endpoint exclusively for drivers.
Drivers are independent processes that connect to HOS over a raw WebSocket on the driver endpoint (ws://host:DRIVER_WS_PORT/driver). A driver's job is simple: announce the devices it controls, keep HOS informed of their state, and execute commands when asked. The driver process never touches the main HOS server codebase — it talks to HOS purely through the WebSocket protocol.
Mobile apps (iOS, Android, web) connect to HOS via REST API for one-time operations and receive real-time state updates via Socket.IO. When a driver pushes a state change, HOS immediately fans it out to all connected clients. This architecture means driver authors write straightforward event-driven code without any knowledge of client protocols or server internals.
Command Flow
When a user taps a device control in the mobile app, the command travels through HOS and arrives at the driver:
Mobile App HOS Server Driver Process
---------- -------------------------------- --------------------------
[Tap command]
|
v
REST API -----> POST /app/os/devices/:id/command
|
v
ActionDispatcher
(routes to correct driver session)
|
v
Driver WebSocket --------> [onAction() fires]
|
v
[Execute on device]
|
v
<------ sendEvent('ACTION_RESULT')
<------ sendEvent('STATE_UPDATE')
|
v
DeviceStateManager
(stores updated state)
|
v
Socket.IO / SSE ---------> [UI updates live]
|
v
[Mobile App refreshes]Step by step:
- User taps a control in the mobile app (e.g. "turn on the living room light")
- Mobile sends
POST /app/os/devices/:id/commandto the HOS REST API - ActionDispatcher looks up which driver session owns that device and forwards the command
- The driver receives an
ACTIONmessage and theonAction()callback fires - The driver executes the command on the physical device (network call, serial, IR, etc.)
- The driver sends
ACTION_RESULTwith asuccessflag and optionalrequestIdcorrelation - The driver sends
STATE_UPDATEwith the new device state (power: true,brightness: 100, etc.) - HOS stores the updated state in DeviceStateManager and broadcasts it to all connected mobile clients via Socket.IO
The driver does not need to know anything about the mobile clients — it just emits state and the server handles distribution.
Driver Connection Model
Every driver connects to HOS using the same three-step handshake:
Driver Process HOS Server
----------------------- -----------------------------------
HosDriver.connect()
|
| ws://host:DRIVER_WS_PORT/driver
|------------------------------------------------->
DriverWebSocketServer
accepts connection
<-------------------------------------------------|
{ ok: true, event: 'CONNECTED' }
driver.register() (automatic when autoRegister: true)
|
|--- { method: 'driver.register',
| params: {
| driverKey: 'MY_DRIVER',
| instanceId: 'my-driver-001',
| protocolVersion: 1
| }
| } ------------------------------------------>
Server validates fields,
creates driver session
<-------------------------------------------------|
{ ok: true, event: 'REGISTERED',
driverKey: 'MY_DRIVER',
instanceId: 'my-driver-001' }
driver.on('registered', ...) fires
— safe to send events nowKey points about the connection model:
- Drivers connect to the
/driverWebSocket path. The port is set byDRIVER_WS_PORTon the server (default:8080). - The server sends a
CONNECTEDacknowledgement immediately on connection — before registration. - The driver must send
driver.registerwithdriverKey,instanceId, andprotocolVersion: 1. The SDK sends this automatically whenautoRegister: true(the default). - After receiving the
REGISTEREDack, the driver can safely callsendEvent()to announce devices and push state. - If the connection drops, HosDriver reconnects automatically with exponential backoff (default: 1 second base, doubles each attempt, caps at 30 seconds) with ±10% jitter to avoid thundering-herd reconnect storms.
- Close code
4001means the server replaced this session with a newer connection that has the samedriverKey+instanceId. HosDriver does not reconnect on4001.
See Getting Started for a walkthrough of the HosDriver API and the exact constructor options.
Key Components
HosDriver (sdk/lib/) The TypeScript client library driver authors import. Wraps the raw ws WebSocket with typed message builders, automatic reconnect logic, and an EventEmitter interface. Driver authors interact exclusively with this class — they never write raw WebSocket code.
DriverWebSocketServer (server) Accepts driver connections on the /driver path. Validates every inbound message against the protocol schema, routes events to internal handlers, and sends ACTION commands down to the correct driver session when the user issues a command.
ActionDispatcher (server) Routes commands from the REST API to the driver that owns the target device. Looks up the device-to-driver mapping, serializes the command, and delivers it over the driver's WebSocket session.
DeviceStateManager (server) Tracks live device state in memory, keyed by device ID. When a driver sends STATE_UPDATE, DeviceStateManager stores the new state and triggers the Socket.IO broadcast to mobile clients.
ProtocolHub (server) Central event bus that connects DriverWebSocketServer, ActionDispatcher, and DeviceStateManager. Driver events flow in through ProtocolHub; commands flow out through it. Driver authors do not interact with ProtocolHub directly — it is an internal server component.
What's Next
- Domain Reference — Standard device types, state fields, and commands per domain
- Driver Manifest — How to package your driver as an uploadable OS-local ZIP
- Troubleshooting — Connection, registration, and message errors