mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-10-27 21:57:14 +01:00
✨ Add time option to adaptive_lighting.apply
Apply the settings Adaptive Lighting would use at a given time of day (today) instead of now, e.g. to switch a room to its evening settings early. The adapted attributes are marked as manually controlled, like a manual change in Home Assistant, so the regular adaptation keeps its hands off until manual control is reset. They are marked before the adaptation, so a regular adaptation can't slip in between and overwrite the values. Sleep mode takes precedence, and the switch's own settings and attributes are left untouched. `prepare_adaptation_data` no longer writes the switch's settings: it did so only to have values to read when the switch was off, and now uses the settings it calculates itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
283fec08ae
commit
54a821cc4c
10 changed files with 267 additions and 8 deletions
16
README.md
16
README.md
|
|
@ -227,8 +227,24 @@ Provide a switch in `entity_id`, a list of `lights`, or both.
|
|||
| `adapt_color` | Whether to adapt the color on supporting lights. 🌈 | ❌ | bool |
|
||||
| `prefer_rgb_color` | Whether to prefer RGB color adjustment over light color temperature when possible. 🌈 | ❌ | bool |
|
||||
| `turn_on_lights` | Whether to turn on lights that are currently off. 🔆 | ❌ | bool |
|
||||
| `time` | Manually apply the settings for this time of day (HH:MM:SS) instead of now. 🕰️ | ❌ | time (HH:MM:SS) |
|
||||
|
||||
<!-- OUTPUT:END -->
|
||||
|
||||
With `time`, the lights get the values Adaptive Lighting would use at that time today, for example to dim a room to its evening settings early.
|
||||
The adapted attributes are then marked as manually controlled, exactly as if you had changed them yourself: the regular adaptation leaves them alone until manual control is reset (see `autoreset_control_seconds` and `adaptive_lighting.set_manual_control`), and an `adaptive_lighting.manual_control` event is fired.
|
||||
When sleep mode is on, the sleep settings are applied instead, whatever the time.
|
||||
|
||||
```yaml
|
||||
action: adaptive_lighting.apply
|
||||
data:
|
||||
entity_id: switch.adaptive_lighting_living_room
|
||||
lights:
|
||||
- light.sofa
|
||||
- light.floor_lamp
|
||||
time: "22:00:00"
|
||||
```
|
||||
|
||||
#### `adaptive_lighting.set_manual_control`
|
||||
|
||||
`adaptive_lighting.set_manual_control` can mark (or unmark) whether a light is "manually controlled", meaning that when a light has `manual_control`, the light is not adapted.
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ def _type_to_str(type_: Any) -> str: # noqa: PLR0911
|
|||
return f"`{type_.__name__}`"
|
||||
if type_ == cv.boolean:
|
||||
return "bool"
|
||||
if type_ == cv.time:
|
||||
return "time (HH:MM:SS)"
|
||||
if isinstance(type_, vol.All):
|
||||
return _format_voluptuous_instance(type_)
|
||||
if isinstance(type_, vol.Any):
|
||||
|
|
|
|||
|
|
@ -464,12 +464,17 @@ class SunLightSettings:
|
|||
self,
|
||||
is_sleep: bool,
|
||||
transition: float | None,
|
||||
dt: datetime.datetime | None = None,
|
||||
) -> dict[str, float | int | tuple[float, float] | tuple[float, float, float]]:
|
||||
"""Get all light settings.
|
||||
|
||||
Uses the current time plus ``transition`` unless ``dt`` is given, in which
|
||||
case the settings are calculated as if it were ``dt``.
|
||||
|
||||
Calculating all values takes <0.5ms.
|
||||
"""
|
||||
dt = utcnow() + timedelta(seconds=transition or 0)
|
||||
if dt is None:
|
||||
dt = utcnow() + timedelta(seconds=transition or 0)
|
||||
return self.brightness_and_color(dt, is_sleep)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -316,6 +316,10 @@ DOCS[CONF_MANUAL_CONTROL] = "Whether to manually control the lights. 🔒"
|
|||
SERVICE_APPLY = "apply"
|
||||
CONF_TURN_ON_LIGHTS = "turn_on_lights"
|
||||
DOCS[CONF_TURN_ON_LIGHTS] = "Whether to turn on lights that are currently off. 🔆"
|
||||
CONF_APPLY_TIME = "time"
|
||||
DOCS[CONF_APPLY_TIME] = (
|
||||
"Manually apply the settings for this time of day (HH:MM:SS) instead of now. 🕰️"
|
||||
)
|
||||
SERVICE_CHANGE_SWITCH_SETTINGS = "change_switch_settings"
|
||||
CONF_USE_DEFAULTS = "use_defaults"
|
||||
DOCS[CONF_USE_DEFAULTS] = (
|
||||
|
|
@ -521,6 +525,7 @@ def apply_service_schema() -> vol.Schema:
|
|||
vol.Optional(ATTR_ADAPT_COLOR, default=True): cv.boolean,
|
||||
vol.Optional(CONF_PREFER_RGB_COLOR, default=False): cv.boolean,
|
||||
vol.Optional(CONF_TURN_ON_LIGHTS, default=False): cv.boolean,
|
||||
vol.Optional(CONF_APPLY_TIME): cv.time,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ apply:
|
|||
example: false
|
||||
selector:
|
||||
boolean: null
|
||||
time:
|
||||
description: Manually apply the settings for this time of day (HH:MM:SS) instead of now. 🕰️
|
||||
example: '22:00:00'
|
||||
selector:
|
||||
time: null
|
||||
set_manual_control:
|
||||
description: Mark whether a light is 'manually controlled'.
|
||||
fields:
|
||||
|
|
|
|||
|
|
@ -143,6 +143,10 @@
|
|||
"turn_on_lights": {
|
||||
"description": "Whether to turn on lights that are currently off. 🔆",
|
||||
"name": "turn_on_lights"
|
||||
},
|
||||
"time": {
|
||||
"description": "Manually apply the settings for this time of day (HH:MM:SS) instead of now. 🕰️",
|
||||
"name": "time"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ from .const import (
|
|||
CONF_ADAPT_DELAY,
|
||||
CONF_ADAPT_ONLY_ON_BARE_TURN_ON,
|
||||
CONF_ADAPT_UNTIL_SLEEP,
|
||||
CONF_APPLY_TIME,
|
||||
CONF_AUTORESET_CONTROL,
|
||||
CONF_BRIGHTNESS_MODE,
|
||||
CONF_BRIGHTNESS_MODE_TIME_DARK,
|
||||
|
|
@ -422,9 +423,23 @@ async def handle_apply_service(hass: HomeAssistant, service_call: ServiceCall) -
|
|||
)
|
||||
switches = _switches_from_service_call(hass, service_call)
|
||||
lights = data[CONF_LIGHTS]
|
||||
apply_time: datetime.time | None = data.get(CONF_APPLY_TIME)
|
||||
# Applying the settings of another time of day only sticks if the regular
|
||||
# adaptation leaves the light alone, so treat it like a manual change.
|
||||
manual_attributes = LightControlAttributes.NONE
|
||||
if apply_time is not None:
|
||||
if data[ATTR_ADAPT_BRIGHTNESS]:
|
||||
manual_attributes |= LightControlAttributes.BRIGHTNESS
|
||||
if data[ATTR_ADAPT_COLOR]:
|
||||
manual_attributes |= LightControlAttributes.COLOR
|
||||
for switch in switches:
|
||||
all_lights = switch._resolve_lights(lights or None)
|
||||
switch.manager.lights.update(all_lights)
|
||||
at_time = None
|
||||
if apply_time is not None:
|
||||
tz = switch._sun_light_settings.timezone # pylint: disable=protected-access
|
||||
today = dt_util.now(tz).date()
|
||||
at_time = datetime.datetime.combine(today, apply_time, tzinfo=tz)
|
||||
for light in all_lights:
|
||||
if data[CONF_TURN_ON_LIGHTS] or is_on(hass, light):
|
||||
context = switch.create_context(
|
||||
|
|
@ -434,6 +449,13 @@ async def handle_apply_service(hass: HomeAssistant, service_call: ServiceCall) -
|
|||
transition = data.get(CONF_TRANSITION)
|
||||
if transition is None:
|
||||
transition = switch.initial_transition
|
||||
if manual_attributes:
|
||||
# Mark before adapting, so a regular adaptation cannot slip in
|
||||
# between and overwrite the values with those of the current time.
|
||||
switch.manager.add_manual_control_attributes(
|
||||
light,
|
||||
manual_attributes,
|
||||
)
|
||||
await switch._adapt_light( # pylint: disable=protected-access
|
||||
light,
|
||||
context=context,
|
||||
|
|
@ -442,7 +464,18 @@ async def handle_apply_service(hass: HomeAssistant, service_call: ServiceCall) -
|
|||
adapt_color=data[ATTR_ADAPT_COLOR],
|
||||
prefer_rgb_color=data[CONF_PREFER_RGB_COLOR],
|
||||
force=True,
|
||||
at_time=at_time,
|
||||
)
|
||||
if manual_attributes:
|
||||
# Use the applied state as the manual-control baseline, like
|
||||
# the state that results from a manual change.
|
||||
if (state := hass.states.get(light)) is not None:
|
||||
switch.manager.update_manual_control_state(
|
||||
light,
|
||||
state,
|
||||
manual_attributes,
|
||||
)
|
||||
switch.fire_manual_control_event(light, context)
|
||||
|
||||
|
||||
async def handle_set_manual_control_service(
|
||||
|
|
@ -1315,8 +1348,13 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
force: bool = False,
|
||||
context: Context | None = None,
|
||||
already_applied: LightControlAttributes = LightControlAttributes.NONE,
|
||||
at_time: datetime.datetime | None = None,
|
||||
) -> AdaptationData | None:
|
||||
"""Prepare `AdaptationData` for adapting a light."""
|
||||
"""Prepare `AdaptationData` for adapting a light.
|
||||
|
||||
If `at_time` is given, the light is adapted as if it were that time. The
|
||||
switch's own settings (and its state attributes) are left untouched.
|
||||
"""
|
||||
adaptation_attributes = self.manager.get_adaption_control_attributes(
|
||||
self,
|
||||
light,
|
||||
|
|
@ -1342,10 +1380,10 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
)
|
||||
return None
|
||||
|
||||
# The switch might be off and not have _settings set.
|
||||
self._settings = self._sun_light_settings.get_settings(
|
||||
settings = self._sun_light_settings.get_settings(
|
||||
self.sleep_mode_switch.is_on,
|
||||
transition,
|
||||
at_time,
|
||||
)
|
||||
|
||||
# Build service data.
|
||||
|
|
@ -1358,7 +1396,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
service_data[ATTR_TRANSITION] = transition
|
||||
|
||||
if "brightness" in features and adapt_brightness:
|
||||
brightness = round(255 * self._settings["brightness_pct"] / 100)
|
||||
brightness = round(255 * settings["brightness_pct"] / 100)
|
||||
service_data[ATTR_BRIGHTNESS] = brightness
|
||||
|
||||
sleep_rgb = (
|
||||
|
|
@ -1370,7 +1408,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
and adapt_color
|
||||
and not (prefer_rgb_color and "color" in features)
|
||||
and not (sleep_rgb and "color" in features)
|
||||
and not (self._settings["force_rgb_color"] and "color" in features)
|
||||
and not (settings["force_rgb_color"] and "color" in features)
|
||||
):
|
||||
_LOGGER.debug("%s: Setting color_temp of light %s", self._name, light)
|
||||
state = self.hass.states.get(light)
|
||||
|
|
@ -1378,12 +1416,12 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
attributes = state.attributes
|
||||
min_kelvin = attributes["min_color_temp_kelvin"]
|
||||
max_kelvin = attributes["max_color_temp_kelvin"]
|
||||
color_temp_kelvin = self._settings["color_temp_kelvin"]
|
||||
color_temp_kelvin = settings["color_temp_kelvin"]
|
||||
color_temp_kelvin = clamp(color_temp_kelvin, min_kelvin, max_kelvin)
|
||||
service_data[ATTR_COLOR_TEMP_KELVIN] = color_temp_kelvin
|
||||
elif "color" in features and adapt_color:
|
||||
_LOGGER.debug("%s: Setting rgb_color of light %s", self._name, light)
|
||||
service_data[ATTR_RGB_COLOR] = self._settings["rgb_color"]
|
||||
service_data[ATTR_RGB_COLOR] = settings["rgb_color"]
|
||||
|
||||
required_attrs = [ATTR_RGB_COLOR, ATTR_COLOR_TEMP_KELVIN, ATTR_BRIGHTNESS]
|
||||
if not any(attr in service_data for attr in required_attrs):
|
||||
|
|
@ -1420,6 +1458,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
adapt_color: bool | None = None,
|
||||
prefer_rgb_color: bool | None = None,
|
||||
force: bool = False,
|
||||
at_time: datetime.datetime | None = None,
|
||||
) -> None:
|
||||
if (lock := self.manager.turn_off_locks.get(light)) and lock.locked():
|
||||
_LOGGER.debug("%s: '%s' is locked", self._name, light)
|
||||
|
|
@ -1433,6 +1472,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
prefer_rgb_color,
|
||||
force,
|
||||
context,
|
||||
at_time=at_time,
|
||||
)
|
||||
if data is None:
|
||||
return # nothing to adapt
|
||||
|
|
|
|||
|
|
@ -144,6 +144,10 @@
|
|||
"turn_on_lights": {
|
||||
"description": "Whether to turn on lights that are currently off. 🔆",
|
||||
"name": "turn_on_lights"
|
||||
},
|
||||
"time": {
|
||||
"description": "Manually apply the settings for this time of day (HH:MM:SS) instead of now. 🕰️",
|
||||
"name": "time"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ Provide a switch in `entity_id`, a list of `lights`, or both.
|
|||
| `adapt_color` | Whether to adapt the color on supporting lights. 🌈 | ❌ | bool |
|
||||
| `prefer_rgb_color` | Whether to prefer RGB color adjustment over light color temperature when possible. 🌈 | ❌ | bool |
|
||||
| `turn_on_lights` | Whether to turn on lights that are currently off. 🔆 | ❌ | bool |
|
||||
| `time` | Manually apply the settings for this time of day (HH:MM:SS) instead of now. 🕰️ | ❌ | time (HH:MM:SS) |
|
||||
|
||||
<!-- OUTPUT:END -->
|
||||
|
||||
|
|
@ -54,6 +55,19 @@ data:
|
|||
adapt_color: true
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Set the lights to this evening's 22:00 settings and hold them there
|
||||
service: adaptive_lighting.apply
|
||||
data:
|
||||
entity_id: switch.adaptive_lighting_living_room
|
||||
lights:
|
||||
- light.sofa
|
||||
- light.floor_lamp
|
||||
time: "22:00:00"
|
||||
```
|
||||
|
||||
With `time`, the lights get the values Adaptive Lighting would use at that time today. The adapted attributes are then marked as manually controlled, exactly as if you had changed them yourself: the regular adaptation leaves them alone until manual control is reset (for example by `autoreset_control_seconds` or `adaptive_lighting.set_manual_control`), and an `adaptive_lighting.manual_control` event is fired. When sleep mode is on, the sleep settings are applied instead, whatever the time.
|
||||
|
||||
---
|
||||
|
||||
## adaptive_lighting.set_manual_control
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ from homeassistant.components.adaptive_lighting.const import (
|
|||
CONF_ADAPT_DELAY,
|
||||
CONF_ADAPT_ONLY_ON_BARE_TURN_ON,
|
||||
CONF_ADAPT_UNTIL_SLEEP,
|
||||
CONF_APPLY_TIME,
|
||||
CONF_AUTORESET_CONTROL,
|
||||
CONF_BRIGHTNESS_MODE,
|
||||
CONF_BRIGHTNESS_MODE_TIME_DARK,
|
||||
|
|
@ -2186,6 +2187,169 @@ async def test_apply_service_uses_each_switch_transition(hass):
|
|||
assert adapt_2.await_args.kwargs["transition"] == 0
|
||||
|
||||
|
||||
async def test_apply_service_at_time(hass):
|
||||
"""Apply with 'time' adapts as if it were that time and marks manual control."""
|
||||
switch, _ = await setup_lights_and_switch(hass, {CONF_MIN_BRIGHTNESS: 10})
|
||||
noon = SUNRISE.replace(hour=12, tzinfo=dt_util.DEFAULT_TIME_ZONE).astimezone(
|
||||
dt_util.UTC,
|
||||
)
|
||||
events = []
|
||||
hass.bus.async_listen(f"{DOMAIN}.manual_control", events.append)
|
||||
|
||||
def light_attributes():
|
||||
return hass.states.get(ENTITY_LIGHT_1).attributes
|
||||
|
||||
async def interval_update(**kwargs):
|
||||
await switch._update_attrs_and_maybe_adapt_lights(
|
||||
context=switch.create_context("interval"),
|
||||
transition=0,
|
||||
**kwargs,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.adaptive_lighting.color_and_brightness.utcnow",
|
||||
return_value=noon,
|
||||
):
|
||||
await interval_update(force=True)
|
||||
assert light_attributes()[ATTR_BRIGHTNESS] == 255
|
||||
noon_color_temp = light_attributes()[ATTR_COLOR_TEMP_KELVIN]
|
||||
settings = deepcopy(switch._settings)
|
||||
|
||||
# After sunset: minimum brightness and the warmest color
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_APPLY,
|
||||
{
|
||||
ATTR_ENTITY_ID: switch.entity_id,
|
||||
CONF_LIGHTS: [ENTITY_LIGHT_1],
|
||||
CONF_APPLY_TIME: "23:30:00",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
sun_light_settings = switch._sun_light_settings
|
||||
tz = sun_light_settings.timezone
|
||||
at_time = datetime.datetime.combine(
|
||||
dt_util.now(tz).date(),
|
||||
datetime.time(23, 30),
|
||||
tzinfo=tz,
|
||||
)
|
||||
expected = sun_light_settings.brightness_and_color(at_time, is_sleep=False)
|
||||
applied_brightness = round(255 * expected[ATTR_BRIGHTNESS_PCT] / 100)
|
||||
assert applied_brightness < 255
|
||||
assert light_attributes()[ATTR_BRIGHTNESS] == applied_brightness
|
||||
assert light_attributes()[ATTR_COLOR_TEMP_KELVIN] < noon_color_temp
|
||||
# The switch keeps reporting the settings for the current time
|
||||
assert switch._settings == settings
|
||||
assert (
|
||||
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
|
||||
== LightControlAttributes.ALL
|
||||
)
|
||||
assert [event.data[ATTR_ENTITY_ID] for event in events] == [ENTITY_LIGHT_1]
|
||||
|
||||
# The regular adaptation leaves the light alone and does not mistake the
|
||||
# applied values for another manual change.
|
||||
await interval_update()
|
||||
assert light_attributes()[ATTR_BRIGHTNESS] == applied_brightness
|
||||
assert len(events) == 1
|
||||
|
||||
# Resetting manual control resumes regular adaptation
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_MANUAL_CONTROL,
|
||||
{
|
||||
ATTR_ENTITY_ID: switch.entity_id,
|
||||
CONF_LIGHTS: [ENTITY_LIGHT_1],
|
||||
CONF_MANUAL_CONTROL: False,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert light_attributes()[ATTR_BRIGHTNESS] == 255
|
||||
assert light_attributes()[ATTR_COLOR_TEMP_KELVIN] == noon_color_temp
|
||||
|
||||
|
||||
async def test_apply_service_at_time_marks_manual_before_adapting(hass):
|
||||
"""No regular adaptation can slip in between applying and marking manual."""
|
||||
switch, _ = await setup_lights_and_switch(hass)
|
||||
manual_during_adaptation = []
|
||||
adapt_light = switch._adapt_light
|
||||
|
||||
async def record_and_adapt(light, *args, **kwargs):
|
||||
manual_during_adaptation.append(
|
||||
switch.manager.get_manual_control_attributes(light),
|
||||
)
|
||||
await adapt_light(light, *args, **kwargs)
|
||||
|
||||
with patch.object(switch, "_adapt_light", side_effect=record_and_adapt):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_APPLY,
|
||||
{
|
||||
ATTR_ENTITY_ID: switch.entity_id,
|
||||
CONF_LIGHTS: [ENTITY_LIGHT_1],
|
||||
CONF_APPLY_TIME: "22:00",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert manual_during_adaptation == [LightControlAttributes.ALL]
|
||||
|
||||
|
||||
async def test_apply_service_at_time_sleep_mode_and_partial(hass):
|
||||
"""Sleep mode takes precedence, and only adapted attributes become manual."""
|
||||
switch, _ = await setup_lights_and_switch(hass, {CONF_SLEEP_TRANSITION: 0})
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: switch.sleep_mode_switch.entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
sleep_brightness = round(255 * DEFAULT_SLEEP_BRIGHTNESS / 100)
|
||||
assert hass.states.get(ENTITY_LIGHT_1).attributes[ATTR_BRIGHTNESS] == (
|
||||
sleep_brightness
|
||||
)
|
||||
|
||||
# Manual brightness change
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: ENTITY_LIGHT_1, ATTR_BRIGHTNESS: 200},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: ENTITY_LIGHT_2},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_APPLY,
|
||||
{
|
||||
ATTR_ENTITY_ID: switch.entity_id,
|
||||
CONF_LIGHTS: [ENTITY_LIGHT_1, ENTITY_LIGHT_2],
|
||||
CONF_APPLY_TIME: datetime.time(12),
|
||||
ATTR_ADAPT_COLOR: False,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_LIGHT_1).attributes[ATTR_BRIGHTNESS] == (
|
||||
sleep_brightness
|
||||
)
|
||||
assert (
|
||||
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
|
||||
== LightControlAttributes.BRIGHTNESS
|
||||
)
|
||||
# Lights that are off are neither adapted nor marked
|
||||
assert hass.states.get(ENTITY_LIGHT_2).state == STATE_OFF
|
||||
assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_2)
|
||||
|
||||
|
||||
async def test_switch_off_on_off(hass):
|
||||
"""Test switch rapid off_on_off."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue