Most smart home gear you will script against has an HTTP API of some kind, and eventually you will write your own too. This thread uses Home Assistant's REST API as the worked example for consuming an API, then lists the rules I follow when designing one. The Python and C# helpers below were run against a local stand-in server that mimics the documented responses, not against a real Home Assistant, so test against yours. Reference: the HA REST API docs.
The HA REST API in brief
- Served under /api/ on the same port as the web UI (8123 by default). Note the trailing slash on GET /api/.
- Every request needs an Authorization: Bearer header. For scripts, create a long-lived access token on your user profile under Security.
- GET /api/states/<entity_id> returns one entity (404 if unknown). POST /api/services/<domain>/<service> calls an action, for example light/turn_on with a JSON body.
- Status codes: 200 or 201 for success; 400, 401, 404, 405 for client errors; 500 if an action fails; 503 while HA shuts down.
- POST /api/states/<entity_id> only changes what HA displays. It does not talk to the device.
- The docs say REST suits simple one-off requests, and that anything which must stay connected or follow changes should use the WebSocket API.
Code: Select all
curl -H "Authorization: Bearer $HA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"entity_id": "light.kitchen"}' \
http://homeassistant.local:8123/api/services/light/turn_onCode: Select all
import os
import requests
BASE = os.environ.get("HA_URL", "http://homeassistant.local:8123")
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['HA_TOKEN']}"
def get_state(entity_id):
r = session.get(f"{BASE}/api/states/{entity_id}", timeout=10)
if r.status_code == 404:
return None
r.raise_for_status()
return r.json()
def call_action(domain, action, **data):
r = session.post(f"{BASE}/api/services/{domain}/{action}", json=data, timeout=10)
r.raise_for_status()
return r.json()
print(get_state("sun.sun"))
call_action("light", "turn_on", entity_id="light.kitchen")Code: Select all
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient
{
BaseAddress = new Uri(Environment.GetEnvironmentVariable("HA_URL") ?? "http://homeassistant.local:8123"),
Timeout = TimeSpan.FromSeconds(10)
};
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("HA_TOKEN"));
using var resp = await http.GetAsync("api/states/sun.sun");
if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) { Console.WriteLine("No such entity"); return; }
resp.EnsureSuccessStatusCode();
var state = await resp.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(state.GetProperty("state").GetString());
using var call = await http.PostAsJsonAsync("api/services/light/turn_on", new { entity_id = "light.kitchen" });
call.EnsureSuccessStatusCode();Rules for consuming any API
- Always set a timeout. Home hardware goes offline, and a script without a timeout can hang for ages.
- Check the status code before you parse the body.
- Retry only things that are safe to repeat (a GET), with a delay between attempts and a limit. Be careful retrying a toggle, since two runs can leave it in the wrong state.
- Keep tokens in environment variables or a secrets file, never in source code, URLs or forum posts. A long-lived token acts as you. Revoke it if it leaks.
- Prefer HTTPS if the request crosses anything but a trusted LAN.
- Use nouns in URLs (/rooms/lounge/temperature) and let the HTTP method be the verb.
- Return JSON with the right Content-Type and meaningful status codes (404 for unknown, 400 for bad input), plus a short error object.
- Make writes idempotent where you can: "set light to on" is safer than "toggle".
- Validate every input. Treat anything from the network as untrusted.
- Add authentication before you bind to anything other than localhost, and keep it off the internet unless it is behind a reverse proxy with proper auth.
- Version it (/v1/) before other people depend on it.
Discussion: Do you use REST or the WebSocket API for Home Assistant scripts, and how do you store and rotate your tokens?