mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 14:29:21 -04:00
Add diagnostics to KACO Modbus (#181771)
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""Diagnostics for KACO Modbus."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .coordinator import KacoConfigEntry
|
||||
|
||||
TO_REDACT = {"serial_number"}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: KacoConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry.
|
||||
|
||||
The raw register map is included because it loads straight into
|
||||
``modbus_connection.mock``, which reproduces a reported problem without
|
||||
the hardware. It covers the polled components only, so the serial number
|
||||
model 1 holds in the clear is not in it.
|
||||
"""
|
||||
coordinator = entry.runtime_data
|
||||
device = coordinator.device
|
||||
info = device.info
|
||||
assert info is not None
|
||||
|
||||
return async_redact_data(
|
||||
{
|
||||
"manufacturer": info.manufacturer,
|
||||
"model": info.model,
|
||||
"firmware": info.firmware,
|
||||
"options": info.options,
|
||||
"serial_number": info.serial_number,
|
||||
"base_address": device.base_address,
|
||||
"models": sorted(device.models or ()),
|
||||
"strings": len(device.strings),
|
||||
"updated": sorted(coordinator.data.updated),
|
||||
"failed": {
|
||||
component: str(error)
|
||||
for component, error in coordinator.data.failed.items()
|
||||
},
|
||||
"raw": await device.async_read_raw(),
|
||||
},
|
||||
TO_REDACT,
|
||||
)
|
||||
@@ -52,7 +52,7 @@ rules:
|
||||
test-coverage: done
|
||||
# Gold
|
||||
devices: done
|
||||
diagnostics: todo
|
||||
diagnostics: done
|
||||
discovery-update-info:
|
||||
status: exempt
|
||||
comment: There is no discovery to carry updated network information.
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
# serializer version: 1
|
||||
# name: test_diagnostics
|
||||
dict({
|
||||
'base_address': 40000,
|
||||
'failed': dict({
|
||||
}),
|
||||
'firmware': 'V5.53',
|
||||
'manufacturer': 'KACO new energy',
|
||||
'model': 'blueplanet 8.6 TL3 INT',
|
||||
'models': list([
|
||||
1,
|
||||
103,
|
||||
113,
|
||||
120,
|
||||
121,
|
||||
122,
|
||||
123,
|
||||
126,
|
||||
129,
|
||||
130,
|
||||
132,
|
||||
135,
|
||||
136,
|
||||
160,
|
||||
64204,
|
||||
]),
|
||||
'options': '086L32',
|
||||
'raw': dict({
|
||||
'holding': dict({
|
||||
'40070': 103,
|
||||
'40071': 50,
|
||||
'40072': 484,
|
||||
'40073': 164,
|
||||
'40074': 162,
|
||||
'40075': 157,
|
||||
'40076': 65534,
|
||||
'40077': 65535,
|
||||
'40078': 65535,
|
||||
'40079': 65535,
|
||||
'40080': 2265,
|
||||
'40081': 2282,
|
||||
'40082': 2278,
|
||||
'40083': 65535,
|
||||
'40084': 100,
|
||||
'40085': 1,
|
||||
'40086': 49944,
|
||||
'40087': 65533,
|
||||
'40088': 99,
|
||||
'40089': 1,
|
||||
'40090': 0,
|
||||
'40091': 1,
|
||||
'40092': 1000,
|
||||
'40093': 65533,
|
||||
'40094': 185,
|
||||
'40095': 63009,
|
||||
'40096': 0,
|
||||
'40097': 230,
|
||||
'40098': 65534,
|
||||
'40099': 4437,
|
||||
'40100': 65535,
|
||||
'40101': 102,
|
||||
'40102': 1,
|
||||
'40103': 469,
|
||||
'40104': 32768,
|
||||
'40105': 32768,
|
||||
'40106': 32768,
|
||||
'40107': 65535,
|
||||
'40108': 4,
|
||||
'40109': 4,
|
||||
'40110': 0,
|
||||
'40111': 0,
|
||||
'40112': 0,
|
||||
'40113': 0,
|
||||
'40114': 65535,
|
||||
'40115': 65535,
|
||||
'40116': 65535,
|
||||
'40117': 65535,
|
||||
'40118': 65535,
|
||||
'40119': 65535,
|
||||
'40120': 65535,
|
||||
'40121': 65535,
|
||||
'40184': 120,
|
||||
'40185': 26,
|
||||
'40186': 4,
|
||||
'40187': 860,
|
||||
'40188': 1,
|
||||
'40189': 860,
|
||||
'40190': 1,
|
||||
'40191': 860,
|
||||
'40192': 32768,
|
||||
'40193': 32768,
|
||||
'40194': 860,
|
||||
'40195': 1,
|
||||
'40196': 65535,
|
||||
'40197': 32768,
|
||||
'40198': 50880,
|
||||
'40199': 32768,
|
||||
'40200': 32768,
|
||||
'40201': 50880,
|
||||
'40202': 65533,
|
||||
'40203': 65535,
|
||||
'40204': 32768,
|
||||
'40205': 65535,
|
||||
'40206': 32768,
|
||||
'40207': 65535,
|
||||
'40208': 32768,
|
||||
'40209': 65535,
|
||||
'40210': 32768,
|
||||
'40212': 121,
|
||||
'40213': 30,
|
||||
'40214': 860,
|
||||
'40215': 2300,
|
||||
'40216': 32768,
|
||||
'40217': 65535,
|
||||
'40218': 65535,
|
||||
'40219': 860,
|
||||
'40220': 32768,
|
||||
'40221': 32768,
|
||||
'40222': 32768,
|
||||
'40223': 32768,
|
||||
'40224': 65535,
|
||||
'40225': 32768,
|
||||
'40226': 32768,
|
||||
'40227': 32768,
|
||||
'40228': 32768,
|
||||
'40229': 65535,
|
||||
'40230': 65535,
|
||||
'40231': 65535,
|
||||
'40232': 65535,
|
||||
'40233': 65535,
|
||||
'40234': 1,
|
||||
'40235': 65535,
|
||||
'40236': 32768,
|
||||
'40237': 32768,
|
||||
'40238': 1,
|
||||
'40239': 32768,
|
||||
'40240': 32768,
|
||||
'40241': 32768,
|
||||
'40242': 32768,
|
||||
'40243': 32768,
|
||||
'40244': 122,
|
||||
'40245': 44,
|
||||
'40246': 7,
|
||||
'40247': 65535,
|
||||
'40248': 1,
|
||||
'40249': 0,
|
||||
'40250': 0,
|
||||
'40251': 0,
|
||||
'40252': 0,
|
||||
'40253': 0,
|
||||
'40254': 0,
|
||||
'40255': 0,
|
||||
'40256': 0,
|
||||
'40257': 0,
|
||||
'40258': 0,
|
||||
'40259': 0,
|
||||
'40260': 0,
|
||||
'40261': 0,
|
||||
'40262': 0,
|
||||
'40263': 0,
|
||||
'40264': 0,
|
||||
'40265': 0,
|
||||
'40266': 0,
|
||||
'40267': 0,
|
||||
'40268': 0,
|
||||
'40269': 0,
|
||||
'40270': 0,
|
||||
'40271': 0,
|
||||
'40272': 0,
|
||||
'40273': 32768,
|
||||
'40274': 32768,
|
||||
'40275': 65535,
|
||||
'40276': 32768,
|
||||
'40277': 65535,
|
||||
'40278': 65535,
|
||||
'40279': 65535,
|
||||
'40280': 65535,
|
||||
'40281': 19265,
|
||||
'40282': 17231,
|
||||
'40283': 8302,
|
||||
'40284': 101,
|
||||
'40285': 12830,
|
||||
'40286': 1609,
|
||||
'40287': 65535,
|
||||
'40288': 65535,
|
||||
'40289': 32768,
|
||||
'40290': 123,
|
||||
'40291': 24,
|
||||
'40292': 65535,
|
||||
'40293': 65535,
|
||||
'40294': 1,
|
||||
'40295': 1000,
|
||||
'40296': 65535,
|
||||
'40297': 300,
|
||||
'40298': 65535,
|
||||
'40299': 0,
|
||||
'40300': 1000,
|
||||
'40301': 65535,
|
||||
'40302': 300,
|
||||
'40303': 65535,
|
||||
'40304': 0,
|
||||
'40305': 0,
|
||||
'40306': 0,
|
||||
'40307': 32768,
|
||||
'40308': 65535,
|
||||
'40309': 300,
|
||||
'40310': 65535,
|
||||
'40311': 1,
|
||||
'40312': 0,
|
||||
'40313': 65535,
|
||||
'40314': 65533,
|
||||
'40315': 65535,
|
||||
'40316': 126,
|
||||
'40317': 226,
|
||||
'40318': 1,
|
||||
'40319': 0,
|
||||
'40320': 65535,
|
||||
'40321': 65535,
|
||||
'40322': 65535,
|
||||
'40323': 4,
|
||||
'40324': 10,
|
||||
'40858': 160,
|
||||
'40859': 48,
|
||||
'40860': 65534,
|
||||
'40861': 65535,
|
||||
'40862': 1,
|
||||
'40863': 32768,
|
||||
'40864': 0,
|
||||
'40865': 0,
|
||||
'40866': 2,
|
||||
'40867': 65535,
|
||||
'40868': 0,
|
||||
'40869': 19792,
|
||||
'40870': 20564,
|
||||
'40871': 8240,
|
||||
'40872': 0,
|
||||
'40873': 0,
|
||||
'40874': 0,
|
||||
'40875': 0,
|
||||
'40876': 0,
|
||||
'40877': 102,
|
||||
'40878': 3551,
|
||||
'40879': 36,
|
||||
'40880': 0,
|
||||
'40881': 0,
|
||||
'40882': 27275,
|
||||
'40883': 15290,
|
||||
'40884': 45,
|
||||
'40885': 65535,
|
||||
'40886': 65535,
|
||||
'40887': 65535,
|
||||
'40888': 1,
|
||||
'40889': 19792,
|
||||
'40890': 20564,
|
||||
'40891': 8241,
|
||||
'40892': 0,
|
||||
'40893': 0,
|
||||
'40894': 0,
|
||||
'40895': 0,
|
||||
'40896': 0,
|
||||
'40897': 122,
|
||||
'40898': 5330,
|
||||
'40899': 65,
|
||||
'40900': 0,
|
||||
'40901': 0,
|
||||
'40902': 27275,
|
||||
'40903': 15290,
|
||||
'40904': 0,
|
||||
'40905': 65535,
|
||||
'40906': 65535,
|
||||
'40907': 65535,
|
||||
}),
|
||||
}),
|
||||
'serial_number': '**REDACTED**',
|
||||
'strings': 2,
|
||||
'updated': list([
|
||||
'inverter',
|
||||
'mppt',
|
||||
'status',
|
||||
]),
|
||||
})
|
||||
# ---
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Test the KACO Modbus diagnostics."""
|
||||
|
||||
import json
|
||||
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import MOCK_SERIAL
|
||||
|
||||
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,
|
||||
init_integration: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test generating diagnostics for a config entry."""
|
||||
diag = await get_diagnostics_for_config_entry(hass, hass_client, init_integration)
|
||||
|
||||
assert diag == snapshot
|
||||
|
||||
|
||||
async def test_diagnostics_does_not_leak_the_serial_number(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
init_integration: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the serial is redacted, and absent from the raw registers.
|
||||
|
||||
Model 1 carries it in the clear, so a raw map that reached back over that
|
||||
block would hand it to anyone who decoded the dump.
|
||||
"""
|
||||
diag = await get_diagnostics_for_config_entry(hass, hass_client, init_integration)
|
||||
|
||||
assert diag["serial_number"] == "**REDACTED**"
|
||||
assert MOCK_SERIAL not in json.dumps(diag)
|
||||
Reference in New Issue
Block a user