Migrate Shelly to use kelvin for color temperature (#79880)
This commit is contained in:
parent
f65dcf3c35
commit
9019fcb5c5
7 changed files with 450 additions and 37 deletions
|
@ -1109,7 +1109,6 @@ omit =
|
||||||
homeassistant/components/shelly/climate.py
|
homeassistant/components/shelly/climate.py
|
||||||
homeassistant/components/shelly/coordinator.py
|
homeassistant/components/shelly/coordinator.py
|
||||||
homeassistant/components/shelly/entity.py
|
homeassistant/components/shelly/entity.py
|
||||||
homeassistant/components/shelly/light.py
|
|
||||||
homeassistant/components/shelly/number.py
|
homeassistant/components/shelly/number.py
|
||||||
homeassistant/components/shelly/sensor.py
|
homeassistant/components/shelly/sensor.py
|
||||||
homeassistant/components/shelly/utils.py
|
homeassistant/components/shelly/utils.py
|
||||||
|
|
|
@ -7,7 +7,7 @@ from aioshelly.block_device import Block
|
||||||
|
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP_KELVIN,
|
||||||
ATTR_EFFECT,
|
ATTR_EFFECT,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
ATTR_RGBW_COLOR,
|
ATTR_RGBW_COLOR,
|
||||||
|
@ -20,10 +20,6 @@ from homeassistant.components.light import (
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util.color import (
|
|
||||||
color_temperature_kelvin_to_mired,
|
|
||||||
color_temperature_mired_to_kelvin,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
DUAL_MODE_LIGHT_MODELS,
|
DUAL_MODE_LIGHT_MODELS,
|
||||||
|
@ -49,10 +45,6 @@ from .utils import (
|
||||||
is_rpc_channel_type_light,
|
is_rpc_channel_type_light,
|
||||||
)
|
)
|
||||||
|
|
||||||
MIRED_MAX_VALUE_WHITE = color_temperature_kelvin_to_mired(KELVIN_MIN_VALUE_WHITE)
|
|
||||||
MIRED_MIN_VALUE = color_temperature_kelvin_to_mired(KELVIN_MAX_VALUE)
|
|
||||||
MIRED_MAX_VALUE_COLOR = color_temperature_kelvin_to_mired(KELVIN_MIN_VALUE_COLOR)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -133,14 +125,11 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity):
|
||||||
super().__init__(coordinator, block)
|
super().__init__(coordinator, block)
|
||||||
self.control_result: dict[str, Any] | None = None
|
self.control_result: dict[str, Any] | None = None
|
||||||
self._attr_supported_color_modes = set()
|
self._attr_supported_color_modes = set()
|
||||||
self._attr_min_mireds = MIRED_MIN_VALUE
|
self._attr_min_color_temp_kelvin = KELVIN_MIN_VALUE_WHITE
|
||||||
self._min_kelvin: int = KELVIN_MIN_VALUE_WHITE
|
self._attr_max_color_temp_kelvin = KELVIN_MAX_VALUE
|
||||||
self._attr_max_mireds = MIRED_MAX_VALUE_WHITE
|
|
||||||
self._max_kelvin: int = KELVIN_MAX_VALUE
|
|
||||||
|
|
||||||
if hasattr(block, "red") and hasattr(block, "green") and hasattr(block, "blue"):
|
if hasattr(block, "red") and hasattr(block, "green") and hasattr(block, "blue"):
|
||||||
self._attr_max_mireds = MIRED_MAX_VALUE_COLOR
|
self._attr_min_color_temp_kelvin = KELVIN_MIN_VALUE_COLOR
|
||||||
self._min_kelvin = KELVIN_MIN_VALUE_COLOR
|
|
||||||
if coordinator.model in RGBW_MODELS:
|
if coordinator.model in RGBW_MODELS:
|
||||||
self._attr_supported_color_modes.add(ColorMode.RGBW)
|
self._attr_supported_color_modes.add(ColorMode.RGBW)
|
||||||
else:
|
else:
|
||||||
|
@ -248,23 +237,20 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity):
|
||||||
return (*self.rgb_color, white)
|
return (*self.rgb_color, white)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color_temp(self) -> int:
|
def color_temp_kelvin(self) -> int:
|
||||||
"""Return the CT color value in mireds."""
|
"""Return the CT color value in kelvin."""
|
||||||
|
color_temp = cast(int, self.block.colorTemp)
|
||||||
if self.control_result:
|
if self.control_result:
|
||||||
color_temp = self.control_result["temp"]
|
color_temp = self.control_result["temp"]
|
||||||
else:
|
|
||||||
color_temp = self.block.colorTemp
|
|
||||||
|
|
||||||
color_temp = min(self._max_kelvin, max(self._min_kelvin, color_temp))
|
return min(
|
||||||
|
self.max_color_temp_kelvin,
|
||||||
return int(color_temperature_kelvin_to_mired(color_temp))
|
max(self.min_color_temp_kelvin, color_temp),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def effect_list(self) -> list[str] | None:
|
def effect_list(self) -> list[str] | None:
|
||||||
"""Return the list of supported effects."""
|
"""Return the list of supported effects."""
|
||||||
if not self.supported_features & LightEntityFeature.EFFECT:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self.coordinator.model == "SHBLB-1":
|
if self.coordinator.model == "SHBLB-1":
|
||||||
return list(SHBLB_1_RGB_EFFECTS.values())
|
return list(SHBLB_1_RGB_EFFECTS.values())
|
||||||
|
|
||||||
|
@ -273,9 +259,6 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity):
|
||||||
@property
|
@property
|
||||||
def effect(self) -> str | None:
|
def effect(self) -> str | None:
|
||||||
"""Return the current effect."""
|
"""Return the current effect."""
|
||||||
if not self.supported_features & LightEntityFeature.EFFECT:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self.control_result:
|
if self.control_result:
|
||||||
effect_index = self.control_result["effect"]
|
effect_index = self.control_result["effect"]
|
||||||
else:
|
else:
|
||||||
|
@ -309,12 +292,19 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity):
|
||||||
if hasattr(self.block, "brightness"):
|
if hasattr(self.block, "brightness"):
|
||||||
params["brightness"] = brightness_pct
|
params["brightness"] = brightness_pct
|
||||||
|
|
||||||
if ATTR_COLOR_TEMP in kwargs and ColorMode.COLOR_TEMP in supported_color_modes:
|
if (
|
||||||
color_temp = color_temperature_mired_to_kelvin(kwargs[ATTR_COLOR_TEMP])
|
ATTR_COLOR_TEMP_KELVIN in kwargs
|
||||||
color_temp = min(self._max_kelvin, max(self._min_kelvin, color_temp))
|
and ColorMode.COLOR_TEMP in supported_color_modes
|
||||||
|
):
|
||||||
# Color temperature change - used only in white mode, switch device mode to white
|
# Color temperature change - used only in white mode, switch device mode to white
|
||||||
|
color_temp = kwargs[ATTR_COLOR_TEMP_KELVIN]
|
||||||
set_mode = "white"
|
set_mode = "white"
|
||||||
params["temp"] = int(color_temp)
|
params["temp"] = int(
|
||||||
|
min(
|
||||||
|
self.max_color_temp_kelvin,
|
||||||
|
max(self.min_color_temp_kelvin, color_temp),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if ATTR_RGB_COLOR in kwargs and ColorMode.RGB in supported_color_modes:
|
if ATTR_RGB_COLOR in kwargs and ColorMode.RGB in supported_color_modes:
|
||||||
# Color channels change - used only in color mode, switch device mode to color
|
# Color channels change - used only in color mode, switch device mode to color
|
||||||
|
@ -328,7 +318,7 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity):
|
||||||
ATTR_RGBW_COLOR
|
ATTR_RGBW_COLOR
|
||||||
]
|
]
|
||||||
|
|
||||||
if ATTR_EFFECT in kwargs and ATTR_COLOR_TEMP not in kwargs:
|
if ATTR_EFFECT in kwargs and ATTR_COLOR_TEMP_KELVIN not in kwargs:
|
||||||
# Color effect change - used only in color mode, switch device mode to color
|
# Color effect change - used only in color mode, switch device mode to color
|
||||||
set_mode = "color"
|
set_mode = "color"
|
||||||
if self.coordinator.model == "SHBLB-1":
|
if self.coordinator.model == "SHBLB-1":
|
||||||
|
|
|
@ -25,6 +25,36 @@ MOCK_SETTINGS = {
|
||||||
"rollers": [{"positioning": True}],
|
"rollers": [{"positioning": True}],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def mock_light_set_state(
|
||||||
|
turn="on",
|
||||||
|
mode="color",
|
||||||
|
red=45,
|
||||||
|
green=55,
|
||||||
|
blue=65,
|
||||||
|
white=70,
|
||||||
|
gain=19,
|
||||||
|
temp=4050,
|
||||||
|
brightness=50,
|
||||||
|
effect=0,
|
||||||
|
transition=0,
|
||||||
|
):
|
||||||
|
"""Mock light block set_state."""
|
||||||
|
return {
|
||||||
|
"ison": turn == "on",
|
||||||
|
"mode": mode,
|
||||||
|
"red": red,
|
||||||
|
"green": green,
|
||||||
|
"blue": blue,
|
||||||
|
"white": white,
|
||||||
|
"gain": gain,
|
||||||
|
"temp": temp,
|
||||||
|
"brightness": brightness,
|
||||||
|
"effect": effect,
|
||||||
|
"transition": transition,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MOCK_BLOCKS = [
|
MOCK_BLOCKS = [
|
||||||
Mock(
|
Mock(
|
||||||
sensor_ids={"inputEvent": "S", "inputEventCnt": 2},
|
sensor_ids={"inputEvent": "S", "inputEventCnt": 2},
|
||||||
|
@ -43,6 +73,15 @@ MOCK_BLOCKS = [
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Mock(
|
||||||
|
sensor_ids={},
|
||||||
|
channel="0",
|
||||||
|
output=mock_light_set_state()["ison"],
|
||||||
|
colorTemp=mock_light_set_state()["temp"],
|
||||||
|
**mock_light_set_state(),
|
||||||
|
type="light",
|
||||||
|
set_state=AsyncMock(side_effect=mock_light_set_state),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
MOCK_CONFIG = {
|
MOCK_CONFIG = {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
"""The scene tests for the myq platform."""
|
"""Tests for Shelly cover platform."""
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
ATTR_CURRENT_POSITION,
|
ATTR_CURRENT_POSITION,
|
||||||
ATTR_POSITION,
|
ATTR_POSITION,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
"""The scene tests for the myq platform."""
|
"""Tests for Shelly diagnostics platform."""
|
||||||
from aiohttp import ClientSession
|
from aiohttp import ClientSession
|
||||||
|
|
||||||
from homeassistant.components.diagnostics import REDACTED
|
from homeassistant.components.diagnostics import REDACTED
|
||||||
|
|
385
tests/components/shelly/test_light.py
Normal file
385
tests/components/shelly/test_light.py
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
"""Tests for Shelly light platform."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.light import (
|
||||||
|
ATTR_BRIGHTNESS,
|
||||||
|
ATTR_COLOR_MODE,
|
||||||
|
ATTR_COLOR_TEMP_KELVIN,
|
||||||
|
ATTR_EFFECT,
|
||||||
|
ATTR_EFFECT_LIST,
|
||||||
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_RGBW_COLOR,
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
|
ATTR_TRANSITION,
|
||||||
|
DOMAIN as LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
ColorMode,
|
||||||
|
LightEntityFeature,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
ATTR_SUPPORTED_FEATURES,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
)
|
||||||
|
|
||||||
|
from . import init_integration
|
||||||
|
|
||||||
|
RELAY_BLOCK_ID = 0
|
||||||
|
LIGHT_BLOCK_ID = 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_block_device_rgbw_bulb(hass, mock_block_device):
|
||||||
|
"""Test block device RGBW bulb."""
|
||||||
|
await init_integration(hass, 1, model="SHBLB-1")
|
||||||
|
|
||||||
|
# Test initial
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_RGBW_COLOR] == (45, 55, 65, 70)
|
||||||
|
assert attributes[ATTR_BRIGHTNESS] == 48
|
||||||
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [
|
||||||
|
ColorMode.COLOR_TEMP,
|
||||||
|
ColorMode.RGBW,
|
||||||
|
]
|
||||||
|
assert attributes[ATTR_SUPPORTED_FEATURES] == LightEntityFeature.EFFECT
|
||||||
|
assert len(attributes[ATTR_EFFECT_LIST]) == 7
|
||||||
|
assert attributes[ATTR_EFFECT] == "Off"
|
||||||
|
|
||||||
|
# Turn off
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="off"
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
# Turn on, RGBW = [70, 80, 90, 20], brightness = 33, effect = Flash
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "light.test_name_channel_1",
|
||||||
|
ATTR_RGBW_COLOR: [70, 80, 90, 30],
|
||||||
|
ATTR_BRIGHTNESS: 33,
|
||||||
|
ATTR_EFFECT: "Flash",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="on", gain=13, brightness=13, red=70, green=80, blue=90, white=30, effect=3
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_COLOR_MODE] == ColorMode.RGBW
|
||||||
|
assert attributes[ATTR_RGBW_COLOR] == (70, 80, 90, 30)
|
||||||
|
assert attributes[ATTR_BRIGHTNESS] == 33
|
||||||
|
assert attributes[ATTR_EFFECT] == "Flash"
|
||||||
|
|
||||||
|
# Turn on, COLOR_TEMP_KELVIN = 3500
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_COLOR_TEMP_KELVIN: 3500},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="on", temp=3500, mode="white"
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_COLOR_MODE] == ColorMode.COLOR_TEMP
|
||||||
|
assert attributes[ATTR_COLOR_TEMP_KELVIN] == 3500
|
||||||
|
|
||||||
|
|
||||||
|
async def test_block_device_rgb_bulb(hass, mock_block_device, monkeypatch, caplog):
|
||||||
|
"""Test block device RGB bulb."""
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "mode")
|
||||||
|
await init_integration(hass, 1, model="SHCB-1")
|
||||||
|
|
||||||
|
# Test initial
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_RGB_COLOR] == (45, 55, 65)
|
||||||
|
assert attributes[ATTR_BRIGHTNESS] == 48
|
||||||
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [
|
||||||
|
ColorMode.COLOR_TEMP,
|
||||||
|
ColorMode.RGB,
|
||||||
|
]
|
||||||
|
assert attributes[ATTR_SUPPORTED_FEATURES] == LightEntityFeature.EFFECT
|
||||||
|
assert len(attributes[ATTR_EFFECT_LIST]) == 4
|
||||||
|
assert attributes[ATTR_EFFECT] == "Off"
|
||||||
|
|
||||||
|
# Turn off
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="off"
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
# Turn on, RGB = [70, 80, 90], brightness = 33, effect = Flash
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "light.test_name_channel_1",
|
||||||
|
ATTR_RGB_COLOR: [70, 80, 90],
|
||||||
|
ATTR_BRIGHTNESS: 33,
|
||||||
|
ATTR_EFFECT: "Flash",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="on", gain=13, brightness=13, red=70, green=80, blue=90, effect=3
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_COLOR_MODE] == ColorMode.RGB
|
||||||
|
assert attributes[ATTR_RGB_COLOR] == (70, 80, 90)
|
||||||
|
assert attributes[ATTR_BRIGHTNESS] == 33
|
||||||
|
assert attributes[ATTR_EFFECT] == "Flash"
|
||||||
|
|
||||||
|
# Turn on, COLOR_TEMP_KELVIN = 3500
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_COLOR_TEMP_KELVIN: 3500},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="on", temp=3500, mode="white"
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_COLOR_MODE] == ColorMode.COLOR_TEMP
|
||||||
|
assert attributes[ATTR_COLOR_TEMP_KELVIN] == 3500
|
||||||
|
|
||||||
|
# Turn on with unsupported effect
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_EFFECT: "Breath"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="on", mode="color"
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_EFFECT] == "Off"
|
||||||
|
assert "Effect 'Breath' not supported" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_block_device_white_bulb(hass, mock_block_device, monkeypatch, caplog):
|
||||||
|
"""Test block device white bulb."""
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "red")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "green")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "blue")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "mode")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "colorTemp")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "effect")
|
||||||
|
await init_integration(hass, 1, model="SHVIN-1")
|
||||||
|
|
||||||
|
# Test initial
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS]
|
||||||
|
assert attributes[ATTR_SUPPORTED_FEATURES] == 0
|
||||||
|
|
||||||
|
# Turn off
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="off"
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
# Turn on, brightness = 33
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_BRIGHTNESS: 33},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="on", gain=13, brightness=13
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_BRIGHTNESS] == 33
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"model",
|
||||||
|
[
|
||||||
|
"SHBDUO-1",
|
||||||
|
"SHCB-1",
|
||||||
|
"SHDM-1",
|
||||||
|
"SHDM-2",
|
||||||
|
"SHRGBW2",
|
||||||
|
"SHVIN-1",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_block_device_support_transition(
|
||||||
|
hass, mock_block_device, model, monkeypatch
|
||||||
|
):
|
||||||
|
"""Test block device supports transition."""
|
||||||
|
monkeypatch.setitem(
|
||||||
|
mock_block_device.settings, "fw", "20220809-122808/v1.12-g99f7e0b"
|
||||||
|
)
|
||||||
|
await init_integration(hass, 1, model=model)
|
||||||
|
|
||||||
|
# Test initial
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert attributes[ATTR_SUPPORTED_FEATURES] & LightEntityFeature.TRANSITION
|
||||||
|
|
||||||
|
# Turn on, TRANSITION = 4
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_TRANSITION: 4},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="on", transition=4000
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
# Turn off, TRANSITION = 6, limit to 5000ms
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_TRANSITION: 6},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="off", transition=5000
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
|
async def test_block_device_relay_app_type_light(hass, mock_block_device, monkeypatch):
|
||||||
|
"""Test block device relay in app type set to light mode."""
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "red")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "green")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "blue")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "mode")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "gain")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "brightness")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "effect")
|
||||||
|
monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "colorTemp")
|
||||||
|
monkeypatch.setitem(
|
||||||
|
mock_block_device.settings["relays"][RELAY_BLOCK_ID], "appliance_type", "light"
|
||||||
|
)
|
||||||
|
await init_integration(hass, 1)
|
||||||
|
assert hass.states.get("switch.test_name_channel_1") is None
|
||||||
|
|
||||||
|
# Test initial
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
attributes = state.attributes
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.ONOFF]
|
||||||
|
assert attributes[ATTR_SUPPORTED_FEATURES] == 0
|
||||||
|
|
||||||
|
# Turn off
|
||||||
|
mock_block_device.blocks[RELAY_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[RELAY_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="off"
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
# Turn on
|
||||||
|
mock_block_device.blocks[RELAY_BLOCK_ID].set_state.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_name_channel_1"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_block_device.blocks[RELAY_BLOCK_ID].set_state.assert_called_once_with(
|
||||||
|
turn="on"
|
||||||
|
)
|
||||||
|
state = hass.states.get("light.test_name_channel_1")
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
|
||||||
|
async def test_block_device_no_light_blocks(hass, mock_block_device, monkeypatch):
|
||||||
|
"""Test block device without light blocks."""
|
||||||
|
monkeypatch.setattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "type", "roller")
|
||||||
|
await init_integration(hass, 1)
|
||||||
|
assert hass.states.get("light.test_name_channel_1") is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_rpc_device_switch_type_lights_mode(hass, mock_rpc_device, monkeypatch):
|
||||||
|
"""Test RPC device with switch in consumption type lights mode."""
|
||||||
|
monkeypatch.setitem(
|
||||||
|
mock_rpc_device.config["sys"]["ui_data"], "consumption_types", ["lights"]
|
||||||
|
)
|
||||||
|
await init_integration(hass, 2)
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_switch_0"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert hass.states.get("light.test_switch_0").state == STATE_ON
|
||||||
|
|
||||||
|
monkeypatch.setitem(mock_rpc_device.status["switch:0"], "output", False)
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "light.test_switch_0"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_rpc_device.mock_update()
|
||||||
|
assert hass.states.get("light.test_switch_0").state == STATE_OFF
|
|
@ -1,4 +1,4 @@
|
||||||
"""The scene tests for the myq platform."""
|
"""Tests for Shelly switch platform."""
|
||||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue