diff --git a/homeassistant/components/broadlink/config_flow.py b/homeassistant/components/broadlink/config_flow.py index 284a9bffe19..4dfc80c6fe9 100644 --- a/homeassistant/components/broadlink/config_flow.py +++ b/homeassistant/components/broadlink/config_flow.py @@ -11,7 +11,7 @@ from broadlink.exceptions import ( ) 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.helpers import config_validation as cv @@ -20,6 +20,7 @@ from .const import ( # pylint: disable=unused-import DEFAULT_PORT, DEFAULT_TIMEOUT, DOMAIN, + DOMAINS_AND_TYPES, ) 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): """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( device.mac.hex(), raise_on_progress=raise_on_progress ) diff --git a/homeassistant/components/broadlink/strings.json b/homeassistant/components/broadlink/strings.json index 44cb1801ede..d17c639469a 100644 --- a/homeassistant/components/broadlink/strings.json +++ b/homeassistant/components/broadlink/strings.json @@ -35,6 +35,7 @@ "already_in_progress": "There is already a configuration flow in progress for this device", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_host": "Invalid hostname or IP address", + "not_supported": "Device not supported", "unknown": "[%key:common::config_flow::error::unknown%]" }, "error": { diff --git a/homeassistant/components/broadlink/translations/en.json b/homeassistant/components/broadlink/translations/en.json index fa3feb88008..bd8dfd0c403 100644 --- a/homeassistant/components/broadlink/translations/en.json +++ b/homeassistant/components/broadlink/translations/en.json @@ -5,6 +5,7 @@ "already_in_progress": "There is already a configuration flow in progress for this device", "cannot_connect": "Failed to connect", "invalid_host": "Invalid hostname or IP address", + "not_supported": "Device not supported", "unknown": "Unexpected error" }, "error": { diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 96d2a51cb09..4368bff8d0f 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20200917.1"], + "requirements": ["home-assistant-frontend==20200918.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/components/hangouts/manifest.json b/homeassistant/components/hangouts/manifest.json index 80eed48cde9..a2605124dc4 100644 --- a/homeassistant/components/hangouts/manifest.json +++ b/homeassistant/components/hangouts/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/hangouts", "requirements": [ - "hangups==0.4.10" + "hangups==0.4.11" ], "codeowners": [] } diff --git a/homeassistant/components/kodi/config_flow.py b/homeassistant/components/kodi/config_flow.py index f10dcbb2d28..067ee8be476 100644 --- a/homeassistant/components/kodi/config_flow.py +++ b/homeassistant/components/kodi/config_flow.py @@ -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: await validate_http(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") 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() async def async_step_discovery_confirm(self, user_input=None): diff --git a/homeassistant/components/modbus/switch.py b/homeassistant/components/modbus/switch.py index 8037d926ef1..c238e105659 100644 --- a/homeassistant/components/modbus/switch.py +++ b/homeassistant/components/modbus/switch.py @@ -171,7 +171,7 @@ class ModbusCoilSwitch(ToggleEntity, RestoreEntity): return self._available = True - return bool(result.bits[0]) + return bool(result.bits[coil]) def _write_coil(self, coil, value): """Write coil using the Modbus hub slave.""" diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index d9bf1bbadfa..bafbead96d6 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -60,6 +60,7 @@ from .const import ( CONF_RETAIN, CONF_STATE_TOPIC, CONF_WILL_MESSAGE, + DATA_MQTT_CONFIG, DEFAULT_BIRTH, DEFAULT_DISCOVERY, DEFAULT_PAYLOAD_AVAILABLE, @@ -88,7 +89,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = "mqtt" DATA_MQTT = "mqtt" -DATA_MQTT_CONFIG = "mqtt_config" SERVICE_PUBLISH = "publish" SERVICE_DUMP = "dump" diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 8b1c350323c..5c4016437a6 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -24,6 +24,7 @@ from .const import ( CONF_BROKER, CONF_DISCOVERY, CONF_WILL_MESSAGE, + DATA_MQTT_CONFIG, DEFAULT_BIRTH, DEFAULT_DISCOVERY, DEFAULT_WILL, @@ -162,6 +163,7 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow): """Manage the MQTT options.""" errors = {} current_config = self.config_entry.data + yaml_config = self.hass.data.get(DATA_MQTT_CONFIG, {}) if user_input is not None: can_connect = await self.hass.async_add_executor_job( try_connection, @@ -178,20 +180,22 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow): errors["base"] = "cannot_connect" fields = OrderedDict() - fields[vol.Required(CONF_BROKER, default=current_config[CONF_BROKER])] = str - fields[vol.Required(CONF_PORT, default=current_config[CONF_PORT])] = vol.Coerce( - int - ) + current_broker = current_config.get(CONF_BROKER, yaml_config.get(CONF_BROKER)) + current_port = current_config.get(CONF_PORT, yaml_config.get(CONF_PORT)) + 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[ vol.Optional( CONF_USERNAME, - description={"suggested_value": current_config.get(CONF_USERNAME)}, + description={"suggested_value": current_user}, ) ] = str fields[ vol.Optional( CONF_PASSWORD, - description={"suggested_value": current_config.get(CONF_PASSWORD)}, + description={"suggested_value": current_pass}, ) ] = str @@ -205,6 +209,7 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow): """Manage the MQTT options.""" errors = {} current_config = self.config_entry.data + yaml_config = self.hass.data.get(DATA_MQTT_CONFIG, {}) options_config = {} if user_input is not None: bad_birth = False @@ -253,16 +258,24 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow): ) return self.async_create_entry(title="", data=None) - birth = {**DEFAULT_BIRTH, **current_config.get(CONF_BIRTH_MESSAGE, {})} - will = {**DEFAULT_WILL, **current_config.get(CONF_WILL_MESSAGE, {})} + birth = { + **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[ - vol.Optional( - CONF_DISCOVERY, - default=current_config.get(CONF_DISCOVERY, DEFAULT_DISCOVERY), - ) - ] = bool + fields[vol.Optional(CONF_DISCOVERY, default=discovery)] = bool # Birth message is disabled if CONF_BIRTH_MESSAGE = {} fields[ diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 7ea6d9d348b..5ab3f756311 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -17,6 +17,8 @@ CONF_RETAIN = ATTR_RETAIN CONF_STATE_TOPIC = "state_topic" CONF_WILL_MESSAGE = "will_message" +DATA_MQTT_CONFIG = "mqtt_config" + DEFAULT_PREFIX = "homeassistant" DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status" DEFAULT_DISCOVERY = False diff --git a/homeassistant/components/rfxtrx/config_flow.py b/homeassistant/components/rfxtrx/config_flow.py index 287e1ec4baf..596f1d0b5e9 100644 --- a/homeassistant/components/rfxtrx/config_flow.py +++ b/homeassistant/components/rfxtrx/config_flow.py @@ -20,4 +20,5 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if entry and import_config.items() != entry.data.items(): self.hass.config_entries.async_update_entry(entry, data=import_config) return self.async_abort(reason="already_configured") + self._abort_if_unique_id_configured() return self.async_create_entry(title="RFXTRX", data=import_config) diff --git a/homeassistant/components/vera/manifest.json b/homeassistant/components/vera/manifest.json index a6afcce65b3..b41d289e6b3 100644 --- a/homeassistant/components/vera/manifest.json +++ b/homeassistant/components/vera/manifest.json @@ -3,6 +3,6 @@ "name": "Vera", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/vera", - "requirements": ["pyvera==0.3.9"], + "requirements": ["pyvera==0.3.10"], "codeowners": ["@vangorra"] } diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index c6b0fa78799..b03d4afd971 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -8,8 +8,8 @@ "pyserial==3.4", "zha-quirks==0.0.44", "zigpy-cc==0.5.2", - "zigpy-deconz==0.9.2", - "zigpy==0.23.2", + "zigpy-deconz==0.10.0", + "zigpy==0.24.1", "zigpy-xbee==0.13.0", "zigpy-zigate==0.6.2", "zigpy-znp==0.1.1" diff --git a/homeassistant/const.py b/homeassistant/const.py index 79d91ef7cb2..818f4a715a4 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 115 -PATCH_VERSION = "1" +PATCH_VERSION = "2" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index e5978245bac..0f88f88469b 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ defusedxml==0.6.0 distro==1.5.0 emoji==0.5.4 hass-nabucasa==0.37.0 -home-assistant-frontend==20200917.1 +home-assistant-frontend==20200918.0 importlib-metadata==1.6.0;python_version<'3.8' jinja2>=2.11.2 netdisco==2.8.2 diff --git a/requirements_all.txt b/requirements_all.txt index cc83c47950f..6aea10a43cf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -717,7 +717,7 @@ ha-philipsjs==0.0.8 habitipy==0.2.0 # homeassistant.components.hangouts -hangups==0.4.10 +hangups==0.4.11 # homeassistant.components.cloud hass-nabucasa==0.37.0 @@ -747,7 +747,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20200917.1 +home-assistant-frontend==20200918.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 @@ -1825,7 +1825,7 @@ pyuptimerobot==0.0.5 # pyuserinput==0.1.11 # homeassistant.components.vera -pyvera==0.3.9 +pyvera==0.3.10 # homeassistant.components.versasense pyversasense==0.0.6 @@ -2302,7 +2302,7 @@ ziggo-mediabox-xl==1.1.0 zigpy-cc==0.5.2 # homeassistant.components.zha -zigpy-deconz==0.9.2 +zigpy-deconz==0.10.0 # homeassistant.components.zha zigpy-xbee==0.13.0 @@ -2314,7 +2314,7 @@ zigpy-zigate==0.6.2 zigpy-znp==0.1.1 # homeassistant.components.zha -zigpy==0.23.2 +zigpy==0.24.1 # homeassistant.components.zoneminder zm-py==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 84f2f0c9a64..d5bd1f036b2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -349,7 +349,7 @@ griddypower==0.1.0 ha-ffmpeg==2.0 # homeassistant.components.hangouts -hangups==0.4.10 +hangups==0.4.11 # homeassistant.components.cloud hass-nabucasa==0.37.0 @@ -370,7 +370,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20200917.1 +home-assistant-frontend==20200918.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 @@ -854,7 +854,7 @@ pytraccar==0.9.0 pytradfri[async]==7.0.2 # homeassistant.components.vera -pyvera==0.3.9 +pyvera==0.3.10 # homeassistant.components.vesync pyvesync==1.1.0 @@ -1062,7 +1062,7 @@ zha-quirks==0.0.44 zigpy-cc==0.5.2 # homeassistant.components.zha -zigpy-deconz==0.9.2 +zigpy-deconz==0.10.0 # homeassistant.components.zha zigpy-xbee==0.13.0 @@ -1074,4 +1074,4 @@ zigpy-zigate==0.6.2 zigpy-znp==0.1.1 # homeassistant.components.zha -zigpy==0.23.2 +zigpy==0.24.1 diff --git a/tests/components/broadlink/__init__.py b/tests/components/broadlink/__init__.py index 95a2ef97c6f..86756c922f1 100644 --- a/tests/components/broadlink/__init__.py +++ b/tests/components/broadlink/__init__.py @@ -56,6 +56,16 @@ BROADLINK_DEVICES = { 20025, 5, ), + "Kitchen": ( # Not supported. + "192.168.0.64", + "34ea34b61d2c", + "LB1", + "Broadlink", + "SmartBulb", + 0x504E, + 57, + 5, + ), } diff --git a/tests/components/broadlink/test_config_flow.py b/tests/components/broadlink/test_config_flow.py index 4089c551ff5..a7660b03da5 100644 --- a/tests/components/broadlink/test_config_flow.py +++ b/tests/components/broadlink/test_config_flow.py @@ -12,13 +12,16 @@ from . import get_device 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) def broadlink_setup_fixture(): """Mock broadlink entry setup.""" 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 @@ -38,7 +41,7 @@ async def test_flow_user_works(hass): assert result["step_id"] == "user" 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["flow_id"], {"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} ) - 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["flow_id"], {"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} ) - 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["flow_id"], {"host": device.host, "timeout": device.timeout}, @@ -106,7 +109,7 @@ async def test_flow_user_mac_already_configured(hass): device.timeout = 20 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["flow_id"], {"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} ) - 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["flow_id"], {"host": "0.0.0.1"}, @@ -142,7 +145,7 @@ async def test_flow_user_invalid_hostname(hass): 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["flow_id"], {"host": "pancakemaster.local"}, @@ -161,7 +164,7 @@ async def test_flow_user_device_not_found(hass): 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["flow_id"], {"host": device.host}, @@ -172,13 +175,32 @@ async def test_flow_user_device_not_found(hass): 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): """Test we handle a network unreachable in the user step.""" result = await hass.config_entries.flow.async_init( 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["flow_id"], {"host": "192.168.1.32"}, @@ -195,7 +217,7 @@ async def test_flow_user_os_error(hass): 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["flow_id"], {"host": "192.168.1.32"}, @@ -216,7 +238,7 @@ async def test_flow_auth_authentication_error(hass): 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["flow_id"], {"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} ) - 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["flow_id"], {"host": device.host}, @@ -258,7 +280,7 @@ async def test_flow_auth_firmware_error(hass): 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["flow_id"], {"host": device.host}, @@ -279,7 +301,7 @@ async def test_flow_auth_network_unreachable(hass): 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["flow_id"], {"host": device.host}, @@ -300,7 +322,7 @@ async def test_flow_auth_os_error(hass): 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["flow_id"], {"host": device.host}, @@ -321,13 +343,13 @@ async def test_flow_reset_works(hass): 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["flow_id"], {"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["flow_id"], {"host": device.host, "timeout": device.timeout}, @@ -353,7 +375,7 @@ async def test_flow_unlock_works(hass): 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["flow_id"], {"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} ) - 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["flow_id"], {"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} ) - 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["flow_id"], {"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} ) - 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["flow_id"], {"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} ) - 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["flow_id"], {"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} ) - 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["flow_id"], {"host": device.host, "timeout": device.timeout}, @@ -527,7 +549,7 @@ async def test_flow_import_works(hass): device = get_device("Living Room") 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( DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, @@ -558,12 +580,12 @@ async def test_flow_import_already_in_progress(hass): device = get_device("Living Room") 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( 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( 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_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( DOMAIN, 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" 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( DOMAIN, 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): """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( DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, @@ -631,9 +653,25 @@ async def test_flow_import_device_not_found(hass): 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): """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( DOMAIN, 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): """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( DOMAIN, 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): """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( DOMAIN, 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): """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( DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, @@ -692,7 +730,7 @@ async def test_flow_reauth_works(hass): mock_api.auth.side_effect = blke.AuthenticationError() 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( DOMAIN, context={"source": "reauth"}, data=data ) @@ -702,7 +740,7 @@ async def test_flow_reauth_works(hass): 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["flow_id"], {"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() 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( DOMAIN, context={"source": "reauth"}, data=data ) @@ -736,7 +774,7 @@ async def test_flow_reauth_invalid_host(hass): device.mac = get_device("Office").mac 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["flow_id"], {"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() 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( DOMAIN, context={"source": "reauth"}, data=data ) @@ -770,7 +808,7 @@ async def test_flow_reauth_valid_host(hass): device.host = "192.168.1.128" 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["flow_id"], {"host": device.host, "timeout": device.timeout},