Add support for HomeKit (#12488)

* Basic Homekit support

* Added Temperatur Sensor
* Added Window Cover

* Code refactored

* Added class HomeAccessory(Accessory)
* Added class HomeBridge(Bridge)
* Changed homekit imports to relative, to enable use in custom_components
* Updated requirements
* Added docs
* Other smaller changes

* Changed Homekit from entity to class

* Changes based on feedback
* Updated config schema
* Add only covers that support set_cover_position

* Addressed comments, updated to pyhap==1.1.5

* For lint: added files to gen_requirements_all
* Added codeowner

* Small change to Wrapper classes

* Moved imports to import_types, small changes

* Small changes, added tests

* Homekit class: removed add_accessory since it's already covered by pyhap
* Added test requirement: HAP-python
* Added test suit for homekit setup and interaction between HA and pyhap
* Added test suit for get_accessories function

* Test bugfix

* Added validate pincode, tests for cover and sensor types
This commit is contained in:
cdce8p 2018-02-19 23:46:22 +01:00 committed by Paulus Schoutsen
parent dc21c61a44
commit eec3bad94f
14 changed files with 642 additions and 0 deletions

View file

@ -0,0 +1,124 @@
"""Tests for the homekit component."""
import unittest
from unittest.mock import patch
import voluptuous as vol
from homeassistant import setup
from homeassistant.core import Event
from homeassistant.components.homekit import (
CONF_PIN_CODE, BRIDGE_NAME, Homekit, valid_pin)
from homeassistant.components.homekit.covers import Window
from homeassistant.components.homekit.sensors import TemperatureSensor
from homeassistant.const import (
CONF_PORT, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from tests.common import get_test_home_assistant
HOMEKIT_PATH = 'homeassistant.components.homekit'
CONFIG_MIN = {'homekit': {}}
CONFIG = {
'homekit': {
CONF_PORT: 11111,
CONF_PIN_CODE: '987-65-432',
}
}
class TestHomekit(unittest.TestCase):
"""Test the Multicover component."""
def setUp(self):
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
def tearDown(self):
"""Stop down everthing that was started."""
self.hass.stop()
@patch(HOMEKIT_PATH + '.Homekit.start_driver')
@patch(HOMEKIT_PATH + '.Homekit.setup_bridge')
@patch(HOMEKIT_PATH + '.Homekit.__init__')
def test_setup_min(self, mock_homekit, mock_setup_bridge,
mock_start_driver):
"""Test async_setup with minimal config option."""
mock_homekit.return_value = None
self.assertTrue(setup.setup_component(
self.hass, 'homekit', CONFIG_MIN))
mock_homekit.assert_called_once_with(self.hass, 51826)
mock_setup_bridge.assert_called_with(b'123-45-678')
mock_start_driver.assert_not_called()
self.hass.start()
self.hass.block_till_done()
mock_start_driver.assert_called_once()
@patch(HOMEKIT_PATH + '.Homekit.start_driver')
@patch(HOMEKIT_PATH + '.Homekit.setup_bridge')
@patch(HOMEKIT_PATH + '.Homekit.__init__')
def test_setup_parameters(self, mock_homekit, mock_setup_bridge,
mock_start_driver):
"""Test async_setup with full config option."""
mock_homekit.return_value = None
self.assertTrue(setup.setup_component(
self.hass, 'homekit', CONFIG))
mock_homekit.assert_called_once_with(self.hass, 11111)
mock_setup_bridge.assert_called_with(b'987-65-432')
def test_validate_pincode(self):
"""Test async_setup with invalid config option."""
schema = vol.Schema(valid_pin)
for value in ('', '123-456-78', 'a23-45-678', '12345678'):
with self.assertRaises(vol.MultipleInvalid):
schema(value)
for value in ('123-45-678', '234-56-789'):
self.assertTrue(schema(value))
@patch('pyhap.accessory_driver.AccessoryDriver.persist')
@patch('pyhap.accessory_driver.AccessoryDriver.stop')
@patch('pyhap.accessory_driver.AccessoryDriver.start')
@patch(HOMEKIT_PATH + '.import_types')
@patch(HOMEKIT_PATH + '.get_accessory')
def test_homekit_pyhap_interaction(
self, mock_get_accessory, mock_import_types,
mock_driver_start, mock_driver_stop, mock_file_persist):
"""Test the interaction between the homekit class and pyhap."""
acc1 = TemperatureSensor(self.hass, 'sensor.temp', 'Temperature')
acc2 = Window(self.hass, 'cover.hall_window', 'Cover')
mock_get_accessory.side_effect = [acc1, acc2]
homekit = Homekit(self.hass, 51826)
homekit.setup_bridge(b'123-45-678')
self.assertEqual(homekit.bridge.display_name, BRIDGE_NAME)
self.hass.states.set('demo.demo1', 'on')
self.hass.states.set('demo.demo2', 'off')
self.hass.start()
self.hass.block_till_done()
homekit.start_driver(Event(EVENT_HOMEASSISTANT_START))
self.assertEqual(mock_get_accessory.call_count, 2)
mock_import_types.assert_called_once()
mock_driver_start.assert_called_once()
accessories = homekit.bridge.accessories
self.assertEqual(accessories[2], acc1)
self.assertEqual(accessories[3], acc2)
mock_driver_stop.assert_not_called()
self.hass.bus.fire(EVENT_HOMEASSISTANT_STOP)
self.hass.block_till_done()
mock_driver_stop.assert_called_once()