Remove deprecated process sensor from System monitor (#123616)
This commit is contained in:
parent
5b6bfa9ac8
commit
6343a086e4
10 changed files with 60 additions and 430 deletions
|
@ -73,7 +73,11 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Migrate old entry."""
|
"""Migrate old entry."""
|
||||||
|
|
||||||
if entry.version == 1:
|
if entry.version > 1:
|
||||||
|
# This means the user has downgraded from a future version
|
||||||
|
return False
|
||||||
|
|
||||||
|
if entry.version == 1 and entry.minor_version < 3:
|
||||||
new_options = {**entry.options}
|
new_options = {**entry.options}
|
||||||
if entry.minor_version == 1:
|
if entry.minor_version == 1:
|
||||||
# Migration copies process sensors to binary sensors
|
# Migration copies process sensors to binary sensors
|
||||||
|
@ -84,6 +88,14 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
entry, options=new_options, version=1, minor_version=2
|
entry, options=new_options, version=1, minor_version=2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if entry.minor_version == 2:
|
||||||
|
new_options = {**entry.options}
|
||||||
|
if SENSOR_DOMAIN in new_options:
|
||||||
|
new_options.pop(SENSOR_DOMAIN)
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry, options=new_options, version=1, minor_version=3
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Migration to version %s.%s successful", entry.version, entry.minor_version
|
"Migration to version %s.%s successful", entry.version, entry.minor_version
|
||||||
)
|
)
|
||||||
|
|
|
@ -95,7 +95,7 @@ class SystemMonitorConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN):
|
||||||
config_flow = CONFIG_FLOW
|
config_flow = CONFIG_FLOW
|
||||||
options_flow = OPTIONS_FLOW
|
options_flow = OPTIONS_FLOW
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
MINOR_VERSION = 2
|
MINOR_VERSION = 3
|
||||||
|
|
||||||
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
||||||
"""Return config entry title."""
|
"""Return config entry title."""
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
"""Repairs platform for the System Monitor integration."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any, cast
|
|
||||||
|
|
||||||
from homeassistant import data_entry_flow
|
|
||||||
from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow
|
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
|
||||||
from homeassistant.helpers import entity_registry as er
|
|
||||||
|
|
||||||
|
|
||||||
class ProcessFixFlow(RepairsFlow):
|
|
||||||
"""Handler for an issue fixing flow."""
|
|
||||||
|
|
||||||
def __init__(self, entry: ConfigEntry, processes: list[str]) -> None:
|
|
||||||
"""Create flow."""
|
|
||||||
super().__init__()
|
|
||||||
self.entry = entry
|
|
||||||
self._processes = processes
|
|
||||||
|
|
||||||
async def async_step_init(
|
|
||||||
self, user_input: dict[str, str] | None = None
|
|
||||||
) -> data_entry_flow.FlowResult:
|
|
||||||
"""Handle the first step of a fix flow."""
|
|
||||||
return await self.async_step_migrate_process_sensor()
|
|
||||||
|
|
||||||
async def async_step_migrate_process_sensor(
|
|
||||||
self, user_input: dict[str, Any] | None = None
|
|
||||||
) -> data_entry_flow.FlowResult:
|
|
||||||
"""Handle the options step of a fix flow."""
|
|
||||||
if user_input is None:
|
|
||||||
return self.async_show_form(
|
|
||||||
step_id="migrate_process_sensor",
|
|
||||||
description_placeholders={"processes": ", ".join(self._processes)},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Migration has copied the sensors to binary sensors
|
|
||||||
# Pop the sensors to repair and remove entities
|
|
||||||
new_options: dict[str, Any] = self.entry.options.copy()
|
|
||||||
new_options.pop(SENSOR_DOMAIN)
|
|
||||||
|
|
||||||
entity_reg = er.async_get(self.hass)
|
|
||||||
entries = er.async_entries_for_config_entry(entity_reg, self.entry.entry_id)
|
|
||||||
for entry in entries:
|
|
||||||
if entry.entity_id.startswith("sensor.") and entry.unique_id.startswith(
|
|
||||||
"process_"
|
|
||||||
):
|
|
||||||
entity_reg.async_remove(entry.entity_id)
|
|
||||||
|
|
||||||
self.hass.config_entries.async_update_entry(self.entry, options=new_options)
|
|
||||||
await self.hass.config_entries.async_reload(self.entry.entry_id)
|
|
||||||
return self.async_create_entry(data={})
|
|
||||||
|
|
||||||
|
|
||||||
async def async_create_fix_flow(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
issue_id: str,
|
|
||||||
data: dict[str, Any] | None,
|
|
||||||
) -> RepairsFlow:
|
|
||||||
"""Create flow."""
|
|
||||||
entry = None
|
|
||||||
if data and (entry_id := data.get("entry_id")):
|
|
||||||
entry_id = cast(str, entry_id)
|
|
||||||
processes: list[str] = data["processes"]
|
|
||||||
entry = hass.config_entries.async_get_entry(entry_id)
|
|
||||||
assert entry
|
|
||||||
return ProcessFixFlow(entry, processes)
|
|
||||||
|
|
||||||
return ConfirmRepairFlow()
|
|
|
@ -14,8 +14,6 @@ import sys
|
||||||
import time
|
import time
|
||||||
from typing import Any, Literal
|
from typing import Any, Literal
|
||||||
|
|
||||||
from psutil import NoSuchProcess
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
DOMAIN as SENSOR_DOMAIN,
|
DOMAIN as SENSOR_DOMAIN,
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
|
@ -25,8 +23,6 @@ from homeassistant.components.sensor import (
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
STATE_OFF,
|
|
||||||
STATE_ON,
|
|
||||||
EntityCategory,
|
EntityCategory,
|
||||||
UnitOfDataRate,
|
UnitOfDataRate,
|
||||||
UnitOfInformation,
|
UnitOfInformation,
|
||||||
|
@ -36,13 +32,12 @@ from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
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 . import SystemMonitorConfigEntry
|
from . import SystemMonitorConfigEntry
|
||||||
from .const import CONF_PROCESS, DOMAIN, NET_IO_TYPES
|
from .const import DOMAIN, NET_IO_TYPES
|
||||||
from .coordinator import SystemMonitorCoordinator
|
from .coordinator import SystemMonitorCoordinator
|
||||||
from .util import get_all_disk_mounts, get_all_network_interfaces, read_cpu_temperature
|
from .util import get_all_disk_mounts, get_all_network_interfaces, read_cpu_temperature
|
||||||
|
|
||||||
|
@ -68,24 +63,6 @@ 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) -> str:
|
|
||||||
"""Return process."""
|
|
||||||
state = STATE_OFF
|
|
||||||
for proc in entity.coordinator.data.processes:
|
|
||||||
try:
|
|
||||||
_LOGGER.debug("process %s for argument %s", proc.name(), entity.argument)
|
|
||||||
if entity.argument == proc.name():
|
|
||||||
state = STATE_ON
|
|
||||||
break
|
|
||||||
except NoSuchProcess as err:
|
|
||||||
_LOGGER.warning(
|
|
||||||
"Failed to load process with ID: %s, old name: %s",
|
|
||||||
err.pid,
|
|
||||||
err.name,
|
|
||||||
)
|
|
||||||
return state
|
|
||||||
|
|
||||||
|
|
||||||
def get_network(entity: SystemMonitorSensor) -> float | None:
|
def get_network(entity: SystemMonitorSensor) -> float | None:
|
||||||
"""Return network in and out."""
|
"""Return network in and out."""
|
||||||
counters = entity.coordinator.data.io_counters
|
counters = entity.coordinator.data.io_counters
|
||||||
|
@ -341,15 +318,6 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = {
|
||||||
value_fn=get_throughput,
|
value_fn=get_throughput,
|
||||||
add_to_update=lambda entity: ("io_counters", ""),
|
add_to_update=lambda entity: ("io_counters", ""),
|
||||||
),
|
),
|
||||||
"process": SysMonitorSensorEntityDescription(
|
|
||||||
key="process",
|
|
||||||
translation_key="process",
|
|
||||||
placeholder="process",
|
|
||||||
icon=get_cpu_icon(),
|
|
||||||
mandatory_arg=True,
|
|
||||||
value_fn=get_process,
|
|
||||||
add_to_update=lambda entity: ("processes", ""),
|
|
||||||
),
|
|
||||||
"processor_use": SysMonitorSensorEntityDescription(
|
"processor_use": SysMonitorSensorEntityDescription(
|
||||||
key="processor_use",
|
key="processor_use",
|
||||||
translation_key="processor_use",
|
translation_key="processor_use",
|
||||||
|
@ -551,35 +519,6 @@ async def async_setup_entry(
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if _type == "process":
|
|
||||||
_entry = entry.options.get(SENSOR_DOMAIN, {})
|
|
||||||
for argument in _entry.get(CONF_PROCESS, []):
|
|
||||||
loaded_resources.add(slugify(f"{_type}_{argument}"))
|
|
||||||
entities.append(
|
|
||||||
SystemMonitorSensor(
|
|
||||||
coordinator,
|
|
||||||
sensor_description,
|
|
||||||
entry.entry_id,
|
|
||||||
argument,
|
|
||||||
True,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
async_create_issue(
|
|
||||||
hass,
|
|
||||||
DOMAIN,
|
|
||||||
"process_sensor",
|
|
||||||
breaks_in_ha_version="2024.9.0",
|
|
||||||
is_fixable=True,
|
|
||||||
is_persistent=False,
|
|
||||||
severity=IssueSeverity.WARNING,
|
|
||||||
translation_key="process_sensor",
|
|
||||||
data={
|
|
||||||
"entry_id": entry.entry_id,
|
|
||||||
"processes": _entry[CONF_PROCESS],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if _type == "processor_use":
|
if _type == "processor_use":
|
||||||
argument = ""
|
argument = ""
|
||||||
is_enabled = check_legacy_resource(f"{_type}_{argument}", legacy_resources)
|
is_enabled = check_legacy_resource(f"{_type}_{argument}", legacy_resources)
|
||||||
|
|
|
@ -22,19 +22,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"issues": {
|
|
||||||
"process_sensor": {
|
|
||||||
"title": "Process sensors are deprecated and will be removed",
|
|
||||||
"fix_flow": {
|
|
||||||
"step": {
|
|
||||||
"migrate_process_sensor": {
|
|
||||||
"title": "Process sensors have been setup as binary sensors",
|
|
||||||
"description": "Process sensors `{processes}` have been created as binary sensors and the sensors will be removed in 2024.9.0.\n\nPlease update all automations, scripts, dashboards or other things depending on these sensors to use the newly created binary sensors instead and press **Submit** to fix this issue."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"entity": {
|
"entity": {
|
||||||
"binary_sensor": {
|
"binary_sensor": {
|
||||||
"process": {
|
"process": {
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
}),
|
}),
|
||||||
'disabled_by': None,
|
'disabled_by': None,
|
||||||
'domain': 'systemmonitor',
|
'domain': 'systemmonitor',
|
||||||
'minor_version': 2,
|
'minor_version': 3,
|
||||||
'options': dict({
|
'options': dict({
|
||||||
'binary_sensor': dict({
|
'binary_sensor': dict({
|
||||||
'process': list([
|
'process': list([
|
||||||
|
|
|
@ -300,24 +300,6 @@
|
||||||
# name: test_sensor[System Monitor Packets out eth1 - state]
|
# name: test_sensor[System Monitor Packets out eth1 - state]
|
||||||
'150'
|
'150'
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensor[System Monitor Process pip - attributes]
|
|
||||||
ReadOnlyDict({
|
|
||||||
'friendly_name': 'System Monitor Process pip',
|
|
||||||
'icon': 'mdi:cpu-64-bit',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_sensor[System Monitor Process pip - state]
|
|
||||||
'on'
|
|
||||||
# ---
|
|
||||||
# name: test_sensor[System Monitor Process python3 - attributes]
|
|
||||||
ReadOnlyDict({
|
|
||||||
'friendly_name': 'System Monitor Process python3',
|
|
||||||
'icon': 'mdi:cpu-64-bit',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_sensor[System Monitor Process python3 - state]
|
|
||||||
'on'
|
|
||||||
# ---
|
|
||||||
# name: test_sensor[System Monitor Processor temperature - attributes]
|
# name: test_sensor[System Monitor Processor temperature - attributes]
|
||||||
ReadOnlyDict({
|
ReadOnlyDict({
|
||||||
'device_class': 'temperature',
|
'device_class': 'temperature',
|
||||||
|
|
|
@ -95,9 +95,49 @@ async def test_migrate_process_sensor_to_binary_sensors(
|
||||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
|
||||||
assert process_sensor is not None
|
|
||||||
assert process_sensor.state == STATE_ON
|
|
||||||
process_sensor = hass.states.get("binary_sensor.system_monitor_process_python3")
|
process_sensor = hass.states.get("binary_sensor.system_monitor_process_python3")
|
||||||
assert process_sensor is not None
|
assert process_sensor is not None
|
||||||
assert process_sensor.state == STATE_ON
|
assert process_sensor.state == STATE_ON
|
||||||
|
|
||||||
|
assert mock_config_entry.minor_version == 3
|
||||||
|
assert mock_config_entry.options == {
|
||||||
|
"binary_sensor": {"process": ["python3", "pip"]},
|
||||||
|
"resources": [
|
||||||
|
"disk_use_percent_/",
|
||||||
|
"disk_use_percent_/home/notexist/",
|
||||||
|
"memory_free_",
|
||||||
|
"network_out_eth0",
|
||||||
|
"process_python3",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migration_from_future_version(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_psutil: Mock,
|
||||||
|
mock_os: Mock,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test migration from future version."""
|
||||||
|
mock_config_entry = MockConfigEntry(
|
||||||
|
title="System Monitor",
|
||||||
|
domain=DOMAIN,
|
||||||
|
version=2,
|
||||||
|
data={},
|
||||||
|
options={
|
||||||
|
"sensor": {"process": ["python3", "pip"]},
|
||||||
|
"resources": [
|
||||||
|
"disk_use_percent_/",
|
||||||
|
"disk_use_percent_/home/notexist/",
|
||||||
|
"memory_free_",
|
||||||
|
"network_out_eth0",
|
||||||
|
"process_python3",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert mock_config_entry.state is ConfigEntryState.MIGRATION_ERROR
|
||||||
|
|
|
@ -1,199 +0,0 @@
|
||||||
"""Test repairs for System Monitor."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from http import HTTPStatus
|
|
||||||
from unittest.mock import Mock
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from syrupy.assertion import SnapshotAssertion
|
|
||||||
|
|
||||||
from homeassistant.components.repairs.websocket_api import (
|
|
||||||
RepairsFlowIndexView,
|
|
||||||
RepairsFlowResourceView,
|
|
||||||
)
|
|
||||||
from homeassistant.components.systemmonitor.const import DOMAIN
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
|
||||||
from homeassistant.helpers import entity_registry as er, issue_registry as ir
|
|
||||||
from homeassistant.setup import async_setup_component
|
|
||||||
|
|
||||||
from tests.common import ANY, MockConfigEntry
|
|
||||||
from tests.typing import ClientSessionGenerator, WebSocketGenerator
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
|
||||||
async def test_migrate_process_sensor(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
entity_registry: er.EntityRegistry,
|
|
||||||
mock_psutil: Mock,
|
|
||||||
mock_os: Mock,
|
|
||||||
hass_client: ClientSessionGenerator,
|
|
||||||
hass_ws_client: WebSocketGenerator,
|
|
||||||
snapshot: SnapshotAssertion,
|
|
||||||
) -> None:
|
|
||||||
"""Test migrating process sensor to binary sensor."""
|
|
||||||
mock_config_entry = MockConfigEntry(
|
|
||||||
title="System Monitor",
|
|
||||||
domain=DOMAIN,
|
|
||||||
data={},
|
|
||||||
options={
|
|
||||||
"binary_sensor": {"process": ["python3", "pip"]},
|
|
||||||
"sensor": {"process": ["python3", "pip"]},
|
|
||||||
"resources": [
|
|
||||||
"disk_use_percent_/",
|
|
||||||
"disk_use_percent_/home/notexist/",
|
|
||||||
"memory_free_",
|
|
||||||
"network_out_eth0",
|
|
||||||
"process_python3",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
mock_config_entry.add_to_hass(hass)
|
|
||||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert hass.config_entries.async_entries(DOMAIN) == snapshot(
|
|
||||||
name="before_migration"
|
|
||||||
)
|
|
||||||
|
|
||||||
assert await async_setup_component(hass, "repairs", {})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
entity = "sensor.system_monitor_process_python3"
|
|
||||||
state = hass.states.get(entity)
|
|
||||||
assert state
|
|
||||||
|
|
||||||
assert entity_registry.async_get(entity)
|
|
||||||
|
|
||||||
ws_client = await hass_ws_client(hass)
|
|
||||||
client = await hass_client()
|
|
||||||
|
|
||||||
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
|
|
||||||
msg = await ws_client.receive_json()
|
|
||||||
|
|
||||||
assert msg["success"]
|
|
||||||
assert len(msg["result"]["issues"]) > 0
|
|
||||||
issue = None
|
|
||||||
for i in msg["result"]["issues"]:
|
|
||||||
if i["issue_id"] == "process_sensor":
|
|
||||||
issue = i
|
|
||||||
assert issue is not None
|
|
||||||
|
|
||||||
url = RepairsFlowIndexView.url
|
|
||||||
resp = await client.post(
|
|
||||||
url, json={"handler": DOMAIN, "issue_id": "process_sensor"}
|
|
||||||
)
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
data = await resp.json()
|
|
||||||
|
|
||||||
flow_id = data["flow_id"]
|
|
||||||
assert data["step_id"] == "migrate_process_sensor"
|
|
||||||
|
|
||||||
url = RepairsFlowResourceView.url.format(flow_id=flow_id)
|
|
||||||
resp = await client.post(url, json={})
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
data = await resp.json()
|
|
||||||
|
|
||||||
# Cannot use identity `is` check here as the value is parsed from JSON
|
|
||||||
assert data["type"] == FlowResultType.CREATE_ENTRY.value
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("binary_sensor.system_monitor_process_python3")
|
|
||||||
assert state
|
|
||||||
|
|
||||||
await ws_client.send_json({"id": 2, "type": "repairs/list_issues"})
|
|
||||||
msg = await ws_client.receive_json()
|
|
||||||
|
|
||||||
assert msg["success"]
|
|
||||||
issue = None
|
|
||||||
for i in msg["result"]["issues"]:
|
|
||||||
if i["issue_id"] == "migrate_process_sensor":
|
|
||||||
issue = i
|
|
||||||
assert not issue
|
|
||||||
|
|
||||||
entity = "sensor.system_monitor_process_python3"
|
|
||||||
state = hass.states.get(entity)
|
|
||||||
assert not state
|
|
||||||
|
|
||||||
assert not entity_registry.async_get(entity)
|
|
||||||
|
|
||||||
assert hass.config_entries.async_entries(DOMAIN) == snapshot(name="after_migration")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
|
||||||
async def test_other_fixable_issues(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
hass_client: ClientSessionGenerator,
|
|
||||||
hass_ws_client: WebSocketGenerator,
|
|
||||||
mock_added_config_entry: ConfigEntry,
|
|
||||||
) -> None:
|
|
||||||
"""Test fixing other issues."""
|
|
||||||
assert await async_setup_component(hass, "repairs", {})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
ws_client = await hass_ws_client(hass)
|
|
||||||
client = await hass_client()
|
|
||||||
|
|
||||||
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
|
|
||||||
msg = await ws_client.receive_json()
|
|
||||||
|
|
||||||
assert msg["success"]
|
|
||||||
|
|
||||||
issue = {
|
|
||||||
"breaks_in_ha_version": "2022.9.0dev0",
|
|
||||||
"domain": DOMAIN,
|
|
||||||
"issue_id": "issue_1",
|
|
||||||
"is_fixable": True,
|
|
||||||
"learn_more_url": "",
|
|
||||||
"severity": "error",
|
|
||||||
"translation_key": "issue_1",
|
|
||||||
}
|
|
||||||
ir.async_create_issue(
|
|
||||||
hass,
|
|
||||||
issue["domain"],
|
|
||||||
issue["issue_id"],
|
|
||||||
breaks_in_ha_version=issue["breaks_in_ha_version"],
|
|
||||||
is_fixable=issue["is_fixable"],
|
|
||||||
is_persistent=False,
|
|
||||||
learn_more_url=None,
|
|
||||||
severity=issue["severity"],
|
|
||||||
translation_key=issue["translation_key"],
|
|
||||||
)
|
|
||||||
|
|
||||||
await ws_client.send_json({"id": 2, "type": "repairs/list_issues"})
|
|
||||||
msg = await ws_client.receive_json()
|
|
||||||
|
|
||||||
assert msg["success"]
|
|
||||||
results = msg["result"]["issues"]
|
|
||||||
assert {
|
|
||||||
"breaks_in_ha_version": "2022.9.0dev0",
|
|
||||||
"created": ANY,
|
|
||||||
"dismissed_version": None,
|
|
||||||
"domain": DOMAIN,
|
|
||||||
"is_fixable": True,
|
|
||||||
"issue_domain": None,
|
|
||||||
"issue_id": "issue_1",
|
|
||||||
"learn_more_url": None,
|
|
||||||
"severity": "error",
|
|
||||||
"translation_key": "issue_1",
|
|
||||||
"translation_placeholders": None,
|
|
||||||
"ignored": False,
|
|
||||||
} in results
|
|
||||||
|
|
||||||
url = RepairsFlowIndexView.url
|
|
||||||
resp = await client.post(url, json={"handler": DOMAIN, "issue_id": "issue_1"})
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
data = await resp.json()
|
|
||||||
|
|
||||||
flow_id = data["flow_id"]
|
|
||||||
assert data["step_id"] == "confirm"
|
|
||||||
|
|
||||||
url = RepairsFlowResourceView.url.format(flow_id=flow_id)
|
|
||||||
resp = await client.post(url)
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
data = await resp.json()
|
|
||||||
|
|
||||||
# Cannot use identity `is` check here as the value is parsed from JSON
|
|
||||||
assert data["type"] == FlowResultType.CREATE_ENTRY.value
|
|
||||||
await hass.async_block_till_done()
|
|
|
@ -14,12 +14,10 @@ from homeassistant.components.systemmonitor.const import DOMAIN
|
||||||
from homeassistant.components.systemmonitor.coordinator import VirtualMemory
|
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_UNAVAILABLE, STATE_UNKNOWN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from .conftest import MockProcess
|
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +36,6 @@ async def test_sensor(
|
||||||
data={},
|
data={},
|
||||||
options={
|
options={
|
||||||
"binary_sensor": {"process": ["python3", "pip"]},
|
"binary_sensor": {"process": ["python3", "pip"]},
|
||||||
"sensor": {"process": ["python3", "pip"]},
|
|
||||||
"resources": [
|
"resources": [
|
||||||
"disk_use_percent_/",
|
"disk_use_percent_/",
|
||||||
"disk_use_percent_/home/notexist/",
|
"disk_use_percent_/home/notexist/",
|
||||||
|
@ -62,10 +59,6 @@ async def test_sensor(
|
||||||
"friendly_name": "System Monitor Memory free",
|
"friendly_name": "System Monitor Memory free",
|
||||||
}
|
}
|
||||||
|
|
||||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
|
||||||
assert process_sensor is not None
|
|
||||||
assert process_sensor.state == STATE_ON
|
|
||||||
|
|
||||||
for entity in er.async_entries_for_config_entry(
|
for entity in er.async_entries_for_config_entry(
|
||||||
entity_registry, mock_config_entry.entry_id
|
entity_registry, mock_config_entry.entry_id
|
||||||
):
|
):
|
||||||
|
@ -154,7 +147,6 @@ async def test_sensor_updating(
|
||||||
data={},
|
data={},
|
||||||
options={
|
options={
|
||||||
"binary_sensor": {"process": ["python3", "pip"]},
|
"binary_sensor": {"process": ["python3", "pip"]},
|
||||||
"sensor": {"process": ["python3", "pip"]},
|
|
||||||
"resources": [
|
"resources": [
|
||||||
"disk_use_percent_/",
|
"disk_use_percent_/",
|
||||||
"disk_use_percent_/home/notexist/",
|
"disk_use_percent_/home/notexist/",
|
||||||
|
@ -172,10 +164,6 @@ async def test_sensor_updating(
|
||||||
assert memory_sensor is not None
|
assert memory_sensor is not None
|
||||||
assert memory_sensor.state == "40.0"
|
assert memory_sensor.state == "40.0"
|
||||||
|
|
||||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
|
||||||
assert process_sensor is not None
|
|
||||||
assert process_sensor.state == STATE_ON
|
|
||||||
|
|
||||||
mock_psutil.virtual_memory.side_effect = Exception("Failed to update")
|
mock_psutil.virtual_memory.side_effect = Exception("Failed to update")
|
||||||
freezer.tick(timedelta(minutes=1))
|
freezer.tick(timedelta(minutes=1))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
|
@ -202,53 +190,6 @@ async def test_sensor_updating(
|
||||||
assert memory_sensor.state == "25.0"
|
assert memory_sensor.state == "25.0"
|
||||||
|
|
||||||
|
|
||||||
async def test_sensor_process_fails(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
mock_psutil: Mock,
|
|
||||||
mock_os: Mock,
|
|
||||||
freezer: FrozenDateTimeFactory,
|
|
||||||
caplog: pytest.LogCaptureFixture,
|
|
||||||
) -> None:
|
|
||||||
"""Test process not exist failure."""
|
|
||||||
mock_config_entry = MockConfigEntry(
|
|
||||||
title="System Monitor",
|
|
||||||
domain=DOMAIN,
|
|
||||||
data={},
|
|
||||||
options={
|
|
||||||
"binary_sensor": {"process": ["python3", "pip"]},
|
|
||||||
"sensor": {"process": ["python3", "pip"]},
|
|
||||||
"resources": [
|
|
||||||
"disk_use_percent_/",
|
|
||||||
"disk_use_percent_/home/notexist/",
|
|
||||||
"memory_free_",
|
|
||||||
"network_out_eth0",
|
|
||||||
"process_python3",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
mock_config_entry.add_to_hass(hass)
|
|
||||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
|
||||||
assert process_sensor is not None
|
|
||||||
assert process_sensor.state == STATE_ON
|
|
||||||
|
|
||||||
_process = MockProcess("python3", True)
|
|
||||||
|
|
||||||
mock_psutil.process_iter.return_value = [_process]
|
|
||||||
|
|
||||||
freezer.tick(timedelta(minutes=1))
|
|
||||||
async_fire_time_changed(hass)
|
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
|
||||||
|
|
||||||
process_sensor = hass.states.get("sensor.system_monitor_process_python3")
|
|
||||||
assert process_sensor is not None
|
|
||||||
assert process_sensor.state == STATE_OFF
|
|
||||||
|
|
||||||
assert "Failed to load process with ID: 1, old name: python3" in caplog.text
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
async def test_sensor_network_sensors(
|
async def test_sensor_network_sensors(
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue