Add diagnostics support to Sonos (#64576)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
jjlawren 2022-01-20 15:26:54 -06:00 committed by GitHub
parent c9e495607e
commit 53aed22d5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 0 deletions

View File

@ -1044,6 +1044,7 @@ omit =
homeassistant/components/somfy_mylink/cover.py
homeassistant/components/sonos/__init__.py
homeassistant/components/sonos/alarms.py
homeassistant/components/sonos/diagnostics.py
homeassistant/components/sonos/entity.py
homeassistant/components/sonos/favorites.py
homeassistant/components/sonos/helpers.py

View File

@ -0,0 +1,105 @@
"""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 .const import DATA_SONOS
from .speaker import SonosSpeaker
MEDIA_DIAGNOSTIC_ATTRIBUTES = (
"album_name",
"artist",
"channel",
"duration",
"image_url",
"queue_position",
"playlist_name",
"source_name",
"title",
"uri",
"_last_event_variables",
)
SPEAKER_DIAGNOSTIC_ATTRIBUTES = (
"available",
"battery_info",
"household_id",
"is_coordinator",
"model_name",
"sonos_group_entities",
"subscription_address",
"subscriptions_failed",
"zone_name",
"_group_members_missing",
"_last_activity",
)
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
payload = {"current_timestamp": time.monotonic()}
for section in ("discovered", "discovery_known", "discovery_ignored"):
payload[section] = {}
data = getattr(hass.data[DATA_SONOS], section)
if isinstance(data, set):
payload[section] = data
continue
for key, value in data.items():
if isinstance(value, SonosSpeaker):
speaker_info = await async_generate_speaker_info(hass, value)
payload[section][key] = speaker_info
else:
payload[section][key] = value
return payload
async def async_generate_media_info(
hass: HomeAssistant, speaker: SonosSpeaker
) -> dict[str, Any]:
"""Generate a diagnostic payload for current media metadata."""
payload = {}
def get_contents(item):
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 MEDIA_DIAGNOSTIC_ATTRIBUTES:
value = getattr(speaker.media, attrib)
payload[attrib] = get_contents(value)
def poll_current_track_info():
return speaker.soco.avTransport.GetPositionInfo(
[("InstanceID", 0), ("Channel", "Master")]
)
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 = {}
for attrib in SPEAKER_DIAGNOSTIC_ATTRIBUTES:
payload[attrib] = getattr(speaker, attrib)
payload["media"] = await async_generate_media_info(hass, speaker)
return payload

View File

@ -123,6 +123,7 @@ class SonosMedia:
self.position: float | None = None
self.position_updated_at: datetime.datetime | None = None
self._last_event_variables: dict[str, Any] | None = None
def clear(self) -> None:
"""Clear basic media info."""
@ -1002,6 +1003,12 @@ class SonosSpeaker:
if new_status == SONOS_STATE_TRANSITIONING:
return
if variables:
# Store for diagnostics
self.media._last_event_variables = ( # pylint: disable=protected-access
variables
)
self.media.clear()
update_position = new_status != self.media.playback_status
self.media.playback_status = new_status