Bridge protocol
The RPCs the bridge serves on its Unix socket, and their wire format.
The bridge serves Effect RPCs as newline-delimited JSON on a Unix socket. From TypeScript, use @timmo001/effect-ha-bridge, which handles the framing for you. This page is for other languages and for debugging.
The socket is at the socket path, in a directory only you can open (0700), and is itself only accessible by you (0600).
RPCs
| RPC | Payload | Result |
|---|---|---|
GetEntity |
{ "entityId": string } |
An entity update, or null when the bridge has no state for it |
WatchEntity |
{ "entityId": string } |
A stream of entity updates: the current state, if any, then every change |
CallAction |
{ "action": string, "data"?: object, "target"?: object, "return_response"?: boolean } |
The action’s response when return_response is true, otherwise null |
GetConfig |
null |
Home Assistant’s config, such as time_zone, location_name and version |
CameraSnapshot |
{ "entityId": "camera.<name>" } |
{ "contentType": string, "data": string }, with the image bytes base64-encoded in data |
An entity update is { "state": EntityState, "name": string }, where state is the state object Home Assistant sends (entity_id, state, attributes and timestamps) and name is the display name described in Bar JSON.
CallAction, GetConfig and CameraSnapshot fail with HomeAssistantError ({ "_tag": "HomeAssistantError", "message": string }) when Home Assistant rejects the request or the bridge isn’t connected to it. CallAction takes the same action, data and target keys as actions in Home Assistant automations.
Messages
Each line is one JSON message. Send a request:
{"_tag":"Request","id":"1","tag":"GetEntity","payload":{"entityId":"sun.sun"},"headers":[]}
A single result comes back as an Exit with the same ID:
{"_tag":"Exit","requestId":"1","exit":{"_tag":"Success","value":{"state":{"entity_id":"sun.sun","state":"above_horizon","attributes":{}},"name":"Sun"}}}
A failure has "_tag":"Failure" and a cause list instead of value. An RPC’s own error is a Fail entry:
{"_tag":"Exit","requestId":"2","exit":{"_tag":"Failure","cause":[{"_tag":"Fail","error":{"_tag":"HomeAssistantError","message":"camera snapshot: StatusCode: non 2xx status code (404 GET ...)"}}]}}
A malformed request comes back as a Die entry, such as {"_tag":"Die","defect":"Expected null"} when GetConfig has no payload.
Keep the connection open until the reply arrives. When the client closes its side, the bridge ends that client’s requests that are still running.
Streams send Chunk messages, each with one or more values:
{"_tag":"Chunk","requestId":"1","values":[{"state":{"entity_id":"sun.sun","state":"above_horizon","attributes":{}},"name":"Sun"}]}
After each chunk, the bridge waits for an Ack before sending the next one:
{"_tag":"Ack","requestId":"1"}
To stop a stream, send {"_tag":"Interrupt","requestId":"1"} or close the connection.
Try it
socket="$XDG_RUNTIME_DIR/ha-bridge/ha-bridge.sock"
{
echo '{"_tag":"Request","id":"1","tag":"GetConfig","payload":null,"headers":[]}'
sleep 1
} | socat - "UNIX-CONNECT:$socket"