"""Диагностика fglair: параметры устройства для проверки и поддержки. Дамп redacted: lanip_key маскируется (HA-конвенция; дампы прикладывают в issues). Полный ключ для secrets.yaml ESPHome берётся явным действием: «Настроить заново» (reconfigure) на карточке интеграции или CLI ``python -m pyfglair discover`` на хосте HA. """ from __future__ import annotations from typing import Any, Optional from homeassistant.components.diagnostics import async_redact_data from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from pyfglair.templates import template_info from .const import ( CONF_DEVICE_PORT, CONF_DSN, CONF_EMAIL, CONF_HOST, CONF_LANIP_KEY, CONF_LANIP_KEY_ID, CONF_LISTEN_PORT, CONF_MODEL, CONF_NAME, CONF_PASSWORD, CONF_TEMP_STEP, CONF_TEMPLATE, ) from .coordinator import FglairRuntime REDACTED = "**REDACTED**" TO_REDACT = {CONF_LANIP_KEY, CONF_PASSWORD, CONF_EMAIL} def _masked(value: Any) -> Any: if not isinstance(value, str) or len(value) <= 4: return REDACTED return f"{value[:4]}…{REDACTED}" def _base_diagnostics(entry: ConfigEntry) -> dict[str, Any]: data = entry.data return { "config": { CONF_HOST: data.get(CONF_HOST), CONF_DEVICE_PORT: data.get(CONF_DEVICE_PORT), CONF_DSN: data.get(CONF_DSN), CONF_NAME: data.get(CONF_NAME), CONF_MODEL: data.get(CONF_MODEL), CONF_TEMPLATE: data.get(CONF_TEMPLATE), CONF_TEMP_STEP: data.get(CONF_TEMP_STEP), CONF_LISTEN_PORT: data.get(CONF_LISTEN_PORT), }, "esp_home": { CONF_DSN: data.get(CONF_DSN), CONF_LANIP_KEY: _masked(data.get(CONF_LANIP_KEY)), CONF_LANIP_KEY_ID: data.get(CONF_LANIP_KEY_ID), CONF_HOST: data.get(CONF_HOST), }, "entry_data": async_redact_data(dict(data), TO_REDACT), "overrides": data.get("overrides", {}), } async def async_get_config_entry_diagnostics( hass: HomeAssistant, entry: ConfigEntry ) -> dict[str, Any]: diag = _base_diagnostics(entry) runtime: Optional[FglairRuntime] = getattr(entry, "runtime_data", None) if runtime is None: diag["runtime"] = {"loaded": False} return diag client = runtime.client values: dict[str, Any] = {} for info in template_info(client.template): value = client.cached(info.prop) if value is None: continue values[info.name] = { "raw": value.int_value, "display": runtime.conversions.to_display( info.prop, value.int_value ), } diag["runtime"] = { "loaded": True, "state": client.state.name.lower(), "last_error": client.last_error.name.lower(), "events_dropped": client.events_dropped, "values": values, } return diag