Refactor support for integrations to drop custom unit conversion (#83228)

* Refactor support for integrations to drop custom unit conversion

* Fix lying comment

* Address review comment
This commit is contained in:
Erik Montnemery 2022-12-05 16:12:37 +01:00 committed by GitHub
parent 1f7a7d5cb5
commit 67875b99a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 28 deletions

View file

@ -484,10 +484,19 @@ class SensorEntity(Entity):
platform: EntityPlatform,
parallel_updates: asyncio.Semaphore | None,
) -> None:
"""Start adding an entity to a platform."""
"""Start adding an entity to a platform.
Allows integrations to remove legacy custom unit conversion which is no longer
needed without breaking existing sensors. Only works for sensors which are in
the entity registry.
This can be removed once core integrations have dropped unneeded custom unit
conversion.
"""
super().add_to_platform_start(hass, platform, parallel_updates)
if self.unique_id is None:
# Bail out if the sensor doesn't have a unique_id or a device class
if self.unique_id is None or self.device_class is None:
return
registry = er.async_get(self.hass)
if not (
@ -499,24 +508,36 @@ class SensorEntity(Entity):
registry_entry = registry.async_get(entity_id)
assert registry_entry
# Store unit override according to automatic unit conversion rules if:
# - no unit override is stored in the entity registry
# - units have changed
# - the unit stored in the registry matches automatic unit conversion rules
# This allows integrations to drop custom unit conversion and rely on automatic
# conversion.
# If the sensor has 'unit_of_measurement' in its sensor options, the user has
# overridden the unit.
# If the sensor has 'sensor.private' in its entity options, it was added after
# automatic unit conversion was implemented.
registry_unit = registry_entry.unit_of_measurement
if (
DOMAIN not in registry_entry.options
and f"{DOMAIN}.private" not in registry_entry.options
and self.unit_of_measurement != registry_unit
and (suggested_unit := self._get_initial_suggested_unit()) == registry_unit
):
registry.async_update_entity_options(
entity_id,
f"{DOMAIN}.private",
{"suggested_unit_of_measurement": suggested_unit},
(
(sensor_options := registry_entry.options.get(DOMAIN))
and CONF_UNIT_OF_MEASUREMENT in sensor_options
)
or f"{DOMAIN}.private" in registry_entry.options
or self.unit_of_measurement == registry_unit
):
return
# Make sure we can convert the units
if (
(unit_converter := UNIT_CONVERTERS.get(self.device_class)) is None
or registry_unit not in unit_converter.VALID_UNITS
or self.unit_of_measurement not in unit_converter.VALID_UNITS
):
return
# Set suggested_unit_of_measurement to the old unit to enable automatic
# conversion
registry.async_update_entity_options(
entity_id,
f"{DOMAIN}.private",
{"suggested_unit_of_measurement": registry_unit},
)
async def async_internal_added_to_hass(self) -> None:
"""Call when the sensor entity is added to hass."""
@ -572,8 +593,12 @@ class SensorEntity(Entity):
return None
def _get_initial_suggested_unit(self) -> str | None:
"""Return initial suggested unit of measurement."""
def get_initial_entity_options(self) -> er.EntityOptionsType | None:
"""Return initial entity options.
These will be stored in the entity registry the first time the entity is seen,
and then never updated.
"""
# Unit suggested by the integration
suggested_unit_of_measurement = self.suggested_unit_of_measurement
@ -583,15 +608,6 @@ class SensorEntity(Entity):
self.device_class, self.native_unit_of_measurement
)
return suggested_unit_of_measurement
def get_initial_entity_options(self) -> er.EntityOptionsType | None:
"""Return initial entity options.
These will be stored in the entity registry the first time the entity is seen,
and then never updated.
"""
suggested_unit_of_measurement = self._get_initial_suggested_unit()
if suggested_unit_of_measurement is None:
return None

View file

@ -889,6 +889,14 @@ async def test_unit_conversion_priority_suggested_unit_change(
621,
SensorDeviceClass.DISTANCE,
),
(
US_CUSTOMARY_SYSTEM,
LENGTH_METERS,
LENGTH_MILES,
1000000,
621.371,
SensorDeviceClass.DISTANCE,
),
],
)
async def test_unit_conversion_priority_legacy_conversion_removed(