""" Support for Wink hubs. 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 from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['python-wink==0.10.0', 'pubnub==3.8.2'] _LOGGER = logging.getLogger(__name__) CHANNELS = [] DOMAIN = 'wink' SUBSCRIPTION_HANDLER = None CONF_CLIENT_ID = 'client_id' CONF_CLIENT_SECRET = 'client_secret' CONF_USER_AGENT = 'user_agent' CONF_OATH = 'oath' CONF_DEFINED_BOTH_MSG = 'Remove access token to use oath2.' CONF_MISSING_OATH_MSG = 'Missing oath2 credentials.' CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Inclusive(CONF_EMAIL, CONF_OATH, msg=CONF_MISSING_OATH_MSG): cv.string, vol.Inclusive(CONF_PASSWORD, CONF_OATH, msg=CONF_MISSING_OATH_MSG): cv.string, vol.Inclusive(CONF_CLIENT_ID, CONF_OATH, msg=CONF_MISSING_OATH_MSG): cv.string, vol.Inclusive(CONF_CLIENT_SECRET, CONF_OATH, msg=CONF_MISSING_OATH_MSG): cv.string, vol.Exclusive(CONF_EMAIL, CONF_OATH, msg=CONF_DEFINED_BOTH_MSG): cv.string, vol.Exclusive(CONF_ACCESS_TOKEN, CONF_OATH, msg=CONF_DEFINED_BOTH_MSG): cv.string, vol.Optional(CONF_USER_AGENT, default=None): cv.string }) }, extra=vol.ALLOW_EXTRA) WINK_COMPONENTS = [ 'binary_sensor', 'sensor', 'light', 'switch', 'lock', 'cover', 'climate' ] def setup(hass, config): """Setup the Wink component.""" import pywink user_agent = config[DOMAIN][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: pywink.set_bearer_token(access_token) else: email = config[DOMAIN][CONF_EMAIL] password = config[DOMAIN][CONF_PASSWORD] client_id = config[DOMAIN]['client_id'] client_secret = config[DOMAIN]['client_secret'] 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) # 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): """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) def _pubnub_update(self, message, channel): 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) 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.""" return '{}.{}'.format(self.__class__, self.wink.device_id()) @property def name(self): """Return the name of the device.""" return self.wink.name() @property def available(self): """True if connection == True.""" return self.wink.available 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.""" if self._battery: return { ATTR_BATTERY_LEVEL: self._battery_level, } @property def _battery_level(self): """Return the battery level.""" if self.wink.battery_level is not None: return self.wink.battery_level * 100