Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
94 changes: 94 additions & 0 deletions homeassistant/components/binary_sensor/tahoma.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""
Support for Tahoma binary sensors.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.tahoma/
"""

import logging
from datetime import timedelta

from homeassistant.components.binary_sensor import (
BinarySensorDevice)
from homeassistant.components.tahoma import (
DOMAIN as TAHOMA_DOMAIN, TahomaDevice)
from homeassistant.const import (STATE_OFF, STATE_ON, ATTR_BATTERY_LEVEL)

DEPENDENCIES = ['tahoma']

_LOGGER = logging.getLogger(__name__)

SCAN_INTERVAL = timedelta(seconds=120)


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Tahoma controller devices."""
_LOGGER.debug("Setup Tahoma Binary sensor platform")
controller = hass.data[TAHOMA_DOMAIN]['controller']
devices = []
for device in hass.data[TAHOMA_DOMAIN]['devices']['smoke']:
devices.append(TahomaBinarySensor(device, controller))
add_devices(devices, True)


class TahomaBinarySensor(TahomaDevice, BinarySensorDevice):
"""Representation of a Tahoma Binary Sensor."""

def __init__(self, tahoma_device, controller):
"""Initialize the sensor."""
super().__init__(tahoma_device, controller)

self._state = None
self._icon = None
self._battery = None

@property
def is_on(self):
"""Return the state of the sensor."""
return bool(self._state == STATE_ON)

@property
def device_class(self):
"""Return the class of the device."""
if self.tahoma_device.type == 'rtds:RTDSSmokeSensor':
return 'smoke'
return None

@property
def icon(self):
"""Icon for device by its type."""
return self._icon

@property
def state_attributes(self):
"""Return the state attributes of the sensor."""
attr = {}
if self._battery is not None:
attr[ATTR_BATTERY_LEVEL] = self._battery
return attr

def update(self):
"""Update the state."""
self.controller.get_states([self.tahoma_device])
if self.tahoma_device.type == 'rtds:RTDSSmokeSensor':
if self.tahoma_device.active_states['core:SmokeState']\
== 'notDetected':
self._state = STATE_OFF
else:
self._state = STATE_ON

if 'core:SensorDefectState' in self.tahoma_device.active_states:
# Set to 'lowBattery' for low battery warning.
self._battery = self.tahoma_device.active_states[
'core:SensorDefectState']
else:
self._battery = None

if self._state == STATE_ON:
self._icon = "mdi:fire"
elif self._battery == 'lowBattery':
self._icon = "mdi:battery-alert"
else:
self._icon = None

_LOGGER.debug("Update %s, state: %s", self._name, self._state)
150 changes: 131 additions & 19 deletions homeassistant/components/cover/tahoma.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@

_LOGGER = logging.getLogger(__name__)

ATTR_CLOSURE = 'closure'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please limit PRs to 1 feature per PR next time. So changes to binary sensor and cover: 2 prs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I will do so in the future. Sorry.

ATTR_MEM_POS = 'memorized_position'
ATTR_RSSI_LEVEL = 'rssi_level'
ATTR_OPEN_CLOSE = 'open_close'
ATTR_STATUS = 'status'
ATTR_LOCK_TIMER = 'priority_lock_timer'
ATTR_LOCK_LEVEL = 'priority_lock_level'
ATTR_LOCK_ORIG = 'priority_lock_originator'


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Tahoma covers."""
Expand All @@ -27,27 +36,84 @@ 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._icon = None
# Can be 0 and bigger
self._lock_timer = 0
# Can be 'comfortLevel1'
self._lock_level = None
# Can be 'wind'
self._lock_originator = None

def update(self):
"""Update method."""
self.controller.get_states([self.tahoma_device])

@property
def current_cover_position(self):
"""
Return current position of cover.
if 'core:ClosureState' in self.tahoma_device.active_states:
self._closure = \
self.tahoma_device.active_states['core:ClosureState']
else:
self._closure = None
if 'core:PriorityLockTimerState' in self.tahoma_device.active_states:
self._lock_timer = \
self.tahoma_device.active_states['core:PriorityLockTimerState']
else:
self._lock_timer = None
if 'io:PriorityLockLevelState' in self.tahoma_device.active_states:
self._lock_level = \
self.tahoma_device.active_states['io:PriorityLockLevelState']
else:
self._lock_level = None
if 'io:PriorityLockOriginatorState' in \
self.tahoma_device.active_states:
self._lock_originator = \
self.tahoma_device.active_states[
'io:PriorityLockOriginatorState']
else:
self._lock_originator = None

# 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

