mirror of
https://github.com/home-assistant/core.git
synced 2026-04-27 05:07:39 +00:00
Add diagnostics to Actron Air (#167145)
This commit is contained in:
35
homeassistant/components/actron_air/diagnostics.py
Normal file
35
homeassistant/components/actron_air/diagnostics.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""Diagnostics support for Actron Air."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.const import CONF_API_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .coordinator import ActronAirConfigEntry
|
||||
|
||||
TO_REDACT = {CONF_API_TOKEN, "master_serial", "serial_number", "serial"}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
entry: ActronAirConfigEntry,
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinators: dict[int, Any] = {}
|
||||
for idx, coordinator in enumerate(entry.runtime_data.system_coordinators.values()):
|
||||
coordinators[idx] = {
|
||||
"system": async_redact_data(
|
||||
coordinator.system.model_dump(mode="json"), TO_REDACT
|
||||
),
|
||||
"status": async_redact_data(
|
||||
coordinator.data.model_dump(mode="json", exclude={"last_known_state"}),
|
||||
TO_REDACT,
|
||||
),
|
||||
}
|
||||
return {
|
||||
"entry_data": async_redact_data(entry.data, TO_REDACT),
|
||||
"coordinators": coordinators,
|
||||
}
|
||||
@@ -41,7 +41,7 @@ rules:
|
||||
|
||||
# Gold
|
||||
devices: done
|
||||
diagnostics: todo
|
||||
diagnostics: done
|
||||
discovery-update-info:
|
||||
status: exempt
|
||||
comment: This integration uses DHCP discovery, however is cloud polling. Therefore there is no information to update.
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Generator
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from actron_neo_api.models.auth import ActronAirDeviceCode
|
||||
from actron_neo_api.models.system import ActronAirSystemInfo
|
||||
from actron_neo_api.models.settings import ActronAirUserAirconSettings
|
||||
from actron_neo_api.models.status import ActronAirStatus
|
||||
from actron_neo_api.models.system import ActronAirACSystem, ActronAirSystemInfo
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.actron_air.const import DOMAIN
|
||||
@@ -14,7 +17,7 @@ from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -29,6 +32,27 @@ def mock_actron_api() -> Generator[AsyncMock]:
|
||||
"homeassistant.components.actron_air.config_flow.ActronAirAPI",
|
||||
new=mock_api,
|
||||
),
|
||||
patch.object(ActronAirACSystem, "set_system_mode", new_callable=AsyncMock),
|
||||
patch.object(
|
||||
ActronAirUserAirconSettings, "set_away_mode", new_callable=AsyncMock
|
||||
),
|
||||
patch.object(
|
||||
ActronAirUserAirconSettings,
|
||||
"set_continuous_mode",
|
||||
new_callable=AsyncMock,
|
||||
),
|
||||
patch.object(
|
||||
ActronAirUserAirconSettings, "set_quiet_mode", new_callable=AsyncMock
|
||||
),
|
||||
patch.object(
|
||||
ActronAirUserAirconSettings, "set_turbo_mode", new_callable=AsyncMock
|
||||
),
|
||||
patch.object(
|
||||
ActronAirUserAirconSettings, "set_temperature", new_callable=AsyncMock
|
||||
),
|
||||
patch.object(
|
||||
ActronAirUserAirconSettings, "set_fan_mode", new_callable=AsyncMock
|
||||
),
|
||||
):
|
||||
api = mock_api.return_value
|
||||
|
||||
@@ -65,47 +89,15 @@ def mock_actron_api() -> Generator[AsyncMock]:
|
||||
return_value=[ActronAirSystemInfo(serial="123456")]
|
||||
)
|
||||
|
||||
# Mock state manager
|
||||
# Build status from fixture JSON
|
||||
status = ActronAirStatus.model_validate(
|
||||
json.loads(load_fixture("status.json", DOMAIN))
|
||||
)
|
||||
status.set_api(api)
|
||||
|
||||
# Mock state manager to return our real pydantic status
|
||||
api.state_manager = MagicMock()
|
||||
status = api.state_manager.get_status.return_value
|
||||
status.master_info.live_temp_c = 22.0
|
||||
status.master_info.live_humidity_pc = 50.0
|
||||
status.ac_system.system_name = "Test System"
|
||||
status.ac_system.serial_number = "123456"
|
||||
status.ac_system.master_wc_model = "Test Model"
|
||||
status.ac_system.master_wc_firmware_version = "1.0.0"
|
||||
status.ac_system.set_system_mode = AsyncMock()
|
||||
status.remote_zone_info = []
|
||||
status.zones = {}
|
||||
status.min_temp = 16
|
||||
status.max_temp = 30
|
||||
status.aircon_system.mode = "OFF"
|
||||
status.fan_mode = "LOW"
|
||||
status.set_point = 24
|
||||
status.room_temp = 25
|
||||
status.is_on = False
|
||||
|
||||
# Mock user_aircon_settings for the switch and climate platforms
|
||||
settings = status.user_aircon_settings
|
||||
settings.away_mode = False
|
||||
settings.continuous_fan_enabled = False
|
||||
settings.quiet_mode_enabled = False
|
||||
settings.turbo_enabled = False
|
||||
settings.turbo_supported = True
|
||||
settings.is_on = False
|
||||
settings.mode = "COOL"
|
||||
settings.base_fan_mode = "LOW"
|
||||
settings.temperature_setpoint_cool_c = 24.0
|
||||
|
||||
settings.set_away_mode = AsyncMock()
|
||||
settings.set_continuous_mode = AsyncMock()
|
||||
settings.set_quiet_mode = AsyncMock()
|
||||
settings.set_turbo_mode = AsyncMock()
|
||||
settings.set_temperature = AsyncMock()
|
||||
settings.set_fan_mode = AsyncMock()
|
||||
|
||||
# Mock ac_system methods for climate platform
|
||||
status.ac_system.set_system_mode = AsyncMock()
|
||||
api.state_manager.get_status.return_value = status
|
||||
|
||||
yield api
|
||||
|
||||
@@ -126,7 +118,7 @@ def mock_zone() -> MagicMock:
|
||||
"""Return a mocked zone."""
|
||||
zone = MagicMock()
|
||||
zone.exists = True
|
||||
zone.zone_id = 1
|
||||
zone.zone_id = 0
|
||||
zone.zone_name = "Test Zone"
|
||||
zone.title = "Living Room"
|
||||
zone.live_temp_c = 22.0
|
||||
@@ -160,7 +152,6 @@ async def init_integration_with_zone(
|
||||
"""Set up the Actron Air integration with zone for testing."""
|
||||
status = mock_actron_api.state_manager.get_status.return_value
|
||||
status.remote_zone_info = [mock_zone]
|
||||
status.zones = {1: mock_zone}
|
||||
|
||||
with patch("homeassistant.components.actron_air.PLATFORMS", [Platform.CLIMATE]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
41
tests/components/actron_air/fixtures/status.json
Normal file
41
tests/components/actron_air/fixtures/status.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"isOnline": true,
|
||||
"lastKnownState": {
|
||||
"AirconSystem": {
|
||||
"MasterWCModel": "Test Model",
|
||||
"MasterSerial": "123456",
|
||||
"MasterWCFirmwareVersion": "1.0.0",
|
||||
"SystemName": "Test System"
|
||||
},
|
||||
"NV_SystemSettings": {
|
||||
"SystemName": "Test System"
|
||||
},
|
||||
"UserAirconSettings": {
|
||||
"isOn": false,
|
||||
"Mode": "COOL",
|
||||
"FanMode": "LOW",
|
||||
"AwayMode": false,
|
||||
"TemperatureSetpoint_Cool_oC": 24.0,
|
||||
"TemperatureSetpoint_Heat_oC": 20.0,
|
||||
"EnabledZones": [],
|
||||
"QuietModeEnabled": false,
|
||||
"TurboMode": {
|
||||
"Enabled": false,
|
||||
"Supported": true
|
||||
}
|
||||
},
|
||||
"MasterInfo": {
|
||||
"LiveTemp_oC": 22.0,
|
||||
"LiveHumidity_pc": 50.0,
|
||||
"LiveOutdoorTemp_oC": 0.0
|
||||
},
|
||||
"NV_Limits": {
|
||||
"UserSetpoint_oC": {
|
||||
"setCool_Min": 16.0,
|
||||
"setCool_Max": 30.0
|
||||
}
|
||||
},
|
||||
"RemoteZoneInfo": []
|
||||
},
|
||||
"serial_number": "123456"
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
'translation_key': None,
|
||||
'unique_id': '123456_zone_1',
|
||||
'unique_id': '123456_zone_0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
@@ -92,8 +92,8 @@
|
||||
<HVACMode.AUTO: 'auto'>,
|
||||
<HVACMode.OFF: 'off'>,
|
||||
]),
|
||||
'max_temp': 30,
|
||||
'min_temp': 16,
|
||||
'max_temp': 30.0,
|
||||
'min_temp': 16.0,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
@@ -145,8 +145,8 @@
|
||||
<HVACMode.AUTO: 'auto'>,
|
||||
<HVACMode.OFF: 'off'>,
|
||||
]),
|
||||
'max_temp': 30,
|
||||
'min_temp': 16,
|
||||
'max_temp': 30.0,
|
||||
'min_temp': 16.0,
|
||||
'supported_features': <ClimateEntityFeature: 393>,
|
||||
'temperature': 24.0,
|
||||
}),
|
||||
|
||||
55
tests/components/actron_air/snapshots/test_diagnostics.ambr
Normal file
55
tests/components/actron_air/snapshots/test_diagnostics.ambr
Normal file
@@ -0,0 +1,55 @@
|
||||
# serializer version: 1
|
||||
# name: test_diagnostics
|
||||
dict({
|
||||
'coordinators': dict({
|
||||
'0': dict({
|
||||
'status': dict({
|
||||
'ac_system': dict({
|
||||
'master_serial': '**REDACTED**',
|
||||
'master_wc_firmware_version': '1.0.0',
|
||||
'master_wc_model': 'Test Model',
|
||||
'outdoor_unit': None,
|
||||
'system_name': 'Test System',
|
||||
}),
|
||||
'alerts': None,
|
||||
'is_online': True,
|
||||
'live_aircon': None,
|
||||
'master_info': dict({
|
||||
'live_humidity_pc': 50.0,
|
||||
'live_outdoor_temp_c': 0.0,
|
||||
'live_temp_c': 22.0,
|
||||
}),
|
||||
'peripherals': list([
|
||||
]),
|
||||
'remote_zone_info': list([
|
||||
]),
|
||||
'serial_number': '**REDACTED**',
|
||||
'user_aircon_settings': dict({
|
||||
'away_mode': False,
|
||||
'enabled_zones': list([
|
||||
]),
|
||||
'fan_mode': 'LOW',
|
||||
'is_on': False,
|
||||
'mode': 'COOL',
|
||||
'quiet_mode_enabled': False,
|
||||
'temperature_setpoint_cool_c': 24.0,
|
||||
'temperature_setpoint_heat_c': 20.0,
|
||||
'turbo_mode_enabled': dict({
|
||||
'Enabled': False,
|
||||
'Supported': True,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'system': dict({
|
||||
'links': dict({
|
||||
}),
|
||||
'serial': '**REDACTED**',
|
||||
'type': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'entry_data': dict({
|
||||
'api_token': '**REDACTED**',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
@@ -36,7 +36,6 @@ async def test_climate_entities(
|
||||
"""Test climate entities."""
|
||||
status = mock_actron_api.state_manager.get_status.return_value
|
||||
status.remote_zone_info = [mock_zone]
|
||||
status.zones = {1: mock_zone}
|
||||
|
||||
with patch("homeassistant.components.actron_air.PLATFORMS", [Platform.CLIMATE]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
@@ -289,7 +288,6 @@ async def test_zone_hvac_mode_unmapped(
|
||||
|
||||
status = mock_actron_api.state_manager.get_status.return_value
|
||||
status.remote_zone_info = [mock_zone]
|
||||
status.zones = {1: mock_zone}
|
||||
|
||||
with patch("homeassistant.components.actron_air.PLATFORMS", [Platform.CLIMATE]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
@@ -309,7 +307,6 @@ async def test_zone_hvac_mode_inactive(
|
||||
|
||||
status = mock_actron_api.state_manager.get_status.return_value
|
||||
status.remote_zone_info = [mock_zone]
|
||||
status.zones = {1: mock_zone}
|
||||
|
||||
with patch("homeassistant.components.actron_air.PLATFORMS", [Platform.CLIMATE]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
28
tests/components/actron_air/test_diagnostics.py
Normal file
28
tests/components/actron_air/test_diagnostics.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""Tests for Actron Air diagnostics."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
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_actron_api: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test diagnostics."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
assert (
|
||||
await get_diagnostics_for_config_entry(hass, hass_client, mock_config_entry)
|
||||
== snapshot
|
||||
)
|
||||
@@ -83,7 +83,10 @@ async def test_turbo_mode_not_supported(
|
||||
) -> None:
|
||||
"""Test turbo mode switch is not created when not supported."""
|
||||
status = mock_actron_api.state_manager.get_status.return_value
|
||||
status.user_aircon_settings.turbo_supported = False
|
||||
status.user_aircon_settings.turbo_mode_enabled = {
|
||||
"Enabled": False,
|
||||
"Supported": False,
|
||||
}
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user