mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 22:38:02 -04:00
Fix data sensors unit to use MiB or GiB as values are binary based (#184243)
This commit is contained in:
@@ -6,7 +6,11 @@ from homeassistant.const import Platform, UnitOfDataRate
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from .const import DOMAIN, THROUGHPUT_UNIQUE_ID_FRAGMENT
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
LEGACY_DATA_SIZE_UNIT_EQUIVALENTS,
|
||||
THROUGHPUT_UNIQUE_ID_FRAGMENT,
|
||||
)
|
||||
from .coordinator import (
|
||||
LibreHardwareMonitorConfigEntry,
|
||||
LibreHardwareMonitorCoordinator,
|
||||
@@ -92,6 +96,24 @@ async def async_migrate_entry(
|
||||
unit_of_measurement=UnitOfDataRate.KIBIBYTES_PER_SECOND,
|
||||
)
|
||||
|
||||
# Migrate binary Data units LHM labels MB and GB to MiB and GiB
|
||||
for reg_entry in registry_entries:
|
||||
if not (
|
||||
data_size_unit := LEGACY_DATA_SIZE_UNIT_EQUIVALENTS.get(
|
||||
reg_entry.unit_of_measurement
|
||||
)
|
||||
):
|
||||
continue
|
||||
_LOGGER.debug(
|
||||
"Migrating entity %s unit from %s to %s",
|
||||
reg_entry.entity_id,
|
||||
reg_entry.unit_of_measurement,
|
||||
data_size_unit,
|
||||
)
|
||||
entity_registry.async_update_entity(
|
||||
reg_entry.entity_id, unit_of_measurement=data_size_unit
|
||||
)
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry, data=config_entry.data, version=2, minor_version=2
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Constants for the LibreHardwareMonitor integration."""
|
||||
|
||||
from homeassistant.const import UnitOfInformation
|
||||
|
||||
DOMAIN = "libre_hardware_monitor"
|
||||
DEFAULT_HOST = "localhost"
|
||||
DEFAULT_PORT = 8085
|
||||
@@ -7,3 +9,9 @@ DEFAULT_SCAN_INTERVAL = 10
|
||||
|
||||
LEGACY_THROUGHPUT_UNIT = "KB/s"
|
||||
THROUGHPUT_UNIQUE_ID_FRAGMENT = "throughput"
|
||||
|
||||
# LHM labels its data sizes MB and GB but calculates them with binary multiples
|
||||
LEGACY_DATA_SIZE_UNIT_EQUIVALENTS: dict[str | None, str] = {
|
||||
"MB": UnitOfInformation.MEBIBYTES,
|
||||
"GB": UnitOfInformation.GIBIBYTES,
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import UnitOfDataRate
|
||||
from homeassistant.const import UnitOfDataRate, UnitOfInformation
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import LibreHardwareMonitorConfigEntry, LibreHardwareMonitorCoordinator
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, LEGACY_DATA_SIZE_UNIT_EQUIVALENTS
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -113,6 +113,9 @@ class LibreHardwareMonitorSensor(
|
||||
elif device_class is SensorDeviceClass.VOLTAGE:
|
||||
# Device class default rounds voltages to whole volts
|
||||
self._attr_suggested_display_precision = 3
|
||||
elif device_class is SensorDeviceClass.DATA_SIZE:
|
||||
# LHM versions >= 0.9.7 report data sizes in raw bytes
|
||||
self._attr_suggested_unit_of_measurement = UnitOfInformation.GIBIBYTES
|
||||
|
||||
self._set_state(sensor_data)
|
||||
self._attr_unique_id: str = f"{entry_id}_{sensor_data.sensor_id}"
|
||||
@@ -128,7 +131,12 @@ class LibreHardwareMonitorSensor(
|
||||
|
||||
def _set_state(self, sensor_data: LibreHardwareMonitorSensorData) -> None:
|
||||
self._attr_native_value: str | None = sensor_data.value
|
||||
self._attr_native_unit_of_measurement = sensor_data.unit
|
||||
# This conversion is only needed for LHM versions < 0.9.7
|
||||
self._attr_native_unit_of_measurement = (
|
||||
LEGACY_DATA_SIZE_UNIT_EQUIVALENTS.get(sensor_data.unit, sensor_data.unit)
|
||||
if sensor_data.type in (SensorType.DATA, SensorType.SMALL_DATA)
|
||||
else sensor_data.unit
|
||||
)
|
||||
self._native_min_value = sensor_data.min
|
||||
self._native_max_value = sensor_data.max
|
||||
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
from dataclasses import replace
|
||||
from datetime import timedelta
|
||||
from types import MappingProxyType
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from librehardwaremonitor_api.model import LibreHardwareMonitorSensorData
|
||||
from librehardwaremonitor_api.sensor_type import SensorType
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.libre_hardware_monitor.const import (
|
||||
@@ -16,7 +19,11 @@ from homeassistant.components.libre_hardware_monitor.recorder import (
|
||||
async_custom_equivalent_units,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, UnitOfDataRate
|
||||
from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
UnitOfDataRate,
|
||||
UnitOfInformation,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
@@ -172,6 +179,111 @@ async def test_migration_to_sensor_device_classes(
|
||||
assert updated_config_entry.minor_version == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"sensor_type",
|
||||
"lhm_value",
|
||||
"lhm_unit",
|
||||
"legacy_unit",
|
||||
"expected_value",
|
||||
"expected_unit",
|
||||
),
|
||||
[
|
||||
pytest.param(
|
||||
SensorType.SMALL_DATA,
|
||||
"733.0",
|
||||
"MB",
|
||||
"MB",
|
||||
733.0,
|
||||
UnitOfInformation.MEBIBYTES,
|
||||
id="small_data",
|
||||
),
|
||||
pytest.param(
|
||||
SensorType.DATA,
|
||||
"16.0",
|
||||
"GB",
|
||||
"GB",
|
||||
16.0,
|
||||
UnitOfInformation.GIBIBYTES,
|
||||
id="data",
|
||||
),
|
||||
pytest.param(
|
||||
SensorType.DATA,
|
||||
"17179869184",
|
||||
"B",
|
||||
"GB",
|
||||
16.0,
|
||||
UnitOfInformation.GIBIBYTES,
|
||||
id="data_reported_in_bytes",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("recorder_mock")
|
||||
async def test_migration_to_binary_data_size_units(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_lhm_client: AsyncMock,
|
||||
sensor_type: SensorType,
|
||||
lhm_value: str,
|
||||
lhm_unit: str,
|
||||
legacy_unit: str,
|
||||
expected_value: float,
|
||||
expected_unit: str,
|
||||
) -> None:
|
||||
"""Test that data sizes LHM labels MB and GB move to MiB and GiB."""
|
||||
legacy_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="192.168.0.20:8085",
|
||||
data=VALID_CONFIG,
|
||||
entry_id="test_entry_id",
|
||||
version=2,
|
||||
minor_version=1,
|
||||
)
|
||||
legacy_config_entry.add_to_hass(hass)
|
||||
|
||||
sensor_id = "gpu-nvidia-0-data-0"
|
||||
mock_lhm_client.get_data.return_value = replace(
|
||||
mock_lhm_client.get_data.return_value,
|
||||
sensor_data=MappingProxyType(
|
||||
{
|
||||
sensor_id: LibreHardwareMonitorSensorData(
|
||||
name="GPU Memory Total",
|
||||
value=lhm_value,
|
||||
type=sensor_type,
|
||||
min=lhm_value,
|
||||
max=lhm_value,
|
||||
unit=lhm_unit,
|
||||
device_id="gpu-nvidia-0",
|
||||
device_name="NVIDIA GeForce RTX 4080 SUPER",
|
||||
device_type="NVIDIA",
|
||||
sensor_id=sensor_id,
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
# Set up data size sensor with the unit it had before device classes
|
||||
object_id = "nvidia_geforce_rtx_4080_gpu_memory_total"
|
||||
entity_registry.async_get_or_create(
|
||||
"sensor",
|
||||
DOMAIN,
|
||||
f"{legacy_config_entry.entry_id}_{sensor_id}",
|
||||
suggested_object_id=object_id,
|
||||
config_entry=legacy_config_entry,
|
||||
unit_of_measurement=legacy_unit,
|
||||
)
|
||||
|
||||
await init_integration(hass, legacy_config_entry)
|
||||
|
||||
entity_entry = entity_registry.async_get(f"sensor.{object_id}")
|
||||
assert entity_entry.unit_of_measurement == expected_unit
|
||||
|
||||
# LHM always calculated binary sizes, so the value is relabelled, not converted
|
||||
state = hass.states.get(f"sensor.{object_id}")
|
||||
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == expected_unit
|
||||
assert float(state.state) == expected_value
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_deprecated_lhm_client")
|
||||
async def test_deprecated_version_blocks_setup(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
|
||||
@@ -125,16 +125,25 @@ async def test_sensors_are_created(
|
||||
),
|
||||
pytest.param(
|
||||
SensorType.DATA,
|
||||
# LHM labels binary gigabytes as GB
|
||||
"GB",
|
||||
SensorDeviceClass.DATA_SIZE,
|
||||
UnitOfInformation.GIGABYTES,
|
||||
UnitOfInformation.GIBIBYTES,
|
||||
id="data",
|
||||
),
|
||||
pytest.param(
|
||||
SensorType.DATA,
|
||||
"B",
|
||||
SensorDeviceClass.DATA_SIZE,
|
||||
UnitOfInformation.GIBIBYTES,
|
||||
id="data_bytes",
|
||||
),
|
||||
pytest.param(
|
||||
SensorType.SMALL_DATA,
|
||||
# LHM labels binary megabytes as MB
|
||||
"MB",
|
||||
SensorDeviceClass.DATA_SIZE,
|
||||
UnitOfInformation.MEGABYTES,
|
||||
UnitOfInformation.GIBIBYTES,
|
||||
id="small_data",
|
||||
),
|
||||
pytest.param(
|
||||
|
||||
Reference in New Issue
Block a user