Add floor lamp and strip light 3 for switchbot integration (#147517)

This commit is contained in:
Retha Runolfsson 2025-06-26 00:01:10 +08:00 committed by GitHub
parent e210681751
commit c05d8aab1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 186 additions and 5 deletions

View File

@ -93,6 +93,8 @@ PLATFORMS_BY_TYPE = {
SupportedModels.AIR_PURIFIER.value: [Platform.FAN, Platform.SENSOR],
SupportedModels.AIR_PURIFIER_TABLE.value: [Platform.FAN, Platform.SENSOR],
SupportedModels.EVAPORATIVE_HUMIDIFIER: [Platform.HUMIDIFIER, Platform.SENSOR],
SupportedModels.FLOOR_LAMP.value: [Platform.LIGHT, Platform.SENSOR],
SupportedModels.STRIP_LIGHT_3.value: [Platform.LIGHT, Platform.SENSOR],
}
CLASS_BY_DEVICE = {
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
@ -119,6 +121,8 @@ CLASS_BY_DEVICE = {
SupportedModels.AIR_PURIFIER.value: switchbot.SwitchbotAirPurifier,
SupportedModels.AIR_PURIFIER_TABLE.value: switchbot.SwitchbotAirPurifier,
SupportedModels.EVAPORATIVE_HUMIDIFIER: switchbot.SwitchbotEvaporativeHumidifier,
SupportedModels.FLOOR_LAMP.value: switchbot.SwitchbotStripLight3,
SupportedModels.STRIP_LIGHT_3.value: switchbot.SwitchbotStripLight3,
}

View File

@ -49,6 +49,8 @@ class SupportedModels(StrEnum):
AIR_PURIFIER = "air_purifier"
AIR_PURIFIER_TABLE = "air_purifier_table"
EVAPORATIVE_HUMIDIFIER = "evaporative_humidifier"
FLOOR_LAMP = "floor_lamp"
STRIP_LIGHT_3 = "strip_light_3"
CONNECTABLE_SUPPORTED_MODEL_TYPES = {
@ -77,6 +79,8 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = {
SwitchbotModel.AIR_PURIFIER: SupportedModels.AIR_PURIFIER,
SwitchbotModel.AIR_PURIFIER_TABLE: SupportedModels.AIR_PURIFIER_TABLE,
SwitchbotModel.EVAPORATIVE_HUMIDIFIER: SupportedModels.EVAPORATIVE_HUMIDIFIER,
SwitchbotModel.FLOOR_LAMP: SupportedModels.FLOOR_LAMP,
SwitchbotModel.STRIP_LIGHT_3: SupportedModels.STRIP_LIGHT_3,
}
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
@ -106,6 +110,8 @@ ENCRYPTED_MODELS = {
SwitchbotModel.AIR_PURIFIER,
SwitchbotModel.AIR_PURIFIER_TABLE,
SwitchbotModel.EVAPORATIVE_HUMIDIFIER,
SwitchbotModel.FLOOR_LAMP,
SwitchbotModel.STRIP_LIGHT_3,
}
ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
@ -120,6 +126,8 @@ ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
SwitchbotModel.AIR_PURIFIER: switchbot.SwitchbotAirPurifier,
SwitchbotModel.AIR_PURIFIER_TABLE: switchbot.SwitchbotAirPurifier,
SwitchbotModel.EVAPORATIVE_HUMIDIFIER: switchbot.SwitchbotEvaporativeHumidifier,
SwitchbotModel.FLOOR_LAMP: switchbot.SwitchbotStripLight3,
SwitchbotModel.STRIP_LIGHT_3: switchbot.SwitchbotStripLight3,
}
HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = {

View File

@ -941,3 +941,61 @@ CEILING_LIGHT_SERVICE_INFO = BluetoothServiceInfoBleak(
connectable=True,
tx_power=-127,
)
STRIP_LIGHT_3_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Strip Light 3",
manufacturer_data={
2409: b'\xc0N0\xe0U\x9a\x85\x9e"\xd0\x00\x00\x00\x00\x00\x00\x12\x91\x00',
},
service_data={
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x00\x00\x00\x10\xd0\xb1"
},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
address="AA:BB:CC:DD:EE:FF",
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
local_name="Strip Light 3",
manufacturer_data={
2409: b'\xc0N0\xe0U\x9a\x85\x9e"\xd0\x00\x00\x00\x00\x00\x00\x12\x91\x00',
},
service_data={
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x00\x00\x00\x10\xd0\xb1"
},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Strip Light 3"),
time=0,
connectable=True,
tx_power=-127,
)
FLOOR_LAMP_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Floor Lamp",
manufacturer_data={
2409: b'\xa0\x85\xe3e,\x06P\xaa"\xd4\x00\x00\x00\x00\x00\x00\r\x93\x00',
},
service_data={
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x00\x00\x00\x10\xd0\xb0"
},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
address="AA:BB:CC:DD:EE:FF",
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
local_name="Floor Lamp",
manufacturer_data={
2409: b'\xa0\x85\xe3e,\x06P\xaa"\xd4\x00\x00\x00\x00\x00\x00\r\x93\x00',
},
service_data={
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x00\x00\x00\x10\xd0\xb0"
},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Floor Lamp"),
time=0,
connectable=True,
tx_power=-127,
)

View File

@ -7,6 +7,7 @@ from unittest.mock import AsyncMock, patch
import pytest
from switchbot.devices.device import SwitchbotOperationError
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP_KELVIN,
@ -20,7 +21,13 @@ from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from . import BULB_SERVICE_INFO, CEILING_LIGHT_SERVICE_INFO, WOSTRIP_SERVICE_INFO
from . import (
BULB_SERVICE_INFO,
CEILING_LIGHT_SERVICE_INFO,
FLOOR_LAMP_SERVICE_INFO,
STRIP_LIGHT_3_SERVICE_INFO,
WOSTRIP_SERVICE_INFO,
)
from tests.common import MockConfigEntry
from tests.components.bluetooth import inject_bluetooth_service_info
@ -71,9 +78,9 @@ BULB_PARAMETERS = (
SET_COLOR_TEMP_PARAMETERS,
(
SERVICE_TURN_ON,
{ATTR_EFFECT: "Breathing"},
{ATTR_EFFECT: "breathing"},
"set_effect",
("Breathing",),
("breathing",),
),
],
)
@ -95,9 +102,25 @@ STRIP_LIGHT_PARAMETERS = (
SET_RGB_PARAMETERS,
(
SERVICE_TURN_ON,
{ATTR_EFFECT: "Halloween"},
{ATTR_EFFECT: "halloween"},
"set_effect",
("Halloween",),
("halloween",),
),
],
)
FLOOR_LAMP_PARAMETERS = (
COMMON_PARAMETERS,
[
TURN_ON_PARAMETERS,
TURN_OFF_PARAMETERS,
SET_BRIGHTNESS_PARAMETERS,
SET_RGB_PARAMETERS,
SET_COLOR_TEMP_PARAMETERS,
(
SERVICE_TURN_ON,
{ATTR_EFFECT: "halloween"},
"set_effect",
("halloween",),
),
],
)
@ -317,3 +340,91 @@ async def test_strip_light_services_exception(
{**service_data, ATTR_ENTITY_ID: entity_id},
blocking=True,
)
@pytest.mark.parametrize(
("sensor_type", "service_info"),
[
("strip_light_3", STRIP_LIGHT_3_SERVICE_INFO),
("floor_lamp", FLOOR_LAMP_SERVICE_INFO),
],
)
@pytest.mark.parametrize(*FLOOR_LAMP_PARAMETERS)
async def test_floor_lamp_services(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
sensor_type: str,
service_info: BluetoothServiceInfoBleak,
service: str,
service_data: dict,
mock_method: str,
expected_args: Any,
) -> None:
"""Test all SwitchBot floor lamp services."""
inject_bluetooth_service_info(hass, service_info)
entry = mock_entry_encrypted_factory(sensor_type=sensor_type)
entry.add_to_hass(hass)
entity_id = "light.test_name"
mocked_instance = AsyncMock(return_value=True)
with patch.multiple(
"homeassistant.components.switchbot.light.switchbot.SwitchbotStripLight3",
**{mock_method: mocked_instance},
update=AsyncMock(return_value=None),
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
await hass.services.async_call(
LIGHT_DOMAIN,
service,
{**service_data, ATTR_ENTITY_ID: entity_id},
blocking=True,
)
mocked_instance.assert_awaited_once_with(*expected_args)
@pytest.mark.parametrize(
("sensor_type", "service_info"),
[
("strip_light_3", STRIP_LIGHT_3_SERVICE_INFO),
("floor_lamp", FLOOR_LAMP_SERVICE_INFO),
],
)
@pytest.mark.parametrize(*FLOOR_LAMP_PARAMETERS)
async def test_floor_lamp_services_exception(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
sensor_type: str,
service_info: BluetoothServiceInfoBleak,
service: str,
service_data: dict,
mock_method: str,
expected_args: Any,
) -> None:
"""Test all SwitchBot floor lamp services with exception."""
inject_bluetooth_service_info(hass, service_info)
entry = mock_entry_encrypted_factory(sensor_type=sensor_type)
entry.add_to_hass(hass)
entity_id = "light.test_name"
exception = SwitchbotOperationError("Operation failed")
error_message = "An error occurred while performing the action: Operation failed"
with patch.multiple(
"homeassistant.components.switchbot.light.switchbot.SwitchbotStripLight3",
**{mock_method: AsyncMock(side_effect=exception)},
update=AsyncMock(return_value=None),
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
with pytest.raises(HomeAssistantError, match=error_message):
await hass.services.async_call(
LIGHT_DOMAIN,
service,
{**service_data, ATTR_ENTITY_ID: entity_id},
blocking=True,
)