diff --git a/.coveragerc b/.coveragerc index 9b5167eb68fbf..d6fa8e84b409a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -648,10 +648,6 @@ omit = homeassistant/components/kodi/const.py homeassistant/components/kodi/media_player.py homeassistant/components/kodi/notify.py - homeassistant/components/konnected/__init__.py - homeassistant/components/konnected/handlers.py - homeassistant/components/konnected/panel.py - homeassistant/components/konnected/switch.py homeassistant/components/kostal_plenticore/__init__.py homeassistant/components/kostal_plenticore/const.py homeassistant/components/kostal_plenticore/helper.py diff --git a/homeassistant/components/konnected/__init__.py b/homeassistant/components/konnected/__init__.py index 620ed12ac54f1..f1c01b42248ab 100644 --- a/homeassistant/components/konnected/__init__.py +++ b/homeassistant/components/konnected/__init__.py @@ -70,10 +70,10 @@ def ensure_pin(value): """Check if valid pin and coerce to string.""" - if value is None: + if value is None: # pragma: no cover raise vol.Invalid("pin value is None") - if PIN_TO_ZONE.get(str(value)) is None: + if PIN_TO_ZONE.get(str(value)) is None: # pragma: no cover raise vol.Invalid("pin not valid") return str(value) @@ -81,10 +81,10 @@ def ensure_pin(value): def ensure_zone(value): """Check if valid zone and coerce to string.""" - if value is None: + if value is None: # pragma: no cover raise vol.Invalid("zone value is None") - if str(value) not in ZONES is None: + if str(value) not in ZONES is None: # pragma: no cover raise vol.Invalid("zone not valid") return str(value) @@ -329,7 +329,7 @@ async def update_sensor(self, request: Request, device_id) -> Response: try: # Konnected 2.2.0 and above supports JSON payloads payload = await request.json() - except json.decoder.JSONDecodeError: + except json.decoder.JSONDecodeError: # pragma: no cover _LOGGER.error( "Your Konnected device software may be out of " "date. Visit https://help.konnected.io for " diff --git a/homeassistant/components/konnected/config_flow.py b/homeassistant/components/konnected/config_flow.py index fcf94a38c18ad..b02d6b18719d4 100644 --- a/homeassistant/components/konnected/config_flow.py +++ b/homeassistant/components/konnected/config_flow.py @@ -179,24 +179,6 @@ def __init__(self) -> None: self.data: dict[str, Any] = {} self.options = OPTIONS_SCHEMA({CONF_IO: {}}) - async def async_gen_config(self, host, port): - """Populate self.data based on panel status. - - This will raise CannotConnect if an error occurs - """ - self.data[CONF_HOST] = host - self.data[CONF_PORT] = port - try: - status = await get_status(self.hass, host, port) - self.data[CONF_ID] = status.get("chipId", status["mac"].replace(":", "")) - except (CannotConnect, KeyError) as err: - raise CannotConnect from err - else: - self.data[CONF_MODEL] = status.get("model", KONN_MODEL) - self.data[CONF_ACCESS_TOKEN] = "".join( - random.choices(f"{string.ascii_uppercase}{string.digits}", k=20) - ) - async def async_step_import(self, device_config): """Import a configuration.yaml config. @@ -499,7 +481,7 @@ async def async_step_options_io(self, user_input=None): errors=errors, ) - return self.async_abort(reason="not_konn_panel") + return self.async_abort(reason="not_konn_panel") # pragma: no cover async def async_step_options_io_ext(self, user_input=None): """Allow the user to configure the extended IO for pro.""" @@ -557,7 +539,7 @@ async def async_step_options_io_ext(self, user_input=None): errors=errors, ) - return self.async_abort(reason="not_konn_panel") + return self.async_abort(reason="not_konn_panel") # pragma: no cover async def async_step_options_binary(self, user_input=None): """Allow the user to configure the IO options for binary sensors.""" @@ -571,34 +553,6 @@ async def async_step_options_binary(self, user_input=None): self.io_cfg.pop(self.active_cfg) self.active_cfg = None - if self.active_cfg: - current_cfg = self.get_current_cfg(CONF_BINARY_SENSORS, self.active_cfg) - return self.async_show_form( - step_id="options_binary", - data_schema=vol.Schema( - { - vol.Required( - CONF_TYPE, - default=current_cfg.get( - CONF_TYPE, BinarySensorDeviceClass.DOOR - ), - ): DEVICE_CLASSES_SCHEMA, - vol.Optional( - CONF_NAME, default=current_cfg.get(CONF_NAME, vol.UNDEFINED) - ): str, - vol.Optional( - CONF_INVERSE, default=current_cfg.get(CONF_INVERSE, False) - ): bool, - } - ), - description_placeholders={ - "zone": f"Zone {self.active_cfg}" - if len(self.active_cfg) < 3 - else self.active_cfg.upper - }, - errors=errors, - ) - # find the next unconfigured binary sensor for key, value in self.io_cfg.items(): if value == CONF_IO_BIN: @@ -644,32 +598,6 @@ async def async_step_options_digital(self, user_input=None): self.io_cfg.pop(self.active_cfg) self.active_cfg = None - if self.active_cfg: - current_cfg = self.get_current_cfg(CONF_SENSORS, self.active_cfg) - return self.async_show_form( - step_id="options_digital", - data_schema=vol.Schema( - { - vol.Required( - CONF_TYPE, default=current_cfg.get(CONF_TYPE, "dht") - ): vol.All(vol.Lower, vol.In(["dht", "ds18b20"])), - vol.Optional( - CONF_NAME, default=current_cfg.get(CONF_NAME, vol.UNDEFINED) - ): str, - vol.Optional( - CONF_POLL_INTERVAL, - default=current_cfg.get(CONF_POLL_INTERVAL, 3), - ): vol.All(vol.Coerce(int), vol.Range(min=1)), - } - ), - description_placeholders={ - "zone": f"Zone {self.active_cfg}" - if len(self.active_cfg) < 3 - else self.active_cfg.upper() - }, - errors=errors, - ) - # find the next unconfigured digital sensor for key, value in self.io_cfg.items(): if value == CONF_IO_DIG: diff --git a/tests/components/konnected/test_config_flow.py b/tests/components/konnected/test_config_flow.py index 7397f03c1fc67..53a8a6435d3b9 100644 --- a/tests/components/konnected/test_config_flow.py +++ b/tests/components/konnected/test_config_flow.py @@ -3,7 +3,7 @@ import pytest -from homeassistant import config_entries +from homeassistant import config_entries, data_entry_flow from homeassistant.components import konnected, ssdp from homeassistant.components.konnected import config_flow @@ -31,7 +31,7 @@ async def test_flow_works(hass, mock_panel): result = await hass.config_entries.flow.async_init( config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "user" mock_panel.get_status.return_value = { @@ -41,7 +41,7 @@ async def test_flow_works(hass, mock_panel): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"port": 1234, "host": "1.2.3.4"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "confirm" assert result["description_placeholders"] == { "model": "Konnected Alarm Panel", @@ -53,7 +53,7 @@ async def test_flow_works(hass, mock_panel): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["data"]["host"] == "1.2.3.4" assert result["data"]["port"] == 1234 assert result["data"]["model"] == "Konnected" @@ -68,7 +68,7 @@ async def test_pro_flow_works(hass, mock_panel): result = await hass.config_entries.flow.async_init( config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "user" # pro uses chipId instead of MAC as unique id @@ -80,7 +80,7 @@ async def test_pro_flow_works(hass, mock_panel): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"port": 1234, "host": "1.2.3.4"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "confirm" assert result["description_placeholders"] == { "model": "Konnected Alarm Panel Pro", @@ -92,7 +92,7 @@ async def test_pro_flow_works(hass, mock_panel): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["data"]["host"] == "1.2.3.4" assert result["data"]["port"] == 1234 assert result["data"]["model"] == "Konnected Pro" @@ -124,7 +124,7 @@ async def test_ssdp(hass, mock_panel): ), ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "confirm" assert result["description_placeholders"] == { "model": "Konnected Alarm Panel", @@ -149,7 +149,7 @@ async def test_ssdp(hass, mock_panel): ), ) - assert result["type"] == "abort" + assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "cannot_connect" # Test abort if invalid data @@ -165,7 +165,7 @@ async def test_ssdp(hass, mock_panel): ), ) - assert result["type"] == "abort" + assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "unknown" # Test abort if invalid manufacturer @@ -183,7 +183,7 @@ async def test_ssdp(hass, mock_panel): ), ) - assert result["type"] == "abort" + assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "not_konn_panel" # Test abort if invalid model @@ -201,7 +201,7 @@ async def test_ssdp(hass, mock_panel): ), ) - assert result["type"] == "abort" + assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "not_konn_panel" # Test abort if already configured @@ -225,7 +225,7 @@ async def test_ssdp(hass, mock_panel): ), ) - assert result["type"] == "abort" + assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "already_configured" @@ -265,21 +265,21 @@ async def test_import_no_host_user_finish(hass, mock_panel): "id": "aabbccddeeff", }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "import_confirm" assert result["description_placeholders"]["id"] == "aabbccddeeff" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "user" # confirm user is prompted to enter host result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"host": "1.1.1.1", "port": 1234} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "confirm" assert result["description_placeholders"] == { "model": "Konnected Alarm Panel Pro", @@ -292,7 +292,51 @@ async def test_import_no_host_user_finish(hass, mock_panel): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + + +async def test_async_step_user_cant_connect(hass, mock_panel): + """Test importing a panel with no host info.""" + + with patch.object( + mock_panel, + "get_status", + side_effect=mock_panel.ClientError(), + ): + result = await hass.config_entries.flow.async_init( + config_flow.DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + "default_options": { + "blink": True, + "discovery": True, + "io": { + "1": "Disabled", + "10": "Disabled", + "11": "Disabled", + "12": "Disabled", + "2": "Disabled", + "3": "Disabled", + "4": "Disabled", + "5": "Disabled", + "6": "Disabled", + "7": "Disabled", + "8": "Disabled", + "9": "Disabled", + "alarm1": "Disabled", + "alarm2_out2": "Disabled", + "out": "Disabled", + "out1": "Disabled", + }, + }, + "id": "somechipid", + "host": "1.2.3.4", + "port": 1234, + "model": "Konnected Alarm Panel", + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" async def test_import_ssdp_host_user_finish(hass, mock_panel): @@ -332,7 +376,7 @@ async def test_import_ssdp_host_user_finish(hass, mock_panel): "id": "somechipid", }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "import_confirm" assert result["description_placeholders"]["id"] == "somechipid" @@ -350,13 +394,13 @@ async def test_import_ssdp_host_user_finish(hass, mock_panel): }, ), ) - assert ssdp_result["type"] == "abort" + assert ssdp_result["type"] == data_entry_flow.FlowResultType.ABORT assert ssdp_result["reason"] == "already_in_progress" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "confirm" assert result["description_placeholders"] == { "model": "Konnected Alarm Panel Pro", @@ -369,7 +413,7 @@ async def test_import_ssdp_host_user_finish(hass, mock_panel): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY async def test_ssdp_already_configured(hass, mock_panel): @@ -397,7 +441,7 @@ async def test_ssdp_already_configured(hass, mock_panel): }, ), ) - assert result["type"] == "abort" + assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "already_configured" @@ -477,7 +521,7 @@ async def test_ssdp_host_update(hass, mock_panel): }, ), ) - assert result["type"] == "abort" + assert result["type"] == data_entry_flow.FlowResultType.ABORT # confirm the host value was updated, access_token was not entry = hass.config_entries.async_entries(config_flow.DOMAIN)[0] @@ -535,13 +579,13 @@ async def test_import_existing_config(hass, mock_panel): } ), ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "confirm" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["data"] == { "host": "1.2.3.4", "port": 1234, @@ -663,7 +707,7 @@ async def test_import_existing_config_entry(hass, mock_panel): }, ) - assert result["type"] == "abort" + assert result["type"] == data_entry_flow.FlowResultType.ABORT # We should have updated the host info but not the access token assert len(hass.config_entries.async_entries("konnected")) == 1 @@ -715,13 +759,13 @@ async def test_import_pin_config(hass, mock_panel): } ), ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "confirm" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["data"] == { "host": "1.2.3.4", "port": 1234, @@ -800,7 +844,7 @@ async def test_option_flow(hass, mock_panel): result = await hass.config_entries.options.async_init( entry.entry_id, context={"source": "test"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_io" result = await hass.config_entries.options.async_configure( @@ -815,7 +859,7 @@ async def test_option_flow(hass, mock_panel): "out": "Switchable Output", }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_binary" assert result["description_placeholders"] == { "zone": "Zone 2", @@ -825,7 +869,7 @@ async def test_option_flow(hass, mock_panel): result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"type": "door"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_binary" assert result["description_placeholders"] == { "zone": "Zone 6", @@ -836,7 +880,7 @@ async def test_option_flow(hass, mock_panel): result["flow_id"], user_input={"type": "window", "name": "winder", "inverse": True}, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_digital" assert result["description_placeholders"] == { "zone": "Zone 3", @@ -846,7 +890,7 @@ async def test_option_flow(hass, mock_panel): result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"type": "dht"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_switch" assert result["description_placeholders"] == { "zone": "Zone 4", @@ -857,7 +901,7 @@ async def test_option_flow(hass, mock_panel): result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_switch" assert result["description_placeholders"] == { "zone": "OUT", @@ -877,7 +921,7 @@ async def test_option_flow(hass, mock_panel): }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_switch" assert result["description_placeholders"] == { "zone": "OUT", @@ -897,7 +941,7 @@ async def test_option_flow(hass, mock_panel): }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_misc" # make sure we enforce url format result = await hass.config_entries.options.async_configure( @@ -910,7 +954,7 @@ async def test_option_flow(hass, mock_panel): }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_misc" result = await hass.config_entries.options.async_configure( result["flow_id"], @@ -921,7 +965,7 @@ async def test_option_flow(hass, mock_panel): "api_host": "http://overridehost:1111", }, ) - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["data"] == { "io": { "2": "Binary Sensor", @@ -986,7 +1030,7 @@ async def test_option_flow_pro(hass, mock_panel): result = await hass.config_entries.options.async_init( entry.entry_id, context={"source": "test"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_io" result = await hass.config_entries.options.async_configure( @@ -1001,7 +1045,7 @@ async def test_option_flow_pro(hass, mock_panel): "7": "Digital Sensor", }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_io_ext" result = await hass.config_entries.options.async_configure( @@ -1017,14 +1061,14 @@ async def test_option_flow_pro(hass, mock_panel): "alarm2_out2": "Disabled", }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_binary" # zone 2 result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"type": "door"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_binary" # zone 6 @@ -1032,42 +1076,42 @@ async def test_option_flow_pro(hass, mock_panel): result["flow_id"], user_input={"type": "window", "name": "winder", "inverse": True}, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_binary" # zone 10 result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"type": "door"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_binary" # zone 11 result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"type": "window"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_digital" # zone 3 result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"type": "dht"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_digital" # zone 7 result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"type": "ds18b20", "name": "temper"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_switch" # zone 4 result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_switch" # zone 8 @@ -1081,21 +1125,21 @@ async def test_option_flow_pro(hass, mock_panel): "repeat": 4, }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_switch" # zone out1 result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_switch" # zone alarm1 result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_misc" result = await hass.config_entries.options.async_configure( @@ -1103,7 +1147,7 @@ async def test_option_flow_pro(hass, mock_panel): user_input={"discovery": False, "blink": True, "override_api_host": False}, ) - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["data"] == { "io": { "10": "Binary Sensor", @@ -1199,7 +1243,7 @@ async def test_option_flow_import(hass, mock_panel): result = await hass.config_entries.options.async_init( entry.entry_id, context={"source": "test"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_io" # confirm the defaults are set based on current config - we"ll spot check this throughout @@ -1216,7 +1260,7 @@ async def test_option_flow_import(hass, mock_panel): "3": "Switchable Output", }, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_io_ext" schema = result["data_schema"]({}) assert schema["8"] == "Disabled" @@ -1225,7 +1269,7 @@ async def test_option_flow_import(hass, mock_panel): result["flow_id"], user_input={}, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_binary" # zone 1 @@ -1236,7 +1280,7 @@ async def test_option_flow_import(hass, mock_panel): result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"type": "door"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_digital" # zone 2 @@ -1247,7 +1291,7 @@ async def test_option_flow_import(hass, mock_panel): result["flow_id"], user_input={"type": "dht"}, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_switch" # zone 3 @@ -1261,7 +1305,7 @@ async def test_option_flow_import(hass, mock_panel): result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={"activation": "high", "more_states": "No"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_misc" schema = result["data_schema"]({}) @@ -1273,7 +1317,7 @@ async def test_option_flow_import(hass, mock_panel): ) # verify the updated fields - assert result["type"] == "create_entry" + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["data"] == { "io": {"1": "Binary Sensor", "2": "Digital Sensor", "3": "Switchable Output"}, "discovery": True, @@ -1346,7 +1390,7 @@ async def test_option_flow_existing(hass, mock_panel): result = await hass.config_entries.options.async_init( entry.entry_id, context={"source": "test"} ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "options_io" # confirm the defaults are pulled in from the existing options diff --git a/tests/components/konnected/test_init.py b/tests/components/konnected/test_init.py index 4b2d09f102745..dfab0562d2d3a 100644 --- a/tests/components/konnected/test_init.py +++ b/tests/components/konnected/test_init.py @@ -477,13 +477,23 @@ async def test_api(hass, hass_client_no_auth, mock_panel): assert resp.status == HTTPStatus.NOT_FOUND # no device provided resp = await client.get("/api/konnected/223344556677") + assert resp.status == HTTPStatus.NOT_FOUND # no device part provided + + resp = await client.get("/api/konnected/device/223344556677") assert resp.status == HTTPStatus.NOT_FOUND # unknown device provided + result = await resp.json() + assert result == {"message": "Device 223344556677 not configured"} resp = await client.get("/api/konnected/device/112233445566") assert resp.status == HTTPStatus.NOT_FOUND # no zone provided result = await resp.json() assert result == {"message": "Switch on zone or pin unknown not configured"} + resp = await client.get("/api/konnected/device/112233445566?zone=6") + assert resp.status == HTTPStatus.OK # get binary value + result = await resp.json() + assert result == {"state": 0, "zone": "6"} + resp = await client.get("/api/konnected/device/112233445566?zone=8") assert resp.status == HTTPStatus.NOT_FOUND # invalid zone result = await resp.json() @@ -558,8 +568,36 @@ async def test_api(hass, hass_client_no_auth, mock_panel): result = await resp.json() assert result == {"message": "ok"} - # Test the put endpoint for sensor updates + # Test the post endpoint for sensor updates + resp = await client.post( + "/api/konnected/device/112233445566", + headers={"Authorization": "Bearer abcdefgh"}, + json={"zone": "1", "state": 1}, + ) + assert resp.status == HTTPStatus.OK + result = await resp.json() + assert result == {"message": "ok"} + + resp = await client.post( + "/api/konnected/device/112233445566", + headers={"Authorization": "Bearer abcdefgh"}, + json={"zone": "out", "state": 1}, + ) + assert resp.status == HTTPStatus.OK + result = await resp.json() + assert result == {"message": "ok"} + resp = await client.post( + "/api/konnected/device/112233445566", + headers={"Authorization": "Bearer abcdefgh"}, + json={"zone": "2", "state": 1}, + ) + assert resp.status == HTTPStatus.OK + result = await resp.json() + assert result == {"message": "ok"} + + # Test the put endpoint for sensor updates + resp = await client.put( "/api/konnected/device/112233445566", headers={"Authorization": "Bearer abcdefgh"}, json={"zone": "1", "state": 1}, @@ -568,6 +606,16 @@ async def test_api(hass, hass_client_no_auth, mock_panel): result = await resp.json() assert result == {"message": "ok"} + # Test the wrong key in json payload + resp = await client.put( + "/api/konnected/device/112233445566", + headers={"Authorization": "Bearer abcdefgh"}, + json={"zoneWRONGKEY": "14", "state": 1}, + ) + assert resp.status == HTTPStatus.BAD_REQUEST + result = await resp.json() + assert result == {"message": "unregistered sensor/actuator"} + async def test_state_updates_zone(hass, hass_client_no_auth, mock_panel): """Test callback view.""" diff --git a/tests/components/konnected/test_panel.py b/tests/components/konnected/test_panel.py index e7e5784ba7169..668e873eef759 100644 --- a/tests/components/konnected/test_panel.py +++ b/tests/components/konnected/test_panel.py @@ -5,6 +5,7 @@ import pytest from homeassistant.components.konnected import config_flow, panel +from homeassistant.components.konnected.errors import CannotConnect from homeassistant.helpers.entity_component import async_update_entity from homeassistant.setup import async_setup_component from homeassistant.util import utcnow @@ -152,6 +153,11 @@ async def test_create_and_setup(hass, mock_panel): "endpoint": "http://192.168.1.1:8123/api/konnected", } + mock_panel.put_zone.side_effect = mock_panel.ClientError + mock_panel.put_device.side_effect = mock_panel.ClientError + with pytest.raises(CannotConnect): + await device.update_switch("1", 0) + # confirm the device settings are saved in hass.data assert device.stored_configuration == { "binary_sensors": { diff --git a/tests/components/konnected/test_switch.py b/tests/components/konnected/test_switch.py new file mode 100644 index 0000000000000..b4e0aacf70b47 --- /dev/null +++ b/tests/components/konnected/test_switch.py @@ -0,0 +1,148 @@ +"""Test Konnected switch object.""" +from unittest.mock import patch + +from homeassistant.components import konnected +from homeassistant.components.konnected import async_setup_entry, config_flow +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN +from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry + + +async def test_on_off(hass): + """Test turning the light on.""" + + device_config = config_flow.CONFIG_ENTRY_SCHEMA( + { + "host": "1.2.3.4", + "port": 1234, + "id": "112233445566", + "model": "Konnected Pro", + "access_token": "abcdefgh", + "api_host": "http://192.168.86.32:8123", + "default_options": config_flow.OPTIONS_SCHEMA({config_flow.CONF_IO: {}}), + } + ) + + device_options = config_flow.OPTIONS_SCHEMA( + { + "api_host": "http://192.168.86.32:8123", + "io": { + "1": "Switchable Output", + "out": "Switchable Output", + }, + "switches": [ + { + "zone": "1", + "name": "alarm", + }, + { + "zone": "out", + "name": "switcher", + "activation": "low", + "momentary": 50, + "pause": 100, + "repeat": -1, + }, + ], + } + ) + + entry = MockConfigEntry( + domain="konnected", + title="Konnected Alarm Panel", + data=device_config, + options=device_options, + ) + entry.add_to_hass(hass) + + assert ( + await async_setup_component( + hass, + konnected.DOMAIN, + {konnected.DOMAIN: {konnected.CONF_ACCESS_TOKEN: "globaltoken"}}, + ) + is True + ) + + with patch( + "homeassistant.components.konnected.panel.AlarmPanel.async_connect", + return_value=True, + ): + + assert await async_setup_entry(hass, entry) is True + + with patch( + "homeassistant.components.konnected.panel.AlarmPanel.available", + return_value=True, + ): + + await hass.async_block_till_done() + + with patch( + "homeassistant.components.konnected.panel.AlarmPanel.update_switch", + return_value={"state": 0}, + ): + + # Turn switch off. + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.alarm"}, + blocking=True, + ) + + # Verify the switch turns off. + entity_state = hass.states.get("switch.alarm") + assert entity_state + assert entity_state.state == "off" + + with patch( + "homeassistant.components.konnected.panel.AlarmPanel.update_switch", + return_value={"state": 1}, + ): + + # Turn switch on. + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.alarm"}, + blocking=True, + ) + + # Verify the switch turns on. + entity_state = hass.states.get("switch.alarm") + assert entity_state + assert entity_state.state == "on" + + # Turn switch off. + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.alarm"}, + blocking=True, + ) + + # Verify the switch turns on. + entity_state = hass.states.get("switch.alarm") + assert entity_state + assert entity_state.state == "on" + + # test with momentary to cover part of async_turn_on that is momentary only + with patch( + "homeassistant.components.konnected.panel.AlarmPanel.update_switch", + return_value={"state": 1}, + ): + # Turn switch on. + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.switcher"}, + blocking=True, + ) + + # Verify the switch turns on. + entity_state = hass.states.get("switch.switcher") + assert entity_state + assert entity_state.state == "off"