mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-17 01:04:05 +02:00
Cleanup after add_to_platform_abort is called, try-except intercept, update strings.json, fixes #658 (#659)
* Cleanup after add_to_platform_abort is called, fixes #658 * Add note * try-catch because of priv * Catch all exceptions * Bump to 1.17.3 in manifest.json * assert that hass is not None * Rename _light_event_action -> _light_state_event_action * update .github/update-strings.py * Update README.md, strings.json, and services.yaml * update name * Fixes * Update README.md, strings.json, and services.yaml * Add name * Do not add name --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
8696092879
commit
e9297d562f
7 changed files with 447 additions and 51 deletions
24
.github/update-strings.py
vendored
24
.github/update-strings.py
vendored
|
|
@ -3,6 +3,8 @@ import json
|
|||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
|
||||
from custom_components.adaptive_lighting import const # noqa: E402
|
||||
|
|
@ -13,20 +15,40 @@ en_fname = folder / "translations" / "en.json"
|
|||
with strings_fname.open() as f:
|
||||
strings = json.load(f)
|
||||
|
||||
# Set "options"
|
||||
data = {k: f"{k}: {const.DOCS[k]}" for k, _, _ in const.VALIDATION_TUPLES}
|
||||
strings["options"]["step"]["init"]["data"] = data
|
||||
|
||||
# Set "services"
|
||||
services_filename = Path("custom_components") / "adaptive_lighting" / "services.yaml"
|
||||
with open(services_filename) as f: # noqa: PTH123
|
||||
services = yaml.safe_load(f)
|
||||
services_json = {}
|
||||
for service_name, dct in services.items():
|
||||
services_json[service_name] = {
|
||||
"name": service_name,
|
||||
"description": dct["description"],
|
||||
"fields": {},
|
||||
}
|
||||
for field_name, field in dct["fields"].items():
|
||||
services_json[service_name]["fields"][field_name] = {
|
||||
"description": field["description"],
|
||||
"name": field_name,
|
||||
}
|
||||
strings["services"] = services_json
|
||||
|
||||
# Write changes to strings.json
|
||||
with strings_fname.open("w") as f:
|
||||
json.dump(strings, f, indent=2, ensure_ascii=False)
|
||||
f.write("\n")
|
||||
|
||||
|
||||
# Sync changes from strings.json to en.json
|
||||
with en_fname.open() as f:
|
||||
en = json.load(f)
|
||||
|
||||
en["config"]["step"]["user"] = strings["config"]["step"]["user"]
|
||||
en["options"]["step"]["init"]["data"] = data
|
||||
en["services"] = services_json
|
||||
|
||||
with en_fname.open("w") as f:
|
||||
json.dump(en, f, indent=2, ensure_ascii=False)
|
||||
|
|
|
|||
8
.github/workflows/update-readme.yml
vendored
8
.github/workflows/update-readme.yml
vendored
|
|
@ -1,4 +1,4 @@
|
|||
name: Update README.md
|
||||
name: Update README.md, strings.json, and services.yaml
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -34,12 +34,12 @@ jobs:
|
|||
- name: Run markdown-code-runner
|
||||
run: markdown-code-runner --debug README.md
|
||||
|
||||
- name: Run update strings.json
|
||||
run: python .github/update-strings.py
|
||||
|
||||
- name: Run update services.yaml
|
||||
run: python .github/update-services.py
|
||||
|
||||
- name: Run update strings.json
|
||||
run: python .github/update-strings.py
|
||||
|
||||
- name: Commit updated README.md, strings.json, and services.yaml
|
||||
id: commit
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Utility functions for HA core."""
|
||||
import logging
|
||||
from collections.abc import Awaitable, Callable
|
||||
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
|
|
@ -6,6 +7,8 @@ from homeassistant.util.read_only_dict import ReadOnlyDict
|
|||
|
||||
from .adaptation_utils import ServiceData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_service_call_interceptor(
|
||||
hass: HomeAssistant,
|
||||
|
|
@ -39,15 +42,18 @@ def setup_service_call_interceptor(
|
|||
existing_service = registered_services[domain][service]
|
||||
|
||||
async def service_func_proxy(call: ServiceCall) -> None:
|
||||
# Convert read-only data to writeable dictionary for modification by interceptor
|
||||
data = dict(call.data)
|
||||
try:
|
||||
# Convert read-only data to writeable dictionary for modification by interceptor
|
||||
data = dict(call.data)
|
||||
|
||||
# Call interceptor
|
||||
await intercept_func(call, data)
|
||||
|
||||
# Convert data back to read-only
|
||||
call.data = ReadOnlyDict(data)
|
||||
# Call interceptor
|
||||
await intercept_func(call, data)
|
||||
|
||||
# Convert data back to read-only
|
||||
call.data = ReadOnlyDict(data)
|
||||
except Exception as e: # noqa: BLE001
|
||||
# Blindly catch all exceptions to avoid breaking light.turn_on
|
||||
_LOGGER.error("Error in service_func_proxy: %s", e)
|
||||
# Call original service handler with processed data
|
||||
await existing_service.job.target(call)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@
|
|||
"iot_class": "calculated",
|
||||
"issue_tracker": "https://github.com/basnijholt/adaptive-lighting/issues",
|
||||
"requirements": ["ulid-transform"],
|
||||
"version": "1.17.2"
|
||||
"version": "1.17.3"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,5 +56,181 @@
|
|||
"option_error": "Invalid option",
|
||||
"entity_missing": "One or more selected light entities are missing from Home Assistant"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"apply": {
|
||||
"name": "apply",
|
||||
"description": "Applies the current Adaptive Lighting settings to lights.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "The `entity_id` of the switch with the settings to apply. 📝",
|
||||
"name": "entity_id"
|
||||
},
|
||||
"lights": {
|
||||
"description": "A light (or list of lights) to apply the settings to. 💡",
|
||||
"name": "lights"
|
||||
},
|
||||
"transition": {
|
||||
"description": "Duration of transition when lights change, in seconds. 🕑",
|
||||
"name": "transition"
|
||||
},
|
||||
"adapt_brightness": {
|
||||
"description": "Whether to adapt the brightness of the light. 🌞",
|
||||
"name": "adapt_brightness"
|
||||
},
|
||||
"adapt_color": {
|
||||
"description": "Whether to adapt the color on supporting lights. 🌈",
|
||||
"name": "adapt_color"
|
||||
},
|
||||
"prefer_rgb_color": {
|
||||
"description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈",
|
||||
"name": "prefer_rgb_color"
|
||||
},
|
||||
"turn_on_lights": {
|
||||
"description": "Whether to turn on lights that are currently off. 🔆",
|
||||
"name": "turn_on_lights"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_manual_control": {
|
||||
"name": "set_manual_control",
|
||||
"description": "Mark whether a light is 'manually controlled'.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "The `entity_id` of the switch in which to (un)mark the light as being `manually controlled`. 📝",
|
||||
"name": "entity_id"
|
||||
},
|
||||
"lights": {
|
||||
"description": "entity_id(s) of lights, if not specified, all lights in the switch are selected. 💡",
|
||||
"name": "lights"
|
||||
},
|
||||
"manual_control": {
|
||||
"description": "Whether to add (\"true\") or remove (\"false\") the light from the \"manual_control\" list. 🔒",
|
||||
"name": "manual_control"
|
||||
}
|
||||
}
|
||||
},
|
||||
"change_switch_settings": {
|
||||
"name": "change_switch_settings",
|
||||
"description": "Change any settings you'd like in the switch. All options here are the same as in the config flow.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "Entity ID of the switch. 📝",
|
||||
"name": "entity_id"
|
||||
},
|
||||
"use_defaults": {
|
||||
"description": "Sets the default values not specified in this service call. Options: \"current\" (default, retains current values), \"factory\" (resets to documented defaults), or \"configuration\" (reverts to switch config defaults). ⚙️",
|
||||
"name": "use_defaults"
|
||||
},
|
||||
"include_config_in_attributes": {
|
||||
"description": "Show all options as attributes on the switch in Home Assistant when set to `true`. 📝",
|
||||
"name": "include_config_in_attributes"
|
||||
},
|
||||
"turn_on_lights": {
|
||||
"description": "Whether to turn on lights that are currently off. 🔆",
|
||||
"name": "turn_on_lights"
|
||||
},
|
||||
"initial_transition": {
|
||||
"description": "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️",
|
||||
"name": "initial_transition"
|
||||
},
|
||||
"sleep_transition": {
|
||||
"description": "Duration of transition when \"sleep mode\" is toggled in seconds. 😴",
|
||||
"name": "sleep_transition"
|
||||
},
|
||||
"max_brightness": {
|
||||
"description": "Maximum brightness percentage. 💡",
|
||||
"name": "max_brightness"
|
||||
},
|
||||
"max_color_temp": {
|
||||
"description": "Coldest color temperature in Kelvin. ❄️",
|
||||
"name": "max_color_temp"
|
||||
},
|
||||
"min_brightness": {
|
||||
"description": "Minimum brightness percentage. 💡",
|
||||
"name": "min_brightness"
|
||||
},
|
||||
"min_color_temp": {
|
||||
"description": "Warmest color temperature in Kelvin. 🔥",
|
||||
"name": "min_color_temp"
|
||||
},
|
||||
"only_once": {
|
||||
"description": "Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄",
|
||||
"name": "only_once"
|
||||
},
|
||||
"prefer_rgb_color": {
|
||||
"description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈",
|
||||
"name": "prefer_rgb_color"
|
||||
},
|
||||
"separate_turn_on_commands": {
|
||||
"description": "Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀",
|
||||
"name": "separate_turn_on_commands"
|
||||
},
|
||||
"send_split_delay": {
|
||||
"description": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️",
|
||||
"name": "send_split_delay"
|
||||
},
|
||||
"sleep_brightness": {
|
||||
"description": "Brightness percentage of lights in sleep mode. 😴",
|
||||
"name": "sleep_brightness"
|
||||
},
|
||||
"sleep_rgb_or_color_temp": {
|
||||
"description": "Use either `\"rgb_color\"` or `\"color_temp\"` in sleep mode. 🌙",
|
||||
"name": "sleep_rgb_or_color_temp"
|
||||
},
|
||||
"sleep_rgb_color": {
|
||||
"description": "RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is \"rgb_color\"). 🌈",
|
||||
"name": "sleep_rgb_color"
|
||||
},
|
||||
"sleep_color_temp": {
|
||||
"description": "Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴",
|
||||
"name": "sleep_color_temp"
|
||||
},
|
||||
"sunrise_offset": {
|
||||
"description": "Adjust sunrise time with a positive or negative offset in seconds. ⏰",
|
||||
"name": "sunrise_offset"
|
||||
},
|
||||
"sunrise_time": {
|
||||
"description": "Set a fixed time (HH:MM:SS) for sunrise. 🌅",
|
||||
"name": "sunrise_time"
|
||||
},
|
||||
"sunset_offset": {
|
||||
"description": "Adjust sunset time with a positive or negative offset in seconds. ⏰",
|
||||
"name": "sunset_offset"
|
||||
},
|
||||
"sunset_time": {
|
||||
"description": "Set a fixed time (HH:MM:SS) for sunset. 🌇",
|
||||
"name": "sunset_time"
|
||||
},
|
||||
"max_sunrise_time": {
|
||||
"description": "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier real sunrises. 🌅",
|
||||
"name": "max_sunrise_time"
|
||||
},
|
||||
"min_sunset_time": {
|
||||
"description": "Set the earliest virtual sunset time (HH:MM:SS), allowing for later real sunsets. 🌇",
|
||||
"name": "min_sunset_time"
|
||||
},
|
||||
"take_over_control": {
|
||||
"description": "Disable Adaptive Lighting if another source calls `light.turn_on` while lights are on and being adapted. Note that this calls `homeassistant.update_entity` every `interval`! 🔒",
|
||||
"name": "take_over_control"
|
||||
},
|
||||
"detect_non_ha_changes": {
|
||||
"description": "Detect non-`light.turn_on` state changes and stop adapting lights. Requires `take_over_control`. 🕵️",
|
||||
"name": "detect_non_ha_changes"
|
||||
},
|
||||
"transition": {
|
||||
"description": "Duration of transition when lights change, in seconds. 🕑",
|
||||
"name": "transition"
|
||||
},
|
||||
"adapt_delay": {
|
||||
"description": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️",
|
||||
"name": "adapt_delay"
|
||||
},
|
||||
"autoreset_control_seconds": {
|
||||
"description": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️",
|
||||
"name": "autoreset_control_seconds"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -437,15 +437,13 @@ async def async_setup_entry( # noqa: PLR0915
|
|||
async_add_entities: AddEntitiesCallback,
|
||||
):
|
||||
"""Set up the AdaptiveLighting switch."""
|
||||
assert hass is not None
|
||||
data = hass.data[DOMAIN]
|
||||
assert config_entry.entry_id in data
|
||||
|
||||
if ATTR_ADAPTIVE_LIGHTING_MANAGER not in data:
|
||||
data[ATTR_ADAPTIVE_LIGHTING_MANAGER] = AdaptiveLightingManager(
|
||||
hass,
|
||||
config_entry,
|
||||
)
|
||||
manager: AdaptiveLightingManager = data[ATTR_ADAPTIVE_LIGHTING_MANAGER]
|
||||
manager = data.setdefault(
|
||||
ATTR_ADAPTIVE_LIGHTING_MANAGER,
|
||||
AdaptiveLightingManager(hass, config_entry),
|
||||
)
|
||||
sleep_mode_switch = SimpleSwitch(
|
||||
which="Sleep Mode",
|
||||
initial_state=False,
|
||||
|
|
@ -503,7 +501,7 @@ async def async_setup_entry( # noqa: PLR0915
|
|||
if not lights:
|
||||
all_lights = switch.lights
|
||||
else:
|
||||
all_lights = _expand_light_groups(switch.hass, lights)
|
||||
all_lights = _expand_light_groups(hass, lights)
|
||||
switch.manager.lights.update(all_lights)
|
||||
for light in all_lights:
|
||||
if data[CONF_TURN_ON_LIGHTS] or is_on(hass, light):
|
||||
|
|
@ -533,7 +531,7 @@ async def async_setup_entry( # noqa: PLR0915
|
|||
if not lights:
|
||||
all_lights = switch.lights
|
||||
else:
|
||||
all_lights = _expand_light_groups(switch.hass, lights)
|
||||
all_lights = _expand_light_groups(hass, lights)
|
||||
if service_call.data[CONF_MANUAL_CONTROL]:
|
||||
for light in all_lights:
|
||||
_fire_manual_control_event(switch, light, service_call.context)
|
||||
|
|
@ -585,7 +583,7 @@ def validate(
|
|||
config_entry: ConfigEntry,
|
||||
service_data: dict[str, Any] | None = None,
|
||||
defaults: dict[str, Any] | None = None,
|
||||
):
|
||||
) -> dict[str, Any]:
|
||||
"""Get the options and data from the config_entry and add defaults."""
|
||||
if defaults is None:
|
||||
data = {key: default for key, default, _ in VALIDATION_TUPLES}
|
||||
|
|
@ -812,6 +810,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
) -> None:
|
||||
"""Initialize the Adaptive Lighting switch."""
|
||||
# Set attributes that can't be modified during runtime
|
||||
assert hass is not None
|
||||
self.hass = hass
|
||||
self.manager = manager
|
||||
self.sleep_mode_switch = sleep_mode_switch
|
||||
|
|
@ -847,7 +846,6 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
# Set and unset tracker in async_turn_on and async_turn_off
|
||||
self.remove_listeners = []
|
||||
self.remove_interval: Callable[[], None] = lambda: None
|
||||
|
||||
_LOGGER.debug(
|
||||
"%s: Setting up with '%s',"
|
||||
" config_entry.data: '%s',"
|
||||
|
|
@ -1005,7 +1003,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
remove_state = async_track_state_change_event(
|
||||
self.hass,
|
||||
entity_ids=self.lights,
|
||||
action=self._light_event_action,
|
||||
action=self._light_state_event_action,
|
||||
)
|
||||
self.remove_listeners.append(remove_state)
|
||||
|
||||
|
|
@ -1035,8 +1033,27 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
interval=adaptation_interval,
|
||||
)
|
||||
|
||||
def _call_on_remove_callbacks(self) -> None:
|
||||
"""Call callbacks registered by async_on_remove."""
|
||||
# This is called when the integration is removed from HA
|
||||
# and in `Entity.add_to_platform_abort`.
|
||||
# For some unknown reason (to me) `async_will_remove_from_hass`
|
||||
# is not called in `add_to_platform_abort`.
|
||||
# See https://github.com/basnijholt/adaptive-lighting/issues/658
|
||||
self._remove_listeners()
|
||||
try:
|
||||
# HACK: this is a private method in `Entity` which can change
|
||||
super()._call_on_remove_callbacks()
|
||||
except AttributeError as err:
|
||||
_LOGGER.error(
|
||||
"%s: Caught AttributeError in `_call_on_remove_callbacks`: %s",
|
||||
self._name,
|
||||
err,
|
||||
)
|
||||
|
||||
def _remove_interval_listener(self) -> None:
|
||||
self.remove_interval()
|
||||
self.remove_interval = lambda: None
|
||||
|
||||
def _remove_listeners(self) -> None:
|
||||
self._remove_interval_listener()
|
||||
|
|
@ -1152,7 +1169,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
)
|
||||
|
||||
# Build service data.
|
||||
service_data = {ATTR_ENTITY_ID: light}
|
||||
service_data: dict[str, Any] = {ATTR_ENTITY_ID: light}
|
||||
features = _supported_features(self.hass, light)
|
||||
|
||||
# Check transition == 0 to fix #378
|
||||
|
|
@ -1289,8 +1306,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
to cancel an ongoing adaptation when a light is turned off.
|
||||
"""
|
||||
# Prevent overlap of multiple adaptation sequences
|
||||
listener = self.manager
|
||||
listener.cancel_ongoing_adaptation_calls(data.entity_id, which=data.which)
|
||||
self.manager.cancel_ongoing_adaptation_calls(data.entity_id, which=data.which)
|
||||
_LOGGER.debug(
|
||||
"%s: execute_cancellable_adaptation_calls with data: %s",
|
||||
self._name,
|
||||
|
|
@ -1300,9 +1316,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
try:
|
||||
task = asyncio.ensure_future(self._execute_adaptation_calls(data))
|
||||
if data.which in ("both", "brightness"):
|
||||
listener.adaptation_tasks_brightness[data.entity_id] = task
|
||||
self.manager.adaptation_tasks_brightness[data.entity_id] = task
|
||||
if data.which in ("both", "color"):
|
||||
listener.adaptation_tasks_color[data.entity_id] = task
|
||||
self.manager.adaptation_tasks_color[data.entity_id] = task
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
_LOGGER.debug(
|
||||
|
|
@ -1427,7 +1443,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
context=self.create_context("sleep", parent=event.context),
|
||||
)
|
||||
|
||||
async def _light_event_action(self, event: Event) -> None:
|
||||
async def _light_state_event_action(self, event: Event) -> None:
|
||||
old_state = event.data.get("old_state")
|
||||
new_state = event.data.get("new_state")
|
||||
entity_id = event.data.get("entity_id")
|
||||
|
|
@ -1801,9 +1817,10 @@ class AdaptiveLightingManager:
|
|||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize the AdaptiveLightingManager that is shared among all switches."""
|
||||
assert hass is not None
|
||||
self.hass = hass
|
||||
data = validate(config_entry)
|
||||
self.lights = set()
|
||||
self.lights: set[str] = set()
|
||||
|
||||
# Tracks 'light.turn_off' service calls
|
||||
self.turn_off_event: dict[str, Event] = {}
|
||||
|
|
@ -1935,6 +1952,15 @@ class AdaptiveLightingManager:
|
|||
return
|
||||
|
||||
entity_id = entity_ids[0]
|
||||
|
||||
# Prevent adaptation of TURN_ON calls when light is already on,
|
||||
# and of TOGGLE calls when toggling off.
|
||||
if self.hass.states.is_state(entity_id, STATE_ON):
|
||||
return
|
||||
|
||||
if self.manual_control.get(entity_id, False):
|
||||
return
|
||||
|
||||
try:
|
||||
adaptive_switch = _switch_with_lights(self.hass, [entity_id])
|
||||
except NoSwitchFoundError:
|
||||
|
|
@ -1952,14 +1978,6 @@ class AdaptiveLightingManager:
|
|||
if entity_id not in adaptive_switch.lights:
|
||||
return
|
||||
|
||||
if self.manual_control.get(entity_id, False):
|
||||
return
|
||||
|
||||
# Prevent adaptation of TURN_ON calls when light is already on,
|
||||
# and of TOGGLE calls when toggling off.
|
||||
if self.hass.states.is_state(entity_id, STATE_ON):
|
||||
return
|
||||
|
||||
_LOGGER.debug(
|
||||
"Intercepted TURN_ON call with data %s (%s)",
|
||||
data,
|
||||
|
|
@ -2143,11 +2161,10 @@ class AdaptiveLightingManager:
|
|||
self.cancel_ongoing_adaptation_calls(light)
|
||||
|
||||
def _get_entity_list(self, service_data: ServiceData) -> list[str]:
|
||||
entity_ids = []
|
||||
|
||||
if ATTR_ENTITY_ID in service_data:
|
||||
entity_ids = cv.ensure_list_csv(service_data[ATTR_ENTITY_ID])
|
||||
elif ATTR_AREA_ID in service_data:
|
||||
return cv.ensure_list_csv(service_data[ATTR_ENTITY_ID])
|
||||
if ATTR_AREA_ID in service_data:
|
||||
entity_ids = []
|
||||
area_ids = cv.ensure_list_csv(service_data[ATTR_AREA_ID])
|
||||
for area_id in area_ids:
|
||||
area_entity_ids = area_entities(self.hass, area_id)
|
||||
|
|
@ -2162,13 +2179,12 @@ class AdaptiveLightingManager:
|
|||
entity_ids,
|
||||
area_id,
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"No entity_ids or area_ids found in service_data: %s",
|
||||
service_data,
|
||||
)
|
||||
|
||||
return entity_ids
|
||||
return entity_ids
|
||||
_LOGGER.debug(
|
||||
"No entity_ids or area_ids found in service_data: %s",
|
||||
service_data,
|
||||
)
|
||||
return []
|
||||
|
||||
async def turn_on_off_event_listener(self, event: Event) -> None:
|
||||
"""Track 'light.turn_off' and 'light.turn_on' service calls."""
|
||||
|
|
|
|||
|
|
@ -57,5 +57,181 @@
|
|||
"option_error": "Invalid option",
|
||||
"entity_missing": "One or more selected light entities are missing from Home Assistant"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"apply": {
|
||||
"name": "apply",
|
||||
"description": "Applies the current Adaptive Lighting settings to lights.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "The `entity_id` of the switch with the settings to apply. 📝",
|
||||
"name": "entity_id"
|
||||
},
|
||||
"lights": {
|
||||
"description": "A light (or list of lights) to apply the settings to. 💡",
|
||||
"name": "lights"
|
||||
},
|
||||
"transition": {
|
||||
"description": "Duration of transition when lights change, in seconds. 🕑",
|
||||
"name": "transition"
|
||||
},
|
||||
"adapt_brightness": {
|
||||
"description": "Whether to adapt the brightness of the light. 🌞",
|
||||
"name": "adapt_brightness"
|
||||
},
|
||||
"adapt_color": {
|
||||
"description": "Whether to adapt the color on supporting lights. 🌈",
|
||||
"name": "adapt_color"
|
||||
},
|
||||
"prefer_rgb_color": {
|
||||
"description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈",
|
||||
"name": "prefer_rgb_color"
|
||||
},
|
||||
"turn_on_lights": {
|
||||
"description": "Whether to turn on lights that are currently off. 🔆",
|
||||
"name": "turn_on_lights"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_manual_control": {
|
||||
"name": "set_manual_control",
|
||||
"description": "Mark whether a light is 'manually controlled'.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "The `entity_id` of the switch in which to (un)mark the light as being `manually controlled`. 📝",
|
||||
"name": "entity_id"
|
||||
},
|
||||
"lights": {
|
||||
"description": "entity_id(s) of lights, if not specified, all lights in the switch are selected. 💡",
|
||||
"name": "lights"
|
||||
},
|
||||
"manual_control": {
|
||||
"description": "Whether to add (\"true\") or remove (\"false\") the light from the \"manual_control\" list. 🔒",
|
||||
"name": "manual_control"
|
||||
}
|
||||
}
|
||||
},
|
||||
"change_switch_settings": {
|
||||
"name": "change_switch_settings",
|
||||
"description": "Change any settings you'd like in the switch. All options here are the same as in the config flow.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "Entity ID of the switch. 📝",
|
||||
"name": "entity_id"
|
||||
},
|
||||
"use_defaults": {
|
||||
"description": "Sets the default values not specified in this service call. Options: \"current\" (default, retains current values), \"factory\" (resets to documented defaults), or \"configuration\" (reverts to switch config defaults). ⚙️",
|
||||
"name": "use_defaults"
|
||||
},
|
||||
"include_config_in_attributes": {
|
||||
"description": "Show all options as attributes on the switch in Home Assistant when set to `true`. 📝",
|
||||
"name": "include_config_in_attributes"
|
||||
},
|
||||
"turn_on_lights": {
|
||||
"description": "Whether to turn on lights that are currently off. 🔆",
|
||||
"name": "turn_on_lights"
|
||||
},
|
||||
"initial_transition": {
|
||||
"description": "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️",
|
||||
"name": "initial_transition"
|
||||
},
|
||||
"sleep_transition": {
|
||||
"description": "Duration of transition when \"sleep mode\" is toggled in seconds. 😴",
|
||||
"name": "sleep_transition"
|
||||
},
|
||||
"max_brightness": {
|
||||
"description": "Maximum brightness percentage. 💡",
|
||||
"name": "max_brightness"
|
||||
},
|
||||
"max_color_temp": {
|
||||
"description": "Coldest color temperature in Kelvin. ❄️",
|
||||
"name": "max_color_temp"
|
||||
},
|
||||
"min_brightness": {
|
||||
"description": "Minimum brightness percentage. 💡",
|
||||
"name": "min_brightness"
|
||||
},
|
||||
"min_color_temp": {
|
||||
"description": "Warmest color temperature in Kelvin. 🔥",
|
||||
"name": "min_color_temp"
|
||||
},
|
||||
"only_once": {
|
||||
"description": "Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄",
|
||||
"name": "only_once"
|
||||
},
|
||||
"prefer_rgb_color": {
|
||||
"description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈",
|
||||
"name": "prefer_rgb_color"
|
||||
},
|
||||
"separate_turn_on_commands": {
|
||||
"description": "Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀",
|
||||
"name": "separate_turn_on_commands"
|
||||
},
|
||||
"send_split_delay": {
|
||||
"description": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️",
|
||||
"name": "send_split_delay"
|
||||
},
|
||||
"sleep_brightness": {
|
||||
"description": "Brightness percentage of lights in sleep mode. 😴",
|
||||
"name": "sleep_brightness"
|
||||
},
|
||||
"sleep_rgb_or_color_temp": {
|
||||
"description": "Use either `\"rgb_color\"` or `\"color_temp\"` in sleep mode. 🌙",
|
||||
"name": "sleep_rgb_or_color_temp"
|
||||
},
|
||||
"sleep_rgb_color": {
|
||||
"description": "RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is \"rgb_color\"). 🌈",
|
||||
"name": "sleep_rgb_color"
|
||||
},
|
||||
"sleep_color_temp": {
|
||||
"description": "Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴",
|
||||
"name": "sleep_color_temp"
|
||||
},
|
||||
"sunrise_offset": {
|
||||
"description": "Adjust sunrise time with a positive or negative offset in seconds. ⏰",
|
||||
"name": "sunrise_offset"
|
||||
},
|
||||
"sunrise_time": {
|
||||
"description": "Set a fixed time (HH:MM:SS) for sunrise. 🌅",
|
||||
"name": "sunrise_time"
|
||||
},
|
||||
"sunset_offset": {
|
||||
"description": "Adjust sunset time with a positive or negative offset in seconds. ⏰",
|
||||
"name": "sunset_offset"
|
||||
},
|
||||
"sunset_time": {
|
||||
"description": "Set a fixed time (HH:MM:SS) for sunset. 🌇",
|
||||
"name": "sunset_time"
|
||||
},
|
||||
"max_sunrise_time": {
|
||||
"description": "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier real sunrises. 🌅",
|
||||
"name": "max_sunrise_time"
|
||||
},
|
||||
"min_sunset_time": {
|
||||
"description": "Set the earliest virtual sunset time (HH:MM:SS), allowing for later real sunsets. 🌇",
|
||||
"name": "min_sunset_time"
|
||||
},
|
||||
"take_over_control": {
|
||||
"description": "Disable Adaptive Lighting if another source calls `light.turn_on` while lights are on and being adapted. Note that this calls `homeassistant.update_entity` every `interval`! 🔒",
|
||||
"name": "take_over_control"
|
||||
},
|
||||
"detect_non_ha_changes": {
|
||||
"description": "Detect non-`light.turn_on` state changes and stop adapting lights. Requires `take_over_control`. 🕵️",
|
||||
"name": "detect_non_ha_changes"
|
||||
},
|
||||
"transition": {
|
||||
"description": "Duration of transition when lights change, in seconds. 🕑",
|
||||
"name": "transition"
|
||||
},
|
||||
"adapt_delay": {
|
||||
"description": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️",
|
||||
"name": "adapt_delay"
|
||||
},
|
||||
"autoreset_control_seconds": {
|
||||
"description": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️",
|
||||
"name": "autoreset_control_seconds"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue