From ed7aea006a0af66526765a875552f7a1ec55fbbe Mon Sep 17 00:00:00 2001 From: cdce8p <30130371+cdce8p@users.noreply.github.com> Date: Fri, 16 Nov 2018 11:08:39 +0100 Subject: [PATCH] Add safe_mode HomeKit (#18356) * Use: 'safe_mode: True' in case of pairing issues --- homeassistant/components/homekit/__init__.py | 13 +++++-- homeassistant/components/homekit/const.py | 2 + tests/components/homekit/test_homekit.py | 39 ++++++++++++++------ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 650fff45c7b..cd517b6da7c 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -22,7 +22,8 @@ from homeassistant.util import get_local_ip from homeassistant.util.decorator import Registry from .const import ( BRIDGE_NAME, CONF_AUTO_START, CONF_ENTITY_CONFIG, CONF_FEATURE_LIST, - CONF_FILTER, DEFAULT_AUTO_START, DEFAULT_PORT, DEVICE_CLASS_CO, + CONF_FILTER, CONF_SAFE_MODE, DEFAULT_AUTO_START, DEFAULT_PORT, + DEFAULT_SAFE_MODE, DEVICE_CLASS_CO, DEVICE_CLASS_CO2, DEVICE_CLASS_PM25, DOMAIN, HOMEKIT_FILE, SERVICE_HOMEKIT_START, TYPE_FAUCET, TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER, TYPE_SWITCH, TYPE_VALVE) @@ -58,6 +59,7 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_IP_ADDRESS): vol.All(ipaddress.ip_address, cv.string), vol.Optional(CONF_AUTO_START, default=DEFAULT_AUTO_START): cv.boolean, + vol.Optional(CONF_SAFE_MODE, default=DEFAULT_SAFE_MODE): cv.boolean, vol.Optional(CONF_FILTER, default={}): FILTER_SCHEMA, vol.Optional(CONF_ENTITY_CONFIG, default={}): validate_entity_config, }) @@ -73,11 +75,12 @@ async def async_setup(hass, config): port = conf[CONF_PORT] ip_address = conf.get(CONF_IP_ADDRESS) auto_start = conf[CONF_AUTO_START] + safe_mode = conf[CONF_SAFE_MODE] entity_filter = conf[CONF_FILTER] entity_config = conf[CONF_ENTITY_CONFIG] homekit = HomeKit(hass, name, port, ip_address, entity_filter, - entity_config) + entity_config, safe_mode) await hass.async_add_executor_job(homekit.setup) if auto_start: @@ -196,7 +199,7 @@ class HomeKit(): """Class to handle all actions between HomeKit and Home Assistant.""" def __init__(self, hass, name, port, ip_address, entity_filter, - entity_config): + entity_config, safe_mode): """Initialize a HomeKit object.""" self.hass = hass self._name = name @@ -204,6 +207,7 @@ class HomeKit(): self._ip_address = ip_address self._filter = entity_filter self._config = entity_config + self._safe_mode = safe_mode self.status = STATUS_READY self.bridge = None @@ -221,6 +225,9 @@ class HomeKit(): self.driver = HomeDriver(self.hass, address=ip_addr, port=self._port, persist_file=path) self.bridge = HomeBridge(self.hass, self.driver, self._name) + if self._safe_mode: + _LOGGER.debug('Safe_mode selected') + self.driver.safe_mode = True def add_bridge_accessory(self, state): """Try adding accessory to bridge if configured beforehand.""" diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 0b4cdf15fb5..d0e3d52b363 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -15,10 +15,12 @@ CONF_ENTITY_CONFIG = 'entity_config' CONF_FEATURE = 'feature' CONF_FEATURE_LIST = 'feature_list' CONF_FILTER = 'filter' +CONF_SAFE_MODE = 'safe_mode' # #### Config Defaults #### DEFAULT_AUTO_START = True DEFAULT_PORT = 51827 +DEFAULT_SAFE_MODE = False # #### Features #### FEATURE_ON_OFF = 'on_off' diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index a831a7e9e5d..4dbb6351ee7 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -9,8 +9,8 @@ from homeassistant.components.homekit import ( STATUS_RUNNING, STATUS_STOPPED, STATUS_WAIT) from homeassistant.components.homekit.accessories import HomeBridge from homeassistant.components.homekit.const import ( - CONF_AUTO_START, BRIDGE_NAME, DEFAULT_PORT, DOMAIN, HOMEKIT_FILE, - SERVICE_HOMEKIT_START) + CONF_AUTO_START, CONF_SAFE_MODE, BRIDGE_NAME, DEFAULT_PORT, + DEFAULT_SAFE_MODE, DOMAIN, HOMEKIT_FILE, SERVICE_HOMEKIT_START) from homeassistant.const import ( CONF_NAME, CONF_IP_ADDRESS, CONF_PORT, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) @@ -49,7 +49,7 @@ async def test_setup_min(hass): hass, DOMAIN, {DOMAIN: {}}) mock_homekit.assert_any_call(hass, BRIDGE_NAME, DEFAULT_PORT, None, ANY, - {}) + {}, DEFAULT_SAFE_MODE) assert mock_homekit().setup.called is True # Test auto start enabled @@ -63,7 +63,8 @@ async def test_setup_min(hass): async def test_setup_auto_start_disabled(hass): """Test async_setup with auto start disabled and test service calls.""" config = {DOMAIN: {CONF_AUTO_START: False, CONF_NAME: 'Test Name', - CONF_PORT: 11111, CONF_IP_ADDRESS: '172.0.0.0'}} + CONF_PORT: 11111, CONF_IP_ADDRESS: '172.0.0.0', + CONF_SAFE_MODE: DEFAULT_SAFE_MODE}} with patch(PATH_HOMEKIT + '.HomeKit') as mock_homekit: mock_homekit.return_value = homekit = Mock() @@ -71,7 +72,7 @@ async def test_setup_auto_start_disabled(hass): hass, DOMAIN, config) mock_homekit.assert_any_call(hass, 'Test Name', 11111, '172.0.0.0', ANY, - {}) + {}, DEFAULT_SAFE_MODE) assert mock_homekit().setup.called is True # Test auto_start disabled @@ -99,7 +100,8 @@ async def test_setup_auto_start_disabled(hass): async def test_homekit_setup(hass, hk_driver): """Test setup of bridge and driver.""" - homekit = HomeKit(hass, BRIDGE_NAME, DEFAULT_PORT, None, {}, {}) + homekit = HomeKit(hass, BRIDGE_NAME, DEFAULT_PORT, None, {}, {}, + DEFAULT_SAFE_MODE) with patch(PATH_HOMEKIT + '.accessories.HomeDriver', return_value=hk_driver) as mock_driver, \ @@ -111,6 +113,7 @@ async def test_homekit_setup(hass, hk_driver): assert isinstance(homekit.bridge, HomeBridge) mock_driver.assert_called_with( hass, address=IP_ADDRESS, port=DEFAULT_PORT, persist_file=path) + assert homekit.driver.safe_mode is False # Test if stop listener is setup assert hass.bus.async_listeners().get(EVENT_HOMEASSISTANT_STOP) == 1 @@ -118,7 +121,8 @@ async def test_homekit_setup(hass, hk_driver): async def test_homekit_setup_ip_address(hass, hk_driver): """Test setup with given IP address.""" - homekit = HomeKit(hass, BRIDGE_NAME, DEFAULT_PORT, '172.0.0.0', {}, {}) + homekit = HomeKit(hass, BRIDGE_NAME, DEFAULT_PORT, '172.0.0.0', {}, {}, + None) with patch(PATH_HOMEKIT + '.accessories.HomeDriver', return_value=hk_driver) as mock_driver: @@ -127,9 +131,20 @@ async def test_homekit_setup_ip_address(hass, hk_driver): hass, address='172.0.0.0', port=DEFAULT_PORT, persist_file=ANY) +async def test_homekit_setup_safe_mode(hass, hk_driver): + """Test if safe_mode flag is set.""" + homekit = HomeKit(hass, BRIDGE_NAME, DEFAULT_PORT, None, {}, {}, True) + + with patch(PATH_HOMEKIT + '.accessories.HomeDriver', + return_value=hk_driver): + await hass.async_add_job(homekit.setup) + assert homekit.driver.safe_mode is True + + async def test_homekit_add_accessory(): """Add accessory if config exists and get_acc returns an accessory.""" - homekit = HomeKit('hass', None, None, None, lambda entity_id: True, {}) + homekit = HomeKit('hass', None, None, None, lambda entity_id: True, {}, + None) homekit.driver = 'driver' homekit.bridge = mock_bridge = Mock() @@ -152,7 +167,7 @@ async def test_homekit_add_accessory(): async def test_homekit_entity_filter(hass): """Test the entity filter.""" entity_filter = generate_filter(['cover'], ['demo.test'], [], []) - homekit = HomeKit(hass, None, None, None, entity_filter, {}) + homekit = HomeKit(hass, None, None, None, entity_filter, {}, None) with patch(PATH_HOMEKIT + '.get_accessory') as mock_get_acc: mock_get_acc.return_value = None @@ -172,7 +187,7 @@ async def test_homekit_entity_filter(hass): async def test_homekit_start(hass, hk_driver, debounce_patcher): """Test HomeKit start method.""" pin = b'123-45-678' - homekit = HomeKit(hass, None, None, None, {}, {'cover.demo': {}}) + homekit = HomeKit(hass, None, None, None, {}, {'cover.demo': {}}, None) homekit.bridge = Mock() homekit.bridge.accessories = [] homekit.driver = hk_driver @@ -203,7 +218,7 @@ async def test_homekit_start(hass, hk_driver, debounce_patcher): async def test_homekit_stop(hass): """Test HomeKit stop method.""" - homekit = HomeKit(hass, None, None, None, None, None) + homekit = HomeKit(hass, None, None, None, None, None, None) homekit.driver = Mock() assert homekit.status == STATUS_READY @@ -222,7 +237,7 @@ async def test_homekit_stop(hass): async def test_homekit_too_many_accessories(hass, hk_driver): """Test adding too many accessories to HomeKit.""" - homekit = HomeKit(hass, None, None, None, None, None) + homekit = HomeKit(hass, None, None, None, None, None, None) homekit.bridge = Mock() homekit.bridge.accessories = range(MAX_DEVICES + 1) homekit.driver = hk_driver