Die neun Effekte der Firmware waren in Home Assistant nicht erreichbar -
das Auswahlfeld, das es schon gab, sind die Szenen. Jetzt bietet die
Lichtentitaet sie an:
* Gewaehlt wird ueber den NAMEN aus der Firmware ("Regenbogen"),
geschickt wird die Kennung ("rainbow"). Eine Liste englischer
Kennungen waere in einer deutschen Oberflaeche unnoetig sperrig.
* Ein Effekt laeuft wie eine Farbe in ALLEN Segmenten; angezeigt wird er
nur, wenn alle denselben fahren.
* Eine Kennung, die nicht in der Liste steht (neuere Firmware, aeltere
Liste), wird roh angezeigt statt verschluckt.
* Ein unbekannter Name wird abgelehnt, statt stillschweigend nichts zu
tun.
Die Liste steht fest in der Firmware und wird deshalb einmal geholt,
nicht je Abfragezyklus - erneut erst, wenn sich die Version aendert. Eine
sechste Anfrage je Zyklus fuer etwas, das sich nie aendert, waere
Verschwendung.
Effekt-Parameter (Geschwindigkeit, Intensitaet, Palette) fehlen noch;
dafuer braucht es Entitaeten je Segment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
137 lines
4.7 KiB
Python
137 lines
4.7 KiB
Python
"""Effekte: Liste, Anzeige, Setzen - und was passiert, wenn sie fehlen."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import MagicMock
|
|
|
|
from freezegun.api import FrozenDateTimeFactory
|
|
from homeassistant.const import ATTR_ENTITY_ID
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import ServiceValidationError
|
|
import pytest
|
|
from pytest_homeassistant_custom_component.common import MockConfigEntry
|
|
|
|
from tests.helpers import abfragen, entity_id_for
|
|
|
|
|
|
def light_id(hass: HomeAssistant) -> str:
|
|
return entity_id_for(hass, "light", "light")
|
|
|
|
|
|
async def test_die_liste_zeigt_namen_nicht_kennungen(
|
|
hass: HomeAssistant, init_integration: MockConfigEntry
|
|
) -> None:
|
|
state = hass.states.get(light_id(hass))
|
|
assert state.attributes["effect_list"] == ["Regenbogen", "Feuer", "Lauflicht"]
|
|
|
|
|
|
async def test_die_liste_wird_nur_einmal_geholt(
|
|
hass: HomeAssistant,
|
|
init_integration: MockConfigEntry,
|
|
mock_client: MagicMock,
|
|
freezer: FrozenDateTimeFactory,
|
|
) -> None:
|
|
"""Sie steht fest in der Firmware - eine sechste Anfrage je Zyklus waere
|
|
Verschwendung."""
|
|
vorher = mock_client.get_effects.await_count
|
|
await abfragen(hass, freezer)
|
|
await abfragen(hass, freezer)
|
|
assert mock_client.get_effects.await_count == vorher
|
|
|
|
|
|
async def test_nach_einem_firmware_update_wird_sie_neu_geholt(
|
|
hass: HomeAssistant,
|
|
init_integration: MockConfigEntry,
|
|
mock_client: MagicMock,
|
|
strip_info: dict[str, Any],
|
|
freezer: FrozenDateTimeFactory,
|
|
) -> None:
|
|
"""Eine neue Firmware kann neue Effekte mitbringen."""
|
|
vorher = mock_client.get_effects.await_count
|
|
strip_info["fw_version"] = "0.2.0"
|
|
await abfragen(hass, freezer)
|
|
assert mock_client.get_effects.await_count == vorher + 1
|
|
|
|
|
|
async def test_laufender_effekt_wird_mit_namen_angezeigt(
|
|
hass: HomeAssistant,
|
|
init_integration: MockConfigEntry,
|
|
strip_segments: dict[str, Any],
|
|
freezer: FrozenDateTimeFactory,
|
|
) -> None:
|
|
strip_segments["segments"][0].update({"mode": "effect", "effect": "fire"})
|
|
await abfragen(hass, freezer)
|
|
assert hass.states.get(light_id(hass)).attributes["effect"] == "Feuer"
|
|
|
|
|
|
async def test_unbekannte_kennung_wird_roh_gezeigt(
|
|
hass: HomeAssistant,
|
|
init_integration: MockConfigEntry,
|
|
strip_segments: dict[str, Any],
|
|
freezer: FrozenDateTimeFactory,
|
|
) -> None:
|
|
"""Neuere Firmware, aeltere Liste - dann lieber die Kennung als nichts."""
|
|
strip_segments["segments"][0].update({"mode": "effect", "effect": "meteor"})
|
|
await abfragen(hass, freezer)
|
|
assert hass.states.get(light_id(hass)).attributes["effect"] == "meteor"
|
|
|
|
|
|
async def test_ohne_effektmodus_kein_effekt(
|
|
hass: HomeAssistant, init_integration: MockConfigEntry
|
|
) -> None:
|
|
assert hass.states.get(light_id(hass)).attributes["effect"] is None
|
|
|
|
|
|
async def test_uneinige_segmente_zeigen_keinen_effekt(
|
|
hass: HomeAssistant,
|
|
init_integration: MockConfigEntry,
|
|
strip_segments: dict[str, Any],
|
|
freezer: FrozenDateTimeFactory,
|
|
) -> None:
|
|
strip_segments["segments"] = [
|
|
{"id": 0, "start": 0, "end": 29, "mode": "effect", "effect": "fire", "brightness": 80},
|
|
{"id": 1, "start": 30, "end": 59, "mode": "effect", "effect": "chase", "brightness": 80},
|
|
]
|
|
await abfragen(hass, freezer)
|
|
assert hass.states.get(light_id(hass)).attributes["effect"] is None
|
|
|
|
|
|
async def test_effekt_setzen_schreibt_die_kennung(
|
|
hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock
|
|
) -> None:
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
{ATTR_ENTITY_ID: light_id(hass), "effect": "Lauflicht"},
|
|
blocking=True,
|
|
)
|
|
(geschrieben,) = mock_client.put_segments.await_args.args
|
|
assert [(s["mode"], s["effect"]) for s in geschrieben] == [("effect", "chase")]
|
|
assert hass.states.get(light_id(hass)).attributes["effect"] == "Lauflicht"
|
|
|
|
|
|
async def test_unbekannter_effekt_wird_abgelehnt(
|
|
hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock
|
|
) -> None:
|
|
with pytest.raises(ServiceValidationError):
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
{ATTR_ENTITY_ID: light_id(hass), "effect": "Diskokugel"},
|
|
blocking=True,
|
|
)
|
|
mock_client.put_segments.assert_not_awaited()
|
|
|
|
|
|
async def test_firmware_ohne_effektliste_bietet_keine_an(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_client: MagicMock,
|
|
) -> None:
|
|
mock_client.get_effects.side_effect = lambda: {"palettes": []}
|
|
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()
|
|
assert hass.states.get(light_id(hass)).attributes["effect_list"] == []
|