Enphase Envoy refactor and extend diagnostics (#109080)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
3b0ea52167
commit
44abe329a2
3 changed files with 3257 additions and 18 deletions
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
import copy
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
|
from attr import asdict
|
||||||
|
|
||||||
from homeassistant.components.diagnostics import async_redact_data
|
from homeassistant.components.diagnostics import async_redact_data
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
@ -14,11 +17,15 @@ from homeassistant.const import (
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
|
from homeassistant.helpers.json import json_dumps
|
||||||
|
from homeassistant.util.json import json_loads
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import EnphaseUpdateCoordinator
|
from .coordinator import EnphaseUpdateCoordinator
|
||||||
|
|
||||||
CONF_TITLE = "title"
|
CONF_TITLE = "title"
|
||||||
|
CLEAN_TEXT = "<<envoyserial>>"
|
||||||
|
|
||||||
TO_REDACT = {
|
TO_REDACT = {
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
|
@ -37,11 +44,78 @@ async def async_get_config_entry_diagnostics(
|
||||||
"""Return diagnostics for a config entry."""
|
"""Return diagnostics for a config entry."""
|
||||||
coordinator: EnphaseUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator: EnphaseUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
return async_redact_data(
|
if TYPE_CHECKING:
|
||||||
{
|
assert coordinator.envoy.data
|
||||||
"entry": entry.as_dict(),
|
envoy_data = coordinator.envoy.data
|
||||||
"envoy_firmware": coordinator.envoy.firmware,
|
envoy = coordinator.envoy
|
||||||
"data": coordinator.data,
|
|
||||||
},
|
device_registry = dr.async_get(hass)
|
||||||
TO_REDACT,
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
|
device_entities = []
|
||||||
|
# for each device associated with the envoy get entity and state information
|
||||||
|
for device in dr.async_entries_for_config_entry(device_registry, entry.entry_id):
|
||||||
|
entities = []
|
||||||
|
for entity in er.async_entries_for_device(
|
||||||
|
entity_registry, device_id=device.id, include_disabled_entities=True
|
||||||
|
):
|
||||||
|
state_dict = None
|
||||||
|
if state := hass.states.get(entity.entity_id):
|
||||||
|
state_dict = dict(state.as_dict())
|
||||||
|
state_dict.pop("context", None)
|
||||||
|
entities.append({"entity": asdict(entity), "state": state_dict})
|
||||||
|
device_entities.append({"device": asdict(device), "entities": entities})
|
||||||
|
|
||||||
|
# remove envoy serial
|
||||||
|
old_serial = coordinator.envoy_serial_number
|
||||||
|
|
||||||
|
coordinator_data = copy.deepcopy(coordinator.data)
|
||||||
|
coordinator_data_cleaned = json_dumps(coordinator_data).replace(
|
||||||
|
old_serial, CLEAN_TEXT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
device_entities_cleaned = json_dumps(device_entities).replace(
|
||||||
|
old_serial, CLEAN_TEXT
|
||||||
|
)
|
||||||
|
|
||||||
|
envoy_model: dict[str, Any] = {
|
||||||
|
"encharge_inventory": envoy_data.encharge_inventory,
|
||||||
|
"encharge_power": envoy_data.encharge_power,
|
||||||
|
"encharge_aggregate": envoy_data.encharge_aggregate,
|
||||||
|
"enpower": envoy_data.enpower,
|
||||||
|
"system_consumption": envoy_data.system_consumption,
|
||||||
|
"system_production": envoy_data.system_production,
|
||||||
|
"system_consumption_phases": envoy_data.system_consumption_phases,
|
||||||
|
"system_production_phases": envoy_data.system_production_phases,
|
||||||
|
"ctmeter_production": envoy_data.ctmeter_production,
|
||||||
|
"ctmeter_consumption": envoy_data.ctmeter_consumption,
|
||||||
|
"ctmeter_production_phases": envoy_data.ctmeter_production_phases,
|
||||||
|
"ctmeter_consumption_phases": envoy_data.ctmeter_consumption_phases,
|
||||||
|
"dry_contact_status": envoy_data.dry_contact_status,
|
||||||
|
"dry_contact_settings": envoy_data.dry_contact_settings,
|
||||||
|
"inverters": envoy_data.inverters,
|
||||||
|
"tariff": envoy_data.tariff,
|
||||||
|
}
|
||||||
|
|
||||||
|
envoy_properties: dict[str, Any] = {
|
||||||
|
"envoy_firmware": envoy.firmware,
|
||||||
|
"part_number": envoy.part_number,
|
||||||
|
"envoy_model": envoy.envoy_model,
|
||||||
|
"supported_features": [feature.name for feature in envoy.supported_features],
|
||||||
|
"phase_mode": envoy.phase_mode,
|
||||||
|
"phase_count": envoy.phase_count,
|
||||||
|
"active_phasecount": envoy.active_phase_count,
|
||||||
|
"ct_count": envoy.ct_meter_count,
|
||||||
|
"ct_consumption_meter": envoy.consumption_meter_type,
|
||||||
|
"ct_production_meter": envoy.production_meter_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostic_data: dict[str, Any] = {
|
||||||
|
"config_entry": async_redact_data(entry.as_dict(), TO_REDACT),
|
||||||
|
"envoy_properties": envoy_properties,
|
||||||
|
"raw_data": json_loads(coordinator_data_cleaned),
|
||||||
|
"envoy_model_data": envoy_model,
|
||||||
|
"envoy_entities_by_device": json_loads(device_entities_cleaned),
|
||||||
|
}
|
||||||
|
|
||||||
|
return diagnostic_data
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,21 +2,35 @@
|
||||||
|
|
||||||
from syrupy import SnapshotAssertion
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||||
from tests.typing import ClientSessionGenerator
|
from tests.typing import ClientSessionGenerator
|
||||||
|
|
||||||
|
# Fields to exclude from snapshot as they change each run
|
||||||
|
TO_EXCLUDE = {
|
||||||
|
"id",
|
||||||
|
"device_id",
|
||||||
|
"via_device_id",
|
||||||
|
"last_updated",
|
||||||
|
"last_changed",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def limit_diagnostic_attrs(prop, path) -> bool:
|
||||||
|
"""Mark attributes to exclude from diagnostic snapshot."""
|
||||||
|
return prop in TO_EXCLUDE
|
||||||
|
|
||||||
|
|
||||||
async def test_entry_diagnostics(
|
async def test_entry_diagnostics(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry,
|
config_entry: ConfigEntry,
|
||||||
hass_client: ClientSessionGenerator,
|
hass_client: ClientSessionGenerator,
|
||||||
setup_enphase_envoy,
|
setup_enphase_envoy,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test config entry diagnostics."""
|
"""Test config entry diagnostics."""
|
||||||
assert (
|
assert await get_diagnostics_for_config_entry(
|
||||||
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
|
hass, hass_client, config_entry
|
||||||
== snapshot
|
) == snapshot(exclude=limit_diagnostic_attrs)
|
||||||
)
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue