Add a WebSocket command
Make data from your integration available to the frontend with a WebSocket command in Core, and call it from the frontend.
The frontend talks to Core over a WebSocket connection. When your integration has data the frontend needs, and it is not an entity state or an attribute, add a WebSocket command. Commands can answer once, or keep sending updates as a subscription.
A command is a function with a decorator that sets its message type and the schema of its data. The type is prefixed with the domain of your integration.
from typing import Any
import probatio
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
@websocket_api.websocket_command(
{
probatio.Required("type"): "example/get_status",
probatio.Required("device_id"): str,
}
)
@callback
def ws_get_status(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Return the status of a device."""
status = hass.data[DOMAIN].get(msg["device_id"])
if status is None:
connection.send_error(msg["id"], "not_found", "Device not found")
return
connection.send_result(msg["id"], {"status": status})Register the command when your integration is set up:
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the example integration."""
websocket_api.async_register_command(hass, ws_get_status)
return TrueYour integration does not need websocket_api as a dependency in its manifest. The command is registered either way, and is available as soon as the WebSocket API runs.
A function decorated with @callback runs in the event loop, so it must not do any I/O. When your command has to wait, for a device or the network, make it async and add @websocket_api.async_response below websocket_command:
@websocket_api.websocket_command(
{probatio.Required("type"): "example/refresh"}
)
@websocket_api.async_response
async def ws_refresh(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Refresh all devices."""
await hass.data[DOMAIN].async_refresh()
connection.send_result(msg["id"])Add @websocket_api.require_admin to a command that only administrators may use.
For data that changes, send the first result, then send an event with the same message ID on every change. Store a function that stops the updates in connection.subscriptions, and Core calls it when the frontend unsubscribes or disconnects:
@websocket_api.websocket_command(
{probatio.Required("type"): "example/subscribe_status"}
)
@callback
def ws_subscribe_status(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Send status updates to the frontend."""
@callback
def on_update(status: dict[str, Any]) -> None:
connection.send_event(msg["id"], status)
connection.subscriptions[msg["id"]] = hass.data[DOMAIN].subscribe(
on_update
)
connection.send_result(msg["id"])The WebSocket API reference covers the WebSocket API of Core in more depth.
Add the types and a function for your command to a module in src/data/, named after your integration. Components call that function, never the raw message:
export interface ExampleStatus {
status: string;
}
export const fetchExampleStatus = (
hass: HomeAssistant,
deviceId: string
) =>
hass.callWS<ExampleStatus>({
type: "example/get_status",
device_id: deviceId,
});
export const subscribeExampleStatus = (
conn: Connection,
onChange: (status: ExampleStatus) => void
) =>
conn.subscribeMessage<ExampleStatus>(onChange, {
type: "example/subscribe_status",
});hass.callWS() returns a promise that rejects with the error when Core sends one. subscribeMessage() returns a promise of a function that unsubscribes. In a component, return it from hassSubscribe() and SubscribeMixin cleans it up. The data layer explains the patterns.
Custom cards and panels can call your command the same way, with hass.callWS() or hass.connection.subscribeMessage().