mirror of
https://github.com/home-assistant/core.git
synced 2026-09-25 17:04:04 -04:00
Add diagnostics to SolarEdge Modbus (#180599)
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
"""Diagnostics support for the SolarEdge Modbus integration."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from modbus_connection.model import Component, RegisterField
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .coordinator import (
|
||||
SolarEdgeModbusConfigEntry,
|
||||
SolarEdgeModbusDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
TO_REDACT = {"serial_number"}
|
||||
|
||||
|
||||
def _component_data(component: Component) -> dict[str, Any]:
|
||||
"""Every decoded register field and computed property of a component.
|
||||
|
||||
Only the classes below the library's own base count: its plumbing, such as
|
||||
the Modbus unit a component reads through, is not device data.
|
||||
"""
|
||||
names = {
|
||||
name
|
||||
for klass in type(component).__mro__
|
||||
if issubclass(klass, Component) and klass is not Component
|
||||
for name, attribute in vars(klass).items()
|
||||
if not name.startswith("_") and isinstance(attribute, (RegisterField, property))
|
||||
}
|
||||
return {name: getattr(component, name) for name in sorted(names)}
|
||||
|
||||
|
||||
def _poll_data(coordinator: SolarEdgeModbusDataUpdateCoordinator) -> dict[str, Any]:
|
||||
"""What a coordinator's most recent poll got out of the device."""
|
||||
return {
|
||||
"updated": sorted(coordinator.data.updated),
|
||||
"failed": {
|
||||
subsystem: str(error)
|
||||
for subsystem, error in coordinator.data.failed.items()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: SolarEdgeModbusConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry.
|
||||
|
||||
What the inverter reports about itself and about what is attached to it,
|
||||
whether or not this integration has entities for it yet.
|
||||
"""
|
||||
runtime_data = entry.runtime_data
|
||||
solaredge = runtime_data.solaredge
|
||||
|
||||
data: dict[str, Any] = {
|
||||
"polls": {"readings": _poll_data(runtime_data.readings)},
|
||||
"common": _component_data(solaredge.common),
|
||||
"inverter": _component_data(solaredge.inverter),
|
||||
"mmppt": (
|
||||
[_component_data(module) for module in solaredge.mmppt.modules]
|
||||
if solaredge.mmppt is not None
|
||||
else None
|
||||
),
|
||||
"meters": [_component_data(meter) for meter in solaredge.meters],
|
||||
"batteries": [_component_data(battery) for battery in solaredge.batteries],
|
||||
"unresponsive_blocks": sorted(solaredge.unresponsive_blocks),
|
||||
}
|
||||
|
||||
return async_redact_data(data, TO_REDACT)
|
||||
@@ -51,7 +51,7 @@ rules:
|
||||
|
||||
# Gold
|
||||
devices: done
|
||||
diagnostics: todo
|
||||
diagnostics: done
|
||||
discovery: done
|
||||
discovery-update-info: done
|
||||
docs-data-update: todo
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
# serializer version: 1
|
||||
# name: test_diagnostics
|
||||
dict({
|
||||
'batteries': list([
|
||||
dict({
|
||||
'dc_current': -0.0,
|
||||
'dc_power': 0.0,
|
||||
'dc_voltage': 403.8467102050781,
|
||||
'energy_available': 9581.759765625,
|
||||
'energy_exported': 0,
|
||||
'energy_imported': 0,
|
||||
'energy_max': 9700.0,
|
||||
'manufacturer': '',
|
||||
'max_charge_peak_power': 6000.0,
|
||||
'max_charge_power': 5000.0,
|
||||
'max_discharge_peak_power': 6100.0,
|
||||
'max_discharge_power': 5100.0,
|
||||
'model': '',
|
||||
'rated_energy': 9700.0,
|
||||
'serial_number': '',
|
||||
'state_of_energy': 99.94781494140625,
|
||||
'state_of_health': 100.0,
|
||||
'status': 6,
|
||||
'temperature_average': 23.608806610107422,
|
||||
'temperature_max': 24.5,
|
||||
'version': '',
|
||||
}),
|
||||
dict({
|
||||
'dc_current': -0.0,
|
||||
'dc_power': 0.0,
|
||||
'dc_voltage': 404.8029479980469,
|
||||
'energy_available': 9706.5595703125,
|
||||
'energy_exported': 0,
|
||||
'energy_imported': 0,
|
||||
'energy_max': 9700.0,
|
||||
'manufacturer': '',
|
||||
'max_charge_peak_power': 0.0,
|
||||
'max_charge_power': 0.0,
|
||||
'max_discharge_peak_power': 0.0,
|
||||
'max_discharge_power': 0.0,
|
||||
'model': '',
|
||||
'rated_energy': 9700.0,
|
||||
'serial_number': '',
|
||||
'state_of_energy': 99.2170181274414,
|
||||
'state_of_health': 100.0,
|
||||
'status': 6,
|
||||
'temperature_average': 23.861509323120117,
|
||||
'temperature_max': 0.0,
|
||||
'version': '',
|
||||
}),
|
||||
]),
|
||||
'common': dict({
|
||||
'device_address': 0,
|
||||
'manufacturer': 'SolarEdge',
|
||||
'model': 'SE10000H-AUSNBBX14',
|
||||
'option': '',
|
||||
'serial_number': '**REDACTED**',
|
||||
'version': '',
|
||||
}),
|
||||
'inverter': dict({
|
||||
'ac_current': 39.99,
|
||||
'ac_current_a': 0.0,
|
||||
'ac_current_b': 0.0,
|
||||
'ac_current_c': 0.0,
|
||||
'ac_energy': 18785652,
|
||||
'ac_frequency': 50.037,
|
||||
'ac_power': 9490,
|
||||
'ac_power_factor': 98,
|
||||
'ac_va': 9600,
|
||||
'ac_var': 1449,
|
||||
'ac_voltage_ab': 250.2,
|
||||
'ac_voltage_an': 250.2,
|
||||
'ac_voltage_bc': 0.0,
|
||||
'ac_voltage_bn': 0.0,
|
||||
'ac_voltage_ca': 0.0,
|
||||
'ac_voltage_cn': 0.0,
|
||||
'dc_current': 0,
|
||||
'dc_power': 9635,
|
||||
'dc_voltage': 0,
|
||||
'did': 101,
|
||||
'on_grid': True,
|
||||
'status': 4,
|
||||
'temperature_cabinet': 0.0,
|
||||
'temperature_heatsink': 47.67,
|
||||
'temperature_other': 0.0,
|
||||
'temperature_transformer': 0.0,
|
||||
'vendor_status': 4,
|
||||
'vendor_status_extended': 0,
|
||||
}),
|
||||
'meters': list([
|
||||
dict({
|
||||
'ac_current': 30.08,
|
||||
'ac_current_a': 10.0,
|
||||
'ac_current_b': 10.02,
|
||||
'ac_current_c': 10.06,
|
||||
'ac_frequency': 50.02,
|
||||
'ac_power': 5279,
|
||||
'ac_power_a': 1750,
|
||||
'ac_power_b': 1760,
|
||||
'ac_power_c': 1769,
|
||||
'ac_power_factor': 98,
|
||||
'ac_power_factor_a': 0,
|
||||
'ac_power_factor_b': 0,
|
||||
'ac_power_factor_c': 0,
|
||||
'ac_va': 5350,
|
||||
'ac_va_a': 0,
|
||||
'ac_va_b': 0,
|
||||
'ac_va_c': 0,
|
||||
'ac_var': 869,
|
||||
'ac_var_a': 0,
|
||||
'ac_var_b': 0,
|
||||
'ac_var_c': 0,
|
||||
'ac_voltage_ab': 400,
|
||||
'ac_voltage_an': 231,
|
||||
'ac_voltage_bc': 401,
|
||||
'ac_voltage_bn': 232,
|
||||
'ac_voltage_ca': 402,
|
||||
'ac_voltage_cn': 233,
|
||||
'ac_voltage_ll': 0,
|
||||
'ac_voltage_ln': 232,
|
||||
'apparent_energy_exported': 0,
|
||||
'apparent_energy_imported': 0,
|
||||
'did': 203,
|
||||
'energy_exported': 2324624.0,
|
||||
'energy_exported_a': 774000.0,
|
||||
'energy_exported_b': 775000.0,
|
||||
'energy_exported_c': 775624.0,
|
||||
'energy_imported': 12320555.0,
|
||||
'energy_imported_a': 4106000.0,
|
||||
'energy_imported_b': 4107000.0,
|
||||
'energy_imported_c': 4107555.0,
|
||||
'events': 0,
|
||||
'manufacturer': '',
|
||||
'model': '',
|
||||
'option': '',
|
||||
'reactive_energy_q1': 0,
|
||||
'reactive_energy_q2': 0,
|
||||
'reactive_energy_q3': 0,
|
||||
'reactive_energy_q4': 0,
|
||||
'serial_number': '',
|
||||
'version': '',
|
||||
}),
|
||||
]),
|
||||
'mmppt': None,
|
||||
'polls': dict({
|
||||
'readings': dict({
|
||||
'failed': dict({
|
||||
}),
|
||||
'updated': list([
|
||||
'batteries[0]',
|
||||
'batteries[1]',
|
||||
'common',
|
||||
'inverter',
|
||||
'meters[0]',
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
'unresponsive_blocks': list([
|
||||
]),
|
||||
})
|
||||
# ---
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Tests for the SolarEdge Modbus diagnostics."""
|
||||
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
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,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""The diagnostics dump matches the snapshot."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
await get_diagnostics_for_config_entry(hass, hass_client, mock_config_entry)
|
||||
== snapshot
|
||||
)
|
||||
Reference in New Issue
Block a user