diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 39422c530b3..19cd851002a 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -32,7 +32,7 @@ if TYPE_CHECKING: ServiceCall, UserService DOMAIN = 'esphome' -REQUIREMENTS = ['aioesphomeapi==1.7.0'] +REQUIREMENTS = ['aioesphomeapi==2.0.0'] _LOGGER = logging.getLogger(__name__) @@ -381,16 +381,15 @@ async def _async_setup_device_registry(hass: HomeAssistantType, async def _register_service(hass: HomeAssistantType, entry_data: RuntimeEntryData, service: 'UserService'): - from aioesphomeapi import USER_SERVICE_ARG_BOOL, USER_SERVICE_ARG_INT, \ - USER_SERVICE_ARG_FLOAT, USER_SERVICE_ARG_STRING + from aioesphomeapi import UserServiceArgType service_name = '{}_{}'.format(entry_data.device_info.name, service.name) schema = {} for arg in service.args: schema[vol.Required(arg.name)] = { - USER_SERVICE_ARG_BOOL: cv.boolean, - USER_SERVICE_ARG_INT: vol.Coerce(int), - USER_SERVICE_ARG_FLOAT: vol.Coerce(float), - USER_SERVICE_ARG_STRING: cv.string, + UserServiceArgType.BOOL: cv.boolean, + UserServiceArgType.INT: vol.Coerce(int), + UserServiceArgType.FLOAT: vol.Coerce(float), + UserServiceArgType.STRING: cv.string, }[arg.type_] async def execute_service(call): diff --git a/homeassistant/components/esphome/cover.py b/homeassistant/components/esphome/cover.py index d86c40e627e..68eb4221a93 100644 --- a/homeassistant/components/esphome/cover.py +++ b/homeassistant/components/esphome/cover.py @@ -3,7 +3,9 @@ import logging from typing import TYPE_CHECKING, Optional from homeassistant.components.cover import ( - SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_STOP, CoverDevice) + ATTR_POSITION, ATTR_TILT_POSITION, SUPPORT_CLOSE, SUPPORT_CLOSE_TILT, + SUPPORT_OPEN, SUPPORT_OPEN_TILT, SUPPORT_SET_POSITION, + SUPPORT_SET_TILT_POSITION, SUPPORT_STOP, CoverDevice) from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType @@ -38,44 +40,97 @@ class EsphomeCover(EsphomeEntity, CoverDevice): def _static_info(self) -> 'CoverInfo': return super()._static_info - @property - def _state(self) -> Optional['CoverState']: - return super()._state - @property def supported_features(self) -> int: """Flag supported features.""" - return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP + flags = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP + if self._static_info.supports_position: + flags |= SUPPORT_SET_POSITION + if self._static_info.supports_tilt: + flags |= (SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT | + SUPPORT_SET_TILT_POSITION) + return flags + + @property + def device_class(self): + """Return the class of this device, from component DEVICE_CLASSES.""" + return self._static_info.device_class @property def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" - return self._static_info.is_optimistic + return self._static_info.assumed_state + + @property + def _state(self) -> Optional['CoverState']: + return super()._state @property def is_closed(self) -> Optional[bool]: """Return if the cover is closed or not.""" if self._state is None: return None - return bool(self._state.state) + # Check closed state with api version due to a protocol change + return self._state.is_closed(self._client.api_version) + + @property + def is_opening(self): + """Return if the cover is opening or not.""" + from aioesphomeapi import CoverOperation + if self._state is None: + return None + return self._state.current_operation == CoverOperation.IS_OPENING + + @property + def is_closing(self): + """Return if the cover is closing or not.""" + from aioesphomeapi import CoverOperation + if self._state is None: + return None + return self._state.current_operation == CoverOperation.IS_CLOSING + + @property + def current_cover_position(self) -> Optional[float]: + """Return current position of cover. 0 is closed, 100 is open.""" + if self._state is None or not self._static_info.supports_position: + return None + return self._state.position * 100.0 + + @property + def current_cover_tilt_position(self) -> Optional[float]: + """Return current position of cover tilt. 0 is closed, 100 is open.""" + if self._state is None or not self._static_info.supports_tilt: + return None + return self._state.tilt * 100.0 async def async_open_cover(self, **kwargs) -> None: """Open the cover.""" - from aioesphomeapi.client import COVER_COMMAND_OPEN - await self._client.cover_command(key=self._static_info.key, - command=COVER_COMMAND_OPEN) + position=1.0) async def async_close_cover(self, **kwargs) -> None: """Close cover.""" - from aioesphomeapi.client import COVER_COMMAND_CLOSE - await self._client.cover_command(key=self._static_info.key, - command=COVER_COMMAND_CLOSE) + position=0.0) - async def async_stop_cover(self, **kwargs): + async def async_stop_cover(self, **kwargs) -> None: """Stop the cover.""" - from aioesphomeapi.client import COVER_COMMAND_STOP + await self._client.cover_command(key=self._static_info.key, stop=True) + async def async_set_cover_position(self, **kwargs) -> None: + """Move the cover to a specific position.""" await self._client.cover_command(key=self._static_info.key, - command=COVER_COMMAND_STOP) + position=kwargs[ATTR_POSITION] / 100) + + async def async_open_cover_tilt(self, **kwargs) -> None: + """Open the cover tilt.""" + await self._client.cover_command(key=self._static_info.key, tilt=1.0) + + async def async_close_cover_tilt(self, **kwargs) -> None: + """Close the cover tilt.""" + await self._client.cover_command(key=self._static_info.key, tilt=0.0) + + async def async_set_cover_tilt_position(self, **kwargs) -> None: + """Move the cover tilt to a specific position.""" + await self._client.cover_command(key=self._static_info.key, + tilt=kwargs[ATTR_TILT_POSITION] / 100) diff --git a/homeassistant/components/esphome/fan.py b/homeassistant/components/esphome/fan.py index 05f18cb014a..973fa85774c 100644 --- a/homeassistant/components/esphome/fan.py +++ b/homeassistant/components/esphome/fan.py @@ -12,7 +12,7 @@ from . import EsphomeEntity, platform_async_setup_entry if TYPE_CHECKING: # pylint: disable=unused-import - from aioesphomeapi import FanInfo, FanState # noqa + from aioesphomeapi import FanInfo, FanState, FanSpeed # noqa DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) @@ -32,12 +32,24 @@ async def async_setup_entry(hass: HomeAssistantType, ) -FAN_SPEED_STR_TO_INT = { - SPEED_LOW: 0, - SPEED_MEDIUM: 1, - SPEED_HIGH: 2 -} -FAN_SPEED_INT_TO_STR = {v: k for k, v in FAN_SPEED_STR_TO_INT.items()} +def _ha_fan_speed_to_esphome(speed: str) -> 'FanSpeed': + # pylint: disable=redefined-outer-name + from aioesphomeapi import FanSpeed # noqa + return { + SPEED_LOW: FanSpeed.LOW, + SPEED_MEDIUM: FanSpeed.MEDIUM, + SPEED_HIGH: FanSpeed.HIGH, + }[speed] + + +def _esphome_fan_speed_to_ha(speed: 'FanSpeed') -> str: + # pylint: disable=redefined-outer-name + from aioesphomeapi import FanSpeed # noqa + return { + FanSpeed.LOW: SPEED_LOW, + FanSpeed.MEDIUM: SPEED_MEDIUM, + FanSpeed.HIGH: SPEED_HIGH, + }[speed] class EsphomeFan(EsphomeEntity, FanEntity): @@ -56,8 +68,9 @@ class EsphomeFan(EsphomeEntity, FanEntity): if speed == SPEED_OFF: await self.async_turn_off() return + await self._client.fan_command( - self._static_info.key, speed=FAN_SPEED_STR_TO_INT[speed]) + self._static_info.key, speed=_ha_fan_speed_to_esphome(speed)) async def async_turn_on(self, speed: Optional[str] = None, **kwargs) -> None: @@ -67,7 +80,7 @@ class EsphomeFan(EsphomeEntity, FanEntity): return data = {'key': self._static_info.key, 'state': True} if speed is not None: - data['speed'] = FAN_SPEED_STR_TO_INT[speed] + data['speed'] = _ha_fan_speed_to_esphome(speed) await self._client.fan_command(**data) # pylint: disable=arguments-differ @@ -94,7 +107,7 @@ class EsphomeFan(EsphomeEntity, FanEntity): return None if not self._static_info.supports_speed: return None - return FAN_SPEED_INT_TO_STR[self._state.speed] + return _esphome_fan_speed_to_ha(self._state.speed) @property def oscillating(self) -> None: diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index b00cdf9607d..734544b49c7 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -1,9 +1,9 @@ { "domain": "esphome", - "name": "Esphome", + "name": "ESPHome", "documentation": "https://www.home-assistant.io/components/esphome", "requirements": [ - "aioesphomeapi==1.7.0" + "aioesphomeapi==2.0.0" ], "dependencies": [], "codeowners": [ diff --git a/homeassistant/components/esphome/switch.py b/homeassistant/components/esphome/switch.py index e5a9d0cf446..e736c1df209 100644 --- a/homeassistant/components/esphome/switch.py +++ b/homeassistant/components/esphome/switch.py @@ -49,7 +49,7 @@ class EsphomeSwitch(EsphomeEntity, SwitchDevice): @property def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" - return self._static_info.optimistic + return self._static_info.assumed_state @property def is_on(self): diff --git a/requirements_all.txt b/requirements_all.txt index 76ec55ee3ce..9709d7c87ef 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -112,7 +112,7 @@ aiobotocore==0.10.2 aiodns==1.1.1 # homeassistant.components.esphome -aioesphomeapi==1.7.0 +aioesphomeapi==2.0.0 # homeassistant.components.freebox aiofreepybox==0.0.8