Additional Wink lock features (#7445)

* Additional Wink lock features
This commit is contained in:
William Scanlon 2017-05-13 14:09:00 -04:00 committed by GitHub
parent cfea4b17e3
commit cfbbade6d1
12 changed files with 279 additions and 4 deletions

View file

@ -4,6 +4,7 @@ Interfaces with Wink Cameras.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.wink/ https://home-assistant.io/components/alarm_control_panel.wink/
""" """
import asyncio
import logging import logging
import homeassistant.components.alarm_control_panel as alarm import homeassistant.components.alarm_control_panel as alarm
@ -42,6 +43,11 @@ class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel):
"""Initialize the Wink alarm.""" """Initialize the Wink alarm."""
super().__init__(wink, hass) super().__init__(wink, hass)
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['alarm_control_panel'].append(self)
@property @property
def state(self): def state(self):
"""Return the state of the device.""" """Return the state of the device."""

View file

@ -4,6 +4,7 @@ Support for Wink binary sensors.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
at https://home-assistant.io/components/binary_sensor.wink/ at https://home-assistant.io/components/binary_sensor.wink/
""" """
import asyncio
import logging import logging
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import BinarySensorDevice
@ -101,6 +102,11 @@ class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity):
else: else:
self.capability = None self.capability = None
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['binary_sensor'].append(self)
@property @property
def is_on(self): def is_on(self):
"""Return true if the binary sensor is on.""" """Return true if the binary sensor is on."""

View file

