mirror of
https://github.com/home-assistant/core.git
synced 2025-04-29 03:37:51 +00:00
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:
parent
75242e67a7
commit
951af6c76d
@ -97,6 +97,9 @@ omit =
|
|||||||
|
|
||||||
homeassistant/components/*/thinkingcleaner.py
|
homeassistant/components/*/thinkingcleaner.py
|
||||||
|
|
||||||
|
homeassistant/components/tradfri.py
|
||||||
|
homeassistant/components/*/tradfri.py
|
||||||
|
|
||||||
homeassistant/components/twilio.py
|
homeassistant/components/twilio.py
|
||||||
homeassistant/components/notify/twilio_sms.py
|
homeassistant/components/notify/twilio_sms.py
|
||||||
homeassistant/components/notify/twilio_call.py
|
homeassistant/components/notify/twilio_call.py
|
||||||
|
@ -8,6 +8,7 @@ MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
|
|||||||
#ENV INSTALL_OPENZWAVE no
|
#ENV INSTALL_OPENZWAVE no
|
||||||
#ENV INSTALL_LIBCEC no
|
#ENV INSTALL_LIBCEC no
|
||||||
#ENV INSTALL_PHANTOMJS no
|
#ENV INSTALL_PHANTOMJS no
|
||||||
|
#ENV INSTALL_COAP_CLIENT no
|
||||||
|
|
||||||
VOLUME /config
|
VOLUME /config
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ class Configurator(object):
|
|||||||
|
|
||||||
# field validation goes here?
|
# 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):
|
def _generate_unique_id(self):
|
||||||
"""Generate a unique configurator ID."""
|
"""Generate a unique configurator ID."""
|
||||||
|
@ -28,11 +28,13 @@ SCAN_INTERVAL = timedelta(seconds=300)
|
|||||||
SERVICE_NETGEAR = 'netgear_router'
|
SERVICE_NETGEAR = 'netgear_router'
|
||||||
SERVICE_WEMO = 'belkin_wemo'
|
SERVICE_WEMO = 'belkin_wemo'
|
||||||
SERVICE_HASS_IOS_APP = 'hass_ios'
|
SERVICE_HASS_IOS_APP = 'hass_ios'
|
||||||
|
SERVICE_IKEA_TRADFRI = 'ikea_tradfri'
|
||||||
|
|
||||||
SERVICE_HANDLERS = {
|
SERVICE_HANDLERS = {
|
||||||
SERVICE_HASS_IOS_APP: ('ios', None),
|
SERVICE_HASS_IOS_APP: ('ios', None),
|
||||||
SERVICE_NETGEAR: ('device_tracker', None),
|
SERVICE_NETGEAR: ('device_tracker', None),
|
||||||
SERVICE_WEMO: ('wemo', None),
|
SERVICE_WEMO: ('wemo', None),
|
||||||
|
SERVICE_IKEA_TRADFRI: ('tradfri', None),
|
||||||
'philips_hue': ('light', 'hue'),
|
'philips_hue': ('light', 'hue'),
|
||||||
'google_cast': ('media_player', 'cast'),
|
'google_cast': ('media_player', 'cast'),
|
||||||
'panasonic_viera': ('media_player', 'panasonic_viera'),
|
'panasonic_viera': ('media_player', 'panasonic_viera'),
|
||||||
|
@ -1,52 +1,29 @@
|
|||||||
"""Support for the IKEA Tradfri platform."""
|
"""Support for the IKEA Tradfri platform."""
|
||||||
|
|
||||||
import logging
|
from homeassistant.components.tradfri import KEY_GATEWAY
|
||||||
|
|
||||||
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
import homeassistant.util.color as color_util
|
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS, ATTR_RGB_COLOR, Light,
|
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light, ATTR_RGB_COLOR,
|
||||||
PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR)
|
SUPPORT_RGB_COLOR, PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA)
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_HOST
|
from homeassistant.util import color as color_util
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
|
DEPENDENCIES = ['tradfri']
|
||||||
SUPPORTED_FEATURES = (SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR)
|
SUPPORTED_FEATURES = (SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR)
|
||||||
|
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA
|
||||||
# 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,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Setup the IKEA Tradfri Light platform."""
|
"""Setup the IKEA Tradfri Light platform."""
|
||||||
import pytradfri
|
if discovery_info is None:
|
||||||
|
return
|
||||||
|
|
||||||
# Assign configuration variables.
|
gateway_id = discovery_info['gateway']
|
||||||
host = config.get(CONF_HOST)
|
gateway = hass.data[KEY_GATEWAY][gateway_id]
|
||||||
securitycode = config.get(CONF_API_KEY)
|
|
||||||
|
|
||||||
api = pytradfri.coap_cli.api_factory(host, securitycode)
|
|
||||||
|
|
||||||
gateway = pytradfri.gateway.Gateway(api)
|
|
||||||
devices = gateway.get_devices()
|
devices = gateway.get_devices()
|
||||||
lights = [dev for dev in devices if dev.has_light_control]
|
lights = [dev for dev in devices if dev.has_light_control]
|
||||||
|
add_devices(Tradfri(light) for light in lights)
|
||||||
_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")
|
|
||||||
|
|
||||||
|
|
||||||
class IKEATradfri(Light):
|
class Tradfri(Light):
|
||||||
"""The platform class required by hass."""
|
"""The platform class required by hass."""
|
||||||
|
|
||||||
def __init__(self, light):
|
def __init__(self, light):
|
||||||
@ -57,8 +34,6 @@ class IKEATradfri(Light):
|
|||||||
self._light_control = light.light_control
|
self._light_control = light.light_control
|
||||||
self._light_data = light.light_control.lights[0]
|
self._light_data = light.light_control.lights[0]
|
||||||
self._name = light.name
|
self._name = light.name
|
||||||
|
|
||||||
self._brightness = None
|
|
||||||
self._rgb_color = None
|
self._rgb_color = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -98,17 +73,17 @@ class IKEATradfri(Light):
|
|||||||
for ATTR_RGB_COLOR, this also supports Philips Hue bulbs.
|
for ATTR_RGB_COLOR, this also supports Philips Hue bulbs.
|
||||||
"""
|
"""
|
||||||
if ATTR_BRIGHTNESS in kwargs:
|
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:
|
if ATTR_RGB_COLOR in kwargs and self._light_data.hex_color is not None:
|
||||||
self._light.light_control.set_hex_color(
|
self._light.light_control.set_hex_color(
|
||||||
color_util.color_rgb_to_hex(*kwargs[ATTR_RGB_COLOR]))
|
color_util.color_rgb_to_hex(*kwargs[ATTR_RGB_COLOR]))
|
||||||
else:
|
|
||||||
self._light.light_control.set_state(True)
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Fetch new state data for this light."""
|
"""Fetch new state data for this light."""
|
||||||
self._light.update()
|
self._light.update()
|
||||||
self._brightness = self._light_data.dimmer
|
|
||||||
|
|
||||||
# Handle Hue lights paired with the gatway
|
# Handle Hue lights paired with the gatway
|
||||||
if self._light_data.hex_color is not None:
|
if self._light_data.hex_color is not None:
|
||||||
|
141
homeassistant/components/tradfri.py
Normal file
141
homeassistant/components/tradfri.py
Normal 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)
|
@ -655,7 +655,7 @@ python-wink==1.2.3
|
|||||||
# homeassistant.components.device_tracker.trackr
|
# homeassistant.components.device_tracker.trackr
|
||||||
pytrackr==0.0.5
|
pytrackr==0.0.5
|
||||||
|
|
||||||
# homeassistant.components.light.tradfri
|
# homeassistant.components.tradfri
|
||||||
pytradfri==0.4
|
pytradfri==0.4
|
||||||
|
|
||||||
# homeassistant.components.device_tracker.unifi
|
# homeassistant.components.device_tracker.unifi
|
||||||
|
@ -175,8 +175,9 @@ class TestColorUtil(unittest.TestCase):
|
|||||||
|
|
||||||
def test_color_rgb_to_hex(self):
|
def test_color_rgb_to_hex(self):
|
||||||
"""Test color_rgb_to_hex."""
|
"""Test color_rgb_to_hex."""
|
||||||
self.assertEqual('000000',
|
assert color_util.color_rgb_to_hex(255, 255, 255) == 'ffffff'
|
||||||
color_util.color_rgb_to_hex(0, 0, 0))
|
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):
|
class ColorTemperatureMiredToKelvinTests(unittest.TestCase):
|
||||||
|
14
virtualization/Docker/scripts/coap_client
Executable file
14
virtualization/Docker/scripts/coap_client
Executable 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
|
@ -12,7 +12,7 @@ PACKAGES=(
|
|||||||
apt-get install -y --no-install-recommends ${PACKAGES[@]}
|
apt-get install -y --no-install-recommends ${PACKAGES[@]}
|
||||||
|
|
||||||
# Clone the latest code from GitHub
|
# 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
|
# Setup the build directory
|
||||||
cd /usr/local/src/openalpr/src
|
cd /usr/local/src/openalpr/src
|
||||||
|
@ -10,6 +10,7 @@ INSTALL_FFMPEG="${INSTALL_FFMPEG:-yes}"
|
|||||||
INSTALL_OPENZWAVE="${INSTALL_OPENZWAVE:-yes}"
|
INSTALL_OPENZWAVE="${INSTALL_OPENZWAVE:-yes}"
|
||||||
INSTALL_LIBCEC="${INSTALL_LIBCEC:-yes}"
|
INSTALL_LIBCEC="${INSTALL_LIBCEC:-yes}"
|
||||||
INSTALL_PHANTOMJS="${INSTALL_PHANTOMJS:-yes}"
|
INSTALL_PHANTOMJS="${INSTALL_PHANTOMJS:-yes}"
|
||||||
|
INSTALL_COAP_CLIENT="${INSTALL_COAP_CLIENT:-yes}"
|
||||||
|
|
||||||
# Required debian packages for running hass or components
|
# Required debian packages for running hass or components
|
||||||
PACKAGES=(
|
PACKAGES=(
|
||||||
@ -62,6 +63,10 @@ if [ "$INSTALL_PHANTOMJS" == "yes" ]; then
|
|||||||
virtualization/Docker/scripts/phantomjs
|
virtualization/Docker/scripts/phantomjs
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$INSTALL_COAP_CLIENT" == "yes" ]; then
|
||||||
|
virtualization/Docker/scripts/coap_client
|
||||||
|
fi
|
||||||
|
|
||||||
# Remove packages
|
# Remove packages
|
||||||
apt-get remove -y --purge ${PACKAGES_DEV[@]}
|
apt-get remove -y --purge ${PACKAGES_DEV[@]}
|
||||||
apt-get -y --purge autoremove
|
apt-get -y --purge autoremove
|
||||||
|
Loading…
x
Reference in New Issue
Block a user