Small fixes for SwitchBot Locks (#84888)
Co-authored-by: Aaron Bach <bachya1208@gmail.com>pull/85120/head
parent
32736b3336
commit
9655619667
|
@ -45,7 +45,11 @@ PLATFORMS_BY_TYPE = {
|
||||||
SupportedModels.CONTACT.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
|
SupportedModels.CONTACT.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
|
||||||
SupportedModels.MOTION.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
|
SupportedModels.MOTION.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
|
||||||
SupportedModels.HUMIDIFIER.value: [Platform.HUMIDIFIER, 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 = {
|
CLASS_BY_DEVICE = {
|
||||||
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
|
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
|
||||||
|
|
|
@ -5,7 +5,9 @@ import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from switchbot import (
|
from switchbot import (
|
||||||
|
SwitchbotAccountConnectionError,
|
||||||
SwitchBotAdvertisement,
|
SwitchBotAdvertisement,
|
||||||
|
SwitchbotAuthenticationError,
|
||||||
SwitchbotLock,
|
SwitchbotLock,
|
||||||
SwitchbotModel,
|
SwitchbotModel,
|
||||||
parse_advertisement_data,
|
parse_advertisement_data,
|
||||||
|
@ -100,7 +102,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||||
"address": short_address(discovery_info.address),
|
"address": short_address(discovery_info.address),
|
||||||
}
|
}
|
||||||
if model_name == SwitchbotModel.LOCK:
|
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"]:
|
if self._discovered_adv.data["isEncrypted"]:
|
||||||
return await self.async_step_password()
|
return await self.async_step_password()
|
||||||
return await self.async_step_confirm()
|
return await self.async_step_confirm()
|
||||||
|
@ -172,11 +174,12 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||||
user_input[CONF_USERNAME],
|
user_input[CONF_USERNAME],
|
||||||
user_input[CONF_PASSWORD],
|
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)
|
return await self.async_step_lock_key(key_details)
|
||||||
except RuntimeError:
|
|
||||||
errors = {
|
|
||||||
"base": "auth_failed",
|
|
||||||
}
|
|
||||||
|
|
||||||
user_input = user_input or {}
|
user_input = user_input or {}
|
||||||
return self.async_show_form(
|
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
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Handle the SwitchBot API chose method step."""
|
"""Handle the SwitchBot API chose method step."""
|
||||||
assert self._discovered_adv is not None
|
assert self._discovered_adv is not None
|
||||||
|
|
||||||
return self.async_show_menu(
|
return self.async_show_menu(
|
||||||
step_id="lock_chose_method",
|
step_id="lock_choose_method",
|
||||||
menu_options=["lock_auth", "lock_key"],
|
menu_options=["lock_auth", "lock_key"],
|
||||||
description_placeholders={
|
description_placeholders={
|
||||||
"name": name_from_discovery(self._discovered_adv),
|
"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]]
|
device_adv = self._discovered_advs[user_input[CONF_ADDRESS]]
|
||||||
await self._async_set_device(device_adv)
|
await self._async_set_device(device_adv)
|
||||||
if device_adv.data.get("modelName") == SwitchbotModel.LOCK:
|
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"]:
|
if device_adv.data["isEncrypted"]:
|
||||||
return await self.async_step_password()
|
return await self.async_step_password()
|
||||||
return await self._async_create_entry_from_discovery(user_input)
|
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]
|
device_adv = list(self._discovered_advs.values())[0]
|
||||||
await self._async_set_device(device_adv)
|
await self._async_set_device(device_adv)
|
||||||
if device_adv.data.get("modelName") == SwitchbotModel.LOCK:
|
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"]:
|
if device_adv.data["isEncrypted"]:
|
||||||
return await self.async_step_password()
|
return await self.async_step_password()
|
||||||
return await self.async_step_confirm()
|
return await self.async_step_confirm()
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"domain": "switchbot",
|
"domain": "switchbot",
|
||||||
"name": "SwitchBot",
|
"name": "SwitchBot",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/switchbot",
|
"documentation": "https://www.home-assistant.io/integrations/switchbot",
|
||||||
"requirements": ["PySwitchbot==0.34.1"],
|
"requirements": ["PySwitchbot==0.36.0"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"dependencies": ["bluetooth"],
|
"dependencies": ["bluetooth"],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
|
|
|
@ -30,11 +30,11 @@
|
||||||
"password": "[%key:common::config_flow::data::password%]"
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lock_chose_method": {
|
"lock_choose_method": {
|
||||||
"description": "Choose configuration method, details can be found in the documentation.",
|
"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": {
|
"menu_options": {
|
||||||
"lock_auth": "SwitchBot app login and password",
|
"lock_auth": "SwitchBot account (recommended)",
|
||||||
"lock_key": "Lock encryption key"
|
"lock_key": "Enter lock encryption key manually"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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."
|
"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": {
|
"lock_choose_method": {
|
||||||
"description": "Choose configuration method, details can be found in the documentation.",
|
"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": {
|
"menu_options": {
|
||||||
"lock_auth": "SwitchBot app login and password",
|
"lock_auth": "SwitchBot account (recommended)",
|
||||||
"lock_key": "Lock encryption key"
|
"lock_key": "Enter lock encryption key manually"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lock_key": {
|
"lock_key": {
|
||||||
|
|
|
@ -40,7 +40,7 @@ PyRMVtransport==0.3.3
|
||||||
PySocks==1.7.1
|
PySocks==1.7.1
|
||||||
|
|
||||||
# homeassistant.components.switchbot
|
# homeassistant.components.switchbot
|
||||||
PySwitchbot==0.34.1
|
PySwitchbot==0.36.0
|
||||||
|
|
||||||
# homeassistant.components.transport_nsw
|
# homeassistant.components.transport_nsw
|
||||||
PyTransportNSW==0.1.1
|
PyTransportNSW==0.1.1
|
||||||
|
|
|
@ -36,7 +36,7 @@ PyRMVtransport==0.3.3
|
||||||
PySocks==1.7.1
|
PySocks==1.7.1
|
||||||
|
|
||||||
# homeassistant.components.switchbot
|
# homeassistant.components.switchbot
|
||||||
PySwitchbot==0.34.1
|
PySwitchbot==0.36.0
|
||||||
|
|
||||||
# homeassistant.components.transport_nsw
|
# homeassistant.components.transport_nsw
|
||||||
PyTransportNSW==0.1.1
|
PyTransportNSW==0.1.1
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from switchbot import SwitchbotAccountConnectionError, SwitchbotAuthenticationError
|
||||||
|
|
||||||
from homeassistant.components.switchbot.const import (
|
from homeassistant.components.switchbot.const import (
|
||||||
CONF_ENCRYPTION_KEY,
|
CONF_ENCRYPTION_KEY,
|
||||||
CONF_KEY_ID,
|
CONF_KEY_ID,
|
||||||
|
@ -99,7 +101,7 @@ async def test_bluetooth_discovery_lock_key(hass):
|
||||||
data=WOLOCK_SERVICE_INFO,
|
data=WOLOCK_SERVICE_INFO,
|
||||||
)
|
)
|
||||||
assert result["type"] == FlowResultType.MENU
|
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 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], user_input={"next_step_id": "lock_key"}
|
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}
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
)
|
)
|
||||||
assert result["type"] == FlowResultType.MENU
|
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 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], user_input={"next_step_id": "lock_key"}
|
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}
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
)
|
)
|
||||||
assert result["type"] == FlowResultType.MENU
|
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 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], user_input={"next_step_id": "lock_auth"}
|
result["flow_id"], user_input={"next_step_id": "lock_auth"}
|
||||||
|
@ -479,7 +481,7 @@ async def test_user_setup_wolock_auth(hass):
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.retrieve_encryption_key",
|
"homeassistant.components.switchbot.config_flow.SwitchbotLock.retrieve_encryption_key",
|
||||||
side_effect=RuntimeError,
|
side_effect=SwitchbotAuthenticationError,
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
|
@ -524,6 +526,43 @@ async def test_user_setup_wolock_auth(hass):
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
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):
|
async def test_user_setup_wolock_or_bot(hass):
|
||||||
"""Test the user initiated form for a lock."""
|
"""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()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] == FlowResultType.MENU
|
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 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], user_input={"next_step_id": "lock_key"}
|
result["flow_id"], user_input={"next_step_id": "lock_key"}
|
||||||
|
|
Loading…
Reference in New Issue