"""Hauptschalter und Helligkeit.""" from __future__ import annotations from datetime import timedelta from typing import Any from unittest.mock import MagicMock from freezegun.api import FrozenDateTimeFactory from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant import pytest from pytest_homeassistant_custom_component.common import MockConfigEntry, async_fire_time_changed from tests.helpers import entity_id_for def light_id(hass: HomeAssistant) -> str: return entity_id_for(hass, "light", "light") async def abfragen(hass: HomeAssistant, freezer: FrozenDateTimeFactory) -> None: """Einen Abfragezyklus vorspulen.""" freezer.tick(timedelta(seconds=31)) async_fire_time_changed(hass) await hass.async_block_till_done() async def test_zustand_und_helligkeit_aus_prozent( hass: HomeAssistant, init_integration: MockConfigEntry ) -> None: state = hass.states.get(light_id(hass)) assert state is not None assert state.state == STATE_ON # Das Geraet rechnet in Prozent: 100 % sind 255. assert state.attributes[ATTR_BRIGHTNESS] == 255 # Alle Segmente stehen auf weiss, also meldet die Entitaet Farbtemperatur. assert state.attributes["color_mode"] == ColorMode.COLOR_TEMP assert state.attributes["color_temp_kelvin"] == 2700 assert state.attributes["pixel_count"] == 60 assert state.attributes["segment_count"] == 1 assert state.attributes["preset"] == 2 @pytest.mark.parametrize( ("prozent", "erwartet"), [(0, 0), (1, 3), (37, 94), (50, 128), (99, 252), (100, 255)] ) async def test_prozent_werden_auf_0_bis_255_umgerechnet( hass: HomeAssistant, init_integration: MockConfigEntry, strip_state: dict[str, Any], freezer: FrozenDateTimeFactory, prozent: int, erwartet: int, ) -> None: strip_state["brightness"] = prozent await abfragen(hass, freezer) assert hass.states.get(light_id(hass)).attributes[ATTR_BRIGHTNESS] == erwartet @pytest.mark.parametrize("wert", [-5, 150]) async def test_unsinnige_helligkeit_wird_gekappt( hass: HomeAssistant, init_integration: MockConfigEntry, strip_state: dict[str, Any], freezer: FrozenDateTimeFactory, wert: int, ) -> None: """Ein Geraet, das ausserhalb von 0..100 antwortet, darf HA nicht sprengen.""" strip_state["brightness"] = wert await abfragen(hass, freezer) assert hass.states.get(light_id(hass)).attributes[ATTR_BRIGHTNESS] in (0, 255) async def test_einschalten_ohne_helligkeit_schickt_nur_on( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock ) -> None: await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: light_id(hass)}, blocking=True ) mock_client.set_state.assert_awaited_once_with(on=True) @pytest.mark.parametrize(("ha_wert", "prozent"), [(255, 100), (128, 50), (3, 1), (1, 0)]) async def test_einschalten_mit_helligkeit_rechnet_in_prozent( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock, ha_wert: int, prozent: int, ) -> None: await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: light_id(hass), ATTR_BRIGHTNESS: ha_wert}, blocking=True, ) mock_client.set_state.assert_awaited_once_with(on=True, brightness=prozent) async def test_ausschalten( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock ) -> None: await hass.services.async_call( "light", "turn_off", {ATTR_ENTITY_ID: light_id(hass)}, blocking=True ) mock_client.set_state.assert_awaited_once_with(on=False) assert hass.states.get(light_id(hass)).state == STATE_OFF async def test_die_antwort_des_geraets_zaehlt_nicht_die_anfrage( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock ) -> None: """Das Geraet darf die Helligkeit abweichend beantworten - dann gilt seine.""" async def antwortet_anders(**fields: Any) -> dict[str, Any]: return {**mock_client.get_state.side_effect(), **fields, "brightness": 20} mock_client.set_state.side_effect = antwortet_anders await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: light_id(hass), ATTR_BRIGHTNESS: 255}, blocking=True, ) assert hass.states.get(light_id(hass)).attributes[ATTR_BRIGHTNESS] == 51 # --- Farbe und Farbtemperatur ------------------------------------------ async def test_farbe_aus_den_segmenten( hass: HomeAssistant, init_integration: MockConfigEntry, strip_segments: dict[str, Any], freezer: FrozenDateTimeFactory, ) -> None: """Die Farbe steht nicht in device/state, sondern je Segment.""" strip_segments["segments"][0].update({"mode": "solid", "color": {"r": 12, "g": 200, "b": 90}}) await abfragen(hass, freezer) state = hass.states.get(light_id(hass)) assert state.attributes["color_mode"] == ColorMode.RGB assert state.attributes["rgb_color"] == (12, 200, 90) async def test_farbe_setzen_schreibt_in_alle_segmente( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock ) -> None: await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: light_id(hass), "rgb_color": (255, 40, 0)}, blocking=True, ) (geschrieben,) = mock_client.put_segments.await_args.args assert [(s["mode"], s["color"]) for s in geschrieben] == [ ("solid", {"r": 255, "g": 40, "b": 0}) ] assert hass.states.get(light_id(hass)).attributes["rgb_color"] == (255, 40, 0) async def test_farbe_setzen_laesst_die_einteilung_stehen( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock, strip_segments: dict[str, Any], freezer: FrozenDateTimeFactory, ) -> None: """Grenzen, Namen und Helligkeit der Segmente bleiben unangetastet. Sonst risse eine Farbe ueber die Lichtentitaet die ganze Einteilung des Strips ein - und die ist muehsam von Hand gemacht. """ strip_segments["segments"] = [ { "id": 0, "name": "Regal", "start": 0, "end": 19, "mode": "white", "color_temp": 2700, "brightness": 80, }, { "id": 1, "name": "Kante", "start": 20, "end": 59, "mode": "white", "color_temp": 4000, "brightness": 55, }, ] await abfragen(hass, freezer) await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: light_id(hass), "rgb_color": (0, 0, 255)}, blocking=True, ) (geschrieben,) = mock_client.put_segments.await_args.args assert [(s["name"], s["start"], s["end"], s["brightness"]) for s in geschrieben] == [ ("Regal", 0, 19, 80), ("Kante", 20, 59, 55), ] assert all(s["mode"] == "solid" for s in geschrieben) async def test_uneinige_segmente_zeigen_keine_farbe( hass: HomeAssistant, init_integration: MockConfigEntry, strip_segments: dict[str, Any], freezer: FrozenDateTimeFactory, ) -> None: """Zwei Farben, eine Entitaet - dann waere jede Angabe gelogen.""" strip_segments["segments"] = [ { "id": 0, "start": 0, "end": 29, "mode": "solid", "color": {"r": 255, "g": 0, "b": 0}, "brightness": 80, }, { "id": 1, "start": 30, "end": 59, "mode": "solid", "color": {"r": 0, "g": 0, "b": 255}, "brightness": 80, }, ] await abfragen(hass, freezer) state = hass.states.get(light_id(hass)) assert state.attributes["color_mode"] == ColorMode.RGB assert state.attributes["rgb_color"] is None async def test_effektsegmente_melden_keine_farbtemperatur( hass: HomeAssistant, init_integration: MockConfigEntry, strip_segments: dict[str, Any], freezer: FrozenDateTimeFactory, ) -> None: strip_segments["segments"][0].update({"mode": "effect", "effect": "rainbow"}) strip_segments["segments"][0].pop("color_temp") await abfragen(hass, freezer) state = hass.states.get(light_id(hass)) assert state.attributes["color_mode"] == ColorMode.RGB assert state.attributes["color_temp_kelvin"] is None @pytest.mark.parametrize(("gewuenscht", "erwartet"), [(2700, 2700), (4000, 4000), (6500, 6500)]) async def test_farbtemperatur_setzen( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock, gewuenscht: int, erwartet: int, ) -> None: await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: light_id(hass), "color_temp_kelvin": gewuenscht}, blocking=True, ) (geschrieben,) = mock_client.put_segments.await_args.args assert [(s["mode"], s["color_temp"]) for s in geschrieben] == [("white", erwartet)] async def test_farbe_wird_vor_dem_einschalten_gesetzt( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock ) -> None: """Sonst leuchtet der Strip kurz in der alten Farbe auf.""" reihenfolge: list[str] = [] mock_client.put_segments.side_effect = lambda segments: reihenfolge.append("segmente") alt = mock_client.set_state.side_effect async def merken(**fields: Any) -> dict[str, Any]: reihenfolge.append("state") return await alt(**fields) mock_client.set_state.side_effect = merken await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: light_id(hass), "rgb_color": (10, 20, 30), ATTR_BRIGHTNESS: 128}, blocking=True, ) assert reihenfolge == ["segmente", "state"] mock_client.set_state.assert_awaited_once_with(on=True, brightness=50) async def test_ohne_farbe_werden_die_segmente_nicht_angefasst( hass: HomeAssistant, init_integration: MockConfigEntry, mock_client: MagicMock ) -> None: await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: light_id(hass)}, blocking=True ) mock_client.put_segments.assert_not_awaited()