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

@ -2,166 +2,164 @@
This includes tests for all mock object types.
"""
from unittest.mock import patch
# pylint: disable=unused-import
from pyhap.loader import get_serv_loader, get_char_loader # noqa F401
import unittest
from unittest.mock import call, patch, Mock
from homeassistant.components.homekit.accessories import (
set_accessory_info, add_preload_service, override_properties,
HomeAccessory, HomeBridge)
add_preload_service, set_accessory_info, override_properties,
HomeAccessory, HomeBridge, HomeDriver)
from homeassistant.components.homekit.const import (
ACCESSORY_MODEL, ACCESSORY_NAME, BRIDGE_MODEL, BRIDGE_NAME,
SERV_ACCESSORY_INFO, SERV_BRIDGING_STATE,
CHAR_MODEL, CHAR_MANUFACTURER, CHAR_NAME, CHAR_SERIAL_NUMBER)
from tests.mock.homekit import (
get_patch_paths, mock_preload_service,
MockTypeLoader, MockAccessory, MockService, MockChar)
PATH_SERV = 'pyhap.loader.get_serv_loader'
PATH_CHAR = 'pyhap.loader.get_char_loader'
PATH_ACC, _ = get_patch_paths()
CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER)
@patch(PATH_CHAR, return_value=MockTypeLoader('char'))
@patch(PATH_SERV, return_value=MockTypeLoader('service'))
def test_add_preload_service(mock_serv, mock_char):
"""Test method add_preload_service.
class TestAccessories(unittest.TestCase):
"""Test pyhap adapter methods."""
The methods 'get_serv_loader' and 'get_char_loader' are mocked.
"""
acc = MockAccessory('Accessory')
serv = add_preload_service(acc, 'TestService',
['TestChar', 'TestChar2'],
['TestOptChar', 'TestOptChar2'])
def test_add_preload_service(self):
"""Test add_preload_service without additional characteristics."""
acc = Mock()
serv = add_preload_service(acc, 'AirPurifier')
self.assertEqual(acc.mock_calls, [call.add_service(serv)])
with self.assertRaises(AssertionError):
serv.get_characteristic('Name')
assert serv.display_name == 'TestService'
assert len(serv.characteristics) == 2
assert len(serv.opt_characteristics) == 2
# Test with typo in service name
with self.assertRaises(KeyError):
add_preload_service(Mock(), 'AirPurifierTypo')
acc.services = []
serv = add_preload_service(acc, 'TestService')
# Test adding additional characteristic as string
serv = add_preload_service(Mock(), 'AirPurifier', 'Name')
serv.get_characteristic('Name')
assert not serv.characteristics
assert not serv.opt_characteristics
# Test adding additional characteristics as list
serv = add_preload_service(Mock(), 'AirPurifier',
['Name', 'RotationSpeed'])
serv.get_characteristic('Name')
serv.get_characteristic('RotationSpeed')
acc.services = []
serv = add_preload_service(acc, 'TestService',
'TestChar', 'TestOptChar')
# Test adding additional characteristic with typo
with self.assertRaises(KeyError):
add_preload_service(Mock(), 'AirPurifier', 'NameTypo')
assert len(serv.characteristics) == 1
assert len(serv.opt_characteristics) == 1
def test_set_accessory_info(self):
"""Test setting the basic accessory information."""
# Test HomeAccessory
acc = HomeAccessory()
set_accessory_info(acc, 'name', 'model', 'manufacturer', '0000')
assert serv.characteristics[0].display_name == 'TestChar'
assert serv.opt_characteristics[0].display_name == 'TestOptChar'
serv = acc.get_service(SERV_ACCESSORY_INFO)
self.assertEqual(serv.get_characteristic(CHAR_NAME).value, 'name')
self.assertEqual(serv.get_characteristic(CHAR_MODEL).value, 'model')
self.assertEqual(
serv.get_characteristic(CHAR_MANUFACTURER).value, 'manufacturer')
self.assertEqual(
serv.get_characteristic(CHAR_SERIAL_NUMBER).value, '0000')
# Test HomeBridge
acc = HomeBridge(None)
set_accessory_info(acc, 'name', 'model', 'manufacturer', '0000')
def test_override_properties():
"""Test override of characteristic properties with MockChar."""
char = MockChar('TestChar')
new_prop = {1: 'Test', 2: 'Demo'}
override_properties(char, new_prop)
serv = acc.get_service(SERV_ACCESSORY_INFO)
self.assertEqual(serv.get_characteristic(CHAR_MODEL).value, 'model')
self.assertEqual(
serv.get_characteristic(CHAR_MANUFACTURER).value, 'manufacturer')
self.assertEqual(
serv.get_characteristic(CHAR_SERIAL_NUMBER).value, '0000')
assert char.properties == new_prop
def test_override_properties(self):
"""Test overriding property values."""
serv = add_preload_service(Mock(), 'AirPurifier', 'RotationSpeed')
char_active = serv.get_characteristic('Active')
char_rotation_speed = serv.get_characteristic('RotationSpeed')
def test_set_accessory_info():
"""Test setting of basic accessory information with MockAccessory."""
acc = MockAccessory('Accessory')
set_accessory_info(acc, 'name', 'model', 'manufacturer', '0000')
self.assertTrue(
char_active.properties['ValidValues'].get('State') is None)
self.assertEqual(char_rotation_speed.properties['maxValue'], 100)
assert len(acc.services) == 1
serv = acc.services[0]
override_properties(char_active, valid_values={'State': 'On'})
override_properties(char_rotation_speed, properties={'maxValue': 200})
assert serv.display_name == SERV_ACCESSORY_INFO
assert len(serv.characteristics) == 4
chars = serv.characteristics
self.assertFalse(
char_active.properties['ValidValues'].get('State') is None)
self.assertEqual(char_rotation_speed.properties['maxValue'], 200)
assert chars[0].display_name == CHAR_NAME
assert chars[0].value == 'name'
assert chars[1].display_name == CHAR_MODEL
assert chars[1].value == 'model'
assert chars[2].display_name == CHAR_MANUFACTURER
assert chars[2].value == 'manufacturer'
assert chars[3].display_name == CHAR_SERIAL_NUMBER
assert chars[3].value == '0000'
def test_home_accessory(self):
"""Test HomeAccessory class."""
acc = HomeAccessory()
self.assertEqual(acc.display_name, ACCESSORY_NAME)
self.assertEqual(acc.category, 1) # Category.OTHER
self.assertEqual(len(acc.services), 1)
serv = acc.services[0] # SERV_ACCESSORY_INFO
self.assertEqual(
serv.get_characteristic(CHAR_MODEL).value, ACCESSORY_MODEL)
acc = HomeAccessory('test_name', 'test_model', 'FAN', aid=2)
self.assertEqual(acc.display_name, 'test_name')
self.assertEqual(acc.category, 3) # Category.FAN
self.assertEqual(acc.aid, 2)
self.assertEqual(len(acc.services), 1)
serv = acc.services[0] # SERV_ACCESSORY_INFO
self.assertEqual(
serv.get_characteristic(CHAR_MODEL).value, 'test_model')
@patch(PATH_ACC, side_effect=mock_preload_service)
def test_home_accessory(mock_pre_serv):
"""Test initializing a HomeAccessory object."""
acc = HomeAccessory('TestAccessory', 'test.accessory', 'WINDOW')
def test_home_bridge(self):
"""Test HomeBridge class."""
bridge = HomeBridge(None)
self.assertEqual(bridge.display_name, BRIDGE_NAME)
self.assertEqual(bridge.category, 2) # Category.BRIDGE
self.assertEqual(len(bridge.services), 2)
serv = bridge.services[0] # SERV_ACCESSORY_INFO
self.assertEqual(serv.display_name, SERV_ACCESSORY_INFO)
self.assertEqual(
serv.get_characteristic(CHAR_MODEL).value, BRIDGE_MODEL)
serv = bridge.services[1] # SERV_BRIDGING_STATE
self.assertEqual(serv.display_name, SERV_BRIDGING_STATE)
assert acc.display_name == 'TestAccessory'
assert acc.category == 13 # Category.WINDOW
assert len(acc.services) == 1
bridge = HomeBridge('hass', 'test_name', 'test_model')
self.assertEqual(bridge.display_name, 'test_name')
self.assertEqual(len(bridge.services), 2)
serv = bridge.services[0] # SERV_ACCESSORY_INFO
self.assertEqual(
serv.get_characteristic(CHAR_MODEL).value, 'test_model')
serv = acc.services[0]
assert serv.display_name == SERV_ACCESSORY_INFO
char_model = serv.get_characteristic(CHAR_MODEL)
assert char_model.get_value() == 'test.accessory'
# setup_message
bridge.setup_message()
# add_paired_client
with patch('pyhap.accessory.Accessory.add_paired_client') \
as mock_add_paired_client, \
patch('homeassistant.components.homekit.accessories.'
'dismiss_setup_message') as mock_dissmiss_msg:
bridge.add_paired_client('client_uuid', 'client_public')
@patch(PATH_ACC, side_effect=mock_preload_service)
def test_home_bridge(mock_pre_serv):
"""Test initializing a HomeBridge object."""
bridge = HomeBridge('TestBridge', 'test.bridge', b'123-45-678')
self.assertEqual(mock_add_paired_client.call_args,
call('client_uuid', 'client_public'))
self.assertEqual(mock_dissmiss_msg.call_args, call('hass'))
assert bridge.display_name == 'TestBridge'
assert bridge.pincode == b'123-45-678'
assert len(bridge.services) == 2
# remove_paired_client
with patch('pyhap.accessory.Accessory.remove_paired_client') \
as mock_remove_paired_client, \
patch('homeassistant.components.homekit.accessories.'
'show_setup_message') as mock_show_msg:
bridge.remove_paired_client('client_uuid')
assert bridge.services[0].display_name == SERV_ACCESSORY_INFO
assert bridge.services[1].display_name == SERV_BRIDGING_STATE
self.assertEqual(
mock_remove_paired_client.call_args, call('client_uuid'))
self.assertEqual(mock_show_msg.call_args, call(bridge, 'hass'))
char_model = bridge.services[0].get_characteristic(CHAR_MODEL)
assert char_model.get_value() == 'test.bridge'
def test_home_driver(self):
"""Test HomeDriver class."""
bridge = HomeBridge(None)
ip_adress = '127.0.0.1'
port = 51826
path = '.homekit.state'
with patch('pyhap.accessory_driver.AccessoryDriver.__init__') \
as mock_driver:
HomeDriver(bridge, ip_adress, port, path)
def test_mock_accessory():
"""Test attributes and functions of a MockAccessory."""
acc = MockAccessory('TestAcc')
serv = MockService('TestServ')
acc.add_service(serv)
assert acc.display_name == 'TestAcc'
assert len(acc.services) == 1
assert acc.get_service('TestServ') == serv
assert acc.get_service('NewServ').display_name == 'NewServ'
assert len(acc.services) == 2
def test_mock_service():
"""Test attributes and functions of a MockService."""
serv = MockService('TestServ')
char = MockChar('TestChar')
opt_char = MockChar('TestOptChar')
serv.add_characteristic(char)
serv.add_opt_characteristic(opt_char)
assert serv.display_name == 'TestServ'
assert len(serv.characteristics) == 1
assert len(serv.opt_characteristics) == 1
assert serv.get_characteristic('TestChar') == char
assert serv.get_characteristic('TestOptChar') == opt_char
assert serv.get_characteristic('NewChar').display_name == 'NewChar'
assert len(serv.characteristics) == 2
def test_mock_char():
"""Test attributes and functions of a MockChar."""
def callback_method(value):
"""Provide a callback options for 'set_value' method."""
assert value == 'With callback'
char = MockChar('TestChar')
char.set_value('Value')
assert char.display_name == 'TestChar'
assert char.get_value() == 'Value'
char.setter_callback = callback_method
char.set_value('With callback')
self.assertEqual(
mock_driver.call_args, call(bridge, ip_adress, port, path))