Add sensor platform to Pterodactyl (#141428)

* Add sensor platform

* Correct CPU Limit state attribute translation

* Remove calculated util entitites, add usage and limit entities

* Use suggested_unit_of_measurement instead of converters

* Start only first word of sensor names in upper case, improve suggested units of sensors

* Simplify update of native_value, set uptime as timestamp

* Add paranthesis around multi-line lambda
This commit is contained in:
elmurato 2025-03-31 10:23:40 +02:00 committed by GitHub
parent f247183e11
commit 0488012c77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 260 additions and 9 deletions

View File

@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant
from .coordinator import PterodactylConfigEntry, PterodactylCoordinator
_PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR]
_PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, entry: PterodactylConfigEntry) -> bool:

View File

@ -32,11 +32,14 @@ class PterodactylData:
uuid: str
identifier: str
state: str
memory_utilization: int
cpu_utilization: float
disk_utilization: int
network_rx_utilization: int
network_tx_utilization: int
cpu_limit: int
disk_usage: int
disk_limit: int
memory_usage: int
memory_limit: int
network_inbound: int
network_outbound: int
uptime: int
@ -108,10 +111,13 @@ class PterodactylAPI:
identifier=identifier,
state=utilization["current_state"],
cpu_utilization=utilization["resources"]["cpu_absolute"],
memory_utilization=utilization["resources"]["memory_bytes"],
disk_utilization=utilization["resources"]["disk_bytes"],
network_rx_utilization=utilization["resources"]["network_rx_bytes"],
network_tx_utilization=utilization["resources"]["network_tx_bytes"],
cpu_limit=server["limits"]["cpu"],
memory_usage=utilization["resources"]["memory_bytes"],
memory_limit=server["limits"]["memory"],
disk_usage=utilization["resources"]["disk_bytes"],
disk_limit=server["limits"]["disk"],
network_inbound=utilization["resources"]["network_rx_bytes"],
network_outbound=utilization["resources"]["network_tx_bytes"],
uptime=utilization["resources"]["uptime"],
)

View File

@ -0,0 +1,33 @@
{
"entity": {
"sensor": {
"cpu_utilization": {
"default": "mdi:cpu-64-bit"
},
"cpu_limit": {
"default": "mdi:cpu-64-bit"
},
"memory_usage": {
"default": "mdi:memory"
},
"memory_limit": {
"default": "mdi:memory"
},
"disk_usage": {
"default": "mdi:harddisk"
},
"disk_limit": {
"default": "mdi:harddisk"
},
"network_inbound": {
"default": "mdi:download"
},
"network_outbound": {
"default": "mdi:upload"
},
"uptime": {
"default": "mdi:timer"
}
}
}
}

View File

