mirror of https://github.com/home-assistant/core
313 lines
8.6 KiB
Python
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()
|