diff --git a/homeassistant/components/alarm_control_panel/wink.py b/homeassistant/components/alarm_control_panel/wink.py index 12dca97dd81..a8cad115883 100644 --- a/homeassistant/components/alarm_control_panel/wink.py +++ b/homeassistant/components/alarm_control_panel/wink.py @@ -4,6 +4,7 @@ Interfaces with Wink Cameras. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/alarm_control_panel.wink/ """ +import asyncio import logging import homeassistant.components.alarm_control_panel as alarm @@ -42,6 +43,11 @@ class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel): """Initialize the Wink alarm.""" 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 def state(self): """Return the state of the device.""" diff --git a/homeassistant/components/binary_sensor/wink.py b/homeassistant/components/binary_sensor/wink.py index 3f77d1d6081..c16c62a5f81 100644 --- a/homeassistant/components/binary_sensor/wink.py +++ b/homeassistant/components/binary_sensor/wink.py @@ -4,6 +4,7 @@ Support for Wink binary sensors. For more details about this platform, please refer to the documentation at at https://home-assistant.io/components/binary_sensor.wink/ """ +import asyncio import logging from homeassistant.components.binary_sensor import BinarySensorDevice @@ -101,6 +102,11 @@ class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity): else: 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 def is_on(self): """Return true if the binary sensor is on.""" diff --git a/homeassistant/components/climate/wink.py b/homeassistant/components/climate/wink.py index 256af2d013c..1be7480a727 100644 --- a/homeassistant/components/climate/wink.py +++ b/homeassistant/components/climate/wink.py @@ -4,6 +4,8 @@ Support for Wink thermostats. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/climate.wink/ """ +import asyncio + from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.components.climate import ( STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice, @@ -52,6 +54,11 @@ class WinkThermostat(WinkDevice, ClimateDevice): super().__init__(wink, hass) 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 def temperature_unit(self): """Return the unit of measurement.""" diff --git a/homeassistant/components/cover/wink.py b/homeassistant/components/cover/wink.py index 5472180db62..d5908c35ca2 100644 --- a/homeassistant/components/cover/wink.py +++ b/homeassistant/components/cover/wink.py @@ -4,6 +4,8 @@ Support for Wink Covers. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/cover.wink/ """ +import asyncio + from homeassistant.components.cover import CoverDevice from homeassistant.components.wink import WinkDevice, DOMAIN @@ -31,6 +33,11 @@ class WinkCoverDevice(WinkDevice, CoverDevice): """Initialize the cover.""" 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): """Close the shade.""" self.wink.set_state(0) diff --git a/homeassistant/components/fan/wink.py b/homeassistant/components/fan/wink.py index e8f5d6fd17a..13f755bcdf3 100644 --- a/homeassistant/components/fan/wink.py +++ b/homeassistant/components/fan/wink.py @@ -4,6 +4,7 @@ Support for Wink fans. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/fan.wink/ """ +import asyncio import logging 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.components.wink import WinkDevice, DOMAIN +DEPENDENCIES = ['wink'] + _LOGGER = logging.getLogger(__name__) SPEED_LOWEST = 'lowest' @@ -34,6 +37,11 @@ class WinkFanDevice(WinkDevice, FanEntity): """Initialize the fan.""" 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: """Set the direction of the fan.""" self.wink.set_fan_direction(direction) diff --git a/homeassistant/components/light/wink.py b/homeassistant/components/light/wink.py index 82b7c9f4f8c..1f046a2ec27 100644 --- a/homeassistant/components/light/wink.py +++ b/homeassistant/components/light/wink.py @@ -4,6 +4,7 @@ Support for Wink lights. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.wink/ """ +import asyncio import colorsys from homeassistant.components.light import ( @@ -38,6 +39,11 @@ class WinkLight(WinkDevice, Light): """Initialize the Wink device.""" 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 def is_on(self): """Return true if light is on.""" diff --git a/homeassistant/components/lock/services.yaml b/homeassistant/components/lock/services.yaml index 6b12d49302d..df370ca0168 100644 --- a/homeassistant/components/lock/services.yaml +++ b/homeassistant/components/lock/services.yaml @@ -55,3 +55,59 @@ unlock: code: description: An optional code to unlock the lock with 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 + diff --git a/homeassistant/components/lock/wink.py b/homeassistant/components/lock/wink.py index 9ac5c579ab5..6fbf9edf954 100644 --- a/homeassistant/components/lock/wink.py +++ b/homeassistant/components/lock/wink.py @@ -4,11 +4,55 @@ Support for Wink locks. For more details about this platform, please refer to the documentation at 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.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'] +_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): """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']: 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): """Representation of a Wink lock.""" @@ -27,6 +123,11 @@ class WinkLockDevice(WinkDevice, LockDevice): """Initialize the lock.""" 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 def is_locked(self): """Return true if device is locked.""" @@ -39,3 +140,60 @@ class WinkLockDevice(WinkDevice, LockDevice): def unlock(self, **kwargs): """Unlock the device.""" 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 diff --git a/homeassistant/components/scene/wink.py b/homeassistant/components/scene/wink.py index 3906e7b5551..008edf6f131 100644 --- a/homeassistant/components/scene/wink.py +++ b/homeassistant/components/scene/wink.py @@ -4,6 +4,7 @@ Support for Wink scenes. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/scene.wink/ """ +import asyncio import logging from homeassistant.components.scene import Scene @@ -29,6 +30,12 @@ class WinkScene(WinkDevice, Scene): def __init__(self, wink, hass): """Initialize the Wink device.""" 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 def is_on(self): diff --git a/homeassistant/components/sensor/wink.py b/homeassistant/components/sensor/wink.py index 27cfbd691ad..b8c2b8a6236 100644 --- a/homeassistant/components/sensor/wink.py +++ b/homeassistant/components/sensor/wink.py @@ -4,6 +4,7 @@ Support for Wink sensors. For more details about this platform, please refer to the documentation at at https://home-assistant.io/components/sensor.wink/ """ +import asyncio import logging from homeassistant.const import TEMP_CELSIUS @@ -58,6 +59,11 @@ class WinkSensorDevice(WinkDevice, Entity): else: 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 def state(self): """Return the state.""" diff --git a/homeassistant/components/switch/wink.py b/homeassistant/components/switch/wink.py index 6783f2201c1..b5feac5fc43 100644 --- a/homeassistant/components/switch/wink.py +++ b/homeassistant/components/switch/wink.py @@ -4,6 +4,7 @@ Support for Wink switches. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.wink/ """ +import asyncio from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.helpers.entity import ToggleEntity @@ -40,6 +41,11 @@ class WinkToggleDevice(WinkDevice, ToggleEntity): """Initialize the Wink device.""" 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 def is_on(self): """Return true if device is on.""" diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index c22e32b51d4..c33e3b14502 100644 --- a/homeassistant/components/wink.py +++ b/homeassistant/components/wink.py @@ -75,6 +75,7 @@ def setup(hass, config): hass.data[DOMAIN] = {} hass.data[DOMAIN]['entities'] = [] hass.data[DOMAIN]['unique_ids'] = [] + hass.data[DOMAIN]['entities'] = {} user_agent = config[DOMAIN].get(CONF_USER_AGENT) @@ -154,10 +155,11 @@ def setup(hass, config): def force_update(call): """Force all devices to poll the Wink 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 - time.sleep(1) - entity.schedule_update_ha_state(True) + for entity in entity_list: + time.sleep(1) + entity.schedule_update_ha_state(True) hass.services.register(DOMAIN, SERVICE_REFRESH_STATES, force_update) def pull_new_devices(call): @@ -169,6 +171,7 @@ def setup(hass, config): # Load components for the devices in Wink that we support for component in WINK_COMPONENTS: + hass.data[DOMAIN]['entities'][component] = [] discovery.load_platform(hass, component, DOMAIN, {}, config) return True @@ -183,7 +186,6 @@ class WinkDevice(Entity): self.wink = wink hass.data[DOMAIN]['pubnub'].add_subscription( self.wink.pubnub_channel, self._pubnub_update) - hass.data[DOMAIN]['entities'].append(self) hass.data[DOMAIN]['unique_ids'].append(self.wink.object_id() + self.wink.name())