@ -0,0 +1,183 @@
"""Sensor platform of the Pterodactyl integration."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime, timedelta
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfInformation
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util import dt as dt_util
from .coordinator import PterodactylConfigEntry, PterodactylCoordinator, PterodactylData
from .entity import PterodactylEntity
KEY_CPU_UTILIZATION = "cpu_utilization"
KEY_CPU_LIMIT = "cpu_limit"
KEY_MEMORY_USAGE = "memory_usage"
KEY_MEMORY_LIMIT = "memory_limit"
KEY_DISK_USAGE = "disk_usage"
KEY_DISK_LIMIT = "disk_limit"
KEY_NETWORK_INBOUND = "network_inbound"
KEY_NETWORK_OUTBOUND = "network_outbound"
KEY_UPTIME = "uptime"
# Coordinator is used to centralize the data updates.
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class PterodactylSensorEntityDescription(SensorEntityDescription):
"""Class describing Pterodactyl sensor entities."""
value_fn: Callable[[PterodactylData], StateType | datetime]
SENSOR_DESCRIPTIONS = [
PterodactylSensorEntityDescription(
key=KEY_CPU_UTILIZATION,
translation_key=KEY_CPU_UTILIZATION,
value_fn=lambda data: data.cpu_utilization,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=0,
),
PterodactylSensorEntityDescription(
key=KEY_CPU_LIMIT,
translation_key=KEY_CPU_LIMIT,
value_fn=lambda data: data.cpu_limit,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=0,
entity_registry_enabled_default=False,
),
PterodactylSensorEntityDescription(
key=KEY_MEMORY_USAGE,
translation_key=KEY_MEMORY_USAGE,
value_fn=lambda data: data.memory_usage,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfInformation.BYTES,
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
),
PterodactylSensorEntityDescription(
key=KEY_MEMORY_LIMIT,
translation_key=KEY_MEMORY_LIMIT,
value_fn=lambda data: data.memory_limit,
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfInformation.MEGABYTES,
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
entity_registry_enabled_default=False,
),
PterodactylSensorEntityDescription(
key=KEY_DISK_USAGE,
translation_key=KEY_DISK_USAGE,
value_fn=lambda data: data.disk_usage,
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfInformation.BYTES,
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
),
PterodactylSensorEntityDescription(
key=KEY_DISK_LIMIT,
translation_key=KEY_DISK_LIMIT,
value_fn=lambda data: data.disk_limit,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfInformation.MEGABYTES,
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
entity_registry_enabled_default=False,
),
PterodactylSensorEntityDescription(
key=KEY_NETWORK_INBOUND,
translation_key=KEY_NETWORK_INBOUND,
value_fn=lambda data: data.network_inbound,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfInformation.BYTES,
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
entity_registry_enabled_default=False,
),
PterodactylSensorEntityDescription(
key=KEY_NETWORK_OUTBOUND,
translation_key=KEY_NETWORK_OUTBOUND,
value_fn=lambda data: data.network_outbound,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfInformation.BYTES,
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
entity_registry_enabled_default=False,
),
PterodactylSensorEntityDescription(
key=KEY_UPTIME,
translation_key=KEY_UPTIME,
value_fn=(
lambda data: dt_util.utcnow() - timedelta(milliseconds=data.uptime)
if data.uptime > 0
else None
),
device_class=SensorDeviceClass.TIMESTAMP,
),
]
async def async_setup_entry(
hass: HomeAssistant,
config_entry: PterodactylConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Pterodactyl sensor platform."""
coordinator = config_entry.runtime_data
async_add_entities(
PterodactylSensorEntity(coordinator, identifier, description, config_entry)
for identifier in coordinator.api.identifiers
for description in SENSOR_DESCRIPTIONS
)
class PterodactylSensorEntity(PterodactylEntity, SensorEntity):
"""Representation of a Pterodactyl sensor base entity."""
entity_description: PterodactylSensorEntityDescription
def __init__(
self,
coordinator: PterodactylCoordinator,
identifier: str,
description: PterodactylSensorEntityDescription,
config_entry: PterodactylConfigEntry,
) -> None:
"""Initialize sensor base entity."""
super().__init__(coordinator, identifier, config_entry)
self.entity_description = description
self._attr_unique_id = f"{self.game_server_data.uuid}_{description.key}"
@property
def native_value(self) -> StateType | datetime:
"""Return native value of sensor."""
return self.entity_description.value_fn(self.game_server_data)

View File

@ -25,6 +25,35 @@
"status": {
"name": "Status"
}
},
"sensor": {
"cpu_utilization": {
"name": "CPU utilization"
},
"cpu_limit": {
"name": "CPU limit"
},
"memory_usage": {
"name": "Memory usage"
},
"memory_limit": {
"name": "Memory limit"
},
"disk_usage": {
"name": "Disk usage"
},
"disk_limit": {
"name": "Disk limit"
},
"network_inbound": {
"name": "Network inbound"
},
"network_outbound": {
"name": "Network outbound"
},
"uptime": {
"name": "Uptime"
}
}
}
}