Proxmox refactor to use UPTIME sensor (#169704)

This commit is contained in:
Erwin Douna
2026-08-26 22:51:40 +02:00
committed by GitHub
parent 3ac7c286eb
commit 3e0d75fec4
3 changed files with 55 additions and 95 deletions
+29 -20
View File
@@ -2,7 +2,7 @@
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime
from datetime import datetime, timedelta
from typing import Any, override
from homeassistant.components.sensor import (
@@ -44,14 +44,14 @@ class ProxmoxNodeSensorEntityDescription(SensorEntityDescription):
class ProxmoxVMSensorEntityDescription(SensorEntityDescription):
"""Class to hold Proxmox VM sensor description."""
value_fn: Callable[[dict[str, Any]], StateType]
value_fn: Callable[[dict[str, Any]], StateType | datetime]
@dataclass(frozen=True, kw_only=True)
class ProxmoxContainerSensorEntityDescription(SensorEntityDescription):
"""Class to hold Proxmox container sensor description."""
value_fn: Callable[[dict[str, Any]], StateType]
value_fn: Callable[[dict[str, Any]], StateType | datetime]
@dataclass(frozen=True, kw_only=True)
@@ -138,12 +138,15 @@ NODE_SENSORS: tuple[ProxmoxNodeSensorEntityDescription, ...] = (
ProxmoxNodeSensorEntityDescription(
key="node_uptime",
translation_key="node_uptime",
value_fn=lambda data: data.node["uptime"],
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
value_fn=(
lambda data: (
(dt_util.utcnow() - timedelta(seconds=data.node["uptime"]))
if data.node["uptime"] is not None
else None
)
),
device_class=SensorDeviceClass.UPTIME,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
ProxmoxNodeSensorEntityDescription(
key="node_status",
@@ -236,12 +239,15 @@ VM_SENSORS: tuple[ProxmoxVMSensorEntityDescription, ...] = (
ProxmoxVMSensorEntityDescription(
key="vm_uptime",
translation_key="vm_uptime",
value_fn=lambda data: data.get("uptime"),
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
value_fn=(
lambda data: (
(dt_util.utcnow() - timedelta(seconds=data["uptime"]))
if data.get("uptime") is not None
else None
)
),
device_class=SensorDeviceClass.UPTIME,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
ProxmoxVMSensorEntityDescription(
key="vm_disk",
@@ -353,12 +359,15 @@ CONTAINER_SENSORS: tuple[ProxmoxContainerSensorEntityDescription, ...] = (
ProxmoxContainerSensorEntityDescription(
key="container_uptime",
translation_key="container_uptime",
value_fn=lambda data: data.get("uptime"),
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
value_fn=(
lambda data: (
(dt_util.utcnow() - timedelta(seconds=data["uptime"]))
if data.get("uptime") is not None
else None
)
),
device_class=SensorDeviceClass.UPTIME,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
ProxmoxContainerSensorEntityDescription(
key="container_disk",
@@ -577,7 +586,7 @@ class ProxmoxVMSensor(ProxmoxVMEntity, SensorEntity):
@property
@override
def native_value(self) -> StateType:
def native_value(self) -> StateType | datetime:
"""Return the native value of the sensor."""
return self.entity_description.value_fn(self.vm_data)
@@ -589,7 +598,7 @@ class ProxmoxContainerSensor(ProxmoxContainerEntity, SensorEntity):
@property
@override
def native_value(self) -> StateType:
def native_value(self) -> StateType | datetime:
"""Return the native value of the sensor."""
return self.entity_description.value_fn(self.container_data)
@@ -600,9 +600,7 @@
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
@@ -620,14 +618,8 @@
'name': None,
'object_id_base': 'Uptime',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_device_class': <SensorDeviceClass.UPTIME: 'uptime'>,
'original_icon': None,
'original_name': 'Uptime',
'platform': 'proxmoxve',
@@ -636,23 +628,21 @@
'supported_features': 0,
'translation_key': 'container_uptime',
'unique_id': '1234_201_container_uptime',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.ct_backup_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'uptime',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'ct-backup Uptime',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.ct_backup_uptime',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '12.0',
'state': '2026-05-03T00:00:00+00:00',
})
# ---
# name: test_all_entities[sensor.ct_nginx_cpu_usage-entry]
@@ -1256,9 +1246,7 @@
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
@@ -1276,14 +1264,8 @@
'name': None,
'object_id_base': 'Uptime',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_device_class': <SensorDeviceClass.UPTIME: 'uptime'>,
'original_icon': None,
'original_name': 'Uptime',
'platform': 'proxmoxve',
@@ -1292,23 +1274,21 @@
'supported_features': 0,
'translation_key': 'container_uptime',
'unique_id': '1234_200_container_uptime',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.ct_nginx_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'uptime',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'ct-nginx Uptime',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.ct_nginx_uptime',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '12.0',
'state': '2026-05-03T00:00:00+00:00',
})
# ---
# name: test_all_entities[sensor.pve1_backup_duration-entry]
@@ -1900,9 +1880,7 @@
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
@@ -1920,14 +1898,8 @@
'name': None,
'object_id_base': 'Uptime',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_device_class': <SensorDeviceClass.UPTIME: 'uptime'>,
'original_icon': None,
'original_name': 'Uptime',
'platform': 'proxmoxve',
@@ -1936,23 +1908,21 @@
'supported_features': 0,
'translation_key': 'node_uptime',
'unique_id': '1234_node/pve1_node_uptime',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.pve1_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'uptime',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'pve1 Uptime',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.pve1_uptime',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '24.0',
'state': '2026-05-02T12:00:00+00:00',
})
# ---
# name: test_all_entities[sensor.storage_local_available_storage-entry]
@@ -3030,9 +3000,7 @@
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
@@ -3050,14 +3018,8 @@
'name': None,
'object_id_base': 'Uptime',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_device_class': <SensorDeviceClass.UPTIME: 'uptime'>,
'original_icon': None,
'original_name': 'Uptime',
'platform': 'proxmoxve',
@@ -3066,23 +3028,21 @@
'supported_features': 0,
'translation_key': 'vm_uptime',
'unique_id': '1234_101_vm_uptime',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.vm_db_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'uptime',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'vm-db Uptime',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.vm_db_uptime',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '24.0',
'state': '2026-05-02T12:00:00+00:00',
})
# ---
# name: test_all_entities[sensor.vm_web_cpu_usage-entry]
@@ -3686,9 +3646,7 @@
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
@@ -3706,14 +3664,8 @@
'name': None,
'object_id_base': 'Uptime',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_device_class': <SensorDeviceClass.UPTIME: 'uptime'>,
'original_icon': None,
'original_name': 'Uptime',
'platform': 'proxmoxve',
@@ -3722,22 +3674,20 @@
'supported_features': 0,
'translation_key': 'vm_uptime',
'unique_id': '1234_100_vm_uptime',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.vm_web_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'uptime',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'vm-web Uptime',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.vm_web_uptime',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '24.0',
'state': '2026-05-02T12:00:00+00:00',
})
# ---
@@ -23,6 +23,7 @@ def enable_all_entities(entity_registry_enabled_by_default: None) -> None:
"""Make sure all entities are enabled."""
@pytest.mark.freeze_time("2026-05-03T12:00:00+00:00")
async def test_all_entities(
hass: HomeAssistant,
snapshot: SnapshotAssertion,