diff --git a/homeassistant/components/binary_sensor/wink.py b/homeassistant/components/binary_sensor/wink.py index d9c2b7d577a..9ec85e63503 100644 --- a/homeassistant/components/binary_sensor/wink.py +++ b/homeassistant/components/binary_sensor/wink.py @@ -7,10 +7,12 @@ at https://home-assistant.io/components/sensor.wink/ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.const import CONF_ACCESS_TOKEN, ATTR_BATTERY_LEVEL +from homeassistant.components.sensor.wink import WinkDevice +from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.helpers.entity import Entity +from homeassistant.loader import get_component -REQUIREMENTS = ['python-wink==0.7.7'] +REQUIREMENTS = ['python-wink==0.7.8', 'pubnub==3.7.8'] # These are the available sensors mapped to binary_sensor class SENSOR_TYPES = { @@ -41,14 +43,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices([WinkBinarySensorDevice(sensor)]) -class WinkBinarySensorDevice(BinarySensorDevice, Entity): +class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity): """Representation of a Wink sensor.""" def __init__(self, wink): """Initialize the Wink binary sensor.""" - self.wink = wink + super().__init__(wink) + wink = get_component('wink') self._unit_of_measurement = self.wink.UNIT - self._battery = self.wink.battery_level self.capability = self.wink.capability() @property @@ -67,35 +69,3 @@ class WinkBinarySensorDevice(BinarySensorDevice, Entity): def sensor_class(self): """Return the class of this sensor, from SENSOR_CLASSES.""" return SENSOR_TYPES.get(self.capability) - - @property - def unique_id(self): - """Return the ID of this wink sensor.""" - return "{}.{}".format(self.__class__, self.wink.device_id()) - - @property - def name(self): - """Return the name of the sensor if any.""" - return self.wink.name() - - @property - def available(self): - """True if connection == True.""" - return self.wink.available - - def update(self): - """Update state of the sensor.""" - self.wink.update_state() - - @property - def device_state_attributes(self): - """Return the state attributes.""" - if self._battery: - return { - ATTR_BATTERY_LEVEL: self._battery_level, - } - - @property - def _battery_level(self): - """Return the battery level.""" - return self.wink.battery_level * 100 diff --git a/homeassistant/components/garage_door/wink.py b/homeassistant/components/garage_door/wink.py index 18ec6f2ba56..73692290f50 100644 --- a/homeassistant/components/garage_door/wink.py +++ b/homeassistant/components/garage_door/wink.py @@ -7,9 +7,10 @@ https://home-assistant.io/components/garage_door.wink/ import logging from homeassistant.components.garage_door import GarageDoorDevice -from homeassistant.const import CONF_ACCESS_TOKEN, ATTR_BATTERY_LEVEL +from homeassistant.components.wink import WinkDevice +from homeassistant.const import CONF_ACCESS_TOKEN -REQUIREMENTS = ['python-wink==0.7.7'] +REQUIREMENTS = ['python-wink==0.7.8', 'pubnub==3.7.8'] def setup_platform(hass, config, add_devices, discovery_info=None): @@ -31,38 +32,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None): pywink.get_garage_doors()) -class WinkGarageDoorDevice(GarageDoorDevice): +class WinkGarageDoorDevice(WinkDevice, GarageDoorDevice): """Representation of a Wink garage door.""" def __init__(self, wink): """Initialize the garage door.""" - self.wink = wink - self._battery = self.wink.battery_level - - @property - def unique_id(self): - """Return the ID of this wink garage door.""" - return "{}.{}".format(self.__class__, self.wink.device_id()) - - @property - def name(self): - """Return the name of the garage door if any.""" - return self.wink.name() - - def update(self): - """Update the state of the garage door.""" - self.wink.update_state() + WinkDevice.__init__(self, wink) @property def is_closed(self): """Return true if door is closed.""" return self.wink.state() == 0 - @property - def available(self): - """True if connection == True.""" - return self.wink.available - def close_door(self): """Close the door.""" self.wink.set_state(0) @@ -70,16 +51,3 @@ class WinkGarageDoorDevice(GarageDoorDevice): def open_door(self): """Open the door.""" self.wink.set_state(1) - - @property - def device_state_attributes(self): - """Return the state attributes.""" - if self._battery: - return { - ATTR_BATTERY_LEVEL: self._battery_level, - } - - @property - def _battery_level(self): - """Return the battery level.""" - return self.wink.battery_level * 100 diff --git a/homeassistant/components/light/wink.py b/homeassistant/components/light/wink.py index 2438cdaab9a..5fdec96f5d4 100644 --- a/homeassistant/components/light/wink.py +++ b/homeassistant/components/light/wink.py @@ -8,12 +8,13 @@ import logging from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, \ Light, ATTR_RGB_COLOR +from homeassistant.components.wink import WinkDevice from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.util import color as color_util from homeassistant.util.color import \ color_temperature_mired_to_kelvin as mired_to_kelvin -REQUIREMENTS = ['python-wink==0.7.7'] +REQUIREMENTS = ['python-wink==0.7.8', 'pubnub==3.7.8'] def setup_platform(hass, config, add_devices_callback, discovery_info=None): @@ -35,26 +36,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): WinkLight(light) for light in pywink.get_bulbs()) -class WinkLight(Light): +class WinkLight(WinkDevice, Light): """Representation of a Wink light.""" def __init__(self, wink): - """ - Initialize the light. - - :type wink: pywink.devices.standard.bulb.WinkBulb - """ - self.wink = wink - - @property - def unique_id(self): - """Return the ID of this Wink light.""" - return "{}.{}".format(self.__class__, self.wink.device_id()) - - @property - def name(self): - """Return the name of the light if any.""" - return self.wink.name() + """Initialize the Wink device.""" + WinkDevice.__init__(self, wink) @property def is_on(self): @@ -66,11 +53,6 @@ class WinkLight(Light): """Return the brightness of the light.""" return int(self.wink.brightness() * 255) - @property - def available(self): - """True if connection == True.""" - return self.wink.available - @property def xy_color(self): """Current bulb color in CIE 1931 (XY) color space.""" @@ -112,7 +94,3 @@ class WinkLight(Light): def turn_off(self): """Turn the switch off.""" self.wink.set_state(False) - - def update(self): - """Update state of the light.""" - self.wink.update_state(require_desired_state_fulfilled=True) diff --git a/homeassistant/components/lock/wink.py b/homeassistant/components/lock/wink.py index 2572796df35..7551302499a 100644 --- a/homeassistant/components/lock/wink.py +++ b/homeassistant/components/lock/wink.py @@ -7,9 +7,10 @@ https://home-assistant.io/components/lock.wink/ import logging from homeassistant.components.lock import LockDevice -from homeassistant.const import CONF_ACCESS_TOKEN, ATTR_BATTERY_LEVEL +from homeassistant.components.wink import WinkDevice +from homeassistant.const import CONF_ACCESS_TOKEN -REQUIREMENTS = ['python-wink==0.7.7'] +REQUIREMENTS = ['python-wink==0.7.8', 'pubnub==3.7.8'] def setup_platform(hass, config, add_devices, discovery_info=None): @@ -30,38 +31,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices(WinkLockDevice(lock) for lock in pywink.get_locks()) -class WinkLockDevice(LockDevice): +class WinkLockDevice(WinkDevice, LockDevice): """Representation of a Wink lock.""" def __init__(self, wink): """Initialize the lock.""" - self.wink = wink - self._battery = self.wink.battery_level - - @property - def unique_id(self): - """Return the id of this wink lock.""" - return "{}.{}".format(self.__class__, self.wink.device_id()) - - @property - def name(self): - """Return the name of the lock if any.""" - return self.wink.name() - - def update(self): - """Update the state of the lock.""" - self.wink.update_state() + WinkDevice.__init__(self, wink) @property def is_locked(self): """Return true if device is locked.""" return self.wink.state() - @property - def available(self): - """True if connection == True.""" - return self.wink.available - def lock(self, **kwargs): """Lock the device.""" self.wink.set_state(True) @@ -69,16 +50,3 @@ class WinkLockDevice(LockDevice): def unlock(self, **kwargs): """Unlock the device.""" self.wink.set_state(False) - - @property - def device_state_attributes(self): - """Return the state attributes.""" - if self._battery: - return { - ATTR_BATTERY_LEVEL: self._battery_level, - } - - @property - def _battery_level(self): - """Return the battery level.""" - return self.wink.battery_level * 100 diff --git a/homeassistant/components/rollershutter/wink.py b/homeassistant/components/rollershutter/wink.py index e01b2573ac6..8a31148da01 100644 --- a/homeassistant/components/rollershutter/wink.py +++ b/homeassistant/components/rollershutter/wink.py @@ -7,9 +7,10 @@ https://home-assistant.io/components/rollershutter.wink/ import logging from homeassistant.components.rollershutter import RollershutterDevice +from homeassistant.components.wink import WinkDevice from homeassistant.const import CONF_ACCESS_TOKEN -REQUIREMENTS = ['python-wink==0.7.7'] +REQUIREMENTS = ['python-wink==0.7.8', 'pubnub==3.7.8'] def setup_platform(hass, config, add_devices, discovery_info=None): @@ -31,38 +32,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None): pywink.get_shades()) -class WinkRollershutterDevice(RollershutterDevice): +class WinkRollershutterDevice(WinkDevice, RollershutterDevice): """Representation of a Wink rollershutter (shades).""" def __init__(self, wink): """Initialize the rollershutter.""" - self.wink = wink - self._battery = None + WinkDevice.__init__(self, wink) @property def should_poll(self): """Wink Shades don't track their position.""" return False - @property - def unique_id(self): - """Return the ID of this wink rollershutter.""" - return "{}.{}".format(self.__class__, self.wink.device_id()) - - @property - def name(self): - """Return the name of the rollershutter if any.""" - return self.wink.name() - - def update(self): - """Update the state of the rollershutter.""" - return self.wink.update_state() - - @property - def available(self): - """True if connection == True.""" - return self.wink.available - def move_down(self): """Close the shade.""" self.wink.set_state(0) diff --git a/homeassistant/components/sensor/wink.py b/homeassistant/components/sensor/wink.py index 3fb914d6cd9..ac885152a2e 100644 --- a/homeassistant/components/sensor/wink.py +++ b/homeassistant/components/sensor/wink.py @@ -7,11 +7,12 @@ at https://home-assistant.io/components/sensor.wink/ import logging from homeassistant.const import (CONF_ACCESS_TOKEN, STATE_CLOSED, - STATE_OPEN, TEMP_CELSIUS, - ATTR_BATTERY_LEVEL) + STATE_OPEN, TEMP_CELSIUS) from homeassistant.helpers.entity import Entity +from homeassistant.components.wink import WinkDevice +from homeassistant.loader import get_component -REQUIREMENTS = ['python-wink==0.7.7'] +REQUIREMENTS = ['python-wink==0.7.8', 'pubnub==3.7.8'] SENSOR_TYPES = ['temperature', 'humidity'] @@ -38,14 +39,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices(WinkEggMinder(eggtray) for eggtray in pywink.get_eggtrays()) -class WinkSensorDevice(Entity): +class WinkSensorDevice(WinkDevice, Entity): """Representation of a Wink sensor.""" def __init__(self, wink): - """Initialize the sensor.""" - self.wink = wink + """Initialize the Wink device.""" + super().__init__(wink) + wink = get_component('wink') self.capability = self.wink.capability() - self._battery = self.wink.battery_level if self.wink.UNIT == "°": self._unit_of_measurement = TEMP_CELSIUS else: @@ -55,9 +56,9 @@ class WinkSensorDevice(Entity): def state(self): """Return the state.""" if self.capability == "humidity": - return self.wink.humidity_percentage() + return round(self.wink.humidity_percentage()) elif self.capability == "temperature": - return self.wink.temperature_float() + return round(self.wink.temperature_float(), 1) else: return STATE_OPEN if self.is_open else STATE_CLOSED @@ -66,80 +67,20 @@ class WinkSensorDevice(Entity): """Return the unit of measurement of this entity, if any.""" return self._unit_of_measurement - @property - def unique_id(self): - """Return the ID of this wink sensor.""" - return "{}.{}".format(self.__class__, self.wink.device_id()) - - @property - def name(self): - """Return the name of the sensor if any.""" - return self.wink.name() - - @property - def available(self): - """True if connection == True.""" - return self.wink.available - - def update(self): - """Update state of the sensor.""" - self.wink.update_state() - @property def is_open(self): """Return true if door is open.""" return self.wink.state() - @property - def device_state_attributes(self): - """Return the state attributes.""" - if self._battery: - return { - ATTR_BATTERY_LEVEL: self._battery_level, - } - @property - def _battery_level(self): - """Return the battery level.""" - return self.wink.battery_level * 100 - - -class WinkEggMinder(Entity): +class WinkEggMinder(WinkDevice, Entity): """Representation of a Wink Egg Minder.""" def __init__(self, wink): """Initialize the sensor.""" - self.wink = wink - self._battery = self.wink.battery_level + WinkDevice.__init__(self, wink) @property def state(self): """Return the state.""" return self.wink.state() - - @property - def unique_id(self): - """Return the id of this wink Egg Minder.""" - return "{}.{}".format(self.__class__, self.wink.device_id()) - - @property - def name(self): - """Return the name of the Egg Minder if any.""" - return self.wink.name() - - def update(self): - """Update state of the Egg Minder.""" - self.wink.update_state() - - @property - def device_state_attributes(self): - """Return the state attributes.""" - if self._battery: - return { - ATTR_BATTERY_LEVEL: self._battery_level, - } - - @property - def _battery_level(self): - """Return the battery level.""" - return self.wink.battery_level * 100 diff --git a/homeassistant/components/switch/wink.py b/homeassistant/components/switch/wink.py index a5b67f5ddcf..64c19e34bc9 100644 --- a/homeassistant/components/switch/wink.py +++ b/homeassistant/components/switch/wink.py @@ -6,10 +6,11 @@ https://home-assistant.io/components/switch.wink/ """ import logging -from homeassistant.components.wink import WinkToggleDevice +from homeassistant.components.wink import WinkDevice from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.helpers.entity import ToggleEntity -REQUIREMENTS = ['python-wink==0.7.7'] +REQUIREMENTS = ['python-wink==0.7.8', 'pubnub==3.7.8'] def setup_platform(hass, config, add_devices, discovery_info=None): @@ -31,3 +32,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices(WinkToggleDevice(switch) for switch in pywink.get_powerstrip_outlets()) add_devices(WinkToggleDevice(switch) for switch in pywink.get_sirens()) + + +class WinkToggleDevice(WinkDevice, ToggleEntity): + """Represents a Wink toggle (switch) device.""" + + def __init__(self, wink): + """Initialize the Wink device.""" + WinkDevice.__init__(self, wink) + + @property + def is_on(self): + """Return true if device is on.""" + return self.wink.state() + + def turn_on(self, **kwargs): + """Turn the device on.""" + self.wink.set_state(True) + + def turn_off(self): + """Turn the device off.""" + self.wink.set_state(False) diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index 85bc7f46cef..4e9fec77ba5 100644 --- a/homeassistant/components/wink.py +++ b/homeassistant/components/wink.py @@ -5,13 +5,17 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/wink/ """ import logging +import json -from homeassistant.const import CONF_ACCESS_TOKEN, ATTR_BATTERY_LEVEL from homeassistant.helpers import validate_config, discovery -from homeassistant.helpers.entity import ToggleEntity +from homeassistant.const import CONF_ACCESS_TOKEN, ATTR_BATTERY_LEVEL +from homeassistant.helpers.entity import Entity DOMAIN = "wink" -REQUIREMENTS = ['python-wink==0.7.7'] +REQUIREMENTS = ['python-wink==0.7.8', 'pubnub==3.7.8'] + +SUBSCRIPTION_HANDLER = None +CHANNELS = [] def setup(hass, config): @@ -22,7 +26,11 @@ def setup(hass, config): return False import pywink + from pubnub import Pubnub pywink.set_bearer_token(config[DOMAIN][CONF_ACCESS_TOKEN]) + global SUBSCRIPTION_HANDLER + SUBSCRIPTION_HANDLER = Pubnub("N/A", pywink.get_subscription_key()) + SUBSCRIPTION_HANDLER.set_heartbeat(120) # Load components for the devices in the Wink that we support for component_name, func_exists in ( @@ -41,13 +49,33 @@ def setup(hass, config): return True -class WinkToggleDevice(ToggleEntity): - """Represents a Wink toggle (switch) device.""" +class WinkDevice(Entity): + """Represents a base Wink device.""" def __init__(self, wink): """Initialize the Wink device.""" + from pubnub import Pubnub self.wink = wink self._battery = self.wink.battery_level + if self.wink.pubnub_channel in CHANNELS: + pubnub = Pubnub("N/A", self.wink.pubnub_key) + pubnub.set_heartbeat(120) + pubnub.subscribe(self.wink.pubnub_channel, + self._pubnub_update, + error=self._pubnub_error) + else: + CHANNELS.append(self.wink.pubnub_channel) + SUBSCRIPTION_HANDLER.subscribe(self.wink.pubnub_channel, + self._pubnub_update, + error=self._pubnub_error) + + def _pubnub_update(self, message, channel): + self.wink.pubnub_update(json.loads(message)) + self.update_ha_state() + + def _pubnub_error(self, message): + logging.getLogger(__name__).error( + "Error on pubnub update for " + self.wink.name()) @property def unique_id(self): @@ -59,28 +87,20 @@ class WinkToggleDevice(ToggleEntity): """Return the name of the device.""" return self.wink.name() - @property - def is_on(self): - """Return true if device is on.""" - return self.wink.state() - @property def available(self): """True if connection == True.""" return self.wink.available - def turn_on(self, **kwargs): - """Turn the device on.""" - self.wink.set_state(True) - - def turn_off(self): - """Turn the device off.""" - self.wink.set_state(False) - def update(self): """Update state of the device.""" self.wink.update_state() + @property + def should_poll(self): + """Only poll if we are not subscribed to pubnub.""" + return self.wink.pubnub_channel is None + @property def device_state_attributes(self): """Return the state attributes.""" diff --git a/requirements_all.txt b/requirements_all.txt index 88a1bed04fd..dd97c51185c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -220,6 +220,16 @@ proliphix==0.1.0 # homeassistant.components.sensor.systemmonitor psutil==4.3.0 +# homeassistant.components.wink +# homeassistant.components.binary_sensor.wink +# homeassistant.components.garage_door.wink +# homeassistant.components.light.wink +# homeassistant.components.lock.wink +# homeassistant.components.rollershutter.wink +# homeassistant.components.sensor.wink +# homeassistant.components.switch.wink +pubnub==3.7.8 + # homeassistant.components.notify.pushbullet pushbullet.py==0.10.0 @@ -324,7 +334,7 @@ python-twitch==1.2.0 # homeassistant.components.rollershutter.wink # homeassistant.components.sensor.wink # homeassistant.components.switch.wink -python-wink==0.7.7 +python-wink==0.7.8 # homeassistant.components.keyboard pyuserinput==0.1.9