core/tests/components/lifx/__init__.py

313 lines
8.6 KiB
Python

"""Tests for the lifx integration."""
from __future__ import annotations
import asyncio
from contextlib import contextmanager
from typing import Any
from unittest.mock import AsyncMock, MagicMock, Mock, patch
from aiolifx.aiolifx import Light
from homeassistant.components.lifx import discovery
from homeassistant.components.lifx.const import TARGET_ANY
MODULE = "homeassistant.components.lifx"
MODULE_CONFIG_FLOW = "homeassistant.components.lifx.config_flow"
IP_ADDRESS = "127.0.0.1"
LABEL = "My Bulb"
GROUP = "My Group"
SERIAL = "aa:bb:cc:dd:ee:cc"
MAC_ADDRESS = "aa:bb:cc:dd:ee:cd"
DHCP_FORMATTED_MAC = "aabbccddeecd"
DEFAULT_ENTRY_TITLE = LABEL
class MockMessage:
"""Mock a lifx message."""
def __init__(self, **kwargs: Any) -> None:
"""Init message."""
self.target_addr = SERIAL
self.count = 9
for k, v in kwargs.items():
if k != "callb":
setattr(self, k, v)
class MockFailingLifxCommand:
"""Mock a lifx command that fails."""
def __init__(self, bulb, **kwargs: Any) -> None:
"""Init command."""
self.bulb = bulb
self.calls = []
def __call__(self, *args, **kwargs):
"""Call command."""
if callb := kwargs.get("callb"):
callb(self.bulb, None)
self.calls.append([args, kwargs])
def reset_mock(self):
"""Reset mock."""
self.calls = []
class MockLifxCommand:
"""Mock a lifx command."""
def __name__(self):
"""Return name."""
return "mock_lifx_command"
def __init__(self, bulb, **kwargs: Any) -> None:
"""Init command."""
self.bulb = bulb
self.calls = []
self.msg_kwargs = {
k.removeprefix("msg_"): v for k, v in kwargs.items() if k.startswith("msg_")
}
for k, v in kwargs.items():
if k.startswith("msg_") or k == "callb":
continue
setattr(self.bulb, k, v)
def __call__(self, *args, **kwargs):
"""Call command."""
if callb := kwargs.get("callb"):
callb(self.bulb, MockMessage(**self.msg_kwargs))
self.calls.append([args, kwargs])
def reset_mock(self):
"""Reset mock."""
self.calls = []
def _mocked_bulb() -> Light:
bulb = Light(asyncio.get_running_loop(), SERIAL, IP_ADDRESS)
bulb.host_firmware_version = "3.00"
bulb.label = LABEL
bulb.group = GROUP
bulb.color = [1, 2, 3, 4]
bulb.power_level = 0
bulb.fire_and_forget = AsyncMock()
bulb.set_reboot = Mock()
bulb.try_sending = AsyncMock()
bulb.set_infrared = MockLifxCommand(bulb)
bulb.get_label = MockLifxCommand(bulb)
bulb.get_group = MockLifxCommand(bulb)
bulb.get_color = MockLifxCommand(bulb)
bulb.set_power = MockLifxCommand(bulb)
bulb.set_color = MockLifxCommand(bulb)
bulb.get_hostfirmware = MockLifxCommand(bulb)
bulb.get_wifiinfo = MockLifxCommand(bulb, signal=100)
bulb.get_version = MockLifxCommand(bulb)
bulb.set_waveform_optional = MockLifxCommand(bulb)
bulb.product = 1 # LIFX Original 1000
return bulb
def _mocked_failing_bulb() -> Light:
bulb = _mocked_bulb()
bulb.get_color = MockFailingLifxCommand(bulb)
bulb.set_power = MockFailingLifxCommand(bulb)
bulb.set_color = MockFailingLifxCommand(bulb)
bulb.get_hostfirmware = MockFailingLifxCommand(bulb)
bulb.get_version = MockFailingLifxCommand(bulb)
return bulb
def _mocked_white_bulb() -> Light:
bulb = _mocked_bulb()
bulb.product = 19 # LIFX White 900 BR30 (High Voltage)
return bulb
def _mocked_brightness_bulb() -> Light:
bulb = _mocked_bulb()
bulb.product = 51 # LIFX Mini White
return bulb
def _mocked_clean_bulb() -> Light:
bulb = _mocked_bulb()
bulb.get_hev_cycle = MockLifxCommand(bulb)
bulb.set_hev_cycle = MockLifxCommand(bulb)
bulb.get_hev_configuration = MockLifxCommand(bulb)
bulb.get_last_hev_cycle_result = MockLifxCommand(bulb)
bulb.hev_cycle_configuration = {"duration": 7200, "indication": False}
bulb.hev_cycle = {
"duration": 7200,
"remaining": 30,
"last_power": False,
}
bulb.last_hev_cycle_result = 0
bulb.product = 90
return bulb
def _mocked_infrared_bulb() -> Light:
bulb = _mocked_bulb()
bulb.product = 29 # LIFX A19 Night Vision
bulb.infrared_brightness = 65535
bulb.set_infrared = MockLifxCommand(bulb)
bulb.get_infrared = MockLifxCommand(bulb, infrared_brightness=65535)
return bulb
def _mocked_light_strip() -> Light:
bulb = _mocked_bulb()
bulb.product = 31 # LIFX Z
bulb.zones_count = 3
bulb.color_zones = [MagicMock()] * 3
bulb.effect = {"effect": "MOVE", "speed": 3, "duration": 0, "direction": "RIGHT"}
bulb.get_color_zones = MockLifxCommand(
bulb,
msg_seq_num=bulb.seq_next(),
msg_count=bulb.zones_count,
msg_index=0,
msg_color=bulb.color_zones,
)
bulb.set_color_zones = MockLifxCommand(bulb)
bulb.get_multizone_effect = MockLifxCommand(bulb)
bulb.set_multizone_effect = MockLifxCommand(bulb)
bulb.get_extended_color_zones = MockLifxCommand(bulb)
bulb.set_extended_color_zones = MockLifxCommand(bulb)
return bulb
def _mocked_tile() -> Light:
bulb = _mocked_bulb()
bulb.product = 55 # LIFX Tile
bulb.effect = {"effect": "OFF"}
bulb.get_tile_effect = MockLifxCommand(bulb)
bulb.set_tile_effect = MockLifxCommand(bulb)
bulb.get64 = MockLifxCommand(bulb)
bulb.get_device_chain = MockLifxCommand(bulb)
return bulb
def _mocked_ceiling() -> Light:
bulb = _mocked_bulb()
bulb.product = 176 # LIFX Ceiling
bulb.effect = {"effect": "OFF"}
bulb.get_tile_effect = MockLifxCommand(bulb)
bulb.set_tile_effect = MockLifxCommand(bulb)
bulb.get64 = MockLifxCommand(bulb)
bulb.get_device_chain = MockLifxCommand(bulb)
return bulb
def _mocked_bulb_old_firmware() -> Light:
bulb = _mocked_bulb()
bulb.host_firmware_version = "2.77"
return bulb
def _mocked_bulb_new_firmware() -> Light:
bulb = _mocked_bulb()
bulb.host_firmware_version = "3.90"
return bulb
def _mocked_relay() -> Light:
bulb = _mocked_bulb()
bulb.product = 70 # LIFX Switch
return bulb
def _patch_device(device: Light | None = None, no_device: bool = False):
"""Patch out discovery."""
class MockLifxConnecton:
"""Mock lifx discovery."""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Init connection."""
if no_device:
self.device = _mocked_failing_bulb()
else:
self.device = device or _mocked_bulb()
self.device.mac_addr = TARGET_ANY
async def async_setup(self):
"""Mock setup."""
def async_stop(self):
"""Mock teardown."""
@contextmanager
def _patcher():
with patch("homeassistant.components.lifx.LIFXConnection", MockLifxConnecton):
yield
return _patcher()
def _patch_discovery(device: Light | None = None, no_device: bool = False):
"""Patch out discovery."""
class MockLifxDiscovery:
"""Mock lifx discovery."""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Init discovery."""
if no_device:
self.lights = {}
return
discovered = device or _mocked_bulb()
self.lights = {discovered.mac_addr: discovered}
def start(self):
"""Mock start."""
def cleanup(self):
"""Mock cleanup."""
@contextmanager
def _patcher():
with (
patch.object(discovery, "DEFAULT_TIMEOUT", 0),
patch(
"homeassistant.components.lifx.discovery.LifxDiscovery",
MockLifxDiscovery,
),
):
yield
return _patcher()
def _patch_config_flow_try_connect(
device: Light | None = None, no_device: bool = False
):
"""Patch out discovery."""
class MockLifxConnection:
"""Mock lifx discovery."""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Init connection."""
if no_device:
self.device = _mocked_failing_bulb()
else:
self.device = device or _mocked_bulb()
self.device.mac_addr = TARGET_ANY
async def async_setup(self):
"""Mock setup."""
def async_stop(self):
"""Mock teardown."""
@contextmanager
def _patcher():
with patch(
"homeassistant.components.lifx.config_flow.LIFXConnection",
MockLifxConnection,
):
yield
return _patcher()