mirror of
https://github.com/Electric-Special/ha-core.git
synced 2026-03-21 08:06:00 +01:00
Portainer add dynamic devices (#155304)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -61,30 +61,53 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up Portainer binary sensors."""
|
||||
coordinator = entry.runtime_data
|
||||
entities: list[BinarySensorEntity] = []
|
||||
|
||||
for endpoint in coordinator.data.values():
|
||||
entities.extend(
|
||||
def _async_add_new_endpoints(endpoints: list[PortainerCoordinatorData]) -> None:
|
||||
"""Add new endpoint binary sensors."""
|
||||
async_add_entities(
|
||||
PortainerEndpointSensor(
|
||||
coordinator,
|
||||
entity_description,
|
||||
endpoint,
|
||||
)
|
||||
for entity_description in ENDPOINT_SENSORS
|
||||
for endpoint in endpoints
|
||||
if entity_description.state_fn(endpoint)
|
||||
)
|
||||
|
||||
entities.extend(
|
||||
def _async_add_new_containers(
|
||||
containers: list[tuple[PortainerCoordinatorData, DockerContainer]],
|
||||
) -> None:
|
||||
"""Add new container binary sensors."""
|
||||
async_add_entities(
|
||||
PortainerContainerSensor(
|
||||
coordinator,
|
||||
entity_description,
|
||||
container,
|
||||
endpoint,
|
||||
)
|
||||
for container in endpoint.containers.values()
|
||||
for (endpoint, container) in containers
|
||||
for entity_description in CONTAINER_SENSORS
|
||||
if entity_description.state_fn(container)
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
coordinator.new_endpoints_callbacks.append(_async_add_new_endpoints)
|
||||
coordinator.new_containers_callbacks.append(_async_add_new_containers)
|
||||
|
||||
_async_add_new_endpoints(
|
||||
[
|
||||
endpoint
|
||||
for endpoint in coordinator.data.values()
|
||||
if endpoint.id in coordinator.known_endpoints
|
||||
]
|
||||
)
|
||||
_async_add_new_containers(
|
||||
[
|
||||
(endpoint, container)
|
||||
for endpoint in coordinator.data.values()
|
||||
for container in endpoint.containers.values()
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class PortainerEndpointSensor(PortainerEndpointEntity, BinarySensorEntity):
|
||||
|
||||
@@ -4,7 +4,6 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from pyportainer import Portainer
|
||||
@@ -30,8 +29,6 @@ from .const import DOMAIN
|
||||
from .coordinator import PortainerCoordinator, PortainerCoordinatorData
|
||||
from .entity import PortainerContainerEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class PortainerButtonDescription(ButtonEntityDescription):
|
||||
@@ -64,18 +61,30 @@ async def async_setup_entry(
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Portainer buttons."""
|
||||
coordinator: PortainerCoordinator = entry.runtime_data
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
PortainerButton(
|
||||
coordinator=coordinator,
|
||||
entity_description=entity_description,
|
||||
device_info=container,
|
||||
via_device=endpoint,
|
||||
def _async_add_new_containers(
|
||||
containers: list[tuple[PortainerCoordinatorData, DockerContainer]],
|
||||
) -> None:
|
||||
"""Add new container button sensors."""
|
||||
async_add_entities(
|
||||
PortainerButton(
|
||||
coordinator,
|
||||
entity_description,
|
||||
container,
|
||||
endpoint,
|
||||
)
|
||||
for (endpoint, container) in containers
|
||||
for entity_description in BUTTONS
|
||||
)
|
||||
for endpoint in coordinator.data.values()
|
||||
for container in endpoint.containers.values()
|
||||
for entity_description in BUTTONS
|
||||
|
||||
coordinator.new_containers_callbacks.append(_async_add_new_containers)
|
||||
_async_add_new_containers(
|
||||
[
|
||||
(endpoint, container)
|
||||
for endpoint in coordinator.data.values()
|
||||
for container in endpoint.containers.values()
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
@@ -64,6 +65,16 @@ class PortainerCoordinator(DataUpdateCoordinator[dict[int, PortainerCoordinatorD
|
||||
)
|
||||
self.portainer = portainer
|
||||
|
||||
self.known_endpoints: set[int] = set()
|
||||
self.known_containers: set[tuple[int, str]] = set()
|
||||
|
||||
self.new_endpoints_callbacks: list[
|
||||
Callable[[list[PortainerCoordinatorData]], None]
|
||||
] = []
|
||||
self.new_containers_callbacks: list[
|
||||
Callable[[list[tuple[PortainerCoordinatorData, DockerContainer]]], None]
|
||||
] = []
|
||||
|
||||
async def _async_setup(self) -> None:
|
||||
"""Set up the Portainer Data Update Coordinator."""
|
||||
try:
|
||||
@@ -152,4 +163,27 @@ class PortainerCoordinator(DataUpdateCoordinator[dict[int, PortainerCoordinatorD
|
||||
docker_info=docker_info,
|
||||
)
|
||||
|
||||
self._async_add_remove_endpoints(mapped_endpoints)
|
||||
|
||||
return mapped_endpoints
|
||||
|
||||
def _async_add_remove_endpoints(
|
||||
self, mapped_endpoints: dict[int, PortainerCoordinatorData]
|
||||
) -> None:
|
||||
"""Add new endpoints, remove non-existing endpoints."""
|
||||
current_endpoints = {endpoint.id for endpoint in mapped_endpoints.values()}
|
||||
new_endpoints = current_endpoints - self.known_endpoints
|
||||
if new_endpoints:
|
||||
_LOGGER.debug("New endpoints found: %s", new_endpoints)
|
||||
self.known_endpoints.update(new_endpoints)
|
||||
|
||||
# Surprise, we also handle containers here :)
|
||||
current_containers = {
|
||||
(endpoint.id, container.id)
|
||||
for endpoint in mapped_endpoints.values()
|
||||
for container in endpoint.containers.values()
|
||||
}
|
||||
new_containers = current_containers - self.known_containers
|
||||
if new_containers:
|
||||
_LOGGER.debug("New containers found: %s", new_containers)
|
||||
self.known_containers.update(new_containers)
|
||||
|
||||
@@ -159,30 +159,53 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up Portainer sensors based on a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
entities: list[SensorEntity] = []
|
||||
|
||||
for endpoint in coordinator.data.values():
|
||||
entities.extend(
|
||||
def _async_add_new_endpoints(endpoints: list[PortainerCoordinatorData]) -> None:
|
||||
"""Add new endpoint sensor."""
|
||||
async_add_entities(
|
||||
PortainerEndpointSensor(
|
||||
coordinator,
|
||||
entity_description,
|
||||
endpoint,
|
||||
)
|
||||
for entity_description in ENDPOINT_SENSORS
|
||||
for endpoint in endpoints
|
||||
if entity_description.value_fn(endpoint)
|
||||
)
|
||||
|
||||
entities.extend(
|
||||
def _async_add_new_containers(
|
||||
containers: list[tuple[PortainerCoordinatorData, DockerContainer]],
|
||||
) -> None:
|
||||
"""Add new container sensors."""
|
||||
async_add_entities(
|
||||
PortainerContainerSensor(
|
||||
coordinator,
|
||||
entity_description,
|
||||
container,
|
||||
endpoint,
|
||||
)
|
||||
for container in endpoint.containers.values()
|
||||
for (endpoint, container) in containers
|
||||
for entity_description in CONTAINER_SENSORS
|
||||
if entity_description.value_fn(container)
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
coordinator.new_endpoints_callbacks.append(_async_add_new_endpoints)
|
||||
coordinator.new_containers_callbacks.append(_async_add_new_containers)
|
||||
|
||||
_async_add_new_endpoints(
|
||||
[
|
||||
endpoint
|
||||
for endpoint in coordinator.data.values()
|
||||
if endpoint.id in coordinator.known_endpoints
|
||||
]
|
||||
)
|
||||
_async_add_new_containers(
|
||||
[
|
||||
(endpoint, container)
|
||||
for endpoint in coordinator.data.values()
|
||||
for container in endpoint.containers.values()
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class PortainerContainerSensor(PortainerContainerEntity, SensorEntity):
|
||||
|
||||
@@ -85,19 +85,30 @@ async def async_setup_entry(
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Portainer switch sensors."""
|
||||
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
PortainerContainerSwitch(
|
||||
coordinator=coordinator,
|
||||
entity_description=entity_description,
|
||||
device_info=container,
|
||||
via_device=endpoint,
|
||||
def _async_add_new_containers(
|
||||
containers: list[tuple[PortainerCoordinatorData, DockerContainer]],
|
||||
) -> None:
|
||||
"""Add new container switch sensors."""
|
||||
async_add_entities(
|
||||
PortainerContainerSwitch(
|
||||
coordinator,
|
||||
entity_description,
|
||||
container,
|
||||
endpoint,
|
||||
)
|
||||
for (endpoint, container) in containers
|
||||
for entity_description in SWITCHES
|
||||
)
|
||||
for endpoint in coordinator.data.values()
|
||||
for container in endpoint.containers.values()
|
||||
for entity_description in SWITCHES
|
||||
|
||||
coordinator.new_containers_callbacks.append(_async_add_new_containers)
|
||||
_async_add_new_containers(
|
||||
[
|
||||
(endpoint, container)
|
||||
for endpoint in coordinator.data.values()
|
||||
for container in endpoint.containers.values()
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user