HomeKit Restructure (new config options) (#12997)

* Restructure
* Pincode will now be autogenerated and display using a persistence notification
* Added 'homekit.start' service
* Added config options
* Renamed files for types
* Improved tests
* Changes (based on feedback)
* Removed CONF_PIN_CODE
* Added services.yaml
* Service will only be registered if auto_start=False
* Bugfix names, changed default port
* Generate aids with zlib.adler32
* Added entity filter, minor changes
* Small changes
This commit is contained in:
cdce8p 2018-03-15 02:48:21 +01:00 committed by GitHub
parent 64f18c62f4
commit d348f09d3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1038 additions and 715 deletions

View file

@ -1,33 +1,22 @@
"""Tests for the HomeKit component."""
import unittest
from unittest.mock import call, patch, ANY
import voluptuous as vol
# pylint: disable=unused-import
from pyhap.accessory_driver import AccessoryDriver # noqa F401
from unittest.mock import call, patch, ANY, Mock
from homeassistant import setup
from homeassistant.core import Event
from homeassistant.components.homekit import (
CONF_PIN_CODE, HOMEKIT_FILE, HomeKit, valid_pin)
from homeassistant.core import State
from homeassistant.components.homekit import HomeKit, generate_aid
from homeassistant.components.homekit.accessories import HomeBridge
from homeassistant.components.homekit.const import (
DOMAIN, HOMEKIT_FILE, CONF_AUTO_START,
DEFAULT_PORT, SERVICE_HOMEKIT_START)
from homeassistant.helpers.entityfilter import generate_filter
from homeassistant.const import (
CONF_PORT, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from tests.common import get_test_home_assistant
from tests.mock.homekit import get_patch_paths, PATH_HOMEKIT
PATH_ACC, _ = get_patch_paths()
IP_ADDRESS = '127.0.0.1'
CONFIG_MIN = {'homekit': {}}
CONFIG = {
'homekit': {
CONF_PORT: 11111,
CONF_PIN_CODE: '987-65-432',
}
}
PATH_HOMEKIT = 'homeassistant.components.homekit'
class TestHomeKit(unittest.TestCase):
@ -41,75 +30,162 @@ class TestHomeKit(unittest.TestCase):
"""Stop down everything that was started."""
self.hass.stop()
def test_validate_pincode(self):
"""Test async_setup with invalid config option."""
schema = vol.Schema(valid_pin)
def test_generate_aid(self):
"""Test generate aid method."""
aid = generate_aid('demo.entity')
self.assertIsInstance(aid, int)
self.assertTrue(aid >= 2 and aid <= 18446744073709551615)
for value in ('', '123-456-78', 'a23-45-678', '12345678', 1234):
with self.assertRaises(vol.MultipleInvalid):
schema(value)
for value in ('123-45-678', '234-56-789'):
self.assertTrue(schema(value))
with patch(PATH_HOMEKIT + '.adler32') as mock_adler32:
mock_adler32.side_effect = [0, 1]
self.assertIsNone(generate_aid('demo.entity'))
@patch(PATH_HOMEKIT + '.HomeKit')
def test_setup_min(self, mock_homekit):
"""Test async_setup with minimal config option."""
"""Test async_setup with min config options."""
self.assertTrue(setup.setup_component(
self.hass, 'homekit', CONFIG_MIN))
self.hass, DOMAIN, {DOMAIN: {}}))
self.assertEqual(mock_homekit.mock_calls,
[call(self.hass, 51826),
call().setup_bridge(b'123-45-678')])
self.assertEqual(mock_homekit.mock_calls, [
call(self.hass, DEFAULT_PORT, ANY, {}),
call().setup()])
# Test auto start enabled
mock_homekit.reset_mock()
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
self.hass.block_till_done()
self.assertEqual(mock_homekit.mock_calls, [call().start(ANY)])
@patch(PATH_HOMEKIT + '.HomeKit')
def test_setup_auto_start_disabled(self, mock_homekit):
"""Test async_setup with auto start disabled and test service calls."""
mock_homekit.return_value = homekit = Mock()
config = {DOMAIN: {CONF_AUTO_START: False, CONF_PORT: 11111}}
self.assertTrue(setup.setup_component(
self.hass, DOMAIN, config))
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
self.hass.block_till_done()
self.assertEqual(mock_homekit.mock_calls,
[call().start_driver(ANY)])
self.assertEqual(mock_homekit.mock_calls, [
call(self.hass, 11111, ANY, {}),
call().setup()])
@patch(PATH_HOMEKIT + '.HomeKit')
def test_setup_parameters(self, mock_homekit):
"""Test async_setup with full config option."""
self.assertTrue(setup.setup_component(
self.hass, 'homekit', CONFIG))
# Test start call with driver stopped.
homekit.reset_mock()
homekit.configure_mock(**{'started': False})
self.assertEqual(mock_homekit.mock_calls,
[call(self.hass, 11111),
call().setup_bridge(b'987-65-432')])
self.hass.services.call('homekit', 'start')
self.assertEqual(homekit.mock_calls, [call.start()])
@patch('pyhap.accessory_driver.AccessoryDriver')
def test_homekit_class(self, mock_acc_driver):
"""Test interaction between the HomeKit class and pyhap."""
with patch(PATH_HOMEKIT + '.accessories.HomeBridge') as mock_bridge:
homekit = HomeKit(self.hass, 51826)
homekit.setup_bridge(b'123-45-678')
# Test start call with driver started.
homekit.reset_mock()
homekit.configure_mock(**{'started': True})
mock_bridge.reset_mock()
self.hass.states.set('demo.demo1', 'on')
self.hass.states.set('demo.demo2', 'off')
self.hass.services.call(DOMAIN, SERVICE_HOMEKIT_START)
self.assertEqual(homekit.mock_calls, [])
with patch(PATH_HOMEKIT + '.get_accessory') as mock_get_acc, \
patch(PATH_HOMEKIT + '.import_types') as mock_import_types, \
def test_homekit_setup(self):
"""Test setup of bridge and driver."""
homekit = HomeKit(self.hass, DEFAULT_PORT, {}, {})
with patch(PATH_HOMEKIT + '.accessories.HomeDriver') as mock_driver, \
patch('homeassistant.util.get_local_ip') as mock_ip:
mock_get_acc.side_effect = ['TempSensor', 'Window']
mock_ip.return_value = IP_ADDRESS
homekit.start_driver(Event(EVENT_HOMEASSISTANT_START))
homekit.setup()
path = self.hass.config.path(HOMEKIT_FILE)
self.assertTrue(isinstance(homekit.bridge, HomeBridge))
self.assertEqual(mock_driver.mock_calls, [
call(homekit.bridge, DEFAULT_PORT, IP_ADDRESS, path)])
self.assertEqual(mock_import_types.call_count, 1)
self.assertEqual(mock_get_acc.call_count, 2)
self.assertEqual(mock_bridge.mock_calls,
[call().add_accessory('TempSensor'),
call().add_accessory('Window')])
self.assertEqual(mock_acc_driver.mock_calls,
[call(homekit.bridge, 51826, IP_ADDRESS, path),
call().start()])
mock_acc_driver.reset_mock()
# Test if stop listener is setup
self.assertEqual(
self.hass.bus.listeners.get(EVENT_HOMEASSISTANT_STOP), 1)
self.hass.bus.fire(EVENT_HOMEASSISTANT_STOP)
self.hass.block_till_done()
def test_homekit_add_accessory(self):
"""Add accessory if config exists and get_acc returns an accessory."""
homekit = HomeKit(self.hass, None, lambda entity_id: True, {})
homekit.bridge = HomeBridge(self.hass)
self.assertEqual(mock_acc_driver.mock_calls, [call().stop()])
with patch(PATH_HOMEKIT + '.accessories.HomeBridge.add_accessory') \
as mock_add_acc, \
patch(PATH_HOMEKIT + '.get_accessory') as mock_get_acc:
mock_get_acc.side_effect = [None, 'acc', None]
homekit.add_bridge_accessory(State('light.demo', 'on'))
self.assertEqual(mock_get_acc.call_args,
call(self.hass, ANY, 363398124, {}))
self.assertFalse(mock_add_acc.called)
homekit.add_bridge_accessory(State('demo.test', 'on'))
self.assertEqual(mock_get_acc.call_args,
call(self.hass, ANY, 294192020, {}))
self.assertTrue(mock_add_acc.called)
homekit.add_bridge_accessory(State('demo.test_2', 'on'))
self.assertEqual(mock_get_acc.call_args,
call(self.hass, ANY, 429982757, {}))
self.assertEqual(mock_add_acc.mock_calls, [call('acc')])
def test_homekit_entity_filter(self):
"""Test the entity filter."""
entity_filter = generate_filter(['cover'], ['demo.test'], [], [])
homekit = HomeKit(self.hass, None, entity_filter, {})
with patch(PATH_HOMEKIT + '.get_accessory') as mock_get_acc:
mock_get_acc.return_value = None
homekit.add_bridge_accessory(State('cover.test', 'open'))
self.assertTrue(mock_get_acc.called)
mock_get_acc.reset_mock()
homekit.add_bridge_accessory(State('demo.test', 'on'))
self.assertTrue(mock_get_acc.called)
mock_get_acc.reset_mock()
homekit.add_bridge_accessory(State('light.demo', 'light'))
self.assertFalse(mock_get_acc.called)
@patch(PATH_HOMEKIT + '.show_setup_message')
@patch(PATH_HOMEKIT + '.HomeKit.add_bridge_accessory')
def test_homekit_start(self, mock_add_bridge_acc, mock_show_setup_msg):
"""Test HomeKit start method."""
homekit = HomeKit(self.hass, None, {}, {'cover.demo': {}})
homekit.bridge = HomeBridge(self.hass)
homekit.driver = Mock()
self.hass.states.set('light.demo', 'on')
state = self.hass.states.all()[0]
homekit.start()
self.assertEqual(mock_add_bridge_acc.mock_calls, [call(state)])
self.assertEqual(mock_show_setup_msg.mock_calls, [
call(homekit.bridge, self.hass)])
self.assertEqual(homekit.driver.mock_calls, [call.start()])
self.assertTrue(homekit.started)
# Test start() if already started
homekit.driver.reset_mock()
homekit.start()
self.assertEqual(homekit.driver.mock_calls, [])
def test_homekit_stop(self):
"""Test HomeKit stop method."""
homekit = HomeKit(None, None, None, None)
homekit.driver = Mock()
# Test if started = False
homekit.stop()
self.assertFalse(homekit.driver.stop.called)
# Test if driver not started
homekit.started = True
homekit.driver.configure_mock(**{'run_sentinel': None})
homekit.stop()
self.assertFalse(homekit.driver.stop.called)
# Test if driver is started
homekit.driver.configure_mock(**{'run_sentinel': 'sentinel'})
homekit.stop()
self.assertTrue(homekit.driver.stop.called)