Files
core/homeassistant/components/switchbot_cloud/cover.py

232 lines
8.6 KiB
Python

"""Support for the Switchbot BlindTilt, Curtain, Curtain3, RollerShade as Cover."""
import asyncio
from typing import Any
from switchbot_api import (
BlindTiltCommands,
CommonCommands,
CurtainCommands,
Device,
Remote,
RollerShadeCommands,
SwitchBotAPI,
)
from homeassistant.components.cover import (
CoverDeviceClass,
CoverEntity,
CoverEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import SwitchbotCloudData, SwitchBotCoordinator
from .const import COVER_ENTITY_AFTER_COMMAND_REFRESH, DOMAIN
from .entity import SwitchBotCloudEntity
async def async_setup_entry(
hass: HomeAssistant,
config: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up SwitchBot Cloud entry."""
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
async_add_entities(
_async_make_entity(data.api, device, coordinator)
for device, coordinator in data.devices.covers
)
class SwitchBotCloudCover(SwitchBotCloudEntity, CoverEntity):
"""Representation of a SwitchBot Cover."""
_attr_name = None
_attr_is_closed: bool | None = None
def _set_attributes(self) -> None:
if self.coordinator.data is None:
return
position: int | None = self.coordinator.data.get("slidePosition")
if position is None:
return
self._attr_current_cover_position = 100 - position
self._attr_current_cover_tilt_position = 100 - position
self._attr_is_closed = position == 100
class SwitchBotCloudCoverCurtain(SwitchBotCloudCover):
"""Representation of a SwitchBot Curtain & Curtain3."""
_attr_device_class = CoverDeviceClass.CURTAIN
_attr_supported_features: CoverEntityFeature = (
CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE
| CoverEntityFeature.STOP
| CoverEntityFeature.SET_POSITION
)
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
await self.send_api_command(CommonCommands.ON)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the cover."""
await self.send_api_command(CommonCommands.OFF)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position."""
position: int | None = kwargs.get("position")
if position is not None:
await self.send_api_command(
CurtainCommands.SET_POSITION,
parameters=f"{0},ff,{100 - position}",
)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop the cover."""
await self.send_api_command(CurtainCommands.PAUSE)
await self.coordinator.async_request_refresh()
class SwitchBotCloudCoverRollerShade(SwitchBotCloudCover):
"""Representation of a SwitchBot RollerShade."""
_attr_device_class = CoverDeviceClass.SHADE
_attr_supported_features: CoverEntityFeature = (
CoverEntityFeature.SET_POSITION
| CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE
)
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
await self.send_api_command(RollerShadeCommands.SET_POSITION, parameters=0)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the cover."""
await self.send_api_command(RollerShadeCommands.SET_POSITION, parameters=100)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position."""
position: int | None = kwargs.get("position")
if position is not None:
await self.send_api_command(
RollerShadeCommands.SET_POSITION, parameters=(100 - position)
)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
class SwitchBotCloudCoverBlindTilt(SwitchBotCloudCover):
"""Representation of a SwitchBot Blind Tilt."""
_attr_direction: str | None = None
_attr_device_class = CoverDeviceClass.BLIND
_attr_supported_features: CoverEntityFeature = (
CoverEntityFeature.SET_TILT_POSITION
| CoverEntityFeature.OPEN_TILT
| CoverEntityFeature.CLOSE_TILT
)
def _set_attributes(self) -> None:
if self.coordinator.data is None:
return
position: int | None = self.coordinator.data.get("slidePosition")
if position is None:
return
self._attr_is_closed = position in [0, 100]
if position > 50:
percent = 100 - ((position - 50) * 2)
else:
percent = 100 - (50 - position) * 2
self._attr_current_cover_position = percent
self._attr_current_cover_tilt_position = percent
direction = self.coordinator.data.get("direction")
self._attr_direction = direction.lower() if direction else None
async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position."""
percent: int | None = kwargs.get("tilt_position")
if percent is not None:
await self.send_api_command(
BlindTiltCommands.SET_POSITION,
parameters=f"{self._attr_direction};{percent}",
)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
async def async_open_cover_tilt(self, **kwargs: Any) -> None:
"""Open the cover."""
await self.send_api_command(BlindTiltCommands.FULLY_OPEN)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
async def async_close_cover_tilt(self, **kwargs: Any) -> None:
"""Close the cover."""
if self._attr_direction is not None:
if "up" in self._attr_direction:
await self.send_api_command(BlindTiltCommands.CLOSE_UP)
else:
await self.send_api_command(BlindTiltCommands.CLOSE_DOWN)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
class SwitchBotCloudCoverGarageDoorOpener(SwitchBotCloudCover):
"""Representation of a SwitchBot Garage Door Opener."""
_attr_device_class = CoverDeviceClass.GARAGE
_attr_supported_features: CoverEntityFeature = (
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
)
def _set_attributes(self) -> None:
if self.coordinator.data is None:
return
door_status: int | None = self.coordinator.data.get("doorStatus")
self._attr_is_closed = None if door_status is None else door_status == 1
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
await self.send_api_command(CommonCommands.ON)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the cover."""
await self.send_api_command(CommonCommands.OFF)
await asyncio.sleep(COVER_ENTITY_AFTER_COMMAND_REFRESH)
await self.coordinator.async_request_refresh()
@callback
def _async_make_entity(
api: SwitchBotAPI, device: Device | Remote, coordinator: SwitchBotCoordinator
) -> (
SwitchBotCloudCoverBlindTilt
| SwitchBotCloudCoverRollerShade
| SwitchBotCloudCoverCurtain
| SwitchBotCloudCoverGarageDoorOpener
):
"""Make a SwitchBotCloudCover device."""
if device.device_type == "Blind Tilt":
return SwitchBotCloudCoverBlindTilt(api, device, coordinator)
if device.device_type == "Roller Shade":
return SwitchBotCloudCoverRollerShade(api, device, coordinator)
if device.device_type == "Garage Door Opener":
return SwitchBotCloudCoverGarageDoorOpener(api, device, coordinator)
return SwitchBotCloudCoverCurtain(api, device, coordinator)