Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 93 additions & 15 deletions custom_components/adaptive_lighting/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from homeassistant.core import callback

from .const import ( # pylint: disable=unused-import
BASIC_OPTIONS,
CONF_LIGHTS,
DOMAIN,
EXTRA_VALIDATION,
Expand Down Expand Up @@ -98,50 +99,127 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
# https://github.com/home-assistant/core/pull/129651
else:
self.config_entry = args[0]
self._basic_options: dict[str, Any] = {}
self._all_lights_with_names: dict[str, str] = {}

async def async_step_init(self, user_input: dict[str, Any] | None = None):
"""Handle options flow."""
def _prepare_lights_schema(self) -> tuple[dict[str, Any], dict[str, str]]:
"""Get available lights and build the multi-select schema.

Returns tuple of (to_replace dict for schema, errors dict).
Also populates self._all_lights_with_names.
"""
conf = self.config_entry
data = validate(conf)
if conf.source == config_entries.SOURCE_IMPORT:
return self.async_show_form(step_id="init", data_schema=None)
errors = {}
if user_input is not None:
validate_options(user_input, errors)
if not errors:
return self.async_create_entry(title="", data=user_input)
errors: dict[str, str] = {}

all_lights_with_names = {
self._all_lights_with_names = {
light: get_friendly_name(self.hass, light)
for light in self.hass.states.async_entity_ids("light")
if _supported_features(self.hass, light)
}
all_lights = list(all_lights_with_names.keys())
all_lights = list(self._all_lights_with_names.keys())

for configured_light in data[CONF_LIGHTS]:
if configured_light not in all_lights:
errors = {CONF_LIGHTS: "entity_missing"}
errors[CONF_LIGHTS] = "entity_missing"
_LOGGER.error(
"%s: light entity %s is configured, but was not found",
data[CONF_NAME],
configured_light,
)
all_lights.append(configured_light)
all_lights_with_names[configured_light] = configured_light
self._all_lights_with_names[configured_light] = configured_light

light_options = {
entity_id: f"{name} ({entity_id})"
for entity_id, name in all_lights_with_names.items()
for entity_id, name in self._all_lights_with_names.items()
}
to_replace = {CONF_LIGHTS: cv.multi_select(light_options)}
return to_replace, errors

def _build_options_schema(
self,
to_replace: dict[str, Any],
include: set[str] | None = None,
exclude: set[str] | None = None,
) -> vol.Schema:
"""Build schema for specified options.

Args:
to_replace: Dict of option names to replacement validators.
include: If provided, only include these option names.
exclude: If provided, exclude these option names.

"""
conf = self.config_entry

options_schema = {}
for name, default, validation in VALIDATION_TUPLES:
# Filter based on include/exclude
if include is not None and name not in include:
continue
if exclude is not None and name in exclude:
continue

key = vol.Optional(name, default=conf.options.get(name, default))
value = to_replace.get(name, validation)
options_schema[key] = value

return vol.Schema(options_schema)

async def async_step_init(self, user_input: dict[str, Any] | None = None):
"""Handle options flow - Step 1: Basic options."""
conf = self.config_entry
if conf.source == config_entries.SOURCE_IMPORT:
return self.async_show_form(step_id="init", data_schema=None)

to_replace, errors = self._prepare_lights_schema()

if user_input is not None:
validate_options(user_input, errors)
if not errors:
self._basic_options = user_input
# Show menu to choose between advanced options or finish
return self.async_show_menu(
step_id="init",
menu_options=["advanced", "finish"],
)

basic_schema = self._build_options_schema(to_replace, include=BASIC_OPTIONS)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(options_schema),
data_schema=basic_schema,
errors=errors,
)

async def async_step_advanced(self, user_input: dict[str, Any] | None = None):
"""Handle options flow - Step 2: Advanced options (optional)."""
errors: dict[str, str] = {}

if user_input is not None:
# Merge basic + advanced options
combined = {**self._basic_options, **user_input}
validate_options(combined, errors)
if not errors:
return self.async_create_entry(title="", data=combined)

# Need to_replace for building schema (though advanced step doesn't use lights)
to_replace, _ = self._prepare_lights_schema()
advanced_schema = self._build_options_schema(to_replace, exclude=BASIC_OPTIONS)
return self.async_show_form(
step_id="advanced",
data_schema=advanced_schema,
errors=errors,
)

async def async_step_finish(
self,
user_input: dict[str, Any] | None = None, # noqa: ARG002
):
"""Finish configuration without advanced options."""
errors: dict[str, str] = {}
validate_options(self._basic_options, errors)
if not errors:
return self.async_create_entry(title="", data=self._basic_options)
# If validation fails, go back to init
return await self.async_step_init()
14 changes: 14 additions & 0 deletions custom_components/adaptive_lighting/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,20 @@

TURNING_OFF_DELAY = 5

# Basic options shown in the first step of the options flow
# These are the most commonly used options that most users will want to configure
BASIC_OPTIONS: set[str] = {
CONF_LIGHTS,
CONF_MIN_BRIGHTNESS,
CONF_MAX_BRIGHTNESS,
CONF_MIN_COLOR_TEMP,
CONF_MAX_COLOR_TEMP,
CONF_SLEEP_BRIGHTNESS,
CONF_SLEEP_COLOR_TEMP,
CONF_TRANSITION,
CONF_INTERVAL,
}

DOCS_MANUAL_CONTROL = {
CONF_ENTITY_ID: "The `entity_id` of the switch in which to (un)mark the "
"light as being `manually controlled`. 📝",
Expand Down
63 changes: 61 additions & 2 deletions custom_components/adaptive_lighting/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@
"options": {
"step": {
"init": {
"title": "Adaptive Lighting options",
"description": "Configure an Adaptive Lighting component. Option names align with the YAML settings. If you've defined this entry in YAML, no options will appear here. For interactive graphs that demonstrate parameter effects, visit [this web app](https://basnijholt.github.io/adaptive-lighting). For further details, see the [official documentation](https://github.com/basnijholt/adaptive-lighting#readme).",
"title": "Basic Settings",
"description": "Configure the most common Adaptive Lighting options. After submitting, you can optionally configure advanced settings or save directly. If you've defined this entry in YAML, no options will appear here. For interactive graphs, visit [this web app](https://basnijholt.github.io/adaptive-lighting).",
"menu_options": {
"advanced": "Configure advanced options",
"finish": "Save and finish"
},
"data": {
"lights": "lights: List of light entity_ids to be controlled (may be empty). 🌟",
"interval": "interval",
Expand Down Expand Up @@ -82,6 +86,61 @@
"send_split_delay": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️",
"adapt_delay": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️"
}
},
"advanced": {
"title": "Advanced Settings",
"description": "Configure advanced Adaptive Lighting options. Most users don't need to change these settings.",
"data": {
"initial_transition": "initial_transition",
"prefer_rgb_color": "prefer_rgb_color: Whether to prefer RGB color adjustment over light color temperature when possible. 🌈",
"sleep_rgb_or_color_temp": "sleep_rgb_or_color_temp",
"sleep_rgb_color": "sleep_rgb_color",
"sleep_transition": "sleep_transition",
"transition_until_sleep": "transition_until_sleep: When enabled, Adaptive Lighting will treat sleep settings as the minimum, transitioning to these values after sunset. 🌙",
"sunrise_time": "sunrise_time",
"min_sunrise_time": "min_sunrise_time",
"max_sunrise_time": "max_sunrise_time",
"sunrise_offset": "sunrise_offset",
"sunset_time": "sunset_time",
"min_sunset_time": "min_sunset_time",
"max_sunset_time": "max_sunset_time",
"sunset_offset": "sunset_offset",
"brightness_mode": "brightness_mode",
"brightness_mode_time_dark": "brightness_mode_time_dark",
"brightness_mode_time_light": "brightness_mode_time_light",
"take_over_control": "take_over_control: 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`! 🔒",
"detect_non_ha_changes": "detect_non_ha_changes: Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. 🕵️ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Disable this feature if you encounter such issues.",
"autoreset_control_seconds": "autoreset_control_seconds",
"only_once": "only_once: Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄",
"adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: When turning lights on initially. If set to `true`, AL adapts only if `light.turn_on` is invoked without specifying color or brightness. ❌🌈 This e.g., prevents adaptation when activating a scene. If `false`, AL adapts regardless of the presence of color or brightness in the initial `service_data`. Needs `take_over_control` enabled. 🕵️",
"separate_turn_on_commands": "separate_turn_on_commands: Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀",
"send_split_delay": "send_split_delay",
"adapt_delay": "adapt_delay",
"skip_redundant_commands": "skip_redundant_commands: Skip sending adaptation commands whose target state already equals the light's known state. Minimizes network traffic and improves the adaptation responsivity in some situations. 📉Disable if physical light states get out of sync with HA's recorded state.",
"intercept": "intercept: Intercept and adapt `light.turn_on` calls to enabling instantaneous color and brightness adaptation. 🏎️ Disable for lights that do not support `light.turn_on` with color and brightness.",
"multi_light_intercept": "multi_light_intercept: Intercept and adapt `light.turn_on` calls that target multiple lights. ➗⚠️ This might result in splitting up a single `light.turn_on` call into multiple calls, e.g., when lights are in different switches. Requires `intercept` to be enabled.",
"include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝"
},
"data_description": {
"initial_transition": "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️",
"sleep_rgb_or_color_temp": "Use either `\"rgb_color\"` or `\"color_temp\"` in sleep mode. 🌙",
"sleep_rgb_color": "RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is \"rgb_color\"). 🌈",
"sleep_transition": "Duration of transition when \"sleep mode\" is toggled in seconds. 😴",
"sunrise_time": "Set a fixed time (HH:MM:SS) for sunrise. 🌅",
"min_sunrise_time": "Set the earliest virtual sunrise time (HH:MM:SS), allowing for later sunrises. 🌅",
"max_sunrise_time": "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier sunrises. 🌅",
"sunrise_offset": "Adjust sunrise time with a positive or negative offset in seconds. ⏰",
"sunset_time": "Set a fixed time (HH:MM:SS) for sunset. 🌇",
"min_sunset_time": "Set the earliest virtual sunset time (HH:MM:SS), allowing for later sunsets. 🌇",
"max_sunset_time": "Set the latest virtual sunset time (HH:MM:SS), allowing for earlier sunsets. 🌇",
"sunset_offset": "Adjust sunset time with a positive or negative offset in seconds. ⏰",
"brightness_mode": "Brightness mode to use. Possible values are `default`, `linear`, and `tanh` (uses `brightness_mode_time_dark` and `brightness_mode_time_light`). 📈",
"brightness_mode_time_dark": "(Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness before/after sunrise/sunset. 📈📉",
"brightness_mode_time_light": "(Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness after/before sunrise/sunset. 📈📉.",
"autoreset_control_seconds": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️",
"send_split_delay": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️",
"adapt_delay": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️"
}
}
},
"error": {
Expand Down
10 changes: 10 additions & 0 deletions custom_components/adaptive_lighting/translations/af.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@
"step": {
"init": {
"title": "Aanpasbare beligting opsies",
"data": {},
"data_description": {},
"menu_options": {
"advanced": "Configure advanced options",
"finish": "Save and finish"
}
},
"advanced": {
"title": "Advanced Settings",
"description": "Configure advanced Adaptive Lighting options. Most users don't need to change these settings.",
"data": {
"adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: Wanneer ligte aanvanklik aangeskakel word. As dit op \"true\" gestel is, pas AL slegs aan as \"light.turn_on\" opgeroep word sonder om kleur of helderheid te spesifiseer. ❌🌈 Dit verhoed bv. aanpassing wanneer 'n toneel geaktiveer word. As `onwaar`, pas AL aan ongeag die teenwoordigheid van kleur of helderheid in die aanvanklike `diens_data`. Moet `oorname_beheer` geaktiveer moet word. 🕵️ "
},
Expand Down
26 changes: 19 additions & 7 deletions custom_components/adaptive_lighting/translations/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,31 @@
"lights": "lights: Списък от entity_ids на лампи за контрол (може да е празен). 🌟",
"interval": "интервал",
"transition": "преход",
"initial_transition": "начален преход",
"min_brightness": "min_brightness: Минимален процент на яркост. 💡",
"max_brightness": "max_brightness: Максимален процент на яркост. 💡",
"min_color_temp": "min_color_temp: Най-топла цветова температура в Келвини. 🔥",
"max_color_temp": "max_color_temp: Най-студена цветова температура в Келвини. ❄️",
"prefer_rgb_color": "prefer_rgb_color: Дали да се предпочита RGB цветова корекция пред температура на светлината, когато е възможно. 🌈",
"sleep_brightness": "яркост при сън",
"sleep_color_temp": "цветова температура при сън"
},
"data_description": {
"interval": "Честота за адаптиране на лампите, в секунди. 🔄",
"transition": "Продължителност на прехода, когато лампите се променят, в секунди. 🕑",
"sleep_brightness": "Процент на яркостта на лампите в режим на сън. 😴",
"sleep_color_temp": "Цветова температура в режим на сън (използва се, когато \"sleep_rgb_or_color_temp\" е \"color_temp\") в Келвин. 😴"
},
"menu_options": {
"advanced": "Configure advanced options",
"finish": "Save and finish"
}
},
"advanced": {
"title": "Advanced Settings",
"description": "Configure advanced Adaptive Lighting options. Most users don't need to change these settings.",
"data": {
"initial_transition": "начален преход",
"prefer_rgb_color": "prefer_rgb_color: Дали да се предпочита RGB цветова корекция пред температура на светлината, когато е възможно. 🌈",
"sleep_rgb_or_color_temp": "RGB или цветова температура при сън",
"sleep_color_temp": "цветова температура при сън",
"sleep_rgb_color": "RGB цвят при сън",
"sleep_transition": "преход при сън",
"transition_until_sleep": "transition_until_sleep: Когато е активирано, Adaptive Lighting ще третира настройките за сън като минимум, преминавайки към тези стойности след залез. 🌙",
Expand Down Expand Up @@ -60,12 +76,8 @@
"include_config_in_attributes": "include_config_in_attributes: Показва всички опции като атрибути на ключа в Home Assistant, когато е зададено на \"true\". 📝"
},
"data_description": {
"interval": "Честота за адаптиране на лампите, в секунди. 🔄",
"transition": "Продължителност на прехода, когато лампите се променят, в секунди. 🕑",
"initial_transition": "Продължителност на първия преход, когато лампите преминават от \"off\" на \"on\" в секунди. ⏲️",
"sleep_brightness": "Процент на яркостта на лампите в режим на сън. 😴",
"sleep_rgb_or_color_temp": "Използвайте или \"\"rgb_color\"\" или \"\"color_temp\"\" в режим на сън. 🌙",
"sleep_color_temp": "Цветова температура в режим на сън (използва се, когато \"sleep_rgb_or_color_temp\" е \"color_temp\") в Келвин. 😴",
"sleep_rgb_color": "RGB цвят в режим на сън (използва се, когато \"sleep_rgb_or_color_temp\" е \"rgb_color\"). 🌈",
"sleep_transition": "Продължителност на прехода, когато се превключва \"режим на сън\" в секунди. 😴",
"sunrise_time": "Задайте фиксирано време (HH:MM:SS) за изгрев. 🌅",
Expand Down
Loading
Loading