py-ha-ws-client
An async Python client for the Home Assistant websocket API.
Built on aiohttp. Requires Python 3.12+.
Version 1.0 is a full rewrite: every method is now a coroutine and the
transport is aiohttp instead of the unmaintained ws4py. See
Migrating from 0.x if you are upgrading.
Install
pip install py_ha_ws_client
Quickstart
import asyncio
from py_ha_ws_client import HomeAssistantWsClient
TOKEN = "<long-lived access token from Home Assistant>"
async def main():
async with HomeAssistantWsClient.with_host_and_port(TOKEN, "homeassistant.local") as client:
# Fetch state
states = await client.get_states()
print(f"{len(states)} entities")
print(await client.get_state("media_player.amplifier"))
# React to changes
async def on_change(event):
trigger = event["variables"]["trigger"]
new_state = trigger["to_state"]["state"]
print(f"amplifier is now {new_state}")
sub = await client.subscribe_trigger(
{"platform": "state", "entity_id": "media_player.amplifier"},
on_change,
)
# Call services
await client.call_service(
"media_player",
"volume_set",
target={"entity_id": "media_player.amplifier"},
service_data={"volume_level": 0.5},
)
await asyncio.sleep(30)
await sub.unsubscribe()
asyncio.run(main())
API
Constructors
HomeAssistantWsClient.with_host_and_port(token, host, port=8123, **options)
HomeAssistantWsClient.with_url(token, url, **options) # url like ws://host:8123/api/websocket
HomeAssistantWsClient.in_ha_addon(**options) # uses $SUPERVISOR_TOKEN
Options (keyword-only):
| option | default | meaning |
|---|---|---|
auto_reconnect |
True |
reconnect, re-auth and re-subscribe after an unexpected disconnect |
connect_timeout |
10.0 |
seconds allowed for the open + auth handshake in connect() |
heartbeat |
30.0 |
websocket ping interval (aiohttp autoping); None disables |
session |
None |
supply your own aiohttp.ClientSession; if omitted the client owns one and closes it on disconnect() |
Lifecycle
await client.connect() # opens socket, runs auth handshake, starts the receive loop
await client.disconnect() # cancels background tasks, closes socket (and owned session); idempotent
async with HomeAssistantWsClient.with_url(token, url) as client:
...
connect() returns only once authenticated and raises HaAuthError
(bad token or handshake timeout) or HaConnectionError (socket failure).
Properties
client.is_connected— the socket is openclient.is_authenticated— the auth handshake completed on the current socket
Requests
result = await client.call_service(domain, service, target=None, service_data=None)
states = await client.get_states()
state = await client.get_state("light.kitchen") # None if unknown
await client.turn_on("light.kitchen")
await client.turn_off("light.kitchen")
Requests are correlated to their responses by message id. call_service
returns the result payload and raises HaError if Home Assistant reports
success: false. A request issued while disconnected raises
HaConnectionError.
Subscriptions
sub = await client.subscribe_events(event_type, callback) # event_type=None -> all events
sub = await client.subscribe_trigger(trigger, callback) # trigger is a raw HA trigger dict
await sub.unsubscribe()
# or:
await client.unsubscribe(sub.id)
callback may be a sync or async callable. It is invoked from the receive
loop with each matching event's event payload. A callback that raises is
logged and does not disturb the receive loop or other subscriptions.
subscribe_* returns a Subscription handle (.id, await .unsubscribe()).
Resilience
- Auto-reconnect (on by default): on an unexpected disconnect a
background task retries with capped exponential backoff (1s → 30s),
re-runs the auth handshake, and re-sends every active subscription. The
Subscriptionhandles you already hold keep working — theiridis refreshed in place. In-flight requests fail withHaConnectionError. - Keepalive: aiohttp sends websocket pings every
heartbeatseconds and treats a missing pong as a disconnect. - All background tasks are cancelled on
disconnect()and onasync withexit.
Exceptions
HaError— base class; also raised when a command returnssuccess: falseHaConnectionError— socket could not be opened / was lost / used while disconnectedHaAuthError— token rejected, or the auth handshake timed out
Migrating from 0.x
The 0.x client was synchronous and threaded (ws4py). 1.0 is
asyncio-native. Changes:
- Everything is a coroutine.
awaitevery call and run inside an event loop (asyncio.run(...)). connect()now blocks until authenticated and raises on failure. Deletewhile not client.connected(): sleep(1)loops.connected()method →is_connectedproperty. Newis_authenticatedproperty.subscribe_to_trigger(entity_id=..., callback=...)→await subscribe_trigger(trigger={...}, callback=...). Pass a raw HA trigger dict, e.g.{"platform": "state", "entity_id": "light.x", "to": "on"}. The callback signature changed fromcallback(entity_id, message)tocallback(event), whereeventis the HA event payload.- New
subscribe_events(event_type, callback)for raw event subscriptions. call_service(..., entity_id=...)→call_service(..., target={"entity_id": ...}). It now returns the result and raisesHaErroronsuccess: false.get_states()/get_state()are awaitable and raise instead of logging a warning when disconnected.- Unsubscribe is supported:
await sub.unsubscribe()orawait client.unsubscribe(sub_id). async withsupport for guaranteed cleanup.- Transport is
aiohttp. Removews4pyfrom your dependencies. - The
turn_on/turn_offhelpers survive, now asawait client.turn_on(entity_id).
Development
See DEVELOPMENT.md. Run the tests with:
pip install -e ".[test]"
pytest
Metadata
Release files for py-ha-ws-client 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| py_ha_ws_client-1.0.0.tar.gz | 17.5 kB | Without provenance | Details for py_ha_ws_client-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| py_ha_ws_client-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for py_ha_ws_client-1.0.0-py3-none-any.whl |
Total release size: 31.4 kB
Release files / py_ha_ws_client-1.0.0.tar.gz
| Download URL | py_ha_ws_client-1.0.0.tar.gz |
|---|---|
| Size | 17.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0184868c44506dbdff50f378870512bbed092bd4c0ad88b4ac5d7e0819edb5b8
|
|
BLAKE2b-256 checksum How to use checksums |
02967b942624747c8d4cff533de7cd410f0edbc00f8b42b755c0c6bf0bf9fbb1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / py_ha_ws_client-1.0.0-py3-none-any.whl
| Download URL | py_ha_ws_client-1.0.0-py3-none-any.whl |
|---|---|
| Size | 13.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
065a26b2c27c4a40fd051340153737bfb3be944849052754b455d00b76b30ba8
|
|
BLAKE2b-256 checksum How to use checksums |
4c75e5a12e1b23cae44484c2bcad80b73dcb55251ac94453e603df5f51389bbd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for py-ha-ws-client 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.