6ae0ed1787
Release / release (push) Successful in 2s
Production-readiness pass: security hardening, performance improvements, new services (send_message, set_repeat, refresh_library), diagnostics, reauth flow, image proxy, per-instance device IDs, exponential WS reconnect backoff, ID validation, stale device cleanup, and supporting integration plumbing. Three rounds of independent code review applied. See RELEASE_NOTES.md for the full changelog.
66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
"""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,
|
|
}
|