From 2992cd35be85c482cdadf6f49721c3e3d20478e9 Mon Sep 17 00:00:00 2001
From: Matthew Garrett <mjg59@coreos.com>
Date: Sat, 21 Jan 2017 14:14:08 -0800
Subject: [PATCH] Add support for Leviton Decora Bluetooth dimmer switches
 (#5434)

* Add support for Leviton Decora Bluetooth dimmer switches

Add support for the Decora Bluetooth smart dimmer switches from Leviton.

* Update decora.py
---
 .coveragerc                              |   1 +
 homeassistant/components/light/decora.py | 124 +++++++++++++++++++++++
 requirements_all.txt                     |   3 +
 script/gen_requirements_all.py           |   3 +-
 4 files changed, 130 insertions(+), 1 deletion(-)
 create mode 100644 homeassistant/components/light/decora.py

diff --git a/.coveragerc b/.coveragerc
index ea0530aa8f7..47f602072c0 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -191,6 +191,7 @@ omit =
     homeassistant/components/keyboard_remote.py
     homeassistant/components/light/avion.py
     homeassistant/components/light/blinksticklight.py
+    homeassistant/components/light/decora.py
     homeassistant/components/light/flux_led.py
     homeassistant/components/light/hue.py
     homeassistant/components/light/hyperion.py
diff --git a/homeassistant/components/light/decora.py b/homeassistant/components/light/decora.py
new file mode 100644
index 00000000000..eaae90f486e
--- /dev/null
+++ b/homeassistant/components/light/decora.py
@@ -0,0 +1,124 @@
+"""
+Support for Decora dimmers.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/light.decora/
+"""
+import logging
+
+import voluptuous as vol
+
+from homeassistant.const import CONF_API_KEY, CONF_DEVICES, CONF_NAME
+from homeassistant.components.light import (
+    ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light,
+    PLATFORM_SCHEMA)
+import homeassistant.helpers.config_validation as cv
+
+REQUIREMENTS = ['decora==0.3']
+
+_LOGGER = logging.getLogger(__name__)
+
+SUPPORT_DECORA_LED = (SUPPORT_BRIGHTNESS)
+
+DEVICE_SCHEMA = vol.Schema({
+    vol.Optional(CONF_NAME): cv.string,
+    vol.Required(CONF_API_KEY): cv.string,
+})
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+    vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA},
+})
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Set up an Decora switch."""
+    lights = []
+    for address, device_config in config[CONF_DEVICES].items():
+        device = {}
+        device['name'] = device_config[CONF_NAME]
+        device['key'] = device_config[CONF_API_KEY]
+        device['address'] = address
+        light = DecoraLight(device)
+        if light.is_valid:
+            lights.append(light)
+
+    add_devices(lights)
+
+
+class DecoraLight(Light):
+    """Representation of an Decora light."""
+
+    def __init__(self, device):
+        """Initialize the light."""
+        # pylint: disable=import-error
+        import decora
+
+        self._name = device['name']
+        self._address = device['address']
+        self._key = device["key"]
+        self._switch = decora.decora(self._address, self._key)
+        self._switch.connect()
+        self._state = self._switch.get_on()
+        self._brightness = self._switch.get_brightness()
+        self.is_valid = True
+
+    @property
+    def unique_id(self):
+        """Return the ID of this light."""
+        return "{}.{}".format(self.__class__, self._address)
+
+    @property
+    def name(self):
+        """Return the name of the device if any."""
+        return self._name
+
+    @property
+    def is_on(self):
+        """Return true if device is on."""
+        return self._state
+
+    @property
+    def brightness(self):
+        """Return the brightness of this light between 0..255."""
+        return self._brightness
+
+    @property
+    def supported_features(self):
+        """Flag supported features."""
+        return SUPPORT_DECORA_LED
+
+    @property
+    def should_poll(self):
+        """We can read the device state, so poll."""
+        return True
+
+    @property
+    def assumed_state(self):
+        """We can read the actual state."""
+        return False
+
+    def set_state(self, brightness):
+        """Set the state of this lamp to the provided brightness."""
+        self._switch.set_brightness(brightness)
+        self._brightness = brightness
+        return True
+
+    def turn_on(self, **kwargs):
+        """Turn the specified or all lights on."""
+        brightness = kwargs.get(ATTR_BRIGHTNESS)
+
+        self._switch.on()
+        if brightness is not None:
+            self.set_state(brightness)
+
+        self._state = True
+
+    def turn_off(self, **kwargs):
+        """Turn the specified or all lights off."""
+        self._switch.off()
+        self._state = False
+
+    def update(self):
+        """Synchronise internal state with the actual light state."""
+        self._brightness = self._switch.get_brightness()
+        self._state = self._switch.get_on()
diff --git a/requirements_all.txt b/requirements_all.txt
index 1b92e6b81c4..68326dbd1b4 100755
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -87,6 +87,9 @@ colorlog>2.1,<3
 # homeassistant.components.binary_sensor.concord232
 concord232==0.14
 
+# homeassistant.components.light.decora
+# decora==0.3
+
 # homeassistant.components.media_player.denonavr
 denonavr==0.3.1
 
diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py
index e23c8d09fea..f5b0a271d84 100755
--- a/script/gen_requirements_all.py
+++ b/script/gen_requirements_all.py
@@ -20,7 +20,8 @@ COMMENT_REQUIREMENTS = (
     'evdev',
     'pycups',
     'python-eq3bt',
-    'avion'
+    'avion',
+    'decora'
 )
 
 IGNORE_PACKAGES = (
-- 
GitLab