mirror of
https://github.com/home-assistant/core.git
synced 2026-08-28 18:24:50 -05:00
302 lines
9.8 KiB
Python
302 lines
9.8 KiB
Python
"""Tests for midea light.py."""
|
|
|
|
from collections.abc import Callable
|
|
from typing import Any
|
|
from unittest.mock import patch
|
|
|
|
from midealocal.const import DeviceType
|
|
from midealocal.devices.ac import DeviceAttributes as ACAttributes
|
|
from midealocal.devices.x13 import DeviceAttributes as X13Attributes, Midea13Device
|
|
from midealocal.exceptions import SocketException
|
|
import pytest
|
|
from syrupy.assertion import SnapshotAssertion
|
|
|
|
from homeassistant.components.light import (
|
|
ATTR_BRIGHTNESS,
|
|
ATTR_COLOR_MODE,
|
|
ATTR_COLOR_TEMP_KELVIN,
|
|
ATTR_EFFECT,
|
|
ATTR_EFFECT_LIST,
|
|
ATTR_MAX_COLOR_TEMP_KELVIN,
|
|
ATTR_MIN_COLOR_TEMP_KELVIN,
|
|
ATTR_SUPPORTED_COLOR_MODES,
|
|
DOMAIN as LIGHT_DOMAIN,
|
|
EFFECT_OFF,
|
|
ColorMode,
|
|
)
|
|
from homeassistant.const import (
|
|
ATTR_ENTITY_ID,
|
|
SERVICE_TURN_OFF,
|
|
SERVICE_TURN_ON,
|
|
Platform,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers import entity_registry as er
|
|
|
|
from . import setup_integration
|
|
from .conftest import DummyDevice, entity_entries
|
|
from .const import TEST_DEVICE_ID
|
|
|
|
from tests.common import MockConfigEntry, snapshot_platform
|
|
|
|
X13_EFFECTS = list(Midea13Device._effects)
|
|
|
|
|
|
def _x13_device() -> DummyDevice:
|
|
device = DummyDevice(
|
|
DeviceType.X13,
|
|
attributes={
|
|
X13Attributes.power: True,
|
|
X13Attributes.brightness: 128,
|
|
X13Attributes.color_temperature: 4000,
|
|
X13Attributes.effect: "none",
|
|
X13Attributes.rgb_color: None,
|
|
},
|
|
)
|
|
device.color_temp_range = [2700, 6500]
|
|
device.effects = X13_EFFECTS
|
|
return device
|
|
|
|
|
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
|
async def test_light_state_snapshot(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
|
|
snapshot: SnapshotAssertion,
|
|
entity_registry: er.EntityRegistry,
|
|
) -> None:
|
|
"""Test async_setup_entry creates the light entity for an X13 device."""
|
|
device = _x13_device()
|
|
config_entry = mock_config_entry(device)
|
|
with patch("homeassistant.components.midea._PLATFORMS", [Platform.LIGHT]):
|
|
await setup_integration(hass, config_entry, device)
|
|
|
|
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
|
|
|
|
|
|
async def test_light_state_and_services(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
|
|
) -> None:
|
|
"""Test light state attributes and service calls reach the device."""
|
|
device = _x13_device()
|
|
config_entry = mock_config_entry(device)
|
|
with patch("homeassistant.components.midea._PLATFORMS", [Platform.LIGHT]):
|
|
await setup_integration(hass, config_entry, device)
|
|
|
|
entity_entry = entity_entries(hass, config_entry)[f"{TEST_DEVICE_ID}_light"]
|
|
|
|
assert (state := hass.states.get(entity_entry.entity_id)) is not None
|
|
assert state.state == "on"
|
|
assert state.attributes[ATTR_BRIGHTNESS] == 128
|
|
assert state.attributes[ATTR_COLOR_TEMP_KELVIN] == 4000
|
|
assert state.attributes[ATTR_MIN_COLOR_TEMP_KELVIN] == 2700
|
|
assert state.attributes[ATTR_MAX_COLOR_TEMP_KELVIN] == 6500
|
|
assert state.attributes[ATTR_EFFECT] == EFFECT_OFF
|
|
assert state.attributes[ATTR_EFFECT_LIST] == [
|
|
EFFECT_OFF,
|
|
*(effect for effect in X13_EFFECTS if effect != "none"),
|
|
]
|
|
assert state.attributes[ATTR_COLOR_MODE] == ColorMode.COLOR_TEMP
|
|
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.COLOR_TEMP]
|
|
|
|
device.calls.clear()
|
|
await hass.services.async_call(
|
|
LIGHT_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{
|
|
ATTR_ENTITY_ID: entity_entry.entity_id,
|
|
ATTR_BRIGHTNESS: 200,
|
|
ATTR_COLOR_TEMP_KELVIN: 5000,
|
|
ATTR_EFFECT: "cinema",
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert device.calls == [
|
|
("set_attribute", "brightness", 200),
|
|
("set_attribute", "color_temperature", 5000),
|
|
("set_attribute", "effect", "cinema"),
|
|
]
|
|
|
|
device.calls.clear()
|
|
await hass.services.async_call(
|
|
LIGHT_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: entity_entry.entity_id, ATTR_EFFECT: EFFECT_OFF},
|
|
blocking=True,
|
|
)
|
|
assert device.calls == [("set_attribute", "effect", "none")]
|
|
|
|
device.calls.clear()
|
|
await hass.services.async_call(
|
|
LIGHT_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: entity_entry.entity_id},
|
|
blocking=True,
|
|
)
|
|
assert device.calls == [("set_attribute", "power", False)]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("initial_power", "service_data", "expected_calls"),
|
|
[
|
|
pytest.param(
|
|
True,
|
|
{ATTR_BRIGHTNESS: 50},
|
|
[("set_attribute", "brightness", 50)],
|
|
id="already_on_skips_repower",
|
|
),
|
|
pytest.param(
|
|
False,
|
|
{},
|
|
[("set_attribute", "power", True)],
|
|
id="off_powers_on",
|
|
),
|
|
],
|
|
)
|
|
async def test_light_turn_on_power_handling(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
|
|
initial_power: bool,
|
|
service_data: dict[str, Any],
|
|
expected_calls: list[tuple],
|
|
) -> None:
|
|
"""Test turn_on only resends power when the device is currently off."""
|
|
device = _x13_device()
|
|
device.attributes[X13Attributes.power] = initial_power
|
|
config_entry = mock_config_entry(device)
|
|
with patch("homeassistant.components.midea._PLATFORMS", [Platform.LIGHT]):
|
|
await setup_integration(hass, config_entry, device)
|
|
|
|
entity_entry = entity_entries(hass, config_entry)[f"{TEST_DEVICE_ID}_light"]
|
|
|
|
device.calls.clear()
|
|
await hass.services.async_call(
|
|
LIGHT_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: entity_entry.entity_id, **service_data},
|
|
blocking=True,
|
|
)
|
|
assert device.calls == expected_calls
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
(
|
|
"attributes",
|
|
"expected_state",
|
|
"expected_color_mode",
|
|
"expected_supported_color_modes",
|
|
"expected_has_effect",
|
|
),
|
|
[
|
|
pytest.param(
|
|
{},
|
|
"unknown",
|
|
# HA only reports a color_mode while is_on is True.
|
|
None,
|
|
[ColorMode.ONOFF],
|
|
False,
|
|
id="before_first_status",
|
|
),
|
|
pytest.param(
|
|
{
|
|
X13Attributes.power: True,
|
|
X13Attributes.brightness: None,
|
|
X13Attributes.color_temperature: None,
|
|
X13Attributes.effect: None,
|
|
X13Attributes.rgb_color: None,
|
|
},
|
|
"on",
|
|
ColorMode.ONOFF,
|
|
[ColorMode.ONOFF],
|
|
False,
|
|
id="onoff_only",
|
|
),
|
|
pytest.param(
|
|
{
|
|
X13Attributes.power: True,
|
|
X13Attributes.brightness: 50,
|
|
X13Attributes.color_temperature: None,
|
|
X13Attributes.effect: None,
|
|
X13Attributes.rgb_color: None,
|
|
},
|
|
"on",
|
|
ColorMode.BRIGHTNESS,
|
|
[ColorMode.BRIGHTNESS],
|
|
False,
|
|
id="brightness_only",
|
|
),
|
|
],
|
|
)
|
|
async def test_light_color_mode_fallbacks(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
|
|
attributes: dict[X13Attributes, Any],
|
|
expected_state: str,
|
|
expected_color_mode: ColorMode | None,
|
|
expected_supported_color_modes: list[ColorMode],
|
|
expected_has_effect: bool,
|
|
) -> None:
|
|
"""Test the light falls back to the correct color mode based on reported capability."""
|
|
device = DummyDevice(DeviceType.X13, attributes=attributes)
|
|
device.color_temp_range = [2700, 6500]
|
|
device.effects = X13_EFFECTS
|
|
config_entry = mock_config_entry(device)
|
|
with patch("homeassistant.components.midea._PLATFORMS", [Platform.LIGHT]):
|
|
await setup_integration(hass, config_entry, device)
|
|
|
|
entity_entry = entity_entries(hass, config_entry)[f"{TEST_DEVICE_ID}_light"]
|
|
assert (state := hass.states.get(entity_entry.entity_id)) is not None
|
|
assert state.state == expected_state
|
|
assert state.attributes[ATTR_COLOR_MODE] == expected_color_mode
|
|
assert (
|
|
state.attributes[ATTR_SUPPORTED_COLOR_MODES] == expected_supported_color_modes
|
|
)
|
|
assert (ATTR_EFFECT in state.attributes) == expected_has_effect
|
|
|
|
|
|
async def test_light_not_created_for_other_device_type(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
|
|
) -> None:
|
|
"""Test no light entity is created for a device type without one."""
|
|
device = DummyDevice(
|
|
DeviceType.AC,
|
|
attributes={
|
|
ACAttributes.power: True,
|
|
ACAttributes.mode: 1,
|
|
ACAttributes.target_temperature: 22.0,
|
|
ACAttributes.indoor_temperature: 21.0,
|
|
},
|
|
)
|
|
config_entry = mock_config_entry(device)
|
|
with patch("homeassistant.components.midea._PLATFORMS", [Platform.LIGHT]):
|
|
await setup_integration(hass, config_entry, device)
|
|
|
|
assert entity_entries(hass, config_entry) == {}
|
|
|
|
|
|
async def test_light_turn_on_raises_on_device_communication_error(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
|
|
) -> None:
|
|
"""Test a device communication failure surfaces as a HomeAssistantError."""
|
|
device = _x13_device()
|
|
config_entry = mock_config_entry(device)
|
|
with patch("homeassistant.components.midea._PLATFORMS", [Platform.LIGHT]):
|
|
await setup_integration(hass, config_entry, device)
|
|
|
|
entity_entry = entity_entries(hass, config_entry)[f"{TEST_DEVICE_ID}_light"]
|
|
|
|
with (
|
|
patch.object(device, "set_attribute", side_effect=SocketException("offline")),
|
|
pytest.raises(HomeAssistantError),
|
|
):
|
|
await hass.services.async_call(
|
|
LIGHT_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: entity_entry.entity_id},
|
|
blocking=True,
|
|
)
|