core/homeassistant/components/cover/__init__.py

377 lines
11 KiB
Python
Raw Normal View History

"""Support for Cover devices."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
2017-02-02 20:39:13 +00:00
import functools as ft
import logging
from typing import Any, final
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
2019-07-31 19:25:30 +00:00
SERVICE_CLOSE_COVER,
SERVICE_CLOSE_COVER_TILT,
SERVICE_OPEN_COVER,
SERVICE_OPEN_COVER_TILT,
2019-07-31 19:25:30 +00:00
SERVICE_SET_COVER_POSITION,
SERVICE_SET_COVER_TILT_POSITION,
2019-07-31 19:25:30 +00:00
SERVICE_STOP_COVER,
SERVICE_STOP_COVER_TILT,
SERVICE_TOGGLE,
2019-07-31 19:25:30 +00:00
SERVICE_TOGGLE_COVER_TILT,
STATE_CLOSED,
STATE_CLOSING,
STATE_OPEN,
STATE_OPENING,
2019-07-31 19:25:30 +00:00
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
)
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.loader import bind_hass
# mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
DOMAIN = "cover"
SCAN_INTERVAL = timedelta(seconds=15)
2019-07-31 19:25:30 +00:00
ENTITY_ID_FORMAT = DOMAIN + ".{}"
# Refer to the cover dev docs for device class descriptions
2019-07-31 19:25:30 +00:00
DEVICE_CLASS_AWNING = "awning"
DEVICE_CLASS_BLIND = "blind"
DEVICE_CLASS_CURTAIN = "curtain"
DEVICE_CLASS_DAMPER = "damper"
DEVICE_CLASS_DOOR = "door"
DEVICE_CLASS_GARAGE = "garage"
DEVICE_CLASS_GATE = "gate"
2019-07-31 19:25:30 +00:00
DEVICE_CLASS_SHADE = "shade"
DEVICE_CLASS_SHUTTER = "shutter"
DEVICE_CLASS_WINDOW = "window"
DEVICE_CLASSES = [
DEVICE_CLASS_AWNING,
DEVICE_CLASS_BLIND,
DEVICE_CLASS_CURTAIN,
DEVICE_CLASS_DAMPER,
DEVICE_CLASS_DOOR,
DEVICE_CLASS_GARAGE,
DEVICE_CLASS_GATE,
DEVICE_CLASS_SHADE,
DEVICE_CLASS_SHUTTER,
2019-07-31 19:25:30 +00:00
DEVICE_CLASS_WINDOW,
]
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
SUPPORT_OPEN = 1
SUPPORT_CLOSE = 2
SUPPORT_SET_POSITION = 4
SUPPORT_STOP = 8
SUPPORT_OPEN_TILT = 16
SUPPORT_CLOSE_TILT = 32
SUPPORT_STOP_TILT = 64
SUPPORT_SET_TILT_POSITION = 128
2019-07-31 19:25:30 +00:00
ATTR_CURRENT_POSITION = "current_position"
ATTR_CURRENT_TILT_POSITION = "current_tilt_position"
ATTR_POSITION = "position"
ATTR_TILT_POSITION = "tilt_position"
@bind_hass
def is_closed(hass, entity_id):
"""Return if the cover is closed based on the statemachine."""
return hass.states.is_state(entity_id, STATE_CLOSED)
async def async_setup(hass, config):
"""Track states and offer events for covers."""
component = hass.data[DOMAIN] = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
2019-07-31 19:25:30 +00:00
)
await component.async_setup(config)
2017-02-02 20:39:13 +00:00
component.async_register_entity_service(
SERVICE_OPEN_COVER, {}, "async_open_cover", [SUPPORT_OPEN]
)
component.async_register_entity_service(
SERVICE_CLOSE_COVER, {}, "async_close_cover", [SUPPORT_CLOSE]
)
component.async_register_entity_service(
2019-07-31 19:25:30 +00:00
SERVICE_SET_COVER_POSITION,
{
vol.Required(ATTR_POSITION): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
)
},
2019-07-31 19:25:30 +00:00
"async_set_cover_position",
[SUPPORT_SET_POSITION],
)
component.async_register_entity_service(
SERVICE_STOP_COVER, {}, "async_stop_cover", [SUPPORT_STOP]
)
component.async_register_entity_service(
SERVICE_TOGGLE, {}, "async_toggle", [SUPPORT_OPEN | SUPPORT_CLOSE]
)
component.async_register_entity_service(
SERVICE_OPEN_COVER_TILT, {}, "async_open_cover_tilt", [SUPPORT_OPEN_TILT]
)
component.async_register_entity_service(
SERVICE_CLOSE_COVER_TILT, {}, "async_close_cover_tilt", [SUPPORT_CLOSE_TILT]
)
component.async_register_entity_service(
SERVICE_STOP_COVER_TILT, {}, "async_stop_cover_tilt", [SUPPORT_STOP_TILT]
)
component.async_register_entity_service(
2019-07-31 19:25:30 +00:00
SERVICE_SET_COVER_TILT_POSITION,
{
vol.Required(ATTR_TILT_POSITION): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
)
},
2019-07-31 19:25:30 +00:00
"async_set_cover_tilt_position",
[SUPPORT_SET_TILT_POSITION],
)
component.async_register_entity_service(
SERVICE_TOGGLE_COVER_TILT,
{},
"async_toggle_tilt",
[SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT],
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry."""
component: EntityComponent = hass.data[DOMAIN]
return await component.async_setup_entry(entry)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
component: EntityComponent = hass.data[DOMAIN]
return await component.async_unload_entry(entry)
@dataclass
class CoverEntityDescription(EntityDescription):
"""A class that describes cover entities."""
class CoverEntity(Entity):
"""Base class for cover entities."""
entity_description: CoverEntityDescription
_attr_current_cover_position: int | None = None
_attr_current_cover_tilt_position: int | None = None
_attr_is_closed: bool | None
_attr_is_closing: bool | None = None
_attr_is_opening: bool | None = None
_attr_state: None = None
@property
def current_cover_position(self) -> int | None:
"""Return current position of cover.
None is unknown, 0 is closed, 100 is fully open.
"""
return self._attr_current_cover_position
@property
def current_cover_tilt_position(self) -> int | None:
"""Return current position of cover tilt.
None is unknown, 0 is closed, 100 is fully open.
"""
return self._attr_current_cover_tilt_position
@property
@final
def state(self) -> str | None:
"""Return the state of the cover."""
Fix COMMAND_CLASS_BARRIER_OPERATOR for dev branch of OpenZwave (#8574) * Update zwave.py to work with updated OpenZwave library Update zwave.py to work with updated OpenZwave library * Update zwave.py * Update zwave.py * Update to fix garage door openers Update to fix garage door support for latest version of openzwavelib * Update to cover.zwave list of states Update to cover.zwave to provide list of states based on dev version of openzwave lib * Some values not saved * Formatting fix * Formatting fix * Variable typo * Formatting fix * Formatting * Variable Update Variable Update and properties added * Formatting fixes * Formatting Fix * Update test case for door states * Formatting / Testing process fix * Formatting * Formatting / Test Fixes * Variable rename * Added members to CoverDevice * Removed un-needed else * Formatting * Formatting * Variable name changes and const updates * Changed variable names to cover_state * Added constains into const.py * Updated to change the main state on the cover device * Fixes * Formatting fixes * Formatting/Variables * Formatting * Variable fixes * Import update * Formatting / Variables * Update test for new states * Revert state changes * Test fix * Variable Fix * Formatting * Variable typo * Missing constant * Variable fix * Requested changes * Added is_opening * Added is_closing * Updated test based on changes * Formatting * Changed cover_state back to _state * Formatting and variable fixes * Test fixes * Formatting and variable touchup * Formatting * Optimizations * Add new cover features to demo * Add tests for demo cover closing/opening * Remove unused STATE_STOPPED * Add tests for new zwave cover values
2017-07-27 22:57:30 +00:00
if self.is_opening:
return STATE_OPENING
if self.is_closing:
return STATE_CLOSING
2021-10-17 17:56:00 +00:00
if (closed := self.is_closed) is None:
return None
return STATE_CLOSED if closed else STATE_OPEN
@final
@property
def state_attributes(self):
"""Return the state attributes."""
data = {}
2021-10-17 17:56:00 +00:00
if (current := self.current_cover_position) is not None:
data[ATTR_CURRENT_POSITION] = current
2021-10-17 17:56:00 +00:00
if (current_tilt := self.current_cover_tilt_position) is not None:
data[ATTR_CURRENT_TILT_POSITION] = current_tilt
return data
@property
def supported_features(self) -> int:
"""Flag supported features."""
if self._attr_supported_features is not None:
return self._attr_supported_features
supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP
if self.current_cover_position is not None:
supported_features |= SUPPORT_SET_POSITION
if self.current_cover_tilt_position is not None:
supported_features |= (
2019-07-31 19:25:30 +00:00
SUPPORT_OPEN_TILT
| SUPPORT_CLOSE_TILT
| SUPPORT_STOP_TILT
| SUPPORT_SET_TILT_POSITION
)
return supported_features
Fix COMMAND_CLASS_BARRIER_OPERATOR for dev branch of OpenZwave (#8574) * Update zwave.py to work with updated OpenZwave library Update zwave.py to work with updated OpenZwave library * Update zwave.py * Update zwave.py * Update to fix garage door openers Update to fix garage door support for latest version of openzwavelib * Update to cover.zwave list of states Update to cover.zwave to provide list of states based on dev version of openzwave lib * Some values not saved * Formatting fix * Formatting fix * Variable typo * Formatting fix * Formatting * Variable Update Variable Update and properties added * Formatting fixes * Formatting Fix * Update test case for door states * Formatting / Testing process fix * Formatting * Formatting / Test Fixes * Variable rename * Added members to CoverDevice * Removed un-needed else * Formatting * Formatting * Variable name changes and const updates * Changed variable names to cover_state * Added constains into const.py * Updated to change the main state on the cover device * Fixes * Formatting fixes * Formatting/Variables * Formatting * Variable fixes * Import update * Formatting / Variables * Update test for new states * Revert state changes * Test fix * Variable Fix * Formatting * Variable typo * Missing constant * Variable fix * Requested changes * Added is_opening * Added is_closing * Updated test based on changes * Formatting * Changed cover_state back to _state * Formatting and variable fixes * Test fixes * Formatting and variable touchup * Formatting * Optimizations * Add new cover features to demo * Add tests for demo cover closing/opening * Remove unused STATE_STOPPED * Add tests for new zwave cover values
2017-07-27 22:57:30 +00:00
@property
def is_opening(self) -> bool | None:
Fix COMMAND_CLASS_BARRIER_OPERATOR for dev branch of OpenZwave (#8574) * Update zwave.py to work with updated OpenZwave library Update zwave.py to work with updated OpenZwave library * Update zwave.py * Update zwave.py * Update to fix garage door openers Update to fix garage door support for latest version of openzwavelib * Update to cover.zwave list of states Update to cover.zwave to provide list of states based on dev version of openzwave lib * Some values not saved * Formatting fix * Formatting fix * Variable typo * Formatting fix * Formatting * Variable Update Variable Update and properties added * Formatting fixes * Formatting Fix * Update test case for door states * Formatting / Testing process fix * Formatting * Formatting / Test Fixes * Variable rename * Added members to CoverDevice * Removed un-needed else * Formatting * Formatting * Variable name changes and const updates * Changed variable names to cover_state * Added constains into const.py * Updated to change the main state on the cover device * Fixes * Formatting fixes * Formatting/Variables * Formatting * Variable fixes * Import update * Formatting / Variables * Update test for new states * Revert state changes * Test fix * Variable Fix * Formatting * Variable typo * Missing constant * Variable fix * Requested changes * Added is_opening * Added is_closing * Updated test based on changes * Formatting * Changed cover_state back to _state * Formatting and variable fixes * Test fixes * Formatting and variable touchup * Formatting * Optimizations * Add new cover features to demo * Add tests for demo cover closing/opening * Remove unused STATE_STOPPED * Add tests for new zwave cover values
2017-07-27 22:57:30 +00:00
"""Return if the cover is opening or not."""
return self._attr_is_opening
Fix COMMAND_CLASS_BARRIER_OPERATOR for dev branch of OpenZwave (#8574) * Update zwave.py to work with updated OpenZwave library Update zwave.py to work with updated OpenZwave library * Update zwave.py * Update zwave.py * Update to fix garage door openers Update to fix garage door support for latest version of openzwavelib * Update to cover.zwave list of states Update to cover.zwave to provide list of states based on dev version of openzwave lib * Some values not saved * Formatting fix * Formatting fix * Variable typo * Formatting fix * Formatting * Variable Update Variable Update and properties added * Formatting fixes * Formatting Fix * Update test case for door states * Formatting / Testing process fix * Formatting * Formatting / Test Fixes * Variable rename * Added members to CoverDevice * Removed un-needed else * Formatting * Formatting * Variable name changes and const updates * Changed variable names to cover_state * Added constains into const.py * Updated to change the main state on the cover device * Fixes * Formatting fixes * Formatting/Variables * Formatting * Variable fixes * Import update * Formatting / Variables * Update test for new states * Revert state changes * Test fix * Variable Fix * Formatting * Variable typo * Missing constant * Variable fix * Requested changes * Added is_opening * Added is_closing * Updated test based on changes * Formatting * Changed cover_state back to _state * Formatting and variable fixes * Test fixes * Formatting and variable touchup * Formatting * Optimizations * Add new cover features to demo * Add tests for demo cover closing/opening * Remove unused STATE_STOPPED * Add tests for new zwave cover values
2017-07-27 22:57:30 +00:00
@property
def is_closing(self) -> bool | None:
Fix COMMAND_CLASS_BARRIER_OPERATOR for dev branch of OpenZwave (#8574) * Update zwave.py to work with updated OpenZwave library Update zwave.py to work with updated OpenZwave library * Update zwave.py * Update zwave.py * Update to fix garage door openers Update to fix garage door support for latest version of openzwavelib * Update to cover.zwave list of states Update to cover.zwave to provide list of states based on dev version of openzwave lib * Some values not saved * Formatting fix * Formatting fix * Variable typo * Formatting fix * Formatting * Variable Update Variable Update and properties added * Formatting fixes * Formatting Fix * Update test case for door states * Formatting / Testing process fix * Formatting * Formatting / Test Fixes * Variable rename * Added members to CoverDevice * Removed un-needed else * Formatting * Formatting * Variable name changes and const updates * Changed variable names to cover_state * Added constains into const.py * Updated to change the main state on the cover device * Fixes * Formatting fixes * Formatting/Variables * Formatting * Variable fixes * Import update * Formatting / Variables * Update test for new states * Revert state changes * Test fix * Variable Fix * Formatting * Variable typo * Missing constant * Variable fix * Requested changes * Added is_opening * Added is_closing * Updated test based on changes * Formatting * Changed cover_state back to _state * Formatting and variable fixes * Test fixes * Formatting and variable touchup * Formatting * Optimizations * Add new cover features to demo * Add tests for demo cover closing/opening * Remove unused STATE_STOPPED * Add tests for new zwave cover values
2017-07-27 22:57:30 +00:00
"""Return if the cover is closing or not."""
return self._attr_is_closing
Fix COMMAND_CLASS_BARRIER_OPERATOR for dev branch of OpenZwave (#8574) * Update zwave.py to work with updated OpenZwave library Update zwave.py to work with updated OpenZwave library * Update zwave.py * Update zwave.py * Update to fix garage door openers Update to fix garage door support for latest version of openzwavelib * Update to cover.zwave list of states Update to cover.zwave to provide list of states based on dev version of openzwave lib * Some values not saved * Formatting fix * Formatting fix * Variable typo * Formatting fix * Formatting * Variable Update Variable Update and properties added * Formatting fixes * Formatting Fix * Update test case for door states * Formatting / Testing process fix * Formatting * Formatting / Test Fixes * Variable rename * Added members to CoverDevice * Removed un-needed else * Formatting * Formatting * Variable name changes and const updates * Changed variable names to cover_state * Added constains into const.py * Updated to change the main state on the cover device * Fixes * Formatting fixes * Formatting/Variables * Formatting * Variable fixes * Import update * Formatting / Variables * Update test for new states * Revert state changes * Test fix * Variable Fix * Formatting * Variable typo * Missing constant * Variable fix * Requested changes * Added is_opening * Added is_closing * Updated test based on changes * Formatting * Changed cover_state back to _state * Formatting and variable fixes * Test fixes * Formatting and variable touchup * Formatting * Optimizations * Add new cover features to demo * Add tests for demo cover closing/opening * Remove unused STATE_STOPPED * Add tests for new zwave cover values
2017-07-27 22:57:30 +00:00
@property
def is_closed(self) -> bool | None:
"""Return if the cover is closed or not."""
return self._attr_is_closed
2019-09-20 15:23:34 +00:00
def open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
raise NotImplementedError()
async def async_open_cover(self, **kwargs):
"""Open the cover."""
await self.hass.async_add_executor_job(ft.partial(self.open_cover, **kwargs))
2017-02-02 20:39:13 +00:00
2019-09-20 15:23:34 +00:00
def close_cover(self, **kwargs: Any) -> None:
"""Close cover."""
raise NotImplementedError()
async def async_close_cover(self, **kwargs):
"""Close cover."""
await self.hass.async_add_executor_job(ft.partial(self.close_cover, **kwargs))
2017-02-02 20:39:13 +00:00
2019-09-20 15:23:34 +00:00
def toggle(self, **kwargs: Any) -> None:
"""Toggle the entity."""
if self.is_closed:
self.open_cover(**kwargs)
else:
self.close_cover(**kwargs)
async def async_toggle(self, **kwargs):
"""Toggle the entity."""
if self.is_closed:
await self.async_open_cover(**kwargs)
else:
await self.async_close_cover(**kwargs)
def set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
await self.hass.async_add_executor_job(
ft.partial(self.set_cover_position, **kwargs)
)
2017-02-02 20:39:13 +00:00
def stop_cover(self, **kwargs):
"""Stop the cover."""
async def async_stop_cover(self, **kwargs):
"""Stop the cover."""
await self.hass.async_add_executor_job(ft.partial(self.stop_cover, **kwargs))
2017-02-02 20:39:13 +00:00
2019-09-20 15:23:34 +00:00
def open_cover_tilt(self, **kwargs: Any) -> None:
"""Open the cover tilt."""
async def async_open_cover_tilt(self, **kwargs):
"""Open the cover tilt."""
await self.hass.async_add_executor_job(
ft.partial(self.open_cover_tilt, **kwargs)
)
2017-02-02 20:39:13 +00:00
2019-09-20 15:23:34 +00:00
def close_cover_tilt(self, **kwargs: Any) -> None:
"""Close the cover tilt."""
async def async_close_cover_tilt(self, **kwargs):
"""Close the cover tilt."""
await self.hass.async_add_executor_job(
ft.partial(self.close_cover_tilt, **kwargs)
)
2017-02-02 20:39:13 +00:00
def set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
async def async_set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
await self.hass.async_add_executor_job(
2019-07-31 19:25:30 +00:00
ft.partial(self.set_cover_tilt_position, **kwargs)
)
2017-02-02 20:39:13 +00:00
def stop_cover_tilt(self, **kwargs):
"""Stop the cover."""
2017-02-02 20:39:13 +00:00
async def async_stop_cover_tilt(self, **kwargs):
"""Stop the cover."""
await self.hass.async_add_executor_job(
ft.partial(self.stop_cover_tilt, **kwargs)
)
2019-09-20 15:23:34 +00:00
def toggle_tilt(self, **kwargs: Any) -> None:
"""Toggle the entity."""
if self.current_cover_tilt_position == 0:
self.open_cover_tilt(**kwargs)
else:
self.close_cover_tilt(**kwargs)
async def async_toggle_tilt(self, **kwargs):
"""Toggle the entity."""
if self.current_cover_tilt_position == 0:
await self.async_open_cover_tilt(**kwargs)
else:
await self.async_close_cover_tilt(**kwargs)
class CoverDevice(CoverEntity):
"""Representation of a cover (for backwards compatibility)."""
def __init_subclass__(cls, **kwargs):
"""Print deprecation warning."""
super().__init_subclass__(**kwargs)
_LOGGER.warning(
2020-08-27 11:56:20 +00:00
"CoverDevice is deprecated, modify %s to extend CoverEntity",
cls.__name__,
)