mirror of
https://github.com/home-assistant/core.git
synced 2026-04-17 15:44:52 +01:00
Dynamically add new devices to Libre Hardware Monitor (#165250)
This commit is contained in:
@@ -62,7 +62,9 @@ class LibreHardwareMonitorCoordinator(DataUpdateCoordinator[LibreHardwareMonitor
|
||||
registry=dr.async_get(self.hass), config_entry_id=self._entry_id
|
||||
)
|
||||
self._previous_devices: dict[DeviceId, DeviceName] = {
|
||||
DeviceId(next(iter(device.identifiers))[1]): DeviceName(device.name)
|
||||
DeviceId(
|
||||
next(iter(device.identifiers))[1].removeprefix(f"{self._entry_id}_")
|
||||
): DeviceName(device.name)
|
||||
for device in device_entries
|
||||
if device.identifiers and device.name
|
||||
}
|
||||
@@ -109,11 +111,6 @@ class LibreHardwareMonitorCoordinator(DataUpdateCoordinator[LibreHardwareMonitor
|
||||
self, detected_devices: dict[DeviceId, DeviceName]
|
||||
) -> None:
|
||||
"""Handle device changes by deleting devices from / adding devices to Home Assistant."""
|
||||
detected_devices = {
|
||||
DeviceId(f"{self.config_entry.entry_id}_{detected_id}"): device_name
|
||||
for detected_id, device_name in detected_devices.items()
|
||||
}
|
||||
|
||||
previous_device_ids = set(self._previous_devices.keys())
|
||||
detected_device_ids = set(detected_devices.keys())
|
||||
|
||||
@@ -131,25 +128,14 @@ class LibreHardwareMonitorCoordinator(DataUpdateCoordinator[LibreHardwareMonitor
|
||||
device_registry = dr.async_get(self.hass)
|
||||
for device_id in orphaned_devices:
|
||||
if device := device_registry.async_get_device(
|
||||
identifiers={(DOMAIN, device_id)}
|
||||
identifiers={(DOMAIN, f"{self._entry_id}_{device_id}")}
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"Removing device: %s", self._previous_devices[device_id]
|
||||
)
|
||||
device_registry.async_update_device(
|
||||
device_id=device.id,
|
||||
remove_config_entry_id=self.config_entry.entry_id,
|
||||
remove_config_entry_id=self._entry_id,
|
||||
)
|
||||
|
||||
if self.data is None:
|
||||
# initial update during integration startup
|
||||
self._previous_devices = detected_devices # type: ignore[unreachable]
|
||||
return
|
||||
|
||||
if new_devices := detected_device_ids - previous_device_ids:
|
||||
_LOGGER.warning(
|
||||
"New Device(s) detected, reload integration to add them to Home Assistant: %s",
|
||||
[detected_devices[DeviceId(device_id)] for device_id in new_devices],
|
||||
)
|
||||
|
||||
self._previous_devices = detected_devices
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from librehardwaremonitor_api.model import LibreHardwareMonitorSensorData
|
||||
from librehardwaremonitor_api.model import DeviceId, LibreHardwareMonitorSensorData
|
||||
from librehardwaremonitor_api.sensor_type import SensorType
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorStateClass
|
||||
@@ -16,6 +17,8 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from . import LibreHardwareMonitorConfigEntry, LibreHardwareMonitorCoordinator
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
STATE_MIN_VALUE = "min_value"
|
||||
@@ -30,10 +33,28 @@ async def async_setup_entry(
|
||||
"""Set up the LibreHardwareMonitor platform."""
|
||||
lhm_coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
LibreHardwareMonitorSensor(lhm_coordinator, config_entry.entry_id, sensor_data)
|
||||
for sensor_data in lhm_coordinator.data.sensor_data.values()
|
||||
)
|
||||
known_devices: set[DeviceId] = set()
|
||||
|
||||
def _check_device() -> None:
|
||||
current_devices = set(lhm_coordinator.data.main_device_ids_and_names)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
_LOGGER.debug("New Device(s) detected, adding: %s", new_devices)
|
||||
known_devices.update(new_devices)
|
||||
new_devices_sensor_data = [
|
||||
sensor_data
|
||||
for sensor_data in lhm_coordinator.data.sensor_data.values()
|
||||
if sensor_data.device_id in new_devices
|
||||
]
|
||||
async_add_entities(
|
||||
LibreHardwareMonitorSensor(
|
||||
lhm_coordinator, config_entry.entry_id, sensor_data
|
||||
)
|
||||
for sensor_data in new_devices_sensor_data
|
||||
)
|
||||
|
||||
_check_device()
|
||||
config_entry.async_on_unload(lhm_coordinator.async_add_listener(_check_device))
|
||||
|
||||
|
||||
class LibreHardwareMonitorSensor(
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from dataclasses import replace
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from types import MappingProxyType
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
@@ -16,7 +15,9 @@ from librehardwaremonitor_api.model import (
|
||||
DeviceId,
|
||||
DeviceName,
|
||||
LibreHardwareMonitorData,
|
||||
LibreHardwareMonitorSensorData,
|
||||
)
|
||||
from librehardwaremonitor_api.sensor_type import SensorType
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
@@ -57,7 +58,6 @@ async def test_sensors_are_created(
|
||||
)
|
||||
async def test_sensors_go_unavailable_in_case_of_error_and_recover_after_successful_retry(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_lhm_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
@@ -288,34 +288,66 @@ async def _mock_orphaned_device(
|
||||
)
|
||||
|
||||
|
||||
async def test_integration_does_not_log_new_devices_on_first_refresh(
|
||||
async def test_integration_dynamically_adds_new_devices(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
mock_lhm_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test that initial data update does not cause warning about new devices."""
|
||||
mock_lhm_client.get_data.return_value = LibreHardwareMonitorData(
|
||||
computer_name=mock_lhm_client.get_data.return_value.computer_name,
|
||||
"""Test that new devices are created when detected."""
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
device_entries: list[DeviceEntry] = dr.async_entries_for_config_entry(
|
||||
registry=device_registry, config_entry_id=mock_config_entry.entry_id
|
||||
)
|
||||
assert len(device_entries) == 3
|
||||
|
||||
mock_lhm_client.get_data.return_value = replace(
|
||||
mock_lhm_client.get_data.return_value,
|
||||
main_device_ids_and_names=MappingProxyType(
|
||||
{
|
||||
**mock_lhm_client.get_data.return_value.main_device_ids_and_names,
|
||||
DeviceId("generic-memory"): DeviceName("Generic Memory"),
|
||||
}
|
||||
),
|
||||
sensor_data=mock_lhm_client.get_data.return_value.sensor_data,
|
||||
is_deprecated_version=False,
|
||||
sensor_data=MappingProxyType(
|
||||
{
|
||||
**mock_lhm_client.get_data.return_value.sensor_data,
|
||||
"generic-memory-test-sensor": LibreHardwareMonitorSensorData(
|
||||
name="Test sensor",
|
||||
value="30",
|
||||
type=SensorType.FACTOR,
|
||||
min="12",
|
||||
max="36",
|
||||
unit=None,
|
||||
device_id="generic-memory",
|
||||
device_name="Generic Memory",
|
||||
device_type="MEMORY",
|
||||
sensor_id="generic-memory-test-sensor",
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
await init_integration(hass, mock_config_entry)
|
||||
freezer.tick(timedelta(DEFAULT_SCAN_INTERVAL))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
libre_hardware_monitor_logs = [
|
||||
record
|
||||
for record in caplog.records
|
||||
if record.name.startswith("homeassistant.components.libre_hardware_monitor")
|
||||
]
|
||||
assert len(libre_hardware_monitor_logs) == 0
|
||||
device_entries: list[DeviceEntry] = dr.async_entries_for_config_entry(
|
||||
registry=device_registry, config_entry_id=mock_config_entry.entry_id
|
||||
)
|
||||
assert len(device_entries) == 4
|
||||
expected_device = next(entry for entry in device_entries if "Generic" in entry.name)
|
||||
assert expected_device.name == "[GAMING-PC] Generic Memory"
|
||||
|
||||
entity_entries = er.async_entries_for_config_entry(
|
||||
entity_registry, mock_config_entry.entry_id
|
||||
)
|
||||
assert "sensor.gaming_pc_generic_memory_test_sensor" in [
|
||||
entry.entity_id for entry in entity_entries
|
||||
]
|
||||
|
||||
|
||||
async def test_non_deprecated_version_does_not_raise_issue(
|
||||
|
||||
Reference in New Issue
Block a user