Add homekit configuration option to bind to default interface (#33999)

* Add homekit configuration option to bind to default interface

Homekit can fail to be discoverable because the
zeroconf default is to bind to all interfaces
(InterfaceChoice.All).  This does not work
on some systems and (InterfaceChoice.Default) which
binds to 0.0.0.0 is needed for homekit to zeroconf
to function.

A new option is available for homekit

zeroconf_default_interface: true

* Update tests

* Update homeassistant/components/homekit/__init__.py

Co-Authored-By: springstan <46536646+springstan@users.noreply.github.com>

* Update homeassistant/components/homekit/__init__.py

Co-Authored-By: springstan <46536646+springstan@users.noreply.github.com>

* Review items

* has a default

* Revert "has a default"

This reverts commit 24ecf0920f05f2793abe8a4242ca3f101306b93a.

Breaks the tests

Co-authored-by: springstan <46536646+springstan@users.noreply.github.com>
This commit is contained in:
J. Nick Koston 2020-04-12 09:59:50 -05:00 committed by GitHub
parent fc670e46d9
commit 5aca16ef01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 7 deletions

View File

@ -4,6 +4,7 @@ import logging
from zlib import adler32
import voluptuous as vol
from zeroconf import InterfaceChoice
from homeassistant.components import cover
from homeassistant.components.cover import DEVICE_CLASS_GARAGE, DEVICE_CLASS_GATE
@ -39,9 +40,11 @@ from .const import (
CONF_FEATURE_LIST,
CONF_FILTER,
CONF_SAFE_MODE,
CONF_ZEROCONF_DEFAULT_INTERFACE,
DEFAULT_AUTO_START,
DEFAULT_PORT,
DEFAULT_SAFE_MODE,
DEFAULT_ZEROCONF_DEFAULT_INTERFACE,
DEVICE_CLASS_CO,
DEVICE_CLASS_CO2,
DEVICE_CLASS_PM25,
@ -98,6 +101,10 @@ CONFIG_SCHEMA = vol.Schema(
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,
vol.Optional(
CONF_ZEROCONF_DEFAULT_INTERFACE,
default=DEFAULT_ZEROCONF_DEFAULT_INTERFACE,
): cv.boolean,
}
)
},
@ -122,6 +129,9 @@ async def async_setup(hass, config):
safe_mode = conf[CONF_SAFE_MODE]
entity_filter = conf[CONF_FILTER]
entity_config = conf[CONF_ENTITY_CONFIG]
interface_choice = (
InterfaceChoice.Default if config.get(CONF_ZEROCONF_DEFAULT_INTERFACE) else None
)
homekit = HomeKit(
hass,
@ -132,6 +142,7 @@ async def async_setup(hass, config):
entity_config,
safe_mode,
advertise_ip,
interface_choice,
)
await hass.async_add_executor_job(homekit.setup)
@ -287,6 +298,7 @@ class HomeKit:
entity_config,
safe_mode,
advertise_ip=None,
interface_choice=None,
):
"""Initialize a HomeKit object."""
self.hass = hass
@ -297,6 +309,7 @@ class HomeKit:
self._config = entity_config
self._safe_mode = safe_mode
self._advertise_ip = advertise_ip
self._interface_choice = interface_choice
self.status = STATUS_READY
self.bridge = None
@ -317,6 +330,7 @@ class HomeKit:
port=self._port,
persist_file=path,
advertised_address=self._advertise_ip,
interface_choice=self._interface_choice,
)
self.bridge = HomeBridge(self.hass, self.driver, self._name)
if self._safe_mode:

View File

@ -21,12 +21,14 @@ CONF_FILTER = "filter"
CONF_LINKED_BATTERY_SENSOR = "linked_battery_sensor"
CONF_LOW_BATTERY_THRESHOLD = "low_battery_threshold"
CONF_SAFE_MODE = "safe_mode"
CONF_ZEROCONF_DEFAULT_INTERFACE = "zeroconf_default_interface"
# #### Config Defaults ####
DEFAULT_AUTO_START = True
DEFAULT_LOW_BATTERY_THRESHOLD = 20
DEFAULT_PORT = 51827
DEFAULT_SAFE_MODE = False
DEFAULT_ZEROCONF_DEFAULT_INTERFACE = False
# #### Features ####
FEATURE_ON_OFF = "on_off"

