mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Add effect mode support for switchbot light (#147326)
* add support for strip light3 and floor lamp * clear the color mode * add led unit test * use property for effect * fix color mode issue * remove new products * fix adv data * adjust log level * add translation and icon
This commit is contained in:
parent
8393f17bb3
commit
c5f8acfe93
@ -60,6 +60,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"light": {
|
||||||
|
"light": {
|
||||||
|
"state_attributes": {
|
||||||
|
"effect": {
|
||||||
|
"state": {
|
||||||
|
"christmas": "mdi:string-lights",
|
||||||
|
"halloween": "mdi:halloween",
|
||||||
|
"sunset": "mdi:weather-sunset",
|
||||||
|
"vitality": "mdi:parachute",
|
||||||
|
"flashing": "mdi:flash",
|
||||||
|
"strobe": "mdi:led-strip-variant",
|
||||||
|
"fade": "mdi:water-opacity",
|
||||||
|
"smooth": "mdi:led-strip-variant",
|
||||||
|
"forest": "mdi:forest",
|
||||||
|
"ocean": "mdi:waves",
|
||||||
|
"autumn": "mdi:leaf-maple",
|
||||||
|
"cool": "mdi:emoticon-cool-outline",
|
||||||
|
"flow": "mdi:pulse",
|
||||||
|
"relax": "mdi:coffee",
|
||||||
|
"modern": "mdi:school-outline",
|
||||||
|
"rose": "mdi:flower",
|
||||||
|
"colorful": "mdi:looks",
|
||||||
|
"flickering": "mdi:led-strip-variant",
|
||||||
|
"breathing": "mdi:heart-pulse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
import switchbot
|
import switchbot
|
||||||
@ -10,14 +11,16 @@ from switchbot import ColorMode as SwitchBotColorMode
|
|||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_TEMP_KELVIN,
|
ATTR_COLOR_TEMP_KELVIN,
|
||||||
|
ATTR_EFFECT,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
ColorMode,
|
ColorMode,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
|
LightEntityFeature,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
|
|
||||||
from .coordinator import SwitchbotConfigEntry, SwitchbotDataUpdateCoordinator
|
from .coordinator import SwitchbotConfigEntry
|
||||||
from .entity import SwitchbotEntity, exception_handler
|
from .entity import SwitchbotEntity, exception_handler
|
||||||
|
|
||||||
SWITCHBOT_COLOR_MODE_TO_HASS = {
|
SWITCHBOT_COLOR_MODE_TO_HASS = {
|
||||||
@ -25,6 +28,7 @@ SWITCHBOT_COLOR_MODE_TO_HASS = {
|
|||||||
SwitchBotColorMode.COLOR_TEMP: ColorMode.COLOR_TEMP,
|
SwitchBotColorMode.COLOR_TEMP: ColorMode.COLOR_TEMP,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
PARALLEL_UPDATES = 0
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
@ -42,34 +46,69 @@ class SwitchbotLightEntity(SwitchbotEntity, LightEntity):
|
|||||||
|
|
||||||
_device: switchbot.SwitchbotBaseLight
|
_device: switchbot.SwitchbotBaseLight
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
_attr_translation_key = "light"
|
||||||
|
|
||||||
def __init__(self, coordinator: SwitchbotDataUpdateCoordinator) -> None:
|
@property
|
||||||
"""Initialize the Switchbot light."""
|
def max_color_temp_kelvin(self) -> int:
|
||||||
super().__init__(coordinator)
|
"""Return the max color temperature."""
|
||||||
device = self._device
|
return self._device.max_temp
|
||||||
self._attr_max_color_temp_kelvin = device.max_temp
|
|
||||||
self._attr_min_color_temp_kelvin = device.min_temp
|
|
||||||
self._attr_supported_color_modes = {
|
|
||||||
SWITCHBOT_COLOR_MODE_TO_HASS[mode] for mode in device.color_modes
|
|
||||||
}
|
|
||||||
self._async_update_attrs()
|
|
||||||
|
|
||||||
@callback
|
@property
|
||||||
def _async_update_attrs(self) -> None:
|
def min_color_temp_kelvin(self) -> int:
|
||||||
"""Handle updating _attr values."""
|
"""Return the min color temperature."""
|
||||||
device = self._device
|
return self._device.min_temp
|
||||||
self._attr_is_on = self._device.on
|
|
||||||
self._attr_brightness = max(0, min(255, round(device.brightness * 2.55)))
|
@property
|
||||||
if device.color_mode == SwitchBotColorMode.COLOR_TEMP:
|
def supported_color_modes(self) -> set[ColorMode]:
|
||||||
self._attr_color_temp_kelvin = device.color_temp
|
"""Return the supported color modes."""
|
||||||
self._attr_color_mode = ColorMode.COLOR_TEMP
|
return {SWITCHBOT_COLOR_MODE_TO_HASS[mode] for mode in self._device.color_modes}
|
||||||
return
|
|
||||||
self._attr_rgb_color = device.rgb
|
@property
|
||||||
self._attr_color_mode = ColorMode.RGB
|
def supported_features(self) -> LightEntityFeature:
|
||||||
|
"""Return the supported features."""
|
||||||
|
return LightEntityFeature.EFFECT if self.effect_list else LightEntityFeature(0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def brightness(self) -> int | None:
|
||||||
|
"""Return the brightness of the light."""
|
||||||
|
return max(0, min(255, round(self._device.brightness * 2.55)))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color_mode(self) -> ColorMode | None:
|
||||||
|
"""Return the color mode of the light."""
|
||||||
|
return SWITCHBOT_COLOR_MODE_TO_HASS.get(
|
||||||
|
self._device.color_mode, ColorMode.UNKNOWN
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def effect_list(self) -> list[str] | None:
|
||||||
|
"""Return the list of effects supported by the light."""
|
||||||
|
return self._device.get_effect_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def effect(self) -> str | None:
|
||||||
|
"""Return the current effect of the light."""
|
||||||
|
return self._device.get_effect()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rgb_color(self) -> tuple[int, int, int] | None:
|
||||||
|
"""Return the RGB color of the light."""
|
||||||
|
return self._device.rgb
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color_temp_kelvin(self) -> int | None:
|
||||||
|
"""Return the color temperature of the light."""
|
||||||
|
return self._device.color_temp
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return true if the light is on."""
|
||||||
|
return self._device.on
|
||||||
|
|
||||||
@exception_handler
|
@exception_handler
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Instruct the light to turn on."""
|
"""Instruct the light to turn on."""
|
||||||
|
_LOGGER.debug("Turning on light %s, address %s", kwargs, self._address)
|
||||||
brightness = round(
|
brightness = round(
|
||||||
cast(int, kwargs.get(ATTR_BRIGHTNESS, self.brightness)) / 255 * 100
|
cast(int, kwargs.get(ATTR_BRIGHTNESS, self.brightness)) / 255 * 100
|
||||||
)
|
)
|
||||||
@ -82,6 +121,10 @@ class SwitchbotLightEntity(SwitchbotEntity, LightEntity):
|
|||||||
kelvin = max(2700, min(6500, kwargs[ATTR_COLOR_TEMP_KELVIN]))
|
kelvin = max(2700, min(6500, kwargs[ATTR_COLOR_TEMP_KELVIN]))
|
||||||
await self._device.set_color_temp(brightness, kelvin)
|
await self._device.set_color_temp(brightness, kelvin)
|
||||||
return
|
return
|
||||||
|
if ATTR_EFFECT in kwargs:
|
||||||
|
effect = kwargs[ATTR_EFFECT]
|
||||||
|
await self._device.set_effect(effect)
|
||||||
|
return
|
||||||
if ATTR_RGB_COLOR in kwargs:
|
if ATTR_RGB_COLOR in kwargs:
|
||||||
rgb = kwargs[ATTR_RGB_COLOR]
|
rgb = kwargs[ATTR_RGB_COLOR]
|
||||||
await self._device.set_rgb(brightness, rgb[0], rgb[1], rgb[2])
|
await self._device.set_rgb(brightness, rgb[0], rgb[1], rgb[2])
|
||||||
@ -94,4 +137,5 @@ class SwitchbotLightEntity(SwitchbotEntity, LightEntity):
|
|||||||
@exception_handler
|
@exception_handler
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Instruct the light to turn off."""
|
"""Instruct the light to turn off."""
|
||||||
|
_LOGGER.debug("Turning off light %s, address %s", kwargs, self._address)
|
||||||
await self._device.turn_off()
|
await self._device.turn_off()
|
||||||
|
@ -246,6 +246,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"light": {
|
||||||
|
"light": {
|
||||||
|
"state_attributes": {
|
||||||
|
"effect": {
|
||||||
|
"state": {
|
||||||
|
"christmas": "Christmas",
|
||||||
|
"halloween": "Halloween",
|
||||||
|
"sunset": "Sunset",
|
||||||
|
"vitality": "Vitality",
|
||||||
|
"flashing": "Flashing",
|
||||||
|
"strobe": "Strobe",
|
||||||
|
"fade": "Fade",
|
||||||
|
"smooth": "Smooth",
|
||||||
|
"forest": "Forest",
|
||||||
|
"ocean": "Ocean",
|
||||||
|
"autumn": "Autumn",
|
||||||
|
"cool": "Cool",
|
||||||
|
"flow": "Flow",
|
||||||
|
"relax": "Relax",
|
||||||
|
"modern": "Modern",
|
||||||
|
"rose": "Rose",
|
||||||
|
"colorful": "Colorful",
|
||||||
|
"flickering": "Flickering",
|
||||||
|
"breathing": "Breathing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exceptions": {
|
"exceptions": {
|
||||||
|
@ -883,3 +883,61 @@ EVAPORATIVE_HUMIDIFIER_SERVICE_INFO = BluetoothServiceInfoBleak(
|
|||||||
connectable=True,
|
connectable=True,
|
||||||
tx_power=-127,
|
tx_power=-127,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
BULB_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||||
|
name="Bulb",
|
||||||
|
manufacturer_data={
|
||||||
|
2409: b"@L\xca\xa7_\x12\x02\x81\x12\x00\x00",
|
||||||
|
},
|
||||||
|
service_data={
|
||||||
|
"0000fd3d-0000-1000-8000-00805f9b34fb": b"u\x00d",
|
||||||
|
},
|
||||||
|
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||||
|
address="AA:BB:CC:DD:EE:FF",
|
||||||
|
rssi=-60,
|
||||||
|
source="local",
|
||||||
|
advertisement=generate_advertisement_data(
|
||||||
|
local_name="Bulb",
|
||||||
|
manufacturer_data={
|
||||||
|
2409: b"@L\xca\xa7_\x12\x02\x81\x12\x00\x00",
|
||||||
|
},
|
||||||
|
service_data={
|
||||||
|
"0000fd3d-0000-1000-8000-00805f9b34fb": b"u\x00d",
|
||||||
|
},
|
||||||
|
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||||
|
),
|
||||||
|
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Bulb"),
|
||||||
|
time=0,
|
||||||
|
connectable=True,
|
||||||
|
tx_power=-127,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
CEILING_LIGHT_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||||
|
name="Ceiling Light",
|
||||||
|
manufacturer_data={
|
||||||
|
2409: b"\xef\xfe\xfb\x9d\x10\xfe\n\x01\x18\xf3\xa4",
|
||||||
|
},
|
||||||
|
service_data={
|
||||||
|
"0000fd3d-0000-1000-8000-00805f9b34fb": b"q\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="Ceiling Light",
|
||||||
|
manufacturer_data={
|
||||||
|
2409: b"\xef\xfe\xfb\x9d\x10\xfe\n\x01\x18\xf3$",
|
||||||
|
},
|
||||||
|
service_data={
|
||||||
|
"0000fd3d-0000-1000-8000-00805f9b34fb": b"q\x00",
|
||||||
|
},
|
||||||
|
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||||
|
),
|
||||||
|
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Ceiling Light"),
|
||||||
|
time=0,
|
||||||
|
connectable=True,
|
||||||
|
tx_power=-127,
|
||||||
|
)
|
||||||
|
@ -5,12 +5,12 @@ from typing import Any
|
|||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from switchbot import ColorMode as switchbotColorMode
|
|
||||||
from switchbot.devices.device import SwitchbotOperationError
|
from switchbot.devices.device import SwitchbotOperationError
|
||||||
|
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_TEMP_KELVIN,
|
ATTR_COLOR_TEMP_KELVIN,
|
||||||
|
ATTR_EFFECT,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
DOMAIN as LIGHT_DOMAIN,
|
DOMAIN as LIGHT_DOMAIN,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
@ -20,89 +20,111 @@ from homeassistant.const import ATTR_ENTITY_ID
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from . import WOSTRIP_SERVICE_INFO
|
from . import BULB_SERVICE_INFO, CEILING_LIGHT_SERVICE_INFO, WOSTRIP_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
|
||||||
|
|
||||||
|
COMMON_PARAMETERS = (
|
||||||
@pytest.mark.parametrize(
|
"service",
|
||||||
(
|
"service_data",
|
||||||
"service",
|
"mock_method",
|
||||||
"service_data",
|
"expected_args",
|
||||||
"mock_method",
|
)
|
||||||
"expected_args",
|
TURN_ON_PARAMETERS = (
|
||||||
"color_modes",
|
SERVICE_TURN_ON,
|
||||||
"color_mode",
|
{},
|
||||||
),
|
"turn_on",
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
TURN_OFF_PARAMETERS = (
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{},
|
||||||
|
"turn_off",
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
SET_BRIGHTNESS_PARAMETERS = (
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_BRIGHTNESS: 128},
|
||||||
|
"set_brightness",
|
||||||
|
(round(128 / 255 * 100),),
|
||||||
|
)
|
||||||
|
SET_RGB_PARAMETERS = (
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
|
||||||
|
"set_rgb",
|
||||||
|
(round(128 / 255 * 100), 255, 0, 0),
|
||||||
|
)
|
||||||
|
SET_COLOR_TEMP_PARAMETERS = (
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
|
||||||
|
"set_color_temp",
|
||||||
|
(round(128 / 255 * 100), 4000),
|
||||||
|
)
|
||||||
|
BULB_PARAMETERS = (
|
||||||
|
COMMON_PARAMETERS,
|
||||||
[
|
[
|
||||||
(
|
TURN_ON_PARAMETERS,
|
||||||
SERVICE_TURN_OFF,
|
TURN_OFF_PARAMETERS,
|
||||||
{},
|
SET_BRIGHTNESS_PARAMETERS,
|
||||||
"turn_off",
|
SET_RGB_PARAMETERS,
|
||||||
(),
|
SET_COLOR_TEMP_PARAMETERS,
|
||||||
{switchbotColorMode.RGB},
|
|
||||||
switchbotColorMode.RGB,
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
{},
|
{ATTR_EFFECT: "Breathing"},
|
||||||
"turn_on",
|
"set_effect",
|
||||||
(),
|
("Breathing",),
|
||||||
{switchbotColorMode.RGB},
|
|
||||||
switchbotColorMode.RGB,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_BRIGHTNESS: 128},
|
|
||||||
"set_brightness",
|
|
||||||
(round(128 / 255 * 100),),
|
|
||||||
{switchbotColorMode.RGB},
|
|
||||||
switchbotColorMode.RGB,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_RGB_COLOR: (255, 0, 0)},
|
|
||||||
"set_rgb",
|
|
||||||
(round(255 / 255 * 100), 255, 0, 0),
|
|
||||||
{switchbotColorMode.RGB},
|
|
||||||
switchbotColorMode.RGB,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_COLOR_TEMP_KELVIN: 4000},
|
|
||||||
"set_color_temp",
|
|
||||||
(100, 4000),
|
|
||||||
{switchbotColorMode.COLOR_TEMP},
|
|
||||||
switchbotColorMode.COLOR_TEMP,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_light_strip_services(
|
CEILING_LIGHT_PARAMETERS = (
|
||||||
|
COMMON_PARAMETERS,
|
||||||
|
[
|
||||||
|
TURN_ON_PARAMETERS,
|
||||||
|
TURN_OFF_PARAMETERS,
|
||||||
|
SET_BRIGHTNESS_PARAMETERS,
|
||||||
|
SET_COLOR_TEMP_PARAMETERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
STRIP_LIGHT_PARAMETERS = (
|
||||||
|
COMMON_PARAMETERS,
|
||||||
|
[
|
||||||
|
TURN_ON_PARAMETERS,
|
||||||
|
TURN_OFF_PARAMETERS,
|
||||||
|
SET_BRIGHTNESS_PARAMETERS,
|
||||||
|
SET_RGB_PARAMETERS,
|
||||||
|
(
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_EFFECT: "Halloween"},
|
||||||
|
"set_effect",
|
||||||
|
("Halloween",),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(*BULB_PARAMETERS)
|
||||||
|
async def test_bulb_services(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_entry_factory: Callable[[str], MockConfigEntry],
|
mock_entry_factory: Callable[[str], MockConfigEntry],
|
||||||
service: str,
|
service: str,
|
||||||
service_data: dict,
|
service_data: dict,
|
||||||
mock_method: str,
|
mock_method: str,
|
||||||
expected_args: Any,
|
expected_args: Any,
|
||||||
color_modes: set | None,
|
|
||||||
color_mode: switchbotColorMode | None,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test all SwitchBot light strip services with proper parameters."""
|
"""Test all SwitchBot bulb services."""
|
||||||
inject_bluetooth_service_info(hass, WOSTRIP_SERVICE_INFO)
|
inject_bluetooth_service_info(hass, BULB_SERVICE_INFO)
|
||||||
|
|
||||||
entry = mock_entry_factory(sensor_type="light_strip")
|
entry = mock_entry_factory(sensor_type="bulb")
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
entity_id = "light.test_name"
|
entity_id = "light.test_name"
|
||||||
|
|
||||||
mocked_instance = AsyncMock(return_value=True)
|
mocked_instance = AsyncMock(return_value=True)
|
||||||
|
|
||||||
with patch.multiple(
|
with patch.multiple(
|
||||||
"homeassistant.components.switchbot.light.switchbot.SwitchbotLightStrip",
|
"homeassistant.components.switchbot.light.switchbot.SwitchbotBulb",
|
||||||
color_modes=color_modes,
|
|
||||||
color_mode=color_mode,
|
|
||||||
update=AsyncMock(return_value=None),
|
|
||||||
**{mock_method: mocked_instance},
|
**{mock_method: mocked_instance},
|
||||||
|
update=AsyncMock(return_value=None),
|
||||||
):
|
):
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -117,79 +139,173 @@ async def test_light_strip_services(
|
|||||||
mocked_instance.assert_awaited_once_with(*expected_args)
|
mocked_instance.assert_awaited_once_with(*expected_args)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(*BULB_PARAMETERS)
|
||||||
("exception", "error_message"),
|
async def test_bulb_services_exception(
|
||||||
[
|
|
||||||
(
|
|
||||||
SwitchbotOperationError("Operation failed"),
|
|
||||||
"An error occurred while performing the action: Operation failed",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
("service", "service_data", "mock_method", "color_modes", "color_mode"),
|
|
||||||
[
|
|
||||||
(
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{},
|
|
||||||
"turn_on",
|
|
||||||
{switchbotColorMode.RGB},
|
|
||||||
switchbotColorMode.RGB,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_TURN_OFF,
|
|
||||||
{},
|
|
||||||
"turn_off",
|
|
||||||
{switchbotColorMode.RGB},
|
|
||||||
switchbotColorMode.RGB,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_BRIGHTNESS: 128},
|
|
||||||
"set_brightness",
|
|
||||||
{switchbotColorMode.RGB},
|
|
||||||
switchbotColorMode.RGB,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_RGB_COLOR: (255, 0, 0)},
|
|
||||||
"set_rgb",
|
|
||||||
{switchbotColorMode.RGB},
|
|
||||||
switchbotColorMode.RGB,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_COLOR_TEMP_KELVIN: 4000},
|
|
||||||
"set_color_temp",
|
|
||||||
{switchbotColorMode.COLOR_TEMP},
|
|
||||||
switchbotColorMode.COLOR_TEMP,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_exception_handling_light_service(
|
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_entry_factory: Callable[[str], MockConfigEntry],
|
mock_entry_factory: Callable[[str], MockConfigEntry],
|
||||||
service: str,
|
service: str,
|
||||||
service_data: dict,
|
service_data: dict,
|
||||||
mock_method: str,
|
mock_method: str,
|
||||||
color_modes: set | None,
|
expected_args: Any,
|
||||||
color_mode: switchbotColorMode | None,
|
|
||||||
exception: Exception,
|
|
||||||
error_message: str,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test exception handling for light service with exception."""
|
"""Test all SwitchBot bulb services with exception."""
|
||||||
inject_bluetooth_service_info(hass, WOSTRIP_SERVICE_INFO)
|
inject_bluetooth_service_info(hass, BULB_SERVICE_INFO)
|
||||||
|
|
||||||
entry = mock_entry_factory(sensor_type="light_strip")
|
entry = mock_entry_factory(sensor_type="bulb")
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
entity_id = "light.test_name"
|
entity_id = "light.test_name"
|
||||||
|
|
||||||
|
exception = SwitchbotOperationError("Operation failed")
|
||||||
|
error_message = "An error occurred while performing the action: Operation failed"
|
||||||
|
|
||||||
with patch.multiple(
|
with patch.multiple(
|
||||||
"homeassistant.components.switchbot.light.switchbot.SwitchbotLightStrip",
|
"homeassistant.components.switchbot.light.switchbot.SwitchbotBulb",
|
||||||
color_modes=color_modes,
|
|
||||||
color_mode=color_mode,
|
|
||||||
update=AsyncMock(return_value=None),
|
|
||||||
**{mock_method: AsyncMock(side_effect=exception)},
|
**{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,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(*CEILING_LIGHT_PARAMETERS)
|
||||||
|
async def test_ceiling_light_services(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_entry_factory: Callable[[str], MockConfigEntry],
|
||||||
|
service: str,
|
||||||
|
service_data: dict,
|
||||||
|
mock_method: str,
|
||||||
|
expected_args: Any,
|
||||||
|
) -> None:
|
||||||
|
"""Test all SwitchBot ceiling light services."""
|
||||||
|
inject_bluetooth_service_info(hass, CEILING_LIGHT_SERVICE_INFO)
|
||||||
|
|
||||||
|
entry = mock_entry_factory(sensor_type="ceiling_light")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
entity_id = "light.test_name"
|
||||||
|
|
||||||
|
mocked_instance = AsyncMock(return_value=True)
|
||||||
|
|
||||||
|
with patch.multiple(
|
||||||
|
"homeassistant.components.switchbot.light.switchbot.SwitchbotCeilingLight",
|
||||||
|
**{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(*CEILING_LIGHT_PARAMETERS)
|
||||||
|
async def test_ceiling_light_services_exception(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_entry_factory: Callable[[str], MockConfigEntry],
|
||||||
|
service: str,
|
||||||
|
service_data: dict,
|
||||||
|
mock_method: str,
|
||||||
|
expected_args: Any,
|
||||||
|
) -> None:
|
||||||
|
"""Test all SwitchBot ceiling light services with exception."""
|
||||||
|
inject_bluetooth_service_info(hass, CEILING_LIGHT_SERVICE_INFO)
|
||||||
|
|
||||||
|
entry = mock_entry_factory(sensor_type="ceiling_light")
|
||||||
|
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.SwitchbotCeilingLight",
|
||||||
|
**{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,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(*STRIP_LIGHT_PARAMETERS)
|
||||||
|
async def test_strip_light_services(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_entry_factory: Callable[[str], MockConfigEntry],
|
||||||
|
service: str,
|
||||||
|
service_data: dict,
|
||||||
|
mock_method: str,
|
||||||
|
expected_args: Any,
|
||||||
|
) -> None:
|
||||||
|
"""Test all SwitchBot strip light services."""
|
||||||
|
inject_bluetooth_service_info(hass, WOSTRIP_SERVICE_INFO)
|
||||||
|
|
||||||
|
entry = mock_entry_factory(sensor_type="light_strip")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
entity_id = "light.test_name"
|
||||||
|
|
||||||
|
mocked_instance = AsyncMock(return_value=True)
|
||||||
|
|
||||||
|
with patch.multiple(
|
||||||
|
"homeassistant.components.switchbot.light.switchbot.SwitchbotLightStrip",
|
||||||
|
**{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(*STRIP_LIGHT_PARAMETERS)
|
||||||
|
async def test_strip_light_services_exception(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_entry_factory: Callable[[str], MockConfigEntry],
|
||||||
|
service: str,
|
||||||
|
service_data: dict,
|
||||||
|
mock_method: str,
|
||||||
|
expected_args: Any,
|
||||||
|
) -> None:
|
||||||
|
"""Test all SwitchBot strip light services with exception."""
|
||||||
|
inject_bluetooth_service_info(hass, WOSTRIP_SERVICE_INFO)
|
||||||
|
|
||||||
|
entry = mock_entry_factory(sensor_type="light_strip")
|
||||||
|
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.SwitchbotLightStrip",
|
||||||
|
**{mock_method: AsyncMock(side_effect=exception)},
|
||||||
|
update=AsyncMock(return_value=None),
|
||||||
):
|
):
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user