Skip to content
Home Assistant Bridge
Esc
↑↓navigate↵open⌘Jpreview
On this page

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"

Last updated on October 1, 2026