-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdiagnostics.py
144 lines (122 loc) · 4.2 KB
/
diagnostics.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""Provides diagnostics for Sonos."""
from __future__ import annotations
import time
from typing import Any
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntry
from .const import DATA_SONOS, DOMAIN
from .speaker import SonosSpeaker
MEDIA_DIAGNOSTIC_ATTRIBUTES = (
"album_name",
"artist",
"channel",
"duration",
"image_url",
"queue_position",
"playlist_name",
"source_name",
"title",
"uri",
)
SPEAKER_DIAGNOSTIC_ATTRIBUTES = (
"available",
"battery_info",
"hardware_version",
"household_id",
"is_coordinator",
"model_name",
"model_number",
"software_version",
"sonos_group_entities",
"subscription_address",
"subscriptions_failed",
"version",
"zone_name",
"_group_members_missing",
"_last_activity",
"_last_event_cache",
)
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
payload: dict[str, Any] = {"current_timestamp": time.monotonic()}
for section in ("discovered", "discovery_known"):
payload[section] = {}
data: set[Any] | dict[str, Any] = getattr(hass.data[DATA_SONOS], section)
if isinstance(data, set):
payload[section] = data
continue
for key, value in data.items():
if isinstance(value, SonosSpeaker):
payload[section][key] = await async_generate_speaker_info(hass, value)
else:
payload[section][key] = value
return payload
async def async_get_device_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry, device: DeviceEntry
) -> dict[str, Any]:
"""Return diagnostics for a device."""
uid = next(
(identifier[1] for identifier in device.identifiers if identifier[0] == DOMAIN),
None,
)
if uid is None:
return {}
if (speaker := hass.data[DATA_SONOS].discovered.get(uid)) is None:
return {}
return await async_generate_speaker_info(hass, speaker)
async def async_generate_media_info(
hass: HomeAssistant, speaker: SonosSpeaker
) -> dict[str, Any]:
"""Generate a diagnostic payload for current media metadata."""
payload: dict[str, Any] = {}
for attrib in MEDIA_DIAGNOSTIC_ATTRIBUTES:
payload[attrib] = getattr(speaker.media, attrib)
def poll_current_track_info() -> dict[str, Any] | str:
try:
return speaker.soco.avTransport.GetPositionInfo(
[("InstanceID", 0), ("Channel", "Master")],
timeout=3,
)
except OSError as ex:
return f"Error retrieving: {ex}"
payload["current_track_poll"] = await hass.async_add_executor_job(
poll_current_track_info
)
return payload
async def async_generate_speaker_info(
hass: HomeAssistant, speaker: SonosSpeaker
) -> dict[str, Any]:
"""Generate the diagnostic payload for a specific speaker."""
payload: dict[str, Any] = {}
def get_contents(
item: int | float | str | dict[str, Any]
) -> int | float | str | dict[str, Any]:
if isinstance(item, (int, float, str)):
return item
if isinstance(item, dict):
payload = {}
for key, value in item.items():
payload[key] = get_contents(value)
return payload
if hasattr(item, "__dict__"):
return vars(item)
return item
for attrib in SPEAKER_DIAGNOSTIC_ATTRIBUTES:
value = getattr(speaker, attrib)
payload[attrib] = get_contents(value)
payload["enabled_entities"] = {
entity_id
for entity_id, s in hass.data[DATA_SONOS].entity_id_mappings.items()
if s is speaker
}
payload["media"] = await async_generate_media_info(hass, speaker)
payload["activity_stats"] = speaker.activity_stats.report()
payload["event_stats"] = speaker.event_stats.report()
payload["zone_group_state_stats"] = {
"processed": speaker.soco.zone_group_state.processed_count,
"total_requests": speaker.soco.zone_group_state.total_requests,
}
return payload