Home Assistant Unofficial Reference 2024.12.1
diagnostics.py
Go to the documentation of this file.
1 """Diagnostics support for MQTT."""
2 
3 from __future__ import annotations
4 
5 from typing import TYPE_CHECKING, Any
6 
7 from homeassistant.components import device_tracker
8 from homeassistant.components.diagnostics import async_redact_data
9 from homeassistant.config_entries import ConfigEntry
10 from homeassistant.const import (
11  ATTR_LATITUDE,
12  ATTR_LONGITUDE,
13  CONF_PASSWORD,
14  CONF_USERNAME,
15 )
16 from homeassistant.core import HomeAssistant, callback, split_entity_id
17 from homeassistant.helpers import device_registry as dr, entity_registry as er
18 from homeassistant.helpers.device_registry import DeviceEntry
19 
20 from . import debug_info, is_connected
21 from .models import DATA_MQTT
22 
23 REDACT_CONFIG = {CONF_PASSWORD, CONF_USERNAME}
24 REDACT_STATE_DEVICE_TRACKER = {ATTR_LATITUDE, ATTR_LONGITUDE}
25 
26 
28  hass: HomeAssistant, entry: ConfigEntry
29 ) -> dict[str, Any]:
30  """Return diagnostics for a config entry."""
31  return _async_get_diagnostics(hass, entry)
32 
33 
35  hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry
36 ) -> dict[str, Any]:
37  """Return diagnostics for a device entry."""
38  return _async_get_diagnostics(hass, entry, device)
39 
40 
41 @callback
43  hass: HomeAssistant,
44  entry: ConfigEntry,
45  device: DeviceEntry | None = None,
46 ) -> dict[str, Any]:
47  """Return diagnostics for a config entry."""
48  mqtt_instance = hass.data[DATA_MQTT].client
49  if TYPE_CHECKING:
50  assert mqtt_instance is not None
51 
52  redacted_config = async_redact_data(mqtt_instance.conf, REDACT_CONFIG)
53 
54  data = {
55  "connected": is_connected(hass),
56  "mqtt_config": redacted_config,
57  }
58 
59  if device:
60  data["device"] = _async_device_as_dict(hass, device)
61  data["mqtt_debug_info"] = debug_info.info_for_device(hass, device.id)
62  else:
63  device_registry = dr.async_get(hass)
64  data.update(
65  devices=[
66  _async_device_as_dict(hass, device)
67  for device in dr.async_entries_for_config_entry(
68  device_registry, entry.entry_id
69  )
70  ],
71  mqtt_debug_info=debug_info.info_for_config_entry(hass),
72  )
73 
74  return data
75 
76 
77 @callback
78 def _async_device_as_dict(hass: HomeAssistant, device: DeviceEntry) -> dict[str, Any]:
79  """Represent an MQTT device as a dictionary."""
80 
81  # Gather information how this MQTT device is represented in Home Assistant
82  entity_registry = er.async_get(hass)
83  data: dict[str, Any] = {
84  "id": device.id,
85  "name": device.name,
86  "name_by_user": device.name_by_user,
87  "disabled": device.disabled,
88  "disabled_by": device.disabled_by,
89  "entities": [],
90  }
91 
92  entities = er.async_entries_for_device(
93  entity_registry,
94  device_id=device.id,
95  include_disabled_entities=True,
96  )
97 
98  def _state_dict(entity_entry: er.RegistryEntry) -> dict[str, Any] | None:
99  state = hass.states.get(entity_entry.entity_id)
100  if not state:
101  return None
102 
103  state_dict = dict(state.as_dict())
104 
105  # The context doesn't provide useful information in this case.
106  state_dict.pop("context", None)
107 
108  entity_domain = split_entity_id(state.entity_id)[0]
109 
110  # Retract some sensitive state attributes
111  if entity_domain == device_tracker.DOMAIN:
112  state_dict["attributes"] = async_redact_data(
113  state_dict["attributes"], REDACT_STATE_DEVICE_TRACKER
114  )
115  return state_dict
116 
117  data["entities"].extend(
118  {
119  "device_class": entity_entry.device_class,
120  "disabled_by": entity_entry.disabled_by,
121  "disabled": entity_entry.disabled,
122  "entity_category": entity_entry.entity_category,
123  "entity_id": entity_entry.entity_id,
124  "icon": entity_entry.icon,
125  "original_device_class": entity_entry.original_device_class,
126  "original_icon": entity_entry.original_icon,
127  "state": state_dict,
128  "unit_of_measurement": entity_entry.unit_of_measurement,
129  }
130  for entity_entry in entities
131  if (state_dict := _state_dict(entity_entry)) is not None
132  )
133 
134  return data
dict async_redact_data(Mapping data, Iterable[Any] to_redact)
Definition: util.py:14
dict[str, Any] async_get_device_diagnostics(HomeAssistant hass, ConfigEntry entry, DeviceEntry device)
Definition: diagnostics.py:36
dict[str, Any] _async_get_diagnostics(HomeAssistant hass, ConfigEntry entry, DeviceEntry|None device=None)
Definition: diagnostics.py:46
dict[str, Any] async_get_config_entry_diagnostics(HomeAssistant hass, ConfigEntry entry)
Definition: diagnostics.py:29
dict[str, Any] _async_device_as_dict(HomeAssistant hass, DeviceEntry device)
Definition: diagnostics.py:78
bool is_connected(HomeAssistant hass)
Definition: __init__.py:553
tuple[str, str] split_entity_id(str entity_id)
Definition: core.py:214