mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 02:07:09 +00:00
Add diagnostics to System Monitor (#111291)
* Add diagnostics to System Monitor * Fix
This commit is contained in:
parent
57d169582d
commit
777428c36d
@ -1,6 +1,7 @@
|
|||||||
"""Constants for System Monitor."""
|
"""Constants for System Monitor."""
|
||||||
|
|
||||||
DOMAIN = "systemmonitor"
|
DOMAIN = "systemmonitor"
|
||||||
|
DOMAIN_COORDINATORS = "systemmonitor_coordinators"
|
||||||
|
|
||||||
CONF_INDEX = "index"
|
CONF_INDEX = "index"
|
||||||
CONF_PROCESS = "process"
|
CONF_PROCESS = "process"
|
||||||
|
@ -14,7 +14,10 @@ import psutil_home_assistant as ha_psutil
|
|||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_component import DEFAULT_SCAN_INTERVAL
|
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
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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."""
|
"""A System monitor Base Data Update Coordinator."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
30
homeassistant/components/systemmonitor/diagnostics.py
Normal file
30
homeassistant/components/systemmonitor/diagnostics.py
Normal 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,
|
||||||
|
}
|
@ -46,7 +46,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateTyp
|
|||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
from homeassistant.util import slugify
|
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 (
|
from .coordinator import (
|
||||||
MonitorCoordinator,
|
MonitorCoordinator,
|
||||||
SystemMonitorBootTimeCoordinator,
|
SystemMonitorBootTimeCoordinator,
|
||||||
@ -747,18 +747,22 @@ async def async_setup_entry( # noqa: C901
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
hass.data[DOMAIN_COORDINATORS] = {}
|
||||||
# No gathering to avoid swamping the executor
|
# 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 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)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ class MockProcess(Process):
|
|||||||
super().__init__(1)
|
super().__init__(1)
|
||||||
self._name = name
|
self._name = name
|
||||||
self._ex = ex
|
self._ex = ex
|
||||||
|
self._create_time = 1708700400
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return a name."""
|
"""Return a name."""
|
||||||
@ -163,6 +164,7 @@ def mock_psutil(mock_process: list[MockProcess]) -> Generator:
|
|||||||
sdiskpart("test3", "/incorrect", "", "", 1, 1),
|
sdiskpart("test3", "/incorrect", "", "", 1, 1),
|
||||||
sdiskpart("proc", "/proc/run", "proc", "", 1, 1),
|
sdiskpart("proc", "/proc/run", "proc", "", 1, 1),
|
||||||
]
|
]
|
||||||
|
mock_psutil.boot_time.return_value = 1708786800.0
|
||||||
mock_psutil.NoSuchProcess = NoSuchProcess
|
mock_psutil.NoSuchProcess = NoSuchProcess
|
||||||
yield mock_psutil
|
yield mock_psutil
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
# ---
|
@ -123,7 +123,7 @@
|
|||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensor[System Monitor Last boot - state]
|
# 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]
|
# name: test_sensor[System Monitor Load (15m) - attributes]
|
||||||
ReadOnlyDict({
|
ReadOnlyDict({
|
||||||
|
25
tests/components/systemmonitor/test_diagnostics.py
Normal file
25
tests/components/systemmonitor/test_diagnostics.py
Normal 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"))
|
Loading…
x
Reference in New Issue
Block a user