mirror of
https://github.com/home-assistant/core.git
synced 2025-07-08 13:57:10 +00:00
Stub light profiles (#42232)
This commit is contained in:
parent
b626df1316
commit
5dac3883b0
@ -199,11 +199,11 @@ def lifx_features(bulb):
|
|||||||
) or aiolifx().products.features_map.get(1)
|
) or aiolifx().products.features_map.get(1)
|
||||||
|
|
||||||
|
|
||||||
def find_hsbk(**kwargs):
|
def find_hsbk(hass, **kwargs):
|
||||||
"""Find the desired color from a number of possible inputs."""
|
"""Find the desired color from a number of possible inputs."""
|
||||||
hue, saturation, brightness, kelvin = [None] * 4
|
hue, saturation, brightness, kelvin = [None] * 4
|
||||||
|
|
||||||
preprocess_turn_on_alternatives(kwargs)
|
preprocess_turn_on_alternatives(hass, kwargs)
|
||||||
|
|
||||||
if ATTR_HS_COLOR in kwargs:
|
if ATTR_HS_COLOR in kwargs:
|
||||||
hue, saturation = kwargs[ATTR_HS_COLOR]
|
hue, saturation = kwargs[ATTR_HS_COLOR]
|
||||||
@ -330,11 +330,11 @@ class LIFXManager:
|
|||||||
period=kwargs.get(ATTR_PERIOD),
|
period=kwargs.get(ATTR_PERIOD),
|
||||||
cycles=kwargs.get(ATTR_CYCLES),
|
cycles=kwargs.get(ATTR_CYCLES),
|
||||||
mode=kwargs.get(ATTR_MODE),
|
mode=kwargs.get(ATTR_MODE),
|
||||||
hsbk=find_hsbk(**kwargs),
|
hsbk=find_hsbk(self.hass, **kwargs),
|
||||||
)
|
)
|
||||||
await self.effects_conductor.start(effect, bulbs)
|
await self.effects_conductor.start(effect, bulbs)
|
||||||
elif service == SERVICE_EFFECT_COLORLOOP:
|
elif service == SERVICE_EFFECT_COLORLOOP:
|
||||||
preprocess_turn_on_alternatives(kwargs)
|
preprocess_turn_on_alternatives(self.hass, kwargs)
|
||||||
|
|
||||||
brightness = None
|
brightness = None
|
||||||
if ATTR_BRIGHTNESS in kwargs:
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
@ -600,7 +600,7 @@ class LIFXLight(LightEntity):
|
|||||||
power_on = kwargs.get(ATTR_POWER, False)
|
power_on = kwargs.get(ATTR_POWER, False)
|
||||||
power_off = not kwargs.get(ATTR_POWER, True)
|
power_off = not kwargs.get(ATTR_POWER, True)
|
||||||
|
|
||||||
hsbk = find_hsbk(**kwargs)
|
hsbk = find_hsbk(self.hass, **kwargs)
|
||||||
|
|
||||||
# Send messages, waiting for ACK each time
|
# Send messages, waiting for ACK each time
|
||||||
ack = AwaitAioLIFX().wait
|
ack = AwaitAioLIFX().wait
|
||||||
|
@ -3,7 +3,6 @@ import csv
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import Dict, Optional, Tuple
|
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -13,6 +12,7 @@ from homeassistant.const import (
|
|||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
)
|
)
|
||||||
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||||
PLATFORM_SCHEMA,
|
PLATFORM_SCHEMA,
|
||||||
@ -28,6 +28,7 @@ import homeassistant.util.color as color_util
|
|||||||
|
|
||||||
DOMAIN = "light"
|
DOMAIN = "light"
|
||||||
SCAN_INTERVAL = timedelta(seconds=30)
|
SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
|
DATA_PROFILES = "light_profiles"
|
||||||
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
|
|
||||||
@ -122,15 +123,6 @@ LIGHT_TURN_ON_SCHEMA = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PROFILE_SCHEMA = vol.Schema(
|
|
||||||
vol.Any(
|
|
||||||
vol.ExactSequence((str, cv.small_float, cv.small_float, cv.byte)),
|
|
||||||
vol.ExactSequence(
|
|
||||||
(str, cv.small_float, cv.small_float, cv.byte, cv.positive_int)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -140,14 +132,17 @@ def is_on(hass, entity_id):
|
|||||||
return hass.states.is_state(entity_id, STATE_ON)
|
return hass.states.is_state(entity_id, STATE_ON)
|
||||||
|
|
||||||
|
|
||||||
def preprocess_turn_on_alternatives(params):
|
def preprocess_turn_on_alternatives(hass, params):
|
||||||
"""Process extra data for turn light on request."""
|
"""Process extra data for turn light on request.
|
||||||
profile = Profiles.get(params.pop(ATTR_PROFILE, None))
|
|
||||||
if profile is not None:
|
Async friendly.
|
||||||
params.setdefault(ATTR_XY_COLOR, profile[:2])
|
"""
|
||||||
params.setdefault(ATTR_BRIGHTNESS, profile[2])
|
# Bail out, we process this later.
|
||||||
if len(profile) > 3:
|
if ATTR_BRIGHTNESS_STEP in params or ATTR_BRIGHTNESS_STEP_PCT in params:
|
||||||
params.setdefault(ATTR_TRANSITION, profile[3])
|
return
|
||||||
|
|
||||||
|
if ATTR_PROFILE in params:
|
||||||
|
hass.data[DATA_PROFILES].apply_profile(params.pop(ATTR_PROFILE), params)
|
||||||
|
|
||||||
color_name = params.pop(ATTR_COLOR_NAME, None)
|
color_name = params.pop(ATTR_COLOR_NAME, None)
|
||||||
if color_name is not None:
|
if color_name is not None:
|
||||||
@ -174,24 +169,12 @@ def preprocess_turn_on_alternatives(params):
|
|||||||
if rgb_color is not None:
|
if rgb_color is not None:
|
||||||
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def filter_turn_off_params(params):
|
def filter_turn_off_params(params):
|
||||||
"""Filter out params not used in turn off."""
|
"""Filter out params not used in turn off."""
|
||||||
return {k: v for k, v in params.items() if k in (ATTR_TRANSITION, ATTR_FLASH)}
|
return {k: v for k, v in params.items() if k in (ATTR_TRANSITION, ATTR_FLASH)}
|
||||||
|
|
||||||
|
|
||||||
def preprocess_turn_off(params):
|
|
||||||
"""Process data for turning light off if brightness is 0."""
|
|
||||||
if ATTR_BRIGHTNESS in params and params[ATTR_BRIGHTNESS] == 0:
|
|
||||||
# Zero brightness: Light will be turned off
|
|
||||||
params = filter_turn_off_params(params)
|
|
||||||
return (True, params) # Light should be turned off
|
|
||||||
|
|
||||||
return (False, None) # Light should be turned on
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
"""Expose light control via state machine and services."""
|
"""Expose light control via state machine and services."""
|
||||||
component = hass.data[DOMAIN] = EntityComponent(
|
component = hass.data[DOMAIN] = EntityComponent(
|
||||||
@ -199,10 +182,8 @@ async def async_setup(hass, config):
|
|||||||
)
|
)
|
||||||
await component.async_setup(config)
|
await component.async_setup(config)
|
||||||
|
|
||||||
# load profiles from files
|
profiles = hass.data[DATA_PROFILES] = Profiles(hass)
|
||||||
profiles_valid = await Profiles.load_profiles(hass)
|
await profiles.async_initialize()
|
||||||
if not profiles_valid:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def preprocess_data(data):
|
def preprocess_data(data):
|
||||||
"""Preprocess the service data."""
|
"""Preprocess the service data."""
|
||||||
@ -212,7 +193,8 @@ async def async_setup(hass, config):
|
|||||||
if entity_field in data
|
if entity_field in data
|
||||||
}
|
}
|
||||||
|
|
||||||
base["params"] = preprocess_turn_on_alternatives(data)
|
preprocess_turn_on_alternatives(hass, data)
|
||||||
|
base["params"] = data
|
||||||
return base
|
return base
|
||||||
|
|
||||||
async def async_handle_light_on_service(light, call):
|
async def async_handle_light_on_service(light, call):
|
||||||
@ -223,17 +205,14 @@ async def async_setup(hass, config):
|
|||||||
params = call.data["params"]
|
params = call.data["params"]
|
||||||
|
|
||||||
if not params:
|
if not params:
|
||||||
default_profile = Profiles.get_default(light.entity_id)
|
profiles.apply_default(light.entity_id, params)
|
||||||
|
|
||||||
if default_profile is not None:
|
# Only process params once we processed brightness step
|
||||||
params = {ATTR_PROFILE: default_profile}
|
if params and (
|
||||||
preprocess_turn_on_alternatives(params)
|
ATTR_BRIGHTNESS_STEP in params or ATTR_BRIGHTNESS_STEP_PCT in params
|
||||||
|
):
|
||||||
elif ATTR_BRIGHTNESS_STEP in params or ATTR_BRIGHTNESS_STEP_PCT in params:
|
|
||||||
brightness = light.brightness if light.is_on else 0
|
brightness = light.brightness if light.is_on else 0
|
||||||
|
|
||||||
params = params.copy()
|
|
||||||
|
|
||||||
if ATTR_BRIGHTNESS_STEP in params:
|
if ATTR_BRIGHTNESS_STEP in params:
|
||||||
brightness += params.pop(ATTR_BRIGHTNESS_STEP)
|
brightness += params.pop(ATTR_BRIGHTNESS_STEP)
|
||||||
|
|
||||||
@ -242,19 +221,18 @@ async def async_setup(hass, config):
|
|||||||
|
|
||||||
params[ATTR_BRIGHTNESS] = max(0, min(255, brightness))
|
params[ATTR_BRIGHTNESS] = max(0, min(255, brightness))
|
||||||
|
|
||||||
turn_light_off, off_params = preprocess_turn_off(params)
|
preprocess_turn_on_alternatives(hass, params)
|
||||||
if turn_light_off:
|
|
||||||
await light.async_turn_off(**off_params)
|
# Zero brightness: Light will be turned off
|
||||||
|
if params.get(ATTR_BRIGHTNESS) == 0:
|
||||||
|
await light.async_turn_off(**filter_turn_off_params(params))
|
||||||
else:
|
else:
|
||||||
await light.async_turn_on(**params)
|
await light.async_turn_on(**params)
|
||||||
|
|
||||||
async def async_handle_toggle_service(light, call):
|
async def async_handle_toggle_service(light, call):
|
||||||
"""Handle toggling a light.
|
"""Handle toggling a light."""
|
||||||
|
|
||||||
If brightness is set to 0, this service will turn the light off.
|
|
||||||
"""
|
|
||||||
if light.is_on:
|
if light.is_on:
|
||||||
off_params = filter_turn_off_params(call.data["params"])
|
off_params = filter_turn_off_params(call.data)
|
||||||
await light.async_turn_off(**off_params)
|
await light.async_turn_off(**off_params)
|
||||||
else:
|
else:
|
||||||
await async_handle_light_on_service(light, call)
|
await async_handle_light_on_service(light, call)
|
||||||
@ -295,17 +273,25 @@ async def async_unload_entry(hass, entry):
|
|||||||
class Profiles:
|
class Profiles:
|
||||||
"""Representation of available color profiles."""
|
"""Representation of available color profiles."""
|
||||||
|
|
||||||
_all: Optional[Dict[str, Tuple[float, float, int]]] = None
|
SCHEMA = vol.Schema(
|
||||||
|
vol.Any(
|
||||||
|
vol.ExactSequence((str, cv.small_float, cv.small_float, cv.byte)),
|
||||||
|
vol.ExactSequence(
|
||||||
|
(str, cv.small_float, cv.small_float, cv.byte, cv.positive_int)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
def __init__(self, hass):
|
||||||
async def load_profiles(cls, hass):
|
"""Initialize profiles."""
|
||||||
"""Load and cache profiles."""
|
self.hass = hass
|
||||||
|
self.data = None
|
||||||
|
|
||||||
def load_profile_data(hass):
|
def _load_profile_data(self):
|
||||||
"""Load built-in profiles and custom profiles."""
|
"""Load built-in profiles and custom profiles."""
|
||||||
profile_paths = [
|
profile_paths = [
|
||||||
os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE),
|
os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE),
|
||||||
hass.config.path(LIGHT_PROFILES_FILE),
|
self.hass.config.path(LIGHT_PROFILES_FILE),
|
||||||
]
|
]
|
||||||
profiles = {}
|
profiles = {}
|
||||||
|
|
||||||
@ -326,13 +312,13 @@ class Profiles:
|
|||||||
color_y,
|
color_y,
|
||||||
brightness,
|
brightness,
|
||||||
*transition,
|
*transition,
|
||||||
) = PROFILE_SCHEMA(rec)
|
) = Profiles.SCHEMA(rec)
|
||||||
|
|
||||||
transition = transition[0] if transition else 0
|
transition = transition[0] if transition else 0
|
||||||
|
|
||||||
profiles[profile] = (
|
profiles[profile] = color_util.color_xy_to_hs(
|
||||||
color_x,
|
color_x, color_y
|
||||||
color_y,
|
) + (
|
||||||
brightness,
|
brightness,
|
||||||
transition,
|
transition,
|
||||||
)
|
)
|
||||||
@ -340,28 +326,36 @@ class Profiles:
|
|||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Error parsing light profile from %s: %s", profile_path, ex
|
"Error parsing light profile from %s: %s", profile_path, ex
|
||||||
)
|
)
|
||||||
return None
|
continue
|
||||||
return profiles
|
return profiles
|
||||||
|
|
||||||
cls._all = await hass.async_add_executor_job(load_profile_data, hass)
|
async def async_initialize(self):
|
||||||
return cls._all is not None
|
"""Load and cache profiles."""
|
||||||
|
self.data = await self.hass.async_add_executor_job(self._load_profile_data)
|
||||||
|
|
||||||
@classmethod
|
@callback
|
||||||
def get(cls, name):
|
def apply_default(self, entity_id, params):
|
||||||
"""Return a named profile."""
|
|
||||||
return cls._all.get(name)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_default(cls, entity_id):
|
|
||||||
"""Return the default turn-on profile for the given light."""
|
"""Return the default turn-on profile for the given light."""
|
||||||
# pylint: disable=unsupported-membership-test
|
|
||||||
name = f"{entity_id}.default"
|
name = f"{entity_id}.default"
|
||||||
if name in cls._all:
|
if name in self.data:
|
||||||
return name
|
self.apply_profile(name, params)
|
||||||
|
return
|
||||||
|
|
||||||
name = "group.all_lights.default"
|
name = "group.all_lights.default"
|
||||||
if name in cls._all:
|
if name in self.data:
|
||||||
return name
|
self.apply_profile(name, params)
|
||||||
return None
|
|
||||||
|
@callback
|
||||||
|
def apply_profile(self, name, params):
|
||||||
|
"""Apply a profile."""
|
||||||
|
profile = self.data.get(name)
|
||||||
|
|
||||||
|
if profile is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
params.setdefault(ATTR_HS_COLOR, profile[:2])
|
||||||
|
params.setdefault(ATTR_BRIGHTNESS, profile[2])
|
||||||
|
params.setdefault(ATTR_TRANSITION, profile[3])
|
||||||
|
|
||||||
|
|
||||||
class LightEntity(ToggleEntity):
|
class LightEntity(ToggleEntity):
|
||||||
|
@ -3,6 +3,7 @@ import abodepy.helpers.constants as CONST
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.common import load_fixture
|
from tests.common import load_fixture
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
|
2
tests/components/axis/conftest.py
Normal file
2
tests/components/axis/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""axis conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
@ -1,5 +1,4 @@
|
|||||||
"""PyTest fixtures and test helpers."""
|
"""PyTest fixtures and test helpers."""
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import blebox_uniapi
|
import blebox_uniapi
|
||||||
@ -11,6 +10,7 @@ from homeassistant.setup import async_setup_component
|
|||||||
|
|
||||||
from tests.async_mock import AsyncMock, PropertyMock, patch
|
from tests.async_mock import AsyncMock, PropertyMock, patch
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
def patch_product_identify(path=None, **kwargs):
|
def patch_product_identify(path=None, **kwargs):
|
||||||
|
2
tests/components/bond/conftest.py
Normal file
2
tests/components/bond/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""bond conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/deconz/conftest.py
Normal file
2
tests/components/deconz/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""deconz conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/demo/conftest.py
Normal file
2
tests/components/demo/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""demo conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/dynalite/conftest.py
Normal file
2
tests/components/dynalite/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""dynalite conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/elgato/conftest.py
Normal file
2
tests/components/elgato/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""elgato conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/everlights/conftest.py
Normal file
2
tests/components/everlights/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""everlights conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/group/conftest.py
Normal file
2
tests/components/group/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""group conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
@ -8,6 +8,7 @@ import pytest
|
|||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
import tests.async_mock
|
import tests.async_mock
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -24,6 +24,7 @@ from .helper import AUTH_TOKEN, HAPID, HAPPIN, HomeFactory
|
|||||||
|
|
||||||
from tests.async_mock import AsyncMock, MagicMock, Mock, patch
|
from tests.async_mock import AsyncMock, MagicMock, Mock, patch
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="mock_connection")
|
@pytest.fixture(name="mock_connection")
|
||||||
|
@ -12,6 +12,7 @@ from homeassistant.components import hue
|
|||||||
from homeassistant.components.hue import sensor_base as hue_sensor_base
|
from homeassistant.components.hue import sensor_base as hue_sensor_base
|
||||||
|
|
||||||
from tests.async_mock import AsyncMock, Mock, patch
|
from tests.async_mock import AsyncMock, Mock, patch
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
|
2
tests/components/hyperion/conftest.py
Normal file
2
tests/components/hyperion/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""hyperion conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
26
tests/components/light/conftest.py
Normal file
26
tests/components/light/conftest.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"""Light conftest."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.light import Profiles
|
||||||
|
|
||||||
|
from tests.async_mock import AsyncMock, patch
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def mock_light_profiles():
|
||||||
|
"""Mock loading of profiles."""
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
def mock_profiles_class(hass):
|
||||||
|
profiles = Profiles(hass)
|
||||||
|
profiles.data = data
|
||||||
|
profiles.async_initialize = AsyncMock()
|
||||||
|
return profiles
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.light.Profiles",
|
||||||
|
SCHEMA=Profiles.SCHEMA,
|
||||||
|
side_effect=mock_profiles_class,
|
||||||
|
):
|
||||||
|
yield data
|
@ -1,8 +1,4 @@
|
|||||||
"""The tests for the Light component."""
|
"""The tests for the Light component."""
|
||||||
# pylint: disable=protected-access
|
|
||||||
from io import StringIO
|
|
||||||
import os
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -20,19 +16,11 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.exceptions import Unauthorized
|
from homeassistant.exceptions import Unauthorized
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
from homeassistant.util import color
|
||||||
|
|
||||||
import tests.async_mock as mock
|
|
||||||
from tests.common import async_mock_service
|
from tests.common import async_mock_service
|
||||||
|
|
||||||
|
orig_Profiles = light.Profiles
|
||||||
@pytest.fixture
|
|
||||||
def mock_storage(hass, hass_storage):
|
|
||||||
"""Clean up user light files at the end."""
|
|
||||||
yield
|
|
||||||
user_light_file = hass.config.path(light.LIGHT_PROFILES_FILE)
|
|
||||||
|
|
||||||
if os.path.isfile(user_light_file):
|
|
||||||
os.remove(user_light_file)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_methods(hass):
|
async def test_methods(hass):
|
||||||
@ -117,7 +105,7 @@ async def test_methods(hass):
|
|||||||
assert call.data[light.ATTR_TRANSITION] == "transition_val"
|
assert call.data[light.ATTR_TRANSITION] == "transition_val"
|
||||||
|
|
||||||
|
|
||||||
async def test_services(hass):
|
async def test_services(hass, mock_light_profiles):
|
||||||
"""Test the provided services."""
|
"""Test the provided services."""
|
||||||
platform = getattr(hass.components, "test.light")
|
platform = getattr(hass.components, "test.light")
|
||||||
|
|
||||||
@ -292,6 +280,7 @@ async def test_services(hass):
|
|||||||
assert data == {}
|
assert data == {}
|
||||||
|
|
||||||
# One of the light profiles
|
# One of the light profiles
|
||||||
|
mock_light_profiles["relax"] = (35.932, 69.412, 144, 0)
|
||||||
prof_name, prof_h, prof_s, prof_bri, prof_t = "relax", 35.932, 69.412, 144, 0
|
prof_name, prof_h, prof_s, prof_bri, prof_t = "relax", 35.932, 69.412, 144, 0
|
||||||
|
|
||||||
# Test light profiles
|
# Test light profiles
|
||||||
@ -418,34 +407,13 @@ async def test_services(hass):
|
|||||||
assert data == {}
|
assert data == {}
|
||||||
|
|
||||||
|
|
||||||
async def test_broken_light_profiles(hass, mock_storage):
|
async def test_light_profiles(hass, mock_light_profiles):
|
||||||
"""Test light profiles."""
|
"""Test light profiles."""
|
||||||
platform = getattr(hass.components, "test.light")
|
platform = getattr(hass.components, "test.light")
|
||||||
platform.init()
|
platform.init()
|
||||||
|
|
||||||
user_light_file = hass.config.path(light.LIGHT_PROFILES_FILE)
|
mock_light_profiles["test"] = color.color_xy_to_hs(0.4, 0.6) + (100, 0)
|
||||||
|
mock_light_profiles["test_off"] = 0, 0, 0, 0
|
||||||
# Setup a wrong light file
|
|
||||||
with open(user_light_file, "w") as user_file:
|
|
||||||
user_file.write("id,x,y,brightness,transition\n")
|
|
||||||
user_file.write("I,WILL,NOT,WORK,EVER\n")
|
|
||||||
|
|
||||||
assert not await async_setup_component(
|
|
||||||
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_light_profiles(hass, mock_storage):
|
|
||||||
"""Test light profiles."""
|
|
||||||
platform = getattr(hass.components, "test.light")
|
|
||||||
platform.init()
|
|
||||||
|
|
||||||
user_light_file = hass.config.path(light.LIGHT_PROFILES_FILE)
|
|
||||||
|
|
||||||
with open(user_light_file, "w") as user_file:
|
|
||||||
user_file.write("id,x,y,brightness\n")
|
|
||||||
user_file.write("test,.4,.6,100\n")
|
|
||||||
user_file.write("test_off,0,0,0\n")
|
|
||||||
|
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
||||||
@ -484,80 +452,21 @@ async def test_light_profiles(hass, mock_storage):
|
|||||||
assert data == {light.ATTR_TRANSITION: 0}
|
assert data == {light.ATTR_TRANSITION: 0}
|
||||||
|
|
||||||
|
|
||||||
async def test_light_profiles_with_transition(hass, mock_storage):
|
async def test_default_profiles_group(hass, mock_light_profiles):
|
||||||
"""Test light profiles with transition."""
|
|
||||||
platform = getattr(hass.components, "test.light")
|
|
||||||
platform.init()
|
|
||||||
|
|
||||||
user_light_file = hass.config.path(light.LIGHT_PROFILES_FILE)
|
|
||||||
|
|
||||||
with open(user_light_file, "w") as user_file:
|
|
||||||
user_file.write("id,x,y,brightness,transition\n")
|
|
||||||
user_file.write("test,.4,.6,100,2\n")
|
|
||||||
user_file.write("test_off,0,0,0,0\n")
|
|
||||||
|
|
||||||
assert await async_setup_component(
|
|
||||||
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
ent1, _, _ = platform.ENTITIES
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
light.DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_PROFILE: "test"},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
_, data = ent1.last_call("turn_on")
|
|
||||||
assert light.is_on(hass, ent1.entity_id)
|
|
||||||
assert data == {
|
|
||||||
light.ATTR_HS_COLOR: (71.059, 100),
|
|
||||||
light.ATTR_BRIGHTNESS: 100,
|
|
||||||
light.ATTR_TRANSITION: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
light.DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_PROFILE: "test_off"},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
_, data = ent1.last_call("turn_off")
|
|
||||||
assert not light.is_on(hass, ent1.entity_id)
|
|
||||||
assert data == {light.ATTR_TRANSITION: 0}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_default_profiles_group(hass, mock_storage):
|
|
||||||
"""Test default turn-on light profile for all lights."""
|
"""Test default turn-on light profile for all lights."""
|
||||||
platform = getattr(hass.components, "test.light")
|
platform = getattr(hass.components, "test.light")
|
||||||
platform.init()
|
platform.init()
|
||||||
|
|
||||||
user_light_file = hass.config.path(light.LIGHT_PROFILES_FILE)
|
|
||||||
real_isfile = os.path.isfile
|
|
||||||
real_open = open
|
|
||||||
|
|
||||||
def _mock_isfile(path):
|
|
||||||
if path == user_light_file:
|
|
||||||
return True
|
|
||||||
return real_isfile(path)
|
|
||||||
|
|
||||||
def _mock_open(path, *args, **kwargs):
|
|
||||||
if path == user_light_file:
|
|
||||||
return StringIO(profile_data)
|
|
||||||
return real_open(path, *args, **kwargs)
|
|
||||||
|
|
||||||
profile_data = "id,x,y,brightness,transition\ngroup.all_lights.default,.4,.6,99,2\n"
|
|
||||||
with mock.patch("os.path.isfile", side_effect=_mock_isfile), mock.patch(
|
|
||||||
"builtins.open", side_effect=_mock_open
|
|
||||||
):
|
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_light_profiles["group.all_lights.default"] = color.color_xy_to_hs(0.4, 0.6) + (
|
||||||
|
99,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
|
||||||
ent, _, _ = platform.ENTITIES
|
ent, _, _ = platform.ENTITIES
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent.entity_id}, blocking=True
|
light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent.entity_id}, blocking=True
|
||||||
@ -571,38 +480,25 @@ async def test_default_profiles_group(hass, mock_storage):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_default_profiles_light(hass, mock_storage):
|
async def test_default_profiles_light(hass, mock_light_profiles):
|
||||||
"""Test default turn-on light profile for a specific light."""
|
"""Test default turn-on light profile for a specific light."""
|
||||||
platform = getattr(hass.components, "test.light")
|
platform = getattr(hass.components, "test.light")
|
||||||
platform.init()
|
platform.init()
|
||||||
|
|
||||||
user_light_file = hass.config.path(light.LIGHT_PROFILES_FILE)
|
|
||||||
real_isfile = os.path.isfile
|
|
||||||
real_open = open
|
|
||||||
|
|
||||||
def _mock_isfile(path):
|
|
||||||
if path == user_light_file:
|
|
||||||
return True
|
|
||||||
return real_isfile(path)
|
|
||||||
|
|
||||||
def _mock_open(path, *args, **kwargs):
|
|
||||||
if path == user_light_file:
|
|
||||||
return StringIO(profile_data)
|
|
||||||
return real_open(path, *args, **kwargs)
|
|
||||||
|
|
||||||
profile_data = (
|
|
||||||
"id,x,y,brightness,transition\n"
|
|
||||||
+ "group.all_lights.default,.3,.5,200,0\n"
|
|
||||||
+ "light.ceiling_2.default,.6,.6,100,3\n"
|
|
||||||
)
|
|
||||||
with mock.patch("os.path.isfile", side_effect=_mock_isfile), mock.patch(
|
|
||||||
"builtins.open", side_effect=_mock_open
|
|
||||||
):
|
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_light_profiles["group.all_lights.default"] = color.color_xy_to_hs(0.3, 0.5) + (
|
||||||
|
200,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
mock_light_profiles["light.ceiling_2.default"] = color.color_xy_to_hs(0.6, 0.6) + (
|
||||||
|
100,
|
||||||
|
3,
|
||||||
|
)
|
||||||
|
|
||||||
dev = next(filter(lambda x: x.entity_id == "light.ceiling_2", platform.ENTITIES))
|
dev = next(filter(lambda x: x.entity_id == "light.ceiling_2", platform.ENTITIES))
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
light.DOMAIN,
|
light.DOMAIN,
|
||||||
@ -775,3 +671,15 @@ def test_deprecated_base_class(caplog):
|
|||||||
|
|
||||||
CustomLight()
|
CustomLight()
|
||||||
assert "Light is deprecated, modify CustomLight" in caplog.text
|
assert "Light is deprecated, modify CustomLight" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_profiles(hass):
|
||||||
|
"""Test profiles loading."""
|
||||||
|
profiles = orig_Profiles(hass)
|
||||||
|
await profiles.async_initialize()
|
||||||
|
assert profiles.data == {
|
||||||
|
"concentrate": (35.932, 69.412, 219, 0),
|
||||||
|
"energize": (43.333, 21.176, 203, 0),
|
||||||
|
"reading": (38.88, 49.02, 240, 0),
|
||||||
|
"relax": (35.932, 69.412, 144, 0),
|
||||||
|
}
|
||||||
|
2
tests/components/litejet/conftest.py
Normal file
2
tests/components/litejet/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""litejet conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/mochad/conftest.py
Normal file
2
tests/components/mochad/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""mochad conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
@ -1 +1,2 @@
|
|||||||
"""Test fixtures for mqtt component."""
|
"""Test fixtures for mqtt component."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
@ -7,6 +7,7 @@ from .common import MQTTMessage
|
|||||||
|
|
||||||
from tests.async_mock import patch
|
from tests.async_mock import patch
|
||||||
from tests.common import load_fixture
|
from tests.common import load_fixture
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="generic_data", scope="session")
|
@pytest.fixture(name="generic_data", scope="session")
|
||||||
|
2
tests/components/rflink/conftest.py
Normal file
2
tests/components/rflink/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""rflink conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
@ -9,6 +9,7 @@ from homeassistant.util.dt import utcnow
|
|||||||
|
|
||||||
from tests.async_mock import patch
|
from tests.async_mock import patch
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
def create_rfx_test_cfg(device="abcd", automatic_add=False, devices=None):
|
def create_rfx_test_cfg(device="abcd", automatic_add=False, devices=None):
|
||||||
|
@ -5,6 +5,7 @@ import pytest
|
|||||||
import requests_mock
|
import requests_mock
|
||||||
|
|
||||||
from tests.common import load_fixture
|
from tests.common import load_fixture
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="requests_mock")
|
@pytest.fixture(name="requests_mock")
|
||||||
|
@ -47,6 +47,7 @@ from homeassistant.setup import async_setup_component
|
|||||||
|
|
||||||
from tests.async_mock import Mock, patch
|
from tests.async_mock import Mock, patch
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
COMPONENT_PREFIX = "homeassistant.components.smartthings."
|
COMPONENT_PREFIX = "homeassistant.components.smartthings."
|
||||||
|
|
||||||
|
2
tests/components/switch/conftest.py
Normal file
2
tests/components/switch/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""switch conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
@ -1,5 +1,4 @@
|
|||||||
"""Test fixtures for Tasmota component."""
|
"""Test fixtures for Tasmota component."""
|
||||||
|
|
||||||
from hatasmota.discovery import get_status_sensor_entities
|
from hatasmota.discovery import get_status_sensor_entities
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -17,6 +16,7 @@ from tests.common import (
|
|||||||
mock_device_registry,
|
mock_device_registry,
|
||||||
mock_registry,
|
mock_registry,
|
||||||
)
|
)
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
2
tests/components/template/conftest.py
Normal file
2
tests/components/template/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""template conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/tplink/conftest.py
Normal file
2
tests/components/tplink/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""tplink conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
@ -4,6 +4,7 @@ import pytest
|
|||||||
from . import MOCK_GATEWAY_ID
|
from . import MOCK_GATEWAY_ID
|
||||||
|
|
||||||
from tests.async_mock import Mock, patch
|
from tests.async_mock import Mock, patch
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
"""Fixtures for tests."""
|
"""Fixtures for tests."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from .common import ComponentFactory
|
from .common import ComponentFactory
|
||||||
|
|
||||||
from tests.async_mock import patch
|
from tests.async_mock import patch
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
2
tests/components/wilight/conftest.py
Normal file
2
tests/components/wilight/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""wilight conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/wled/conftest.py
Normal file
2
tests/components/wled/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""wled conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/yeelight/conftest.py
Normal file
2
tests/components/yeelight/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""yeelight conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
2
tests/components/zerproc/conftest.py
Normal file
2
tests/components/zerproc/conftest.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""zerproc conftest."""
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
@ -1,5 +1,4 @@
|
|||||||
"""Test configuration for the ZHA component."""
|
"""Test configuration for the ZHA component."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import zigpy
|
import zigpy
|
||||||
from zigpy.application import ControllerApplication
|
from zigpy.application import ControllerApplication
|
||||||
@ -16,6 +15,7 @@ from .common import FakeDevice, FakeEndpoint, get_zha_gateway
|
|||||||
|
|
||||||
from tests.async_mock import AsyncMock, MagicMock, PropertyMock, patch
|
from tests.async_mock import AsyncMock, MagicMock, PropertyMock, patch
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
|
|
||||||
FIXTURE_GRP_ID = 0x1001
|
FIXTURE_GRP_ID = 0x1001
|
||||||
FIXTURE_GRP_NAME = "fixture group"
|
FIXTURE_GRP_NAME = "fixture group"
|
||||||
|
@ -4,6 +4,7 @@ import pytest
|
|||||||
from homeassistant.components.zwave import const
|
from homeassistant.components.zwave import const
|
||||||
|
|
||||||
from tests.async_mock import AsyncMock, MagicMock, patch
|
from tests.async_mock import AsyncMock, MagicMock, patch
|
||||||
|
from tests.components.light.conftest import mock_light_profiles # noqa
|
||||||
from tests.mock.zwave import MockNetwork, MockNode, MockOption, MockValue
|
from tests.mock.zwave import MockNetwork, MockNode, MockOption, MockValue
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user