homeassistant
tested on simulator
| Tier | Tier 1 |
|---|---|
| Protocol | Home Assistant REST API, Home Assistant WebSocket API |
| Covers | lights, switches and smart plugs, fans, number and select helpers, sensors and binary sensors, Zigbee, Z-Wave, Matter and Wi-Fi devices that Home Assistant integrates |
| Hazard default | low |
| Conformance | control |
| Tested with | Home Assistant simulator over an HTTP mock transport and a real local WebSocket server |
Estimated reach. Tens of thousands of device models, through more than two thousand integrations. Home Assistant lists over two thousand integrations, and each brand integration covers many models. Every device Home Assistant shows as a light, switch, fan, number, select, sensor or binary sensor entity works through this bridge with no extra code. Zigbee, Z-Wave and Matter devices are reached the same way.
The homeassistant driver is a bridge that exposes a few Home Assistant entities, selected by the device file, as one OpenHAL device, so the safety kernel's limits, interlocks and confirmations apply to those entities. Everything else in the Home Assistant instance stays out of reach.
| Driver name | homeassistant |
| Libraries | httpx 0.28.1 (BSD-3-Clause), websockets 17.3 (BSD-3-Clause) |
| Extra | uv tool install --compile-bytecode "openhal-hub[homeassistant] @ git+https://github.com/sohan-bhat/OpenHAL" |
| Hazard default | low (set the file's hazard to what the entities really drive) |
| Safe state | from the device file only |
| Self-describe | yes, the selected entities with their units and Home Assistant's own limits as suggestions |
Which entities are exposed
entitynames one entity, orentitieslists several, and a channel'smap.entitymust be one of the selected entities, otherwise the file is refused when it loads.- With neither, the entities named in channel maps are the selection.
match.homeassistantis a glob, and every selected entity must match it.- The device file's limits apply even when Home Assistant has its own limits, so a
numberentity'smin,maxandsteponly appear indescribe()assuggestedvalues.
Channel roles
A channel's role comes from the entity's domain and from the channel name or map.attribute. With one selected entity no map is needed, but with several entities, give map.entity and name the channel after the role, or end the channel's name with the role (lamp_brightness).
| Domain | Channel name or attribute | Type | Reads | Writes |
|---|---|---|---|---|
| light | power (any other name) | bool | state on or off | light.turn_on, light.turn_off |
| light | brightness | number, percent | brightness 0 to 255, 0 % when off | light.turn_on with brightness_pct |
| light | color_temp (color_temp_kelvin) | number, K | color_temp_kelvin | light.turn_on with color_temp_kelvin |
| switch, input_boolean | power | bool | state | turn_on, turn_off |
| fan | power | bool | state | fan.turn_on, fan.turn_off |
| fan | speed (percentage) | number, percent | percentage | fan.set_percentage |
| fan | preset (preset_mode) | enum | preset_mode | fan.set_preset_mode |
| number, input_number | value | number | state, converted from unit_of_measurement | set_value, converted to it |
| select, input_select | option | enum | state | select_option |
| sensor | any | number or string | state, converted from unit_of_measurement | read-only |
| binary_sensor | any | bool | state | read-only |
| any | map: { attribute: name } | any | that attribute | read-only |
Units from Home Assistant (°C, °F, %, kWh, hPa and the rest) are converted to the channel's unit. A sensor whose unit can't convert fails its read with a clear message.
An entity in state unavailable fails its reads, and health() reports the device disconnected, while an entity in state unknown reads as no value.
Transports
transport: rest, the default, sendsGET /api/states/<entity_id>per read andPOST /api/services/<domain>/<service>per write.- With
transport: websocket, the driver authenticates, sendssubscribe_entitiesfor the selected entities only and keeps their states in a cache, so reads cost no round trip, and writes usecall_service.
Both transports send the long-lived access token from the environment variable named in token_env (default HA_TOKEN).
Setup
- Install the extra with
uv tool install --compile-bytecode "openhal-hub[homeassistant] @ git+https://github.com/sohan-bhat/OpenHAL". - In Home Assistant, open your profile, Security, and create a long-lived access token. A non-admin user that can see only the needed entities is the safest choice.
- Export the token, for example
export HA_TOKEN=..., where the hub runs. - Start from
openhal/drivers/homeassistant/examples/office-climate.hal.yaml, or fromdescribe(). - Validate with
openhal file validate, then confirm the limits by hand.
Parameters
| Key | Default | Meaning |
|---|---|---|
host | required | Home Assistant host name or address |
port | 8123 | Port |
scheme | http | http or https (and ws or wss) |
token_env | HA_TOKEN | Environment variable that holds the access token |
entity | none | The one selected entity |
entities | none | The selected entities, up to 64 |
transport | rest | rest or websocket |
timeout | 5 s | Deadline for each request |
Example device file
device: Desk lamp
id: desk-lamp
match: { homeassistant: light.desk_lamp }
connect: { homeassistant: homeassistant.local, port: 8123, token_env: HA_TOKEN, entity: light.desk_lamp }
write:
power: { type: bool }
brightness: { unit: percent, min: 1, max: 100, step: 1 }
safe_state: { power: false }
hazard: lowKnown limitations
- Climate, cover, lock, vacuum, media player and other domains are read-only through
map.attributefor now, because locks and covers move things and need their own careful mapping. - Because the driver has only what Home Assistant reports, a device that an integration marks available while the device is not available still passes the health check.
- Home Assistant automations can still change the same entities, because OpenHAL limits only what goes through OpenHAL.
- Rate limits and interlocks act on OpenHAL commands only, so a person at a wall switch bypasses them by design.
Hazard defaults
The default hazard is low, because lights, fans and sensors are the usual case, but a switch that powers a heater or a pump deserves medium or high in the device file.
Sources
- Home Assistant REST API, https://developers.home-assistant.io/docs/api/rest/
- Home Assistant WebSocket API, https://developers.home-assistant.io/docs/api/websocket/ (auth, call_service, subscribe_entities and its compressed state format)
- Entity attributes and services, https://developers.home-assistant.io/docs/core/entity/light/ and the fan, number and select pages next to it
- Long-lived access tokens, https://www.home-assistant.io/docs/authentication/