Skip to content

Commit 698ea4f

Browse files
author
Nathan Larsen
authored
Merge pull request #200 from GrandMoff100/websocket-docs
Websocket docs
2 parents 040b6af + 29007fc commit 698ea4f

14 files changed

Lines changed: 240 additions & 68 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ venv/
1717
.replit
1818
.breakpoints
1919

20+
# Informal testing
21+
main.py
22+
2023
# Cache files
2124
*.sqlite
2225

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/GrandMoff100/HomeassistantAPI?style=for-the-badge)](https://github.com/GrandMoff100/HomeassistantAPI/releases)
88

99
<a href="https://home-assistant.io">
10-
<img src="https://github.com/GrandMoff100/HomeAssistantAPI/blob/7edb4e6298d37bda19c08b807613c6d351788491/docs/images/homeassistant-logo.png?raw=true" width="60%">
10+
<img src="https://github.com/GrandMoff100/HomeAssistantAPI/blob/7edb4e6298d37bda19c08b807613c6d351788491/docs/images/homeassistant-logo.png?raw=true" width="80%">
1111
</a>
1212

1313
## Python wrapper for Homeassistant's [Websocket API](https://developers.home-assistant.io/docs/api/websocket/) and [REST API](https://developers.home-assistant.io/docs/api/rest/)

docs/advanced.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Rather than the default behavior, which is saving the cache to memory or not at
1212
If you want to persist your requests cache you can pass your own custom cached session to :py:class:`Client`'s init method.
1313
You can pass a variety of options to your cached session like how fast to expire the cache, where to cache it (the cache backend), and what to do when the cache is expired.
1414

15-
Depending on whether you are using this in an async of sync project you will want to use either :py:class:`aiohttp_client_cache.backends.CachedSession` or :py:class:`requests_cache.CachedSession` respectively.
15+
Depending on whether you are using this in an async or sync project you will want to use either :py:class:`aiohttp_client_cache.backends.CachedSession` or :py:class:`requests_cache.CachedSession` respectively.
1616
See the docs for `requests_cache <https://requests-cache.readthedocs.io/en/latest/>`__ and `aiohttp_client_cache <https://aiohttp-client-cache.readthedocs.io/en/latest/>`__ for how to implement these backends, options, and much more.
1717

1818
You can simply pass them to your client like so.

docs/api.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ Code Reference
44
.. automodule:: homeassistant_api
55
:platform: Linux, Windows, MacOS
66
:inherited-members:
7-
:exclude-members: model_json_schema, model_copy, model_rebuild, model_dump, construct, copy, dict, from_orm, json, parse_file, model_validate, parse_raw, parse_str, parse_url, schema, schema_json, schema_yaml, schema_yml, to_orm, update_forward_refs, validate, validate_file, validate_obj, validate_raw, validate_str, validate_url, model_validate_strings, model_validate_json, model_validate, model_post_init, model_parametrized_name, model_extra, model_fields_set, model_dump_json, model_construct, model_computed_fields
7+
:exclude-members: model_json_schema, model_copy, model_rebuild, model_dump, construct, copy, dict, from_orm, json, parse_file, model_validate, parse_raw, parse_obj, parse_str, parse_url, schema, schema_json, schema_yaml, schema_yml, to_orm, update_forward_refs, validate, validate_file, validate_obj, validate_raw, validate_str, validate_url, model_validate_strings, model_validate_json, model_validate, model_post_init, model_parametrized_name, model_extra, model_fields_set, model_dump_json, model_construct, model_computed_fields

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
# -- Project information -----------------------------------------------------
2222

2323
project = "Homeassistant API"
24-
copyright = "2024, Nathan Larsen" # pylint: disable=redefined-builtin
25-
author = "Nate Larsen"
24+
copyright = "2023-2025, Nathan Larsen" # pylint: disable=redefined-builtin
25+
author = "Nathan Larsen"
2626

2727
# The full version, including alpha/beta/rc tags
2828
with open("../pyproject.toml") as f:

docs/index.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
Welcome to Homeassistant API!
1010
=============================
1111

12-
Homeassistant API is a pythonic module that interacts with `Homeassistant's REST API integration <https://developers.home-assistant.io/docs/api/rest>`_.
13-
You can use it to remotely control your Home Assistant like getting entity states, triggering services, etc.
12+
Homeassistant API is a pythonic module that interacts with `Homeassistant's REST API integration <https://developers.home-assistant.io/docs/api/rest>`_ and Homeassistant's `Websocket API <https://developers.home-assistant.io/docs/api/websocket>`_.
13+
You can use it to remotely control your Home Assistant to do things like turn on lights, change the temperature, or listen for when the garage door opens.
1414

1515
Index
1616
----------
@@ -30,6 +30,7 @@ Features
3030
----------
3131

3232
- Full consumption of the Home Assistant REST API endpoints.
33+
- Full consumption of the Home Assistant Websocket API (all of the documented commands and some undocumented ones)
3334
- Convenient Pydantic Models for data validation.
3435
- Syncrononous and Asynchronous support for integrating with all applications and/or libraries.
3536
- Modular design for intuitive readability.

docs/usage.rst

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@ Usage
66
The Basics...
77
#################
88

9-
This library is centered around the :py:class:`Client` class.
9+
This library is centered around the :py:class:`Client` and :py:class:`WebsocketClient` classes.
1010
Once you have have your api base url and Long Lived Access Token from Home Assistant we can start to do stuff.
11-
The rest of this guide assumes you have the :py:class:`Client` saved to a :code:`client` variable.
11+
The rest of this guide assumes you have the :py:class:`Client` saved to a :code:`client` variable or a :py:class:`WebsocketClient` saved to a :code:`ws_client` variable.
1212
Most of these examples require some integrations to be setup inside Home Assistant for the examples to actually work.
1313
The most commonly used features of this library include triggering services and getting and modifying entity states.
1414

15-
1615
.. code-block:: python
1716
:linenos:
1817
1918
import os
2019
from homeassistant_api import Client
2120
22-
URL = '<API BASE URL>'
21+
URL = '<API BASE URL>' # Example: 'https://foobarhomeassistant.duckdns.org:8123/api'
2322
TOKEN = '<LONG LIVED ACCESS TOKEN>'
2423
2524
# Assigns the Client object to a variable and checks if it's running.
@@ -31,6 +30,18 @@ The most commonly used features of this library include triggering services and
3130
# Triggers the light.turn_on service on the entity `light.my_living_room_light`
3231
3332
33+
.. code-block:: python
34+
:linenos:
35+
36+
from homeassistant_api import WebsocketClient
37+
38+
WS_URL = '<WS API BASE URL>' # Example: 'https://foobarhomeassistant.duckdns.org:8123/api/websocket'
39+
TOKEN = '<LONG LIVED ACCESS TOKEN>'
40+
41+
with WebsocketClient(WS_URL, TOKEN) as ws_client: # opens a websocket connection to Home Assistant
42+
print(ws_client.render_template("{{ states('sensor.my_sensor') }}"))
43+
44+
3445
.. code-block:: python
3546
:linenos:
3647
@@ -66,6 +77,15 @@ Services
6677
6778
changed_states = light.toggle(entity_id="light.light_bulb_1")
6879
80+
.. code-block:: python
81+
82+
climate = ws_client.get_domain("climate")
83+
84+
print(climate.services)
85+
# {'set_temperature': Service(service_id='set_temperature', name='Set temperature', description='Set the target temperature for a climate entity.\n', ...
86+
87+
changed_states = climate.set_temperature(entity_id="climate.my_thermostat", temperature=72)
88+
6989
Entities
7090
*************
7191

@@ -95,12 +115,15 @@ Entities
95115
door.set_state(State(state="My new state", attributes={"open_height": "5ft"}))
96116
# <State "My new state" entity_id="cover.garage_door">
97117
118+
## All of these methods can be used with the WebsocketClient as well [except for set_state because the WS API doesn't support it :((( ].
98119
99120
Using Client with :code:`async`/:code:`await`
100121
*************************************************
101122
Are you wondering if you can use :code:`homeassistant_api` using Python's :code:`async`/:code:`await` syntax?
102123
Good news! You can!
103124

125+
(You can't use the WebsocketClient with :code:`async`/:code:`await` yet because we haven't implemented it yet.)
126+
104127
Async Services
105128
********************
106129
.. code-block:: python
@@ -159,8 +182,67 @@ Async Entities
159182
# <State "My new state" entity_id="cover.garage_door">
160183
161184
185+
Using Events (Listening and Firing)
186+
*****************************************
187+
188+
.. code-block:: python
189+
190+
from homeassistant_api import WebsocketClient
191+
192+
WS_URL = '<WS API BASE URL>' # Example: 'https://foobarhomeassistant.duckdns.org:8123/api/websocket'
193+
TOKEN = '<LONG LIVED ACCESS TOKEN>'
162194
195+
with WebsocketClient(WS_URL, TOKEN) as ws_client:
196+
with ws_client.listen_events() as events:
197+
for event in events:
198+
print(event)
199+
200+
# Or if you want to listen for a specific event type until dinner time.
201+
with ws_client.listen_events('state_changed') as events:
202+
for event in events:
203+
print(event)
204+
if event.data.entity_id == 'myalarmclock.dinner_time' and event.data.new_state.state == 'now':
205+
break
206+
207+
# Or if you want to listen for just 10 events.
208+
with ws_client.listen_events("my_event") as events:
209+
for _, event in zip(range(10), events):
210+
print(event)
211+
212+
# Alternatively for just one event.
213+
with ws_client.listen_events("my_event") as events:
214+
event = next(events)
215+
print(event)
216+
217+
# Now to fire an event.
218+
ws_client.fire_event("my_event", my_arg="my_value")
219+
220+
221+
Listening for Triggers
222+
**************************
163223

224+
.. code-block:: python
225+
226+
from homeassistant_api import WebsocketClient
227+
228+
with WebsocketClient(WS_URL, TOKEN) as ws_client:
229+
with ws_client.listen_triggers() as triggers: # see WebsocketClient.listen_triggers for more info.
230+
for trigger in triggers:
231+
print(trigger)
232+
233+
# Another more specific example, listening for event triggers.
234+
with ws_client.listen_triggers("event", event_type="my_event") as triggers:
235+
ws_client.fire_event("my_event", my_arg="my_value")
236+
237+
for trigger in triggers:
238+
print(trigger.variables.my_arg) # This is the value of my_arg from the event fired above.
239+
240+
# Another one, listening for time triggers.
241+
future = ws_client.get_rendered_template(
242+
"{{ (now() + timedelta(seconds=1)).strftime('%H:%M:%S') }}"
243+
)
244+
with ws_client.listen_trigger("time", at=future) as triggers: # `at` can be HH:MM or HH:MM:SS
245+
print(next(triggers))
164246
165247
What's Next?
166248
#############

homeassistant_api/__init__.py

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,40 @@
33
__all__ = (
44
"Client",
55
"State",
6+
"Context",
7+
"Domain",
68
"Service",
7-
"History",
89
"Group",
9-
"Event",
1010
"Entity",
11-
"Domain",
12-
"Processing",
11+
"History",
12+
"Event",
1313
"LogbookEntry",
14-
"APIConfigurationError",
15-
"EndpointNotFoundError",
16-
"HomeassistantAPIError",
17-
"MalformedDataError",
18-
"MalformedInputError",
19-
"MethodNotAllowedError",
20-
"ParameterMissingError",
21-
"RequestError",
22-
"UnauthorizedError",
2314
"WebsocketClient",
15+
"AuthInvalid",
16+
"AuthOk",
17+
"AuthRequired",
18+
"ResultResponse",
19+
"ErrorResponse",
20+
"PingResponse",
21+
"EventResponse",
2422
)
2523

2624
from .client import Client
27-
from .errors import (
28-
APIConfigurationError,
29-
EndpointNotFoundError,
30-
HomeassistantAPIError,
31-
MalformedDataError,
32-
MalformedInputError,
33-
MethodNotAllowedError,
34-
ParameterMissingError,
35-
RequestError,
36-
UnauthorizedError,
25+
from .models.domains import Domain, Service
26+
from .models.entity import Entity, Group
27+
from .models.events import Event
28+
from .models.history import History
29+
from .models.logbook import LogbookEntry
30+
from .models.states import Context, State
31+
from .models.websocket import (
32+
AuthInvalid,
33+
AuthOk,
34+
AuthRequired,
35+
ErrorResponse,
36+
EventResponse,
37+
PingResponse,
38+
ResultResponse,
3739
)
38-
from .models import Domain, Entity, Event, Group, History, LogbookEntry, Service, State
39-
from .processing import Processing
4040
from .websocket import WebsocketClient
4141

4242
Domain.model_rebuild()

homeassistant_api/rawclient.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
class RawClient(RawBaseClient):
4242
"""
43-
The base object for interacting with Homeassistant.
43+
The base object for interacting with Homeassistant via the REST API.
4444
4545
:param api_url: The location of the api endpoint. e.g. :code:`http://localhost:8123/api` Required.
4646
:param token: The refresh or long lived access token to authenticate your requests. Required.

homeassistant_api/rawwebsocket.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
ResultResponse,
2323
)
2424

25-
2625
logger = logging.getLogger(__name__)
2726

2827

0 commit comments

Comments
 (0)