Make Tradfri discoverable (#7128)

* Make Tradfri discoverable

* Fix lint errors

* Fix bugs and clean up calls to light_control

* Add more color util tests

* Add coap client to dockerfile
This commit is contained in:
Paulus Schoutsen 2017-04-16 14:37:39 -07:00 committed by GitHub
parent 75242e67a7
commit 951af6c76d
11 changed files with 188 additions and 46 deletions

View file

@ -97,6 +97,9 @@ omit =
homeassistant/components/*/thinkingcleaner.py
homeassistant/components/tradfri.py
homeassistant/components/*/tradfri.py
homeassistant/components/twilio.py
homeassistant/components/notify/twilio_sms.py
homeassistant/components/notify/twilio_call.py

View file

@ -8,6 +8,7 @@ MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
#ENV INSTALL_OPENZWAVE no
#ENV INSTALL_LIBCEC no
#ENV INSTALL_PHANTOMJS no
#ENV INSTALL_COAP_CLIENT no
VOLUME /config

View file

@ -180,7 +180,7 @@ class Configurator(object):
# field validation goes here?
callback(call.data.get(ATTR_FIELDS, {}))
self.hass.async_add_job(callback, call.data.get(ATTR_FIELDS, {}))
def _generate_unique_id(self):
"""Generate a unique configurator ID."""

View file

@ -28,11 +28,13 @@ SCAN_INTERVAL = timedelta(seconds=300)
SERVICE_NETGEAR = 'netgear_router'
SERVICE_WEMO = 'belkin_wemo'
SERVICE_HASS_IOS_APP = 'hass_ios'
SERVICE_IKEA_TRADFRI = 'ikea_tradfri'
SERVICE_HANDLERS = {
SERVICE_HASS_IOS_APP: ('ios', None),
SERVICE_NETGEAR: ('device_tracker', None),
SERVICE_WEMO: ('wemo', None),
SERVICE_IKEA_TRADFRI: ('tradfri', None),
'philips_hue': ('light', 'hue'),
'google_cast': ('media_player', 'cast'),
'panasonic_viera': ('media_player', 'panasonic_viera'),

View file

@ -1,52 +1,29 @@
"""Support for the IKEA Tradfri platform."""
import logging
import voluptuous as vol
import homeassistant.util.color as color_util
from homeassistant.components.tradfri import KEY_GATEWAY
from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_RGB_COLOR, Light,
PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR)
from homeassistant.const import CONF_API_KEY, CONF_HOST
import homeassistant.helpers.config_validation as cv
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light, ATTR_RGB_COLOR,
SUPPORT_RGB_COLOR, PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA)
from homeassistant.util import color as color_util
DEPENDENCIES = ['tradfri']
SUPPORTED_FEATURES = (SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR)
# https://github.com/ggravlingen/pytradfri
REQUIREMENTS = ['pytradfri==0.4']
_LOGGER = logging.getLogger(__name__)
# Validation of the user's configuration
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_API_KEY): cv.string,
})
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the IKEA Tradfri Light platform."""
import pytradfri
if discovery_info is None:
return
# Assign configuration variables.
host = config.get(CONF_HOST)
securitycode = config.get(CONF_API_KEY)
api = pytradfri.coap_cli.api_factory(host, securitycode)
gateway = pytradfri.gateway.Gateway(api)
gateway_id = discovery_info['gateway']
gateway = hass.data[KEY_GATEWAY][gateway_id]
devices = gateway.get_devices()
lights = [dev for dev in devices if dev.has_light_control]
_LOGGER.debug("IKEA Tradfri Hub | init | Initialization Process Complete")
add_devices(IKEATradfri(light) for light in lights)
_LOGGER.debug("IKEA Tradfri Hub | get_lights | All Lights Loaded")
add_devices(Tradfri(light) for light in lights)
class IKEATradfri(Light):
class Tradfri(Light):
"""The platform class required by hass."""
def __init__(self, light):
@ -57,8 +34,6 @@ class IKEATradfri(Light):
self._light_control = light.light_control
self._light_data = light.light_control.lights[0]
self._name = light.name
self._brightness = None
self._rgb_color = None
@property
@ -98,17 +73,17 @@ class IKEATradfri(Light):
for ATTR_RGB_COLOR, this also supports Philips Hue bulbs.
"""
if ATTR_BRIGHTNESS in kwargs:
self._light.light_control.set_dimmer(kwargs.get(ATTR_BRIGHTNESS))
self._light_control.set_dimmer(kwargs[ATTR_BRIGHTNESS])
else:
self._light_control.set_state(True)
if ATTR_RGB_COLOR in kwargs and self._light_data.hex_color is not None:
self._light.light_control.set_hex_color(
color_util.color_rgb_to_hex(*kwargs[ATTR_RGB_COLOR]))
else:
self._light.light_control.set_state(True)
def update(self):
"""Fetch new state data for this light."""
self._light.update()
self._brightness = self._light_data.dimmer
# Handle Hue lights paired with the gatway
if self._light_data.hex_color is not None:

View file

