Home Assistant Unofficial Reference 2024.12.1
device_registry.py
Go to the documentation of this file.
1 """HTTP views to interact with the device registry."""
2 
3 from __future__ import annotations
4 
5 from typing import Any, cast
6 
7 import voluptuous as vol
8 
9 from homeassistant import loader
10 from homeassistant.components import websocket_api
11 from homeassistant.components.websocket_api import require_admin
12 from homeassistant.core import HomeAssistant, callback
13 from homeassistant.exceptions import HomeAssistantError
14 from homeassistant.helpers import device_registry as dr
15 from homeassistant.helpers.device_registry import DeviceEntry, DeviceEntryDisabler
16 
17 
18 @callback
19 def async_setup(hass: HomeAssistant) -> bool:
20  """Enable the Device Registry views."""
21 
22  websocket_api.async_register_command(hass, websocket_list_devices)
23  websocket_api.async_register_command(hass, websocket_update_device)
24  websocket_api.async_register_command(
25  hass, websocket_remove_config_entry_from_device
26  )
27  return True
28 
29 
30 @callback
31 @websocket_api.websocket_command( { vol.Required("type"): "config/device_registry/list",
32  }
33 )
35  hass: HomeAssistant,
37  msg: dict[str, Any],
38 ) -> None:
39  """Handle list devices command."""
40  registry = dr.async_get(hass)
41  # Build start of response message
42  msg_json_prefix = (
43  f'{{"id":{msg["id"]},"type": "{websocket_api.TYPE_RESULT}",'
44  f'"success":true,"result": ['
45  ).encode()
46  # Concatenate cached entity registry item JSON serializations
47  inner = b",".join(
48  [
49  entry.json_repr
50  for entry in registry.devices.values()
51  if entry.json_repr is not None
52  ]
53  )
54  msg_json = b"".join((msg_json_prefix, inner, b"]}"))
55  connection.send_message(msg_json)
56 
57 
58 @require_admin
59 @websocket_api.websocket_command( { vol.Required("type"): "config/device_registry/update",
60  vol.Optional("area_id"): vol.Any(str, None),
61  vol.Required("device_id"): str,
62  # We only allow setting disabled_by user via API.
63  # No Enum support like this in voluptuous, use .value
64  vol.Optional("disabled_by"): vol.Any(DeviceEntryDisabler.USER.value, None),
65  vol.Optional("labels"): [str],
66  vol.Optional("name_by_user"): vol.Any(str, None),
67  }
68 )
69 @callback
71  hass: HomeAssistant,
73  msg: dict[str, Any],
74 ) -> None:
75  """Handle update device websocket command."""
76  registry = dr.async_get(hass)
77 
78  msg.pop("type")
79  msg_id = msg.pop("id")
80 
81  if msg.get("disabled_by") is not None:
82  msg["disabled_by"] = DeviceEntryDisabler(msg["disabled_by"])
83 
84  if "labels" in msg:
85  # Convert labels to a set
86  msg["labels"] = set(msg["labels"])
87 
88  entry = cast(DeviceEntry, registry.async_update_device(**msg))
89 
90  connection.send_message(websocket_api.result_message(msg_id, entry.dict_repr))
91 
92 
93 @websocket_api.require_admin
94 @websocket_api.websocket_command( { "type": "config/device_registry/remove_config_entry", "config_entry_id": str, "device_id": str, } )
95 @websocket_api.async_response
97  hass: HomeAssistant,
99  msg: dict[str, Any],
100 ) -> None:
101  """Remove config entry from a device."""
102  registry = dr.async_get(hass)
103  config_entry_id = msg["config_entry_id"]
104  device_id = msg["device_id"]
105 
106  if (config_entry := hass.config_entries.async_get_entry(config_entry_id)) is None:
107  raise HomeAssistantError("Unknown config entry")
108 
109  if not config_entry.supports_remove_device:
110  raise HomeAssistantError("Config entry does not support device removal")
111 
112  if (device_entry := registry.async_get(device_id)) is None:
113  raise HomeAssistantError("Unknown device")
114 
115  if config_entry_id not in device_entry.config_entries:
116  raise HomeAssistantError("Config entry not in device")
117 
118  try:
119  integration = await loader.async_get_integration(hass, config_entry.domain)
120  component = await integration.async_get_component()
121  except (ImportError, loader.IntegrationNotFound) as exc:
122  raise HomeAssistantError("Integration not found") from exc
123 
124  if not await component.async_remove_config_entry_device(
125  hass, config_entry, device_entry
126  ):
127  raise HomeAssistantError(
128  "Failed to remove device entry, rejected by integration"
129  )
130 
131  # Integration might have removed the config entry already, that is fine.
132  if registry.async_get(device_id):
133  entry = registry.async_update_device(
134  device_id, remove_config_entry_id=config_entry_id
135  )
136 
137  entry_as_dict = entry.dict_repr if entry else None
138  else:
139  entry_as_dict = None
140 
141  connection.send_message(websocket_api.result_message(msg["id"], entry_as_dict))