View File

@ -2,6 +2,7 @@
from unittest.mock import ANY, Mock, patch
import pytest
from zeroconf import InterfaceChoice
from homeassistant import setup
from homeassistant.components.homekit import (
@ -67,7 +68,7 @@ async def test_setup_min(hass):
assert await setup.async_setup_component(hass, DOMAIN, {DOMAIN: {}})
mock_homekit.assert_any_call(
hass, BRIDGE_NAME, DEFAULT_PORT, None, ANY, {}, DEFAULT_SAFE_MODE, None
hass, BRIDGE_NAME, DEFAULT_PORT, None, ANY, {}, DEFAULT_SAFE_MODE, None, None
)
assert mock_homekit().setup.called is True
@ -96,7 +97,7 @@ async def test_setup_auto_start_disabled(hass):
assert await setup.async_setup_component(hass, DOMAIN, config)
mock_homekit.assert_any_call(
hass, "Test Name", 11111, "172.0.0.0", ANY, {}, DEFAULT_SAFE_MODE, None
hass, "Test Name", 11111, "172.0.0.0", ANY, {}, DEFAULT_SAFE_MODE, None, None
)
assert mock_homekit().setup.called is True
@ -139,6 +140,7 @@ async def test_homekit_setup(hass, hk_driver):
port=DEFAULT_PORT,
persist_file=path,
advertised_address=None,
interface_choice=None,
)
assert homekit.driver.safe_mode is False
@ -160,6 +162,7 @@ async def test_homekit_setup_ip_address(hass, hk_driver):
port=DEFAULT_PORT,
persist_file=ANY,
advertised_address=None,
interface_choice=None,
)
@ -179,12 +182,41 @@ async def test_homekit_setup_advertise_ip(hass, hk_driver):
port=DEFAULT_PORT,
persist_file=ANY,
advertised_address="192.168.1.100",
interface_choice=None,
)
async def test_homekit_setup_interface_choice(hass, hk_driver):
"""Test setup with interface choice of Default."""
homekit = HomeKit(
hass,
BRIDGE_NAME,
DEFAULT_PORT,
"0.0.0.0",
{},
{},
None,
None,
InterfaceChoice.Default,
)
with patch(
f"{PATH_HOMEKIT}.accessories.HomeDriver", return_value=hk_driver
) as mock_driver:
await hass.async_add_executor_job(homekit.setup)
mock_driver.assert_called_with(
hass,
address="0.0.0.0",
port=DEFAULT_PORT,
persist_file=ANY,
advertised_address=None,
interface_choice=InterfaceChoice.Default,
)
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)
homekit = HomeKit(hass, BRIDGE_NAME, DEFAULT_PORT, None, {}, {}, True, None)
with patch(f"{PATH_HOMEKIT}.accessories.HomeDriver", return_value=hk_driver):
await hass.async_add_executor_job(homekit.setup)
@ -193,7 +225,7 @@ async def test_homekit_setup_safe_mode(hass, hk_driver):
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, {}, None)
homekit = HomeKit("hass", None, None, None, lambda entity_id: True, {}, None, None)
homekit.driver = "driver"
homekit.bridge = mock_bridge = Mock()
@ -215,7 +247,7 @@ async def test_homekit_add_accessory():
async def test_homekit_remove_accessory():
"""Remove accessory from bridge."""
homekit = HomeKit("hass", None, None, None, lambda entity_id: True, {}, None)
homekit = HomeKit("hass", None, None, None, lambda entity_id: True, {}, None, None)
homekit.driver = "driver"
homekit.bridge = mock_bridge = Mock()
mock_bridge.accessories = {"light.demo": "acc"}
@ -228,7 +260,7 @@ async def test_homekit_remove_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, {}, None)
homekit = HomeKit(hass, None, None, None, entity_filter, {}, None, None)
with patch(f"{PATH_HOMEKIT}.get_accessory") as mock_get_acc:
mock_get_acc.return_value = None
@ -248,7 +280,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": {}}, None)
homekit = HomeKit(hass, None, None, None, {}, {"cover.demo": {}}, None, None)
homekit.bridge = Mock()
homekit.bridge.accessories = []
homekit.driver = hk_driver