Isolate systemmonitor from psutil shared state (#111110)
* Isolate systemmonitor from psutil shared state * Mods * typing * Fix tests * Fix read temp issue --------- Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
2ff0102bce
commit
9e46c2e2b3
15 changed files with 148 additions and 181 deletions
|
@ -2,12 +2,16 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import psutil_home_assistant as ha_psutil
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||||
|
@ -15,7 +19,8 @@ PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up System Monitor from a config entry."""
|
"""Set up System Monitor from a config entry."""
|
||||||
|
psutil_wrapper = await hass.async_add_executor_job(ha_psutil.PsutilWrapper)
|
||||||
|
hass.data[DOMAIN] = psutil_wrapper
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -9,7 +9,8 @@ import logging
|
||||||
import sys
|
import sys
|
||||||
from typing import Generic, Literal
|
from typing import Generic, Literal
|
||||||
|
|
||||||
import psutil
|
from psutil import NoSuchProcess, Process
|
||||||
|
import psutil_home_assistant as ha_psutil
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
DOMAIN as BINARY_SENSOR_DOMAIN,
|
DOMAIN as BINARY_SENSOR_DOMAIN,
|
||||||
|
@ -50,7 +51,7 @@ def get_cpu_icon() -> Literal["mdi:cpu-64-bit", "mdi:cpu-32-bit"]:
|
||||||
return "mdi:cpu-32-bit"
|
return "mdi:cpu-32-bit"
|
||||||
|
|
||||||
|
|
||||||
def get_process(entity: SystemMonitorSensor[list[psutil.Process]]) -> bool:
|
def get_process(entity: SystemMonitorSensor[list[Process]]) -> bool:
|
||||||
"""Return process."""
|
"""Return process."""
|
||||||
state = False
|
state = False
|
||||||
for proc in entity.coordinator.data:
|
for proc in entity.coordinator.data:
|
||||||
|
@ -59,7 +60,7 @@ def get_process(entity: SystemMonitorSensor[list[psutil.Process]]) -> bool:
|
||||||
if entity.argument == proc.name():
|
if entity.argument == proc.name():
|
||||||
state = True
|
state = True
|
||||||
break
|
break
|
||||||
except psutil.NoSuchProcess as err:
|
except NoSuchProcess as err:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Failed to load process with ID: %s, old name: %s",
|
"Failed to load process with ID: %s, old name: %s",
|
||||||
err.pid,
|
err.pid,
|
||||||
|
@ -77,10 +78,8 @@ class SysMonitorBinarySensorEntityDescription(
|
||||||
value_fn: Callable[[SystemMonitorSensor[dataT]], bool]
|
value_fn: Callable[[SystemMonitorSensor[dataT]], bool]
|
||||||
|
|
||||||
|
|
||||||
SENSOR_TYPES: tuple[
|
SENSOR_TYPES: tuple[SysMonitorBinarySensorEntityDescription[list[Process]], ...] = (
|
||||||
SysMonitorBinarySensorEntityDescription[list[psutil.Process]], ...
|
SysMonitorBinarySensorEntityDescription[list[Process]](
|
||||||
] = (
|
|
||||||
SysMonitorBinarySensorEntityDescription[list[psutil.Process]](
|
|
||||||
key="binary_process",
|
key="binary_process",
|
||||||
translation_key="process",
|
translation_key="process",
|
||||||
icon=get_cpu_icon(),
|
icon=get_cpu_icon(),
|
||||||
|
@ -94,8 +93,12 @@ async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up System Montor binary sensors based on a config entry."""
|
"""Set up System Montor binary sensors based on a config entry."""
|
||||||
|
psutil_wrapper: ha_psutil.PsutilWrapper = hass.data[DOMAIN]
|
||||||
|
|
||||||
entities: list[SystemMonitorSensor] = []
|
entities: list[SystemMonitorSensor] = []
|
||||||
process_coordinator = SystemMonitorProcessCoordinator(hass, "Process coordinator")
|
process_coordinator = SystemMonitorProcessCoordinator(
|
||||||
|
hass, psutil_wrapper, "Process coordinator"
|
||||||
|
)
|
||||||
await process_coordinator.async_request_refresh()
|
await process_coordinator.async_request_refresh()
|
||||||
|
|
||||||
for sensor_description in SENSOR_TYPES:
|
for sensor_description in SENSOR_TYPES:
|
||||||
|
|
|
@ -86,7 +86,7 @@ async def validate_import_sensor_setup(
|
||||||
async def get_sensor_setup_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
|
async def get_sensor_setup_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
|
||||||
"""Return process sensor setup schema."""
|
"""Return process sensor setup schema."""
|
||||||
hass = handler.parent_handler.hass
|
hass = handler.parent_handler.hass
|
||||||
processes = list(await hass.async_add_executor_job(get_all_running_processes))
|
processes = list(await hass.async_add_executor_job(get_all_running_processes, hass))
|
||||||
return vol.Schema(
|
return vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_PROCESS): SelectSelector(
|
vol.Required(CONF_PROCESS): SelectSelector(
|
||||||
|
|
|
@ -8,8 +8,9 @@ import logging
|
||||||
import os
|
import os
|
||||||
from typing import NamedTuple, TypeVar
|
from typing import NamedTuple, TypeVar
|
||||||
|
|
||||||
import psutil
|
from psutil import Process
|
||||||
from psutil._common import sdiskusage, shwtemp, snetio, snicaddr, sswap
|
from psutil._common import sdiskusage, shwtemp, snetio, snicaddr, sswap
|
||||||
|
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
|
||||||
|
@ -40,7 +41,7 @@ dataT = TypeVar(
|
||||||
| dict[str, list[snicaddr]]
|
| dict[str, list[snicaddr]]
|
||||||
| dict[str, snetio]
|
| dict[str, snetio]
|
||||||
| float
|
| float
|
||||||
| list[psutil.Process]
|
| list[Process]
|
||||||
| sswap
|
| sswap
|
||||||
| VirtualMemory
|
| VirtualMemory
|
||||||
| tuple[float, float, float]
|
| tuple[float, float, float]
|
||||||
|
@ -52,7 +53,9 @@ dataT = TypeVar(
|
||||||
class MonitorCoordinator(DataUpdateCoordinator[dataT]):
|
class MonitorCoordinator(DataUpdateCoordinator[dataT]):
|
||||||
"""A System monitor Base Data Update Coordinator."""
|
"""A System monitor Base Data Update Coordinator."""
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, name: str) -> None:
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, psutil_wrapper: ha_psutil.PsutilWrapper, name: str
|
||||||
|
) -> None:
|
||||||
"""Initialize the coordinator."""
|
"""Initialize the coordinator."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
|
@ -61,6 +64,7 @@ class MonitorCoordinator(DataUpdateCoordinator[dataT]):
|
||||||
update_interval=DEFAULT_SCAN_INTERVAL,
|
update_interval=DEFAULT_SCAN_INTERVAL,
|
||||||
always_update=False,
|
always_update=False,
|
||||||
)
|
)
|
||||||
|
self._psutil = psutil_wrapper.psutil
|
||||||
|
|
||||||
async def _async_update_data(self) -> dataT:
|
async def _async_update_data(self) -> dataT:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
|
@ -74,15 +78,22 @@ class MonitorCoordinator(DataUpdateCoordinator[dataT]):
|
||||||
class SystemMonitorDiskCoordinator(MonitorCoordinator[sdiskusage]):
|
class SystemMonitorDiskCoordinator(MonitorCoordinator[sdiskusage]):
|
||||||
"""A System monitor Disk Data Update Coordinator."""
|
"""A System monitor Disk Data Update Coordinator."""
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, name: str, argument: str) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
psutil_wrapper: ha_psutil.PsutilWrapper,
|
||||||
|
name: str,
|
||||||
|
argument: str,
|
||||||
|
) -> None:
|
||||||
"""Initialize the disk coordinator."""
|
"""Initialize the disk coordinator."""
|
||||||
super().__init__(hass, name)
|
super().__init__(hass, psutil_wrapper, name)
|
||||||
self._argument = argument
|
self._argument = argument
|
||||||
|
|
||||||
def update_data(self) -> sdiskusage:
|
def update_data(self) -> sdiskusage:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
try:
|
try:
|
||||||
return psutil.disk_usage(self._argument)
|
usage: sdiskusage = self._psutil.disk_usage(self._argument)
|
||||||
|
return usage
|
||||||
except PermissionError as err:
|
except PermissionError as err:
|
||||||
raise UpdateFailed(f"No permission to access {self._argument}") from err
|
raise UpdateFailed(f"No permission to access {self._argument}") from err
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
|
@ -94,7 +105,8 @@ class SystemMonitorSwapCoordinator(MonitorCoordinator[sswap]):
|
||||||
|
|
||||||
def update_data(self) -> sswap:
|
def update_data(self) -> sswap:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
return psutil.swap_memory()
|
swap: sswap = self._psutil.swap_memory()
|
||||||
|
return swap
|
||||||
|
|
||||||
|
|
||||||
class SystemMonitorMemoryCoordinator(MonitorCoordinator[VirtualMemory]):
|
class SystemMonitorMemoryCoordinator(MonitorCoordinator[VirtualMemory]):
|
||||||
|
@ -102,7 +114,7 @@ class SystemMonitorMemoryCoordinator(MonitorCoordinator[VirtualMemory]):
|
||||||
|
|
||||||
def update_data(self) -> VirtualMemory:
|
def update_data(self) -> VirtualMemory:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
memory = psutil.virtual_memory()
|
memory = self._psutil.virtual_memory()
|
||||||
return VirtualMemory(
|
return VirtualMemory(
|
||||||
memory.total, memory.available, memory.percent, memory.used, memory.free
|
memory.total, memory.available, memory.percent, memory.used, memory.free
|
||||||
)
|
)
|
||||||
|
@ -113,7 +125,8 @@ class SystemMonitorNetIOCoordinator(MonitorCoordinator[dict[str, snetio]]):
|
||||||
|
|
||||||
def update_data(self) -> dict[str, snetio]:
|
def update_data(self) -> dict[str, snetio]:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
return psutil.net_io_counters(pernic=True)
|
io_counters: dict[str, snetio] = self._psutil.net_io_counters(pernic=True)
|
||||||
|
return io_counters
|
||||||
|
|
||||||
|
|
||||||
class SystemMonitorNetAddrCoordinator(MonitorCoordinator[dict[str, list[snicaddr]]]):
|
class SystemMonitorNetAddrCoordinator(MonitorCoordinator[dict[str, list[snicaddr]]]):
|
||||||
|
@ -121,13 +134,19 @@ class SystemMonitorNetAddrCoordinator(MonitorCoordinator[dict[str, list[snicaddr
|
||||||
|
|
||||||
def update_data(self) -> dict[str, list[snicaddr]]:
|
def update_data(self) -> dict[str, list[snicaddr]]:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
return psutil.net_if_addrs()
|
addresses: dict[str, list[snicaddr]] = self._psutil.net_if_addrs()
|
||||||
|
return addresses
|
||||||
|
|
||||||
|
|
||||||
class SystemMonitorLoadCoordinator(MonitorCoordinator[tuple[float, float, float]]):
|
class SystemMonitorLoadCoordinator(
|
||||||
|
MonitorCoordinator[tuple[float, float, float] | None]
|
||||||
|
):
|
||||||
"""A System monitor Load Data Update Coordinator."""
|
"""A System monitor Load Data Update Coordinator."""
|
||||||
|
|
||||||
def update_data(self) -> tuple[float, float, float]:
|
def update_data(self) -> tuple[float, float, float] | None:
|
||||||
|
"""Coordinator is not async."""
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> tuple[float, float, float] | None:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
return os.getloadavg()
|
return os.getloadavg()
|
||||||
|
|
||||||
|
@ -136,8 +155,17 @@ class SystemMonitorProcessorCoordinator(MonitorCoordinator[float | None]):
|
||||||
"""A System monitor Processor Data Update Coordinator."""
|
"""A System monitor Processor Data Update Coordinator."""
|
||||||
|
|
||||||
def update_data(self) -> float | None:
|
def update_data(self) -> float | None:
|
||||||
"""Fetch data."""
|
"""Coordinator is not async."""
|
||||||
cpu_percent = psutil.cpu_percent(interval=None)
|
|
||||||
|
async def _async_update_data(self) -> float | None:
|
||||||
|
"""Get cpu usage.
|
||||||
|
|
||||||
|
Unlikely the rest of the coordinators, this one is async
|
||||||
|
since it does not block and we need to make sure it runs
|
||||||
|
in the same thread every time as psutil checks the thread
|
||||||
|
tid and compares it against the previous one.
|
||||||
|
"""
|
||||||
|
cpu_percent: float = self._psutil.cpu_percent(interval=None)
|
||||||
if cpu_percent > 0.0:
|
if cpu_percent > 0.0:
|
||||||
return cpu_percent
|
return cpu_percent
|
||||||
return None
|
return None
|
||||||
|
@ -148,15 +176,15 @@ class SystemMonitorBootTimeCoordinator(MonitorCoordinator[datetime]):
|
||||||
|
|
||||||
def update_data(self) -> datetime:
|
def update_data(self) -> datetime:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
return dt_util.utc_from_timestamp(psutil.boot_time())
|
return dt_util.utc_from_timestamp(self._psutil.boot_time())
|
||||||
|
|
||||||
|
|
||||||
class SystemMonitorProcessCoordinator(MonitorCoordinator[list[psutil.Process]]):
|
class SystemMonitorProcessCoordinator(MonitorCoordinator[list[Process]]):
|
||||||
"""A System monitor Process Data Update Coordinator."""
|
"""A System monitor Process Data Update Coordinator."""
|
||||||
|
|
||||||
def update_data(self) -> list[psutil.Process]:
|
def update_data(self) -> list[Process]:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
processes = psutil.process_iter()
|
processes = self._psutil.process_iter()
|
||||||
return list(processes)
|
return list(processes)
|
||||||
|
|
||||||
|
|
||||||
|
@ -166,6 +194,7 @@ class SystemMonitorCPUtempCoordinator(MonitorCoordinator[dict[str, list[shwtemp]
|
||||||
def update_data(self) -> dict[str, list[shwtemp]]:
|
def update_data(self) -> dict[str, list[shwtemp]]:
|
||||||
"""Fetch data."""
|
"""Fetch data."""
|
||||||
try:
|
try:
|
||||||
return psutil.sensors_temperatures()
|
temps: dict[str, list[shwtemp]] = self._psutil.sensors_temperatures()
|
||||||
|
return temps
|
||||||
except AttributeError as err:
|
except AttributeError as err:
|
||||||
raise UpdateFailed("OS does not provide temperature sensors") from err
|
raise UpdateFailed("OS does not provide temperature sensors") from err
|
||||||
|
|
|
@ -6,5 +6,5 @@
|
||||||
"documentation": "https://www.home-assistant.io/integrations/systemmonitor",
|
"documentation": "https://www.home-assistant.io/integrations/systemmonitor",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["psutil"],
|
"loggers": ["psutil"],
|
||||||
"requirements": ["psutil==5.9.8"]
|
"requirements": ["psutil-home-assistant==0.0.1", "psutil==5.9.8"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,9 @@ import sys
|
||||||
import time
|
import time
|
||||||
from typing import Any, Generic, Literal
|
from typing import Any, Generic, Literal
|
||||||
|
|
||||||
import psutil
|
from psutil import NoSuchProcess, Process
|
||||||
from psutil._common import sdiskusage, shwtemp, snetio, snicaddr, sswap
|
from psutil._common import sdiskusage, shwtemp, snetio, snicaddr, sswap
|
||||||
|
import psutil_home_assistant as ha_psutil
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
|
@ -89,10 +90,10 @@ def get_processor_temperature(
|
||||||
entity: SystemMonitorSensor[dict[str, list[shwtemp]]],
|
entity: SystemMonitorSensor[dict[str, list[shwtemp]]],
|
||||||
) -> float | None:
|
) -> float | None:
|
||||||
"""Return processor temperature."""
|
"""Return processor temperature."""
|
||||||
return read_cpu_temperature(entity.coordinator.data)
|
return read_cpu_temperature(entity.hass, entity.coordinator.data)
|
||||||
|
|
||||||
|
|
||||||
def get_process(entity: SystemMonitorSensor[list[psutil.Process]]) -> str:
|
def get_process(entity: SystemMonitorSensor[list[Process]]) -> str:
|
||||||
"""Return process."""
|
"""Return process."""
|
||||||
state = STATE_OFF
|
state = STATE_OFF
|
||||||
for proc in entity.coordinator.data:
|
for proc in entity.coordinator.data:
|
||||||
|
@ -101,7 +102,7 @@ def get_process(entity: SystemMonitorSensor[list[psutil.Process]]) -> str:
|
||||||
if entity.argument == proc.name():
|
if entity.argument == proc.name():
|
||||||
state = STATE_ON
|
state = STATE_ON
|
||||||
break
|
break
|
||||||
except psutil.NoSuchProcess as err:
|
except NoSuchProcess as err:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Failed to load process with ID: %s, old name: %s",
|
"Failed to load process with ID: %s, old name: %s",
|
||||||
err.pid,
|
err.pid,
|
||||||
|
@ -332,7 +333,7 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription[Any]] = {
|
||||||
mandatory_arg=True,
|
mandatory_arg=True,
|
||||||
value_fn=get_throughput,
|
value_fn=get_throughput,
|
||||||
),
|
),
|
||||||
"process": SysMonitorSensorEntityDescription[list[psutil.Process]](
|
"process": SysMonitorSensorEntityDescription[list[Process]](
|
||||||
key="process",
|
key="process",
|
||||||
translation_key="process",
|
translation_key="process",
|
||||||
placeholder="process",
|
placeholder="process",
|
||||||
|
@ -487,12 +488,16 @@ async def async_setup_entry( # noqa: C901
|
||||||
entities: list[SystemMonitorSensor] = []
|
entities: list[SystemMonitorSensor] = []
|
||||||
legacy_resources: set[str] = set(entry.options.get("resources", []))
|
legacy_resources: set[str] = set(entry.options.get("resources", []))
|
||||||
loaded_resources: set[str] = set()
|
loaded_resources: set[str] = set()
|
||||||
|
psutil_wrapper: ha_psutil.PsutilWrapper = hass.data[DOMAIN]
|
||||||
|
|
||||||
def get_arguments() -> dict[str, Any]:
|
def get_arguments() -> dict[str, Any]:
|
||||||
"""Return startup information."""
|
"""Return startup information."""
|
||||||
disk_arguments = get_all_disk_mounts()
|
disk_arguments = get_all_disk_mounts(hass)
|
||||||
network_arguments = get_all_network_interfaces()
|
network_arguments = get_all_network_interfaces(hass)
|
||||||
cpu_temperature = read_cpu_temperature()
|
try:
|
||||||
|
cpu_temperature = read_cpu_temperature(hass)
|
||||||
|
except AttributeError:
|
||||||
|
cpu_temperature = 0.0
|
||||||
return {
|
return {
|
||||||
"disk_arguments": disk_arguments,
|
"disk_arguments": disk_arguments,
|
||||||
"network_arguments": network_arguments,
|
"network_arguments": network_arguments,
|
||||||
|
@ -504,31 +509,39 @@ async def async_setup_entry( # noqa: C901
|
||||||
disk_coordinators: dict[str, SystemMonitorDiskCoordinator] = {}
|
disk_coordinators: dict[str, SystemMonitorDiskCoordinator] = {}
|
||||||
for argument in startup_arguments["disk_arguments"]:
|
for argument in startup_arguments["disk_arguments"]:
|
||||||
disk_coordinators[argument] = SystemMonitorDiskCoordinator(
|
disk_coordinators[argument] = SystemMonitorDiskCoordinator(
|
||||||
hass, f"Disk {argument} coordinator", argument
|
hass, psutil_wrapper, f"Disk {argument} coordinator", argument
|
||||||
)
|
)
|
||||||
swap_coordinator = SystemMonitorSwapCoordinator(hass, "Swap coordinator")
|
swap_coordinator = SystemMonitorSwapCoordinator(
|
||||||
memory_coordinator = SystemMonitorMemoryCoordinator(hass, "Memory coordinator")
|
hass, psutil_wrapper, "Swap coordinator"
|
||||||
net_io_coordinator = SystemMonitorNetIOCoordinator(hass, "Net IO coordnator")
|
)
|
||||||
|
memory_coordinator = SystemMonitorMemoryCoordinator(
|
||||||
|
hass, psutil_wrapper, "Memory coordinator"
|
||||||
|
)
|
||||||
|
net_io_coordinator = SystemMonitorNetIOCoordinator(
|
||||||
|
hass, psutil_wrapper, "Net IO coordnator"
|
||||||
|
)
|
||||||
net_addr_coordinator = SystemMonitorNetAddrCoordinator(
|
net_addr_coordinator = SystemMonitorNetAddrCoordinator(
|
||||||
hass, "Net address coordinator"
|
hass, psutil_wrapper, "Net address coordinator"
|
||||||
)
|
)
|
||||||
system_load_coordinator = SystemMonitorLoadCoordinator(
|
system_load_coordinator = SystemMonitorLoadCoordinator(
|
||||||
hass, "System load coordinator"
|
hass, psutil_wrapper, "System load coordinator"
|
||||||
)
|
)
|
||||||
processor_coordinator = SystemMonitorProcessorCoordinator(
|
processor_coordinator = SystemMonitorProcessorCoordinator(
|
||||||
hass, "Processor coordinator"
|
hass, psutil_wrapper, "Processor coordinator"
|
||||||
)
|
)
|
||||||
boot_time_coordinator = SystemMonitorBootTimeCoordinator(
|
boot_time_coordinator = SystemMonitorBootTimeCoordinator(
|
||||||
hass, "Boot time coordinator"
|
hass, psutil_wrapper, "Boot time coordinator"
|
||||||
|
)
|
||||||
|
process_coordinator = SystemMonitorProcessCoordinator(
|
||||||
|
hass, psutil_wrapper, "Process coordinator"
|
||||||
)
|
)
|
||||||
process_coordinator = SystemMonitorProcessCoordinator(hass, "Process coordinator")
|
|
||||||
cpu_temp_coordinator = SystemMonitorCPUtempCoordinator(
|
cpu_temp_coordinator = SystemMonitorCPUtempCoordinator(
|
||||||
hass, "CPU temperature coordinator"
|
hass, psutil_wrapper, "CPU temperature coordinator"
|
||||||
)
|
)
|
||||||
|
|
||||||
for argument in startup_arguments["disk_arguments"]:
|
for argument in startup_arguments["disk_arguments"]:
|
||||||
disk_coordinators[argument] = SystemMonitorDiskCoordinator(
|
disk_coordinators[argument] = SystemMonitorDiskCoordinator(
|
||||||
hass, f"Disk {argument} coordinator", argument
|
hass, psutil_wrapper, f"Disk {argument} coordinator", argument
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER.debug("Setup from options %s", entry.options)
|
_LOGGER.debug("Setup from options %s", entry.options)
|
||||||
|
@ -722,7 +735,7 @@ async def async_setup_entry( # noqa: C901
|
||||||
_LOGGER.debug("Loading legacy %s with argument %s", _type, argument)
|
_LOGGER.debug("Loading legacy %s with argument %s", _type, argument)
|
||||||
if not disk_coordinators.get(argument):
|
if not disk_coordinators.get(argument):
|
||||||
disk_coordinators[argument] = SystemMonitorDiskCoordinator(
|
disk_coordinators[argument] = SystemMonitorDiskCoordinator(
|
||||||
hass, f"Disk {argument} coordinator", argument
|
hass, psutil_wrapper, f"Disk {argument} coordinator", argument
|
||||||
)
|
)
|
||||||
entities.append(
|
entities.append(
|
||||||
SystemMonitorSensor(
|
SystemMonitorSensor(
|
||||||
|
|
|
@ -3,20 +3,23 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import psutil
|
|
||||||
from psutil._common import shwtemp
|
from psutil._common import shwtemp
|
||||||
|
import psutil_home_assistant as ha_psutil
|
||||||
|
|
||||||
from .const import CPU_SENSOR_PREFIXES
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .const import CPU_SENSOR_PREFIXES, DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SKIP_DISK_TYPES = {"proc", "tmpfs", "devtmpfs"}
|
SKIP_DISK_TYPES = {"proc", "tmpfs", "devtmpfs"}
|
||||||
|
|
||||||
|
|
||||||
def get_all_disk_mounts() -> set[str]:
|
def get_all_disk_mounts(hass: HomeAssistant) -> set[str]:
|
||||||
"""Return all disk mount points on system."""
|
"""Return all disk mount points on system."""
|
||||||
|
psutil_wrapper: ha_psutil = hass.data[DOMAIN]
|
||||||
disks: set[str] = set()
|
disks: set[str] = set()
|
||||||
for part in psutil.disk_partitions(all=True):
|
for part in psutil_wrapper.psutil.disk_partitions(all=True):
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
if "cdrom" in part.opts or part.fstype == "":
|
if "cdrom" in part.opts or part.fstype == "":
|
||||||
# skip cd-rom drives with no disk in it; they may raise
|
# skip cd-rom drives with no disk in it; they may raise
|
||||||
|
@ -27,7 +30,7 @@ def get_all_disk_mounts() -> set[str]:
|
||||||
# Ignore disks which are memory
|
# Ignore disks which are memory
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
usage = psutil.disk_usage(part.mountpoint)
|
usage = psutil_wrapper.psutil.disk_usage(part.mountpoint)
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"No permission for running user to access %s", part.mountpoint
|
"No permission for running user to access %s", part.mountpoint
|
||||||
|
@ -44,10 +47,11 @@ def get_all_disk_mounts() -> set[str]:
|
||||||
return disks
|
return disks
|
||||||
|
|
||||||
|
|
||||||
def get_all_network_interfaces() -> set[str]:
|
def get_all_network_interfaces(hass: HomeAssistant) -> set[str]:
|
||||||
"""Return all network interfaces on system."""
|
"""Return all network interfaces on system."""
|
||||||
|
psutil_wrapper: ha_psutil = hass.data[DOMAIN]
|
||||||
interfaces: set[str] = set()
|
interfaces: set[str] = set()
|
||||||
for interface, _ in psutil.net_if_addrs().items():
|
for interface, _ in psutil_wrapper.psutil.net_if_addrs().items():
|
||||||
if interface.startswith("veth"):
|
if interface.startswith("veth"):
|
||||||
# Don't load docker virtual network interfaces
|
# Don't load docker virtual network interfaces
|
||||||
continue
|
continue
|
||||||
|
@ -56,20 +60,24 @@ def get_all_network_interfaces() -> set[str]:
|
||||||
return interfaces
|
return interfaces
|
||||||
|
|
||||||
|
|
||||||
def get_all_running_processes() -> set[str]:
|
def get_all_running_processes(hass: HomeAssistant) -> set[str]:
|
||||||
"""Return all running processes on system."""
|
"""Return all running processes on system."""
|
||||||
|
psutil_wrapper: ha_psutil = hass.data.get(DOMAIN, ha_psutil.PsutilWrapper())
|
||||||
processes: set[str] = set()
|
processes: set[str] = set()
|
||||||
for proc in psutil.process_iter(["name"]):
|
for proc in psutil_wrapper.psutil.process_iter(["name"]):
|
||||||
if proc.name() not in processes:
|
if proc.name() not in processes:
|
||||||
processes.add(proc.name())
|
processes.add(proc.name())
|
||||||
_LOGGER.debug("Running processes: %s", ", ".join(processes))
|
_LOGGER.debug("Running processes: %s", ", ".join(processes))
|
||||||
return processes
|
return processes
|
||||||
|
|
||||||
|
|
||||||
def read_cpu_temperature(temps: dict[str, list[shwtemp]] | None = None) -> float | None:
|
def read_cpu_temperature(
|
||||||
|
hass: HomeAssistant, temps: dict[str, list[shwtemp]] | None = None
|
||||||
|
) -> float | None:
|
||||||
"""Attempt to read CPU / processor temperature."""
|
"""Attempt to read CPU / processor temperature."""
|
||||||
if not temps:
|
if temps is None:
|
||||||
temps = psutil.sensors_temperatures()
|
psutil_wrapper: ha_psutil = hass.data[DOMAIN]
|
||||||
|
temps = psutil_wrapper.psutil.sensors_temperatures()
|
||||||
entry: shwtemp
|
entry: shwtemp
|
||||||
|
|
||||||
_LOGGER.debug("CPU Temperatures: %s", temps)
|
_LOGGER.debug("CPU Temperatures: %s", temps)
|
||||||
|
|
|
@ -1573,6 +1573,7 @@ proxmoxer==2.0.1
|
||||||
|
|
||||||
# homeassistant.components.hardware
|
# homeassistant.components.hardware
|
||||||
# homeassistant.components.recorder
|
# homeassistant.components.recorder
|
||||||
|
# homeassistant.components.systemmonitor
|
||||||
psutil-home-assistant==0.0.1
|
psutil-home-assistant==0.0.1
|
||||||
|
|
||||||
# homeassistant.components.systemmonitor
|
# homeassistant.components.systemmonitor
|
||||||
|
|
|
@ -1229,6 +1229,7 @@ prometheus-client==0.17.1
|
||||||
|
|
||||||
# homeassistant.components.hardware
|
# homeassistant.components.hardware
|
||||||
# homeassistant.components.recorder
|
# homeassistant.components.recorder
|
||||||
|
# homeassistant.components.systemmonitor
|
||||||
psutil-home-assistant==0.0.1
|
psutil-home-assistant==0.0.1
|
||||||
|
|
||||||
# homeassistant.components.systemmonitor
|
# homeassistant.components.systemmonitor
|
||||||
|
|
|
@ -1,38 +1,20 @@
|
||||||
"""Fixtures for the System Monitor integration."""
|
"""Fixtures for the System Monitor integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
import socket
|
import socket
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, NonCallableMock, patch
|
||||||
|
|
||||||
from psutil import NoSuchProcess, Process
|
from psutil import NoSuchProcess, Process
|
||||||
from psutil._common import sdiskpart, sdiskusage, shwtemp, snetio, snicaddr, sswap
|
from psutil._common import sdiskpart, sdiskusage, shwtemp, snetio, snicaddr, sswap
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.systemmonitor.const import DOMAIN
|
from homeassistant.components.systemmonitor.const import DOMAIN
|
||||||
|
from homeassistant.components.systemmonitor.coordinator import VirtualMemory
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
# Different depending on platform so making according to Linux
|
|
||||||
svmem = namedtuple(
|
|
||||||
"svmem",
|
|
||||||
[
|
|
||||||
"total",
|
|
||||||
"available",
|
|
||||||
"percent",
|
|
||||||
"used",
|
|
||||||
"free",
|
|
||||||
"active",
|
|
||||||
"inactive",
|
|
||||||
"buffers",
|
|
||||||
"cached",
|
|
||||||
"shared",
|
|
||||||
"slab",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def mock_sys_platform() -> Generator[None, None, None]:
|
def mock_sys_platform() -> Generator[None, None, None]:
|
||||||
|
@ -92,7 +74,6 @@ async def mock_added_config_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
) -> MockConfigEntry:
|
) -> MockConfigEntry:
|
||||||
"""Mock ConfigEntry that's been added to HA."""
|
"""Mock ConfigEntry that's been added to HA."""
|
||||||
|
@ -112,30 +93,26 @@ def mock_process() -> list[MockProcess]:
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_psutil(mock_process: list[MockProcess]) -> Mock:
|
def mock_psutil(mock_process: list[MockProcess]) -> Generator:
|
||||||
"""Mock psutil."""
|
"""Mock psutil."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.systemmonitor.coordinator.psutil",
|
"homeassistant.components.systemmonitor.ha_psutil.PsutilWrapper",
|
||||||
autospec=True,
|
) as psutil_wrapper:
|
||||||
) as mock_psutil:
|
_wrapper = psutil_wrapper.return_value
|
||||||
|
_wrapper.psutil = NonCallableMock()
|
||||||
|
mock_psutil = _wrapper.psutil
|
||||||
mock_psutil.disk_usage.return_value = sdiskusage(
|
mock_psutil.disk_usage.return_value = sdiskusage(
|
||||||
500 * 1024**3, 300 * 1024**3, 200 * 1024**3, 60.0
|
500 * 1024**3, 300 * 1024**3, 200 * 1024**3, 60.0
|
||||||
)
|
)
|
||||||
mock_psutil.swap_memory.return_value = sswap(
|
mock_psutil.swap_memory.return_value = sswap(
|
||||||
100 * 1024**2, 60 * 1024**2, 40 * 1024**2, 60.0, 1, 1
|
100 * 1024**2, 60 * 1024**2, 40 * 1024**2, 60.0, 1, 1
|
||||||
)
|
)
|
||||||
mock_psutil.virtual_memory.return_value = svmem(
|
mock_psutil.virtual_memory.return_value = VirtualMemory(
|
||||||
100 * 1024**2,
|
100 * 1024**2,
|
||||||
40 * 1024**2,
|
40 * 1024**2,
|
||||||
40.0,
|
40.0,
|
||||||
60 * 1024**2,
|
60 * 1024**2,
|
||||||
30 * 1024**2,
|
30 * 1024**2,
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
)
|
)
|
||||||
mock_psutil.net_io_counters.return_value = {
|
mock_psutil.net_io_counters.return_value = {
|
||||||
"eth0": snetio(100 * 1024**2, 100 * 1024**2, 50, 50, 0, 0, 0, 0),
|
"eth0": snetio(100 * 1024**2, 100 * 1024**2, 50, 50, 0, 0, 0, 0),
|
||||||
|
@ -180,65 +157,18 @@ def mock_psutil(mock_process: list[MockProcess]) -> Mock:
|
||||||
mock_psutil.sensors_temperatures.return_value = {
|
mock_psutil.sensors_temperatures.return_value = {
|
||||||
"cpu0-thermal": [shwtemp("cpu0-thermal", 50.0, 60.0, 70.0)]
|
"cpu0-thermal": [shwtemp("cpu0-thermal", 50.0, 60.0, 70.0)]
|
||||||
}
|
}
|
||||||
mock_psutil.NoSuchProcess = NoSuchProcess
|
mock_psutil.disk_partitions.return_value = [
|
||||||
yield mock_psutil
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_util(mock_process) -> Mock:
|
|
||||||
"""Mock psutil."""
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.systemmonitor.util.psutil", autospec=True
|
|
||||||
) as mock_util:
|
|
||||||
mock_util.net_if_addrs.return_value = {
|
|
||||||
"eth0": [
|
|
||||||
snicaddr(
|
|
||||||
socket.AF_INET,
|
|
||||||
"192.168.1.1",
|
|
||||||
"255.255.255.0",
|
|
||||||
"255.255.255.255",
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
"eth1": [
|
|
||||||
snicaddr(
|
|
||||||
socket.AF_INET,
|
|
||||||
"192.168.10.1",
|
|
||||||
"255.255.255.0",
|
|
||||||
"255.255.255.255",
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
"vethxyzxyz": [
|
|
||||||
snicaddr(
|
|
||||||
socket.AF_INET,
|
|
||||||
"172.16.10.1",
|
|
||||||
"255.255.255.0",
|
|
||||||
"255.255.255.255",
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
}
|
|
||||||
mock_process = [MockProcess("python3")]
|
|
||||||
mock_util.process_iter.return_value = mock_process
|
|
||||||
# sensors_temperatures not available on MacOS so we
|
|
||||||
# need to override the spec
|
|
||||||
mock_util.sensors_temperatures = Mock()
|
|
||||||
mock_util.sensors_temperatures.return_value = {
|
|
||||||
"cpu0-thermal": [shwtemp("cpu0-thermal", 50.0, 60.0, 70.0)]
|
|
||||||
}
|
|
||||||
mock_util.disk_partitions.return_value = [
|
|
||||||
sdiskpart("test", "/", "ext4", "", 1, 1),
|
sdiskpart("test", "/", "ext4", "", 1, 1),
|
||||||
sdiskpart("test2", "/media/share", "ext4", "", 1, 1),
|
sdiskpart("test2", "/media/share", "ext4", "", 1, 1),
|
||||||
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_util.disk_usage.return_value = sdiskusage(10, 10, 0, 0)
|
mock_psutil.NoSuchProcess = NoSuchProcess
|
||||||
yield mock_util
|
yield mock_psutil
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_os() -> Mock:
|
def mock_os() -> Generator:
|
||||||
"""Mock os."""
|
"""Mock os."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.systemmonitor.coordinator.os"
|
"homeassistant.components.systemmonitor.coordinator.os"
|
||||||
|
|
|
@ -24,7 +24,6 @@ async def test_binary_sensor(
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -65,7 +64,6 @@ async def test_binary_sensor(
|
||||||
async def test_binary_sensor_icon(
|
async def test_binary_sensor_icon(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_util: Mock,
|
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
|
|
|
@ -71,7 +71,6 @@ async def test_migrate_process_sensor_to_binary_sensors(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -28,7 +28,6 @@ async def test_migrate_process_sensor(
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
hass_client: ClientSessionGenerator,
|
hass_client: ClientSessionGenerator,
|
||||||
hass_ws_client: WebSocketGenerator,
|
hass_ws_client: WebSocketGenerator,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
|
|
|
@ -11,6 +11,7 @@ from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.components.systemmonitor.const import DOMAIN
|
from homeassistant.components.systemmonitor.const import DOMAIN
|
||||||
|
from homeassistant.components.systemmonitor.coordinator import VirtualMemory
|
||||||
from homeassistant.components.systemmonitor.sensor import get_cpu_icon
|
from homeassistant.components.systemmonitor.sensor import get_cpu_icon
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN
|
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||||
|
@ -18,7 +19,7 @@ from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from .conftest import MockProcess, svmem
|
from .conftest import MockProcess
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
|
@ -28,7 +29,6 @@ async def test_sensor(
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -82,7 +82,6 @@ async def test_process_sensor_not_loaded(
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -128,7 +127,6 @@ async def test_sensor_not_loading_veth_networks(
|
||||||
async def test_sensor_icon(
|
async def test_sensor_icon(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_util: Mock,
|
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
|
@ -150,7 +148,6 @@ async def test_sensor_yaml(
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the sensor imported from YAML."""
|
"""Test the sensor imported from YAML."""
|
||||||
config = {
|
config = {
|
||||||
|
@ -182,7 +179,6 @@ async def test_sensor_yaml_fails_missing_argument(
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the sensor imported from YAML fails on missing mandatory argument."""
|
"""Test the sensor imported from YAML fails on missing mandatory argument."""
|
||||||
config = {
|
config = {
|
||||||
|
@ -203,7 +199,6 @@ async def test_sensor_updating(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the sensor."""
|
"""Test the sensor."""
|
||||||
|
@ -245,18 +240,12 @@ async def test_sensor_updating(
|
||||||
assert memory_sensor.state == STATE_UNAVAILABLE
|
assert memory_sensor.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
mock_psutil.virtual_memory.side_effect = None
|
mock_psutil.virtual_memory.side_effect = None
|
||||||
mock_psutil.virtual_memory.return_value = svmem(
|
mock_psutil.virtual_memory.return_value = VirtualMemory(
|
||||||
100 * 1024**2,
|
100 * 1024**2,
|
||||||
25 * 1024**2,
|
25 * 1024**2,
|
||||||
25.0,
|
25.0,
|
||||||
60 * 1024**2,
|
60 * 1024**2,
|
||||||
30 * 1024**2,
|
30 * 1024**2,
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
)
|
)
|
||||||
freezer.tick(timedelta(minutes=1))
|
freezer.tick(timedelta(minutes=1))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
|
@ -271,7 +260,6 @@ async def test_sensor_process_fails(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -394,7 +382,6 @@ async def test_sensor_network_sensors(
|
||||||
async def test_missing_cpu_temperature(
|
async def test_missing_cpu_temperature(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_util: Mock,
|
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
|
@ -404,7 +391,7 @@ async def test_missing_cpu_temperature(
|
||||||
mock_psutil.sensors_temperatures.return_value = {
|
mock_psutil.sensors_temperatures.return_value = {
|
||||||
"not_exist": [shwtemp("not_exist", 50.0, 60.0, 70.0)]
|
"not_exist": [shwtemp("not_exist", 50.0, 60.0, 70.0)]
|
||||||
}
|
}
|
||||||
mock_util.sensors_temperatures.return_value = {
|
mock_psutil.sensors_temperatures.return_value = {
|
||||||
"not_exist": [shwtemp("not_exist", 50.0, 60.0, 70.0)]
|
"not_exist": [shwtemp("not_exist", 50.0, 60.0, 70.0)]
|
||||||
}
|
}
|
||||||
mock_config_entry.add_to_hass(hass)
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
@ -419,7 +406,6 @@ async def test_missing_cpu_temperature(
|
||||||
async def test_processor_temperature(
|
async def test_processor_temperature(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_util: Mock,
|
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Test System Monitor utils."""
|
"""Test System Monitor utils."""
|
||||||
|
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from psutil._common import sdiskpart
|
from psutil._common import sdiskpart
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -22,7 +22,6 @@ async def test_disk_setup_failure(
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
side_effect: Exception,
|
side_effect: Exception,
|
||||||
error_text: str,
|
error_text: str,
|
||||||
|
@ -30,18 +29,15 @@ async def test_disk_setup_failure(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the disk failures."""
|
"""Test the disk failures."""
|
||||||
|
|
||||||
with patch(
|
mock_psutil.disk_usage.side_effect = side_effect
|
||||||
"homeassistant.components.systemmonitor.util.psutil.disk_usage",
|
mock_config_entry.add_to_hass(hass)
|
||||||
side_effect=side_effect,
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
):
|
await hass.async_block_till_done()
|
||||||
mock_config_entry.add_to_hass(hass)
|
|
||||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
disk_sensor = hass.states.get("sensor.system_monitor_disk_free_media_share")
|
disk_sensor = hass.states.get("sensor.system_monitor_disk_free_media_share")
|
||||||
assert disk_sensor is None
|
assert disk_sensor is None
|
||||||
|
|
||||||
assert error_text in caplog.text
|
assert error_text in caplog.text
|
||||||
|
|
||||||
|
|
||||||
async def test_disk_util(
|
async def test_disk_util(
|
||||||
|
@ -49,12 +45,11 @@ async def test_disk_util(
|
||||||
entity_registry_enabled_by_default: None,
|
entity_registry_enabled_by_default: None,
|
||||||
mock_psutil: Mock,
|
mock_psutil: Mock,
|
||||||
mock_os: Mock,
|
mock_os: Mock,
|
||||||
mock_util: Mock,
|
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the disk failures."""
|
"""Test the disk failures."""
|
||||||
|
|
||||||
mock_util.disk_partitions.return_value = [
|
mock_psutil.psutil.disk_partitions.return_value = [
|
||||||
sdiskpart("test", "/", "ext4", "", 1, 1), # Should be ok
|
sdiskpart("test", "/", "ext4", "", 1, 1), # Should be ok
|
||||||
sdiskpart("test2", "/media/share", "ext4", "", 1, 1), # Should be ok
|
sdiskpart("test2", "/media/share", "ext4", "", 1, 1), # Should be ok
|
||||||
sdiskpart("test3", "/incorrect", "", "", 1, 1), # Should be skipped as no type
|
sdiskpart("test3", "/incorrect", "", "", 1, 1), # Should be skipped as no type
|
||||||
|
|
Loading…
Add table
Reference in a new issue