mirror of
https://github.com/home-assistant/developers.home-assistant.git
synced 2025-07-10 10:56:28 +00:00
Update the WS API docs (#937)
This commit is contained in:
parent
5401141431
commit
35fed7195f
@ -2,15 +2,7 @@
|
|||||||
title: "WebSocket API"
|
title: "WebSocket API"
|
||||||
---
|
---
|
||||||
|
|
||||||
Home Assistant contains a WebSocket API. This API can be used to stream information from a Home Assistant instance to any client that implements WebSockets. Implementations in different languages:
|
Home Assistant contains a WebSocket API. This API can be used to stream information from a Home Assistant instance to any client that implements WebSockets. We maintain a [JavaScript library](https://github.com/home-assistant/home-assistant-js-websocket) which we use in our frontend.
|
||||||
|
|
||||||
- [JavaScript](https://github.com/home-assistant/home-assistant-js-websocket) - powers the frontend
|
|
||||||
- [Python](https://raw.githubusercontent.com/home-assistant/home-assistant-dev-helper/master/ha-websocket-client.py) - CLI client using [`asyncws`](https://async-websockets.readthedocs.io/en/latest/)
|
|
||||||
- [JavaScript/HTML](https://raw.githubusercontent.com/home-assistant/home-assistant-dev-helper/master/ha-websocket.html) - WebSocket connection in your browser
|
|
||||||
|
|
||||||
Connect your websocket implementation to `ws://localhost:8123/api/websocket`. You will need a valid access token.
|
|
||||||
|
|
||||||
If you are not using the [`frontend`](https://www.home-assistant.io/components/frontend/) in your setup then you need to add the [`websocket_api` component](https://www.home-assistant.io/components/websocket_api/) to your `configuration.yaml` file to use the WebSocket API.
|
|
||||||
|
|
||||||
## Server states
|
## Server states
|
||||||
|
|
||||||
@ -61,7 +53,8 @@ When a client connects to the server, the server sends out `auth_required`.
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "auth_required"
|
"type": "auth_required",
|
||||||
|
"ha_version": "2021.5.3"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -78,7 +71,8 @@ If the client supplies valid authentication, the authentication phase will compl
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "auth_ok"
|
"type": "auth_ok",
|
||||||
|
"ha_version": "2021.5.3"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,104 +8,73 @@ As a component you might have information that you want to make available to the
|
|||||||
|
|
||||||
To register a command, you need to have a message type, a message schema and a message handler. Your component does not have to add the websocket API as a dependency. You register your command, and if the user is using the websocket API, the command will be made available.
|
To register a command, you need to have a message type, a message schema and a message handler. Your component does not have to add the websocket API as a dependency. You register your command, and if the user is using the websocket API, the command will be made available.
|
||||||
|
|
||||||
### Message Types
|
### Defining your command schema
|
||||||
|
|
||||||
Message types are made up the domain and the message type, separated by a forward slash. In the below example, we're defining `media_player/thumbnail`.
|
A command schema is made up of a message type and what type of data we expect when the command is invoked. You define both the command type and the data schema via a decorator on your command handler. Message handlers are callback functions that are run inside the event loop.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# The type of the message
|
|
||||||
WS_TYPE_MEDIA_PLAYER_THUMBNAIL = "media_player/thumbnail"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Message Schema
|
|
||||||
|
|
||||||
The message schema defines what type of data we expect when the message is invoked. It is defined as a voluptuous schema and has to extend the base web socket command schema.
|
|
||||||
|
|
||||||
```python
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.components import websocket_api
|
from homeassistant.components import websocket_api
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
|
@websocket_api.websocket_command(
|
||||||
# The schema for the message
|
|
||||||
SCHEMA_WEBSOCKET_GET_THUMBNAIL = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
|
||||||
{
|
{
|
||||||
"type": WS_TYPE_MEDIA_PLAYER_THUMBNAIL,
|
vol.Required("type"): "frontend/get_panels",
|
||||||
# The entity that we want to retrieve the thumbnail for.
|
vol.Optional("preload_panels"): bool,
|
||||||
"entity_id": cv.entity_id,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@callback
|
||||||
|
def ws_get_panels(
|
||||||
|
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
|
||||||
|
) -> None:
|
||||||
|
"""Handle the websocket command."""
|
||||||
|
panels = ...
|
||||||
|
connection.send_result(msg["id"], {"panels": panels})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Defining a handler
|
#### Doing I/O or sending a delayed response
|
||||||
|
|
||||||
Message handlers are callback functions that are run inside the event loop. If you want to do I/O or have to wait for your result, create a new function and queue it up using `hass.async_add_job`. This is done so that the websocket API can get back to handling the next message as soon as possible.
|
If your command needs to interact with the network, a device or needs to compute information, you will need to queue a job to do the work and send the response. To do this, make your function async and decorate with `@websocket_api.async_response`.
|
||||||
|
|
||||||
#### Sending a direct response
|
|
||||||
|
|
||||||
If you are defining a command that is querying simple information, you might be able to fulfill the request while the handler is being called by the websocket API. To do this, use `connection.to_write.put_nowait`.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@callback
|
from homeassistant.components import websocket_api
|
||||||
def websocket_handle_thumbnail(hass, connection, msg):
|
|
||||||
"""Handle getting a thumbnail."""
|
|
||||||
|
|
||||||
# We know the answer without having to fetch any information,
|
@websocket_api.websocket_command(
|
||||||
# so we send it directly.
|
{
|
||||||
connection.to_write.put_nowait(
|
vol.Required("type"): "camera/get_thumbnail",
|
||||||
websocket_api.result_message(
|
vol.Optional("entity_id"): str,
|
||||||
msg["id"], {"thumbnail": "http://via.placeholder.com/350x150"}
|
}
|
||||||
)
|
)
|
||||||
)
|
@websocket_api.async_response
|
||||||
```
|
async def ws_handle_thumbnail(
|
||||||
|
hass: HomeAssistant, connection: ActiveConnection, msg: dict
|
||||||
#### Sending a delayed response
|
) -> None:
|
||||||
|
|
||||||
If your command needs to interact with the network, a device or needs to compute information, you will need to queue a job to do the work and send the response. To do this, use `connection.send_message_outside`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
@callback
|
|
||||||
def websocket_handle_thumbnail(hass, connection, msg):
|
|
||||||
"""Handle get media player cover command."""
|
"""Handle get media player cover command."""
|
||||||
# Retrieve media player using passed in entity id.
|
# Retrieve media player using passed in entity id.
|
||||||
player = hass.data[DOMAIN].get_entity(msg["entity_id"])
|
player = hass.data[DOMAIN].get_entity(msg["entity_id"])
|
||||||
|
|
||||||
# If the player does not exist, send an error message.
|
# If the player does not exist, send an error message.
|
||||||
if player is None:
|
if player is None:
|
||||||
connection.to_write.put_nowait(
|
connection.send_error(
|
||||||
websocket_api.error_message(
|
|
||||||
msg["id"], "entity_not_found", "Entity not found"
|
msg["id"], "entity_not_found", "Entity not found"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Define a function to be enqueued.
|
data, content_type = await player.async_get_media_image()
|
||||||
async def send_image():
|
|
||||||
"""Send image."""
|
|
||||||
data, content_type = await player.async_get_media_image()
|
|
||||||
|
|
||||||
# No media player thumbnail available
|
# No media player thumbnail available
|
||||||
if data is None:
|
if data is None:
|
||||||
connection.send_message_outside(
|
connection.send_error(
|
||||||
websocket_api.error_message(
|
msg["id"], "thumbnail_fetch_failed", "Failed to fetch thumbnail"
|
||||||
msg["id"], "thumbnail_fetch_failed", "Failed to fetch thumbnail"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
connection.send_message_outside(
|
|
||||||
websocket_api.result_message(
|
|
||||||
msg["id"],
|
|
||||||
{
|
|
||||||
"content_type": content_type,
|
|
||||||
"content": base64.b64encode(data).decode("utf-8"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
|
||||||
# Player exist. Queue up a job to send the thumbnail.
|
connection.send_result(
|
||||||
hass.async_add_job(send_image())
|
msg["id"],
|
||||||
|
{
|
||||||
|
"content_type": content_type,
|
||||||
|
"content": base64.b64encode(data).decode("utf-8"),
|
||||||
|
},
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Registering with the Websocket API
|
### Registering with the Websocket API
|
||||||
@ -115,11 +84,8 @@ With all pieces defined, it's time to register the command. This is done inside
|
|||||||
```python
|
```python
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
"""Setup of your component."""
|
"""Setup of your component."""
|
||||||
hass.components.websocket_api.async_register_command(
|
hass.components.websocket_api.async_register_command(ws_get_panels)
|
||||||
WS_TYPE_MEDIA_PLAYER_THUMBNAIL,
|
hass.components.websocket_api.async_register_command(ws_handle_thumbnail)
|
||||||
websocket_handle_thumbnail,
|
|
||||||
SCHEMA_WEBSOCKET_GET_THUMBNAIL,
|
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Calling the command from the frontend (JavaScript)
|
## Calling the command from the frontend (JavaScript)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user