core/homeassistant/components/homekit/__init__.py

133 lines
4.0 KiB
Python

"""Support for Apple HomeKit.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/homekit/
"""
import asyncio
import logging
import re
import voluptuous as vol
from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, CONF_PORT,
TEMP_CELSIUS, TEMP_FAHRENHEIT,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from homeassistant.util import get_local_ip
from homeassistant.util.decorator import Registry
TYPES = Registry()
_LOGGER = logging.getLogger(__name__)
_RE_VALID_PINCODE = r"^(\d{3}-\d{2}-\d{3})$"
DOMAIN = 'homekit'
REQUIREMENTS = ['HAP-python==1.1.7']
BRIDGE_NAME = 'Home Assistant'
CONF_PIN_CODE = 'pincode'
HOMEKIT_FILE = '.homekit.state'
def valid_pin(value):
"""Validate pin code value."""
match = re.match(_RE_VALID_PINCODE, str(value).strip())
if not match:
raise vol.Invalid("Pin must be in the format: '123-45-678'")
return match.group(0)
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.All({
vol.Optional(CONF_PORT, default=51826): vol.Coerce(int),
vol.Optional(CONF_PIN_CODE, default='123-45-678'): valid_pin,
})
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
"""Setup the HomeKit component."""
_LOGGER.debug("Begin setup HomeKit")
conf = config[DOMAIN]
port = conf.get(CONF_PORT)
pin = str.encode(conf.get(CONF_PIN_CODE))
homekit = HomeKit(hass, port)
homekit.setup_bridge(pin)
hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, homekit.start_driver)
return True
def import_types():
"""Import all types from files in the HomeKit directory."""
_LOGGER.debug("Import type files.")
# pylint: disable=unused-variable
from . import covers, sensors # noqa F401
def get_accessory(hass, state):
"""Take state and return an accessory object if supported."""
if state.domain == 'sensor':
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
if unit == TEMP_CELSIUS or unit == TEMP_FAHRENHEIT:
_LOGGER.debug("Add \"%s\" as \"%s\"",
state.entity_id, 'TemperatureSensor')
return TYPES['TemperatureSensor'](hass, state.entity_id,
state.name)
elif state.domain == 'cover':
# Only add covers that support set_cover_position
if state.attributes.get(ATTR_SUPPORTED_FEATURES) & 4:
_LOGGER.debug("Add \"%s\" as \"%s\"",
state.entity_id, 'Window')
return TYPES['Window'](hass, state.entity_id, state.name)
return None
class HomeKit():
"""Class to handle all actions between HomeKit and Home Assistant."""
def __init__(self, hass, port):
"""Initialize a HomeKit object."""
self._hass = hass
self._port = port
self.bridge = None
self.driver = None
def setup_bridge(self, pin):
"""Setup the bridge component to track all accessories."""
from .accessories import HomeBridge
self.bridge = HomeBridge(BRIDGE_NAME, 'homekit.bridge', pin)
def start_driver(self, event):
"""Start the accessory driver."""
from pyhap.accessory_driver import AccessoryDriver
self._hass.bus.listen_once(
EVENT_HOMEASSISTANT_STOP, self.stop_driver)
import_types()
_LOGGER.debug("Start adding accessories.")
for state in self._hass.states.all():
acc = get_accessory(self._hass, state)
if acc is not None:
self.bridge.add_accessory(acc)
ip_address = get_local_ip()
path = self._hass.config.path(HOMEKIT_FILE)
self.driver = AccessoryDriver(self.bridge, self._port,
ip_address, path)
_LOGGER.debug("Driver started")
self.driver.start()
def stop_driver(self, event):
"""Stop the accessory driver."""
_LOGGER.debug("Driver stop")
if self.driver is not None:
self.driver.stop()