From 4c03d670c1443bc2d0d6bed6678e4eff15960029 Mon Sep 17 00:00:00 2001 From: William Scanlon Date: Wed, 30 Nov 2016 16:12:26 -0500 Subject: [PATCH] Wink PubNub v4 (#4561) * PubNub v4 * Updated to pubnubsub-handler 0.0.5 * Updated requirements_all.txt --- .../components/binary_sensor/wink.py | 25 ++---- homeassistant/components/climate/wink.py | 6 +- homeassistant/components/cover/wink.py | 8 +- homeassistant/components/light/wink.py | 6 +- homeassistant/components/lock/wink.py | 6 +- homeassistant/components/sensor/wink.py | 17 +++-- homeassistant/components/switch/wink.py | 14 ++-- homeassistant/components/wink.py | 76 +++++++++++-------- requirements_all.txt | 4 +- 9 files changed, 82 insertions(+), 80 deletions(-) diff --git a/homeassistant/components/binary_sensor/wink.py b/homeassistant/components/binary_sensor/wink.py index e4448d96e36..2d0e3f7226f 100644 --- a/homeassistant/components/binary_sensor/wink.py +++ b/homeassistant/components/binary_sensor/wink.py @@ -4,8 +4,6 @@ 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 json -import logging from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.sensor.wink import WinkDevice @@ -34,38 +32,25 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for sensor in pywink.get_sensors(): if sensor.capability() in SENSOR_TYPES: - add_devices([WinkBinarySensorDevice(sensor)]) + add_devices([WinkBinarySensorDevice(sensor, hass)]) for key in pywink.get_keys(): - add_devices([WinkBinarySensorDevice(key)]) + add_devices([WinkBinarySensorDevice(key, hass)]) for sensor in pywink.get_smoke_and_co_detectors(): - add_devices([WinkBinarySensorDevice(sensor)]) + add_devices([WinkBinarySensorDevice(sensor, hass)]) class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity): """Representation of a Wink binary sensor.""" - def __init__(self, wink): + def __init__(self, wink, hass): """Initialize the Wink binary sensor.""" - super().__init__(wink) + super().__init__(wink, hass) wink = get_component('wink') self._unit_of_measurement = self.wink.UNIT self.capability = self.wink.capability() - def _pubnub_update(self, message, channel): - try: - if 'data' in message: - json_data = json.dumps(message.get('data')) - else: - json_data = message - self.wink.pubnub_update(json.loads(json_data)) - self.update_ha_state() - except (AttributeError, KeyError): - error = "Pubnub returned invalid json for " + self.name - logging.getLogger(__name__).error(error) - self.update_ha_state(True) - @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 a0094a7c290..733d2baddf7 100644 --- a/homeassistant/components/climate/wink.py +++ b/homeassistant/components/climate/wink.py @@ -30,7 +30,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Wink thermostat.""" import pywink temp_unit = hass.config.units.temperature_unit - add_devices(WinkThermostat(thermostat, temp_unit) + add_devices(WinkThermostat(thermostat, hass, temp_unit) for thermostat in pywink.get_thermostats()) @@ -38,9 +38,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class WinkThermostat(WinkDevice, ClimateDevice): """Representation of a Wink thermostat.""" - def __init__(self, wink, temp_unit): + def __init__(self, wink, hass, temp_unit): """Initialize the Wink device.""" - super().__init__(wink) + super().__init__(wink, hass) wink = get_component('wink') self._config_temp_unit = temp_unit diff --git a/homeassistant/components/cover/wink.py b/homeassistant/components/cover/wink.py index c57c2180446..264cec70a7e 100644 --- a/homeassistant/components/cover/wink.py +++ b/homeassistant/components/cover/wink.py @@ -15,18 +15,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Wink cover platform.""" import pywink - add_devices(WinkCoverDevice(shade) for shade in + add_devices(WinkCoverDevice(shade, hass) for shade in pywink.get_shades()) - add_devices(WinkCoverDevice(door) for door in + add_devices(WinkCoverDevice(door, hass) for door in pywink.get_garage_doors()) class WinkCoverDevice(WinkDevice, CoverDevice): """Representation of a Wink cover device.""" - def __init__(self, wink): + def __init__(self, wink, hass): """Initialize the cover.""" - WinkDevice.__init__(self, wink) + WinkDevice.__init__(self, wink, hass) def close_cover(self): """Close the shade.""" diff --git a/homeassistant/components/light/wink.py b/homeassistant/components/light/wink.py index 1d292a53419..1a4556ee46b 100644 --- a/homeassistant/components/light/wink.py +++ b/homeassistant/components/light/wink.py @@ -23,15 +23,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Wink lights.""" import pywink - add_devices(WinkLight(light) for light in pywink.get_bulbs()) + add_devices(WinkLight(light, hass) for light in pywink.get_bulbs()) class WinkLight(WinkDevice, Light): """Representation of a Wink light.""" - def __init__(self, wink): + def __init__(self, wink, hass): """Initialize the Wink device.""" - WinkDevice.__init__(self, wink) + WinkDevice.__init__(self, wink, hass) @property def is_on(self): diff --git a/homeassistant/components/lock/wink.py b/homeassistant/components/lock/wink.py index 2e44c277b02..4536387e4ac 100644 --- a/homeassistant/components/lock/wink.py +++ b/homeassistant/components/lock/wink.py @@ -15,15 +15,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Wink platform.""" import pywink - add_devices(WinkLockDevice(lock) for lock in pywink.get_locks()) + add_devices(WinkLockDevice(lock, hass) for lock in pywink.get_locks()) class WinkLockDevice(WinkDevice, LockDevice): """Representation of a Wink lock.""" - def __init__(self, wink): + def __init__(self, wink, hass): """Initialize the lock.""" - WinkDevice.__init__(self, wink) + WinkDevice.__init__(self, wink, hass) @property def is_locked(self): diff --git a/homeassistant/components/sensor/wink.py b/homeassistant/components/sensor/wink.py index 455b3b03290..379d9ac43e5 100644 --- a/homeassistant/components/sensor/wink.py +++ b/homeassistant/components/sensor/wink.py @@ -22,24 +22,25 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for sensor in pywink.get_sensors(): if sensor.capability() in SENSOR_TYPES: - add_devices([WinkSensorDevice(sensor)]) + add_devices([WinkSensorDevice(sensor, hass)]) - add_devices(WinkEggMinder(eggtray) for eggtray in pywink.get_eggtrays()) + for eggtray in pywink.get_eggtrays(): + add_devices([WinkEggMinder(eggtray, hass)]) for piggy_bank in pywink.get_piggy_banks(): try: if piggy_bank.capability() in SENSOR_TYPES: - add_devices([WinkSensorDevice(piggy_bank)]) + add_devices([WinkSensorDevice(piggy_bank, hass)]) except AttributeError: - logging.getLogger(__name__).error("Device is not a sensor") + logging.getLogger(__name__).info("Device is not a sensor") class WinkSensorDevice(WinkDevice, Entity): """Representation of a Wink sensor.""" - def __init__(self, wink): + def __init__(self, wink, hass): """Initialize the Wink device.""" - super().__init__(wink) + super().__init__(wink, hass) wink = get_component('wink') self.capability = self.wink.capability() if self.wink.UNIT == '°': @@ -84,9 +85,9 @@ class WinkSensorDevice(WinkDevice, Entity): class WinkEggMinder(WinkDevice, Entity): """Representation of a Wink Egg Minder.""" - def __init__(self, wink): + def __init__(self, wink, hass): """Initialize the sensor.""" - WinkDevice.__init__(self, wink) + WinkDevice.__init__(self, wink, hass) @property def state(self): diff --git a/homeassistant/components/switch/wink.py b/homeassistant/components/switch/wink.py index 8bae64bbf99..22793d81f3f 100644 --- a/homeassistant/components/switch/wink.py +++ b/homeassistant/components/switch/wink.py @@ -15,18 +15,20 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Wink platform.""" import pywink - add_devices(WinkToggleDevice(switch) for switch in pywink.get_switches()) - add_devices(WinkToggleDevice(switch) for switch in - pywink.get_powerstrip_outlets()) - add_devices(WinkToggleDevice(switch) for switch in pywink.get_sirens()) + for switch in pywink.get_switches(): + add_devices([WinkToggleDevice(switch, hass)]) + for switch in pywink.get_powerstrip_outlets(): + add_devices([WinkToggleDevice(switch, hass)]) + for switch in pywink.get_sirens(): + add_devices([WinkToggleDevice(switch, hass)]) class WinkToggleDevice(WinkDevice, ToggleEntity): """Representation of a Wink toggle device.""" - def __init__(self, wink): + def __init__(self, wink, hass): """Initialize the Wink device.""" - WinkDevice.__init__(self, wink) + WinkDevice.__init__(self, wink, hass) @property def is_on(self): diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index c678024f6a3..dbd7d8760ce 100644 --- a/homeassistant/components/wink.py +++ b/homeassistant/components/wink.py @@ -5,17 +5,18 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/wink/ """ import logging -import json import voluptuous as vol from homeassistant.helpers import discovery from homeassistant.const import CONF_ACCESS_TOKEN, ATTR_BATTERY_LEVEL, \ - CONF_EMAIL, CONF_PASSWORD + CONF_EMAIL, CONF_PASSWORD, \ + EVENT_HOMEASSISTANT_START, \ + EVENT_HOMEASSISTANT_STOP from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-wink==0.10.0', 'pubnub==3.8.2'] +REQUIREMENTS = ['python-wink==0.10.1', 'pubnubsub-handler==0.0.5'] _LOGGER = logging.getLogger(__name__) @@ -57,13 +58,13 @@ WINK_COMPONENTS = [ def setup(hass, config): """Setup the Wink component.""" import pywink + from pubnubsubhandler import PubNubSubscriptionHandler - user_agent = config[DOMAIN][CONF_USER_AGENT] + user_agent = config[DOMAIN].get(CONF_USER_AGENT) if user_agent: pywink.set_user_agent(user_agent) - from pubnub import Pubnub access_token = config[DOMAIN].get(CONF_ACCESS_TOKEN) if access_token: @@ -76,49 +77,62 @@ def setup(hass, config): pywink.set_wink_credentials(email, password, client_id, client_secret) - global SUBSCRIPTION_HANDLER - SUBSCRIPTION_HANDLER = Pubnub( - 'N/A', pywink.get_subscription_key(), ssl_on=True) - SUBSCRIPTION_HANDLER.set_heartbeat(120) + hass.data[DOMAIN] = {} + hass.data[DOMAIN]['entities'] = [] + hass.data[DOMAIN]['pubnub'] = PubNubSubscriptionHandler( + pywink.get_subscription_key(), + pywink.wink_api_fetch) + + def start_subscription(event): + """Start the pubnub subscription.""" + hass.data[DOMAIN]['pubnub'].subscribe() + hass.bus.listen(EVENT_HOMEASSISTANT_START, start_subscription) + + def stop_subscription(event): + """Stop the pubnub subscription.""" + hass.data[DOMAIN]['pubnub'].unsubscribe() + hass.bus.listen(EVENT_HOMEASSISTANT_STOP, stop_subscription) + + 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']: + entity.update_ha_state(True) + hass.services.register(DOMAIN, 'Refresh state from Wink', force_update) # Load components for the devices in Wink that we support for component in WINK_COMPONENTS: discovery.load_platform(hass, component, DOMAIN, {}, config) + return True class WinkDevice(Entity): """Representation a base Wink device.""" - def __init__(self, wink): + def __init__(self, wink, hass): """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, ssl_on=True) - 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) + hass.data[DOMAIN]['pubnub'].add_subscription( + self.wink.pubnub_channel, + self._pubnub_update) + hass.data[DOMAIN]['entities'].append(self) - def _pubnub_update(self, message, channel): + def _pubnub_update(self, message): try: - self.wink.pubnub_update(json.loads(message)) - self.update_ha_state() - except (AttributeError, KeyError): - error = "Pubnub returned invalid json for " + self.name - logging.getLogger(__name__).error(error) + if message is None: + _LOGGER.error("Error on pubnub update for " + self.name + + " pollin API for current state") + self.update_ha_state(True) + else: + self.wink.pubnub_update(message) + self.update_ha_state() + except (ValueError, KeyError, AttributeError): + _LOGGER.error("Error in pubnub JSON for " + self.name + + " pollin API for current state") self.update_ha_state(True) - def _pubnub_error(self, message): - _LOGGER.error("Error on pubnub update for " + self.wink.name()) - @property def unique_id(self): """Return the ID of this Wink device.""" diff --git a/requirements_all.txt b/requirements_all.txt index ad1a41b457b..1cbcf58c654 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -340,7 +340,7 @@ proliphix==0.4.1 psutil==5.0.0 # homeassistant.components.wink -pubnub==3.8.2 +pubnubsub-handler==0.0.5 # homeassistant.components.notify.pushbullet pushbullet.py==0.10.0 @@ -465,7 +465,7 @@ python-telegram-bot==5.2.0 python-twitch==1.3.0 # homeassistant.components.wink -python-wink==0.10.0 +python-wink==0.10.1 # homeassistant.components.keyboard # pyuserinput==0.1.11