-
-
Notifications
You must be signed in to change notification settings - Fork 34.3k
Add support for 2 Tahoma IO awning covers #15660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fbb76bb
96237f9
98753e3
e4735f5
7467b2e
070e0e1
1b2a42a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,10 @@ | |
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/cover.tahoma/ | ||
""" | ||
from datetime import timedelta | ||
import logging | ||
|
||
from homeassistant.util.dt import utcnow | ||
from homeassistant.components.cover import CoverDevice, ATTR_POSITION | ||
from homeassistant.components.tahoma import ( | ||
DOMAIN as TAHOMA_DOMAIN, TahomaDevice) | ||
|
@@ -14,6 +16,13 @@ | |
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
ATTR_MEM_POS = 'memorized_position' | ||
ATTR_RSSI_LEVEL = 'rssi_level' | ||
ATTR_LOCK_START_TS = 'lock_start_ts' | ||
ATTR_LOCK_END_TS = 'lock_end_ts' | ||
ATTR_LOCK_LEVEL = 'lock_level' | ||
ATTR_LOCK_ORIG = 'lock_originator' | ||
|
||
|
||
def setup_platform(hass, config, add_devices, discovery_info=None): | ||
"""Set up the Tahoma covers.""" | ||
|
@@ -27,27 +36,107 @@ def setup_platform(hass, config, add_devices, discovery_info=None): | |
class TahomaCover(TahomaDevice, CoverDevice): | ||
"""Representation a Tahoma Cover.""" | ||
|
||
def __init__(self, tahoma_device, controller): | ||
"""Initialize the device.""" | ||
super().__init__(tahoma_device, controller) | ||
|
||
self._closure = 0 | ||
# 100 equals open | ||
self._position = 100 | ||
self._closed = False | ||
self._rssi_level = None | ||
self._icon = None | ||
# Can be 0 and bigger | ||
self._lock_timer = 0 | ||
self._lock_start_ts = None | ||
self._lock_end_ts = None | ||
# Can be 'comfortLevel1', 'comfortLevel2', 'comfortLevel3', | ||
# 'comfortLevel4', 'environmentProtection', 'humanProtection', | ||
# 'userLevel1', 'userLevel2' | ||
self._lock_level = None | ||
# Can be 'LSC', 'SAAC', 'SFC', 'UPS', 'externalGateway', 'localUser', | ||
# 'myself', 'rain', 'security', 'temperature', 'timer', 'user', 'wind' | ||
self._lock_originator = None | ||
|
||
def update(self): | ||
"""Update method.""" | ||
self.controller.get_states([self.tahoma_device]) | ||
|
||
# For vertical covers | ||
self._closure = self.tahoma_device.active_states.get( | ||
'core:ClosureState') | ||
# For horizontal covers | ||
if self._closure is None: | ||
self._closure = self.tahoma_device.active_states.get( | ||
'core:DeploymentState') | ||
|
||
# For all, if available | ||
if 'core:PriorityLockTimerState' in self.tahoma_device.active_states: | ||
old_lock_timer = self._lock_timer | ||
self._lock_timer = \ | ||
self.tahoma_device.active_states['core:PriorityLockTimerState'] | ||
# Derive timestamps from _lock_timer, only if not already set or | ||
# something has changed | ||
if self._lock_timer > 0: | ||
_LOGGER.debug("Update %s, lock_timer: %d", self._name, | ||
self._lock_timer) | ||
if self._lock_start_ts is None: | ||
self._lock_start_ts = utcnow() | ||
if self._lock_end_ts is None or \ | ||
old_lock_timer != self._lock_timer: | ||
self._lock_end_ts = utcnow() +\ | ||
timedelta(seconds=self._lock_timer) | ||
else: | ||
self._lock_start_ts = None | ||
self._lock_end_ts = None | ||
else: | ||
self._lock_timer = 0 | ||
self._lock_start_ts = None | ||
self._lock_end_ts = None | ||
|
||
self._lock_level = self.tahoma_device.active_states.get( | ||
'io:PriorityLockLevelState') | ||
|
||
self._lock_originator = self.tahoma_device.active_states.get( | ||
'io:PriorityLockOriginatorState') | ||
|
||
self._rssi_level = self.tahoma_device.active_states.get( | ||
'core:RSSILevelState') | ||
|
||
# Define which icon to use | ||
if self._lock_timer > 0: | ||
if self._lock_originator == 'wind': | ||
self._icon = 'mdi:weather-windy' | ||
else: | ||
self._icon = 'mdi:lock-alert' | ||
else: | ||
self._icon = None | ||
|
||
# Define current position. | ||
# _position: 0 is closed, 100 is fully open. | ||
# 'core:ClosureState': 100 is closed, 0 is fully open. | ||
if self._closure is not None: | ||
self._position = 100 - self._closure | ||
if self._position <= 5: | ||
self._position = 0 | ||
if self._position >= 95: | ||
self._position = 100 | ||
self._closed = self._position == 0 | ||
else: | ||
self._position = None | ||
if 'core:OpenClosedState' in self.tahoma_device.active_states: | ||
self._closed = \ | ||
self.tahoma_device.active_states['core:OpenClosedState']\ | ||
== 'closed' | ||
else: | ||
self._closed = False | ||
|
||
_LOGGER.debug("Update %s, position: %d", self._name, self._position) | ||
|
||
@property | ||
def current_cover_position(self): | ||
""" | ||
Return current position of cover. | ||
|
||
0 is closed, 100 is fully open. | ||
""" | ||
try: | ||
position = 100 - \ | ||
self.tahoma_device.active_states['core:ClosureState'] | ||
if position <= 5: | ||
return 0 | ||
if position >= 95: | ||
return 100 | ||
return position | ||
except KeyError: | ||
return None | ||
"""Return current position of cover.""" | ||
return self._position | ||
|
||
def set_cover_position(self, **kwargs): | ||
"""Move the cover to a specific position.""" | ||
|
@@ -56,8 +145,7 @@ def set_cover_position(self, **kwargs): | |
@ 8000 property | ||
def is_closed(self): | ||
"""Return if the cover is closed.""" | ||
if self.current_cover_position is not None: | ||
return self.current_cover_position == 0 | ||
return self._closed | ||
|
||
@property | ||
def device_class(self): | ||
|
@@ -66,13 +154,47 @@ def device_class(self): | |
return 'window' | ||
return None | ||
|
||
@property | ||
def device_state_attributes(self): | ||
"""Return the device state attributes.""" | ||
attr = {} | ||
super_attr = super().device_state_attributes | ||
if super_attr is not None: | ||
attr.update(super_attr) | ||
|
||
if 'core:Memorized1PositionState' in self.tahoma_device.active_states: | ||
attr[ATTR_MEM_POS] = self.tahoma_device.active_states[ | ||
'core:Memorized1PositionState'] | ||
if self._rssi_level is not None: | ||
attr[ATTR_RSSI_LEVEL] = self._rssi_level | ||
if self._lock_start_ts is not None: | ||
attr[ATTR_LOCK_START_TS] = self._lock_start_ts.isoformat() | ||
if self._lock_end_ts is not None: | ||
attr[ATTR_LOCK_END_TS] = self._lock_end_ts.isoformat() | ||
if self._lock_level is not None: | ||
attr[ATTR_LOCK_LEVEL] = self._lock_level | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure, it is an attribute provided by the Tahoma API. My best guess is, that it describes the level of the lock, eg. the strength of the wind. I usually see 'comfortLevel1' with my covers in case of wind. |
||
if self._lock_originator is not None: | ||
attr[ATTR_LOCK_ORIG] = self._lock_originator | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason for the lock state. I usually see wind. As well documented in the code. |
||
return attr | ||
|
||
@property | ||
def icon(self): | ||
"""Return the icon to use in the frontend, if any.""" | ||
return self._icon | ||
|
||
def open_cover(self, **kwargs): | ||
"""Open the cover.""" | ||
self.apply_action('open') | ||
if self.tahoma_device.type == 'io:HorizontalAwningIOComponent': | ||
self.apply_action('close') | ||
else: | ||
self.apply_action('open') | ||
|
||
def close_cover(self, **kwargs): | ||
"""Close the cover.""" | ||
self.apply_action('close') | ||
if self.tahoma_device.type == 'io:HorizontalAwningIOComponent': | ||
self.apply_action('open') | ||
else: | ||
self.apply_action('close') | ||
|
||
def stop_cover(self, **kwargs): | ||
"""Stop the cover.""" | ||
|
@@ -87,5 +209,10 @@ def stop_cover(self, **kwargs): | |
'rts:ExteriorVenetianBlindRTSComponent', | ||
'rts:BlindRTSComponent'): | ||
self.apply_action('my') | ||
elif self.tahoma_device.type in \ | ||
('io:HorizontalAwningIOComponent', | ||
'io:RollerShutterGenericIOComponent', | ||
'io:VerticalExteriorAwningIOComponent'): | ||
self.apply_action('stop') | ||
else: | ||
self.apply_action('stopIdentify') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this be true every update as long as the timer is running?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the timer value is actually not a real timer. It is not updated with every update. It stays at a certain value for quite some time (usually at about 600 seconds), but can be updated anytime by the system, if for example stronger wind is detected (or the wind did not stop after the timer duration).