Go client for Home Assistant REST API. This client targets the official REST API documentation: https://developers.home-assistant.io/docs/api/rest
It also includes WebSocket API client helpers based on: https://developers.home-assistant.io/docs/api/websocket
The client follows official Home Assistant REST and WebSocket API docs.
- Go
1.25.9+ - Home Assistant with long-lived access token
- Open Home Assistant in the browser.
- Click your user profile (bottom-left).
- Scroll to Long-Lived Access Tokens.
- Click Create Token, give it a name, and copy the value.
v2.0.0is the first stable v2 release and freezes the public API for thev2major line.v2.xreleases may add new helpers/options and bug fixes, but will not introduce intentional breaking API changes.- Breaking API changes will be introduced only in a new major version (for example
v3). - The package targets official Home Assistant REST and WebSocket APIs and follows their documented behavior where possible.
- Minimum supported Go version is
1.25.9+(see CI/tooling in this repository). - Security issues should be reported using the process in
SECURITY.md.
- Full coverage of all Home Assistant APIs (focus is official REST/WS docs).
- Automations/blueprints scheduling or orchestration.
- Deep schema validation for service payloads (delegated to HA).
- Token management or authentication flows (token must be provided).
- Strong typing of dynamic event attributes (kept as
map[string]interface{}). - Support for unofficial/custom endpoints.
go get github.com/mkelcik/go-ha-client/v2@latestpackage main
import (
"context"
"fmt"
"time"
ha "github.com/mkelcik/go-ha-client/v2"
)
func main() {
client, err := ha.NewClient(
"http://homeassistant.local:8123",
ha.WithToken("YOUR_LONG_LIVED_TOKEN"),
ha.WithTimeout(30*time.Second),
)
if err != nil {
panic(err)
}
if err := client.Ping(context.Background()); err != nil {
panic(err)
}
cfg, err := client.GetConfig(context.Background())
if err != nil {
panic(err)
}
fmt.Printf("Connected to Home Assistant %s\n", cfg.Version)
}- Use REST for request/response operations (query state, call service, render template).
- Use WebSocket for realtime subscriptions (
state_changed, triggers, live event stream).
Change Token and Host to your actual home-assistant bearer token and address
Check home-assistant documentation how get access token.
Shortest setup:
client, err := ha.NewClientWithDefaults("http://my-ha.home", "mytoken")Configurable setup:
package main
import (
"context"
"fmt"
ha "github.com/mkelcik/go-ha-client/v2"
"time"
)
func main() {
client, err := ha.NewClient("http://my-ha.home",
ha.WithToken("mytoken"),
ha.WithTimeout(30*time.Second), // Optional (default is 30s)
)
if err != nil {
panic(err)
}
// ping instance
if err := client.Ping(context.Background()); err != nil {
fmt.Println("connection error", err)
} else {
fmt.Println("connection ok")
}
// example of home-assistant instance info
cfg, err := client.GetConfig(context.Background())
if err != nil {
panic(err)
}
fmt.Printf("%+v", cfg)
}Enable debug logs for REST and WebSocket requests/responses:
client, err := ha.NewClient("http://ha.home",
ha.WithToken("token"),
ha.WithDebug(),
ha.WithTimeout(30*time.Second),
)
if err != nil {
panic(err)
}When enabled, request/response bodies are logged (tokens are redacted for WS auth).
Ready-to-run examples for beginners are in examples/.
Recommended first runs:
examples/rest_ping- check connection and print instance info.examples/rest_ping_defaults- shortest startup withNewClientWithDefaults.examples/rest_switch_control- turn on/off/toggle a switch over REST.examples/ws_switch_control- turn on/off/toggle a switch over WebSocket.examples/ws_watch_light_state- watch light state changes in real time.examples/rest_camera_snapshot- fetch and save camera JPEG.examples/helpers_light_commands- quick tour of command/entity helper functions.examples/helpers_decode_event_data- simpleDecodeEventDatahelper demo (offline).
The examples folder also includes:
- Template rendering
- History query builder usage
- Wait-for-state helper usage (
WaitForState,WaitForStateEquals, andWaitForStateIn) - Auto-reconnect WebSocket setup
- CallServiceForEntity helper usage (REST and WebSocket)
- State-changed subscription helpers for single or multiple entities
- Typed call_service event decoding helper usage
WithOnReconnectandWithOnReconnectErrorcallbacks are called synchronously from the reconnect loop.- Keep those callbacks short and non-blocking (offload heavy work to another goroutine if needed).
- Use typed event helpers (
WSEvent.StateChanged,WSEvent.CallServiceEvent) to avoid manual payload casting. - Event payloads use the real Home Assistant
entity_id(for examplelight.light_janka_ambient_level_light_color_on_off), not the shorter UI/friendly name you may see in cards. - In Home Assistant, find the exact
entity_idinDeveloper Tools -> States(fieldentity_id) orSettings -> Devices & Services -> Entities(entity detail view).
Use sentinel errors with errors.Is:
if _, err := client.GetStateForEntity(ctx, ""); errors.Is(err, ha.ErrEmptyEntityID) {
fmt.Println("entity id is required")
}
if err := client.Ping(ctx); errors.Is(err, ha.ErrUnauthorized) {
fmt.Println("token is invalid or expired")
}- Open one WS connection and reuse it (
ws.Connectonce,defer ws.Close()). WaitForState,WaitForStateEquals, andWaitForStateInare safe to run concurrently from multiple goroutines on one connected WS client.- Always unsubscribe when done (
defer sub.Unsubscribe(...)). - Auto-reconnect is opt-in (disabled by default).
- During auto-reconnect, subscriptions are restored and buffered errors may be forwarded (buffers can still drop when full).
- Reconnect callbacks (
WithOnReconnect,WithOnReconnectError) are blocking/synchronous and should stay lightweight.