Add entity translations to Glances (#107189)

This commit is contained in:
Joost Lekkerkerker 2024-01-14 11:12:30 +01:00 committed by GitHub
parent 5e79cd8715
commit 965499dd90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 433 additions and 437 deletions

View File

@ -13,15 +13,12 @@ from homeassistant.components.sensor import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_NAME,
PERCENTAGE, PERCENTAGE,
REVOLUTIONS_PER_MINUTE, REVOLUTIONS_PER_MINUTE,
Platform,
UnitOfInformation, UnitOfInformation,
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -35,7 +32,6 @@ class GlancesSensorEntityDescriptionMixin:
"""Mixin for required keys.""" """Mixin for required keys."""
type: str type: str
name_suffix: str
@dataclass(frozen=True) @dataclass(frozen=True)
@ -49,7 +45,7 @@ SENSOR_TYPES = {
("fs", "disk_use_percent"): GlancesSensorEntityDescription( ("fs", "disk_use_percent"): GlancesSensorEntityDescription(
key="disk_use_percent", key="disk_use_percent",
type="fs", type="fs",
name_suffix="used percent", translation_key="disk_usage",
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
icon="mdi:harddisk", icon="mdi:harddisk",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -57,7 +53,7 @@ SENSOR_TYPES = {
("fs", "disk_use"): GlancesSensorEntityDescription( ("fs", "disk_use"): GlancesSensorEntityDescription(
key="disk_use", key="disk_use",
type="fs", type="fs",
name_suffix="used", translation_key="disk_used",
native_unit_of_measurement=UnitOfInformation.GIBIBYTES, native_unit_of_measurement=UnitOfInformation.GIBIBYTES,
device_class=SensorDeviceClass.DATA_SIZE, device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:harddisk", icon="mdi:harddisk",
@ -66,7 +62,7 @@ SENSOR_TYPES = {
("fs", "disk_free"): GlancesSensorEntityDescription( ("fs", "disk_free"): GlancesSensorEntityDescription(
key="disk_free", key="disk_free",
type="fs", type="fs",
name_suffix="free", translation_key="disk_free",
native_unit_of_measurement=UnitOfInformation.GIBIBYTES, native_unit_of_measurement=UnitOfInformation.GIBIBYTES,
device_class=SensorDeviceClass.DATA_SIZE, device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:harddisk", icon="mdi:harddisk",
@ -75,7 +71,7 @@ SENSOR_TYPES = {
("mem", "memory_use_percent"): GlancesSensorEntityDescription( ("mem", "memory_use_percent"): GlancesSensorEntityDescription(
key="memory_use_percent", key="memory_use_percent",
type="mem", type="mem",
name_suffix="RAM used percent", translation_key="memory_usage",
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
icon="mdi:memory", icon="mdi:memory",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -83,7 +79,7 @@ SENSOR_TYPES = {
("mem", "memory_use"): GlancesSensorEntityDescription( ("mem", "memory_use"): GlancesSensorEntityDescription(
key="memory_use", key="memory_use",
type="mem", type="mem",
name_suffix="RAM used", translation_key="memory_used",
native_unit_of_measurement=UnitOfInformation.MEBIBYTES, native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
device_class=SensorDeviceClass.DATA_SIZE, device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:memory", icon="mdi:memory",
@ -92,7 +88,7 @@ SENSOR_TYPES = {
("mem", "memory_free"): GlancesSensorEntityDescription( ("mem", "memory_free"): GlancesSensorEntityDescription(
key="memory_free", key="memory_free",
type="mem", type="mem",
name_suffix="RAM free", translation_key="memory_free",
native_unit_of_measurement=UnitOfInformation.MEBIBYTES, native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
device_class=SensorDeviceClass.DATA_SIZE, device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:memory", icon="mdi:memory",
@ -101,7 +97,7 @@ SENSOR_TYPES = {
("memswap", "swap_use_percent"): GlancesSensorEntityDescription( ("memswap", "swap_use_percent"): GlancesSensorEntityDescription(
key="swap_use_percent", key="swap_use_percent",
type="memswap", type="memswap",
name_suffix="Swap used percent", translation_key="swap_usage",
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
icon="mdi:memory", icon="mdi:memory",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -109,7 +105,7 @@ SENSOR_TYPES = {
("memswap", "swap_use"): GlancesSensorEntityDescription( ("memswap", "swap_use"): GlancesSensorEntityDescription(
key="swap_use", key="swap_use",
type="memswap", type="memswap",
name_suffix="Swap used", translation_key="swap_used",
native_unit_of_measurement=UnitOfInformation.GIBIBYTES, native_unit_of_measurement=UnitOfInformation.GIBIBYTES,
device_class=SensorDeviceClass.DATA_SIZE, device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:memory", icon="mdi:memory",
@ -118,7 +114,7 @@ SENSOR_TYPES = {
("memswap", "swap_free"): GlancesSensorEntityDescription( ("memswap", "swap_free"): GlancesSensorEntityDescription(
key="swap_free", key="swap_free",
type="memswap", type="memswap",
name_suffix="Swap free", translation_key="swap_free",
native_unit_of_measurement=UnitOfInformation.GIBIBYTES, native_unit_of_measurement=UnitOfInformation.GIBIBYTES,
device_class=SensorDeviceClass.DATA_SIZE, device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:memory", icon="mdi:memory",
@ -127,42 +123,42 @@ SENSOR_TYPES = {
("load", "processor_load"): GlancesSensorEntityDescription( ("load", "processor_load"): GlancesSensorEntityDescription(
key="processor_load", key="processor_load",
type="load", type="load",
name_suffix="CPU load", translation_key="processor_load",
icon=CPU_ICON, icon=CPU_ICON,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
("processcount", "process_running"): GlancesSensorEntityDescription( ("processcount", "process_running"): GlancesSensorEntityDescription(
key="process_running", key="process_running",
type="processcount", type="processcount",
name_suffix="Running", translation_key="process_running",
icon=CPU_ICON, icon=CPU_ICON,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
("processcount", "process_total"): GlancesSensorEntityDescription( ("processcount", "process_total"): GlancesSensorEntityDescription(
key="process_total", key="process_total",
type="processcount", type="processcount",
name_suffix="Total", translation_key="process_total",
icon=CPU_ICON, icon=CPU_ICON,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
("processcount", "process_thread"): GlancesSensorEntityDescription( ("processcount", "process_thread"): GlancesSensorEntityDescription(
key="process_thread", key="process_thread",
type="processcount", type="processcount",
name_suffix="Thread", translation_key="process_threads",
icon=CPU_ICON, icon=CPU_ICON,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
("processcount", "process_sleeping"): GlancesSensorEntityDescription( ("processcount", "process_sleeping"): GlancesSensorEntityDescription(
key="process_sleeping", key="process_sleeping",
type="processcount", type="processcount",
name_suffix="Sleeping", translation_key="process_sleeping",
icon=CPU_ICON, icon=CPU_ICON,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
("cpu", "cpu_use_percent"): GlancesSensorEntityDescription( ("cpu", "cpu_use_percent"): GlancesSensorEntityDescription(
key="cpu_use_percent", key="cpu_use_percent",
type="cpu", type="cpu",
name_suffix="CPU used", translation_key="cpu_usage",
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
icon=CPU_ICON, icon=CPU_ICON,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -170,7 +166,7 @@ SENSOR_TYPES = {
("sensors", "temperature_core"): GlancesSensorEntityDescription( ("sensors", "temperature_core"): GlancesSensorEntityDescription(
key="temperature_core", key="temperature_core",
type="sensors", type="sensors",
name_suffix="Temperature", translation_key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -178,7 +174,7 @@ SENSOR_TYPES = {
("sensors", "temperature_hdd"): GlancesSensorEntityDescription( ("sensors", "temperature_hdd"): GlancesSensorEntityDescription(
key="temperature_hdd", key="temperature_hdd",
type="sensors", type="sensors",
name_suffix="Temperature", translation_key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -186,7 +182,7 @@ SENSOR_TYPES = {
("sensors", "fan_speed"): GlancesSensorEntityDescription( ("sensors", "fan_speed"): GlancesSensorEntityDescription(
key="fan_speed", key="fan_speed",
type="sensors", type="sensors",
name_suffix="Fan speed", translation_key="fan_speed",
native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
icon="mdi:fan", icon="mdi:fan",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -194,7 +190,7 @@ SENSOR_TYPES = {
("sensors", "battery"): GlancesSensorEntityDescription( ("sensors", "battery"): GlancesSensorEntityDescription(
key="battery", key="battery",
type="sensors", type="sensors",
name_suffix="Charge", translation_key="charge",
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY, device_class=SensorDeviceClass.BATTERY,
icon="mdi:battery", icon="mdi:battery",
@ -203,14 +199,14 @@ SENSOR_TYPES = {
("docker", "docker_active"): GlancesSensorEntityDescription( ("docker", "docker_active"): GlancesSensorEntityDescription(
key="docker_active", key="docker_active",
type="docker", type="docker",
name_suffix="Containers active", translation_key="container_active",
icon="mdi:docker", icon="mdi:docker",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
("docker", "docker_cpu_use"): GlancesSensorEntityDescription( ("docker", "docker_cpu_use"): GlancesSensorEntityDescription(
key="docker_cpu_use", key="docker_cpu_use",
type="docker", type="docker",
name_suffix="Containers CPU used", translation_key="container_cpu_usage",
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
icon="mdi:docker", icon="mdi:docker",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -218,7 +214,7 @@ SENSOR_TYPES = {
("docker", "docker_memory_use"): GlancesSensorEntityDescription( ("docker", "docker_memory_use"): GlancesSensorEntityDescription(
key="docker_memory_use", key="docker_memory_use",
type="docker", type="docker",
name_suffix="Containers RAM used", translation_key="container_memory_used",
native_unit_of_measurement=UnitOfInformation.MEBIBYTES, native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
device_class=SensorDeviceClass.DATA_SIZE, device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:docker", icon="mdi:docker",
@ -227,14 +223,14 @@ SENSOR_TYPES = {
("raid", "available"): GlancesSensorEntityDescription( ("raid", "available"): GlancesSensorEntityDescription(
key="available", key="available",
type="raid", type="raid",
name_suffix="Raid available", translation_key="raid_available",
icon="mdi:harddisk", icon="mdi:harddisk",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
("raid", "used"): GlancesSensorEntityDescription( ("raid", "used"): GlancesSensorEntityDescription(
key="used", key="used",
type="raid", type="raid",
name_suffix="Raid used", translation_key="raid_used",
icon="mdi:harddisk", icon="mdi:harddisk",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
@ -249,54 +245,26 @@ async def async_setup_entry(
"""Set up the Glances sensors.""" """Set up the Glances sensors."""
coordinator: GlancesDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] coordinator: GlancesDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
name = config_entry.data.get(CONF_NAME)
entities = [] entities = []
@callback
def _migrate_old_unique_ids(
hass: HomeAssistant, old_unique_id: str, new_key: str
) -> None:
"""Migrate unique IDs to the new format."""
ent_reg = er.async_get(hass)
if entity_id := ent_reg.async_get_entity_id(
Platform.SENSOR, DOMAIN, old_unique_id
):
ent_reg.async_update_entity(
entity_id, new_unique_id=f"{config_entry.entry_id}-{new_key}"
)
for sensor_type, sensors in coordinator.data.items(): for sensor_type, sensors in coordinator.data.items():
if sensor_type in ["fs", "sensors", "raid"]: if sensor_type in ["fs", "sensors", "raid"]:
for sensor_label, params in sensors.items(): for sensor_label, params in sensors.items():
for param in params: for param in params:
if sensor_description := SENSOR_TYPES.get((sensor_type, param)): if sensor_description := SENSOR_TYPES.get((sensor_type, param)):
_migrate_old_unique_ids(
hass,
f"{coordinator.host}-{name} {sensor_label} {sensor_description.name_suffix}",
f"{sensor_label}-{sensor_description.key}",
)
entities.append( entities.append(
GlancesSensor( GlancesSensor(
coordinator, coordinator,
name,
sensor_label,
sensor_description, sensor_description,
sensor_label,
) )
) )
else: else:
for sensor in sensors: for sensor in sensors:
if sensor_description := SENSOR_TYPES.get((sensor_type, sensor)): if sensor_description := SENSOR_TYPES.get((sensor_type, sensor)):
_migrate_old_unique_ids(
hass,
f"{coordinator.host}-{name} {sensor_description.name_suffix}",
f"-{sensor_description.key}",
)
entities.append( entities.append(
GlancesSensor( GlancesSensor(
coordinator, coordinator,
name,
"",
sensor_description, sensor_description,
) )
) )
@ -313,21 +281,23 @@ class GlancesSensor(CoordinatorEntity[GlancesDataUpdateCoordinator], SensorEntit
def __init__( def __init__(
self, self,
coordinator: GlancesDataUpdateCoordinator, coordinator: GlancesDataUpdateCoordinator,
name: str | None,
sensor_name_prefix: str,
description: GlancesSensorEntityDescription, description: GlancesSensorEntityDescription,
sensor_label: str = "",
) -> None: ) -> None:
"""Initialize the sensor.""" """Initialize the sensor."""
super().__init__(coordinator) super().__init__(coordinator)
self._sensor_name_prefix = sensor_name_prefix self._sensor_label = sensor_label
self.entity_description = description self.entity_description = description
self._attr_name = f"{sensor_name_prefix} {description.name_suffix}".strip() if sensor_label:
self._attr_translation_placeholders = {"sensor_label": sensor_label}
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.config_entry.entry_id)}, identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
manufacturer="Glances", manufacturer="Glances",
name=name or coordinator.host, name=coordinator.host,
)
self._attr_unique_id = (
f"{coordinator.config_entry.entry_id}-{sensor_label}-{description.key}"
) )
self._attr_unique_id = f"{coordinator.config_entry.entry_id}-{sensor_name_prefix}-{description.key}"
@property @property
def available(self) -> bool: def available(self) -> bool:
@ -346,8 +316,8 @@ class GlancesSensor(CoordinatorEntity[GlancesDataUpdateCoordinator], SensorEntit
"""Return the state of the resources.""" """Return the state of the resources."""
value = self.coordinator.data[self.entity_description.type] value = self.coordinator.data[self.entity_description.type]
if isinstance(value.get(self._sensor_name_prefix), dict): if isinstance(value.get(self._sensor_label), dict):
return cast( return cast(
StateType, value[self._sensor_name_prefix][self.entity_description.key] StateType, value[self._sensor_label][self.entity_description.key]
) )
return cast(StateType, value[self.entity_description.key]) return cast(StateType, value[self.entity_description.key])

View File

@ -30,6 +30,79 @@
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
} }
}, },
"entity": {
"sensor": {
"disk_usage": {
"name": "{sensor_label} disk usage"
},
"disk_used": {
"name": "{sensor_label} disk used"
},
"disk_free": {
"name": "{sensor_label} disk free"
},
"memory_usage": {
"name": "Memory usage"
},
"memory_use": {
"name": "Memory use"
},
"memory_free": {
"name": "Memory free"
},
"swap_usage": {
"name": "Swap usage"
},
"swap_use": {
"name": "Swap use"
},
"swap_free": {
"name": "Swap free"
},
"cpu_load": {
"name": "CPU load"
},
"process_running": {
"name": "Running"
},
"process_total": {
"name": "Total"
},
"process_threads": {
"name": "Threads"
},
"process_sleeping": {
"name": "Sleeping"
},
"cpu_usage": {
"name": "CPU usage"
},
"temperature": {
"name": "{sensor_label} temperature"
},
"fan_speed": {
"name": "{sensor_label} fan speed"
},
"charge": {
"name": "{sensor_label} charge"
},
"container_active": {
"name": "Containers active"
},
"container_cpu_usage": {
"name": "Containers CPU usage"
},
"container_memory_used": {
"name": "Containers memory used"
},
"raid_available": {
"name": "{sensor_label} available"
},
"raid_used": {
"name": "{sensor_label} used"
}
}
},
"issues": { "issues": {
"deprecated_version": { "deprecated_version": {
"title": "Glances servers with version 2 is deprecated", "title": "Glances servers with version 2 is deprecated",

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,7 @@
"""Tests for glances sensors.""" """Tests for glances sensors."""
import pytest
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
from homeassistant.components.glances.const import DOMAIN from homeassistant.components.glances.const import DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
@ -29,48 +27,3 @@ async def test_sensor_states(
assert hass.states.get(entity_entry.entity_id) == snapshot( assert hass.states.get(entity_entry.entity_id) == snapshot(
name=f"{entity_entry.entity_id}-state" name=f"{entity_entry.entity_id}-state"
) )
@pytest.mark.parametrize(
("object_id", "old_unique_id", "new_unique_id"),
[
(
"glances_ssl_used_percent",
"0.0.0.0-Glances /ssl used percent",
"/ssl-disk_use_percent",
),
(
"glances_cpu_thermal_1_temperature",
"0.0.0.0-Glances cpu_thermal 1 Temperature",
"cpu_thermal 1-temperature_core",
),
],
)
async def test_migrate_unique_id(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
object_id: str,
old_unique_id: str,
new_unique_id: str,
) -> None:
"""Test unique id migration."""
old_config_data = {**MOCK_USER_INPUT, "name": "Glances"}
entry = MockConfigEntry(domain=DOMAIN, data=old_config_data)
entry.add_to_hass(hass)
entity: er.RegistryEntry = entity_registry.async_get_or_create(
suggested_object_id=object_id,
disabled_by=None,
domain=SENSOR_DOMAIN,
platform=DOMAIN,
unique_id=old_unique_id,
config_entry=entry,
)
assert entity.unique_id == old_unique_id
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
entity_migrated = entity_registry.async_get(entity.entity_id)
assert entity_migrated
assert entity_migrated.unique_id == f"{entry.entry_id}-{new_unique_id}"