core/tests/components/kulersky/test_light.py

290 lines
9.2 KiB
Python

"""Test the Kuler Sky lights."""
from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch
from bleak.backends.device import BLEDevice
import pykulersky
import pytest
from homeassistant.components.kulersky.const import DOMAIN
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_HS_COLOR,
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_XY_COLOR,
SCAN_INTERVAL,
ColorMode,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME,
ATTR_SUPPORTED_FEATURES,
CONF_ADDRESS,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_component import async_update_entity
from homeassistant.util import dt as dt_util
from tests.common import MockConfigEntry, async_fire_time_changed
@pytest.fixture
def mock_ble_device() -> Generator[MagicMock]:
"""Mock BLEDevice."""
with patch(
"homeassistant.components.kulersky.async_ble_device_from_address",
return_value=BLEDevice(address="AA:BB:CC:11:22:33", name="Bedroom", details={}),
) as ble_device:
yield ble_device
@pytest.fixture
async def mock_entry() -> MockConfigEntry:
"""Create a mock light entity."""
return MockConfigEntry(
domain=DOMAIN,
data={CONF_ADDRESS: "AA:BB:CC:11:22:33"},
title="Bedroom",
version=2,
)
@pytest.fixture
async def mock_light(
hass: HomeAssistant, mock_entry: MockConfigEntry, mock_ble_device: MagicMock
) -> Generator[AsyncMock]:
"""Mock pykulersky light."""
light = AsyncMock()
light.address = "AA:BB:CC:11:22:33"
light.name = "Bedroom"
light.connect.return_value = True
light.get_color.return_value = (0, 0, 0, 0)
with patch(
"pykulersky.Light",
return_value=light,
):
mock_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
assert light.connect.called
yield light
async def test_init(hass: HomeAssistant, mock_light: AsyncMock) -> None:
"""Test platform setup."""
state = hass.states.get("light.bedroom")
assert state.state == STATE_OFF
assert dict(state.attributes) == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.RGBW],
ATTR_SUPPORTED_FEATURES: 0,
ATTR_COLOR_MODE: None,
ATTR_BRIGHTNESS: None,
ATTR_HS_COLOR: None,
ATTR_RGB_COLOR: None,
ATTR_XY_COLOR: None,
ATTR_RGBW_COLOR: None,
}
async def test_remove_entry(
hass: HomeAssistant, mock_light: MagicMock, mock_entry: MockConfigEntry
) -> None:
"""Test platform setup."""
await hass.config_entries.async_remove(mock_entry.entry_id)
assert mock_light.disconnect.called
async def test_remove_entry_exceptions_caught(
hass: HomeAssistant, mock_light: MagicMock, mock_entry: MockConfigEntry
) -> None:
"""Assert that disconnect exceptions are caught."""
mock_light.disconnect.side_effect = pykulersky.PykulerskyException("Mock error")
await hass.config_entries.async_remove(mock_entry.entry_id)
assert mock_light.disconnect.called
async def test_update_exception(hass: HomeAssistant, mock_light: MagicMock) -> None:
"""Test platform setup."""
mock_light.get_color.side_effect = pykulersky.PykulerskyException
await async_update_entity(hass, "light.bedroom")
state = hass.states.get("light.bedroom")
assert state is not None
assert state.state == STATE_UNAVAILABLE
async def test_light_turn_on(hass: HomeAssistant, mock_light: MagicMock) -> None:
"""Test KulerSkyLight turn_on."""
mock_light.get_color.return_value = (255, 255, 255, 255)
await hass.services.async_call(
"light",
"turn_on",
{ATTR_ENTITY_ID: "light.bedroom"},
blocking=True,
)
await hass.async_block_till_done()
mock_light.set_color.assert_called_with(255, 255, 255, 255)
mock_light.get_color.return_value = (50, 50, 50, 50)
await hass.services.async_call(
"light",
"turn_on",
{ATTR_ENTITY_ID: "light.bedroom", ATTR_BRIGHTNESS: 50},
blocking=True,
)
await hass.async_block_till_done()
mock_light.set_color.assert_called_with(50, 50, 50, 50)
mock_light.get_color.return_value = (50, 25, 13, 6)
await hass.services.async_call(
"light",
"turn_on",
{ATTR_ENTITY_ID: "light.bedroom", ATTR_RGBW_COLOR: (255, 128, 64, 32)},
blocking=True,
)
await hass.async_block_till_done()
mock_light.set_color.assert_called_with(50, 25, 13, 6)
# RGB color is converted to RGBW by assigning the white component to the white
# channel, see color_rgb_to_rgbw
mock_light.get_color.return_value = (0, 17, 50, 17)
await hass.services.async_call(
"light",
"turn_on",
{ATTR_ENTITY_ID: "light.bedroom", ATTR_RGB_COLOR: (64, 128, 255)},
blocking=True,
)
await hass.async_block_till_done()
mock_light.set_color.assert_called_with(0, 17, 50, 17)
# HS color is converted to RGBW by assigning the white component to the white
# channel, see color_rgb_to_rgbw
mock_light.get_color.return_value = (50, 41, 0, 50)
await hass.services.async_call(
"light",
"turn_on",
{ATTR_ENTITY_ID: "light.bedroom", ATTR_HS_COLOR: (50, 50)},
blocking=True,
)
await hass.async_block_till_done()
mock_light.set_color.assert_called_with(50, 41, 0, 50)
async def test_light_turn_off(hass: HomeAssistant, mock_light: MagicMock) -> None:
"""Test KulerSkyLight turn_on."""
mock_light.get_color.return_value = (0, 0, 0, 0)
await hass.services.async_call(
"light",
"turn_off",
{ATTR_ENTITY_ID: "light.bedroom"},
blocking=True,
)
await hass.async_block_till_done()
mock_light.set_color.assert_called_with(0, 0, 0, 0)
async def test_light_update(hass: HomeAssistant, mock_light: MagicMock) -> None:
"""Test KulerSkyLight update."""
utcnow = dt_util.utcnow()
state = hass.states.get("light.bedroom")
assert state.state == STATE_OFF
assert dict(state.attributes) == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.RGBW],
ATTR_SUPPORTED_FEATURES: 0,
ATTR_COLOR_MODE: None,
ATTR_BRIGHTNESS: None,
ATTR_HS_COLOR: None,
ATTR_RGB_COLOR: None,
ATTR_RGBW_COLOR: None,
ATTR_XY_COLOR: None,
}
# Test an exception during discovery
mock_light.get_color.side_effect = pykulersky.PykulerskyException("TEST")
utcnow = utcnow + SCAN_INTERVAL
async_fire_time_changed(hass, utcnow)
await hass.async_block_till_done()
state = hass.states.get("light.bedroom")
assert state.state == STATE_UNAVAILABLE
assert dict(state.attributes) == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.RGBW],
ATTR_SUPPORTED_FEATURES: 0,
}
mock_light.get_color.side_effect = None
mock_light.get_color.return_value = (80, 160, 255, 0)
utcnow = utcnow + SCAN_INTERVAL
async_fire_time_changed(hass, utcnow)
await hass.async_block_till_done()
state = hass.states.get("light.bedroom")
assert state.state == STATE_ON
assert dict(state.attributes) == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.RGBW],
ATTR_SUPPORTED_FEATURES: 0,
ATTR_COLOR_MODE: ColorMode.RGBW,
ATTR_BRIGHTNESS: 255,
ATTR_HS_COLOR: (pytest.approx(212.571), pytest.approx(68.627)),
ATTR_RGB_COLOR: (80, 160, 255),
ATTR_RGBW_COLOR: (80, 160, 255, 0),
ATTR_XY_COLOR: (pytest.approx(0.17), pytest.approx(0.193)),
}
mock_light.get_color.side_effect = None
mock_light.get_color.return_value = (80, 160, 200, 255)
utcnow = utcnow + SCAN_INTERVAL
async_fire_time_changed(hass, utcnow)
await hass.async_block_till_done()
state = hass.states.get("light.bedroom")
assert state.state == STATE_ON
assert dict(state.attributes) == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.RGBW],
ATTR_SUPPORTED_FEATURES: 0,
ATTR_COLOR_MODE: ColorMode.RGBW,
ATTR_BRIGHTNESS: 255,
ATTR_HS_COLOR: (pytest.approx(199.701), pytest.approx(26.275)),
ATTR_RGB_COLOR: (188, 233, 255),
ATTR_RGBW_COLOR: (80, 160, 200, 255),
ATTR_XY_COLOR: (pytest.approx(0.259), pytest.approx(0.306)),
}
mock_light.get_color.side_effect = None
mock_light.get_color.return_value = (80, 160, 200, 240)
utcnow = utcnow + SCAN_INTERVAL
async_fire_time_changed(hass, utcnow)
await hass.async_block_till_done()
state = hass.states.get("light.bedroom")
assert state.state == STATE_ON
assert dict(state.attributes) == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.RGBW],
ATTR_SUPPORTED_FEATURES: 0,
ATTR_COLOR_MODE: ColorMode.RGBW,
ATTR_BRIGHTNESS: 240,
ATTR_HS_COLOR: (pytest.approx(200.0), pytest.approx(27.059)),
ATTR_RGB_COLOR: (186, 232, 255),
ATTR_RGBW_COLOR: (85, 170, 212, 255),
ATTR_XY_COLOR: (pytest.approx(0.257), pytest.approx(0.305)),
}