8000 Add support for 2 Tahoma IO awning covers by fucm · Pull Request #15660 · home-assistant/core · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

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

Merged
merged 7 commits into from
Aug 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 146 additions & 19 deletions homeassistant/components/cover/tahoma.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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."""
Expand All @@ -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:
Copy link
Member

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?

Copy link
Contributor Author

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).

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."""
Expand All @@ -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):
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
In the code I added all possible values:
Can be 'comfortLevel1', 'comfortLevel2', 'comfortLevel3', 'comfortLevel4', 'environmentProtection', 'humanProtection', 'userLevel1', 'userLevel2'

if self._lock_originator is not None:
attr[ATTR_LOCK_ORIG] = self._lock_originator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
Can be 'LSC', 'SAAC', 'SFC', 'UPS', 'externalGateway', 'localUser', 'myself', 'rain', 'security', 'temperature', 'timer', 'user', 'wind'

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."""
Expand All @@ -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')
2 changes: 2 additions & 0 deletions homeassistant/components/tahoma.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
'io:WindowOpenerVeluxIOComponent': 'cover',
'io:LightIOSystemSensor': 'sensor',
'rts:GarageDoor4TRTSComponent': 'switch',
'io:VerticalExteriorAwningIOComponent': 'cover',
'io:HorizontalAwningIOComponent': 'cover',
'rtds:RTDSSmokeSensor': 'smoke',
}

Expand Down
0