mirror of
https://github.com/home-assistant/core.git
synced 2026-09-28 02:18:10 -04:00
Prepare the breakup of the coordinator in LibreNMS (#182388)
This commit is contained in:
@@ -1,9 +1,23 @@
|
||||
"""The LibreNMS integration."""
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from aiolibrenms import Librenms
|
||||
from aiolibrenms.const import CONNECT_ERRORS
|
||||
from aiolibrenms.exceptions import LibrenmsUnauthenticatedError
|
||||
|
||||
from .coordinator import LibrenmsConfigEntry, LibrenmsDataUpdateCoordinator
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
CONF_SSL,
|
||||
CONF_VERIFY_SSL,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import LibrenmsCentralDataUpdateCoordinator, LibrenmsConfigEntry
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
@@ -11,7 +25,28 @@ PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: LibrenmsConfigEntry) -> bool:
|
||||
"""Set up LibreNMS from a config entry."""
|
||||
|
||||
coordinator = LibrenmsDataUpdateCoordinator(hass, entry)
|
||||
api = Librenms(
|
||||
async_get_clientsession(hass, entry.data[CONF_VERIFY_SSL]),
|
||||
entry.data[CONF_API_KEY],
|
||||
entry.data[CONF_HOST],
|
||||
entry.data[CONF_PORT],
|
||||
entry.data[CONF_SSL],
|
||||
)
|
||||
|
||||
try:
|
||||
await api.system.async_get_system_info()
|
||||
except LibrenmsUnauthenticatedError as err:
|
||||
raise ConfigEntryAuthFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="auth_error",
|
||||
) from err
|
||||
except CONNECT_ERRORS as err:
|
||||
raise ConfigEntryNotReady(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
) from err
|
||||
|
||||
coordinator = LibrenmsCentralDataUpdateCoordinator(hass, entry, api)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from homeassistant.components.binary_sensor import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import LibrenmsConfigEntry, LibrenmsDataUpdateCoordinator
|
||||
from .coordinator import LibrenmsCentralDataUpdateCoordinator, LibrenmsConfigEntry
|
||||
from .entity import LibrenmsDeviceEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -64,7 +64,7 @@ class LibrenmsDeviceBinarySensorEntity(LibrenmsDeviceEntity, BinarySensorEntity)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: LibrenmsDataUpdateCoordinator,
|
||||
coordinator: LibrenmsCentralDataUpdateCoordinator,
|
||||
description: LibrenmsDeviceBinarySensorEntityDescription,
|
||||
device_id: int,
|
||||
) -> None:
|
||||
|
||||
@@ -13,16 +13,10 @@ from aiolibrenms.system.models import LibrenmsSystemInfo
|
||||
from yarl import URL
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
CONF_SSL,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SSL
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DOMAIN
|
||||
@@ -31,30 +25,30 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LibrenmsData:
|
||||
class LibrenmsCentralData:
|
||||
"""Data class for storing data from the API."""
|
||||
|
||||
system: LibrenmsSystemInfo
|
||||
devices: dict[int, LibrenmsDeviceInfo]
|
||||
|
||||
|
||||
type LibrenmsConfigEntry = ConfigEntry[LibrenmsDataUpdateCoordinator]
|
||||
type LibrenmsConfigEntry = ConfigEntry[LibrenmsCentralDataUpdateCoordinator]
|
||||
|
||||
|
||||
class LibrenmsDataUpdateCoordinator(DataUpdateCoordinator[LibrenmsData]):
|
||||
"""Class to manage fetching LibreNMS data."""
|
||||
class LibrenmsBaseDataUpdateCoordinator[T](DataUpdateCoordinator[T]):
|
||||
"""Base class to manage fetching LibreNMS data."""
|
||||
|
||||
config_entry: LibrenmsConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: LibrenmsConfigEntry) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: LibrenmsConfigEntry,
|
||||
api: Librenms,
|
||||
update_interval: timedelta,
|
||||
) -> None:
|
||||
"""Initialize the data update coordinator."""
|
||||
self.api = Librenms(
|
||||
async_get_clientsession(hass, config_entry.data[CONF_VERIFY_SSL]),
|
||||
config_entry.data[CONF_API_KEY],
|
||||
config_entry.data[CONF_HOST],
|
||||
config_entry.data[CONF_PORT],
|
||||
config_entry.data[CONF_SSL],
|
||||
)
|
||||
self.api = api
|
||||
self.configuration_url = str(
|
||||
URL.build(
|
||||
scheme="https" if config_entry.data[CONF_SSL] else "http",
|
||||
@@ -67,27 +61,28 @@ class LibrenmsDataUpdateCoordinator(DataUpdateCoordinator[LibrenmsData]):
|
||||
_LOGGER,
|
||||
config_entry=config_entry,
|
||||
name=DOMAIN,
|
||||
update_interval=update_interval,
|
||||
)
|
||||
|
||||
|
||||
class LibrenmsCentralDataUpdateCoordinator(
|
||||
LibrenmsBaseDataUpdateCoordinator[LibrenmsCentralData]
|
||||
):
|
||||
"""Coordinator to fetch librenms system data and monitored devices meta data."""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: LibrenmsConfigEntry, api: Librenms
|
||||
) -> None:
|
||||
"""Initialize the data update coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
config_entry=config_entry,
|
||||
api=api,
|
||||
update_interval=timedelta(seconds=60),
|
||||
)
|
||||
|
||||
@override
|
||||
async def _async_setup(self) -> None:
|
||||
"""Handle setup of the coordinator."""
|
||||
try:
|
||||
await self.api.system.async_get_system_info()
|
||||
except LibrenmsUnauthenticatedError as err:
|
||||
raise ConfigEntryAuthFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="auth_error",
|
||||
) from err
|
||||
except CONNECT_ERRORS as err:
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
) from err
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> LibrenmsData:
|
||||
async def _async_update_data(self) -> LibrenmsCentralData:
|
||||
"""Update data via internal method."""
|
||||
try:
|
||||
system = await self.api.system.async_get_system_info()
|
||||
@@ -104,4 +99,24 @@ class LibrenmsDataUpdateCoordinator(DataUpdateCoordinator[LibrenmsData]):
|
||||
translation_placeholders={"error": str(err)},
|
||||
) from err
|
||||
|
||||
return LibrenmsData(system, {dev.device_id: dev for dev in devices})
|
||||
device_reg = dr.async_get(self.hass)
|
||||
for device in devices:
|
||||
identifier = f"{self.config_entry.entry_id}_{device.device_id}"
|
||||
sw_version = device.version
|
||||
model = None
|
||||
if device.os != "ping":
|
||||
if sw_version and (feature := device.features) is not None:
|
||||
sw_version += f" ({feature})"
|
||||
model = device.hardware
|
||||
|
||||
device_reg.async_get_or_create(
|
||||
config_entry_id=self.config_entry.entry_id,
|
||||
identifiers={(DOMAIN, identifier)},
|
||||
sw_version=sw_version,
|
||||
configuration_url=f"{self.configuration_url}/device/{device.device_id}",
|
||||
name=device.display,
|
||||
model=model,
|
||||
serial_number=device.serial,
|
||||
)
|
||||
|
||||
return LibrenmsCentralData(system, {dev.device_id: dev for dev in devices})
|
||||
|
||||
@@ -8,39 +8,26 @@ from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import LibrenmsDataUpdateCoordinator
|
||||
from .coordinator import LibrenmsCentralDataUpdateCoordinator
|
||||
|
||||
|
||||
class LibrenmsDeviceEntity(CoordinatorEntity[LibrenmsDataUpdateCoordinator]):
|
||||
class LibrenmsDeviceEntity(CoordinatorEntity[LibrenmsCentralDataUpdateCoordinator]):
|
||||
"""Define LibreNMS device base entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: LibrenmsDataUpdateCoordinator,
|
||||
coordinator: LibrenmsCentralDataUpdateCoordinator,
|
||||
device_id: int,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
self.device_id = device_id
|
||||
|
||||
identifier = f"{coordinator.config_entry.entry_id}_{self.device_id}"
|
||||
sw_version = self._data.version
|
||||
model = None
|
||||
configuration_url = f"{coordinator.configuration_url}/device/{self.device_id}"
|
||||
if self._data.os != "ping":
|
||||
if sw_version and (feature := self._data.features) is not None:
|
||||
sw_version += f" ({feature})"
|
||||
model = self._data.hardware
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, identifier)},
|
||||
sw_version=sw_version,
|
||||
configuration_url=configuration_url,
|
||||
name=self._data.display,
|
||||
model=model,
|
||||
serial_number=self._data.serial,
|
||||
identifiers={
|
||||
(DOMAIN, f"{coordinator.config_entry.entry_id}_{self.device_id}")
|
||||
}
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -55,14 +42,14 @@ class LibrenmsDeviceEntity(CoordinatorEntity[LibrenmsDataUpdateCoordinator]):
|
||||
return self.coordinator.data.devices[self.device_id]
|
||||
|
||||
|
||||
class LibrenmsSystemEntity(CoordinatorEntity[LibrenmsDataUpdateCoordinator]):
|
||||
class LibrenmsSystemEntity(CoordinatorEntity[LibrenmsCentralDataUpdateCoordinator]):
|
||||
"""Define LibreNMS base entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: LibrenmsDataUpdateCoordinator,
|
||||
coordinator: LibrenmsCentralDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
@@ -15,9 +15,9 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .coordinator import (
|
||||
LibrenmsCentralData,
|
||||
LibrenmsCentralDataUpdateCoordinator,
|
||||
LibrenmsConfigEntry,
|
||||
LibrenmsData,
|
||||
LibrenmsDataUpdateCoordinator,
|
||||
)
|
||||
from .entity import LibrenmsSystemEntity
|
||||
|
||||
@@ -29,8 +29,8 @@ PARALLEL_UPDATES = 0
|
||||
class LibrenmsSystemSensorEntityDescription(SensorEntityDescription):
|
||||
"""Librenms system sensor entity description."""
|
||||
|
||||
value: Callable[[LibrenmsData], StateType]
|
||||
is_suitable: Callable[[LibrenmsData], bool] = lambda _: True
|
||||
value: Callable[[LibrenmsCentralData], StateType]
|
||||
is_suitable: Callable[[LibrenmsCentralData], bool] = lambda _: True
|
||||
|
||||
|
||||
SYSTEM_SENSOR_TYPES: tuple[LibrenmsSystemSensorEntityDescription, ...] = (
|
||||
@@ -99,7 +99,7 @@ class LibrenmsSystemSensorEntity(LibrenmsSystemEntity, SensorEntity):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: LibrenmsDataUpdateCoordinator,
|
||||
coordinator: LibrenmsCentralDataUpdateCoordinator,
|
||||
description: LibrenmsSystemSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
|
||||
@@ -58,7 +58,7 @@ async def mock_librenms(
|
||||
"""Mock the LibreNMS API."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.librenms.coordinator.Librenms", autospec=True
|
||||
"homeassistant.components.librenms.Librenms", autospec=True
|
||||
) as mock_librenms,
|
||||
patch(
|
||||
"homeassistant.components.librenms.config_flow.Librenms", new=mock_librenms
|
||||
|
||||
@@ -1,6 +1,118 @@
|
||||
# serializer version: 1
|
||||
# name: test_sensors.12
|
||||
list([
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'configuration_url': 'https://librenms/device/1',
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'librenms',
|
||||
'01KXX1E2EMMSCDQ2K4A0C7JA9T_1',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': None,
|
||||
'model': 'SFVH_KV01_SFOS',
|
||||
'model_id': None,
|
||||
'name': 'SophosXG',
|
||||
'name_by_user': None,
|
||||
'serial_number': 'C01yyyyyyxxxxxx',
|
||||
'sw_version': '22.0.1 MR-1-Build490',
|
||||
'via_device_id': None,
|
||||
}),
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'configuration_url': 'https://librenms/device/3',
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'librenms',
|
||||
'01KXX1E2EMMSCDQ2K4A0C7JA9T_3',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': None,
|
||||
'model': 'MC2425adw',
|
||||
'model_id': None,
|
||||
'name': 'Drucker',
|
||||
'name_by_user': None,
|
||||
'serial_number': '7529936145YFW',
|
||||
'sw_version': 'CXNZJ.250.038',
|
||||
'via_device_id': None,
|
||||
}),
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'configuration_url': 'https://librenms/device/13',
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'librenms',
|
||||
'01KXX1E2EMMSCDQ2K4A0C7JA9T_13',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': None,
|
||||
'model': 'QEMU Standard PC (i440FX + PIIX, 1996)',
|
||||
'model_id': None,
|
||||
'name': 'homeassistant',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'sw_version': '6.18.37-haos (Home Assistant OS 18.1)',
|
||||
'via_device_id': None,
|
||||
}),
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'configuration_url': 'https://librenms/device/29',
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'librenms',
|
||||
'01KXX1E2EMMSCDQ2K4A0C7JA9T_29',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': None,
|
||||
'model': None,
|
||||
'model_id': None,
|
||||
'name': 'firmware.esphome.io (104.21.87.21)',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
}),
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entry_id': <ANY>,
|
||||
|
||||
Reference in New Issue
Block a user