0 is closed, 100 is fully open.
"""
try:
position = 100 - \
# Define current position.
# _position: 0 is closed, 100 is fully open.
# 'core:ClosureState': 100 is closed, 0 is fully open.
if 'core:ClosureState' in self.tahoma_device.active_states:
self._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
if self._position <= 5:
self._position = 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually, I don't know. It was there since the beginning (#10652). So, I kept the code.

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."""
return self._position

def set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
Expand All @@ -56,8 +122,7 @@ def set_cover_position(self, **kwargs):
@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 +131,56 @@ def device_class(self):
return 'window'
return None

@property
def state_attributes(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do not override state_attributes. Devices can override device_state_attributes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll update it.

"""Return the state attributes of the sensor."""
attr = {}
super_attr = super().state_attributes
if super_attr is not None:
attr.update(super_attr)

if self._closure is not None:
attr[ATTR_CLOSURE] = self._closure
if 'core:Memorized1PositionState' in self.tahoma_device.active_states:
attr[ATTR_MEM_POS] = self.tahoma_device.active_states[
'core:Memorized1PositionState']
if 'core:RSSILevelState' in self.tahoma_device.active_states:
attr[ATTR_RSSI_LEVEL] = self.tahoma_device.active_states[
'core:RSSILevelState']
if 'core:OpenClosedState' in self.tahoma_device.active_states:
attr[ATTR_OPEN_CLOSE] = self.tahoma_device.active_states[
'core:OpenClosedState']
if 'core:StatusState' in self.tahoma_device.active_states:
attr[ATTR_STATUS] = self.tahoma_device.active_states[
'core:StatusState']
if self._lock_timer is not None:
attr[ATTR_LOCK_TIMER] = self._lock_timer

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should store any times in the state machine. Only time stamps are allowed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I read the docs about the states again and I don't understand, why the lock timer should go into the state machine. It's an attribute of the device for how long it is locked (before it can be used again, e.g. open cover after wind has been detected). It relates directly to lock_level and lock_originator. Could you please point me to an example or the appropriate documentation?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can store timestamps, so store the exact time when it can be used. Don't store relative times that change on every update.

if self._lock_level is not None:
attr[ATTR_LOCK_LEVEL] = self._lock_level
if self._lock_originator is not None:
attr[ATTR_LOCK_ORIG] = self._lock_originator
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':
# The commands open and close seem to be reversed.
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':
# The commands open and close seem to be reversed.
self.apply_action('open')
else:
self.apply_action('close')

def stop_cover(self, **kwargs):
"""Stop the cover."""
Expand All @@ -83,5 +191,9 @@ def stop_cover(self, **kwargs):
('rts:BlindRTSComponent',
'io:ExteriorVenetianBlindIOComponent'):
self.apply_action('my')
elif self.tahoma_device.type in \
('io:VerticalExteriorAwningIOComponent',
'io:HorizontalAwningIOComponent'):
self.apply_action('stop')
else:
self.apply_action('stopIdentify')
23 changes: 22 additions & 1 deletion homeassistant/components/sensor/tahoma.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
from homeassistant.helpers.entity import Entity
from homeassistant.components.tahoma import (
DOMAIN as TAHOMA_DOMAIN, TahomaDevice)
from homeassistant.const import (ATTR_BATTERY_LEVEL)

DEPENDENCIES = ['tahoma']

_LOGGER = logging.getLogger(__name__)

SCAN_INTERVAL = timedelta(seconds=10)
SCAN_INTERVAL = timedelta(seconds=120)

ATTR_RSSI_LEVEL = 'rssi_level'
ATTR_STATUS = 'status'


def setup_platform(hass, config, add_devices, discovery_info=None):
Expand Down Expand Up @@ -53,6 +57,21 @@ def unit_of_measurement(self):
elif self.tahoma_device.type == 'Humidity Sensor':
return '%'

@property
def state_attributes(self):
"""Return the state attributes of the sensor."""
attr = {}
if 'core:RSSILevelState' in self.tahoma_device.active_states:
attr[ATTR_RSSI_LEVEL] = \
self.tahoma_device.active_states['core:RSSILevelState']
if 'core:SensorDefectState' in self.tahoma_device.active_states:
attr[ATTR_BATTERY_LEVEL] = \
self.tahoma_device.active_states['core:SensorDefectState']
if 'core:StatusState' in self.tahoma_device.active_states:
attr[ATTR_STATUS] = \
self.tahoma_device.active_states['core:StatusState']
return attr

def update(self):
"""Update the state."""
self.controller.get_states([self.tahoma_device])
Expand All @@ -62,3 +81,5 @@ def update(self):
if self.tahoma_device.type == 'io:SomfyContactIOSystemSensor':
self.current_value = self.tahoma_device.active_states[
'core:ContactState']

_LOGGER.debug("Update %s, value: %d", self._name, self.current_value)
Loading