From 4e42478ece244e00293065c0405ff9b6fe10333c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20Wickstr=C3=B6m?= Date: Mon, 23 Mar 2026 23:49:41 +0200 Subject: [PATCH] Add diagnostics to Huum integration (#166230) --- homeassistant/components/huum/diagnostics.py | 37 +++++++++++++ .../components/huum/quality_scale.yaml | 2 +- tests/components/huum/conftest.py | 38 ++++++++++++- .../huum/snapshots/test_diagnostics.ambr | 40 ++++++++++++++ tests/components/huum/test_diagnostics.py | 53 +++++++++++++++++++ 5 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/huum/diagnostics.py create mode 100644 tests/components/huum/snapshots/test_diagnostics.ambr create mode 100644 tests/components/huum/test_diagnostics.py diff --git a/homeassistant/components/huum/diagnostics.py b/homeassistant/components/huum/diagnostics.py new file mode 100644 index 00000000000..3abbb00790d --- /dev/null +++ b/homeassistant/components/huum/diagnostics.py @@ -0,0 +1,37 @@ +"""Diagnostics support for Huum.""" + +from __future__ import annotations + +from typing import Any + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.core import HomeAssistant + +from .coordinator import HuumConfigEntry + +TO_REDACT_DATA = {"sauna_name", "payment_end_date"} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: HuumConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + coordinator = entry.runtime_data + + result: dict[str, Any] = { + "entry": { + "version": entry.version, + }, + "coordinator": { + "last_update_success": coordinator.last_update_success, + "last_exception": ( + str(coordinator.last_exception) if coordinator.last_exception else None + ), + }, + } + + if coordinator.data is None: + return result + + result["data"] = async_redact_data(coordinator.data.to_dict(), TO_REDACT_DATA) + return result diff --git a/homeassistant/components/huum/quality_scale.yaml b/homeassistant/components/huum/quality_scale.yaml index d2d75fd86c5..8d54d94107f 100644 --- a/homeassistant/components/huum/quality_scale.yaml +++ b/homeassistant/components/huum/quality_scale.yaml @@ -46,7 +46,7 @@ rules: # Gold devices: done - diagnostics: todo + diagnostics: done discovery: todo discovery-update-info: todo docs-data-update: done diff --git a/tests/components/huum/conftest.py b/tests/components/huum/conftest.py index 99b0fbe008b..a62df87fba9 100644 --- a/tests/components/huum/conftest.py +++ b/tests/components/huum/conftest.py @@ -1,7 +1,7 @@ """Configuration for Huum tests.""" from collections.abc import Generator -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, Mock, patch from huum.const import SaunaStatus import pytest @@ -16,6 +16,7 @@ from tests.common import MockConfigEntry def mock_huum() -> Generator[AsyncMock]: """Mock data from the API.""" huum = AsyncMock() + with ( patch( "homeassistant.components.huum.config_flow.Huum.status", @@ -38,8 +39,9 @@ def mock_huum() -> Generator[AsyncMock]: huum.config = 3 huum.door_closed = True huum.temperature = 30 - huum.sauna_name = 123456 + huum.sauna_name = "Home sauna" huum.target_temperature = 80 + huum.payment_end_date = "2026-12-31" huum.light = 1 huum.humidity = 0 huum.target_humidity = 5 @@ -50,6 +52,38 @@ def mock_huum() -> Generator[AsyncMock]: huum.sauna_config.min_temp = 40 huum.sauna_config.max_timer = 0 huum.sauna_config.min_timer = 0 + + def _to_dict() -> dict[str, object]: + return { + "status": huum.status, + "config": huum.config, + "door_closed": huum.door_closed, + "temperature": huum.temperature, + "sauna_name": huum.sauna_name, + "target_temperature": huum.target_temperature, + "start_date": None, + "end_date": None, + "duration": None, + "steamer_error": None, + "payment_end_date": huum.payment_end_date, + "is_private": None, + "show_modal": None, + "light": huum.light, + "humidity": huum.humidity, + "target_humidity": huum.target_humidity, + "remote_safety_state": None, + "sauna_config": { + "child_lock": huum.sauna_config.child_lock, + "max_heating_time": huum.sauna_config.max_heating_time, + "min_heating_time": huum.sauna_config.min_heating_time, + "max_temp": huum.sauna_config.max_temp, + "min_temp": huum.sauna_config.min_temp, + "max_timer": huum.sauna_config.max_timer, + "min_timer": huum.sauna_config.min_timer, + }, + } + + huum.to_dict = Mock(side_effect=_to_dict) huum.turn_on = turn_on huum.toggle_light = toggle_light diff --git a/tests/components/huum/snapshots/test_diagnostics.ambr b/tests/components/huum/snapshots/test_diagnostics.ambr new file mode 100644 index 00000000000..7fddec18050 --- /dev/null +++ b/tests/components/huum/snapshots/test_diagnostics.ambr @@ -0,0 +1,40 @@ +# serializer version: 1 +# name: test_diagnostics + dict({ + 'coordinator': dict({ + 'last_exception': None, + 'last_update_success': True, + }), + 'data': dict({ + 'config': 3, + 'door_closed': True, + 'duration': None, + 'end_date': None, + 'humidity': 0, + 'is_private': None, + 'light': 1, + 'payment_end_date': '**REDACTED**', + 'remote_safety_state': None, + 'sauna_config': dict({ + 'child_lock': 'OFF', + 'max_heating_time': 3, + 'max_temp': 110, + 'max_timer': 0, + 'min_heating_time': 0, + 'min_temp': 40, + 'min_timer': 0, + }), + 'sauna_name': '**REDACTED**', + 'show_modal': None, + 'start_date': None, + 'status': 232, + 'steamer_error': None, + 'target_humidity': 5, + 'target_temperature': 80, + 'temperature': 30, + }), + 'entry': dict({ + 'version': 1, + }), + }) +# --- diff --git a/tests/components/huum/test_diagnostics.py b/tests/components/huum/test_diagnostics.py new file mode 100644 index 00000000000..92fb2bf6fb7 --- /dev/null +++ b/tests/components/huum/test_diagnostics.py @@ -0,0 +1,53 @@ +"""Tests for the Huum diagnostics.""" + +from __future__ import annotations + +from unittest.mock import AsyncMock + +from syrupy.assertion import SnapshotAssertion + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from . import setup_with_selected_platforms + +from tests.common import MockConfigEntry +from tests.components.diagnostics import get_diagnostics_for_config_entry +from tests.typing import ClientSessionGenerator + + +async def test_diagnostics( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + mock_config_entry: MockConfigEntry, + mock_huum: AsyncMock, + snapshot: SnapshotAssertion, +) -> None: + """Test diagnostics.""" + await setup_with_selected_platforms(hass, mock_config_entry, [Platform.CLIMATE]) + + diagnostics = await get_diagnostics_for_config_entry( + hass, hass_client, mock_config_entry + ) + + assert diagnostics == snapshot + + +async def test_diagnostics_without_coordinator_data( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + mock_config_entry: MockConfigEntry, + mock_huum: AsyncMock, +) -> None: + """Test diagnostics when coordinator data is unavailable.""" + await setup_with_selected_platforms(hass, mock_config_entry, [Platform.CLIMATE]) + + mock_config_entry.runtime_data.data = None + + diagnostics = await get_diagnostics_for_config_entry( + hass, hass_client, mock_config_entry + ) + + assert diagnostics["coordinator"]["last_update_success"] is True + assert diagnostics["coordinator"]["last_exception"] is None + assert "data" not in diagnostics