176 lines
5.0 KiB
Python
176 lines
5.0 KiB
Python
"""Handle websocket api for Matter."""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Coroutine
|
|
from functools import wraps
|
|
from typing import Any, Concatenate, ParamSpec
|
|
|
|
from matter_server.common.errors import MatterError
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.components import websocket_api
|
|
from homeassistant.components.websocket_api import ActiveConnection
|
|
from homeassistant.core import HomeAssistant, callback
|
|
|
|
from .adapter import MatterAdapter
|
|
from .helpers import get_matter
|
|
|
|
_P = ParamSpec("_P")
|
|
|
|
ID = "id"
|
|
TYPE = "type"
|
|
|
|
|
|
@callback
|
|
def async_register_api(hass: HomeAssistant) -> None:
|
|
"""Register all of our api endpoints."""
|
|
websocket_api.async_register_command(hass, websocket_commission)
|
|
websocket_api.async_register_command(hass, websocket_commission_on_network)
|
|
websocket_api.async_register_command(hass, websocket_set_thread_dataset)
|
|
websocket_api.async_register_command(hass, websocket_set_wifi_credentials)
|
|
|
|
|
|
def async_get_matter_adapter(
|
|
func: Callable[
|
|
[HomeAssistant, ActiveConnection, dict[str, Any], MatterAdapter],
|
|
Coroutine[Any, Any, None],
|
|
],
|
|
) -> Callable[
|
|
[HomeAssistant, ActiveConnection, dict[str, Any]], Coroutine[Any, Any, None]
|
|
]:
|
|
"""Decorate function to get the MatterAdapter."""
|
|
|
|
@wraps(func)
|
|
async def _get_matter(
|
|
hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
|
|
) -> None:
|
|
"""Provide the Matter client to the function."""
|
|
matter = get_matter(hass)
|
|
|
|
await func(hass, connection, msg, matter)
|
|
|
|
return _get_matter
|
|
|
|
|
|
def async_handle_failed_command(
|
|
func: Callable[
|
|
Concatenate[HomeAssistant, ActiveConnection, dict[str, Any], _P],
|
|
Coroutine[Any, Any, None],
|
|
],
|
|
) -> Callable[
|
|
Concatenate[HomeAssistant, ActiveConnection, dict[str, Any], _P],
|
|
Coroutine[Any, Any, None],
|
|
]:
|
|
"""Decorate function to handle MatterError and send relevant error."""
|
|
|
|
@wraps(func)
|
|
async def async_handle_failed_command_func(
|
|
hass: HomeAssistant,
|
|
connection: ActiveConnection,
|
|
msg: dict[str, Any],
|
|
*args: _P.args,
|
|
**kwargs: _P.kwargs,
|
|
) -> None:
|
|
"""Handle MatterError within function and send relevant error."""
|
|
try:
|
|
await func(hass, connection, msg, *args, **kwargs)
|
|
except MatterError as err:
|
|
connection.send_error(msg[ID], str(err.error_code), err.args[0])
|
|
|
|
return async_handle_failed_command_func
|
|
|
|
|
|
@websocket_api.require_admin
|
|
@websocket_api.websocket_command(
|
|
{
|
|
vol.Required(TYPE): "matter/commission",
|
|
vol.Required("code"): str,
|
|
vol.Optional("network_only"): bool,
|
|
}
|
|
)
|
|
@websocket_api.async_response
|
|
@async_handle_failed_command
|
|
@async_get_matter_adapter
|
|
async def websocket_commission(
|
|
hass: HomeAssistant,
|
|
connection: ActiveConnection,
|
|
msg: dict[str, Any],
|
|
matter: MatterAdapter,
|
|
) -> None:
|
|
"""Add a device to the network and commission the device."""
|
|
await matter.matter_client.commission_with_code(
|
|
msg["code"], network_only=msg.get("network_only", True)
|
|
)
|
|
connection.send_result(msg[ID])
|
|
|
|
|
|
@websocket_api.require_admin
|
|
@websocket_api.websocket_command(
|
|
{
|
|
vol.Required(TYPE): "matter/commission_on_network",
|
|
vol.Required("pin"): int,
|
|
vol.Optional("ip_addr"): str,
|
|
}
|
|
)
|
|
@websocket_api.async_response
|
|
@async_handle_failed_command
|
|
@async_get_matter_adapter
|
|
async def websocket_commission_on_network(
|
|
hass: HomeAssistant,
|
|
connection: ActiveConnection,
|
|
msg: dict[str, Any],
|
|
matter: MatterAdapter,
|
|
) -> None:
|
|
"""Commission a device already on the network."""
|
|
await matter.matter_client.commission_on_network(
|
|
msg["pin"], ip_addr=msg.get("ip_addr", None)
|
|
)
|
|
connection.send_result(msg[ID])
|
|
|
|
|
|
@websocket_api.require_admin
|
|
@websocket_api.websocket_command(
|
|
{
|
|
vol.Required(TYPE): "matter/set_thread",
|
|
vol.Required("thread_operation_dataset"): str,
|
|
}
|
|
)
|
|
@websocket_api.async_response
|
|
@async_handle_failed_command
|
|
@async_get_matter_adapter
|
|
async def websocket_set_thread_dataset(
|
|
hass: HomeAssistant,
|
|
connection: ActiveConnection,
|
|
msg: dict[str, Any],
|
|
matter: MatterAdapter,
|
|
) -> None:
|
|
"""Set thread dataset."""
|
|
await matter.matter_client.set_thread_operational_dataset(
|
|
msg["thread_operation_dataset"]
|
|
)
|
|
connection.send_result(msg[ID])
|
|
|
|
|
|
@websocket_api.require_admin
|
|
@websocket_api.websocket_command(
|
|
{
|
|
vol.Required(TYPE): "matter/set_wifi_credentials",
|
|
vol.Required("network_name"): str,
|
|
vol.Required("password"): str,
|
|
}
|
|
)
|
|
@websocket_api.async_response
|
|
@async_handle_failed_command
|
|
@async_get_matter_adapter
|
|
async def websocket_set_wifi_credentials(
|
|
hass: HomeAssistant,
|
|
connection: ActiveConnection,
|
|
msg: dict[str, Any],
|
|
matter: MatterAdapter,
|
|
) -> None:
|
|
"""Set WiFi credentials for a device."""
|
|
await matter.matter_client.set_wifi_credentials(
|
|
ssid=msg["network_name"], credentials=msg["password"]
|
|
)
|
|
connection.send_result(msg[ID])
|