"""Diagnostics support for Emby Media Player.""" from __future__ import annotations import hashlib from dataclasses import asdict from typing import Any from homeassistant.components.diagnostics import async_redact_data from homeassistant.core import HomeAssistant from . import EmbyConfigEntry from .const import CONF_API_KEY TO_REDACT_ENTRY = {CONF_API_KEY} # Session-level fields that could allow Emby command injection if leaked # alongside the API key. TO_REDACT_SESSION = {"session_id", "device_id", "user_id"} def _stable_token(value: str) -> str: """Stable, irreversible token for a session identifier. The first 10 chars of an MD5 are enough to correlate entries in a single diagnostics dump without exposing the real Emby ID. """ return "sid-" + hashlib.md5(value.encode("utf-8")).hexdigest()[:10] # noqa: S324 async def async_get_config_entry_diagnostics( hass: HomeAssistant, entry: EmbyConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" runtime = entry.runtime_data coordinator = runtime.coordinator sessions_dump: dict[str, Any] = {} if coordinator.data: for sid, session in coordinator.data.items(): session_dict = asdict(session) # Convert datetimes to isoformat for JSON friendliness. for key, value in list(session_dict.items()): if hasattr(value, "isoformat"): session_dict[key] = value.isoformat() # Nested play_state.updated_at may also be a datetime. if isinstance(value, dict): for sub_k, sub_v in list(value.items()): if hasattr(sub_v, "isoformat"): value[sub_k] = sub_v.isoformat() sessions_dump[_stable_token(sid)] = async_redact_data( session_dict, TO_REDACT_SESSION ) return { "entry": { "title": entry.title, "data": async_redact_data(dict(entry.data), TO_REDACT_ENTRY), "options": dict(entry.options), "unique_id": entry.unique_id, }, "server_id": runtime.server_id, "websocket_connected": coordinator.websocket_connected, "last_update_success": coordinator.last_update_success, "sessions": sessions_dump, }