Add support for controlling homekit lights and switches (#13346)
* Add support for controlling homekit lights and switches This adds support for controlling lights and switches that expose a HomeKit control interface, avoiding the requirement to implement protocol-specific components. * Comment out the homekit requirement This needs to build native code, so leave it commented for now * Review updates * Make HomeKit auto-discovery optional Add an "enable" argument to the discovery component and add a list of optional devices types (currently just HomeKit) to discover * Further review comments * Update requirements_all.txt * Fix houndci complaints * Further review updates * Final review fixup * Lint fixups * Fix discovery tests * Further review updates
This commit is contained in:
parent
60508f7215
commit
ac2298189e
8 changed files with 454 additions and 2 deletions
|
@ -109,6 +109,9 @@ omit =
|
||||||
homeassistant/components/hive.py
|
homeassistant/components/hive.py
|
||||||
homeassistant/components/*/hive.py
|
homeassistant/components/*/hive.py
|
||||||
|
|
||||||
|
homeassistant/components/homekit_controller/__init__.py
|
||||||
|
homeassistant/components/*/homekit_controller.py
|
||||||
|
|
||||||
homeassistant/components/homematic/__init__.py
|
homeassistant/components/homematic/__init__.py
|
||||||
homeassistant/components/*/homematic.py
|
homeassistant/components/*/homematic.py
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ SERVICE_HUE = 'philips_hue'
|
||||||
SERVICE_DECONZ = 'deconz'
|
SERVICE_DECONZ = 'deconz'
|
||||||
SERVICE_DAIKIN = 'daikin'
|
SERVICE_DAIKIN = 'daikin'
|
||||||
SERVICE_SAMSUNG_PRINTER = 'samsung_printer'
|
SERVICE_SAMSUNG_PRINTER = 'samsung_printer'
|
||||||
|
SERVICE_HOMEKIT = 'homekit'
|
||||||
|
|
||||||
CONFIG_ENTRY_HANDLERS = {
|
CONFIG_ENTRY_HANDLERS = {
|
||||||
SERVICE_HUE: 'hue',
|
SERVICE_HUE: 'hue',
|
||||||
|
@ -79,13 +80,20 @@ SERVICE_HANDLERS = {
|
||||||
'songpal': ('media_player', 'songpal'),
|
'songpal': ('media_player', 'songpal'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OPTIONAL_SERVICE_HANDLERS = {
|
||||||
|
SERVICE_HOMEKIT: ('homekit_controller', None),
|
||||||
|
}
|
||||||
|
|
||||||
CONF_IGNORE = 'ignore'
|
CONF_IGNORE = 'ignore'
|
||||||
|
CONF_ENABLE = 'enable'
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
vol.Required(DOMAIN): vol.Schema({
|
vol.Required(DOMAIN): vol.Schema({
|
||||||
vol.Optional(CONF_IGNORE, default=[]):
|
vol.Optional(CONF_IGNORE, default=[]):
|
||||||
vol.All(cv.ensure_list, [
|
vol.All(cv.ensure_list, [
|
||||||
vol.In(list(CONFIG_ENTRY_HANDLERS) + list(SERVICE_HANDLERS))])
|
vol.In(list(CONFIG_ENTRY_HANDLERS) + list(SERVICE_HANDLERS))]),
|
||||||
|
vol.Optional(CONF_ENABLE, default=[]):
|
||||||
|
vol.All(cv.ensure_list, [vol.In(OPTIONAL_SERVICE_HANDLERS)])
|
||||||
}),
|
}),
|
||||||
}, extra=vol.ALLOW_EXTRA)
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
@ -104,6 +112,9 @@ async def async_setup(hass, config):
|
||||||
# Platforms ignore by config
|
# Platforms ignore by config
|
||||||
ignored_platforms = config[DOMAIN][CONF_IGNORE]
|
ignored_platforms = config[DOMAIN][CONF_IGNORE]
|
||||||
|
|
||||||
|
# Optional platforms enabled by config
|
||||||
|
enabled_platforms = config[DOMAIN][CONF_ENABLE]
|
||||||
|
|
||||||
async def new_service_found(service, info):
|
async def new_service_found(service, info):
|
||||||
"""Handle a new service if one is found."""
|
"""Handle a new service if one is found."""
|
||||||
if service in ignored_platforms:
|
if service in ignored_platforms:
|
||||||
|
@ -126,6 +137,9 @@ async def async_setup(hass, config):
|
||||||
|
|
||||||
comp_plat = SERVICE_HANDLERS.get(service)
|
comp_plat = SERVICE_HANDLERS.get(service)
|
||||||
|
|
||||||
|
if not comp_plat and service in enabled_platforms:
|
||||||
|
comp_plat = OPTIONAL_SERVICE_HANDLERS[service]
|
||||||
|
|
||||||
# We do not know how to handle this service.
|
# We do not know how to handle this service.
|
||||||
if not comp_plat:
|
if not comp_plat:
|
||||||
logger.info("Unknown service discovered: %s %s", service, info)
|
logger.info("Unknown service discovered: %s %s", service, info)
|
||||||
|
|
228
homeassistant/components/homekit_controller/__init__.py
Normal file
228
homeassistant/components/homekit_controller/__init__.py
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
"""
|
||||||
|
Support for Homekit device discovery.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/homekit_controller/
|
||||||
|
"""
|
||||||
|
import http
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from homeassistant.components.discovery import SERVICE_HOMEKIT
|
||||||
|
from homeassistant.helpers import discovery
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
|
REQUIREMENTS = ['homekit==0.5']
|
||||||
|
|
||||||
|
DOMAIN = 'homekit_controller'
|
||||||
|
HOMEKIT_DIR = '.homekit'
|
||||||
|
|
||||||
|
# Mapping from Homekit type to component.
|
||||||
|
HOMEKIT_ACCESSORY_DISPATCH = {
|
||||||
|
'lightbulb': 'light',
|
||||||
|
'outlet': 'switch',
|
||||||
|
}
|
||||||
|
|
||||||
|
KNOWN_ACCESSORIES = "{}-accessories".format(DOMAIN)
|
||||||
|
KNOWN_DEVICES = "{}-devices".format(DOMAIN)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def homekit_http_send(self, message_body=None):
|
||||||
|
r"""Send the currently buffered request and clear the buffer.
|
||||||
|
|
||||||
|
Appends an extra \r\n to the buffer.
|
||||||
|
A message_body may be specified, to be appended to the request.
|
||||||
|
"""
|
||||||
|
self._buffer.extend((b"", b""))
|
||||||
|
msg = b"\r\n".join(self._buffer)
|
||||||
|
del self._buffer[:]
|
||||||
|
|
||||||
|
if message_body is not None:
|
||||||
|
msg = msg + message_body
|
||||||
|
|
||||||
|
self.send(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def get_serial(accessory):
|
||||||
|
"""Obtain the serial number of a HomeKit device."""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import homekit
|
||||||
|
for service in accessory['services']:
|
||||||
|
if homekit.ServicesTypes.get_short(service['type']) != \
|
||||||
|
'accessory-information':
|
||||||
|
continue
|
||||||
|
for characteristic in service['characteristics']:
|
||||||
|
ctype = homekit.CharacteristicsTypes.get_short(
|
||||||
|
characteristic['type'])
|
||||||
|
if ctype != 'serial-number':
|
||||||
|
continue
|
||||||
|
return characteristic['value']
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class HKDevice():
|
||||||
|
"""HomeKit device."""
|
||||||
|
|
||||||
|
def __init__(self, hass, host, port, model, hkid, config_num, config):
|
||||||
|
"""Initialise a generic HomeKit device."""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import homekit
|
||||||
|
|
||||||
|
_LOGGER.info("Setting up Homekit device %s", model)
|
||||||
|
self.hass = hass
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.model = model
|
||||||
|
self.hkid = hkid
|
||||||
|
self.config_num = config_num
|
||||||
|
self.config = config
|
||||||
|
self.configurator = hass.components.configurator
|
||||||
|
|
||||||
|
data_dir = os.path.join(hass.config.path(), HOMEKIT_DIR)
|
||||||
|
if not os.path.isdir(data_dir):
|
||||||
|
os.mkdir(data_dir)
|
||||||
|
|
||||||
|
self.pairing_file = os.path.join(data_dir, 'hk-{}'.format(hkid))
|
||||||
|
self.pairing_data = homekit.load_pairing(self.pairing_file)
|
||||||
|
|
||||||
|
# Monkey patch httpclient for increased compatibility
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
http.client.HTTPConnection._send_output = homekit_http_send
|
||||||
|
|
||||||
|
self.conn = http.client.HTTPConnection(self.host, port=self.port)
|
||||||
|
if self.pairing_data is not None:
|
||||||
|
self.accessory_setup()
|
||||||
|
else:
|
||||||
|
self.configure()
|
||||||
|
|
||||||
|
def accessory_setup(self):
|
||||||
|
"""Handle setup of a HomeKit accessory."""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import homekit
|
||||||
|
self.controllerkey, self.accessorykey = \
|
||||||
|
homekit.get_session_keys(self.conn, self.pairing_data)
|
||||||
|
self.securecon = homekit.SecureHttp(self.conn.sock,
|
||||||
|
self.accessorykey,
|
||||||
|
self.controllerkey)
|
||||||
|
response = self.securecon.get('/accessories')
|
||||||
|
data = json.loads(response.read().decode())
|
||||||
|
for accessory in data['accessories']:
|
||||||
|
serial = get_serial(accessory)
|
||||||
|
if serial in self.hass.data[KNOWN_ACCESSORIES]:
|
||||||
|
continue
|
||||||
|
self.hass.data[KNOWN_ACCESSORIES][serial] = self
|
||||||
|
aid = accessory['aid']
|
||||||
|
for service in accessory['services']:
|
||||||
|
service_info = {'serial': serial,
|
||||||
|
'aid': aid,
|
||||||
|
'iid': service['iid']}
|
||||||
|
devtype = homekit.ServicesTypes.get_short(service['type'])
|
||||||
|
_LOGGER.debug("Found %s", devtype)
|
||||||
|
component = HOMEKIT_ACCESSORY_DISPATCH.get(devtype, None)
|
||||||
|
if component is not None:
|
||||||
|
discovery.load_platform(self.hass, component, DOMAIN,
|
||||||
|
service_info, self.config)
|
||||||
|
|
||||||
|
def device_config_callback(self, callback_data):
|
||||||
|
"""Handle initial pairing."""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import homekit
|
||||||
|
pairing_id = str(uuid.uuid4())
|
||||||
|
code = callback_data.get('code').strip()
|
||||||
|
self.pairing_data = homekit.perform_pair_setup(
|
||||||
|
self.conn, code, pairing_id)
|
||||||
|
if self.pairing_data is not None:
|
||||||
|
homekit.save_pairing(self.pairing_file, self.pairing_data)
|
||||||
|
self.accessory_setup()
|
||||||
|
else:
|
||||||
|
error_msg = "Unable to pair, please try again"
|
||||||
|
_configurator = self.hass.data[DOMAIN+self.hkid]
|
||||||
|
self.configurator.notify_errors(_configurator, error_msg)
|
||||||
|
|
||||||
|
def configure(self):
|
||||||
|
"""Obtain the pairing code for a HomeKit device."""
|
||||||
|
description = "Please enter the HomeKit code for your {}".format(
|
||||||
|
self.model)
|
||||||
|
self.hass.data[DOMAIN+self.hkid] = \
|
||||||
|
self.configurator.request_config(self.model,
|
||||||
|
self.device_config_callback,
|
||||||
|
description=description,
|
||||||
|
submit_caption="submit",
|
||||||
|
fields=[{'id': 'code',
|
||||||
|
'name': 'HomeKit code',
|
||||||
|
'type': 'string'}])
|
||||||
|
|
||||||
|
|
||||||
|
class HomeKitEntity(Entity):
|
||||||
|
"""Representation of a Home Assistant HomeKit device."""
|
||||||
|
|
||||||
|
def __init__(self, accessory, devinfo):
|
||||||
|
"""Initialise a generic HomeKit device."""
|
||||||
|
self._name = accessory.model
|
||||||
|
self._securecon = accessory.securecon
|
||||||
|
self._aid = devinfo['aid']
|
||||||
|
self._iid = devinfo['iid']
|
||||||
|
self._address = "homekit-{}-{}".format(devinfo['serial'], self._iid)
|
||||||
|
self._features = 0
|
||||||
|
self._chars = {}
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Obtain a HomeKit device's state."""
|
||||||
|
response = self._securecon.get('/accessories')
|
||||||
|
data = json.loads(response.read().decode())
|
||||||
|
for accessory in data['accessories']:
|
||||||
|
if accessory['aid'] != self._aid:
|
||||||
|
continue
|
||||||
|
for service in accessory['services']:
|
||||||
|
if service['iid'] != self._iid:
|
||||||
|
continue
|
||||||
|
self.update_characteristics(service['characteristics'])
|
||||||
|
break
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return the ID of this device."""
|
||||||
|
return self._address
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the device if any."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def update_characteristics(self, characteristics):
|
||||||
|
"""Synchronise a HomeKit device state with Home Assistant."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: too-many-function-args
|
||||||
|
def setup(hass, config):
|
||||||
|
"""Set up for Homekit devices."""
|
||||||
|
def discovery_dispatch(service, discovery_info):
|
||||||
|
"""Dispatcher for Homekit discovery events."""
|
||||||
|
# model, id
|
||||||
|
host = discovery_info['host']
|
||||||
|
port = discovery_info['port']
|
||||||
|
model = discovery_info['properties']['md']
|
||||||
|
hkid = discovery_info['properties']['id']
|
||||||
|
config_num = int(discovery_info['properties']['c#'])
|
||||||
|
|
||||||
|
# Only register a device once, but rescan if the config has changed
|
||||||
|
if hkid in hass.data[KNOWN_DEVICES]:
|
||||||
|
device = hass.data[KNOWN_DEVICES][hkid]
|
||||||
|
if config_num > device.config_num and \
|
||||||
|
device.pairing_info is not None:
|
||||||
|
device.accessory_setup()
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug('Discovered unique device %s', hkid)
|
||||||
|
device = HKDevice(hass, host, port, model, hkid, config_num, config)
|
||||||
|
hass.data[KNOWN_DEVICES][hkid] = device
|
||||||
|
|
||||||
|
hass.data[KNOWN_ACCESSORIES] = {}
|
||||||
|
hass.data[KNOWN_DEVICES] = {}
|
||||||
|
discovery.listen(hass, SERVICE_HOMEKIT, discovery_dispatch)
|
||||||
|
return True
|
134
homeassistant/components/light/homekit_controller.py
Normal file
134
homeassistant/components/light/homekit_controller.py
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
"""
|
||||||
|
Support for Homekit lights.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/light.homekit_controller/
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.components.homekit_controller import (
|
||||||
|
HomeKitEntity, KNOWN_ACCESSORIES)
|
||||||
|
from homeassistant.components.light import (
|
||||||
|
ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_COLOR_TEMP, SUPPORT_BRIGHTNESS,
|
||||||
|
SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light)
|
||||||
|
|
||||||
|
DEPENDENCIES = ['homekit_controller']
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Set up Homekit lighting."""
|
||||||
|
if discovery_info is not None:
|
||||||
|
accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']]
|
||||||
|
add_devices([HomeKitLight(accessory, discovery_info)], True)
|
||||||
|
|
||||||
|
|
||||||
|
class HomeKitLight(HomeKitEntity, Light):
|
||||||
|
"""Representation of a Homekit light."""
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
"""Initialise the light."""
|
||||||
|
super().__init__(*args)
|
||||||
|
self._on = None
|
||||||
|
self._brightness = None
|
||||||
|
self._color_temperature = None
|
||||||
|
self._hue = None
|
||||||
|
self._saturation = None
|
||||||
|
|
||||||
|
def update_characteristics(self, characteristics):
|
||||||
|
"""Synchronise light state with Home Assistant."""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import homekit
|
||||||
|
|
||||||
|
for characteristic in characteristics:
|
||||||
|
ctype = characteristic['type']
|
||||||
|
ctype = homekit.CharacteristicsTypes.get_short(ctype)
|
||||||
|
if ctype == "on":
|
||||||
|
self._chars['on'] = characteristic['iid']
|
||||||
|
self._on = characteristic['value']
|
||||||
|
elif ctype == 'brightness':
|
||||||
|
self._chars['brightness'] = characteristic['iid']
|
||||||
|
self._features |= SUPPORT_BRIGHTNESS
|
||||||
|
self._brightness = characteristic['value']
|
||||||
|
elif ctype == 'color-temperature':
|
||||||
|
self._chars['color_temperature'] = characteristic['iid']
|
||||||
|
self._features |= SUPPORT_COLOR_TEMP
|
||||||
|
self._color_temperature = characteristic['value']
|
||||||
|
elif ctype == "hue":
|
||||||
|
self._chars['hue'] = characteristic['iid']
|
||||||
|
self._features |= SUPPORT_COLOR
|
||||||
|
self._hue = characteristic['value']
|
||||||
|
elif ctype == "saturation":
|
||||||
|
self._chars['saturation'] = characteristic['iid']
|
||||||
|
self._features |= SUPPORT_COLOR
|
||||||
|
self._saturation = characteristic['value']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""Return true if device is on."""
|
||||||
|
return self._on
|
||||||
|
|
||||||
|
@property
|
||||||
|
def brightness(self):
|
||||||
|
"""Return the brightness of this light between 0..255."""
|
||||||
|
if self._features & SUPPORT_BRIGHTNESS:
|
||||||
|
return self._brightness * 255 / 100
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hs_color(self):
|
||||||
|
"""Return the color property."""
|
||||||
|
if self._features & SUPPORT_COLOR:
|
||||||
|
return (self._hue, self._saturation)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color_temp(self):
|
||||||
|
"""Return the color temperature."""
|
||||||
|
if self._features & SUPPORT_COLOR_TEMP:
|
||||||
|
return self._color_temperature
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_features(self):
|
||||||
|
"""Flag supported features."""
|
||||||
|
return self._features
|
||||||
|
|
||||||
|
def turn_on(self, **kwargs):
|
||||||
|
"""Turn the specified light on."""
|
||||||
|
hs_color = kwargs.get(ATTR_HS_COLOR)
|
||||||
|
temperature = kwargs.get(ATTR_COLOR_TEMP)
|
||||||
|
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
||||||
|
|
||||||
|
characteristics = []
|
||||||
|
if hs_color is not None:
|
||||||
|
characteristics.append({'aid': self._aid,
|
||||||
|
'iid': self._chars['hue'],
|
||||||
|
'value': hs_color[0]})
|
||||||
|
characteristics.append({'aid': self._aid,
|
||||||
|
'iid': self._chars['saturation'],
|
||||||
|
'value': hs_color[1]})
|
||||||
|
if brightness is not None:
|
||||||
|
characteristics.append({'aid': self._aid,
|
||||||
|
'iid': self._chars['brightness'],
|
||||||
|
'value': int(brightness * 100 / 255)})
|
||||||
|
|
||||||
|
if temperature is not None:
|
||||||
|
characteristics.append({'aid': self._aid,
|
||||||
|
'iid': self._chars['color-temperature'],
|
||||||
|
'value': int(temperature)})
|
||||||
|
characteristics.append({'aid': self._aid,
|
||||||
|
'iid': self._chars['on'],
|
||||||
|
'value': True})
|
||||||
|
body = json.dumps({'characteristics': characteristics})
|
||||||
|
self._securecon.put('/characteristics', body)
|
||||||
|
|
||||||
|
def turn_off(self, **kwargs):
|
||||||
|
"""Turn the specified light off."""
|
||||||
|
characteristics = [{'aid': self._aid,
|
||||||
|
'iid': self._chars['on'],
|
||||||
|
'value': False}]
|
||||||
|
body = json.dumps({'characteristics': characteristics})
|
||||||
|
self._securecon.put('/characteristics', body)
|
68
homeassistant/components/switch/homekit_controller.py
Normal file
68
homeassistant/components/switch/homekit_controller.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
"""
|
||||||
|
Support for Homekit switches.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/switch.homekit_controller/
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.components.homekit_controller import (HomeKitEntity,
|
||||||
|
KNOWN_ACCESSORIES)
|
||||||
|
from homeassistant.components.switch import SwitchDevice
|
||||||
|
|
||||||
|
DEPENDENCIES = ['homekit_controller']
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Set up Homekit switch support."""
|
||||||
|
if discovery_info is not None:
|
||||||
|
accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']]
|
||||||
|
add_devices([HomeKitSwitch(accessory, discovery_info)], True)
|
||||||
|
|
||||||
|
|
||||||
|
class HomeKitSwitch(HomeKitEntity, SwitchDevice):
|
||||||
|
"""Representation of a Homekit switch."""
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
"""Initialise the switch."""
|
||||||
|
super().__init__(*args)
|
||||||
|
self._on = None
|
||||||
|
|
||||||
|
def update_characteristics(self, characteristics):
|
||||||
|
"""Synchronise the switch state with Home Assistant."""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import homekit
|
||||||
|
|
||||||
|
for characteristic in characteristics:
|
||||||
|
ctype = characteristic['type']
|
||||||
|
ctype = homekit.CharacteristicsTypes.get_short(ctype)
|
||||||
|
if ctype == "on":
|
||||||
|
self._chars['on'] = characteristic['iid']
|
||||||
|
self._on = characteristic['value']
|
||||||
|
elif ctype == "outlet-in-use":
|
||||||
|
self._chars['outlet-in-use'] = characteristic['iid']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""Return true if device is on."""
|
||||||
|
return self._on
|
||||||
|
|
||||||
|
def turn_on(self, **kwargs):
|
||||||
|
"""Turn the specified switch on."""
|
||||||
|
self._on = True
|
||||||
|
characteristics = [{'aid': self._aid,
|
||||||
|
'iid': self._chars['on'],
|
||||||
|
'value': True}]
|
||||||
|
body = json.dumps({'characteristics': characteristics})
|
||||||
|
self._securecon.put('/characteristics', body)
|
||||||
|
|
||||||
|
def turn_off(self, **kwargs):
|
||||||
|
"""Turn the specified switch off."""
|
||||||
|
characteristics = [{'aid': self._aid,
|
||||||
|
'iid': self._chars['on'],
|
||||||
|
'value': False}]
|
||||||
|
body = json.dumps({'characteristics': characteristics})
|
||||||
|
self._securecon.put('/characteristics', body)
|
|
@ -381,6 +381,9 @@ holidays==0.9.4
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20180404.0
|
home-assistant-frontend==20180404.0
|
||||||
|
|
||||||
|
# homeassistant.components.homekit_controller
|
||||||
|
# homekit==0.5
|
||||||
|
|
||||||
# homeassistant.components.homematicip_cloud
|
# homeassistant.components.homematicip_cloud
|
||||||
homematicip==0.8
|
homematicip==0.8
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ COMMENT_REQUIREMENTS = (
|
||||||
'i2csense',
|
'i2csense',
|
||||||
'credstash',
|
'credstash',
|
||||||
'bme680',
|
'bme680',
|
||||||
|
'homekit',
|
||||||
)
|
)
|
||||||
|
|
||||||
TEST_REQUIREMENTS = (
|
TEST_REQUIREMENTS = (
|
||||||
|
|
|
@ -25,7 +25,8 @@ UNKNOWN_SERVICE = 'this_service_will_never_be_supported'
|
||||||
|
|
||||||
BASE_CONFIG = {
|
BASE_CONFIG = {
|
||||||
discovery.DOMAIN: {
|
discovery.DOMAIN: {
|
||||||
'ignore': []
|
'ignore': [],
|
||||||
|
'enable': []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue