Merge pull request #40310 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2020-09-19 23:06:09 +02:00 committed by GitHub
commit ddbadb1e26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 157 additions and 76 deletions

View File

@ -11,7 +11,7 @@ from broadlink.exceptions import (
) )
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries, data_entry_flow
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_TIMEOUT, CONF_TYPE from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_TIMEOUT, CONF_TYPE
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
@ -20,6 +20,7 @@ from .const import ( # pylint: disable=unused-import
DEFAULT_PORT, DEFAULT_PORT,
DEFAULT_TIMEOUT, DEFAULT_TIMEOUT,
DOMAIN, DOMAIN,
DOMAINS_AND_TYPES,
) )
from .helpers import format_mac from .helpers import format_mac
@ -36,6 +37,19 @@ class BroadlinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_set_device(self, device, raise_on_progress=True): async def async_set_device(self, device, raise_on_progress=True):
"""Define a device for the config flow.""" """Define a device for the config flow."""
supported_types = {
device_type
for _, device_types in DOMAINS_AND_TYPES
for device_type in device_types
}
if device.type not in supported_types:
LOGGER.error(
"Unsupported device: %s. If it worked before, please open "
"an issue at https://github.com/home-assistant/core/issues",
hex(device.devtype),
)
raise data_entry_flow.AbortFlow("not_supported")
await self.async_set_unique_id( await self.async_set_unique_id(
device.mac.hex(), raise_on_progress=raise_on_progress device.mac.hex(), raise_on_progress=raise_on_progress
) )

View File

@ -35,6 +35,7 @@
"already_in_progress": "There is already a configuration flow in progress for this device", "already_in_progress": "There is already a configuration flow in progress for this device",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_host": "Invalid hostname or IP address", "invalid_host": "Invalid hostname or IP address",
"not_supported": "Device not supported",
"unknown": "[%key:common::config_flow::error::unknown%]" "unknown": "[%key:common::config_flow::error::unknown%]"
}, },
"error": { "error": {

View File

@ -5,6 +5,7 @@
"already_in_progress": "There is already a configuration flow in progress for this device", "already_in_progress": "There is already a configuration flow in progress for this device",
"cannot_connect": "Failed to connect", "cannot_connect": "Failed to connect",
"invalid_host": "Invalid hostname or IP address", "invalid_host": "Invalid hostname or IP address",
"not_supported": "Device not supported",
"unknown": "Unexpected error" "unknown": "Unexpected error"
}, },
"error": { "error": {

View File

@ -2,7 +2,7 @@
"domain": "frontend", "domain": "frontend",
"name": "Home Assistant Frontend", "name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend", "documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": ["home-assistant-frontend==20200917.1"], "requirements": ["home-assistant-frontend==20200918.0"],
"dependencies": [ "dependencies": [
"api", "api",
"auth", "auth",

View File

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/hangouts", "documentation": "https://www.home-assistant.io/integrations/hangouts",
"requirements": [ "requirements": [
"hangups==0.4.10" "hangups==0.4.11"
], ],
"codeowners": [] "codeowners": []
} }

View File

@ -116,6 +116,9 @@ class KodiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
} }
) )
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context.update({"title_placeholders": {CONF_NAME: self._name}})
try: try:
await validate_http(self.hass, self._get_data()) await validate_http(self.hass, self._get_data())
await validate_ws(self.hass, self._get_data()) await validate_ws(self.hass, self._get_data())
@ -129,8 +132,6 @@ class KodiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
_LOGGER.exception("Unexpected exception") _LOGGER.exception("Unexpected exception")
return self.async_abort(reason="unknown") return self.async_abort(reason="unknown")
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context.update({"title_placeholders": {CONF_NAME: self._name}})
return await self.async_step_discovery_confirm() return await self.async_step_discovery_confirm()
async def async_step_discovery_confirm(self, user_input=None): async def async_step_discovery_confirm(self, user_input=None):

View File

@ -171,7 +171,7 @@ class ModbusCoilSwitch(ToggleEntity, RestoreEntity):
return return
self._available = True self._available = True
return bool(result.bits[0]) return bool(result.bits[coil])
def _write_coil(self, coil, value): def _write_coil(self, coil, value):
"""Write coil using the Modbus hub slave.""" """Write coil using the Modbus hub slave."""

View File

@ -60,6 +60,7 @@ from .const import (
CONF_RETAIN, CONF_RETAIN,
CONF_STATE_TOPIC, CONF_STATE_TOPIC,
CONF_WILL_MESSAGE, CONF_WILL_MESSAGE,
DATA_MQTT_CONFIG,
DEFAULT_BIRTH, DEFAULT_BIRTH,
DEFAULT_DISCOVERY, DEFAULT_DISCOVERY,
DEFAULT_PAYLOAD_AVAILABLE, DEFAULT_PAYLOAD_AVAILABLE,
@ -88,7 +89,6 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = "mqtt" DOMAIN = "mqtt"
DATA_MQTT = "mqtt" DATA_MQTT = "mqtt"
DATA_MQTT_CONFIG = "mqtt_config"
SERVICE_PUBLISH = "publish" SERVICE_PUBLISH = "publish"
SERVICE_DUMP = "dump" SERVICE_DUMP = "dump"

View File

@ -24,6 +24,7 @@ from .const import (
CONF_BROKER, CONF_BROKER,
CONF_DISCOVERY, CONF_DISCOVERY,
CONF_WILL_MESSAGE, CONF_WILL_MESSAGE,
DATA_MQTT_CONFIG,
DEFAULT_BIRTH, DEFAULT_BIRTH,
DEFAULT_DISCOVERY, DEFAULT_DISCOVERY,
DEFAULT_WILL, DEFAULT_WILL,
@ -162,6 +163,7 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow):
"""Manage the MQTT options.""" """Manage the MQTT options."""
errors = {} errors = {}
current_config = self.config_entry.data current_config = self.config_entry.data
yaml_config = self.hass.data.get(DATA_MQTT_CONFIG, {})
if user_input is not None: if user_input is not None:
can_connect = await self.hass.async_add_executor_job( can_connect = await self.hass.async_add_executor_job(
try_connection, try_connection,
@ -178,20 +180,22 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow):
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
fields = OrderedDict() fields = OrderedDict()
fields[vol.Required(CONF_BROKER, default=current_config[CONF_BROKER])] = str current_broker = current_config.get(CONF_BROKER, yaml_config.get(CONF_BROKER))
fields[vol.Required(CONF_PORT, default=current_config[CONF_PORT])] = vol.Coerce( current_port = current_config.get(CONF_PORT, yaml_config.get(CONF_PORT))
int current_user = current_config.get(CONF_USERNAME, yaml_config.get(CONF_USERNAME))
) current_pass = current_config.get(CONF_PASSWORD, yaml_config.get(CONF_PASSWORD))
fields[vol.Required(CONF_BROKER, default=current_broker)] = str
fields[vol.Required(CONF_PORT, default=current_port)] = vol.Coerce(int)
fields[ fields[
vol.Optional( vol.Optional(
CONF_USERNAME, CONF_USERNAME,
description={"suggested_value": current_config.get(CONF_USERNAME)}, description={"suggested_value": current_user},
) )
] = str ] = str
fields[ fields[
vol.Optional( vol.Optional(
CONF_PASSWORD, CONF_PASSWORD,
description={"suggested_value": current_config.get(CONF_PASSWORD)}, description={"suggested_value": current_pass},
) )
] = str ] = str
@ -205,6 +209,7 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow):
"""Manage the MQTT options.""" """Manage the MQTT options."""
errors = {} errors = {}
current_config = self.config_entry.data current_config = self.config_entry.data
yaml_config = self.hass.data.get(DATA_MQTT_CONFIG, {})
options_config = {} options_config = {}
if user_input is not None: if user_input is not None:
bad_birth = False bad_birth = False
@ -253,16 +258,24 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow):
) )
return self.async_create_entry(title="", data=None) return self.async_create_entry(title="", data=None)
birth = {**DEFAULT_BIRTH, **current_config.get(CONF_BIRTH_MESSAGE, {})} birth = {
will = {**DEFAULT_WILL, **current_config.get(CONF_WILL_MESSAGE, {})} **DEFAULT_BIRTH,
**current_config.get(
CONF_BIRTH_MESSAGE, yaml_config.get(CONF_BIRTH_MESSAGE, {})
),
}
will = {
**DEFAULT_WILL,
**current_config.get(
CONF_WILL_MESSAGE, yaml_config.get(CONF_WILL_MESSAGE, {})
),
}
discovery = current_config.get(
CONF_DISCOVERY, yaml_config.get(CONF_DISCOVERY, DEFAULT_DISCOVERY)
)
fields = OrderedDict() fields = OrderedDict()
fields[ fields[vol.Optional(CONF_DISCOVERY, default=discovery)] = bool
vol.Optional(
CONF_DISCOVERY,
default=current_config.get(CONF_DISCOVERY, DEFAULT_DISCOVERY),
)
] = bool
# Birth message is disabled if CONF_BIRTH_MESSAGE = {} # Birth message is disabled if CONF_BIRTH_MESSAGE = {}
fields[ fields[

View File

@ -17,6 +17,8 @@ CONF_RETAIN = ATTR_RETAIN
CONF_STATE_TOPIC = "state_topic" CONF_STATE_TOPIC = "state_topic"
CONF_WILL_MESSAGE = "will_message" CONF_WILL_MESSAGE = "will_message"
DATA_MQTT_CONFIG = "mqtt_config"
DEFAULT_PREFIX = "homeassistant" DEFAULT_PREFIX = "homeassistant"
DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status" DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status"
DEFAULT_DISCOVERY = False DEFAULT_DISCOVERY = False

View File

@ -20,4 +20,5 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if entry and import_config.items() != entry.data.items(): if entry and import_config.items() != entry.data.items():
self.hass.config_entries.async_update_entry(entry, data=import_config) self.hass.config_entries.async_update_entry(entry, data=import_config)
return self.async_abort(reason="already_configured") return self.async_abort(reason="already_configured")
self._abort_if_unique_id_configured()
return self.async_create_entry(title="RFXTRX", data=import_config) return self.async_create_entry(title="RFXTRX", data=import_config)

View File

@ -3,6 +3,6 @@
"name": "Vera", "name": "Vera",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/vera", "documentation": "https://www.home-assistant.io/integrations/vera",
"requirements": ["pyvera==0.3.9"], "requirements": ["pyvera==0.3.10"],
"codeowners": ["@vangorra"] "codeowners": ["@vangorra"]
} }

View File

@ -8,8 +8,8 @@
"pyserial==3.4", "pyserial==3.4",
"zha-quirks==0.0.44", "zha-quirks==0.0.44",
"zigpy-cc==0.5.2", "zigpy-cc==0.5.2",
"zigpy-deconz==0.9.2", "zigpy-deconz==0.10.0",
"zigpy==0.23.2", "zigpy==0.24.1",
"zigpy-xbee==0.13.0", "zigpy-xbee==0.13.0",
"zigpy-zigate==0.6.2", "zigpy-zigate==0.6.2",
"zigpy-znp==0.1.1" "zigpy-znp==0.1.1"

View File

@ -1,7 +1,7 @@
"""Constants used by Home Assistant components.""" """Constants used by Home Assistant components."""
MAJOR_VERSION = 0 MAJOR_VERSION = 0
MINOR_VERSION = 115 MINOR_VERSION = 115
PATCH_VERSION = "1" PATCH_VERSION = "2"
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER = (3, 7, 1) REQUIRED_PYTHON_VER = (3, 7, 1)

View File

@ -13,7 +13,7 @@ defusedxml==0.6.0
distro==1.5.0 distro==1.5.0
emoji==0.5.4 emoji==0.5.4
hass-nabucasa==0.37.0 hass-nabucasa==0.37.0
home-assistant-frontend==20200917.1 home-assistant-frontend==20200918.0
importlib-metadata==1.6.0;python_version<'3.8' importlib-metadata==1.6.0;python_version<'3.8'
jinja2>=2.11.2 jinja2>=2.11.2
netdisco==2.8.2 netdisco==2.8.2

View File

@ -717,7 +717,7 @@ ha-philipsjs==0.0.8
habitipy==0.2.0 habitipy==0.2.0
# homeassistant.components.hangouts # homeassistant.components.hangouts
hangups==0.4.10 hangups==0.4.11
# homeassistant.components.cloud # homeassistant.components.cloud
hass-nabucasa==0.37.0 hass-nabucasa==0.37.0
@ -747,7 +747,7 @@ hole==0.5.1
holidays==0.10.3 holidays==0.10.3
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20200917.1 home-assistant-frontend==20200918.0
# homeassistant.components.zwave # homeassistant.components.zwave
homeassistant-pyozw==0.1.10 homeassistant-pyozw==0.1.10
@ -1825,7 +1825,7 @@ pyuptimerobot==0.0.5
# pyuserinput==0.1.11 # pyuserinput==0.1.11
# homeassistant.components.vera # homeassistant.components.vera
pyvera==0.3.9 pyvera==0.3.10
# homeassistant.components.versasense # homeassistant.components.versasense
pyversasense==0.0.6 pyversasense==0.0.6
@ -2302,7 +2302,7 @@ ziggo-mediabox-xl==1.1.0
zigpy-cc==0.5.2 zigpy-cc==0.5.2
# homeassistant.components.zha # homeassistant.components.zha
zigpy-deconz==0.9.2 zigpy-deconz==0.10.0
# homeassistant.components.zha # homeassistant.components.zha
zigpy-xbee==0.13.0 zigpy-xbee==0.13.0
@ -2314,7 +2314,7 @@ zigpy-zigate==0.6.2
zigpy-znp==0.1.1 zigpy-znp==0.1.1
# homeassistant.components.zha # homeassistant.components.zha
zigpy==0.23.2 zigpy==0.24.1
# homeassistant.components.zoneminder # homeassistant.components.zoneminder
zm-py==0.4.0 zm-py==0.4.0

View File

@ -349,7 +349,7 @@ griddypower==0.1.0
ha-ffmpeg==2.0 ha-ffmpeg==2.0
# homeassistant.components.hangouts # homeassistant.components.hangouts
hangups==0.4.10 hangups==0.4.11
# homeassistant.components.cloud # homeassistant.components.cloud
hass-nabucasa==0.37.0 hass-nabucasa==0.37.0
@ -370,7 +370,7 @@ hole==0.5.1
holidays==0.10.3 holidays==0.10.3
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20200917.1 home-assistant-frontend==20200918.0
# homeassistant.components.zwave # homeassistant.components.zwave
homeassistant-pyozw==0.1.10 homeassistant-pyozw==0.1.10
@ -854,7 +854,7 @@ pytraccar==0.9.0
pytradfri[async]==7.0.2 pytradfri[async]==7.0.2
# homeassistant.components.vera # homeassistant.components.vera
pyvera==0.3.9 pyvera==0.3.10
# homeassistant.components.vesync # homeassistant.components.vesync
pyvesync==1.1.0 pyvesync==1.1.0
@ -1062,7 +1062,7 @@ zha-quirks==0.0.44
zigpy-cc==0.5.2 zigpy-cc==0.5.2
# homeassistant.components.zha # homeassistant.components.zha
zigpy-deconz==0.9.2 zigpy-deconz==0.10.0
# homeassistant.components.zha # homeassistant.components.zha
zigpy-xbee==0.13.0 zigpy-xbee==0.13.0
@ -1074,4 +1074,4 @@ zigpy-zigate==0.6.2
zigpy-znp==0.1.1 zigpy-znp==0.1.1
# homeassistant.components.zha # homeassistant.components.zha
zigpy==0.23.2 zigpy==0.24.1

View File

@ -56,6 +56,16 @@ BROADLINK_DEVICES = {
20025, 20025,
5, 5,
), ),
"Kitchen": ( # Not supported.
"192.168.0.64",
"34ea34b61d2c",
"LB1",
"Broadlink",
"SmartBulb",
0x504E,
57,
5,
),
} }

View File

@ -12,13 +12,16 @@ from . import get_device
from tests.async_mock import call, patch from tests.async_mock import call, patch
DEVICE_DISCOVERY = "homeassistant.components.broadlink.config_flow.blk.discover"
DEVICE_FACTORY = "homeassistant.components.broadlink.config_flow.blk.gendevice"
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def broadlink_setup_fixture(): def broadlink_setup_fixture():
"""Mock broadlink entry setup.""" """Mock broadlink entry setup."""
with patch( with patch(
"homeassistant.components.broadlink.async_setup_entry", return_value=True "homeassistant.components.broadlink.async_setup", return_value=True
): ), patch("homeassistant.components.broadlink.async_setup_entry", return_value=True):
yield yield
@ -38,7 +41,7 @@ async def test_flow_user_works(hass):
assert result["step_id"] == "user" assert result["step_id"] == "user"
assert result["errors"] == {} assert result["errors"] == {}
with patch("broadlink.discover", return_value=[mock_api]) as mock_discover: with patch(DEVICE_DISCOVERY, return_value=[mock_api]) as mock_discover:
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -69,7 +72,7 @@ async def test_flow_user_already_in_progress(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[device.get_mock_api()]): with patch(DEVICE_DISCOVERY, return_value=[device.get_mock_api()]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -79,7 +82,7 @@ async def test_flow_user_already_in_progress(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[device.get_mock_api()]): with patch(DEVICE_DISCOVERY, return_value=[device.get_mock_api()]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -106,7 +109,7 @@ async def test_flow_user_mac_already_configured(hass):
device.timeout = 20 device.timeout = 20
mock_api = device.get_mock_api() mock_api = device.get_mock_api()
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -125,7 +128,7 @@ async def test_flow_user_invalid_ip_address(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", side_effect=OSError(errno.EINVAL, None)): with patch(DEVICE_DISCOVERY, side_effect=OSError(errno.EINVAL, None)):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": "0.0.0.1"}, {"host": "0.0.0.1"},
@ -142,7 +145,7 @@ async def test_flow_user_invalid_hostname(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", side_effect=OSError(socket.EAI_NONAME, None)): with patch(DEVICE_DISCOVERY, side_effect=OSError(socket.EAI_NONAME, None)):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": "pancakemaster.local"}, {"host": "pancakemaster.local"},
@ -161,7 +164,7 @@ async def test_flow_user_device_not_found(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[]): with patch(DEVICE_DISCOVERY, return_value=[]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host}, {"host": device.host},
@ -172,13 +175,32 @@ async def test_flow_user_device_not_found(hass):
assert result["errors"] == {"base": "cannot_connect"} assert result["errors"] == {"base": "cannot_connect"}
async def test_flow_user_device_not_supported(hass):
"""Test we handle a device not supported in the user step."""
device = get_device("Kitchen")
mock_api = device.get_mock_api()
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"host": device.host},
)
assert result["type"] == "abort"
assert result["reason"] == "not_supported"
async def test_flow_user_network_unreachable(hass): async def test_flow_user_network_unreachable(hass):
"""Test we handle a network unreachable in the user step.""" """Test we handle a network unreachable in the user step."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", side_effect=OSError(errno.ENETUNREACH, None)): with patch(DEVICE_DISCOVERY, side_effect=OSError(errno.ENETUNREACH, None)):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": "192.168.1.32"}, {"host": "192.168.1.32"},
@ -195,7 +217,7 @@ async def test_flow_user_os_error(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", side_effect=OSError()): with patch(DEVICE_DISCOVERY, side_effect=OSError()):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": "192.168.1.32"}, {"host": "192.168.1.32"},
@ -216,7 +238,7 @@ async def test_flow_auth_authentication_error(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -237,7 +259,7 @@ async def test_flow_auth_device_offline(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host}, {"host": device.host},
@ -258,7 +280,7 @@ async def test_flow_auth_firmware_error(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host}, {"host": device.host},
@ -279,7 +301,7 @@ async def test_flow_auth_network_unreachable(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host}, {"host": device.host},
@ -300,7 +322,7 @@ async def test_flow_auth_os_error(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host}, {"host": device.host},
@ -321,13 +343,13 @@ async def test_flow_reset_works(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
) )
with patch("broadlink.discover", return_value=[device.get_mock_api()]): with patch(DEVICE_DISCOVERY, return_value=[device.get_mock_api()]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -353,7 +375,7 @@ async def test_flow_unlock_works(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -392,7 +414,7 @@ async def test_flow_unlock_device_offline(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -419,7 +441,7 @@ async def test_flow_unlock_firmware_error(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -446,7 +468,7 @@ async def test_flow_unlock_network_unreachable(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -473,7 +495,7 @@ async def test_flow_unlock_os_error(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -499,7 +521,7 @@ async def test_flow_do_not_unlock(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -527,7 +549,7 @@ async def test_flow_import_works(hass):
device = get_device("Living Room") device = get_device("Living Room")
mock_api = device.get_mock_api() mock_api = device.get_mock_api()
with patch("broadlink.discover", return_value=[mock_api]) as mock_discover: with patch(DEVICE_DISCOVERY, return_value=[mock_api]) as mock_discover:
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -558,12 +580,12 @@ async def test_flow_import_already_in_progress(hass):
device = get_device("Living Room") device = get_device("Living Room")
data = {"host": device.host} data = {"host": device.host}
with patch("broadlink.discover", return_value=[device.get_mock_api()]): with patch(DEVICE_DISCOVERY, return_value=[device.get_mock_api()]):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=data DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=data
) )
with patch("broadlink.discover", return_value=[device.get_mock_api()]): with patch(DEVICE_DISCOVERY, return_value=[device.get_mock_api()]):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=data DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=data
) )
@ -579,7 +601,7 @@ async def test_flow_import_host_already_configured(hass):
mock_entry.add_to_hass(hass) mock_entry.add_to_hass(hass)
mock_api = device.get_mock_api() mock_api = device.get_mock_api()
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -602,7 +624,7 @@ async def test_flow_import_mac_already_configured(hass):
device.host = "192.168.1.16" device.host = "192.168.1.16"
mock_api = device.get_mock_api() mock_api = device.get_mock_api()
with patch("broadlink.discover", return_value=[mock_api]): with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -620,7 +642,7 @@ async def test_flow_import_mac_already_configured(hass):
async def test_flow_import_device_not_found(hass): async def test_flow_import_device_not_found(hass):
"""Test we handle a device not found in the import step.""" """Test we handle a device not found in the import step."""
with patch("broadlink.discover", return_value=[]): with patch(DEVICE_DISCOVERY, return_value=[]):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -631,9 +653,25 @@ async def test_flow_import_device_not_found(hass):
assert result["reason"] == "cannot_connect" assert result["reason"] == "cannot_connect"
async def test_flow_import_device_not_supported(hass):
"""Test we handle a device not supported in the import step."""
device = get_device("Kitchen")
mock_api = device.get_mock_api()
with patch(DEVICE_DISCOVERY, return_value=[mock_api]):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={"host": device.host},
)
assert result["type"] == "abort"
assert result["reason"] == "not_supported"
async def test_flow_import_invalid_ip_address(hass): async def test_flow_import_invalid_ip_address(hass):
"""Test we handle an invalid IP address in the import step.""" """Test we handle an invalid IP address in the import step."""
with patch("broadlink.discover", side_effect=OSError(errno.EINVAL, None)): with patch(DEVICE_DISCOVERY, side_effect=OSError(errno.EINVAL, None)):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -646,7 +684,7 @@ async def test_flow_import_invalid_ip_address(hass):
async def test_flow_import_invalid_hostname(hass): async def test_flow_import_invalid_hostname(hass):
"""Test we handle an invalid hostname in the import step.""" """Test we handle an invalid hostname in the import step."""
with patch("broadlink.discover", side_effect=OSError(socket.EAI_NONAME, None)): with patch(DEVICE_DISCOVERY, side_effect=OSError(socket.EAI_NONAME, None)):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -659,7 +697,7 @@ async def test_flow_import_invalid_hostname(hass):
async def test_flow_import_network_unreachable(hass): async def test_flow_import_network_unreachable(hass):
"""Test we handle a network unreachable in the import step.""" """Test we handle a network unreachable in the import step."""
with patch("broadlink.discover", side_effect=OSError(errno.ENETUNREACH, None)): with patch(DEVICE_DISCOVERY, side_effect=OSError(errno.ENETUNREACH, None)):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -672,7 +710,7 @@ async def test_flow_import_network_unreachable(hass):
async def test_flow_import_os_error(hass): async def test_flow_import_os_error(hass):
"""Test we handle an OS error in the import step.""" """Test we handle an OS error in the import step."""
with patch("broadlink.discover", side_effect=OSError()): with patch(DEVICE_DISCOVERY, side_effect=OSError()):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -692,7 +730,7 @@ async def test_flow_reauth_works(hass):
mock_api.auth.side_effect = blke.AuthenticationError() mock_api.auth.side_effect = blke.AuthenticationError()
data = {"name": device.name, **device.get_entry_data()} data = {"name": device.name, **device.get_entry_data()}
with patch("broadlink.gendevice", return_value=mock_api): with patch(DEVICE_FACTORY, return_value=mock_api):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=data DOMAIN, context={"source": "reauth"}, data=data
) )
@ -702,7 +740,7 @@ async def test_flow_reauth_works(hass):
mock_api = device.get_mock_api() mock_api = device.get_mock_api()
with patch("broadlink.discover", return_value=[mock_api]) as mock_discover: with patch(DEVICE_DISCOVERY, return_value=[mock_api]) as mock_discover:
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -728,7 +766,7 @@ async def test_flow_reauth_invalid_host(hass):
mock_api.auth.side_effect = blke.AuthenticationError() mock_api.auth.side_effect = blke.AuthenticationError()
data = {"name": device.name, **device.get_entry_data()} data = {"name": device.name, **device.get_entry_data()}
with patch("broadlink.gendevice", return_value=mock_api): with patch(DEVICE_FACTORY, return_value=mock_api):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=data DOMAIN, context={"source": "reauth"}, data=data
) )
@ -736,7 +774,7 @@ async def test_flow_reauth_invalid_host(hass):
device.mac = get_device("Office").mac device.mac = get_device("Office").mac
mock_api = device.get_mock_api() mock_api = device.get_mock_api()
with patch("broadlink.discover", return_value=[mock_api]) as mock_discover: with patch(DEVICE_DISCOVERY, return_value=[mock_api]) as mock_discover:
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},
@ -762,7 +800,7 @@ async def test_flow_reauth_valid_host(hass):
mock_api.auth.side_effect = blke.AuthenticationError() mock_api.auth.side_effect = blke.AuthenticationError()
data = {"name": device.name, **device.get_entry_data()} data = {"name": device.name, **device.get_entry_data()}
with patch("broadlink.gendevice", return_value=mock_api): with patch(DEVICE_FACTORY, return_value=mock_api):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=data DOMAIN, context={"source": "reauth"}, data=data
) )
@ -770,7 +808,7 @@ async def test_flow_reauth_valid_host(hass):
device.host = "192.168.1.128" device.host = "192.168.1.128"
mock_api = device.get_mock_api() mock_api = device.get_mock_api()
with patch("broadlink.discover", return_value=[mock_api]) as mock_discover: with patch(DEVICE_DISCOVERY, return_value=[mock_api]) as mock_discover:
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": device.host, "timeout": device.timeout}, {"host": device.host, "timeout": device.timeout},