core/homeassistant/components/zwave/cover.py

359 lines
12 KiB
Python
Raw Normal View History

"""Support for Z-Wave covers."""
import logging
from homeassistant.components.cover import (
ATTR_POSITION,
ATTR_TILT_POSITION,
2019-07-31 19:25:30 +00:00
DOMAIN,
SUPPORT_CLOSE,
SUPPORT_OPEN,
CoverDevice,
2019-07-31 19:25:30 +00:00
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
2019-03-08 22:08:19 +00:00
from . import (
2019-07-31 19:25:30 +00:00
CONF_INVERT_OPENCLOSE_BUTTONS,
CONF_INVERT_PERCENT,
CONF_TILT_OPEN_POSITION,
ZWaveDeviceEntity,
2019-07-31 19:25:30 +00:00
workaround,
)
2019-03-08 22:08:19 +00:00
from .const import (
2019-07-31 19:25:30 +00:00
COMMAND_CLASS_BARRIER_OPERATOR,
COMMAND_CLASS_MANUFACTURER_PROPRIETARY,
COMMAND_CLASS_SWITCH_BINARY,
COMMAND_CLASS_SWITCH_MULTILEVEL,
2019-07-31 19:25:30 +00:00
DATA_NETWORK,
)
_LOGGER = logging.getLogger(__name__)
SUPPORT_GARAGE = SUPPORT_OPEN | SUPPORT_CLOSE
def _to_hex_str(id_in_bytes):
"""Convert a two byte value to a hex string.
Example: 0x1234 --> '0x1234'
"""
return "0x{:04x}".format(id_in_bytes)
# For some reason node.manufacturer_id is of type string. So we need to convert
# the values.
FIBARO = _to_hex_str(workaround.FIBARO)
FIBARO222_SHUTTERS = [
_to_hex_str(workaround.FGR222_SHUTTER2),
_to_hex_str(workaround.FGRM222_SHUTTER2),
]
2019-07-31 19:25:30 +00:00
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Old method of setting up Z-Wave covers."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Z-Wave Cover from Config Entry."""
2019-07-31 19:25:30 +00:00
@callback
def async_add_cover(cover):
"""Add Z-Wave Cover."""
async_add_entities([cover])
2019-07-31 19:25:30 +00:00
async_dispatcher_connect(hass, "zwave_new_cover", async_add_cover)
def get_device(hass, values, node_config, **kwargs):
"""Create Z-Wave entity device."""
2019-03-08 22:08:19 +00:00
invert_buttons = node_config.get(CONF_INVERT_OPENCLOSE_BUTTONS)
invert_percent = node_config.get(CONF_INVERT_PERCENT)
2019-07-31 19:25:30 +00:00
if (
values.primary.command_class == COMMAND_CLASS_SWITCH_MULTILEVEL
and values.primary.index == 0
):
if (
values.primary.node.manufacturer_id == FIBARO
and values.primary.node.product_type in FIBARO222_SHUTTERS
):
return FibaroFGRM222(
hass,
values,
invert_buttons,
invert_percent,
node_config.get(CONF_TILT_OPEN_POSITION),
)
return ZwaveRollershutter(hass, values, invert_buttons, invert_percent)
2019-03-08 22:08:19 +00:00
if values.primary.command_class == COMMAND_CLASS_SWITCH_BINARY:
return ZwaveGarageDoorSwitch(values)
2019-07-31 19:25:30 +00:00
if values.primary.command_class == COMMAND_CLASS_BARRIER_OPERATOR:
return ZwaveGarageDoorBarrier(values)
return None
2019-03-08 22:08:19 +00:00
class ZwaveRollershutter(ZWaveDeviceEntity, CoverDevice):
"""Representation of an Z-Wave cover."""
def __init__(self, hass, values, invert_buttons, invert_percent):
"""Initialize the Z-Wave rollershutter."""
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
2019-03-08 22:08:19 +00:00
self._network = hass.data[DATA_NETWORK]
self._open_id = None
self._close_id = None
self._current_position = None
self._invert_buttons = invert_buttons
self._invert_percent = invert_percent
self._workaround = workaround.get_device_mapping(values.primary)
if self._workaround:
_LOGGER.debug("Using workaround %s", self._workaround)
self.update_properties()
def update_properties(self):
"""Handle data changes for node values."""
# Position value
self._current_position = self.values.primary.data
2019-07-31 19:25:30 +00:00
if (
self.values.open
and self.values.close
and self._open_id is None
and self._close_id is None
):
if self._invert_buttons:
self._open_id = self.values.close.value_id
self._close_id = self.values.open.value_id
else:
self._open_id = self.values.open.value_id
self._close_id = self.values.close.value_id
@property
def is_closed(self):
"""Return if the cover is closed."""
if self.current_cover_position is None:
return None
if self.current_cover_position > 0:
return False
return True
@property
def current_cover_position(self):
"""Return the current position of Zwave roller shutter."""
if self._workaround == workaround.WORKAROUND_NO_POSITION:
return None
if self._current_position is not None:
if self._current_position <= 5:
return 100 if self._invert_percent else 0
if self._current_position >= 95:
return 0 if self._invert_percent else 100
2019-07-31 19:25:30 +00:00
return (
100 - self._current_position
if self._invert_percent
else self._current_position
2019-07-31 19:25:30 +00:00
)
def open_cover(self, **kwargs):
"""Move the roller shutter up."""
self._network.manager.pressButton(self._open_id)
def close_cover(self, **kwargs):
"""Move the roller shutter down."""
self._network.manager.pressButton(self._close_id)
def set_cover_position(self, **kwargs):
"""Move the roller shutter to a specific position."""
2019-07-31 19:25:30 +00:00
self.node.set_dimmer(
self.values.primary.value_id,
(100 - kwargs.get(ATTR_POSITION))
if self._invert_percent
else kwargs.get(ATTR_POSITION),
)
def stop_cover(self, **kwargs):
"""Stop the roller shutter."""
self._network.manager.releaseButton(self._open_id)
2019-03-08 22:08:19 +00:00
class ZwaveGarageDoorBase(ZWaveDeviceEntity, CoverDevice):
"""Base class for a Zwave garage door device."""
def __init__(self, values):
"""Initialize the zwave garage door."""
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
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
self._state = None
self.update_properties()
def update_properties(self):
"""Handle data changes for node values."""
self._state = self.values.primary.data
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
_LOGGER.debug("self._state=%s", self._state)
@property
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
2019-07-31 19:25:30 +00:00
return "garage"
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_GARAGE
class ZwaveGarageDoorSwitch(ZwaveGarageDoorBase):
"""Representation of a switch based Zwave garage door device."""
@property
def is_closed(self):
"""Return the current position of Zwave garage door."""
return not self._state
def close_cover(self, **kwargs):
"""Close the garage door."""
self.values.primary.data = False
def open_cover(self, **kwargs):
"""Open the garage door."""
self.values.primary.data = True
class ZwaveGarageDoorBarrier(ZwaveGarageDoorBase):
"""Representation of a barrier operator Zwave garage door device."""
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):
"""Return true if cover is in an opening state."""
return self._state == "Opening"
@property
def is_closing(self):
2018-01-27 19:58:27 +00:00
"""Return true if cover is in a closing state."""
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 self._state == "Closing"
@property
def is_closed(self):
"""Return the current position of Zwave garage door."""
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 self._state == "Closed"
def close_cover(self, **kwargs):
"""Close the garage door."""
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
self.values.primary.data = "Closed"
def open_cover(self, **kwargs):
"""Open the garage door."""
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
self.values.primary.data = "Opened"
class FibaroFGRM222(ZwaveRollershutter):
"""Implementation of proprietary features for Fibaro FGR-222 / FGRM-222.
This adds support for the tilt feature for the ventian blind mode.
To enable this you need to configure the devices to use the venetian blind
mode and to enable the proprietary command class:
* Set "3: Reports type to Blind position reports sent"
to value "the main controller using Fibaro Command Class"
* Set "10: Roller Shutter operating modes"
to value "2 - Venetian Blind Mode, with positioning"
"""
def __init__(
self, hass, values, invert_buttons, invert_percent, open_tilt_position: int
):
"""Initialize the FGRM-222."""
self._value_blinds = None
self._value_tilt = None
self._has_tilt_mode = False # type: bool
self._open_tilt_position = 50 # type: int
if open_tilt_position is not None:
self._open_tilt_position = open_tilt_position
super().__init__(hass, values, invert_buttons, invert_percent)
@property
def current_cover_tilt_position(self) -> int:
"""Get the tilt of the blinds.
Saturate values <5 and >94 so that it's easier to detect the end
positions in automations.
"""
if not self._has_tilt_mode:
return None
if self._value_tilt.data <= 5:
return 0
if self._value_tilt.data >= 95:
return 100
return self._value_tilt.data
def set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
if not self._has_tilt_mode:
_LOGGER.error("Can't set cover tilt as device is not yet set up.")
else:
# Limit the range to [0-99], as this what that the ZWave command
# accepts.
tilt_position = max(0, min(99, kwargs.get(ATTR_TILT_POSITION)))
_LOGGER.debug("setting tilt to %d", tilt_position)
self._value_tilt.data = tilt_position
def open_cover_tilt(self, **kwargs):
"""Set slats to horizontal position."""
self.set_cover_tilt_position(tilt_position=self._open_tilt_position)
def close_cover_tilt(self, **kwargs):
"""Close the slats."""
self.set_cover_tilt_position(tilt_position=0)
def set_cover_position(self, **kwargs):
"""Move the roller shutter to a specific position.
If the venetian blinds mode is not activated, fall back to
the behavior of the parent class.
"""
if not self._has_tilt_mode:
super().set_cover_position(**kwargs)
else:
_LOGGER.debug("Setting cover position to %s", kwargs.get(ATTR_POSITION))
self._value_blinds.data = kwargs.get(ATTR_POSITION)
def _configure_values(self):
"""Get the value objects from the node."""
for value in self.node.get_values(
class_id=COMMAND_CLASS_MANUFACTURER_PROPRIETARY
).values():
if value is None:
continue
if value.index == 0:
self._value_blinds = value
elif value.index == 1:
self._value_tilt = value
else:
_LOGGER.warning(
"Undefined index %d for this command class", value.index
)
if self._value_tilt is not None:
# We reached here because the user has configured the Fibaro to
# report using the MANUFACTURER_PROPRIETARY command class. The only
# reason for the user to configure this way is if tilt support is
# needed (aka venetian blind mode). Therefore, turn it on.
#
# Note: This is safe to do even if the user has accidentally set
# this configuration parameter, or configuration parameter 10 to
# something other than venetian blind mode. The controller will just
# ignore potential tilt settings sent from home assistant in this
# case.
self._has_tilt_mode = True
_LOGGER.info(
"Zwave node %s is a Fibaro FGR-222/FGRM-222 with tilt support.",
self.node_id,
)
def update_properties(self):
"""React on properties being updated."""
if not self._has_tilt_mode:
self._configure_values()
if self._value_blinds is not None:
self._current_position = self._value_blinds.data
else:
super().update_properties()