1 """The Hardware websocket API."""
3 from __future__
import annotations
6 from dataclasses
import asdict, dataclass
7 from datetime
import datetime, timedelta
10 import psutil_home_assistant
as ha_psutil
11 import voluptuous
as vol
19 from .const
import DOMAIN
20 from .hardware
import async_process_hardware_platforms
21 from .models
import HardwareProtocol
24 @dataclass(slots=True)
29 remove_periodic_timer: CALLBACK_TYPE |
None
34 """Set up the hardware websocket API."""
35 websocket_api.async_register_command(hass, ws_info)
36 websocket_api.async_register_command(hass, ws_subscribe_system_status)
38 ha_psutil=await hass.async_add_executor_job(ha_psutil.PsutilWrapper),
39 remove_periodic_timer=
None,
44 @websocket_api.websocket_command(
{
vol.Required("type"):
"hardware/info",
47 @websocket_api.async_response
51 """Return hardware info."""
54 if "hardware_platform" not in hass.data[DOMAIN]:
57 hardware_platform: dict[str, HardwareProtocol] = hass.data[DOMAIN][
60 for platform
in hardware_platform.values():
61 if hasattr(platform,
"async_info"):
62 with contextlib.suppress(HomeAssistantError):
63 hardware_info.extend([asdict(hw)
for hw
in platform.async_info(hass)])
65 connection.send_result(msg[
"id"], {
"hardware": hardware_info})
69 @websocket_api.websocket_command(
{
vol.Required("type"):
"hardware/subscribe_system_status",
75 """Subscribe to system status updates."""
77 system_status: SystemStatus = hass.data[DOMAIN][
"system_status"]
80 def async_update_status(now: datetime) ->
None:
85 cpu_percentage = round(
86 system_status.ha_psutil.psutil.cpu_percent(interval=
None)
88 virtual_memory = system_status.ha_psutil.psutil.virtual_memory()
90 "cpu_percent": cpu_percentage,
91 "memory_used_percent": virtual_memory.percent,
92 "memory_used_mb": round(
93 (virtual_memory.total - virtual_memory.available) / 1024**2, 1
95 "memory_free_mb": round(virtual_memory.available / 1024**2, 1),
96 "timestamp": dt_util.utcnow().isoformat(),
98 for conn, msg_id
in system_status.subscribers:
99 conn.send_message(websocket_api.event_message(msg_id, json_msg))
101 if not system_status.subscribers:
103 hass, async_update_status,
timedelta(seconds=5)
106 system_status.subscribers.add((connection, msg[
"id"]))
109 def cancel_subscription() -> None:
110 system_status.subscribers.remove((connection, msg[
"id"]))
111 if not system_status.subscribers
and system_status.remove_periodic_timer:
112 system_status.remove_periodic_timer()
113 system_status.remove_periodic_timer =
None
115 connection.subscriptions[msg[
"id"]] = cancel_subscription
117 connection.send_message(websocket_api.result_message(msg[
"id"]))
118
None async_process_hardware_platforms(HomeAssistant hass)
None async_setup(HomeAssistant hass)
None ws_info(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
None ws_subscribe_system_status(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)