In Home Assistant liessen sich nur Szenen und Helligkeit stellen, keine Farben - device/state traegt naemlich keine. Die steht je Segment, und die Segmente hat der Koordinator bisher gar nicht gelesen. Jetzt holt er device/segments mit (fuenfte Anfrage je Zyklus), und die Lichtentitaet bietet rgb_color und color_temp_kelvin an: * Setzen schreibt in ALLE Segmente, aber nur die Farbfelder - Grenzen, Namen und Helligkeit bleiben stehen. Die Einteilung eines Strips ist von Hand gemacht, die reisst eine Farbwahl nicht ein. Geschrieben wird die ganze Liste auf einmal, danach zurueckgelesen statt angenommen. * Angezeigt wird nur, worauf sich alle Segmente einigen. Zwei Farben und eine Entitaet - da waere jede Angabe gelogen, also gibt es keine. * Farbtemperatur nur, wenn alle auf "white" stehen; sonst RGB, sonst blendete HA den Farbkreis aus, sobald ein Segment einen Effekt faehrt. * Die Farbe geht VOR dem Einschalten raus, sonst leuchtet der Strip kurz in der alten auf. Die Spanne 2700-6500 K ist die, die die Firmware auf die Weisskanaele umrechnet (nl_ct.h); ohne Weisskanaele naehert sie sie in RGB an. Dazu: das Markenbild ist jetzt dasselbe wie bei ninlight - Michaels Wunsch, die beiden Integrationen gehoeren zusammen. Das Skript fuer ein eigenes Motiv bleibt als Ausgangspunkt liegen.
147 lines
4.5 KiB
Python
147 lines
4.5 KiB
Python
"""Fixtures fuer die Tests, die gegen eine Home-Assistant-Instanz laufen."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Generator
|
|
from copy import deepcopy
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
from homeassistant.const import CONF_HOST, CONF_TOKEN
|
|
from homeassistant.core import HomeAssistant
|
|
import pytest
|
|
from pytest_homeassistant_custom_component.common import MockConfigEntry
|
|
|
|
from custom_components.ninstrip.api import NinstripClient
|
|
from custom_components.ninstrip.const import DOMAIN
|
|
from tests_unit.samples import (
|
|
HOST_K1Y,
|
|
INFO_K1Y,
|
|
MAC_K1Y,
|
|
PRESETS_K1Y,
|
|
SEGMENTS_K1Y,
|
|
SETTINGS_COMPILED,
|
|
STATE_K1Y,
|
|
TOKEN,
|
|
)
|
|
|
|
CLIENT_PATCH_TARGETS = [
|
|
"custom_components.ninstrip.NinstripClient",
|
|
"custom_components.ninstrip.config_flow.NinstripClient",
|
|
]
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def auto_enable_custom_integrations(enable_custom_integrations: None) -> Generator[None]:
|
|
"""custom_components/ in jedem Home-Assistant-Test laden."""
|
|
yield
|
|
|
|
|
|
@pytest.fixture
|
|
def strip_state() -> dict[str, Any]:
|
|
return deepcopy(STATE_K1Y)
|
|
|
|
|
|
@pytest.fixture
|
|
def strip_info() -> dict[str, Any]:
|
|
return deepcopy(INFO_K1Y)
|
|
|
|
|
|
@pytest.fixture
|
|
def strip_settings() -> dict[str, Any]:
|
|
return deepcopy(SETTINGS_COMPILED)
|
|
|
|
|
|
@pytest.fixture
|
|
def strip_presets() -> dict[str, Any]:
|
|
return deepcopy(PRESETS_K1Y)
|
|
|
|
|
|
@pytest.fixture
|
|
def strip_segments() -> dict[str, Any]:
|
|
return deepcopy(SEGMENTS_K1Y)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client(
|
|
strip_state: dict[str, Any],
|
|
strip_info: dict[str, Any],
|
|
strip_settings: dict[str, Any],
|
|
strip_presets: dict[str, Any],
|
|
strip_segments: dict[str, Any],
|
|
) -> Generator[MagicMock]:
|
|
"""Ein Geraet, dessen Antworten den veraenderbaren strip_*-Fixtures folgen."""
|
|
client = MagicMock(spec=NinstripClient)
|
|
client.host = HOST_K1Y
|
|
client.get_state = AsyncMock(side_effect=lambda: deepcopy(strip_state))
|
|
client.get_info = AsyncMock(side_effect=lambda: deepcopy(strip_info))
|
|
client.get_settings = AsyncMock(side_effect=lambda: deepcopy(strip_settings))
|
|
client.get_presets = AsyncMock(side_effect=lambda: deepcopy(strip_presets))
|
|
client.get_segments = AsyncMock(side_effect=lambda: deepcopy(strip_segments))
|
|
|
|
async def put_segments(segments: list[dict[str, Any]]) -> None:
|
|
# Wie das Geraet: es nimmt die Liste als Ganzes an und antwortet nur
|
|
# mit "ok"; der Aufrufer liest danach zurueck.
|
|
strip_segments["segments"] = deepcopy(segments)
|
|
|
|
client.put_segments = AsyncMock(side_effect=put_segments)
|
|
|
|
async def set_state(**fields: Any) -> dict[str, Any]:
|
|
strip_state.update(fields)
|
|
return deepcopy(strip_state)
|
|
|
|
client.set_state = AsyncMock(side_effect=set_state)
|
|
|
|
async def set_sacn(**fields: Any) -> None:
|
|
# Wie das Geraet: es merkt sich, was es annimmt, und der Aufrufer
|
|
# liest den Block danach zurueck.
|
|
strip_settings.setdefault("sacn", {}).update(fields)
|
|
|
|
client.set_sacn = AsyncMock(side_effect=set_sacn)
|
|
client.apply_preset = AsyncMock(return_value=None)
|
|
client.save_preset = AsyncMock(return_value=None)
|
|
client.reboot = AsyncMock(return_value=None)
|
|
client.fw_update = AsyncMock(return_value=None)
|
|
patches = [patch(target, return_value=client) for target in CLIENT_PATCH_TARGETS]
|
|
for p in patches:
|
|
p.start()
|
|
yield client
|
|
for p in patches:
|
|
p.stop()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_config_entry() -> MockConfigEntry:
|
|
return MockConfigEntry(
|
|
domain=DOMAIN,
|
|
title="ninstrip ad4c",
|
|
unique_id=MAC_K1Y,
|
|
data={CONF_HOST: HOST_K1Y, CONF_TOKEN: TOKEN},
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
async def init_integration(
|
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_client: MagicMock
|
|
) -> MockConfigEntry:
|
|
mock_config_entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
return mock_config_entry
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_setup_entry() -> Generator[AsyncMock]:
|
|
"""Tests des Einrichtungsablaufs duerfen den Eintrag nicht wirklich aufsetzen."""
|
|
with patch("custom_components.ninstrip.async_setup_entry", return_value=True) as mock:
|
|
yield mock
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def firmware_dir(tmp_path: Path) -> Generator[Path]:
|
|
"""Jeder Test bekommt seinen eigenen, anfangs leeren Firmware-Ordner."""
|
|
verzeichnis = tmp_path / "ninstrip_firmware"
|
|
with patch("custom_components.ninstrip.shared.firmware_directory", return_value=verzeichnis):
|
|
yield verzeichnis
|