@ -4,6 +4,8 @@ Support for Wink thermostats.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.wink/ https://home-assistant.io/components/climate.wink/
""" """
import asyncio
from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.components.wink import WinkDevice, DOMAIN
from homeassistant.components.climate import ( from homeassistant.components.climate import (
STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice, STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice,
@ -52,6 +54,11 @@ class WinkThermostat(WinkDevice, ClimateDevice):
super().__init__(wink, hass) super().__init__(wink, hass)
self._config_temp_unit = temp_unit self._config_temp_unit = temp_unit
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['climate'].append(self)
@property @property
def temperature_unit(self): def temperature_unit(self):
"""Return the unit of measurement.""" """Return the unit of measurement."""

View file

@ -4,6 +4,8 @@ Support for Wink Covers.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.wink/ https://home-assistant.io/components/cover.wink/
""" """
import asyncio
from homeassistant.components.cover import CoverDevice from homeassistant.components.cover import CoverDevice
from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.components.wink import WinkDevice, DOMAIN
@ -31,6 +33,11 @@ class WinkCoverDevice(WinkDevice, CoverDevice):
"""Initialize the cover.""" """Initialize the cover."""
super().__init__(wink, hass) super().__init__(wink, hass)
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['cover'].append(self)
def close_cover(self): def close_cover(self):
"""Close the shade.""" """Close the shade."""
self.wink.set_state(0) self.wink.set_state(0)

View file

@ -4,6 +4,7 @@ Support for Wink fans.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/fan.wink/ https://home-assistant.io/components/fan.wink/
""" """
import asyncio
import logging import logging
from homeassistant.components.fan import (FanEntity, SPEED_HIGH, from homeassistant.components.fan import (FanEntity, SPEED_HIGH,
@ -12,6 +13,8 @@ from homeassistant.components.fan import (FanEntity, SPEED_HIGH,
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import ToggleEntity
from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.components.wink import WinkDevice, DOMAIN
DEPENDENCIES = ['wink']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
SPEED_LOWEST = 'lowest' SPEED_LOWEST = 'lowest'
@ -34,6 +37,11 @@ class WinkFanDevice(WinkDevice, FanEntity):
"""Initialize the fan.""" """Initialize the fan."""
super().__init__(wink, hass) super().__init__(wink, hass)
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['fan'].append(self)
def set_direction(self: ToggleEntity, direction: str) -> None: def set_direction(self: ToggleEntity, direction: str) -> None:
"""Set the direction of the fan.""" """Set the direction of the fan."""
self.wink.set_fan_direction(direction) self.wink.set_fan_direction(direction)

View file

@ -4,6 +4,7 @@ Support for Wink lights.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.wink/ https://home-assistant.io/components/light.wink/
""" """
import asyncio
import colorsys import colorsys
from homeassistant.components.light import ( from homeassistant.components.light import (
@ -38,6 +39,11 @@ class WinkLight(WinkDevice, Light):
"""Initialize the Wink device.""" """Initialize the Wink device."""
super().__init__(wink, hass) super().__init__(wink, hass)
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['light'].append(self)
@property @property
def is_on(self): def is_on(self):
"""Return true if light is on.""" """Return true if light is on."""

View file

@ -55,3 +55,59 @@ unlock:
code: code:
description: An optional code to unlock the lock with description: An optional code to unlock the lock with
example: 1234 example: 1234
wink_set_lock_vacation_mode:
description: Set vacation mode for all or specified locks. Disables all user codes.
fields:
entity_id:
description: Name of lock to unlock
example: 'lock.front_door'
enabled:
description: enable or disable. true or false.
example: true
wink_set_lock_alarm_mode:
description: Set alarm mode for all or specified locks.
fields:
entity_id:
description: Name of lock to unlock
example: 'lock.front_door'
mode:
description: One of tamper, activity, or forced_entry
example: tamper
wink_set_lock_alarm_sensitivity:
description: Set alarm sensitivity for all or specified locks.
fields:
entity_id:
description: Name of lock to unlock
example: 'lock.front_door'
sensitivity:
description: One of low, medium_low, medium, medium_high, high
example: medium
wink_set_lock_alarm_state:
description: Set alarm state.
fields:
entity_id:
description: Name of lock to unlock
example: 'lock.front_door'
enabled:
description: enable or disable. true or false.
example: true
wink_set_lock_beeper_state:
description: Set beeper state.
fields:
entity_id:
description: Name of lock to unlock
example: 'lock.front_door'
enabled:
description: enable or disable. true or false.
example: true

View file

@ -4,11 +4,55 @@ Support for Wink locks.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/lock.wink/ https://home-assistant.io/components/lock.wink/
""" """
import asyncio
import logging
from os import path
import voluptuous as vol
from homeassistant.components.lock import LockDevice from homeassistant.components.lock import LockDevice
from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.components.wink import WinkDevice, DOMAIN
import homeassistant.helpers.config_validation as cv
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN
from homeassistant.config import load_yaml_config_file
DEPENDENCIES = ['wink'] DEPENDENCIES = ['wink']
_LOGGER = logging.getLogger(__name__)
SERVICE_SET_VACATION_MODE = 'wink_set_lock_vacation_mode'
SERVICE_SET_ALARM_MODE = 'wink_set_lock_alarm_mode'
SERVICE_SET_ALARM_SENSITIVITY = 'wink_set_lock_alarm_sensitivity'
SERVICE_SET_ALARM_STATE = 'wink_set_lock_alarm_state'
SERVICE_SET_BEEPER_STATE = 'wink_set_lock_beeper_state'
ATTR_ENABLED = 'enabled'
ATTR_SENSITIVITY = 'sensitivity'
ATTR_MODE = 'mode'
ALARM_SENSITIVITY_MAP = {"low": 0.2, "medium_low": 0.4,
"medium": 0.6, "medium_high": 0.8,
"high": 1.0}
ALARM_MODES_MAP = {"tamper": "tamper",
"activity": "alert",
"forced_entry": "forced_entry"}
SET_ENABLED_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_ENABLED): cv.string,
})
SET_SENSITIVITY_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_SENSITIVITY): vol.In(ALARM_SENSITIVITY_MAP)
})
SET_ALARM_MODES_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_MODE): vol.In(ALARM_MODES_MAP)
})
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Wink platform.""" """Set up the Wink platform."""
@ -19,6 +63,58 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if _id not in hass.data[DOMAIN]['unique_ids']: if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkLockDevice(lock, hass)]) add_devices([WinkLockDevice(lock, hass)])
def service_handle(service):
"""Handler for services."""
entity_ids = service.data.get('entity_id')
all_locks = hass.data[DOMAIN]['entities']['lock']
locks_to_set = []
if entity_ids is None:
locks_to_set = all_locks
else:
for lock in all_locks:
if lock.entity_id in entity_ids:
locks_to_set.append(lock)
for lock in locks_to_set:
if service.service == SERVICE_SET_VACATION_MODE:
lock.set_vacation_mode(service.data.get(ATTR_ENABLED))
elif service.service == SERVICE_SET_ALARM_STATE:
lock.set_alarm_state(service.data.get(ATTR_ENABLED))
elif service.service == SERVICE_SET_BEEPER_STATE:
lock.set_beeper_state(service.data.get(ATTR_ENABLED))
elif service.service == SERVICE_SET_ALARM_MODE:
lock.set_alarm_mode(service.data.get(ATTR_MODE))
elif service.service == SERVICE_SET_ALARM_SENSITIVITY:
lock.set_alarm_sensitivity(service.data.get(ATTR_SENSITIVITY))
descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_SET_VACATION_MODE,
service_handle,
descriptions.get(SERVICE_SET_VACATION_MODE),
schema=SET_ENABLED_SCHEMA)
hass.services.register(DOMAIN, SERVICE_SET_ALARM_STATE,
service_handle,
descriptions.get(SERVICE_SET_ALARM_STATE),
schema=SET_ENABLED_SCHEMA)
hass.services.register(DOMAIN, SERVICE_SET_BEEPER_STATE,
service_handle,
descriptions.get(SERVICE_SET_BEEPER_STATE),
schema=SET_ENABLED_SCHEMA)
hass.services.register(DOMAIN, SERVICE_SET_ALARM_MODE,
service_handle,
descriptions.get(SERVICE_SET_ALARM_MODE),
schema=SET_ALARM_MODES_SCHEMA)
hass.services.register(DOMAIN, SERVICE_SET_ALARM_SENSITIVITY,
service_handle,
descriptions.get(SERVICE_SET_ALARM_SENSITIVITY),
schema=SET_SENSITIVITY_SCHEMA)
class WinkLockDevice(WinkDevice, LockDevice): class WinkLockDevice(WinkDevice, LockDevice):
"""Representation of a Wink lock.""" """Representation of a Wink lock."""
@ -27,6 +123,11 @@ class WinkLockDevice(WinkDevice, LockDevice):
"""Initialize the lock.""" """Initialize the lock."""
super().__init__(wink, hass) super().__init__(wink, hass)
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['lock'].append(self)
@property @property
def is_locked(self): def is_locked(self):
"""Return true if device is locked.""" """Return true if device is locked."""
@ -39,3 +140,60 @@ class WinkLockDevice(WinkDevice, LockDevice):
def unlock(self, **kwargs): def unlock(self, **kwargs):
"""Unlock the device.""" """Unlock the device."""
self.wink.set_state(False) self.wink.set_state(False)
def set_alarm_state(self, enabled):
"""Set lock's alarm state."""
self.wink.set_alarm_state(enabled)
def set_vacation_mode(self, enabled):
"""Set lock's vacation mode."""
self.wink.set_vacation_mode(enabled)
def set_beeper_state(self, enabled):
"""Set lock's beeper mode."""
self.wink.set_beeper_mode(enabled)
def set_alarm_sensitivity(self, sensitivity):
"""
Set lock's alarm sensitivity.
Valid sensitivities:
0.2, 0.4, 0.6, 0.8, 1.0
"""
self.wink.set_alarm_sensitivity(sensitivity)
def set_alarm_mode(self, mode):
"""
Set lock's alarm mode.
Valid modes:
alert - Beep when lock is locked or unlocked
tamper - 15 sec alarm when lock is disturbed when locked
forced_entry - 3 min alarm when significant force applied
to door when locked.
"""
self.wink.set_alarm_mode(mode)
@property
def device_state_attributes(self):
"""Return the state attributes."""
super_attrs = super().device_state_attributes
sensitivity = dict_value_to_key(ALARM_SENSITIVITY_MAP,
self.wink.alarm_sensitivity())
super_attrs['alarm sensitivity'] = sensitivity
super_attrs['vacation mode'] = self.wink.vacation_mode_enabled()
super_attrs['beeper mode'] = self.wink.beeper_enabled()
super_attrs['auto lock'] = self.wink.auto_lock_enabled()
alarm_mode = dict_value_to_key(ALARM_MODES_MAP,
self.wink.alarm_mode())
super_attrs['alarm mode'] = alarm_mode
super_attrs['alarm enabled'] = self.wink.alarm_enabled()
return super_attrs
def dict_value_to_key(dict_map, comp_value):
"""Return the key that has the provided value."""
for key, value in dict_map.items():
if value == comp_value:
return key
return STATE_UNKNOWN

