Add storages to Proxmox (#166409)

This commit is contained in:
Erwin Douna authored and GitHub committed 2026-03-25 16:11:41 +01:00
1 parent c598aa6964
commit cd0834bfbe
12 files changed
+1158 -11

No files matched your search

@@ -15,9 +15,21 @@ from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import NODE_ONLINE, STATUS_OK, VM_CONTAINER_RUNNING
from .const import (
NODE_ONLINE,
STATUS_OK,
STORAGE_ACTIVE,
STORAGE_ENABLED,
STORAGE_SHARED,
VM_CONTAINER_RUNNING,
)
from .coordinator import ProxmoxConfigEntry, ProxmoxNodeData
from .entity import ProxmoxContainerEntity, ProxmoxNodeEntity, ProxmoxVMEntity
from .entity import (
ProxmoxContainerEntity,
ProxmoxNodeEntity,
ProxmoxStorageEntity,
ProxmoxVMEntity,
)
PARALLEL_UPDATES = 0
@@ -43,6 +55,13 @@ class ProxmoxNodeBinarySensorEntityDescription(BinarySensorEntityDescription):
state_fn: Callable[[ProxmoxNodeData], bool | None]
@dataclass(frozen=True, kw_only=True)
class ProxmoxStorageBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Class to hold Proxmox storage binary sensor description."""
state_fn: Callable[[dict[str, Any]], bool | None]
NODE_SENSORS: tuple[ProxmoxNodeBinarySensorEntityDescription, ...] = (
ProxmoxNodeBinarySensorEntityDescription(
key="status",
@@ -82,6 +101,27 @@ VM_SENSORS: tuple[ProxmoxVMBinarySensorEntityDescription, ...] = (
),
)
STORAGE_SENSORS: tuple[ProxmoxStorageBinarySensorEntityDescription, ...] = (
ProxmoxStorageBinarySensorEntityDescription(
key="storage_active",
translation_key="storage_active",
state_fn=lambda data: data["active"] == STORAGE_ACTIVE,
entity_category=EntityCategory.DIAGNOSTIC,
),
ProxmoxStorageBinarySensorEntityDescription(
key="storage_enabled",
translation_key="storage_enabled",
state_fn=lambda data: data["enabled"] == STORAGE_ENABLED,
entity_category=EntityCategory.DIAGNOSTIC,
),
ProxmoxStorageBinarySensorEntityDescription(
key="storage_shared",
translation_key="storage_shared",
state_fn=lambda data: data["shared"] == STORAGE_SHARED,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
async def async_setup_entry(
hass: HomeAssistant,
@@ -121,9 +161,22 @@ async def async_setup_entry(
for entity_description in CONTAINER_SENSORS
)
def _async_add_new_storages(
storages: list[tuple[ProxmoxNodeData, dict[str, Any]]],
) -> None:
"""Add new storage binary sensors."""
async_add_entities(
ProxmoxStorageBinarySensor(
coordinator, entity_description, storage, node_data
)
for (node_data, storage) in storages
for entity_description in STORAGE_SENSORS
)
coordinator.new_nodes_callbacks.append(_async_add_new_nodes)
coordinator.new_vms_callbacks.append(_async_add_new_vms)
coordinator.new_containers_callbacks.append(_async_add_new_containers)
coordinator.new_storages_callbacks.append(_async_add_new_storages)
_async_add_new_nodes(
[
@@ -148,6 +201,14 @@ async def async_setup_entry(
if (node_data.node["node"], vmid) in coordinator.known_containers
]
)
_async_add_new_storages(
[
(node_data, storage_data)
for node_data in coordinator.data.values()
for storage_id, storage_data in node_data.storages.items()
if (node_data.node["node"], storage_id) in coordinator.known_storages
]
)
class ProxmoxNodeBinarySensor(ProxmoxNodeEntity, BinarySensorEntity):
@@ -181,3 +242,14 @@ class ProxmoxContainerBinarySensor(ProxmoxContainerEntity, BinarySensorEntity):
def is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""
return self.entity_description.state_fn(self.container_data)
class ProxmoxStorageBinarySensor(ProxmoxStorageEntity, BinarySensorEntity):
"""Representation of a Proxmox Storage binary sensor."""
entity_description: ProxmoxStorageBinarySensorEntityDescription
@property
def is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""
return self.entity_description.state_fn(self.storage_data)
@@ -16,6 +16,9 @@ CONF_USER = "user"
NODE_ONLINE = "online"
VM_CONTAINER_RUNNING = "running"
STORAGE_ACTIVE = 1
STORAGE_SHARED = 1
STORAGE_ENABLED = 1
STATUS_OK = "ok"
AUTH_PAM = "pam"
@@ -54,6 +54,7 @@ class ProxmoxNodeData:
node: dict[str, Any] = field(default_factory=dict)
vms: dict[int, dict[str, Any]] = field(default_factory=dict)
containers: dict[int, dict[str, Any]] = field(default_factory=dict)
storages: dict[str, dict[str, Any]] = field(default_factory=dict)
backups: list[dict[str, Any]] = field(default_factory=list)
@@ -80,6 +81,7 @@ class ProxmoxCoordinator(DataUpdateCoordinator[dict[str, ProxmoxNodeData]]):
self.known_nodes: set[str] = set()
self.known_vms: set[tuple[str, int]] = set()
self.known_containers: set[tuple[str, int]] = set()
self.known_storages: set[tuple[str, str]] = set()
self.permissions: dict[str, dict[str, int]] = {}
self.new_nodes_callbacks: list[Callable[[list[ProxmoxNodeData]], None]] = []
@@ -89,6 +91,9 @@ class ProxmoxCoordinator(DataUpdateCoordinator[dict[str, ProxmoxNodeData]]):
self.new_containers_callbacks: list[
Callable[[list[tuple[ProxmoxNodeData, dict[str, Any]]]], None]
] = []
self.new_storages_callbacks: list[
Callable[[list[tuple[ProxmoxNodeData, dict[str, Any]]]], None]
] = []
async def _async_setup(self) -> None:
"""Set up the coordinator."""
@@ -173,13 +178,16 @@ class ProxmoxCoordinator(DataUpdateCoordinator[dict[str, ProxmoxNodeData]]):
) from err
data: dict[str, ProxmoxNodeData] = {}
for node, (vms, containers, backups) in zip(nodes, vms_containers, strict=True):
for node, (vms, containers, storages, backups) in zip(
nodes, vms_containers, strict=True
):
data[node[CONF_NODE]] = ProxmoxNodeData(
node=node,
vms={int(vm["vmid"]): vm for vm in vms},
containers={
int(container["vmid"]): container for container in containers
},
storages={s["storage"]: s for s in storages},
backups=backups,
)
@@ -229,9 +237,16 @@ class ProxmoxCoordinator(DataUpdateCoordinator[dict[str, ProxmoxNodeData]]):
self,
) -> tuple[
list[dict[str, Any]],
list[tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]],
list[
tuple[
list[dict[str, Any]],
list[dict[str, Any]],
list[dict[str, Any]],
list[dict[str, Any]],
]
],
]:
"""Fetch all nodes, and then proceed to the VMs, containers, and backups."""
"""Fetch all nodes, and then proceed to the VMs, containers, storages, and backups."""
nodes = self.proxmox.nodes.get() or []
node_data = [self._get_node_data(node) for node in nodes]
return nodes, node_data
@@ -239,22 +254,29 @@ class ProxmoxCoordinator(DataUpdateCoordinator[dict[str, ProxmoxNodeData]]):
def _get_node_data(
self,
node: dict[str, Any],
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]:
"""Get vms, containers, and backups for a node."""
) -> tuple[
list[dict[str, Any]],
list[dict[str, Any]],
list[dict[str, Any]],
list[dict[str, Any]],
]:
"""Get vms, containers, storages, and backups for a node."""
if node.get("status") != NODE_ONLINE:
_LOGGER.debug(
"Node %s is offline, skipping VM/container fetch",
"Node %s is offline, skipping VM/container/storage fetch",
node[CONF_NODE],
)
return [], [], []
return [], [], [], []
vms = self.proxmox.nodes(node[CONF_NODE]).qemu.get() or []
containers = self.proxmox.nodes(node[CONF_NODE]).lxc.get() or []
storages = self.proxmox.nodes(node[CONF_NODE]).storage.get() or []
backups = (
self.proxmox.nodes(node[CONF_NODE]).tasks.get(typefilter="vzdump", limit=1)
or []
)
return vms, containers, backups
return vms, containers, storages, backups
def _async_add_remove_nodes(self, data: dict[str, ProxmoxNodeData]) -> None:
"""Add new nodes/VMs/containers, track removals."""
@@ -303,6 +325,23 @@ class ProxmoxCoordinator(DataUpdateCoordinator[dict[str, ProxmoxNodeData]]):
for containers_callback in self.new_containers_callbacks:
containers_callback(new_container_data)
current_storages = {
(node_name, storage_name)
for node_name, node_data in data.items()
for storage_name in node_data.storages
}
self.known_storages &= current_storages
new_storages = current_storages - self.known_storages
if new_storages:
_LOGGER.debug("New storages found: %s", new_storages)
self.known_storages.update(new_storages)
new_storage_data = [
(data[node_name], data[node_name].storages[storage_name])
for node_name, storage_name in new_storages
]
for storages_callback in self.new_storages_callbacks:
storages_callback(new_storage_data)
class ProxmoxSetupError(Exception):
"""Base exception for Proxmox setup issues."""
@@ -65,6 +65,59 @@ class ProxmoxNodeEntity(ProxmoxCoordinatorEntity):
return super().available and self.device_name in self.coordinator.data
class ProxmoxStorageEntity(ProxmoxCoordinatorEntity):
"""Represents a Storage entity."""
def __init__(
self,
coordinator: ProxmoxCoordinator,
entity_description: EntityDescription,
storage_data: dict[str, Any],
node_data: ProxmoxNodeData,
) -> None:
"""Initialize the Proxmox Storage entity."""
super().__init__(coordinator)
self.entity_description = entity_description
self._storage_data = storage_data
self._node_name = node_data.node["node"]
self.device_id = storage_data["storage"]
self.device_name = storage_data["storage"]
self._attr_device_info = DeviceInfo(
identifiers={
(
DOMAIN,
f"{coordinator.config_entry.entry_id}_storage_{self.device_id}",
)
},
name=f"Storage ({self.device_name})",
model="Storage",
configuration_url=_proxmox_base_url(coordinator).with_fragment(
f"v1:0:=storage/{self._node_name}/{storage_data['storage']}"
),
via_device=(
DOMAIN,
f"{coordinator.config_entry.entry_id}_node_{node_data.node['id']}",
),
)
self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{self._node_name}_{self.device_id}_{entity_description.key}"
@property
def available(self) -> bool:
"""Return if the device is available."""
return (
super().available
and self._node_name in self.coordinator.data
and self.device_id in self.coordinator.data[self._node_name].storages
)
@property
def storage_data(self) -> dict[str, Any]:
"""Return the Storage data."""
return self.coordinator.data[self._node_name].storages[self.device_id]
class ProxmoxVMEntity(ProxmoxCoordinatorEntity):
"""Represents a VM entity."""
@@ -1,5 +1,25 @@
{
"entity": {
"binary_sensor": {
"storage_active": {
"default": "mdi:play",
"state": {
"off": "mdi:stop"
}
},
"storage_enabled": {
"default": "mdi:power",
"state": {
"off": "mdi:power-off"
}
},
"storage_shared": {
"default": "mdi:lan-connect",
"state": {
"off": "mdi:lan-disconnect"
}
}
},
"button": {
"hibernate": {
"default": "mdi:power-sleep"
@@ -84,6 +104,18 @@
"node_status": {
"default": "mdi:server"
},
"storage_available": {
"default": "mdi:harddisk"
},
"storage_total": {
"default": "mdi:harddisk"
},
"storage_used": {
"default": "mdi:harddisk"
},
"storage_used_percentage": {
"default": "mdi:harddisk"
},
"vm_cpu": {
"default": "mdi:cpu-64-bit"
},
+87 -1
View File
@@ -21,7 +21,12 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import dt as dt_util
from .coordinator import ProxmoxConfigEntry, ProxmoxNodeData
from .entity import ProxmoxContainerEntity, ProxmoxNodeEntity, ProxmoxVMEntity
from .entity import (
ProxmoxContainerEntity,
ProxmoxNodeEntity,
ProxmoxStorageEntity,
ProxmoxVMEntity,
)
PARALLEL_UPDATES = 0
@@ -47,6 +52,13 @@ class ProxmoxContainerSensorEntityDescription(SensorEntityDescription):
value_fn: Callable[[dict[str, Any]], StateType]
@dataclass(frozen=True, kw_only=True)
class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
"""Class to hold Proxmox storage sensor description."""
value_fn: Callable[[dict[str, Any]], StateType]
NODE_SENSORS: tuple[ProxmoxNodeSensorEntityDescription, ...] = (
ProxmoxNodeSensorEntityDescription(
key="node_cpu",
@@ -377,6 +389,50 @@ CONTAINER_SENSORS: tuple[ProxmoxContainerSensorEntityDescription, ...] = (
),
)
STORAGE_SENSORS: tuple[ProxmoxStorageSensorEntityDescription, ...] = (
ProxmoxStorageSensorEntityDescription(
key="storage_used",
translation_key="storage_used",
value_fn=lambda data: data["used"],
device_class=SensorDeviceClass.DATA_SIZE,
native_unit_of_measurement=UnitOfInformation.BYTES,
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
ProxmoxStorageSensorEntityDescription(
key="storage_total",
translation_key="storage_total",
value_fn=lambda data: data["total"],
device_class=SensorDeviceClass.DATA_SIZE,
native_unit_of_measurement=UnitOfInformation.BYTES,
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
ProxmoxStorageSensorEntityDescription(
key="storage_available",
translation_key="storage_available",
value_fn=lambda data: data["avail"],
device_class=SensorDeviceClass.DATA_SIZE,
native_unit_of_measurement=UnitOfInformation.BYTES,
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
ProxmoxStorageSensorEntityDescription(
key="storage_used_percentage",
translation_key="storage_used_percentage",
value_fn=lambda data: round(data["used_fraction"] * 100, 1),
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
)
async def async_setup_entry(
hass: HomeAssistant,
@@ -416,9 +472,20 @@ async def async_setup_entry(
for entity_description in CONTAINER_SENSORS
)
def _async_add_new_storages(
storages: list[tuple[ProxmoxNodeData, dict[str, Any]]],
) -> None:
"""Add new storage sensors."""
async_add_entities(
ProxmoxStorageSensor(coordinator, entity_description, storage, node_data)
for (node_data, storage) in storages
for entity_description in STORAGE_SENSORS
)
coordinator.new_nodes_callbacks.append(_async_add_new_nodes)
coordinator.new_vms_callbacks.append(_async_add_new_vms)
coordinator.new_containers_callbacks.append(_async_add_new_containers)
coordinator.new_storages_callbacks.append(_async_add_new_storages)
_async_add_new_nodes(
[
@@ -443,6 +510,14 @@ async def async_setup_entry(
if (node_data.node["node"], vmid) in coordinator.known_containers
]
)
_async_add_new_storages(
[
(node_data, storage_data)
for node_data in coordinator.data.values()
for storage_name, storage_data in node_data.storages.items()
if (node_data.node["node"], storage_name) in coordinator.known_storages
]
)
class ProxmoxNodeSensor(ProxmoxNodeEntity, SensorEntity):
@@ -476,3 +551,14 @@ class ProxmoxContainerSensor(ProxmoxContainerEntity, SensorEntity):
def native_value(self) -> StateType:
"""Return the native value of the sensor."""
return self.entity_description.value_fn(self.container_data)
class ProxmoxStorageSensor(ProxmoxStorageEntity, SensorEntity):
"""Represents a Proxmox VE storage sensor."""
entity_description: ProxmoxStorageSensorEntityDescription
@property
def native_value(self) -> StateType:
"""Return the native value of the sensor."""
return self.entity_description.value_fn(self.storage_data)
@@ -110,6 +110,27 @@
},
"status": {
"name": "Status"
},
"storage_active": {
"name": "Storage active",
"state": {
"off": "Inactive",
"on": "Active"
}
},
"storage_enabled": {
"name": "Storage enabled",
"state": {
"off": "Disabled",
"on": "Enabled"
}
},
"storage_shared": {
"name": "Storage shared",
"state": {
"off": "Not shared",
"on": "Shared"
}
}
},
"button": {
@@ -217,6 +238,18 @@
"node_uptime": {
"name": "Uptime"
},
"storage_available": {
"name": "Available storage"
},
"storage_total": {
"name": "Total storage"
},
"storage_used": {
"name": "Used storage"
},
"storage_used_percentage": {
"name": "Storage usage percentage"
},
"vm_cpu": {
"name": "CPU usage"
},
+3
View File
@@ -118,6 +118,9 @@ def mock_proxmox_client():
node_mock.qemu.get.return_value = qemu_list
node_mock.lxc.get.return_value = lxc_list
node_mock.storage.get.return_value = load_json_array_fixture(
"nodes/storage.json", DOMAIN
)
node_mock.tasks.get.return_value = load_json_array_fixture(
"nodes/tasks.json", DOMAIN
)
@@ -0,0 +1,26 @@
[
{
"type": "dir",
"used_fraction": 0.85,
"storage": "local",
"content": "iso,backup,vztmpl",
"used": 85734563840,
"active": 1,
"enabled": 1,
"shared": 0,
"total": 100861726720,
"avail": 9956425728
},
{
"shared": 1,
"enabled": 1,
"avail": 523769479168,
"total": 1915211874304,
"used_fraction": 0.73,
"storage": "synology",
"type": "nfs",
"active": 1,
"content": "backup,images",
"used": 1391442395136
}
]
@@ -203,6 +203,306 @@
'state': 'on',
})
# ---
# name: test_all_entities[binary_sensor.storage_local_storage_active-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.storage_local_storage_active',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Storage active',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Storage active',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_active',
'unique_id': '1234_pve1_local_storage_active',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[binary_sensor.storage_local_storage_active-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Storage (local) Storage active',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.storage_local_storage_active',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[binary_sensor.storage_local_storage_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.storage_local_storage_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Storage enabled',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Storage enabled',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_enabled',
'unique_id': '1234_pve1_local_storage_enabled',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[binary_sensor.storage_local_storage_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Storage (local) Storage enabled',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.storage_local_storage_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[binary_sensor.storage_local_storage_shared-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.storage_local_storage_shared',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Storage shared',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Storage shared',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_shared',
'unique_id': '1234_pve1_local_storage_shared',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[binary_sensor.storage_local_storage_shared-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Storage (local) Storage shared',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.storage_local_storage_shared',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[binary_sensor.storage_synology_storage_active-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.storage_synology_storage_active',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Storage active',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Storage active',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_active',
'unique_id': '1234_pve1_synology_storage_active',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[binary_sensor.storage_synology_storage_active-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Storage (synology) Storage active',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.storage_synology_storage_active',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[binary_sensor.storage_synology_storage_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.storage_synology_storage_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Storage enabled',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Storage enabled',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_enabled',
'unique_id': '1234_pve1_synology_storage_enabled',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[binary_sensor.storage_synology_storage_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Storage (synology) Storage enabled',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.storage_synology_storage_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[binary_sensor.storage_synology_storage_shared-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.storage_synology_storage_shared',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Storage shared',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Storage shared',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_shared',
'unique_id': '1234_pve1_synology_storage_shared',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[binary_sensor.storage_synology_storage_shared-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Storage (synology) Storage shared',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.storage_synology_storage_shared',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[binary_sensor.vm_db_status-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
@@ -103,6 +103,32 @@
'type': 'node',
'uptime': 86400,
}),
'storages': dict({
'local': dict({
'active': 1,
'avail': 9956425728,
'content': 'iso,backup,vztmpl',
'enabled': 1,
'shared': 0,
'storage': 'local',
'total': 100861726720,
'type': 'dir',
'used': 85734563840,
'used_fraction': 0.85,
}),
'synology': dict({
'active': 1,
'avail': 523769479168,
'content': 'backup,images',
'enabled': 1,
'shared': 1,
'storage': 'synology',
'total': 1915211874304,
'type': 'nfs',
'used': 1391442395136,
'used_fraction': 0.73,
}),
}),
'vms': dict({
'100': dict({
'cpu': 0.15,
@@ -1946,6 +1946,480 @@
'state': '24.0',
})
# ---
# name: test_all_entities[sensor.storage_local_available_storage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.storage_local_available_storage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Available storage',
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
}),
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
'original_icon': None,
'original_name': 'Available storage',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_available',
'unique_id': '1234_pve1_local_storage_available',
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
})
# ---
# name: test_all_entities[sensor.storage_local_available_storage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_size',
'friendly_name': 'Storage (local) Available storage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
'context': <ANY>,
'entity_id': 'sensor.storage_local_available_storage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '9.27264404296875',
})
# ---
# name: test_all_entities[sensor.storage_local_storage_usage_percentage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.storage_local_storage_usage_percentage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Storage usage percentage',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Storage usage percentage',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_used_percentage',
'unique_id': '1234_pve1_local_storage_used_percentage',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[sensor.storage_local_storage_usage_percentage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Storage (local) Storage usage percentage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.storage_local_storage_usage_percentage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '85.0',
})
# ---
# name: test_all_entities[sensor.storage_local_total_storage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.storage_local_total_storage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Total storage',
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
}),
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
'original_icon': None,
'original_name': 'Total storage',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_total',
'unique_id': '1234_pve1_local_storage_total',
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
})
# ---
# name: test_all_entities[sensor.storage_local_total_storage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_size',
'friendly_name': 'Storage (local) Total storage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
'context': <ANY>,
'entity_id': 'sensor.storage_local_total_storage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '93.9348030090332',
})
# ---
# name: test_all_entities[sensor.storage_local_used_storage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.storage_local_used_storage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Used storage',
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
}),
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
'original_icon': None,
'original_name': 'Used storage',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_used',
'unique_id': '1234_pve1_local_storage_used',
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
})
# ---
# name: test_all_entities[sensor.storage_local_used_storage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_size',
'friendly_name': 'Storage (local) Used storage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
'context': <ANY>,
'entity_id': 'sensor.storage_local_used_storage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '79.8465347290039',
})
# ---
# name: test_all_entities[sensor.storage_synology_available_storage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.storage_synology_available_storage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Available storage',
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
}),
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
'original_icon': None,
'original_name': 'Available storage',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_available',
'unique_id': '1234_pve1_synology_storage_available',
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
})
# ---
# name: test_all_entities[sensor.storage_synology_available_storage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_size',
'friendly_name': 'Storage (synology) Available storage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
'context': <ANY>,
'entity_id': 'sensor.storage_synology_available_storage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '487.79833984375',
})
# ---
# name: test_all_entities[sensor.storage_synology_storage_usage_percentage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.storage_synology_storage_usage_percentage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Storage usage percentage',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Storage usage percentage',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_used_percentage',
'unique_id': '1234_pve1_synology_storage_used_percentage',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[sensor.storage_synology_storage_usage_percentage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Storage (synology) Storage usage percentage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.storage_synology_storage_usage_percentage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '73.0',
})
# ---
# name: test_all_entities[sensor.storage_synology_total_storage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.storage_synology_total_storage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Total storage',
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
}),
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
'original_icon': None,
'original_name': 'Total storage',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_total',
'unique_id': '1234_pve1_synology_storage_total',
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
})
# ---
# name: test_all_entities[sensor.storage_synology_total_storage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_size',
'friendly_name': 'Storage (synology) Total storage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
'context': <ANY>,
'entity_id': 'sensor.storage_synology_total_storage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1783.68005371094',
})
# ---
# name: test_all_entities[sensor.storage_synology_used_storage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.storage_synology_used_storage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Used storage',
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
}),
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
'original_icon': None,
'original_name': 'Used storage',
'platform': 'proxmoxve',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'storage_used',
'unique_id': '1234_pve1_synology_storage_used',
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
})
# ---
# name: test_all_entities[sensor.storage_synology_used_storage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_size',
'friendly_name': 'Storage (synology) Used storage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfInformation.GIBIBYTES: 'GiB'>,
}),
'context': <ANY>,
'entity_id': 'sensor.storage_synology_used_storage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1295.88171386719',
})
# ---
# name: test_all_entities[sensor.vm_db_cpu_usage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([