diff --git a/homeassistant/components/fronius/diagnostics.py b/homeassistant/components/fronius/diagnostics.py new file mode 100644 index 00000000000..17737ba31f8 --- /dev/null +++ b/homeassistant/components/fronius/diagnostics.py @@ -0,0 +1,46 @@ +"""Diagnostics support for Fronius.""" + +from typing import Any + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.core import HomeAssistant + +from . import FroniusConfigEntry + +TO_REDACT = {"unique_id", "unique_identifier", "serial"} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: FroniusConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + diag: dict[str, Any] = {} + solar_net = config_entry.runtime_data + fronius = solar_net.fronius + + diag["config_entry"] = config_entry.as_dict() + diag["inverter_info"] = await fronius.inverter_info() + + diag["coordinators"] = {"inverters": {}} + for inv in solar_net.inverter_coordinators: + diag["coordinators"]["inverters"] |= inv.data + + diag["coordinators"]["logger"] = ( + solar_net.logger_coordinator.data if solar_net.logger_coordinator else None + ) + diag["coordinators"]["meter"] = ( + solar_net.meter_coordinator.data if solar_net.meter_coordinator else None + ) + diag["coordinators"]["ohmpilot"] = ( + solar_net.ohmpilot_coordinator.data if solar_net.ohmpilot_coordinator else None + ) + diag["coordinators"]["power_flow"] = ( + solar_net.power_flow_coordinator.data + if solar_net.power_flow_coordinator + else None + ) + diag["coordinators"]["storage"] = ( + solar_net.storage_coordinator.data if solar_net.storage_coordinator else None + ) + + return async_redact_data(diag, TO_REDACT) diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index 54ae65e6727..e92ec00b117 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -117,7 +117,6 @@ NO_IOT_CLASS = [ # https://github.com/home-assistant/developers.home-assistant/pull/1512 NO_DIAGNOSTICS = [ "dlna_dms", - "fronius", "gdacs", "geonetnz_quakes", "google_assistant_sdk", diff --git a/tests/components/fronius/__init__.py b/tests/components/fronius/__init__.py index f1630d6cd7e..6cefae734a0 100644 --- a/tests/components/fronius/__init__.py +++ b/tests/components/fronius/__init__.py @@ -25,6 +25,7 @@ async def setup_fronius_integration( """Create the Fronius integration.""" entry = MockConfigEntry( domain=DOMAIN, + entry_id="f1e2b9837e8adaed6fa682acaa216fd8", unique_id=unique_id, # has to match mocked logger unique_id data={ CONF_HOST: MOCK_HOST, diff --git a/tests/components/fronius/snapshots/test_diagnostics.ambr b/tests/components/fronius/snapshots/test_diagnostics.ambr new file mode 100644 index 00000000000..f23d63a58e3 --- /dev/null +++ b/tests/components/fronius/snapshots/test_diagnostics.ambr @@ -0,0 +1,370 @@ +# serializer version: 1 +# name: test_diagnostics + dict({ + 'config_entry': dict({ + 'data': dict({ + 'host': 'http://fronius', + 'is_logger': True, + }), + 'disabled_by': None, + 'domain': 'fronius', + 'entry_id': 'f1e2b9837e8adaed6fa682acaa216fd8', + 'minor_version': 1, + 'options': dict({ + }), + 'pref_disable_new_entities': False, + 'pref_disable_polling': False, + 'source': 'user', + 'title': 'Mock Title', + 'unique_id': '**REDACTED**', + 'version': 1, + }), + 'coordinators': dict({ + 'inverters': dict({ + '1': dict({ + 'current_ac': dict({ + 'unit': 'A', + 'value': 5.19, + }), + 'current_dc': dict({ + 'unit': 'A', + 'value': 2.19, + }), + 'energy_day': dict({ + 'unit': 'Wh', + 'value': 1113, + }), + 'energy_total': dict({ + 'unit': 'Wh', + 'value': 44188000, + }), + 'energy_year': dict({ + 'unit': 'Wh', + 'value': 25508798, + }), + 'error_code': dict({ + 'value': 0, + }), + 'frequency_ac': dict({ + 'unit': 'Hz', + 'value': 49.94, + }), + 'led_color': dict({ + 'value': 2, + }), + 'led_state': dict({ + 'value': 0, + }), + 'power_ac': dict({ + 'unit': 'W', + 'value': 1190, + }), + 'status': dict({ + 'Code': 0, + 'Reason': '', + 'UserMessage': '', + }), + 'status_code': dict({ + 'value': 7, + }), + 'timestamp': dict({ + 'value': '2021-10-07T10:01:17+02:00', + }), + 'voltage_ac': dict({ + 'unit': 'V', + 'value': 227.9, + }), + 'voltage_dc': dict({ + 'unit': 'V', + 'value': 518, + }), + }), + }), + 'logger': dict({ + 'system': dict({ + 'cash_factor': dict({ + 'unit': 'EUR/kWh', + 'value': 0.07800000160932541, + }), + 'co2_factor': dict({ + 'unit': 'kg/kWh', + 'value': 0.5299999713897705, + }), + 'delivery_factor': dict({ + 'unit': 'EUR/kWh', + 'value': 0.15000000596046448, + }), + 'hardware_platform': dict({ + 'value': 'wilma', + }), + 'hardware_version': dict({ + 'value': '2.4E', + }), + 'product_type': dict({ + 'value': 'fronius-datamanager-card', + }), + 'software_version': dict({ + 'value': '3.18.7-1', + }), + 'status': dict({ + 'Code': 0, + 'Reason': '', + 'UserMessage': '', + }), + 'time_zone': dict({ + 'value': 'CEST', + }), + 'time_zone_location': dict({ + 'value': 'Vienna', + }), + 'timestamp': dict({ + 'value': '2021-10-06T23:56:32+02:00', + }), + 'unique_identifier': '**REDACTED**', + 'utc_offset': dict({ + 'value': 7200, + }), + }), + }), + 'meter': dict({ + '0': dict({ + 'current_ac_phase_1': dict({ + 'unit': 'A', + 'value': 7.755, + }), + 'current_ac_phase_2': dict({ + 'unit': 'A', + 'value': 6.68, + }), + 'current_ac_phase_3': dict({ + 'unit': 'A', + 'value': 10.102, + }), + 'enable': dict({ + 'value': 1, + }), + 'energy_reactive_ac_consumed': dict({ + 'unit': 'VArh', + 'value': 59960790, + }), + 'energy_reactive_ac_produced': dict({ + 'unit': 'VArh', + 'value': 723160, + }), + 'energy_real_ac_minus': dict({ + 'unit': 'Wh', + 'value': 35623065, + }), + 'energy_real_ac_plus': dict({ + 'unit': 'Wh', + 'value': 15303334, + }), + 'energy_real_consumed': dict({ + 'unit': 'Wh', + 'value': 15303334, + }), + 'energy_real_produced': dict({ + 'unit': 'Wh', + 'value': 35623065, + }), + 'frequency_phase_average': dict({ + 'unit': 'Hz', + 'value': 50, + }), + 'manufacturer': dict({ + 'value': 'Fronius', + }), + 'meter_location': dict({ + 'value': 0, + }), + 'model': dict({ + 'value': 'Smart Meter 63A', + }), + 'power_apparent': dict({ + 'unit': 'VA', + 'value': 5592.57, + }), + 'power_apparent_phase_1': dict({ + 'unit': 'VA', + 'value': 1772.793, + }), + 'power_apparent_phase_2': dict({ + 'unit': 'VA', + 'value': 1527.048, + }), + 'power_apparent_phase_3': dict({ + 'unit': 'VA', + 'value': 2333.562, + }), + 'power_factor': dict({ + 'value': 1, + }), + 'power_factor_phase_1': dict({ + 'value': -0.99, + }), + 'power_factor_phase_2': dict({ + 'value': -0.99, + }), + 'power_factor_phase_3': dict({ + 'value': 0.99, + }), + 'power_reactive': dict({ + 'unit': 'VAr', + 'value': 2.87, + }), + 'power_reactive_phase_1': dict({ + 'unit': 'VAr', + 'value': 51.48, + }), + 'power_reactive_phase_2': dict({ + 'unit': 'VAr', + 'value': 115.63, + }), + 'power_reactive_phase_3': dict({ + 'unit': 'VAr', + 'value': -164.24, + }), + 'power_real': dict({ + 'unit': 'W', + 'value': 5592.57, + }), + 'power_real_phase_1': dict({ + 'unit': 'W', + 'value': 1765.55, + }), + 'power_real_phase_2': dict({ + 'unit': 'W', + 'value': 1515.8, + }), + 'power_real_phase_3': dict({ + 'unit': 'W', + 'value': 2311.22, + }), + 'serial': '**REDACTED**', + 'visible': dict({ + 'value': 1, + }), + 'voltage_ac_phase_1': dict({ + 'unit': 'V', + 'value': 228.6, + }), + 'voltage_ac_phase_2': dict({ + 'unit': 'V', + 'value': 228.6, + }), + 'voltage_ac_phase_3': dict({ + 'unit': 'V', + 'value': 231, + }), + 'voltage_ac_phase_to_phase_12': dict({ + 'unit': 'V', + 'value': 395.9, + }), + 'voltage_ac_phase_to_phase_23': dict({ + 'unit': 'V', + 'value': 398, + }), + 'voltage_ac_phase_to_phase_31': dict({ + 'unit': 'V', + 'value': 398, + }), + }), + }), + 'ohmpilot': None, + 'power_flow': dict({ + 'power_flow': dict({ + 'energy_day': dict({ + 'unit': 'Wh', + 'value': 1101.7000732421875, + }), + 'energy_total': dict({ + 'unit': 'Wh', + 'value': 44188000, + }), + 'energy_year': dict({ + 'unit': 'Wh', + 'value': 25508788, + }), + 'meter_location': dict({ + 'value': 'grid', + }), + 'meter_mode': dict({ + 'value': 'meter', + }), + 'power_battery': dict({ + 'unit': 'W', + 'value': None, + }), + 'power_grid': dict({ + 'unit': 'W', + 'value': 1703.74, + }), + 'power_load': dict({ + 'unit': 'W', + 'value': -2814.74, + }), + 'power_photovoltaics': dict({ + 'unit': 'W', + 'value': 1111, + }), + 'relative_autonomy': dict({ + 'unit': '%', + 'value': 39.4707859340472, + }), + 'relative_self_consumption': dict({ + 'unit': '%', + 'value': 100, + }), + 'status': dict({ + 'Code': 0, + 'Reason': '', + 'UserMessage': '', + }), + 'timestamp': dict({ + 'value': '2021-10-07T10:00:43+02:00', + }), + }), + }), + 'storage': None, + }), + 'inverter_info': dict({ + 'inverters': list([ + dict({ + 'custom_name': dict({ + 'value': 'Symo 20', + }), + 'device_id': dict({ + 'value': '1', + }), + 'device_type': dict({ + 'manufacturer': 'Fronius', + 'model': 'Symo 20.0-3-M', + 'value': 121, + }), + 'error_code': dict({ + 'value': 0, + }), + 'pv_power': dict({ + 'unit': 'W', + 'value': 23100, + }), + 'show': dict({ + 'value': 1, + }), + 'status_code': dict({ + 'value': 7, + }), + 'unique_id': '**REDACTED**', + }), + ]), + 'status': dict({ + 'Code': 0, + 'Reason': '', + 'UserMessage': '', + }), + 'timestamp': dict({ + 'value': '2021-10-07T13:41:00+02:00', + }), + }), + }) +# --- diff --git a/tests/components/fronius/test_diagnostics.py b/tests/components/fronius/test_diagnostics.py new file mode 100644 index 00000000000..7d8a49dcb7d --- /dev/null +++ b/tests/components/fronius/test_diagnostics.py @@ -0,0 +1,31 @@ +"""Tests for the diagnostics data provided by the KNX integration.""" + +from syrupy import SnapshotAssertion + +from homeassistant.core import HomeAssistant + +from . import mock_responses, setup_fronius_integration + +from tests.components.diagnostics import get_diagnostics_for_config_entry +from tests.test_util.aiohttp import AiohttpClientMocker +from tests.typing import ClientSessionGenerator + + +async def test_diagnostics( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + aioclient_mock: AiohttpClientMocker, + snapshot: SnapshotAssertion, +) -> None: + """Test diagnostics.""" + mock_responses(aioclient_mock) + entry = await setup_fronius_integration(hass) + + assert ( + await get_diagnostics_for_config_entry( + hass, + hass_client, + entry, + ) + == snapshot + )