View file

@ -4,6 +4,7 @@ Support for Wink scenes.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/scene.wink/ https://home-assistant.io/components/scene.wink/
""" """
import asyncio
import logging import logging
from homeassistant.components.scene import Scene from homeassistant.components.scene import Scene
@ -29,6 +30,12 @@ class WinkScene(WinkDevice, Scene):
def __init__(self, wink, hass): def __init__(self, wink, hass):
"""Initialize the Wink device.""" """Initialize the Wink device."""
super().__init__(wink, hass) super().__init__(wink, hass)
hass.data[DOMAIN]['entities']['scene'].append(self)
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['scene'].append(self)
@property @property
def is_on(self): def is_on(self):

View file

@ -4,6 +4,7 @@ Support for Wink sensors.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
at https://home-assistant.io/components/sensor.wink/ at https://home-assistant.io/components/sensor.wink/
""" """
import asyncio
import logging import logging
from homeassistant.const import TEMP_CELSIUS from homeassistant.const import TEMP_CELSIUS
@ -58,6 +59,11 @@ class WinkSensorDevice(WinkDevice, Entity):
else: else:
self._unit_of_measurement = self.wink.unit() self._unit_of_measurement = self.wink.unit()
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['sensor'].append(self)
@property @property
def state(self): def state(self):
"""Return the state.""" """Return the state."""

