mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Add initial support for SwitchBot relay switch (#130863)
* Support relay switch * 更新下版本 * add test case * change to async_abort * Upgrade PySwitchbot to 0.53.2 * change unit to volt * upgrade pySwitchbot dependency * bump lib, will be split into a seperate PR after testing is finished * dry * dry * dry * dry * dry * dry * dry * update tests * fixes * fixes * cleanups * fixes * fixes * fixes * bump again --------- Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
b6819cbff3
commit
861d9b3341
@ -24,6 +24,7 @@ from .const import (
|
|||||||
CONF_RETRY_COUNT,
|
CONF_RETRY_COUNT,
|
||||||
CONNECTABLE_SUPPORTED_MODEL_TYPES,
|
CONNECTABLE_SUPPORTED_MODEL_TYPES,
|
||||||
DEFAULT_RETRY_COUNT,
|
DEFAULT_RETRY_COUNT,
|
||||||
|
ENCRYPTED_MODELS,
|
||||||
HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL,
|
HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL,
|
||||||
SupportedModels,
|
SupportedModels,
|
||||||
)
|
)
|
||||||
@ -61,6 +62,8 @@ PLATFORMS_BY_TYPE = {
|
|||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
],
|
],
|
||||||
SupportedModels.HUB2.value: [Platform.SENSOR],
|
SupportedModels.HUB2.value: [Platform.SENSOR],
|
||||||
|
SupportedModels.RELAY_SWITCH_1PM.value: [Platform.SWITCH, Platform.SENSOR],
|
||||||
|
SupportedModels.RELAY_SWITCH_1.value: [Platform.SWITCH],
|
||||||
}
|
}
|
||||||
CLASS_BY_DEVICE = {
|
CLASS_BY_DEVICE = {
|
||||||
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
|
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
|
||||||
@ -73,6 +76,8 @@ CLASS_BY_DEVICE = {
|
|||||||
SupportedModels.LOCK.value: switchbot.SwitchbotLock,
|
SupportedModels.LOCK.value: switchbot.SwitchbotLock,
|
||||||
SupportedModels.LOCK_PRO.value: switchbot.SwitchbotLock,
|
SupportedModels.LOCK_PRO.value: switchbot.SwitchbotLock,
|
||||||
SupportedModels.BLIND_TILT.value: switchbot.SwitchbotBlindTilt,
|
SupportedModels.BLIND_TILT.value: switchbot.SwitchbotBlindTilt,
|
||||||
|
SupportedModels.RELAY_SWITCH_1PM.value: switchbot.SwitchbotRelaySwitch,
|
||||||
|
SupportedModels.RELAY_SWITCH_1.value: switchbot.SwitchbotRelaySwitch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -116,9 +121,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: SwitchbotConfigEntry) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
cls = CLASS_BY_DEVICE.get(sensor_type, switchbot.SwitchbotDevice)
|
cls = CLASS_BY_DEVICE.get(sensor_type, switchbot.SwitchbotDevice)
|
||||||
if cls is switchbot.SwitchbotLock:
|
if switchbot_model in ENCRYPTED_MODELS:
|
||||||
try:
|
try:
|
||||||
device = switchbot.SwitchbotLock(
|
device = cls(
|
||||||
device=ble_device,
|
device=ble_device,
|
||||||
key_id=entry.data.get(CONF_KEY_ID),
|
key_id=entry.data.get(CONF_KEY_ID),
|
||||||
encryption_key=entry.data.get(CONF_ENCRYPTION_KEY),
|
encryption_key=entry.data.get(CONF_ENCRYPTION_KEY),
|
||||||
|
@ -10,7 +10,7 @@ from switchbot import (
|
|||||||
SwitchBotAdvertisement,
|
SwitchBotAdvertisement,
|
||||||
SwitchbotApiError,
|
SwitchbotApiError,
|
||||||
SwitchbotAuthenticationError,
|
SwitchbotAuthenticationError,
|
||||||
SwitchbotLock,
|
SwitchbotModel,
|
||||||
parse_advertisement_data,
|
parse_advertisement_data,
|
||||||
)
|
)
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -44,8 +44,9 @@ from .const import (
|
|||||||
DEFAULT_LOCK_NIGHTLATCH,
|
DEFAULT_LOCK_NIGHTLATCH,
|
||||||
DEFAULT_RETRY_COUNT,
|
DEFAULT_RETRY_COUNT,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
ENCRYPTED_MODELS,
|
||||||
|
ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS,
|
||||||
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES,
|
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES,
|
||||||
SUPPORTED_LOCK_MODELS,
|
|
||||||
SUPPORTED_MODEL_TYPES,
|
SUPPORTED_MODEL_TYPES,
|
||||||
SupportedModels,
|
SupportedModels,
|
||||||
)
|
)
|
||||||
@ -112,8 +113,8 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
"name": data["modelFriendlyName"],
|
"name": data["modelFriendlyName"],
|
||||||
"address": short_address(discovery_info.address),
|
"address": short_address(discovery_info.address),
|
||||||
}
|
}
|
||||||
if model_name in SUPPORTED_LOCK_MODELS:
|
if model_name in ENCRYPTED_MODELS:
|
||||||
return await self.async_step_lock_choose_method()
|
return await self.async_step_encrypted_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()
|
||||||
@ -171,7 +172,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_lock_auth(
|
async def async_step_encrypted_auth(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle the SwitchBot API auth step."""
|
"""Handle the SwitchBot API auth step."""
|
||||||
@ -179,8 +180,10 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
assert self._discovered_adv is not None
|
assert self._discovered_adv is not None
|
||||||
description_placeholders = {}
|
description_placeholders = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
model: SwitchbotModel = self._discovered_adv.data["modelName"]
|
||||||
|
cls = ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS[model]
|
||||||
try:
|
try:
|
||||||
key_details = await SwitchbotLock.async_retrieve_encryption_key(
|
key_details = await cls.async_retrieve_encryption_key(
|
||||||
async_get_clientsession(self.hass),
|
async_get_clientsession(self.hass),
|
||||||
self._discovered_adv.address,
|
self._discovered_adv.address,
|
||||||
user_input[CONF_USERNAME],
|
user_input[CONF_USERNAME],
|
||||||
@ -198,11 +201,11 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
errors = {"base": "auth_failed"}
|
errors = {"base": "auth_failed"}
|
||||||
description_placeholders = {"error_detail": str(ex)}
|
description_placeholders = {"error_detail": str(ex)}
|
||||||
else:
|
else:
|
||||||
return await self.async_step_lock_key(key_details)
|
return await self.async_step_encrypted_key(key_details)
|
||||||
|
|
||||||
user_input = user_input or {}
|
user_input = user_input or {}
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="lock_auth",
|
step_id="encrypted_auth",
|
||||||
errors=errors,
|
errors=errors,
|
||||||
data_schema=vol.Schema(
|
data_schema=vol.Schema(
|
||||||
{
|
{
|
||||||
@ -218,32 +221,34 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_lock_choose_method(
|
async def async_step_encrypted_choose_method(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""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_choose_method",
|
step_id="encrypted_choose_method",
|
||||||
menu_options=["lock_auth", "lock_key"],
|
menu_options=["encrypted_auth", "encrypted_key"],
|
||||||
description_placeholders={
|
description_placeholders={
|
||||||
"name": name_from_discovery(self._discovered_adv),
|
"name": name_from_discovery(self._discovered_adv),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_lock_key(
|
async def async_step_encrypted_key(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle the encryption key step."""
|
"""Handle the encryption key step."""
|
||||||
errors = {}
|
errors = {}
|
||||||
assert self._discovered_adv is not None
|
assert self._discovered_adv is not None
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
if not await SwitchbotLock.verify_encryption_key(
|
model: SwitchbotModel = self._discovered_adv.data["modelName"]
|
||||||
|
cls = ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS[model]
|
||||||
|
if not await cls.verify_encryption_key(
|
||||||
self._discovered_adv.device,
|
self._discovered_adv.device,
|
||||||
user_input[CONF_KEY_ID],
|
user_input[CONF_KEY_ID],
|
||||||
user_input[CONF_ENCRYPTION_KEY],
|
user_input[CONF_ENCRYPTION_KEY],
|
||||||
model=self._discovered_adv.data["modelName"],
|
model=model,
|
||||||
):
|
):
|
||||||
errors = {
|
errors = {
|
||||||
"base": "encryption_key_invalid",
|
"base": "encryption_key_invalid",
|
||||||
@ -252,7 +257,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
return await self._async_create_entry_from_discovery(user_input)
|
return await self._async_create_entry_from_discovery(user_input)
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="lock_key",
|
step_id="encrypted_key",
|
||||||
errors=errors,
|
errors=errors,
|
||||||
data_schema=vol.Schema(
|
data_schema=vol.Schema(
|
||||||
{
|
{
|
||||||
@ -309,8 +314,8 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
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") in SUPPORTED_LOCK_MODELS:
|
if device_adv.data.get("modelName") in ENCRYPTED_MODELS:
|
||||||
return await self.async_step_lock_choose_method()
|
return await self.async_step_encrypted_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)
|
||||||
@ -321,8 +326,8 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
# or simply confirm it
|
# or simply confirm it
|
||||||
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") in SUPPORTED_LOCK_MODELS:
|
if device_adv.data.get("modelName") in ENCRYPTED_MODELS:
|
||||||
return await self.async_step_lock_choose_method()
|
return await self.async_step_encrypted_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,6 +2,7 @@
|
|||||||
|
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
import switchbot
|
||||||
from switchbot import SwitchbotModel
|
from switchbot import SwitchbotModel
|
||||||
|
|
||||||
DOMAIN = "switchbot"
|
DOMAIN = "switchbot"
|
||||||
@ -30,6 +31,8 @@ class SupportedModels(StrEnum):
|
|||||||
LOCK_PRO = "lock_pro"
|
LOCK_PRO = "lock_pro"
|
||||||
BLIND_TILT = "blind_tilt"
|
BLIND_TILT = "blind_tilt"
|
||||||
HUB2 = "hub2"
|
HUB2 = "hub2"
|
||||||
|
RELAY_SWITCH_1PM = "relay_switch_1pm"
|
||||||
|
RELAY_SWITCH_1 = "relay_switch_1"
|
||||||
|
|
||||||
|
|
||||||
CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||||
@ -44,6 +47,8 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
|||||||
SwitchbotModel.LOCK_PRO: SupportedModels.LOCK_PRO,
|
SwitchbotModel.LOCK_PRO: SupportedModels.LOCK_PRO,
|
||||||
SwitchbotModel.BLIND_TILT: SupportedModels.BLIND_TILT,
|
SwitchbotModel.BLIND_TILT: SupportedModels.BLIND_TILT,
|
||||||
SwitchbotModel.HUB2: SupportedModels.HUB2,
|
SwitchbotModel.HUB2: SupportedModels.HUB2,
|
||||||
|
SwitchbotModel.RELAY_SWITCH_1PM: SupportedModels.RELAY_SWITCH_1PM,
|
||||||
|
SwitchbotModel.RELAY_SWITCH_1: SupportedModels.RELAY_SWITCH_1,
|
||||||
}
|
}
|
||||||
|
|
||||||
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||||
@ -59,7 +64,21 @@ SUPPORTED_MODEL_TYPES = (
|
|||||||
CONNECTABLE_SUPPORTED_MODEL_TYPES | NON_CONNECTABLE_SUPPORTED_MODEL_TYPES
|
CONNECTABLE_SUPPORTED_MODEL_TYPES | NON_CONNECTABLE_SUPPORTED_MODEL_TYPES
|
||||||
)
|
)
|
||||||
|
|
||||||
SUPPORTED_LOCK_MODELS = {SwitchbotModel.LOCK, SwitchbotModel.LOCK_PRO}
|
ENCRYPTED_MODELS = {
|
||||||
|
SwitchbotModel.RELAY_SWITCH_1,
|
||||||
|
SwitchbotModel.RELAY_SWITCH_1PM,
|
||||||
|
SwitchbotModel.LOCK,
|
||||||
|
SwitchbotModel.LOCK_PRO,
|
||||||
|
}
|
||||||
|
|
||||||
|
ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
|
||||||
|
SwitchbotModel, switchbot.SwitchbotEncryptedDevice
|
||||||
|
] = {
|
||||||
|
SwitchbotModel.LOCK: switchbot.SwitchbotLock,
|
||||||
|
SwitchbotModel.LOCK_PRO: switchbot.SwitchbotLock,
|
||||||
|
SwitchbotModel.RELAY_SWITCH_1PM: switchbot.SwitchbotRelaySwitch,
|
||||||
|
SwitchbotModel.RELAY_SWITCH_1: switchbot.SwitchbotRelaySwitch,
|
||||||
|
}
|
||||||
|
|
||||||
HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = {
|
HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = {
|
||||||
str(v): k for k, v in SUPPORTED_MODEL_TYPES.items()
|
str(v): k for k, v in SUPPORTED_MODEL_TYPES.items()
|
||||||
|
@ -14,6 +14,8 @@ from homeassistant.const import (
|
|||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||||
EntityCategory,
|
EntityCategory,
|
||||||
|
UnitOfElectricCurrent,
|
||||||
|
UnitOfElectricPotential,
|
||||||
UnitOfPower,
|
UnitOfPower,
|
||||||
UnitOfTemperature,
|
UnitOfTemperature,
|
||||||
)
|
)
|
||||||
@ -82,6 +84,18 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = {
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
device_class=SensorDeviceClass.POWER,
|
device_class=SensorDeviceClass.POWER,
|
||||||
),
|
),
|
||||||
|
"current": SensorEntityDescription(
|
||||||
|
key="current",
|
||||||
|
native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
device_class=SensorDeviceClass.CURRENT,
|
||||||
|
),
|
||||||
|
"voltage": SensorEntityDescription(
|
||||||
|
key="voltage",
|
||||||
|
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
device_class=SensorDeviceClass.VOLTAGE,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,25 +16,25 @@
|
|||||||
"password": "[%key:common::config_flow::data::password%]"
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lock_key": {
|
"encrypted_key": {
|
||||||
"description": "The {name} device requires encryption key, details on how to obtain it can be found in the documentation.",
|
"description": "The {name} device requires encryption key, details on how to obtain it can be found in the documentation.",
|
||||||
"data": {
|
"data": {
|
||||||
"key_id": "Key ID",
|
"key_id": "Key ID",
|
||||||
"encryption_key": "Encryption key"
|
"encryption_key": "Encryption key"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lock_auth": {
|
"encrypted_auth": {
|
||||||
"description": "Please provide your SwitchBot app username and password. This data won't be saved and only used to retrieve your locks encryption key. Usernames and passwords are case sensitive.",
|
"description": "Please provide your SwitchBot app username and password. This data won't be saved and only used to retrieve your device's encryption key. Usernames and passwords are case sensitive.",
|
||||||
"data": {
|
"data": {
|
||||||
"username": "[%key:common::config_flow::data::username%]",
|
"username": "[%key:common::config_flow::data::username%]",
|
||||||
"password": "[%key:common::config_flow::data::password%]"
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lock_choose_method": {
|
"encrypted_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.",
|
"description": "An encrypted SwitchBot device 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 account (recommended)",
|
"encrypted_auth": "SwitchBot account (recommended)",
|
||||||
"lock_key": "Enter lock encryption key manually"
|
"encrypted_key": "Enter encryption key manually"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -230,3 +230,23 @@ WOMETERTHPC_SERVICE_INFO = BluetoothServiceInfoBleak(
|
|||||||
connectable=True,
|
connectable=True,
|
||||||
tx_power=-127,
|
tx_power=-127,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
WORELAY_SWITCH_1PM_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||||
|
name="W1080000",
|
||||||
|
manufacturer_data={2409: b"$X|\x0866G\x81\x00\x00\x001\x00\x00\x00\x00"},
|
||||||
|
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"<\x00\x00\x00"},
|
||||||
|
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||||
|
address="AA:BB:CC:DD:EE:FF",
|
||||||
|
rssi=-60,
|
||||||
|
source="local",
|
||||||
|
advertisement=generate_advertisement_data(
|
||||||
|
local_name="W1080000",
|
||||||
|
manufacturer_data={2409: b"$X|\x0866G\x81\x00\x00\x001\x00\x00\x00\x00"},
|
||||||
|
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"<\x00\x00\x00"},
|
||||||
|
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||||
|
),
|
||||||
|
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "W1080000"),
|
||||||
|
time=0,
|
||||||
|
connectable=True,
|
||||||
|
tx_power=-127,
|
||||||
|
)
|
||||||
|
@ -30,6 +30,7 @@ from . import (
|
|||||||
WOHAND_SERVICE_INFO,
|
WOHAND_SERVICE_INFO,
|
||||||
WOHAND_SERVICE_INFO_NOT_CONNECTABLE,
|
WOHAND_SERVICE_INFO_NOT_CONNECTABLE,
|
||||||
WOLOCK_SERVICE_INFO,
|
WOLOCK_SERVICE_INFO,
|
||||||
|
WORELAY_SWITCH_1PM_SERVICE_INFO,
|
||||||
WOSENSORTH_SERVICE_INFO,
|
WOSENSORTH_SERVICE_INFO,
|
||||||
init_integration,
|
init_integration,
|
||||||
patch_async_setup_entry,
|
patch_async_setup_entry,
|
||||||
@ -95,7 +96,7 @@ async def test_bluetooth_discovery_requires_password(hass: HomeAssistant) -> Non
|
|||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_bluetooth_discovery_lock_key(hass: HomeAssistant) -> None:
|
async def test_bluetooth_discovery_encrypted_key(hass: HomeAssistant) -> None:
|
||||||
"""Test discovery via bluetooth with a lock."""
|
"""Test discovery via bluetooth with a lock."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -103,18 +104,18 @@ async def test_bluetooth_discovery_lock_key(hass: HomeAssistant) -> None:
|
|||||||
data=WOLOCK_SERVICE_INFO,
|
data=WOLOCK_SERVICE_INFO,
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.MENU
|
assert result["type"] is FlowResultType.MENU
|
||||||
assert result["step_id"] == "lock_choose_method"
|
assert result["step_id"] == "encrypted_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": "encrypted_key"}
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "lock_key"
|
assert result["step_id"] == "encrypted_key"
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key",
|
"switchbot.SwitchbotLock.verify_encryption_key",
|
||||||
return_value=False,
|
return_value=False,
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
@ -127,13 +128,13 @@ async def test_bluetooth_discovery_lock_key(hass: HomeAssistant) -> None:
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "lock_key"
|
assert result["step_id"] == "encrypted_key"
|
||||||
assert result["errors"] == {"base": "encryption_key_invalid"}
|
assert result["errors"] == {"base": "encryption_key_invalid"}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch_async_setup_entry() as mock_setup_entry,
|
patch_async_setup_entry() as mock_setup_entry,
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key",
|
"switchbot.SwitchbotLock.verify_encryption_key",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
@ -158,6 +159,51 @@ async def test_bluetooth_discovery_lock_key(hass: HomeAssistant) -> None:
|
|||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_bluetooth_discovery_key(hass: HomeAssistant) -> None:
|
||||||
|
"""Test discovery via bluetooth with a encrypted device."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_BLUETOOTH},
|
||||||
|
data=WORELAY_SWITCH_1PM_SERVICE_INFO,
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.MENU
|
||||||
|
assert result["step_id"] == "encrypted_choose_method"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "encrypted_key"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch_async_setup_entry() as mock_setup_entry,
|
||||||
|
patch(
|
||||||
|
"switchbot.SwitchbotRelaySwitch.verify_encryption_key", return_value=True
|
||||||
|
),
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_KEY_ID: "ff",
|
||||||
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == "Relay Switch 1PM EEFF"
|
||||||
|
assert result["data"] == {
|
||||||
|
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
|
||||||
|
CONF_KEY_ID: "ff",
|
||||||
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
|
CONF_SENSOR_TYPE: "relay_switch_1pm",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_bluetooth_discovery_already_setup(hass: HomeAssistant) -> None:
|
async def test_bluetooth_discovery_already_setup(hass: HomeAssistant) -> None:
|
||||||
"""Test discovery via bluetooth with a valid device when already setup."""
|
"""Test discovery via bluetooth with a valid device when already setup."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
@ -400,7 +446,7 @@ async def test_user_setup_single_bot_with_password(hass: HomeAssistant) -> None:
|
|||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_user_setup_wolock_key(hass: HomeAssistant) -> None:
|
async def test_user_setup_woencrypted_key(hass: HomeAssistant) -> None:
|
||||||
"""Test the user initiated form for a lock."""
|
"""Test the user initiated form for a lock."""
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -411,18 +457,18 @@ async def test_user_setup_wolock_key(hass: HomeAssistant) -> None:
|
|||||||
DOMAIN, context={"source": SOURCE_USER}
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.MENU
|
assert result["type"] is FlowResultType.MENU
|
||||||
assert result["step_id"] == "lock_choose_method"
|
assert result["step_id"] == "encrypted_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": "encrypted_key"}
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "lock_key"
|
assert result["step_id"] == "encrypted_key"
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key",
|
"switchbot.SwitchbotLock.verify_encryption_key",
|
||||||
return_value=False,
|
return_value=False,
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
@ -435,13 +481,13 @@ async def test_user_setup_wolock_key(hass: HomeAssistant) -> None:
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "lock_key"
|
assert result["step_id"] == "encrypted_key"
|
||||||
assert result["errors"] == {"base": "encryption_key_invalid"}
|
assert result["errors"] == {"base": "encryption_key_invalid"}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch_async_setup_entry() as mock_setup_entry,
|
patch_async_setup_entry() as mock_setup_entry,
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key",
|
"switchbot.SwitchbotLock.verify_encryption_key",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
@ -466,7 +512,7 @@ async def test_user_setup_wolock_key(hass: HomeAssistant) -> None:
|
|||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_user_setup_wolock_auth(hass: HomeAssistant) -> None:
|
async def test_user_setup_woencrypted_auth(hass: HomeAssistant) -> None:
|
||||||
"""Test the user initiated form for a lock."""
|
"""Test the user initiated form for a lock."""
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -477,18 +523,18 @@ async def test_user_setup_wolock_auth(hass: HomeAssistant) -> None:
|
|||||||
DOMAIN, context={"source": SOURCE_USER}
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.MENU
|
assert result["type"] is FlowResultType.MENU
|
||||||
assert result["step_id"] == "lock_choose_method"
|
assert result["step_id"] == "encrypted_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": "encrypted_auth"}
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "lock_auth"
|
assert result["step_id"] == "encrypted_auth"
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.async_retrieve_encryption_key",
|
"switchbot.SwitchbotLock.async_retrieve_encryption_key",
|
||||||
side_effect=SwitchbotAuthenticationError("error from api"),
|
side_effect=SwitchbotAuthenticationError("error from api"),
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
@ -500,18 +546,18 @@ async def test_user_setup_wolock_auth(hass: HomeAssistant) -> None:
|
|||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "lock_auth"
|
assert result["step_id"] == "encrypted_auth"
|
||||||
assert result["errors"] == {"base": "auth_failed"}
|
assert result["errors"] == {"base": "auth_failed"}
|
||||||
assert "error from api" in result["description_placeholders"]["error_detail"]
|
assert "error from api" in result["description_placeholders"]["error_detail"]
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch_async_setup_entry() as mock_setup_entry,
|
patch_async_setup_entry() as mock_setup_entry,
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key",
|
"switchbot.SwitchbotLock.verify_encryption_key",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.async_retrieve_encryption_key",
|
"switchbot.SwitchbotLock.async_retrieve_encryption_key",
|
||||||
return_value={
|
return_value={
|
||||||
CONF_KEY_ID: "ff",
|
CONF_KEY_ID: "ff",
|
||||||
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
@ -539,7 +585,9 @@ async def test_user_setup_wolock_auth(hass: HomeAssistant) -> None:
|
|||||||
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: HomeAssistant) -> None:
|
async def test_user_setup_woencrypted_auth_switchbot_api_down(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
"""Test the user initiated form for a lock when the switchbot api is down."""
|
"""Test the user initiated form for a lock when the switchbot api is down."""
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -550,18 +598,18 @@ async def test_user_setup_wolock_auth_switchbot_api_down(hass: HomeAssistant) ->
|
|||||||
DOMAIN, context={"source": SOURCE_USER}
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.MENU
|
assert result["type"] is FlowResultType.MENU
|
||||||
assert result["step_id"] == "lock_choose_method"
|
assert result["step_id"] == "encrypted_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": "encrypted_auth"}
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "lock_auth"
|
assert result["step_id"] == "encrypted_auth"
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.async_retrieve_encryption_key",
|
"switchbot.SwitchbotLock.async_retrieve_encryption_key",
|
||||||
side_effect=SwitchbotAccountConnectionError("Switchbot API down"),
|
side_effect=SwitchbotAccountConnectionError("Switchbot API down"),
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
@ -600,20 +648,20 @@ async def test_user_setup_wolock_or_bot(hass: HomeAssistant) -> None:
|
|||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] is FlowResultType.MENU
|
assert result["type"] is FlowResultType.MENU
|
||||||
assert result["step_id"] == "lock_choose_method"
|
assert result["step_id"] == "encrypted_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": "encrypted_key"}
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "lock_key"
|
assert result["step_id"] == "encrypted_key"
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch_async_setup_entry() as mock_setup_entry,
|
patch_async_setup_entry() as mock_setup_entry,
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key",
|
"switchbot.SwitchbotLock.verify_encryption_key",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
@ -845,3 +893,163 @@ async def test_options_flow_lock_pro(hass: HomeAssistant) -> None:
|
|||||||
assert len(mock_setup_entry.mock_calls) == 0
|
assert len(mock_setup_entry.mock_calls) == 0
|
||||||
|
|
||||||
assert entry.options[CONF_LOCK_NIGHTLATCH] is True
|
assert entry.options[CONF_LOCK_NIGHTLATCH] is True
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_setup_worelay_switch_1pm_key(hass: HomeAssistant) -> None:
|
||||||
|
"""Test the user initiated form for a relay switch 1pm."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
|
||||||
|
return_value=[WORELAY_SWITCH_1PM_SERVICE_INFO],
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.MENU
|
||||||
|
assert result["step_id"] == "encrypted_choose_method"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "encrypted_key"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch_async_setup_entry() as mock_setup_entry,
|
||||||
|
patch(
|
||||||
|
"switchbot.SwitchbotRelaySwitch.verify_encryption_key", return_value=True
|
||||||
|
),
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_KEY_ID: "ff",
|
||||||
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == "Relay Switch 1PM EEFF"
|
||||||
|
assert result["data"] == {
|
||||||
|
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
|
||||||
|
CONF_KEY_ID: "ff",
|
||||||
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
|
CONF_SENSOR_TYPE: "relay_switch_1pm",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_setup_worelay_switch_1pm_auth(hass: HomeAssistant) -> None:
|
||||||
|
"""Test the user initiated form for a relay switch 1pm."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
|
||||||
|
return_value=[WORELAY_SWITCH_1PM_SERVICE_INFO],
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.MENU
|
||||||
|
assert result["step_id"] == "encrypted_choose_method"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "encrypted_auth"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"switchbot.SwitchbotRelaySwitch.async_retrieve_encryption_key",
|
||||||
|
side_effect=SwitchbotAuthenticationError("error from api"),
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: "",
|
||||||
|
CONF_PASSWORD: "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "encrypted_auth"
|
||||||
|
assert result["errors"] == {"base": "auth_failed"}
|
||||||
|
assert "error from api" in result["description_placeholders"]["error_detail"]
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch_async_setup_entry() as mock_setup_entry,
|
||||||
|
patch(
|
||||||
|
"switchbot.SwitchbotRelaySwitch.async_retrieve_encryption_key",
|
||||||
|
return_value={
|
||||||
|
CONF_KEY_ID: "ff",
|
||||||
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"switchbot.SwitchbotRelaySwitch.verify_encryption_key", return_value=True
|
||||||
|
),
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: "username",
|
||||||
|
CONF_PASSWORD: "password",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == "Relay Switch 1PM EEFF"
|
||||||
|
assert result["data"] == {
|
||||||
|
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
|
||||||
|
CONF_KEY_ID: "ff",
|
||||||
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
|
CONF_SENSOR_TYPE: "relay_switch_1pm",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_setup_worelay_switch_1pm_auth_switchbot_api_down(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test the user initiated form for a relay switch 1pm when the switchbot api is down."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
|
||||||
|
return_value=[WORELAY_SWITCH_1PM_SERVICE_INFO],
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.MENU
|
||||||
|
assert result["step_id"] == "encrypted_choose_method"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "encrypted_auth"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"switchbot.SwitchbotRelaySwitch.async_retrieve_encryption_key",
|
||||||
|
side_effect=SwitchbotAccountConnectionError("Switchbot API down"),
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: "",
|
||||||
|
CONF_PASSWORD: "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "api_error"
|
||||||
|
assert result["description_placeholders"] == {"error_detail": "Switchbot API down"}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
"""Test the switchbot sensors."""
|
"""Test the switchbot sensors."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.components.switchbot.const import DOMAIN
|
from homeassistant.components.switchbot.const import (
|
||||||
|
CONF_ENCRYPTION_KEY,
|
||||||
|
CONF_KEY_ID,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
@ -15,7 +21,11 @@ from homeassistant.const import (
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import WOHAND_SERVICE_INFO, WOMETERTHPC_SERVICE_INFO
|
from . import (
|
||||||
|
WOHAND_SERVICE_INFO,
|
||||||
|
WOMETERTHPC_SERVICE_INFO,
|
||||||
|
WORELAY_SWITCH_1PM_SERVICE_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
from tests.components.bluetooth import inject_bluetooth_service_info
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
@ -105,3 +115,39 @@ async def test_co2_sensor(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_relay_switch_1pm_power_sensor(hass: HomeAssistant) -> None:
|
||||||
|
"""Test setting up creates the power sensor."""
|
||||||
|
await async_setup_component(hass, DOMAIN, {})
|
||||||
|
inject_bluetooth_service_info(hass, WORELAY_SWITCH_1PM_SERVICE_INFO)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"switchbot.SwitchbotRelaySwitch.update",
|
||||||
|
return_value=None,
|
||||||
|
):
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
|
||||||
|
CONF_NAME: "test-name",
|
||||||
|
CONF_SENSOR_TYPE: "relay_switch_1pm",
|
||||||
|
CONF_KEY_ID: "ff",
|
||||||
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
|
},
|
||||||
|
unique_id="aabbccddeeaa",
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
power_sensor = hass.states.get("sensor.test_name_power")
|
||||||
|
power_sensor_attrs = power_sensor.attributes
|
||||||
|
assert power_sensor.state == "4.9"
|
||||||
|
assert power_sensor_attrs[ATTR_FRIENDLY_NAME] == "test-name Power"
|
||||||
|
assert power_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "W"
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user