@ -0,0 +1,141 @@
"""
Support for Ikea Tradfri.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/ikea_tradfri/
"""
import asyncio
import json
import logging
import os
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.const import CONF_HOST, CONF_API_KEY
from homeassistant.loader import get_component
from homeassistant.components.discovery import SERVICE_IKEA_TRADFRI
DOMAIN = 'tradfri'
CONFIG_FILE = 'tradfri.conf'
KEY_CONFIG = 'tradfri_configuring'
KEY_GATEWAY = 'tradfri_gateway'
REQUIREMENTS = ['pytradfri==0.4']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Inclusive(CONF_HOST, 'gateway'): cv.string,
vol.Inclusive(CONF_API_KEY, 'gateway'): cv.string,
})
}, extra=vol.ALLOW_EXTRA)
_LOGGER = logging.getLogger(__name__)
def request_configuration(hass, config, host):
"""Request configuration steps from the user."""
configurator = get_component('configurator')
hass.data.setdefault(KEY_CONFIG, {})
instance = hass.data[KEY_CONFIG].get(host)
# Configuration already in progress
if instance:
return
@asyncio.coroutine
def configuration_callback(callback_data):
"""Called when config is submitted."""
res = yield from _setup_gateway(hass, config, host,
callback_data.get('key'))
if not res:
hass.async_add_job(configurator.notify_errors, instance,
"Unable to connect.")
return
def success():
"""Set up was successful."""
conf = _read_config(hass)
conf[host] = {'key': callback_data.get('key')}
_write_config(hass, conf)
hass.async_add_job(configurator.request_done, instance)
hass.async_add_job(success)
instance = configurator.request_config(
hass, "IKEA Trådfri", configuration_callback,
description='Please enter the security code written at the bottom of '
'your IKEA Trådfri Gateway.',
submit_caption="Confirm",
fields=[{'id': 'key', 'name': 'Security Code', 'type': 'password'}]
)
@asyncio.coroutine
def async_setup(hass, config):
"""Setup Tradfri."""
conf = config.get(DOMAIN, {})
host = conf.get(CONF_HOST)
key = conf.get(CONF_API_KEY)
@asyncio.coroutine
def gateway_discovered(service, info):
"""Called when a gateway is discovered."""
keys = yield from hass.async_add_job(_read_config, hass)
host = info['host']
if host in keys:
yield from _setup_gateway(hass, config, host, keys[host]['key'])
else:
hass.async_add_job(request_configuration, hass, config, host)
discovery.async_listen(hass, SERVICE_IKEA_TRADFRI, gateway_discovered)
if host is None:
return True
return (yield from _setup_gateway(hass, config, host, key))
@asyncio.coroutine
def _setup_gateway(hass, hass_config, host, key):
"""Create a gateway."""
from pytradfri import cli_api_factory, Gateway, RequestError
try:
api = cli_api_factory(host, key)
except RequestError:
return False
gateway = Gateway(api)
gateway_id = gateway.get_gateway_info().id
hass.data.setdefault(KEY_GATEWAY, {})
gateways = hass.data[KEY_GATEWAY]
# Check if already set up
if gateway_id in gateways:
return True
gateways[gateway_id] = gateway
hass.async_add_job(discovery.async_load_platform(
hass, 'light', DOMAIN, {'gateway': gateway_id}, hass_config))
return True
def _read_config(hass):
"""Read tradfri config."""
path = hass.config.path(CONFIG_FILE)
if not os.path.isfile(path):
return {}
with open(path) as f_handle:
# Guard against empty file
return json.loads(f_handle.read() or '{}')
def _write_config(hass, config):
"""Write tradfri config."""
data = json.dumps(config)
with open(hass.config.path(CONFIG_FILE), 'w', encoding='utf-8') as outfile:
outfile.write(data)

View file

@ -655,7 +655,7 @@ python-wink==1.2.3
# homeassistant.components.device_tracker.trackr
pytrackr==0.0.5
# homeassistant.components.light.tradfri
# homeassistant.components.tradfri
pytradfri==0.4
# homeassistant.components.device_tracker.unifi

View file

@ -175,8 +175,9 @@ class TestColorUtil(unittest.TestCase):
def test_color_rgb_to_hex(self):
"""Test color_rgb_to_hex."""
self.assertEqual('000000',
color_util.color_rgb_to_hex(0, 0, 0))
assert color_util.color_rgb_to_hex(255, 255, 255) == 'ffffff'
assert color_util.color_rgb_to_hex(0, 0, 0) == '000000'
assert color_util.color_rgb_to_hex(51, 153, 255) == '3399ff'
class ColorTemperatureMiredToKelvinTests(unittest.TestCase):

View file

@ -0,0 +1,14 @@
#!/bin/sh
# Installs a modified coap client with support for dtls for use with IKEA Tradfri
# Stop on errors
set -e
apt-get install -y --no-install-recommends git autoconf automake libtool
git clone --depth 1 --recursive -b dtls https://github.com/home-assistant/libcoap.git
cd libcoap
./autogen.sh
./configure --disable-documentation --disable-shared
make
make install

View file

@ -12,7 +12,7 @@ PACKAGES=(
apt-get install -y --no-install-recommends ${PACKAGES[@]}
# Clone the latest code from GitHub
git clone https://github.com/openalpr/openalpr.git /usr/local/src/openalpr
git clone --depth 1 https://github.com/openalpr/openalpr.git /usr/local/src/openalpr
# Setup the build directory
cd /usr/local/src/openalpr/src

View file

@ -10,6 +10,7 @@ INSTALL_FFMPEG="${INSTALL_FFMPEG:-yes}"
INSTALL_OPENZWAVE="${INSTALL_OPENZWAVE:-yes}"
INSTALL_LIBCEC="${INSTALL_LIBCEC:-yes}"
INSTALL_PHANTOMJS="${INSTALL_PHANTOMJS:-yes}"
INSTALL_COAP_CLIENT="${INSTALL_COAP_CLIENT:-yes}"
# Required debian packages for running hass or components
PACKAGES=(
@ -62,6 +63,10 @@ if [ "$INSTALL_PHANTOMJS" == "yes" ]; then
virtualization/Docker/scripts/phantomjs
fi
if [ "$INSTALL_COAP_CLIENT" == "yes" ]; then
virtualization/Docker/scripts/coap_client
fi
# Remove packages
apt-get remove -y --purge ${PACKAGES_DEV[@]}
apt-get -y --purge autoremove