View file

@ -4,6 +4,7 @@ Support for Wink switches.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.wink/ https://home-assistant.io/components/switch.wink/
""" """
import asyncio
from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.components.wink import WinkDevice, DOMAIN
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import ToggleEntity
@ -40,6 +41,11 @@ class WinkToggleDevice(WinkDevice, ToggleEntity):
"""Initialize the Wink device.""" """Initialize the Wink device."""
super().__init__(wink, hass) super().__init__(wink, hass)
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['switch'].append(self)
@property @property
def is_on(self): def is_on(self):
"""Return true if device is on.""" """Return true if device is on."""

View file

@ -75,6 +75,7 @@ def setup(hass, config):
hass.data[DOMAIN] = {} hass.data[DOMAIN] = {}
hass.data[DOMAIN]['entities'] = [] hass.data[DOMAIN]['entities'] = []
hass.data[DOMAIN]['unique_ids'] = [] hass.data[DOMAIN]['unique_ids'] = []
hass.data[DOMAIN]['entities'] = {}
user_agent = config[DOMAIN].get(CONF_USER_AGENT) user_agent = config[DOMAIN].get(CONF_USER_AGENT)
@ -154,10 +155,11 @@ def setup(hass, config):
def force_update(call): def force_update(call):
"""Force all devices to poll the Wink API.""" """Force all devices to poll the Wink API."""
_LOGGER.info("Refreshing Wink states from API") _LOGGER.info("Refreshing Wink states from API")
for entity in hass.data[DOMAIN]['entities']: for entity_list in hass.data[DOMAIN]['entities'].values():
# Throttle the calls to Wink API # Throttle the calls to Wink API
time.sleep(1) for entity in entity_list:
entity.schedule_update_ha_state(True) time.sleep(1)
entity.schedule_update_ha_state(True)
hass.services.register(DOMAIN, SERVICE_REFRESH_STATES, force_update) hass.services.register(DOMAIN, SERVICE_REFRESH_STATES, force_update)
def pull_new_devices(call): def pull_new_devices(call):
@ -169,6 +171,7 @@ def setup(hass, config):
# Load components for the devices in Wink that we support # Load components for the devices in Wink that we support
for component in WINK_COMPONENTS: for component in WINK_COMPONENTS:
hass.data[DOMAIN]['entities'][component] = []
discovery.load_platform(hass, component, DOMAIN, {}, config) discovery.load_platform(hass, component, DOMAIN, {}, config)
return True return True
@ -183,7 +186,6 @@ class WinkDevice(Entity):
self.wink = wink self.wink = wink
hass.data[DOMAIN]['pubnub'].add_subscription( hass.data[DOMAIN]['pubnub'].add_subscription(
self.wink.pubnub_channel, self._pubnub_update) self.wink.pubnub_channel, self._pubnub_update)
hass.data[DOMAIN]['entities'].append(self)
hass.data[DOMAIN]['unique_ids'].append(self.wink.object_id() + hass.data[DOMAIN]['unique_ids'].append(self.wink.object_id() +
self.wink.name()) self.wink.name())