Add diagnostics to System Monitor (#111291)

* Add diagnostics to System Monitor

* Fix
This commit is contained in:
G Johansson 2024-02-24 20:11:11 +01:00 committed by GitHub
parent 57d169582d
commit 777428c36d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 162 additions and 14 deletions

View File

@ -1,6 +1,7 @@
"""Constants for System Monitor."""
DOMAIN = "systemmonitor"
DOMAIN_COORDINATORS = "systemmonitor_coordinators"
CONF_INDEX = "index"
CONF_PROCESS = "process"

View File

@ -14,7 +14,10 @@ import psutil_home_assistant as ha_psutil
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_component import DEFAULT_SCAN_INTERVAL
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.helpers.update_coordinator import (
TimestampDataUpdateCoordinator,
UpdateFailed,
)
from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
@ -50,7 +53,7 @@ dataT = TypeVar(
)
class MonitorCoordinator(DataUpdateCoordinator[dataT]):
class MonitorCoordinator(TimestampDataUpdateCoordinator[dataT]):
"""A System monitor Base Data Update Coordinator."""
def __init__(

View File

@ -0,0 +1,30 @@
"""Diagnostics support for Sensibo."""
from __future__ import annotations
from typing import Any
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN_COORDINATORS
from .coordinator import MonitorCoordinator
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for Sensibo config entry."""
coordinators: dict[str, MonitorCoordinator] = hass.data[DOMAIN_COORDINATORS]
diag_data = {}
for _type, coordinator in coordinators.items():
diag_data[_type] = {
"last_update_success": coordinator.last_update_success,
"last_update": str(coordinator.last_update_success_time),
"data": str(coordinator.data),
}
return {
"entry": entry.as_dict(),
"coordinators": diag_data,
}

View File

@ -46,7 +46,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateTyp
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import slugify
from .const import CONF_PROCESS, DOMAIN, NET_IO_TYPES
from .const import CONF_PROCESS, DOMAIN, DOMAIN_COORDINATORS, NET_IO_TYPES
from .coordinator import (
MonitorCoordinator,
SystemMonitorBootTimeCoordinator,
@ -747,18 +747,22 @@ async def async_setup_entry( # noqa: C901
)
)
hass.data[DOMAIN_COORDINATORS] = {}
# No gathering to avoid swamping the executor
for coordinator in disk_coordinators.values():
for argument, coordinator in disk_coordinators.items():
hass.data[DOMAIN_COORDINATORS][f"disk_{argument}"] = coordinator
hass.data[DOMAIN_COORDINATORS]["boot_time"] = boot_time_coordinator
hass.data[DOMAIN_COORDINATORS]["cpu_temp"] = cpu_temp_coordinator
hass.data[DOMAIN_COORDINATORS]["memory"] = memory_coordinator
hass.data[DOMAIN_COORDINATORS]["net_addr"] = net_addr_coordinator
hass.data[DOMAIN_COORDINATORS]["net_io"] = net_io_coordinator
hass.data[DOMAIN_COORDINATORS]["process"] = process_coordinator
hass.data[DOMAIN_COORDINATORS]["processor"] = processor_coordinator
hass.data[DOMAIN_COORDINATORS]["swap"] = swap_coordinator
hass.data[DOMAIN_COORDINATORS]["system_load"] = system_load_coordinator
for coordinator in hass.data[DOMAIN_COORDINATORS].values():
await coordinator.async_request_refresh()
await boot_time_coordinator.async_request_refresh()
await cpu_temp_coordinator.async_request_refresh()
await memory_coordinator.async_request_refresh()
await net_addr_coordinator.async_request_refresh()
await net_io_coordinator.async_request_refresh()
await process_coordinator.async_request_refresh()
await processor_coordinator.async_request_refresh()
await swap_coordinator.async_request_refresh()
await system_load_coordinator.async_request_refresh()
async_add_entities(entities)

View File

@ -31,6 +31,7 @@ class MockProcess(Process):
super().__init__(1)
self._name = name
self._ex = ex
self._create_time = 1708700400
def name(self):
"""Return a name."""
@ -163,6 +164,7 @@ def mock_psutil(mock_process: list[MockProcess]) -> Generator:
sdiskpart("test3", "/incorrect", "", "", 1, 1),
sdiskpart("proc", "/proc/run", "proc", "", 1, 1),
]
mock_psutil.boot_time.return_value = 1708786800.0
mock_psutil.NoSuchProcess = NoSuchProcess
yield mock_psutil

View File

@ -0,0 +1,83 @@
# serializer version: 1
# name: test_diagnostics
dict({
'coordinators': dict({
'boot_time': dict({
'data': '2024-02-24 15:00:00+00:00',
'last_update_success': True,
}),
'cpu_temp': dict({
'data': "{'cpu0-thermal': [shwtemp(label='cpu0-thermal', current=50.0, high=60.0, critical=70.0)]}",
'last_update_success': True,
}),
'disk_/': dict({
'data': 'sdiskusage(total=536870912000, used=322122547200, free=214748364800, percent=60.0)',
'last_update_success': True,
}),
'disk_/home/notexist/': dict({
'data': 'sdiskusage(total=536870912000, used=322122547200, free=214748364800, percent=60.0)',
'last_update_success': True,
}),
'disk_/media/share': dict({
'data': 'sdiskusage(total=536870912000, used=322122547200, free=214748364800, percent=60.0)',
'last_update_success': True,
}),
'memory': dict({
'data': 'VirtualMemory(total=104857600, available=41943040, percent=40.0, used=62914560, free=31457280)',
'last_update_success': True,
}),
'net_addr': dict({
'data': "{'eth0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.1', netmask='255.255.255.0', broadcast='255.255.255.255', ptp=None)], 'eth1': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.10.1', netmask='255.255.255.0', broadcast='255.255.255.255', ptp=None)], 'vethxyzxyz': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='172.16.10.1', netmask='255.255.255.0', broadcast='255.255.255.255', ptp=None)]}",
'last_update_success': True,
}),
'net_io': dict({
'data': "{'eth0': snetio(bytes_sent=104857600, bytes_recv=104857600, packets_sent=50, packets_recv=50, errin=0, errout=0, dropin=0, dropout=0), 'eth1': snetio(bytes_sent=209715200, bytes_recv=209715200, packets_sent=150, packets_recv=150, errin=0, errout=0, dropin=0, dropout=0), 'vethxyzxyz': snetio(bytes_sent=314572800, bytes_recv=314572800, packets_sent=150, packets_recv=150, errin=0, errout=0, dropin=0, dropout=0)}",
'last_update_success': True,
}),
'process': dict({
'data': "[tests.components.systemmonitor.conftest.MockProcess(pid=1, name='python3', status='sleeping', started='2024-02-23 15:00:00'), tests.components.systemmonitor.conftest.MockProcess(pid=1, name='pip', status='sleeping', started='2024-02-23 15:00:00')]",
'last_update_success': True,
}),
'processor': dict({
'data': '10.0',
'last_update_success': True,
}),
'swap': dict({
'data': 'sswap(total=104857600, used=62914560, free=41943040, percent=60.0, sin=1, sout=1)',
'last_update_success': True,
}),
'system_load': dict({
'data': '(1, 2, 3)',
'last_update_success': True,
}),
}),
'entry': dict({
'data': dict({
}),
'disabled_by': None,
'domain': 'systemmonitor',
'minor_version': 2,
'options': dict({
'binary_sensor': dict({
'process': list([
'python3',
'pip',
]),
}),
'resources': list([
'disk_use_percent_/',
'disk_use_percent_/home/notexist/',
'memory_free_',
'network_out_eth0',
'process_python3',
]),
}),
'pref_disable_new_entities': False,
'pref_disable_polling': False,
'source': 'user',
'title': 'System Monitor',
'unique_id': None,
'version': 1,
}),
})
# ---

View File

@ -123,7 +123,7 @@
})
# ---
# name: test_sensor[System Monitor Last boot - state]
'2023-12-30T21:55:38+00:00'
'2024-02-24T15:00:00+00:00'
# ---
# name: test_sensor[System Monitor Load (15m) - attributes]
ReadOnlyDict({

View File

@ -0,0 +1,25 @@
"""Tests for the diagnostics data provided by the System Monitor integration."""
from unittest.mock import Mock
from syrupy import SnapshotAssertion
from syrupy.filters import props
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_psutil: Mock,
mock_os: Mock,
mock_added_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Test diagnostics."""
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_added_config_entry
) == snapshot(exclude=props("last_update", "entry_id"))