From 9655619667784c64683fef1e6e3bc10cb796ef48 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 30 Dec 2022 18:05:25 -1000 Subject: [PATCH] Small fixes for SwitchBot Locks (#84888) Co-authored-by: Aaron Bach --- .../components/switchbot/__init__.py | 6 ++- .../components/switchbot/config_flow.py | 21 ++++---- .../components/switchbot/manifest.json | 2 +- .../components/switchbot/strings.json | 8 +-- .../components/switchbot/translations/en.json | 8 +-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../components/switchbot/test_config_flow.py | 49 +++++++++++++++++-- 8 files changed, 72 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py index b79e42ba5b9..5d4f29b9dfe 100644 --- a/homeassistant/components/switchbot/__init__.py +++ b/homeassistant/components/switchbot/__init__.py @@ -45,7 +45,11 @@ PLATFORMS_BY_TYPE = { SupportedModels.CONTACT.value: [Platform.BINARY_SENSOR, Platform.SENSOR], SupportedModels.MOTION.value: [Platform.BINARY_SENSOR, Platform.SENSOR], SupportedModels.HUMIDIFIER.value: [Platform.HUMIDIFIER, Platform.SENSOR], - SupportedModels.LOCK.value: [Platform.BINARY_SENSOR, Platform.LOCK], + SupportedModels.LOCK.value: [ + Platform.BINARY_SENSOR, + Platform.LOCK, + Platform.SENSOR, + ], } CLASS_BY_DEVICE = { SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight, diff --git a/homeassistant/components/switchbot/config_flow.py b/homeassistant/components/switchbot/config_flow.py index a71e30b2f96..6ba0e463718 100644 --- a/homeassistant/components/switchbot/config_flow.py +++ b/homeassistant/components/switchbot/config_flow.py @@ -5,7 +5,9 @@ import logging from typing import Any from switchbot import ( + SwitchbotAccountConnectionError, SwitchBotAdvertisement, + SwitchbotAuthenticationError, SwitchbotLock, SwitchbotModel, parse_advertisement_data, @@ -100,7 +102,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): "address": short_address(discovery_info.address), } if model_name == SwitchbotModel.LOCK: - return await self.async_step_lock_chose_method() + return await self.async_step_lock_choose_method() if self._discovered_adv.data["isEncrypted"]: return await self.async_step_password() return await self.async_step_confirm() @@ -172,11 +174,12 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): user_input[CONF_USERNAME], user_input[CONF_PASSWORD], ) + except SwitchbotAccountConnectionError as ex: + raise AbortFlow("cannot_connect") from ex + except SwitchbotAuthenticationError: + errors = {"base": "auth_failed"} + else: return await self.async_step_lock_key(key_details) - except RuntimeError: - errors = { - "base": "auth_failed", - } user_input = user_input or {} return self.async_show_form( @@ -195,14 +198,14 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): }, ) - async def async_step_lock_chose_method( + async def async_step_lock_choose_method( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle the SwitchBot API chose method step.""" assert self._discovered_adv is not None return self.async_show_menu( - step_id="lock_chose_method", + step_id="lock_choose_method", menu_options=["lock_auth", "lock_key"], description_placeholders={ "name": name_from_discovery(self._discovered_adv), @@ -286,7 +289,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): device_adv = self._discovered_advs[user_input[CONF_ADDRESS]] await self._async_set_device(device_adv) if device_adv.data.get("modelName") == SwitchbotModel.LOCK: - return await self.async_step_lock_chose_method() + return await self.async_step_lock_choose_method() if device_adv.data["isEncrypted"]: return await self.async_step_password() return await self._async_create_entry_from_discovery(user_input) @@ -298,7 +301,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): device_adv = list(self._discovered_advs.values())[0] await self._async_set_device(device_adv) if device_adv.data.get("modelName") == SwitchbotModel.LOCK: - return await self.async_step_lock_chose_method() + return await self.async_step_lock_choose_method() if device_adv.data["isEncrypted"]: return await self.async_step_password() return await self.async_step_confirm() diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index b5b3d633285..f18a80b1b89 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.34.1"], + "requirements": ["PySwitchbot==0.36.0"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/homeassistant/components/switchbot/strings.json b/homeassistant/components/switchbot/strings.json index 10a623a70d7..08fd960334a 100644 --- a/homeassistant/components/switchbot/strings.json +++ b/homeassistant/components/switchbot/strings.json @@ -30,11 +30,11 @@ "password": "[%key:common::config_flow::data::password%]" } }, - "lock_chose_method": { - "description": "Choose configuration method, details can be found in the documentation.", + "lock_choose_method": { + "description": "A SwitchBot lock can be set up in Home Assistant in two different ways.\n\nYou can enter the key id and encryption key yourself, or Home Assistant can import them from your SwitchBot account.", "menu_options": { - "lock_auth": "SwitchBot app login and password", - "lock_key": "Lock encryption key" + "lock_auth": "SwitchBot account (recommended)", + "lock_key": "Enter lock encryption key manually" } } }, diff --git a/homeassistant/components/switchbot/translations/en.json b/homeassistant/components/switchbot/translations/en.json index ab2ffa8d6ac..b5658e58d6b 100644 --- a/homeassistant/components/switchbot/translations/en.json +++ b/homeassistant/components/switchbot/translations/en.json @@ -23,11 +23,11 @@ }, "description": "Please provide your SwitchBot app username and password. This data won't be saved and only used to retrieve your locks encryption key." }, - "lock_chose_method": { - "description": "Choose configuration method, details can be found in the documentation.", + "lock_choose_method": { + "description": "A SwitchBot lock can be set up in Home Assistant in two different ways.\n\nYou can enter the key id and encryption key yourself, or Home Assistant can import them from your SwitchBot account.", "menu_options": { - "lock_auth": "SwitchBot app login and password", - "lock_key": "Lock encryption key" + "lock_auth": "SwitchBot account (recommended)", + "lock_key": "Enter lock encryption key manually" } }, "lock_key": { diff --git a/requirements_all.txt b/requirements_all.txt index 43cc42d9512..756240346cc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -40,7 +40,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.34.1 +PySwitchbot==0.36.0 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4a9d3801e83..2e1dafe0009 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -36,7 +36,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.34.1 +PySwitchbot==0.36.0 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/tests/components/switchbot/test_config_flow.py b/tests/components/switchbot/test_config_flow.py index 6e1a1a14c6a..1a3db48f192 100644 --- a/tests/components/switchbot/test_config_flow.py +++ b/tests/components/switchbot/test_config_flow.py @@ -2,6 +2,8 @@ from unittest.mock import patch +from switchbot import SwitchbotAccountConnectionError, SwitchbotAuthenticationError + from homeassistant.components.switchbot.const import ( CONF_ENCRYPTION_KEY, CONF_KEY_ID, @@ -99,7 +101,7 @@ async def test_bluetooth_discovery_lock_key(hass): data=WOLOCK_SERVICE_INFO, ) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "lock_chose_method" + assert result["step_id"] == "lock_choose_method" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"next_step_id": "lock_key"} @@ -404,7 +406,7 @@ async def test_user_setup_wolock_key(hass): DOMAIN, context={"source": SOURCE_USER} ) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "lock_chose_method" + assert result["step_id"] == "lock_choose_method" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"next_step_id": "lock_key"} @@ -467,7 +469,7 @@ async def test_user_setup_wolock_auth(hass): DOMAIN, context={"source": SOURCE_USER} ) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "lock_chose_method" + assert result["step_id"] == "lock_choose_method" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"next_step_id": "lock_auth"} @@ -479,7 +481,7 @@ async def test_user_setup_wolock_auth(hass): with patch( "homeassistant.components.switchbot.config_flow.SwitchbotLock.retrieve_encryption_key", - side_effect=RuntimeError, + side_effect=SwitchbotAuthenticationError, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -524,6 +526,43 @@ async def test_user_setup_wolock_auth(hass): assert len(mock_setup_entry.mock_calls) == 1 +async def test_user_setup_wolock_auth_switchbot_api_down(hass): + """Test the user initiated form for a lock when the switchbot api is down.""" + + with patch( + "homeassistant.components.switchbot.config_flow.async_discovered_service_info", + return_value=[WOLOCK_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "lock_choose_method" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"next_step_id": "lock_auth"} + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_auth" + assert result["errors"] == {} + + with patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.retrieve_encryption_key", + side_effect=SwitchbotAccountConnectionError, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "", + CONF_PASSWORD: "", + }, + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "cannot_connect" + + async def test_user_setup_wolock_or_bot(hass): """Test the user initiated form for a lock.""" @@ -547,7 +586,7 @@ async def test_user_setup_wolock_or_bot(hass): ) await hass.async_block_till_done() assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "lock_chose_method" + assert result["step_id"] == "lock_choose_method" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"next_step_id": "lock_key"}