Skip to content

Commit 8737340

Browse files
authored
Merge branch 'dev' into clean-tokens
2 parents 877ce6c + ec86ac9 commit 8737340

46 files changed

Lines changed: 1839 additions & 474 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.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

.readthedocs.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ version: 2
22

33
# Set the version of Python and other tools you might need
44
build:
5-
os: ubuntu-20.04
5+
os: ubuntu-22.04
66
tools:
77
python: "3.10"
88
jobs:
99
pre_create_environment:
1010
- asdf plugin add poetry
11-
- asdf install poetry latest
12-
- asdf global poetry latest
11+
- asdf install poetry 1.8.3
12+
- asdf global poetry 1.8.3
1313
- poetry export -f requirements.txt --output requirements.txt --with docs
1414
post_install:
1515
- pip install -r requirements.txt

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"python.testing.pytestArgs": [
3+
"tests"
4+
],
5+
"python.testing.unittestEnabled": false,
6+
"python.testing.pytestEnabled": true
7+
}

README.md

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,66 @@
11
# HomeassistantAPI
22

33
[![Code Coverage](https://img.shields.io/codecov/c/github/GrandMoff100/HomeAssistantAPI/dev?style=for-the-badge&token=SJFC3HX5R1)](https://codecov.io/gh/GrandMoff100/HomeAssistantAPI)
4-
[![PyPI - Downloads](https://img.shields.io/pypi/dm/HomeAssistant-API?style=for-the-badge)](https://pypi.org/project/homeassistant_api)
4+
[![PyPI - Downloads](https://img.shields.io/pypi/dm/HomeAssistant-API?style=for-the-badge)](https://pypistats.org/packages/homeassistant-api)
55
![GitHub commits since latest release (by date including pre-releases)](https://img.shields.io/github/commits-since/GrandMoff100/HomeassistantAPI/latest/dev?include_prereleases&style=for-the-badge)
66
[![Read the Docs (version)](https://img.shields.io/readthedocs/homeassistantapi?style=for-the-badge)](https://homeassistantapi.readthedocs.io/en/latest/?badge=latest)
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

13-
## Python wrapper for Homeassistant's [REST API](https://developers.home-assistant.io/docs/api/rest/)
13+
## 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/)
1414

15-
Here is a quick example.
15+
> Note: As of [this comment](https://github.com/home-assistant/architecture/discussions/1074#discussioncomment-9196867) the REST API is not getting any new features or endpoints.
16+
> However, it is not going to be deprecated according to [this comment](https://github.com/home-assistant/developers.home-assistant/pull/2150#pullrequestreview-2017433583)
17+
> But it is recommended to use the Websocket API for new integrations.
18+
19+
### REST API Examples
1620

1721
```py
1822
from homeassistant_api import Client
1923

2024
with Client(
21-
'<API Server URL>',
25+
'<API Server URL>', # i.e. 'http://homeassistant.local:8123/api/'
2226
'<Your Long Lived Access-Token>'
2327
) as client:
28+
light = client.trigger_service('light', 'turn_on', entity_id="light.living_room")
29+
```
30+
31+
All the methods also support async/await!
32+
Just prefix the method with `async_` and pass the `use_async=True` argument to the `Client` constructor.
33+
Then you can use the methods as coroutines
34+
(i.e. `await light.async_turn_on(...)`).
35+
36+
```py
37+
import asyncio
38+
from homeassistant_api import Client
2439

25-
light = client.get_domain("light")
40+
async def main():
41+
with Client(
42+
'<REST API Server URL>', # i.e. 'http://homeassistant.local:8123/api/'
43+
'<Your Long Lived Access-Token>',
44+
use_async=True
45+
) as client:
46+
light = await client.async_trigger_service('light', 'turn_on', entity_id="light.living_room")
2647

27-
light.turn_on(entity_id="light.living_room_lamp")
48+
asyncio.run(main())
2849
```
2950

30-
All the methods also support async!
31-
Just prefix the method with `async_`
32-
(i.e. `await light.async_turn_on(...)`).
51+
### Websocket API Example
52+
53+
```py
54+
from homeassistant_api import WebsocketClient
55+
56+
with WebsocketClient(
57+
'<WS API Server URL>', # i.e. 'ws://homeassistant.local:8123/api/websocket'
58+
'<Your Long Lived Access-Token>'
59+
) as ws_client:
60+
light = ws_client.trigger_service('light', 'turn_on', entity_id="light.living_room")
61+
```
62+
63+
> Note: The Websocket API is not yet supported in async/await mode.
3364
3465
## Documentation
3566

compose.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: "3.9"
2-
31
services:
42
server:
53
image: "homeassistant/home-assistant:stable"
@@ -19,6 +17,7 @@ services:
1917
- server
2018
environment:
2119
HOMEASSISTANTAPI_URL: http://server:8123/api
20+
HOMEASSISTANTAPI_WS_URL: ws://server:8123/api/websocket
2221
HOMEASSISTANTAPI_TOKEN: ${HOMEASSISTANTAPI_TOKEN}
2322
DELAY: 60
2423

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, parse_obj, 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: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Configuration file for the Sphinx documentation builder."""
2+
23
#
34
# This file only contains a selection of the most common options. For a full
45
# list see the documentation:
@@ -20,8 +21,8 @@
2021
# -- Project information -----------------------------------------------------
2122

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

2627
# The full version, including alpha/beta/rc tags
2728
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
#############

0 commit comments

Comments
 (0)