mirror of
https://github.com/home-assistant/core.git
synced 2026-09-27 09:58:07 -04:00
Add SwitchBot Lock Ultra Max support (#180483)
Co-authored-by: Fan Kai <fankai@onero.com>
This commit is contained in:
@@ -197,6 +197,11 @@ PLATFORMS_BY_TYPE = {
|
||||
Platform.LOCK,
|
||||
Platform.SENSOR,
|
||||
],
|
||||
SupportedModels.LOCK_ULTRA_MAX.value: [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.LOCK,
|
||||
Platform.SENSOR,
|
||||
],
|
||||
SupportedModels.WEATHER_STATION.value: [Platform.SENSOR],
|
||||
SupportedModels.CANDLE_WARMER_LAMP.value: [Platform.LIGHT, Platform.SENSOR],
|
||||
SupportedModels.RGBIC_NEON_ROPE_LIGHT.value: [Platform.LIGHT, Platform.SENSOR],
|
||||
@@ -257,6 +262,7 @@ CLASS_BY_DEVICE = {
|
||||
SupportedModels.LOCK_VISION_PRO.value: switchbot.SwitchbotLock,
|
||||
SupportedModels.LOCK_VISION.value: switchbot.SwitchbotLock,
|
||||
SupportedModels.LOCK_PRO_WIFI.value: switchbot.SwitchbotLock,
|
||||
SupportedModels.LOCK_ULTRA_MAX.value: switchbot.SwitchbotLock,
|
||||
SupportedModels.CANDLE_WARMER_LAMP.value: switchbot.SwitchbotCandleWarmerLamp,
|
||||
SupportedModels.RGBIC_NEON_ROPE_LIGHT.value: switchbot.SwitchbotRgbicNeonLight,
|
||||
SupportedModels.RGBIC_NEON_WIRE_ROPE_LIGHT.value: switchbot.SwitchbotRgbicNeonLight,
|
||||
|
||||
@@ -482,6 +482,7 @@ class SwitchbotOptionsFlowHandler(OptionsFlow):
|
||||
SupportedModels.LOCK,
|
||||
SupportedModels.LOCK_PRO,
|
||||
SupportedModels.LOCK_ULTRA,
|
||||
SupportedModels.LOCK_ULTRA_MAX,
|
||||
SupportedModels.LOCK_PRO_WIFI,
|
||||
SupportedModels.LOCK_VISION,
|
||||
SupportedModels.LOCK_VISION_PRO,
|
||||
|
||||
@@ -47,6 +47,7 @@ class SupportedModels(StrEnum):
|
||||
HUB3 = "hub3"
|
||||
LOCK_LITE = "lock_lite"
|
||||
LOCK_ULTRA = "lock_ultra"
|
||||
LOCK_ULTRA_MAX = "lock_ultra_max"
|
||||
AIR_PURIFIER_JP = "air_purifier_jp"
|
||||
AIR_PURIFIER_US = "air_purifier_us"
|
||||
AIR_PURIFIER_TABLE_JP = "air_purifier_table_jp"
|
||||
@@ -101,6 +102,7 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||
SwitchbotModel.K10_PRO_COMBO_VACUUM: SupportedModels.K10_PRO_COMBO_VACUUM,
|
||||
SwitchbotModel.LOCK_LITE: SupportedModels.LOCK_LITE,
|
||||
SwitchbotModel.LOCK_ULTRA: SupportedModels.LOCK_ULTRA,
|
||||
SwitchbotModel.LOCK_ULTRA_MAX: SupportedModels.LOCK_ULTRA_MAX,
|
||||
SwitchbotModel.AIR_PURIFIER_JP: SupportedModels.AIR_PURIFIER_JP,
|
||||
SwitchbotModel.AIR_PURIFIER_US: SupportedModels.AIR_PURIFIER_US,
|
||||
SwitchbotModel.AIR_PURIFIER_TABLE_JP: SupportedModels.AIR_PURIFIER_TABLE_JP,
|
||||
@@ -159,6 +161,7 @@ ENCRYPTED_MODELS = {
|
||||
SwitchbotModel.LOCK_PRO,
|
||||
SwitchbotModel.LOCK_LITE,
|
||||
SwitchbotModel.LOCK_ULTRA,
|
||||
SwitchbotModel.LOCK_ULTRA_MAX,
|
||||
SwitchbotModel.AIR_PURIFIER_JP,
|
||||
SwitchbotModel.AIR_PURIFIER_US,
|
||||
SwitchbotModel.AIR_PURIFIER_TABLE_JP,
|
||||
@@ -193,6 +196,7 @@ ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
|
||||
SwitchbotModel.RELAY_SWITCH_1: switchbot.SwitchbotRelaySwitch,
|
||||
SwitchbotModel.LOCK_LITE: switchbot.SwitchbotLock,
|
||||
SwitchbotModel.LOCK_ULTRA: switchbot.SwitchbotLock,
|
||||
SwitchbotModel.LOCK_ULTRA_MAX: switchbot.SwitchbotLock,
|
||||
SwitchbotModel.AIR_PURIFIER_JP: switchbot.SwitchbotAirPurifier,
|
||||
SwitchbotModel.AIR_PURIFIER_US: switchbot.SwitchbotAirPurifier,
|
||||
SwitchbotModel.AIR_PURIFIER_TABLE_JP: switchbot.SwitchbotAirPurifier,
|
||||
|
||||
@@ -860,6 +860,31 @@ LOCK_PRO_WIFI_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||
)
|
||||
|
||||
|
||||
LOCK_ULTRA_MAX_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||
name="Lock Ultra Max",
|
||||
manufacturer_data={2409: b"\xb0\xe9\xfe\x11\x22\x33\x2a\x88\x18\x64\x00\x91"},
|
||||
service_data={
|
||||
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x80\x64\x00\x11\x9f\xb8"
|
||||
},
|
||||
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||
address="AA:BB:CC:DD:EE:FF",
|
||||
rssi=-60,
|
||||
source="local",
|
||||
advertisement=generate_advertisement_data(
|
||||
local_name="Lock Ultra Max",
|
||||
manufacturer_data={2409: b"\xb0\xe9\xfe\x11\x22\x33\x2a\x88\x18\x64\x00\x91"},
|
||||
service_data={
|
||||
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x80\x64\x00\x11\x9f\xb8"
|
||||
},
|
||||
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||
),
|
||||
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Lock Ultra Max"),
|
||||
time=0,
|
||||
connectable=True,
|
||||
tx_power=-127,
|
||||
)
|
||||
|
||||
|
||||
AIR_PURIFIER_TABLE_US_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||
name="Air Purifier Table US",
|
||||
manufacturer_data={
|
||||
|
||||
@@ -26,6 +26,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from . import (
|
||||
LOCK_ULTRA_MAX_SERVICE_INFO,
|
||||
NOT_SWITCHBOT_INFO,
|
||||
USER_INPUT,
|
||||
WOCURTAIN_SERVICE_INFO,
|
||||
@@ -176,6 +177,40 @@ async def test_bluetooth_discovery_encrypted_key(hass: HomeAssistant) -> None:
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_bluetooth_discovery_lock_ultra_max(hass: HomeAssistant) -> None:
|
||||
"""Test discovery of an encrypted Lock Ultra Max."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_BLUETOOTH},
|
||||
data=LOCK_ULTRA_MAX_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"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "encrypted_key"
|
||||
|
||||
with (
|
||||
patch_async_setup_entry() as mock_setup_entry,
|
||||
patch("switchbot.SwitchbotLock.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["data"][CONF_SENSOR_TYPE] == "lock_ultra_max"
|
||||
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(
|
||||
@@ -1883,15 +1918,18 @@ async def test_user_show_menu_when_no_scanners(hass: HomeAssistant) -> None:
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_options_flow_lock_pro_wifi(hass: HomeAssistant) -> None:
|
||||
"""Test updating options for lock_pro_wifi."""
|
||||
@pytest.mark.parametrize("sensor_type", ["lock_pro_wifi", "lock_ultra_max"])
|
||||
async def test_options_flow_lock_with_night_latch(
|
||||
hass: HomeAssistant, sensor_type: str
|
||||
) -> None:
|
||||
"""Test updating options for locks with night latch support."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_NAME: "test-name",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_SENSOR_TYPE: "lock_pro_wifi",
|
||||
CONF_SENSOR_TYPE: sensor_type,
|
||||
},
|
||||
options={CONF_RETRY_COUNT: 10},
|
||||
unique_id="aabbccddeeff",
|
||||
|
||||
@@ -24,6 +24,7 @@ from . import (
|
||||
LOCK_LITE_SERVICE_INFO,
|
||||
LOCK_PRO_WIFI_SERVICE_INFO,
|
||||
LOCK_SERVICE_INFO,
|
||||
LOCK_ULTRA_MAX_SERVICE_INFO,
|
||||
LOCK_ULTRA_SERVICE_INFO,
|
||||
LOCK_VISION_PRO_SERVICE_INFO,
|
||||
LOCK_VISION_SERVICE_INFO,
|
||||
@@ -41,6 +42,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info
|
||||
("lock", LOCK_SERVICE_INFO),
|
||||
("lock_lite", LOCK_LITE_SERVICE_INFO),
|
||||
("lock_ultra", LOCK_ULTRA_SERVICE_INFO),
|
||||
("lock_ultra_max", LOCK_ULTRA_MAX_SERVICE_INFO),
|
||||
("lock_vision", LOCK_VISION_SERVICE_INFO),
|
||||
("lock_vision_pro", LOCK_VISION_PRO_SERVICE_INFO),
|
||||
("lock_pro_wifi", LOCK_PRO_WIFI_SERVICE_INFO),
|
||||
@@ -92,6 +94,7 @@ async def test_lock_services(
|
||||
("lock", LOCK_SERVICE_INFO),
|
||||
("lock_lite", LOCK_LITE_SERVICE_INFO),
|
||||
("lock_ultra", LOCK_ULTRA_SERVICE_INFO),
|
||||
("lock_ultra_max", LOCK_ULTRA_MAX_SERVICE_INFO),
|
||||
("lock_vision", LOCK_VISION_SERVICE_INFO),
|
||||
("lock_vision_pro", LOCK_VISION_PRO_SERVICE_INFO),
|
||||
("lock_pro_wifi", LOCK_PRO_WIFI_SERVICE_INFO),
|
||||
@@ -137,6 +140,28 @@ async def test_lock_services_with_night_latch_enabled(
|
||||
mocked_instance.assert_awaited_once()
|
||||
|
||||
|
||||
async def test_lock_ultra_max_entities(
|
||||
hass: HomeAssistant,
|
||||
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
|
||||
) -> None:
|
||||
"""Test Lock Ultra Max creates lock, door, and battery entities."""
|
||||
inject_bluetooth_service_info(hass, LOCK_ULTRA_MAX_SERVICE_INFO)
|
||||
|
||||
entry = mock_entry_encrypted_factory(sensor_type="lock_ultra_max")
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch.multiple(
|
||||
"homeassistant.components.switchbot.lock.switchbot.SwitchbotLock",
|
||||
update=AsyncMock(return_value=None),
|
||||
):
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("lock.test_name") is not None
|
||||
assert hass.states.get("binary_sensor.test_name") is not None
|
||||
assert hass.states.get("sensor.test_name_battery") is not None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "error_message"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user