Skip to content

Commit

Permalink
Deprecate some deprecated const constants (home-assistant#106230)
Browse files Browse the repository at this point in the history
* Deprecate some deprecated const constants

* Improve code

* fix typing

* Apply suggestions from code review

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
  • Loading branch information
edenhaus and MartinHjelmare authored Dec 23, 2023
1 parent 55a5e9c commit ebdf7b9
Show file tree
Hide file tree
Showing 6 changed files with 391 additions and 86 deletions.
56 changes: 28 additions & 28 deletions homeassistant/components/sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,36 @@

# pylint: disable-next=hass-deprecated-import
from homeassistant.const import ( # noqa: F401
_DEPRECATED_DEVICE_CLASS_AQI,
_DEPRECATED_DEVICE_CLASS_BATTERY,
_DEPRECATED_DEVICE_CLASS_CO,
_DEPRECATED_DEVICE_CLASS_CO2,
_DEPRECATED_DEVICE_CLASS_CURRENT,
_DEPRECATED_DEVICE_CLASS_DATE,
_DEPRECATED_DEVICE_CLASS_ENERGY,
_DEPRECATED_DEVICE_CLASS_FREQUENCY,
_DEPRECATED_DEVICE_CLASS_GAS,
_DEPRECATED_DEVICE_CLASS_HUMIDITY,
_DEPRECATED_DEVICE_CLASS_ILLUMINANCE,
_DEPRECATED_DEVICE_CLASS_MONETARY,
_DEPRECATED_DEVICE_CLASS_NITROGEN_DIOXIDE,
_DEPRECATED_DEVICE_CLASS_NITROGEN_MONOXIDE,
_DEPRECATED_DEVICE_CLASS_NITROUS_OXIDE,
_DEPRECATED_DEVICE_CLASS_OZONE,
_DEPRECATED_DEVICE_CLASS_PM1,
_DEPRECATED_DEVICE_CLASS_PM10,
_DEPRECATED_DEVICE_CLASS_PM25,
_DEPRECATED_DEVICE_CLASS_POWER,
_DEPRECATED_DEVICE_CLASS_POWER_FACTOR,
_DEPRECATED_DEVICE_CLASS_PRESSURE,
_DEPRECATED_DEVICE_CLASS_SIGNAL_STRENGTH,
_DEPRECATED_DEVICE_CLASS_SULPHUR_DIOXIDE,
_DEPRECATED_DEVICE_CLASS_TEMPERATURE,
_DEPRECATED_DEVICE_CLASS_TIMESTAMP,
_DEPRECATED_DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
_DEPRECATED_DEVICE_CLASS_VOLTAGE,
ATTR_UNIT_OF_MEASUREMENT,
CONF_UNIT_OF_MEASUREMENT,
DEVICE_CLASS_AQI,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_CO,
DEVICE_CLASS_CO2,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_DATE,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_FREQUENCY,
DEVICE_CLASS_GAS,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_MONETARY,
DEVICE_CLASS_NITROGEN_DIOXIDE,
DEVICE_CLASS_NITROGEN_MONOXIDE,
DEVICE_CLASS_NITROUS_OXIDE,
DEVICE_CLASS_OZONE,
DEVICE_CLASS_PM1,
DEVICE_CLASS_PM10,
DEVICE_CLASS_PM25,
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_SIGNAL_STRENGTH,
DEVICE_CLASS_SULPHUR_DIOXIDE,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_TIMESTAMP,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
DEVICE_CLASS_VOLTAGE,
EntityCategory,
UnitOfTemperature,
)
Expand Down
213 changes: 162 additions & 51 deletions homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import annotations

from enum import StrEnum
from typing import Final
from typing import Any, Final

APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2024
Expand Down Expand Up @@ -307,34 +307,147 @@ class Platform(StrEnum):
# #### DEVICE CLASSES ####
# DEVICE_CLASS_* below are deprecated as of 2021.12
# use the SensorDeviceClass enum instead.
DEVICE_CLASS_AQI: Final = "aqi"
DEVICE_CLASS_BATTERY: Final = "battery"
DEVICE_CLASS_CO: Final = "carbon_monoxide"
DEVICE_CLASS_CO2: Final = "carbon_dioxide"
DEVICE_CLASS_CURRENT: Final = "current"
DEVICE_CLASS_DATE: Final = "date"
DEVICE_CLASS_ENERGY: Final = "energy"
DEVICE_CLASS_FREQUENCY: Final = "frequency"
DEVICE_CLASS_GAS: Final = "gas"
DEVICE_CLASS_HUMIDITY: Final = "humidity"
DEVICE_CLASS_ILLUMINANCE: Final = "illuminance"
DEVICE_CLASS_MONETARY: Final = "monetary"
DEVICE_CLASS_NITROGEN_DIOXIDE = "nitrogen_dioxide"
DEVICE_CLASS_NITROGEN_MONOXIDE = "nitrogen_monoxide"
DEVICE_CLASS_NITROUS_OXIDE = "nitrous_oxide"
DEVICE_CLASS_OZONE: Final = "ozone"
DEVICE_CLASS_PM1: Final = "pm1"
DEVICE_CLASS_PM10: Final = "pm10"
DEVICE_CLASS_PM25: Final = "pm25"
DEVICE_CLASS_POWER_FACTOR: Final = "power_factor"
DEVICE_CLASS_POWER: Final = "power"
DEVICE_CLASS_PRESSURE: Final = "pressure"
DEVICE_CLASS_SIGNAL_STRENGTH: Final = "signal_strength"
DEVICE_CLASS_SULPHUR_DIOXIDE = "sulphur_dioxide"
DEVICE_CLASS_TEMPERATURE: Final = "temperature"
DEVICE_CLASS_TIMESTAMP: Final = "timestamp"
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds"
DEVICE_CLASS_VOLTAGE: Final = "voltage"
_DEPRECATED_DEVICE_CLASS_AQI: Final = ("aqi", "SensorDeviceClass.AQI", "2025.1")
_DEPRECATED_DEVICE_CLASS_BATTERY: Final = (
"battery",
"SensorDeviceClass.BATTERY",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_CO: Final = (
"carbon_monoxide",
"SensorDeviceClass.CO",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_CO2: Final = (
"carbon_dioxide",
"SensorDeviceClass.CO2",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_CURRENT: Final = (
"current",
"SensorDeviceClass.CURRENT",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_DATE: Final = ("date", "SensorDeviceClass.DATE", "2025.1")
_DEPRECATED_DEVICE_CLASS_ENERGY: Final = (
"energy",
"SensorDeviceClass.ENERGY",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_FREQUENCY: Final = (
"frequency",
"SensorDeviceClass.FREQUENCY",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_GAS: Final = ("gas", "SensorDeviceClass.GAS", "2025.1")
_DEPRECATED_DEVICE_CLASS_HUMIDITY: Final = (
"humidity",
"SensorDeviceClass.HUMIDITY",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_ILLUMINANCE: Final = (
"illuminance",
"SensorDeviceClass.ILLUMINANCE",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_MONETARY: Final = (
"monetary",
"SensorDeviceClass.MONETARY",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_NITROGEN_DIOXIDE = (
"nitrogen_dioxide",
"SensorDeviceClass.NITROGEN_DIOXIDE",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_NITROGEN_MONOXIDE = (
"nitrogen_monoxide",
"SensorDeviceClass.NITROGEN_MONOXIDE",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_NITROUS_OXIDE = (
"nitrous_oxide",
"SensorDeviceClass.NITROUS_OXIDE",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_OZONE: Final = ("ozone", "SensorDeviceClass.OZONE", "2025.1")
_DEPRECATED_DEVICE_CLASS_PM1: Final = ("pm1", "SensorDeviceClass.PM1", "2025.1")
_DEPRECATED_DEVICE_CLASS_PM10: Final = ("pm10", "SensorDeviceClass.PM10", "2025.1")
_DEPRECATED_DEVICE_CLASS_PM25: Final = ("pm25", "SensorDeviceClass.PM25", "2025.1")
_DEPRECATED_DEVICE_CLASS_POWER_FACTOR: Final = (
"power_factor",
"SensorDeviceClass.POWER_FACTOR",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_POWER: Final = ("power", "SensorDeviceClass.POWER", "2025.1")
_DEPRECATED_DEVICE_CLASS_PRESSURE: Final = (
"pressure",
"SensorDeviceClass.PRESSURE",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_SIGNAL_STRENGTH: Final = (
"signal_strength",
"SensorDeviceClass.SIGNAL_STRENGTH",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_SULPHUR_DIOXIDE = (
"sulphur_dioxide",
"SensorDeviceClass.SULPHUR_DIOXIDE",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_TEMPERATURE: Final = (
"temperature",
"SensorDeviceClass.TEMPERATURE",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_TIMESTAMP: Final = (
"timestamp",
"SensorDeviceClass.TIMESTAMP",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS = (
"volatile_organic_compounds",
"SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS",
"2025.1",
)
_DEPRECATED_DEVICE_CLASS_VOLTAGE: Final = (
"voltage",
"SensorDeviceClass.VOLTAGE",
"2025.1",
)


# Can be removed if no deprecated constant are in this module anymore
def __getattr__(name: str) -> Any:
"""Check if the not found name is a deprecated constant.
If it is, print a deprecation warning and return the value of the constant.
Otherwise raise AttributeError.
"""
module_globals = globals()
if f"_DEPRECATED_{name}" not in module_globals:
raise AttributeError(f"Module {__name__} has no attribute {name!r}")

# Avoid circular import
from .helpers.deprecation import ( # pylint: disable=import-outside-toplevel
check_if_deprecated_constant,
)

return check_if_deprecated_constant(name, module_globals)


# Can be removed if no deprecated constant are in this module anymore
def __dir__() -> list[str]:
"""Return dir() with deprecated constants."""
# Copied method from homeassistant.helpers.deprecattion#dir_with_deprecated_constants to avoid import cycle
module_globals = globals()

return list(module_globals) + [
name.removeprefix("_DEPRECATED_")
for name in module_globals
if name.startswith("_DEPRECATED_")
]


# #### STATES ####
STATE_ON: Final = "on"
Expand Down Expand Up @@ -1168,14 +1281,28 @@ class UnitOfDataRate(StrEnum):
# cloud, alexa, or google_home components
CLOUD_NEVER_EXPOSED_ENTITIES: Final[list[str]] = ["group.all_locks"]


class EntityCategory(StrEnum):
"""Category of an entity.
An entity with a category will:
- Not be exposed to cloud, Alexa, or Google Assistant components
- Not be included in indirect service calls to devices or areas
"""

# Config: An entity which allows changing the configuration of a device.
CONFIG = "config"

# Diagnostic: An entity exposing some configuration parameter,
# or diagnostics of a device.
DIAGNOSTIC = "diagnostic"


# ENTITY_CATEGOR* below are deprecated as of 2021.12
# use the EntityCategory enum instead.
ENTITY_CATEGORY_CONFIG: Final = "config"
ENTITY_CATEGORY_DIAGNOSTIC: Final = "diagnostic"
ENTITY_CATEGORIES: Final[list[str]] = [
ENTITY_CATEGORY_CONFIG,
ENTITY_CATEGORY_DIAGNOSTIC,
]
_DEPRECATED_ENTITY_CATEGORY_CONFIG: Final = (EntityCategory.CONFIG, "2025.1")
_DEPRECATED_ENTITY_CATEGORY_DIAGNOSTIC: Final = (EntityCategory.DIAGNOSTIC, "2025.1")
ENTITY_CATEGORIES: Final[list[str]] = [cls.value for cls in EntityCategory]

# The ID of the Home Assistant Media Player Cast App
CAST_APP_ID_HOMEASSISTANT_MEDIA: Final = "B45F4572"
Expand All @@ -1191,19 +1318,3 @@ class UnitOfDataRate(StrEnum):
FORMAT_DATE: Final = "%Y-%m-%d"
FORMAT_TIME: Final = "%H:%M:%S"
FORMAT_DATETIME: Final = f"{FORMAT_DATE} {FORMAT_TIME}"


class EntityCategory(StrEnum):
"""Category of an entity.
An entity with a category will:
- Not be exposed to cloud, Alexa, or Google Assistant components
- Not be included in indirect service calls to devices or areas
"""

# Config: An entity which allows changing the configuration of a device.
CONFIG = "config"

# Diagnostic: An entity exposing some configuration parameter,
# or diagnostics of a device.
DIAGNOSTIC = "diagnostic"
18 changes: 16 additions & 2 deletions homeassistant/helpers/deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
"""
module_name = module_globals.get("__name__")
logger = logging.getLogger(module_name)
value = replacement = None
if (deprecated_const := module_globals.get(_PREFIX_DEPRECATED + name)) is None:
raise AttributeError(f"Module {module_name!r} has no attribute {name!r}")
if isinstance(deprecated_const, DeprecatedConstant):
Expand All @@ -264,9 +265,22 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
f"{deprecated_const.enum.__class__.__name__}.{deprecated_const.enum.name}"
)
breaks_in_ha_version = deprecated_const.breaks_in_ha_version
else:
elif isinstance(deprecated_const, tuple):
# Use DeprecatedConstant and DeprecatedConstant instead, where possible
# Used to avoid import cycles.
if len(deprecated_const) == 3:
value = deprecated_const[0]
replacement = deprecated_const[1]
breaks_in_ha_version = deprecated_const[2]
elif len(deprecated_const) == 2 and isinstance(deprecated_const[0], Enum):
enum = deprecated_const[0]
value = enum.value
replacement = f"{enum.__class__.__name__}.{enum.name}"
breaks_in_ha_version = deprecated_const[1]

if value is None or replacement is None:
msg = (
f"Value of {_PREFIX_DEPRECATED}{name!r} is an instance of {type(deprecated_const)} "
f"Value of {_PREFIX_DEPRECATED}{name} is an instance of {type(deprecated_const)} "
"but an instance of DeprecatedConstant or DeprecatedConstantEnum is required"
)

Expand Down
43 changes: 43 additions & 0 deletions tests/components/sensor/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -2535,3 +2535,46 @@ def test_deprecated_constants(
import_and_test_deprecated_constant_enum(
caplog, module, enum, "STATE_CLASS_", "2025.1"
)


@pytest.mark.parametrize(
("enum"),
[
sensor.SensorDeviceClass.AQI,
sensor.SensorDeviceClass.BATTERY,
sensor.SensorDeviceClass.CO,
sensor.SensorDeviceClass.CO2,
sensor.SensorDeviceClass.CURRENT,
sensor.SensorDeviceClass.DATE,
sensor.SensorDeviceClass.ENERGY,
sensor.SensorDeviceClass.FREQUENCY,
sensor.SensorDeviceClass.GAS,
sensor.SensorDeviceClass.HUMIDITY,
sensor.SensorDeviceClass.ILLUMINANCE,
sensor.SensorDeviceClass.MONETARY,
sensor.SensorDeviceClass.NITROGEN_DIOXIDE,
sensor.SensorDeviceClass.NITROGEN_MONOXIDE,
sensor.SensorDeviceClass.NITROUS_OXIDE,
sensor.SensorDeviceClass.OZONE,
sensor.SensorDeviceClass.PM1,
sensor.SensorDeviceClass.PM10,
sensor.SensorDeviceClass.PM25,
sensor.SensorDeviceClass.POWER_FACTOR,
sensor.SensorDeviceClass.POWER,
sensor.SensorDeviceClass.PRESSURE,
sensor.SensorDeviceClass.SIGNAL_STRENGTH,
sensor.SensorDeviceClass.SULPHUR_DIOXIDE,
sensor.SensorDeviceClass.TEMPERATURE,
sensor.SensorDeviceClass.TIMESTAMP,
sensor.SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
sensor.SensorDeviceClass.VOLTAGE,
],
)
def test_deprecated_constants_sensor_device_class(
caplog: pytest.LogCaptureFixture,
enum: sensor.SensorStateClass,
) -> None:
"""Test deprecated constants."""
import_and_test_deprecated_constant_enum(
caplog, sensor, enum, "DEVICE_CLASS_", "2025.1"
)
Loading

0 comments on commit ebdf7b9

Please sign in to comment.