From 21299729042e2e166f0971caf49c5f81aaa685c5 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Sun, 30 Jan 2022 15:15:51 -0600 Subject: [PATCH] Add activity statistics to Sonos diagnostics (#65214) --- homeassistant/components/sonos/__init__.py | 1 + homeassistant/components/sonos/diagnostics.py | 1 + homeassistant/components/sonos/speaker.py | 4 +- homeassistant/components/sonos/statistics.py | 50 +++++++++++++++---- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index 27b9d720b0f..673e0ac4dfe 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -193,6 +193,7 @@ class SonosDiscoveryManager: async def _async_stop_event_listener(self, event: Event | None = None) -> None: for speaker in self.data.discovered.values(): + speaker.activity_stats.log_report() speaker.event_stats.log_report() await asyncio.gather( *(speaker.async_offline() for speaker in self.data.discovered.values()) diff --git a/homeassistant/components/sonos/diagnostics.py b/homeassistant/components/sonos/diagnostics.py index 007348a66bb..707449e4002 100644 --- a/homeassistant/components/sonos/diagnostics.py +++ b/homeassistant/components/sonos/diagnostics.py @@ -130,5 +130,6 @@ async def async_generate_speaker_info( 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() return payload diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 4de0638d10c..5f06fe976ac 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -62,7 +62,7 @@ from .const import ( ) from .favorites import SonosFavorites from .helpers import soco_error -from .statistics import EventStatistics +from .statistics import ActivityStatistics, EventStatistics NEVER_TIME = -1200.0 EVENT_CHARGING = { @@ -177,6 +177,7 @@ class SonosSpeaker: self._event_dispatchers: dict[str, Callable] = {} self._last_activity: float = NEVER_TIME self._last_event_cache: dict[str, Any] = {} + self.activity_stats: ActivityStatistics = ActivityStatistics(self.zone_name) self.event_stats: EventStatistics = EventStatistics(self.zone_name) # Scheduled callback handles @@ -528,6 +529,7 @@ class SonosSpeaker: """Track the last activity on this speaker, set availability and resubscribe.""" _LOGGER.debug("Activity on %s from %s", self.zone_name, source) self._last_activity = time.monotonic() + self.activity_stats.activity(source, self._last_activity) was_available = self.available self.available = True if not was_available: diff --git a/homeassistant/components/sonos/statistics.py b/homeassistant/components/sonos/statistics.py index 8cade9b0aa5..a850e5a8caf 100644 --- a/homeassistant/components/sonos/statistics.py +++ b/homeassistant/components/sonos/statistics.py @@ -9,13 +9,49 @@ from soco.events_base import Event as SonosEvent, parse_event_xml _LOGGER = logging.getLogger(__name__) -class EventStatistics: +class SonosStatistics: + """Base class of Sonos statistics.""" + + def __init__(self, zone_name: str, kind: str) -> None: + """Initialize SonosStatistics.""" + self._stats = {} + self._stat_type = kind + self.zone_name = zone_name + + def report(self) -> dict: + """Generate a report for use in diagnostics.""" + return self._stats.copy() + + def log_report(self) -> None: + """Log statistics for this speaker.""" + _LOGGER.debug( + "%s statistics for %s: %s", + self._stat_type, + self.zone_name, + self.report(), + ) + + +class ActivityStatistics(SonosStatistics): + """Representation of Sonos activity statistics.""" + + def __init__(self, zone_name: str) -> None: + """Initialize ActivityStatistics.""" + super().__init__(zone_name, "Activity") + + def activity(self, source: str, timestamp: float) -> None: + """Track an activity occurrence.""" + activity_entry = self._stats.setdefault(source, {"count": 0}) + activity_entry["count"] += 1 + activity_entry["last_seen"] = timestamp + + +class EventStatistics(SonosStatistics): """Representation of Sonos event statistics.""" def __init__(self, zone_name: str) -> None: """Initialize EventStatistics.""" - self._stats = {} - self.zone_name = zone_name + super().__init__(zone_name, "Event") def receive(self, event: SonosEvent) -> None: """Mark a received event by subscription type.""" @@ -38,11 +74,3 @@ class EventStatistics: payload["soco:from_didl_string"] = from_didl_string.cache_info() payload["soco:parse_event_xml"] = parse_event_xml.cache_info() return payload - - def log_report(self) -> None: - """Log event statistics for this speaker.""" - _LOGGER.debug( - "Event statistics for %s: %s", - self.zone_name, - self.report(), - )