Skip to content

Commit

Permalink
Move enum mapper to own file to prevent circular dependency (#90890)
Browse files Browse the repository at this point in the history
* Move enum_mapper to own file to prevent circular dependency

* Add enum mapper test
  • Loading branch information
jesserockz authored Apr 6, 2023
1 parent 2fc34e7 commit eb469d6
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 67 deletions.
39 changes: 2 additions & 37 deletions homeassistant/components/esphome/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
import functools
import logging
import math
from typing import Any, Generic, NamedTuple, TypeVar, cast, overload
from typing import Any, Generic, NamedTuple, TypeVar, cast

from aioesphomeapi import (
APIClient,
APIConnectionError,
APIIntEnum,
APIVersion,
DeviceInfo as EsphomeDeviceInfo,
EntityCategory as EsphomeEntityCategory,
Expand Down Expand Up @@ -64,6 +63,7 @@

# Import config flow so that it's added to the registry
from .entry_data import RuntimeEntryData
from .enum_mapper import EsphomeEnumMapper

CONF_DEVICE_NAME = "device_name"
CONF_NOISE_PSK = "noise_psk"
Expand Down Expand Up @@ -687,41 +687,6 @@ def _wrapper(self: _EntityT) -> _R | None:
return _wrapper


_EnumT = TypeVar("_EnumT", bound=APIIntEnum)
_ValT = TypeVar("_ValT")


class EsphomeEnumMapper(Generic[_EnumT, _ValT]):
"""Helper class to convert between hass and esphome enum values."""

def __init__(self, mapping: dict[_EnumT, _ValT]) -> None:
"""Construct a EsphomeEnumMapper."""
# Add none mapping
augmented_mapping: dict[
_EnumT | None, _ValT | None
] = mapping # type: ignore[assignment]
augmented_mapping[None] = None

self._mapping = augmented_mapping
self._inverse: dict[_ValT, _EnumT] = {v: k for k, v in mapping.items()}

@overload
def from_esphome(self, value: _EnumT) -> _ValT:
...

@overload
def from_esphome(self, value: _EnumT | None) -> _ValT | None:
...

def from_esphome(self, value: _EnumT | None) -> _ValT | None:
"""Convert from an esphome int representation to a hass string."""
return self._mapping[value]

def from_hass(self, value: _ValT) -> _EnumT:
"""Convert from a hass string to a esphome int representation."""
return self._inverse[value]


ICON_SCHEMA = vol.Schema(cv.icon)


Expand Down
8 changes: 2 additions & 6 deletions homeassistant/components/esphome/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,8 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import (
EsphomeEntity,
EsphomeEnumMapper,
esphome_state_property,
platform_async_setup_entry,
)
from . import EsphomeEntity, esphome_state_property, platform_async_setup_entry
from .enum_mapper import EsphomeEnumMapper

FAN_QUIET = "quiet"

Expand Down
39 changes: 39 additions & 0 deletions homeassistant/components/esphome/enum_mapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Helper class to convert between Home Assistant and ESPHome enum values."""

from typing import Generic, TypeVar, overload

from aioesphomeapi import APIIntEnum

_EnumT = TypeVar("_EnumT", bound=APIIntEnum)
_ValT = TypeVar("_ValT")


class EsphomeEnumMapper(Generic[_EnumT, _ValT]):
"""Helper class to convert between hass and esphome enum values."""

def __init__(self, mapping: dict[_EnumT, _ValT]) -> None:
"""Construct a EsphomeEnumMapper."""
# Add none mapping
augmented_mapping: dict[
_EnumT | None, _ValT | None
] = mapping # type: ignore[assignment]
augmented_mapping[None] = None

self._mapping = augmented_mapping
self._inverse: dict[_ValT, _EnumT] = {v: k for k, v in mapping.items()}

@overload
def from_esphome(self, value: _EnumT) -> _ValT:
...

@overload
def from_esphome(self, value: _EnumT | None) -> _ValT | None:
...

def from_esphome(self, value: _EnumT | None) -> _ValT | None:
"""Convert from an esphome int representation to a hass string."""
return self._mapping[value]

def from_hass(self, value: _ValT) -> _EnumT:
"""Convert from a hass string to a esphome int representation."""
return self._inverse[value]
8 changes: 2 additions & 6 deletions homeassistant/components/esphome/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@
ranged_value_to_percentage,
)

from . import (
EsphomeEntity,
EsphomeEnumMapper,
esphome_state_property,
platform_async_setup_entry,
)
from . import EsphomeEntity, esphome_state_property, platform_async_setup_entry
from .enum_mapper import EsphomeEnumMapper

ORDERED_NAMED_FAN_SPEEDS = [FanSpeed.LOW, FanSpeed.MEDIUM, FanSpeed.HIGH]

Expand Down
8 changes: 2 additions & 6 deletions homeassistant/components/esphome/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,8 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import (
EsphomeEntity,
EsphomeEnumMapper,
esphome_state_property,
platform_async_setup_entry,
)
from . import EsphomeEntity, esphome_state_property, platform_async_setup_entry
from .enum_mapper import EsphomeEnumMapper


async def async_setup_entry(
Expand Down
8 changes: 2 additions & 6 deletions homeassistant/components/esphome/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.enum import try_parse_enum

from . import (
EsphomeEntity,
EsphomeEnumMapper,
esphome_state_property,
platform_async_setup_entry,
)
from . import EsphomeEntity, esphome_state_property, platform_async_setup_entry
from .enum_mapper import EsphomeEnumMapper


async def async_setup_entry(
Expand Down
8 changes: 2 additions & 6 deletions homeassistant/components/esphome/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,8 @@
from homeassistant.util import dt
from homeassistant.util.enum import try_parse_enum

from . import (
EsphomeEntity,
EsphomeEnumMapper,
esphome_state_property,
platform_async_setup_entry,
)
from . import EsphomeEntity, esphome_state_property, platform_async_setup_entry
from .enum_mapper import EsphomeEnumMapper


async def async_setup_entry(
Expand Down
42 changes: 42 additions & 0 deletions tests/components/esphome/test_enum_mapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Test ESPHome enum mapper."""

from aioesphomeapi import APIIntEnum

from homeassistant.backports.enum import StrEnum
from homeassistant.components.esphome.enum_mapper import EsphomeEnumMapper


class MockEnum(APIIntEnum):
"""Mock enum."""

ESPHOME_FOO = 1
ESPHOME_BAR = 2


class MockStrEnum(StrEnum):
"""Mock enum."""

HA_FOO = "foo"
HA_BAR = "bar"


MOCK_MAPPING: EsphomeEnumMapper[MockEnum, MockStrEnum] = EsphomeEnumMapper(
{
MockEnum.ESPHOME_FOO: MockStrEnum.HA_FOO,
MockEnum.ESPHOME_BAR: MockStrEnum.HA_BAR,
}
)


async def test_map_esphome_to_ha() -> None:
"""Test mapping from ESPHome to HA."""

assert MOCK_MAPPING.from_esphome(MockEnum.ESPHOME_FOO) == MockStrEnum.HA_FOO
assert MOCK_MAPPING.from_esphome(MockEnum.ESPHOME_BAR) == MockStrEnum.HA_BAR


async def test_map_ha_to_esphome() -> None:
"""Test mapping from HA to ESPHome."""

assert MOCK_MAPPING.from_hass(MockStrEnum.HA_FOO) == MockEnum.ESPHOME_FOO
assert MOCK_MAPPING.from_hass(MockStrEnum.HA_BAR) == MockEnum.ESPHOME_BAR

0 comments on commit eb469d6

Please sign in to comment.