diff --git a/.coveragerc b/.coveragerc index c5729df3521..1aa034ca577 100644 --- a/.coveragerc +++ b/.coveragerc @@ -305,6 +305,7 @@ omit = homeassistant/components/light/blinkt.py homeassistant/components/light/blinksticklight.py homeassistant/components/light/decora.py + homeassistant/components/light/decora_wifi.py homeassistant/components/light/flux_led.py homeassistant/components/light/hue.py homeassistant/components/light/hyperion.py diff --git a/homeassistant/components/light/decora_wifi.py b/homeassistant/components/light/decora_wifi.py new file mode 100644 index 00000000000..3a4b892cbfe --- /dev/null +++ b/homeassistant/components/light/decora_wifi.py @@ -0,0 +1,147 @@ +""" +Interfaces with the myLeviton API for Decora Smart WiFi products. + +See: +http://www.leviton.com/en/products/lighting-controls/decora-smart-with-wifi + +Uses Leviton's cloud services API for cloud-to-cloud integration. + +""" + +import logging + +import voluptuous as vol + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, ATTR_TRANSITION, Light, + PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, SUPPORT_TRANSITION) +from homeassistant.const import ( + CONF_USERNAME, CONF_PASSWORD, + EVENT_HOMEASSISTANT_STOP) +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['decora_wifi==1.3'] + +_LOGGER = logging.getLogger(__name__) + +# Validation of the user's configuration +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, +}) + +NOTIFICATION_ID = 'leviton_notification' +NOTIFICATION_TITLE = 'myLeviton Decora Setup' + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Decora WiFi platform.""" + # pylint: disable=import-error + from decora_wifi import DecoraWiFiSession + from decora_wifi.models.person import Person + from decora_wifi.models.residential_account import ResidentialAccount + + email = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + session = DecoraWiFiSession() + + try: + success = session.login(email, password) + + # If login failed, notify user. + if success is None: + msg = 'Failed to log into myLeviton Services. Check credentials.' + _LOGGER.error(msg) + hass.components.persistent_notification.create( + msg, title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) + return False + + # Gather all the available devices... + perms = session.user.get_residential_permissions() + all_switches = [] + for permission in perms: + acct = ResidentialAccount(session, permission.residentialAccountId) + for residence in acct.get_residences(): + for switch in residence.get_iot_switches(): + all_switches.append(switch) + + add_devices(DecoraWifiLight(sw) for sw in all_switches) + except ValueError: + _LOGGER.error('Failed to communicate with myLeviton Service.') + + # Listen for the stop event and log out. + def logout(event): + """Log out...""" + try: + if session is not None: + Person.logout(session) + except ValueError: + _LOGGER.error('Failed to log out of myLeviton Service.') + + hass.bus.listen(EVENT_HOMEASSISTANT_STOP, logout) + + +class DecoraWifiLight(Light): + """Representation of a Decora WiFi switch.""" + + def __init__(self, switch): + """Initialize the switch.""" + self._switch = switch + + @property + def supported_features(self): + """Return supported features.""" + if self._switch.canSetLevel: + return SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION + else: + return 0 + + @property + def name(self): + """Return the display name of this switch.""" + return self._switch.name + + @property + def brightness(self): + """Return the brightness of the dimmer switch.""" + return int(self._switch.brightness * 255 / 100) + + @property + def is_on(self): + """Return true if switch is on.""" + return self._switch.power == 'ON' + + def turn_on(self, **kwargs): + """Instruct the switch to turn on & adjust brightness.""" + attribs = {'power': 'ON'} + + if ATTR_BRIGHTNESS in kwargs: + min_level = self._switch.get('minLevel', 0) + max_level = self._switch.get('maxLevel', 100) + brightness = int(kwargs[ATTR_BRIGHTNESS] * max_level / 255) + brightness = max(brightness, min_level) + attribs['brightness'] = brightness + + if ATTR_TRANSITION in kwargs: + transition = int(kwargs[ATTR_TRANSITION]) + attribs['fadeOnTime'] = attribs['fadeOffTime'] = transition + + try: + self._switch.update_attributes(attribs) + except ValueError: + _LOGGER.error('Failed to turn on myLeviton switch.') + + def turn_off(self, **kwargs): + """Instruct the switch to turn off.""" + attribs = {'power': 'OFF'} + try: + self._switch.update_attributes(attribs) + except ValueError: + _LOGGER.error('Failed to turn off myLeviton switch.') + + def update(self): + """Fetch new state data for this switch.""" + try: + self._switch.refresh() + except ValueError: + _LOGGER.error('Failed to update myLeviton switch data.') diff --git a/requirements_all.txt b/requirements_all.txt index a0e06926e76..b2a51a60338 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -159,6 +159,9 @@ datapoint==0.4.3 # homeassistant.components.light.decora # decora==0.6 +# homeassistant.components.light.decora_wifi +# decora_wifi==1.3 + # homeassistant.components.media_player.denonavr denonavr==0.5.2