diff --git a/.all-contributorsrc b/.all-contributorsrc
index 76a8cf13..d999de5a 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1062,6 +1062,96 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "Tommatheussen",
+ "name": "Tom Matheussen",
+ "avatar_url": "https://avatars.githubusercontent.com/u/13683094?v=4",
+ "profile": "https://github.com/Tommatheussen",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "ams2990",
+ "name": "ams2990",
+ "avatar_url": "https://avatars.githubusercontent.com/u/488907?v=4",
+ "profile": "https://github.com/ams2990",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "DataGhost",
+ "name": "DataGhost",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3911340?v=4",
+ "profile": "https://github.com/DataGhost",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "Wijt",
+ "name": "Furkan Kaya",
+ "avatar_url": "https://avatars.githubusercontent.com/u/23127261?v=4",
+ "profile": "https://iamfurkan.com",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "Rafael4A",
+ "name": "Rafael do Amaral Porciuncula",
+ "avatar_url": "https://avatars.githubusercontent.com/u/32150173?v=4",
+ "profile": "https://github.com/Rafael4A",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "hhjuhl",
+ "name": "hhjuhl",
+ "avatar_url": "https://avatars.githubusercontent.com/u/84127693?v=4",
+ "profile": "https://github.com/hhjuhl",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "Athishbalu",
+ "name": "B.Athish",
+ "avatar_url": "https://avatars.githubusercontent.com/u/177029556?v=4",
+ "profile": "https://github.com/Athishbalu",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "maksim2005UKR",
+ "name": "Горпиніч Максим Олександрович",
+ "avatar_url": "https://avatars.githubusercontent.com/u/233082001?v=4",
+ "profile": "https://github.com/maksim2005UKR",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "plageoj",
+ "name": "Masayuki Sugahara",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10688301?v=4",
+ "profile": "https://plageoj.me",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "therealmate",
+ "name": "therealmate",
+ "avatar_url": "https://avatars.githubusercontent.com/u/61843503?v=4",
+ "profile": "https://github.com/therealmate",
+ "contributions": [
+ "translation"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.devcontainer.json b/.devcontainer.json
index 0a09ed54..8d5e2bd9 100644
--- a/.devcontainer.json
+++ b/.devcontainer.json
@@ -1,6 +1,6 @@
{
"name": "basnijholt/adaptive_lighting",
- "image": "mcr.microsoft.com/devcontainers/python:1-3.13",
+ "image": "mcr.microsoft.com/devcontainers/python:3-3.13",
"postCreateCommand": "./scripts/setup-devcontainer && . .venv/bin/activate",
"forwardPorts": [
8123
diff --git a/.github/workflows/deploy-webapp.yml b/.github/workflows/deploy-webapp.yml
index e52963b0..698d8fec 100644
--- a/.github/workflows/deploy-webapp.yml
+++ b/.github/workflows/deploy-webapp.yml
@@ -30,10 +30,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Set Up Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
python-version: 3.13.5
@@ -53,7 +53,7 @@ jobs:
uses: actions/configure-pages@v5
- name: Upload artifact
- uses: actions/upload-pages-artifact@v3
+ uses: actions/upload-pages-artifact@v4
with:
# Upload the 'site' directory, where your app has been built
path: "site"
diff --git a/.github/workflows/hassfest.yaml b/.github/workflows/hassfest.yaml
index 4d141e56..69f93e15 100644
--- a/.github/workflows/hassfest.yaml
+++ b/.github/workflows/hassfest.yaml
@@ -11,5 +11,5 @@ jobs:
validate_hassfest:
runs-on: "ubuntu-latest"
steps:
- - uses: "actions/checkout@v4.2.2"
+ - uses: "actions/checkout@v6.0.0"
- uses: home-assistant/actions/hassfest@master
diff --git a/.github/workflows/install_dependencies/action.yml b/.github/workflows/install_dependencies/action.yml
index 8c4483f7..4ab58169 100644
--- a/.github/workflows/install_dependencies/action.yml
+++ b/.github/workflows/install_dependencies/action.yml
@@ -14,25 +14,25 @@ runs:
using: "composite"
steps:
- name: Check out code from GitHub
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
repository: ${{ github.repository }}
ref: ${{ github.ref }}
persist-credentials: false
fetch-depth: 0
- name: Check out code from GitHub
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
repository: home-assistant/core
path: core
ref: ${{ inputs.core-version }}
- name: Set up Python ${{ inputs.python-version }}
id: python
- uses: actions/setup-python@v5.6.0
+ uses: actions/setup-python@v6.1.0
with:
python-version: ${{ inputs.python-version }}
- name: Set up UV
- uses: astral-sh/setup-uv@v6
+ uses: astral-sh/setup-uv@v7
- name: Install dependencies
shell: bash
run: |
diff --git a/.github/workflows/main-to-master-sync.yml b/.github/workflows/main-to-master-sync.yml
index d5c915c8..424f50b3 100644
--- a/.github/workflows/main-to-master-sync.yml
+++ b/.github/workflows/main-to-master-sync.yml
@@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml
index 7f579afa..57263d00 100644
--- a/.github/workflows/pre-commit.yaml
+++ b/.github/workflows/pre-commit.yaml
@@ -9,6 +9,6 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v5
+ - uses: actions/checkout@v6
+ - uses: actions/setup-python@v6
- uses: pre-commit/action@v3.0.1
diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml
index fb25eff0..b8a59b40 100644
--- a/.github/workflows/pytest.yaml
+++ b/.github/workflows/pytest.yaml
@@ -26,13 +26,23 @@ jobs:
python-version: "3.13"
- core-version: "2025.5.3"
python-version: "3.13"
- - core-version: "2025.6.1"
+ - core-version: "2025.6.3"
+ python-version: "3.13"
+ - core-version: "2025.7.4"
+ python-version: "3.13"
+ - core-version: "2025.8.3"
+ python-version: "3.13"
+ - core-version: "2025.9.4"
+ python-version: "3.13"
+ - core-version: "2025.10.4"
+ python-version: "3.13"
+ - core-version: "2025.11.3"
python-version: "3.13"
- core-version: "dev"
python-version: "3.13"
steps:
- name: Check out code from GitHub
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Home Assistant
uses: ./.github/workflows/install_dependencies
diff --git a/.github/workflows/update-readme.yml b/.github/workflows/update-readme.yml
index a4422130..7cdb8e24 100644
--- a/.github/workflows/update-readme.yml
+++ b/.github/workflows/update-readme.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Home Assistant
uses: ./.github/workflows/install_dependencies
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 79c7fe00..3fa46b4e 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -11,7 +11,7 @@ jobs:
validate_hacs:
runs-on: "ubuntu-latest"
steps:
- - uses: "actions/checkout@v4"
+ - uses: "actions/checkout@v6"
- name: HACS validation
uses: "hacs/action@main"
with:
diff --git a/Dockerfile b/Dockerfile
index 3bd36ddd..c4d9c695 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,6 +9,12 @@
FROM ghcr.io/astral-sh/uv:debian
+# Install build dependencies for Python extensions
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ python3-dev \
+ build-essential \
+ && rm -rf /var/lib/apt/lists/*
+
# Clone home-assistant/core
RUN git clone --depth 1 --branch dev https://github.com/home-assistant/core.git /core
diff --git a/README.md b/README.md
index 7af1feb8..faf8c51a 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
[](https://github.com/hacs/integration)

-[](#contributors-)
+[](#contributors-)
# 🌞 Adaptive Lighting: Enhance Your Home's Atmosphere with Smart, Sun-Synchronized Lighting 🌙
@@ -616,6 +616,18 @@ Notice the values of `brightness_mode_time_light` and `brightness_mode_time_dark
 Luna Jernberg 🌍 |
 Jeff Wilson 💻 |
 Rasmus Lundsgaard 💻 |
+  Tom Matheussen 💻 |
+  ams2990 💻 |
+  DataGhost 💻 |
+
+
+  Furkan Kaya 🌍 |
+  Rafael do Amaral Porciuncula 🌍 |
+  hhjuhl 🌍 |
+  B.Athish 🌍 |
+  Горпиніч Максим Олександрович 🌍 |
+  Masayuki Sugahara 🌍 |
+  therealmate 🌍 |
diff --git a/custom_components/adaptive_lighting/__init__.py b/custom_components/adaptive_lighting/__init__.py
index 9bcf295c..44151c84 100644
--- a/custom_components/adaptive_lighting/__init__.py
+++ b/custom_components/adaptive_lighting/__init__.py
@@ -1,7 +1,7 @@
"""Adaptive Lighting integration in Home-Assistant."""
import logging
-from typing import Any, Optional
+from typing import Any
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
@@ -38,7 +38,7 @@ CONFIG_SCHEMA = vol.Schema(
async def reload_configuration_yaml(event: Event) -> None:
"""Reload configuration.yaml."""
- hass: HomeAssistant | None = event.data["hass"] if "hass" in event.data else None
+ hass: HomeAssistant | None = event.data.get("hass")
if hass is not None:
await hass.services.async_call("homeassistant", "check_config", {})
else:
diff --git a/custom_components/adaptive_lighting/_docs_helpers.py b/custom_components/adaptive_lighting/_docs_helpers.py
index 46404a34..afbb9d66 100644
--- a/custom_components/adaptive_lighting/_docs_helpers.py
+++ b/custom_components/adaptive_lighting/_docs_helpers.py
@@ -57,8 +57,6 @@ def _type_to_str(type_: Any) -> str: # noqa: PLR0911
def generate_config_markdown_table() -> str:
- import pandas as pd
-
rows: list[dict[str, str]] = []
for k, default, type_ in VALIDATION_TUPLES:
description = DOCS[k]
@@ -87,10 +85,7 @@ def _generate_service_markdown_table(
schema: dict[str, tuple[Any, Any]] | vol.Schema,
alternative_docs: dict[str, str] | None = None,
) -> str:
- if isinstance(schema, vol.Schema):
- schema_dict = _schema_to_dict(schema)
- else:
- schema_dict = schema
+ schema_dict = _schema_to_dict(schema) if isinstance(schema, vol.Schema) else schema
rows: list[dict[str, str]] = []
for k, (default, type_) in schema_dict.items():
if alternative_docs is not None and k in alternative_docs:
diff --git a/custom_components/adaptive_lighting/adaptation_utils.py b/custom_components/adaptive_lighting/adaptation_utils.py
index b68a5bc5..14c28ae8 100644
--- a/custom_components/adaptive_lighting/adaptation_utils.py
+++ b/custom_components/adaptive_lighting/adaptation_utils.py
@@ -107,7 +107,7 @@ async def _create_service_call_data_iterator(
hass: HomeAssistant,
service_datas: list[ServiceData],
filter_by_state: bool,
-) -> AsyncGenerator[ServiceData, None]:
+) -> AsyncGenerator[ServiceData]:
"""Enumerates and filters a list of service datas on the fly.
If filtering is enabled, every service data is filtered by the current state of
@@ -142,7 +142,7 @@ class AdaptationData:
entity_id: str
context: Context
sleep_time: float
- service_call_datas: AsyncGenerator[ServiceData, None]
+ service_call_datas: AsyncGenerator[ServiceData]
force: bool
max_length: int
which: Literal["brightness", "color", "both"]
diff --git a/custom_components/adaptive_lighting/color_and_brightness.py b/custom_components/adaptive_lighting/color_and_brightness.py
index 47caea03..34da23c6 100644
--- a/custom_components/adaptive_lighting/color_and_brightness.py
+++ b/custom_components/adaptive_lighting/color_and_brightness.py
@@ -8,8 +8,8 @@ import datetime
import logging
import math
from dataclasses import dataclass
-from datetime import timedelta
-from functools import cached_property, partial
+from datetime import UTC, timedelta
+from functools import partial
from typing import TYPE_CHECKING, Any, Literal, cast
from homeassistant.util.color import (
@@ -17,9 +17,10 @@ from homeassistant.util.color import (
color_temperature_to_rgb,
color_xy_to_hs,
)
+from propcache.api import cached_property
if TYPE_CHECKING:
- import astral
+ import astral.location
# Same as homeassistant.const.SUN_EVENT_SUNRISE and homeassistant.const.SUN_EVENT_SUNSET
# We re-define them here to not depend on homeassistant in this file.
@@ -32,7 +33,6 @@ SUN_EVENT_MIDNIGHT = "solar_midnight"
_ORDER = (SUN_EVENT_SUNRISE, SUN_EVENT_NOON, SUN_EVENT_SUNSET, SUN_EVENT_MIDNIGHT)
_ALLOWED_ORDERS = {_ORDER[i:] + _ORDER[:i] for i in range(len(_ORDER))}
-UTC = datetime.timezone.utc
utcnow: partial[datetime.datetime] = partial(datetime.datetime.now, UTC)
utcnow.__doc__ = "Get now in UTC time."
@@ -44,7 +44,7 @@ class SunEvents:
"""Track the state of the sun and associated light settings."""
name: str
- astral_location: astral.Location
+ astral_location: astral.location.Location
sunrise_time: datetime.time | None
min_sunrise_time: datetime.time | None
max_sunrise_time: datetime.time | None
@@ -198,7 +198,7 @@ class SunLightSettings:
"""Track the state of the sun and associated light settings."""
name: str
- astral_location: astral.Location
+ astral_location: astral.location.Location
adapt_until_sleep: bool
max_brightness: int
max_color_temp: int
@@ -296,7 +296,7 @@ class SunLightSettings:
)
return clamp(brightness, self.min_brightness, self.max_brightness)
- def brightness_pct(self, dt: datetime.datetime, is_sleep: bool) -> float:
+ def brightness_pct(self, dt: datetime.datetime, is_sleep: bool) -> float | None:
"""Calculate the brightness in %."""
if is_sleep:
return self.sleep_brightness
@@ -331,7 +331,7 @@ class SunLightSettings:
) -> dict[str, Any]:
"""Calculate the brightness and color."""
sun_position = self.sun.sun_position(dt)
- rgb_color: tuple[float, float, float]
+ rgb_color: tuple[int, int, int]
# Variable `force_rgb_color` is needed for RGB color after sunset (if enabled)
force_rgb_color = False
brightness_pct = self.brightness_pct(dt, is_sleep)
diff --git a/custom_components/adaptive_lighting/hass_utils.py b/custom_components/adaptive_lighting/hass_utils.py
index d411379c..550ae350 100644
--- a/custom_components/adaptive_lighting/hass_utils.py
+++ b/custom_components/adaptive_lighting/hass_utils.py
@@ -4,6 +4,7 @@ import logging
from collections.abc import Awaitable, Callable
from homeassistant.core import HomeAssistant, ServiceCall
+from homeassistant.helpers import device_registry, entity_registry
from homeassistant.util.read_only_dict import ReadOnlyDict
from .adaptation_utils import ServiceData
@@ -11,6 +12,25 @@ from .adaptation_utils import ServiceData
_LOGGER = logging.getLogger(__name__)
+def area_entities(hass: HomeAssistant, area_id: str):
+ """Get all entities linked to an area."""
+ ent_reg = entity_registry.async_get(hass)
+ entity_ids = [
+ entry.entity_id
+ for entry in entity_registry.async_entries_for_area(ent_reg, area_id)
+ ]
+ dev_reg = device_registry.async_get(hass)
+ entity_ids.extend(
+ [
+ entity.entity_id
+ for device in device_registry.async_entries_for_area(dev_reg, area_id)
+ for entity in entity_registry.async_entries_for_device(ent_reg, device.id)
+ if entity.area_id is None
+ ],
+ )
+ return entity_ids
+
+
def setup_service_call_interceptor(
hass: HomeAssistant,
domain: str,
@@ -27,7 +47,7 @@ def setup_service_call_interceptor(
# This is necessary to replace a registered service handler with our
# proxy handler to intercept calls.
registered_services = (
- hass.services._services # pylint: disable=protected-access # type: ignore
+ hass.services._services # pylint: disable=protected-access # type: ignore[attr-defined]
)
except AttributeError as error:
msg = (
diff --git a/custom_components/adaptive_lighting/manifest.json b/custom_components/adaptive_lighting/manifest.json
index 92100afa..856abfe8 100644
--- a/custom_components/adaptive_lighting/manifest.json
+++ b/custom_components/adaptive_lighting/manifest.json
@@ -8,5 +8,5 @@
"iot_class": "calculated",
"issue_tracker": "https://github.com/basnijholt/adaptive-lighting/issues",
"requirements": ["ulid-transform"],
- "version": "1.26.0"
+ "version": "1.27.0"
}
diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py
index 353be57d..dbb72ea1 100644
--- a/custom_components/adaptive_lighting/switch.py
+++ b/custom_components/adaptive_lighting/switch.py
@@ -75,7 +75,6 @@ from homeassistant.helpers.event import (
)
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.sun import get_astral_location
-from homeassistant.helpers.template import area_entities
from homeassistant.util import slugify
from homeassistant.util.color import (
color_temperature_to_rgb,
@@ -153,7 +152,7 @@ from .const import (
apply_service_schema,
replace_none_str,
)
-from .hass_utils import setup_service_call_interceptor
+from .hass_utils import area_entities, setup_service_call_interceptor
from .helpers import (
clamp,
color_difference_redmean,
@@ -626,7 +625,7 @@ def _is_light_group(state: State) -> bool:
def _supported_features(hass: HomeAssistant, light: str) -> set[str]:
state = hass.states.get(light)
assert state is not None
- supported_features = int(state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)) # type: ignore
+ supported_features = int(state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)) # type: ignore[arg-type]
assert isinstance(supported_features, int)
supported: set[str] = set()
@@ -634,7 +633,7 @@ def _supported_features(hass: HomeAssistant, light: str) -> set[str]:
if supported_features & LightEntityFeature.TRANSITION:
supported.add("transition")
- supported_color_modes = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES, set()) # type: ignore
+ supported_color_modes = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES, set()) # type: ignore[arg-type]
color_modes = {
ColorMode.RGB,
ColorMode.RGBW,
@@ -695,6 +694,58 @@ def _add_missing_attributes(
return old_attributes, new_attributes
+def _has_color_mode_changed(
+ light: str,
+ old_attributes: dict[str, Any],
+ new_attributes: dict[str, Any],
+ context: Context,
+) -> bool:
+ """Check if the light's color mode changed (e.g., color_temp to RGB or vice versa).
+
+ This must be called BEFORE _add_missing_attributes() to detect mode changes
+ using the original attributes. See issue #1275.
+ """
+ old_has_color_temp = old_attributes.get(ATTR_COLOR_TEMP_KELVIN) is not None
+ old_has_rgb = old_attributes.get(ATTR_RGB_COLOR) is not None
+ old_has_xy = old_attributes.get(ATTR_XY_COLOR) is not None
+
+ new_has_color_temp = new_attributes.get(ATTR_COLOR_TEMP_KELVIN) is not None
+ new_has_rgb = new_attributes.get(ATTR_RGB_COLOR) is not None
+ new_has_xy = new_attributes.get(ATTR_XY_COLOR) is not None
+
+ # Determine old and new color modes
+ # Priority: color_temp > rgb > xy (matching typical light behavior)
+ if old_has_color_temp:
+ old_mode = "color_temp"
+ elif old_has_rgb:
+ old_mode = "rgb"
+ elif old_has_xy:
+ old_mode = "xy"
+ else:
+ old_mode = None
+
+ if new_has_color_temp:
+ new_mode = "color_temp"
+ elif new_has_rgb:
+ new_mode = "rgb"
+ elif new_has_xy:
+ new_mode = "xy"
+ else:
+ new_mode = None
+
+ # Check if mode changed
+ if old_mode is not None and new_mode is not None and old_mode != new_mode:
+ _LOGGER.debug(
+ "Light mode of %s changed from %s to %s with context.id='%s'",
+ light,
+ old_mode,
+ new_mode,
+ context.id,
+ )
+ return True
+ return False
+
+
def _attributes_have_changed(
light: str,
old_attributes: dict[str, Any],
@@ -707,6 +758,17 @@ def _attributes_have_changed(
# so we must protect for `None` here
# see https://github.com/home-assistant/core/pull/101946
+ # Check for color mode changes BEFORE attribute conversion
+ # This detects external changes like Hue scenes switching from color_temp to RGB
+ # See: https://github.com/basnijholt/adaptive-lighting/issues/1275
+ if adapt_color and _has_color_mode_changed(
+ light,
+ old_attributes,
+ new_attributes,
+ context,
+ ):
+ return True
+
if adapt_color:
old_attributes, new_attributes = _add_missing_attributes(
old_attributes,
@@ -767,6 +829,7 @@ def _attributes_have_changed(
context.id,
)
return True
+
return False
@@ -1133,8 +1196,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
self.manager.reset(*self.lights)
async def _async_update_at_interval_action(
- self, now: Any = None
- ) -> None: # noqa: ARG002
+ self,
+ now: Any = None, # noqa: ARG002
+ ) -> None:
"""Update the attributes and maybe adapt the lights."""
await self._update_attrs_and_maybe_adapt_lights(
context=self.create_context("interval"),
@@ -1566,7 +1630,7 @@ class SimpleSwitch(SwitchEntity, RestoreEntity):
self.hass = hass
data = validate(config_entry)
self._icon = icon
- self._state: bool | None = None
+ self._state: bool = initial_state
self._which = which
self._config_name = data[CONF_NAME]
self._unique_id = f"{self._config_name}_{slugify(self._which)}"
@@ -2118,7 +2182,9 @@ class AdaptiveLightingManager:
self._handle_timer(light, self.transition_timers, last_transition, reset)
def set_auto_reset_manual_control_times(
- self, lights: list[str], time: float
+ self,
+ lights: list[str],
+ time: float,
) -> None:
"""Set the time after which the lights are automatically reset."""
if time == 0:
@@ -2685,7 +2751,6 @@ class _AsyncSingleShotTimer:
async def _run(self) -> None:
"""Run the timer. Don't call this directly, use start() instead."""
- self.start_time = dt_util.utcnow()
await asyncio.sleep(self.delay)
if self.callback:
if asyncio.iscoroutinefunction(self.callback):
@@ -2701,6 +2766,10 @@ class _AsyncSingleShotTimer:
"""Start the timer."""
if self.task is not None and not self.task.done():
self.task.cancel()
+ # Set start_time before creating task to avoid race condition
+ # where is_running() returns True but start_time is still None
+ # See: https://github.com/basnijholt/adaptive-lighting/issues/1272
+ self.start_time = dt_util.utcnow()
self.task = asyncio.create_task(self._run())
def cancel(self) -> None:
diff --git a/custom_components/adaptive_lighting/translations/da.json b/custom_components/adaptive_lighting/translations/da.json
index f0ff8742..347c3f5c 100644
--- a/custom_components/adaptive_lighting/translations/da.json
+++ b/custom_components/adaptive_lighting/translations/da.json
@@ -40,7 +40,8 @@
"detect_non_ha_changes": "detect_non_ha_changes: Registrer alle ændringer på >10% på et lys (også udenfor HA), kræver at 'take_over_control' er slået til (kalder 'homeassistant.update_entity' hvert 'interval'!)",
"transition": "Overgangsperiode når en ændring i lyset udføres (i sekunder)",
"transition_until_sleep": "overgang_til_sove: Når aktiveret, vil adaptiv belysning behandle søvnindstillinger som minimum, og overgår til disse værdier efter solnedgang. 🌙",
- "adapt_only_on_bare_turn_on": "tilpas_kun_ved_enkelt_tænd: Når du tænder lys for første gang. Hvis indstillet til 'true', tilpasser AL kun, hvis 'lys.tænd' er kaldt uden at angive farve eller lysstyrke. ❌🌈 Dette forhindrer f.eks. tilpasning, når du aktiverer en scene. Hvis indstillet til 'false' tilpasser AL sig uanset tilstanden af farve eller lysstyrke i den oprindelige 'service_data'. Har brug for at 'take_over_control' er aktiveret. 🕵️ "
+ "adapt_only_on_bare_turn_on": "tilpas_kun_ved_enkelt_tænd: Når du tænder lys for første gang. Hvis indstillet til 'true', tilpasser AL kun, hvis 'lys.tænd' er kaldt uden at angive farve eller lysstyrke. ❌🌈 Dette forhindrer f.eks. tilpasning, når du aktiverer en scene. Hvis indstillet til 'false' tilpasser AL sig uanset tilstanden af farve eller lysstyrke i den oprindelige 'service_data'. Har brug for at 'take_over_control' er aktiveret. 🕵️",
+ "include_config_in_attributes": "include_config_in_attributes: Vis alle indstillinger som attributter for kontakten når dette er sat til »true«. 📝"
},
"data_description": {
"interval": "Frekvens til at tilpasse lysene, i sekunder. 🔄",
@@ -59,7 +60,9 @@
"sunrise_offset": "Juster solopgangstiden med en positiv eller negativ offset på få sekunder. ⏰",
"max_sunset_time": "Indstil den seneste virtuelle solnedgangstid (HH:MM:SS), hvilket giver mulighed for tidligere solnedgange. 🌇",
"sleep_color_temp": "Farvetemperatur i søvntilstand (bruges når `sleep_rgb_or_color_temp` er `color_temp`) i Kelvin. 😴",
- "brightness_mode": "Lysstyrketilstand til brug. Mulige værdier er \"default\", \"linear\" og \"tanh\" (bruger \"brightness_mode_time_dark\" og \"brightness_mode_time_light\"). 📈"
+ "brightness_mode": "Lysstyrketilstand til brug. Mulige værdier er \"default\", \"linear\" og \"tanh\" (bruger \"brightness_mode_time_dark\" og \"brightness_mode_time_light\"). 📈",
+ "send_split_delay": "Forsinkelse (ms) mellem »separate_turn_on_commands« for lyskilder som ikke understøtter simultane styrke- og farveindstillinger. ⏲️",
+ "initial_transition": "Den første overgangs varighed når lysene ændres fra »off« til »on« i sekunder. ⏲️"
}
}
},
@@ -153,12 +156,26 @@
},
"sleep_color_temp": {
"description": "Farvetemperatur i søvntilstand (bruges når `sleep_rgb_or_color_temp` er `color_temp`) i Kelvin. 😴"
+ },
+ "send_split_delay": {
+ "description": "Forsinkelse (ms) mellem »separate_turn_on_commands« for lyskilder som ikke understøtter simultane styrke- og farveindstillinger. ⏲️"
+ },
+ "detect_non_ha_changes": {
+ "description": "Opdager og stopper tilpasningen ved tilstandsændringer, som ikke er udløst af »light.turn_on«. Indstillingen »take_over_control« skal være aktiveret. 🕵️ Advarsel: ⚠️ Nogle lyskilder kan rapportere en falsk tændt-tilstand, hvilket kan medføre af lyskilden tændes når det ikke er meningen. Slå denne funktion fra, hvis du oplever dette problem."
+ },
+ "initial_transition": {
+ "description": "Den første overgangs varighed når lysene ændres fra »off« til »on« i sekunder. ⏲️"
}
},
"description": "Skift de indstillinger du ønsker i kontakten. Alle muligheder her er de samme som i konfigurationsflowet."
},
"set_manual_control": {
- "description": "Markér om et lys er 'manuelt kontrolleret'."
+ "description": "Markér om et lys er 'manuelt kontrolleret'.",
+ "fields": {
+ "lights": {
+ "description": "entity_id(er) af lys, hvis ikke specificeret, vil alle lys i kontakten være valgt. 💡"
+ }
+ }
}
}
}
diff --git a/custom_components/adaptive_lighting/translations/gl.json b/custom_components/adaptive_lighting/translations/gl.json
index 2752f95c..5a1f211c 100644
--- a/custom_components/adaptive_lighting/translations/gl.json
+++ b/custom_components/adaptive_lighting/translations/gl.json
@@ -3,7 +3,11 @@
"step": {
"init": {
"data_description": {
- "sleep_brightness": "Porcentaxe de brillo das luces en modo durmir. 😴"
+ "sleep_brightness": "Porcentaxe de brillo das luces en modo durmir. 😴",
+ "send_split_delay": "Retraso (ms) entre `separate_turn_on_commands`",
+ "sunrise_offset": "Axusta a hora do amencer cun desfasamento positivo ou negativo en segundos. ⏰",
+ "sunset_offset": "Axusta a hora da posta de sol cun desfasamento positivo ou negativo en segundos. ⏰",
+ "interval": "Frecuencia para adaptar as luces, en segundos. 🔄"
},
"title": "Configuración de Iluminación Adaptativa"
}
@@ -18,6 +22,15 @@
},
"only_once": {
"description": "Adaptar luces só cando estean acesas (`true`) ou mantelas adaptándose (`false`). 🔄"
+ },
+ "sunrise_offset": {
+ "description": "Axusta a hora do amencer cun desfasamento positivo ou negativo en segundos. ⏰"
+ },
+ "sunset_offset": {
+ "description": "Axusta a hora da posta de sol cun desfasamento positivo ou negativo en segundos. ⏰"
+ },
+ "transition": {
+ "description": "Duración da transición cando as luces cambian, en segundos. 🕑"
}
}
}
diff --git a/custom_components/adaptive_lighting/translations/hu.json b/custom_components/adaptive_lighting/translations/hu.json
index 3af17eff..d349699e 100644
--- a/custom_components/adaptive_lighting/translations/hu.json
+++ b/custom_components/adaptive_lighting/translations/hu.json
@@ -30,7 +30,7 @@
"max_brightness": "max_brightness: Maximális fényerő százalékban megadva. 💡",
"detect_non_ha_changes": "detect_non_ha_changes: `Világítás: Bekapcsolás`- szolgáltatás meghívástól eltérő állapotváltozások esetén észleli és leállítja az illesztéseket. A `take_over_control` beállítás engedélyezése szükséges. 🕵️ Vigyázat: ⚠️ Egyes lights tévesen jelezhetik a \"bekapcsolt\" állapotot, ami váratlanul bekapcsolódó lámpákhoz vezethet. Ha ilyen problémákat tapasztal, tiltsa le ezt a funkciót.",
"multi_light_intercept": "multi_light_intercept: `Világítás: Bekapcsolás` szolgáltatás hívások elfogása és adaptálása, amelyek több fényt céloznak meg. ➗⚠️ Ez azt eredményezheti, hogy egyetlen `Világítás: Bekapcsolás` szolgáltatás hívás több hívásra oszlik fel, pl. ha a lights különböző kapcsolókban vannak. Az `elfogás` engedélyezése szükséges.",
- "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: Kizárólag a bekapcsoláskor érvényes. A beállítást \"igaz\"-ra állítva, az AL csak akkor végzi az illesztést, amennyiben a \"Világítás: Bekapcsolás\" szolgáltatás meghívása a szín és fényerő paraméterek megadása nélkül történik.❌🌈 Ez pl. alkalmas az illesztés felfüggesztésére egy jelenet aktiválásakor. \"Hamis\" beállítás esetén az AL elvégzi a kezdeti illesztést a szín és fényerő paraméterek meghívásától függetlenül. A használatához engedélyezve kell lennie a \"take_over_control\" beállításnak. 🕵️ ",
+ "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: Kizárólag a bekapcsoláskor érvényes. A beállítást \"igaz\"-ra állítva, az AL csak akkor végzi az illesztést, amennyiben a \"Világítás: Bekapcsolás\" szolgáltatás meghívása a szín és fényerő paraméterek megadása nélkül történik.❌🌈 Ez pl. alkalmas az illesztés felfüggesztésére egy jelenet aktiválásakor. \"Hamis\" beállítás esetén az AL elvégzi a kezdeti illesztést a szín és fényerő paraméterek meghívásától függetlenül. A használatához engedélyezve kell lennie a \"take_over_control\" beállításnak. 🕵️",
"skip_redundant_commands": "skip_redundant_commands: Az olyan adaptációs parancsok küldésének kihagyása, amelyek célállapota már megegyezik a fény ismert állapotával. Minimalizálja a hálózati forgalmat, és bizonyos helyzetekben javítja az adaptációs reakciókészséget. 📉Kapcsolja ki, ha a fizikai fényállapotok nem szinkronizálódnak a HA rögzített állapotával.",
"separate_turn_on_commands": "separate_turn_on_commands: Elkülönített `Világítás: Bekapcsolás` hívásokat használ a szín és a fényerő beállításához, ami néhány világítás típusnál szükséges. 🔀",
"max_color_temp": "max_color_temp: A leghidegebb színhőmérséklet kelvinben. ❄️",
diff --git a/custom_components/adaptive_lighting/translations/ja.json b/custom_components/adaptive_lighting/translations/ja.json
index 065d8b3b..38f13d81 100644
--- a/custom_components/adaptive_lighting/translations/ja.json
+++ b/custom_components/adaptive_lighting/translations/ja.json
@@ -1,5 +1,5 @@
{
- "title": "適応型照明",
+ "title": "明るさの自動調整",
"services": {
"change_switch_settings": {
"fields": {
@@ -7,7 +7,7 @@
"description": "日の出時間を基準に秒単位で正値もしくは負値で調整する。⏰"
},
"only_once": {
- "description": "適応型照明を照明がオンになっているときのみ(`true`)それとも適応し続ける場合は(`false`)。"
+ "description": "一度だけ明るさを自動調整するには(`true`)、常に自動調整し続ける場合は(`false`)。"
},
"sunset_offset": {
"description": "日の入時間を基準に秒単位で正値もしくは負値で調整する。⏰"
@@ -35,7 +35,7 @@
"sunrise_offset": "日の出時間を基準に秒単位で正値もしくは負値で調整する。⏰",
"sunset_offset": "日の入時間を基準に秒単位で正値もしくは負値で調整する。⏰"
},
- "title": "適応型照明オプション"
+ "title": "明るさの自動調整オプション"
}
}
}
diff --git a/custom_components/adaptive_lighting/translations/pt-BR.json b/custom_components/adaptive_lighting/translations/pt-BR.json
index 43eacb01..c4fc796f 100644
--- a/custom_components/adaptive_lighting/translations/pt-BR.json
+++ b/custom_components/adaptive_lighting/translations/pt-BR.json
@@ -39,7 +39,11 @@
"sunset_time": "sunset_time: substituição manual do horário do pôr do sol, se 'Nenhum', ele usa o horário real do nascer do sol em sua localização (HH:MM:SS)",
"take_over_control": "take_over_control: Se qualquer coisa, exceto Adaptive Lighting, chamar 'light.turn_on' quando uma luz já estiver acesa, pare de adaptar essa luz até que ela (ou o interruptor) desligue -> ligue.",
"detect_non_ha_changes": "detect_non_ha_changes: detecta todas as alterações > 10% feitas nas luzes (também fora do HA), requer que 'take_over_control' seja ativado (chama 'homeassistant.update_entity' a cada 'intervalo'!)",
- "transition": "Tempo de transição ao aplicar uma mudança nas luzes (segundos)"
+ "transition": "Tempo de transição ao aplicar uma mudança nas luzes (segundos)",
+ "skip_redundant_commands": "skip_redundant_commands: Deixar de enviar comandos de adaptação cujo estado alvo já seja igual ao estado atual da luz. Minimiza o tráfego de rede e melhora a responsividade da adaptação em algumas situações. 📉Desative se os estados físicos das luzes podem ficar diferentes do estado registrado no HA.",
+ "transition_until_sleep": "transition_until_sleep: Quando ativada, a Iluminação Adaptativa considerará as configurações de sono como o valor mínimo, transicionando para esses valores após o pôr do sol. 🌙",
+ "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: Ao ligar as luzes inicialmente. Se definido como `true`, a Iluminação Adaptativa se adapta somente se o comando `light.turn_on` for chamado sem especificar cor ou brilho. ❌🌈 Isso, por exemplo, impede a adaptação ao ativar uma cena. Se false, a Iluminação Adaptativa se adapta independentemente da presença de cor ou brilho nos dados iniciais do `service_data`. Precisa de `take_over_control` ativado. 🕵️",
+ "intercept": "intercept: Interceptar e adaptar os chamados `light.turn_on` para ativar a adaptação instantânea de cor e brilho. 🏎️ Desative para luzes que não suportam `light.turn_on` com cor e brilho."
}
}
},
diff --git a/custom_components/adaptive_lighting/translations/ru.json b/custom_components/adaptive_lighting/translations/ru.json
index fd4a4b55..79f3cd02 100644
--- a/custom_components/adaptive_lighting/translations/ru.json
+++ b/custom_components/adaptive_lighting/translations/ru.json
@@ -42,7 +42,7 @@
"transition": "Время перехода при применении изменения к источникам света. (секунды)",
"adapt_delay": "Время ожидания между включением света и применением адаптации. Может помочь избежать мерцания. (секунды)",
"multi_light_intercept": "multi_light_intercept: перехватывает и адаптирует вызовы `light.turn_on`, нацеленные на несколько источников света. ➗⚠️ Это может привести к разделению одного вызова `light.turn_on` на несколько вызовов, например, когда освещение включено в разные выключатели. Требуется, чтобы `перехват` был включен.",
- "adapt_only_on_bare_turn_on": "Adapt_only_on_bare_turn_on: При первоначальном включении света. Если установлено значение «true», AL адаптируется только в том случае, если «light.turn_on» вызывается без указания цвета или яркости. ❌🌈 Это, например, предотвращает адаптацию при активации сцены. Если false, AL адаптируется независимо от наличия цвета или яркости в исходных service_data. Требуется включить take_over_control. 🕵️ ",
+ "adapt_only_on_bare_turn_on": "Adapt_only_on_bare_turn_on: При первоначальном включении света. Если установлено значение «true», AL адаптируется только в том случае, если «light.turn_on» вызывается без указания цвета или яркости. ❌🌈 Это, например, предотвращает адаптацию при активации сцены. Если false, AL адаптируется независимо от наличия цвета или яркости в исходных service_data. Требуется включить take_over_control. 🕵️",
"skip_redundant_commands": "Skip_redundant_commands: Пропустить отправку команд адаптации, целевое состояние которых уже равно известному состоянию источника света. Минимизирует сетевой трафик и улучшает скорость адаптации в некоторых ситуациях. 📉Отключите, если физические состояния освещения не синхронизируются с записанным состоянием HA.",
"intercept": "intercept: перехватывать и адаптировать вызовы `light.turn_on` для обеспечения мгновенной адаптации цвета и яркости. 🏎️ Отключите источники света, которые не поддерживают `light.turn_on` с цветом и яркостью.",
"include_config_in_attributes": "include_config_in_attributes: отображать все параметры в качестве атрибутов на переключателе в Home Assistant, если установлено значение `true`. 📝",
diff --git a/custom_components/adaptive_lighting/translations/sl.json b/custom_components/adaptive_lighting/translations/sl.json
new file mode 100644
index 00000000..dc78acaa
--- /dev/null
+++ b/custom_components/adaptive_lighting/translations/sl.json
@@ -0,0 +1,75 @@
+{
+ "options": {
+ "step": {
+ "init": {
+ "data": {
+ "prefer_rgb_color": "prefer_rgb_color: Ali v primeru možnosti raje uporabiti prilagoditev RGB barve kot barvno temperaturo luči. 🌈",
+ "transition_until_sleep": "transition_until_sleep: Če je omogočeno, bo Adaptive Lighting obravnaval nastavitve spanja kot minimalne vrednosti in bo po zahodu sonca prehajal na te vrednosti. 🌙",
+ "take_over_control": "take_over_control: Onemogoči Adaptive Lighting, če drug vir pokliče \"light.turn_on\", ko so luči prižgane in se prilagajajo. Opozorilo: to ob vsakem intervalu kliče \"homeassistant.update_entity\"! 🔒",
+ "detect_non_ha_changes": "„detect_non_ha_changes: Zazna in ustavi prilagoditve za spremembe stanja, ki niso posledica \"light.turn_on\". Zahteva omogočeno \"take_over_control\". 🕵️ Pozor: ⚠️ Nekatere luči lahko nepravilno poročajo, da so prižgane, kar lahko povzroči nepričakovano vklapljanje. Onemogočite to funkcijo, če naletite na takšne težave.",
+ "lights": "lights: Seznam entity_id-jev luči za nadzor (lahko je prazen). 🌟",
+ "min_brightness": "min_brightness: Odstotek najmanjše svetlosti. 💡",
+ "max_brightness": "max_brightness: Odstotek največeje svetlosti. 💡",
+ "min_color_temp": "min_color_temp: Najtoplejša barvna temperatura v Kelvinih. 🔥",
+ "max_color_temp": "max_color_temp: Najhladnejša barvna temperatura v kelvinih. ❄️",
+ "separate_turn_on_commands": "separate_turn_on_commands: Uporabi ločene klice \"light.turn_on\" za barvo in jakost, kar je potrebno za nekatere tipe luči. 🔀",
+ "skip_redundant_commands": "skip_redundant_commands: Preskoči pošiljanje prilagoditvenih ukazov, če je ciljano stanje že enako poznanemu stanju luči. Zmanjšuje omrežni promet in izboljšuje odzivnost prilagajanja v določenih situacijah. 📉 Onemogočite, če se fizična stanja luči ne ujemajo z zabeleženim stanjem v HA.",
+ "intercept": "intercept: Prestreza in prilagaja klice \"light.turn_on\" za takojšnjo prilagoditev barve in jakosti. 🏎️ Onemogočite za luči, ki ne podpirajo \"light.turn_on\" z barvo in svetlostjo.",
+ "multi_light_intercept": "multi_light_intercept: Prestreza in prilagaja klice \"light.turn_on\", ki ciljajo več luči. ➗⚠️ To lahko privede do razdelitve enega klica \"light.turn_on\" v več klicev, npr. ko so luči na različnih stikalih. Zahteva omogočeno \"intercept\".",
+ "include_config_in_attributes": "include_config_in_attributes: Ko je nastavljeno na \"true\", prikaže vse možnosti kot atribute stikala v Home Assistantu. 📝",
+ "only_once": "only_once: Prilagodi luči samo ob vklopu (true) ali pa jih še naprej prilagajaj (false). 🔄",
+ "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: Ob začetnem vklopu luči. Če je nastavljeno na \"true\", AL prilagodi samo, če je \"light.turn_on\" klic brez podanih parametrov barve ali jakosti. ❌🌈 S tem se npr. prepreči prilagajanje pri aktivaciji scene. Če je \"false\", AL prilagodi ne glede na prisotnost barve ali jakosti v začetnih \"service_data\". Zahteva omogočeno \"take_over_control\". 🕵️"
+ },
+ "data_description": {
+ "sunrise_offset": "Prilagodite čas sončnega vzhoda z pozitivnim ali negativnim zamikom v sekundah. ⏰",
+ "send_split_delay": "Zamik (ms) med \"separate_turn_on_commands\" za luči, ki ne podpirajo hkratne nastavitve jakosti in barve. ⏲️",
+ "transition": "Trajanje prehoda pri spreminjanju luči, v sekundah. 🕑",
+ "interval": "Pogostost prilagajanja luči, v sekundah. 🔄",
+ "sleep_brightness": "Odstotek svetlosti luči v načinu spanja. 😴",
+ "sleep_rgb_or_color_temp": "V načinu spanja uporabi \"rgb_color\" ali \"color_temp\". 🌙",
+ "sleep_color_temp": "Barvna temperatura v načinu spanja (uporabljena, ko je \"sleep_rgb_or_color_temp\" nastavljena na \"color_temp\") v Kelvinih. 😴",
+ "sleep_transition": "Trajanje prehoda, ko se preklopi način spanja, v sekundah. 😴",
+ "sunrise_time": "Nastavi fiksni čas (HH:MM:SS) sončnega vzhoda. 🌅",
+ "min_sunrise_time": "Nastavi najzgodnejši navidezni sončni vzhod (HH:MM:SS), dovoljuje kasnejše vzhode. 🌅",
+ "sunset_time": "Nastavite fiksni čas (HH:MM:SS) za sončni zahod. 🌇",
+ "min_sunset_time": "Nastavite najzgodnejši navidezni čas sončnega zahoda (HH:MM:SS), dovoljuje kasnejše sončne zahode. 🌇",
+ "sunset_offset": "Prilagodite čas sončnega zahoda s pozitivnim ali negativnim zamikom v sekundah. ⏰",
+ "brightness_mode_time_dark": "(Prezrto, če je \"brightness_mode='default'\") Trajanje v sekundah za postopno povečanje ali zmanjšanje svetlosti pred/po sončnem vzhodu/zahodu. 📈📉",
+ "brightness_mode_time_light": "(Prezrto, če je \"brightness_mode='default'\") Trajanje v sekundah za postopno povečanje ali zmanjšanje svetlosti po/pred sončnem vzhodu/zahodu. 📈📉",
+ "autoreset_control_seconds": "Samodejno ponastavi ročni nadzor po določenem številu sekund. Nastavite na 0, da onemogočite. ⏲️",
+ "max_sunrise_time": "Nastavi najkasnejši virtualni sončni vzhod (HH:MM:SS), dovoljuje zgodnejše vzhode. 🌅",
+ "max_sunset_time": "Nastavite najpoznejši navidezni čas sončnega zahoda (HH:MM:SS), dovoljuje zgodnejše sončne zahode. 🌇",
+ "brightness_mode": "Način upravljanja svetlosti. Možne vrednosti so \"default\", \"linear\" in \"tanh\" (uporablja \"brightness_mode_time_dark\" in \"brightness_mode_time_light\"). 📈",
+ "initial_transition": "Trajanje prvega prehoda, ko se luči prižgejo (iz \"off\" v \"on\"), v sekundah. ⏲️",
+ "sleep_rgb_color": "RGB barva v načinu spanja (uporabljena, ko je \"sleep_rgb_or_color_temp\" nastavljeno na \"rgb_color\"). 🌈"
+ },
+ "title": "Nastavitve prilagodljive osvetlitve",
+ "description": "Konfigurirajte komponento Adaptive Lighting. Imena možnosti so usklajena z nastavitvami v YAML. Če ste ta vnos definirali v YAML, tukaj ne bodo prikazane nobene možnosti. Za interaktivne grafe, ki ponazarjajo učinke parametrov, obiščite to [spletno aplikacijo](https://basnijholt.github.io/adaptive-lighting). Za dodatne podrobnosti glejte [uradno dokumentacijo](https://github.com/basnijholt/adaptive-lighting#readme)."
+ }
+ }
+ },
+ "config": {
+ "step": {
+ "user": {
+ "title": "Izberite ime za instanco Adaptive Lighting",
+ "description": "Vsaka instanca lahko vsebuje več luči!"
+ }
+ },
+ "abort": {
+ "already_configured": "Naprava je že konfigurirana"
+ }
+ },
+ "services": {
+ "change_switch_settings": {
+ "fields": {
+ "only_once": {
+ "description": "Prilagajaj luči samo kadar so prižgane (\"true\") ali konstantno jih prilagajaj (\"false\")"
+ },
+ "max_sunrise_time": {
+ "description": "Nastavite najpoznejši navidezni čas sončnega vzhoda (HH:MM:SS), dovoljuje zgodnejše sončne vzhode. 🌅"
+ }
+ }
+ }
+ },
+ "title": "Prilagodljiva osvetlitev"
+}
diff --git a/custom_components/adaptive_lighting/translations/tr.json b/custom_components/adaptive_lighting/translations/tr.json
new file mode 100644
index 00000000..3b5c2d6e
--- /dev/null
+++ b/custom_components/adaptive_lighting/translations/tr.json
@@ -0,0 +1,202 @@
+{
+ "title": "Akıllı Aydınlatma",
+ "options": {
+ "step": {
+ "init": {
+ "title": "Akıllı Aydınlatma seçenekleri",
+ "data": {
+ "adapt_only_on_bare_turn_on": "Işıklar açıldığında geçerlidir. `true` olarak ayarlanırsa, eklenti yalnızca `light.turn_on` işlemi renk veya parlaklık belirtilmeden çağrıldığında uyarlama yapar (örneğin sahne etkinleştirmelerinde uyarlama yapılmaz). ❌🌈\n`false` olarak ayarlanırsa, renk veya parlaklık belirtilmiş olsa bile uyarlama yapılır. Bu ayarın çalışması için `take_over_control` etkin olmalıdır. 🕵️",
+ "detect_non_ha_changes": "`detect_non_ha_changes`: `light.turn_on` dışındaki durum değişikliklerini algılar ve uyarlamayı durdurur. Bu ayarın çalışması için `take_over_control` etkin olmalıdır. 🕵️\nDikkat: ⚠️ Bazı ışıklar yanlışlıkla “açık” durumunu bildirebilir, bu da ışıkların beklenmedik şekilde açılmasına yol açabilir. Böyle bir durumla karşılaşırsanız bu özelliği devre dışı bırakın.",
+ "include_config_in_attributes": "`include_config_in_attributes`: `true` olarak ayarlandığında, tüm seçenekleri Home Assistant’ta anahtarın attribute’ları olarak gösterir. 📝",
+ "intercept": "`intercept`: `light.turn_on` çağrılarını yakalar ve renk ile parlaklığın anında uyarlanmasını sağlar. 🏎️ Renk ve parlaklığı desteklemeyen ışıklar için devre dışı bırakın.",
+ "lights": "`lights`: Kontrol edilecek ışıkların entity_id listesi (boş bırakılabilir). 🌟",
+ "max_brightness": "`max_brightness`: Maksimum parlaklık yüzdesi. 💡",
+ "max_color_temp": "`max_color_temp`: En düşük renk sıcaklığı (Kelvin cinsinden). ❄️",
+ "min_brightness": "min_brightness: Minimum parlaklık yüzdesi.💡",
+ "min_color_temp": "`min_color_temp`: En yüksek (sıcak) renk sıcaklığı (Kelvin cinsinden). 🔥",
+ "multi_light_intercept": "`multi_light_intercept`: Birden fazla ışığı hedefleyen `light.turn_on` çağrılarını yakalar ve uyarlama yapar. ➗⚠️ Bu, örneğin ışıklar farklı anahtarlardaysa tek bir `light.turn_on` çağrısının birden fazla çağrıya bölünmesine yol açabilir. `intercept` etkin olmalıdır.",
+ "only_once": "`only_once`: Işıkları yalnızca açıldıklarında mı uyarlasın (`true`), yoksa sürekli uyarlamaya devam mı etsin (`false`). 🔄",
+ "prefer_rgb_color": "`prefer_rgb_color`: Mümkünse ışık renk sıcaklığı yerine RGB renk ayarını tercih edip etmeyeceğini belirler. 🌈",
+ "separate_turn_on_commands": "`separate_turn_on_commands`: Renk ve parlaklık için ayrı `light.turn_on` çağrıları kullanır; bazı ışık türleri için gereklidir. 🔀",
+ "skip_redundant_commands": "`skip_redundant_commands`: Hedef durumu ışığın bilinen durumu ile aynı olan uyarlama komutlarını atlar. Ağ trafiğini azaltır ve bazı durumlarda uyarlamanın yanıt hızını artırır. 📉 \nFiziksel ışık durumları HA’daki kaydedilen durumla senkronize değilse devre dışı bırakın.",
+ "take_over_control": "`take_over_control`: Işıklar açıkken ve uyarlanırken başka bir kaynaktan `light.turn_on` çağrılırsa Adaptive Lighting’i devre dışı bırakır. Dikkat: Bu işlem her `interval` süresinde `homeassistant.update_entity` çağrısı yapar! 🔒",
+ "transition_until_sleep": "`transition_until_sleep`: Etkinleştirildiğinde, Adaptive Lighting uyku ayarlarını minimum değer olarak kabul eder ve gün batımından sonra bu değerlere geçiş yapar. 🌙"
+ },
+ "data_description": {
+ "sunrise_offset": "Gün doğumu saatini, saniye cinsinden pozitif veya negatif bir kaydırma ile ayarlayın. ⏰",
+ "sunset_offset": "Gün batımı saatini, saniye cinsinden pozitif veya negatif bir kaydırma ile ayarlayın. ⏰",
+ "autoreset_control_seconds": "Manuel kontrolü belirtilen saniye sonunda otomatik olarak sıfırlar. Devre dışı bırakmak için 0 olarak ayarlayın. ⏲️",
+ "brightness_mode": "Kullanılacak parlaklık modunu belirtir. Olası değerler: `default`, `linear` ve `tanh` (`brightness_mode_time_dark` ve `brightness_mode_time_light` ayarlarını kullanır). 📈",
+ "sleep_brightness": "Uyku modundayken ışıkların parlaklık yüzdesi 😴",
+ "sleep_color_temp": "Uyku modunda renk sıcaklığı ( `sleep_rgb_or_color_temp` `color_temp` olarak ayarlandığında kullanılır) Kelvin cinsinden. 😴",
+ "send_split_delay": "Parlaklık ve renk ayarını aynı anda desteklemeyen ışıklar için `separate_turn_on_commands` arasındaki gecikme (ms). ⏲️",
+ "initial_transition": "Işıklar `off` durumundan `on` durumuna geçerken ilk geçişin süresi (saniye cinsinden). ⏲️",
+ "transition": "Işıklar değişirken geçiş süresi (saniye cinsinden). 🕑",
+ "sleep_transition": "“Uyku modu” açılıp kapatıldığında geçiş süresi (saniye cinsinden). 😴",
+ "interval": "Işıkların uyarlanma sıklığı (saniye cinsinden). 🔄",
+ "brightness_mode_time_light": "(`brightness_mode='default'` ise göz ardı edilir) Gün doğumu/gün batımı öncesi/sonrası parlaklığı kademeli olarak artırma/azaltma süresi (saniye cinsinden). 📈📉",
+ "brightness_mode_time_dark": "(`brightness_mode='default'` ise göz ardı edilir) Gün doğumu/gün batımı öncesi/sonrası parlaklığı kademeli olarak artırma/azaltma süresi (saniye cinsinden). 📈📉",
+ "sleep_rgb_color": "Uyku modunda RGB renk ( `sleep_rgb_or_color_temp` \"rgb_color\" olarak ayarlandığında kullanılır). 🌈",
+ "sunrise_time": "Gün doğumu için sabit bir saat (SS:DD:YY) belirleyin. 🌅",
+ "sunset_time": "Gün batımı için sabit bir saat (SS:DD:YY) belirleyin. 🌇",
+ "min_sunrise_time": "En erken sanal gün doğumu saatini (SS:DD:YY) belirleyin; daha geç gün doğumlarına izin verir. 🌅",
+ "min_sunset_time": "En erken sanal gün batımı saatini (SS:DD:YY) belirleyin; daha geç gün batımlarına izin verir. 🌇",
+ "max_sunrise_time": "En geç sanal gün doğumu saatini (SS:DD:YY) belirleyin; daha erken gün doğumlarına izin verir. 🌅",
+ "max_sunset_time": "En geç sanal gün batımı saatini (SS:DD:YY) belirleyin; daha erken gün batımlarına izin verir. 🌇",
+ "sleep_rgb_or_color_temp": "Uyku modunda `\"rgb_color\"` veya `\"color_temp\"` kullanın. 🌙",
+ "adapt_delay": "Işık açıldıktan sonra Adaptive Lighting’in değişiklikleri uygulamasına kadar bekleme süresi (saniye cinsinden). Titremeyi önlemeye yardımcı olabilir. ⏲️"
+ },
+ "description": "Bir Adaptive Lighting bileşenini yapılandırın. Seçenek adları YAML ayarlarıyla uyumludur. Eğer bu girdiyi YAML’da tanımladıysanız, burada seçenekler görünmez. \nParametrelerin etkilerini gösteren etkileşimli grafikler için [bu web uygulamasını](https://basnijholt.github.io/adaptive-lighting) ziyaret edebilirsiniz. Daha fazla bilgi için [resmi dokümantasyona](https://github.com/basnijholt/adaptive-lighting#readme) bakın."
+ }
+ },
+ "error": {
+ "option_error": "Geçersiz seçenek",
+ "entity_missing": "Seçilen bir veya birden fazla ışık entity’si Home Assistant’ta bulunamadı."
+ }
+ },
+ "services": {
+ "change_switch_settings": {
+ "fields": {
+ "only_once": {
+ "description": "Işıkları yalnızca açıkken (`true`) uyarla, veya sürekli olarak uyarlamaya devam et (`false`).🔄"
+ },
+ "sunrise_offset": {
+ "description": "Gün doğumu saatini, saniye cinsinden pozitif veya negatif bir kaydırma ile ayarlayın. ⏰"
+ },
+ "sunset_offset": {
+ "description": "Gün batımı saatini, saniye cinsinden pozitif veya negatif bir kaydırma ile ayarlayın. ⏰"
+ },
+ "autoreset_control_seconds": {
+ "description": "Manuel kontrolü belirtilen saniye sonunda otomatik olarak sıfırlar. Devre dışı bırakmak için 0 olarak ayarlayın. ⏲️"
+ },
+ "sleep_brightness": {
+ "description": "Uyku modundayken ışıkların parlaklık yüzdesi 😴"
+ },
+ "max_color_temp": {
+ "description": "Kelvin cinsinden en düşük renk sıcaklığı. ❄️"
+ },
+ "sleep_color_temp": {
+ "description": "Uyku modunda renk sıcaklığı ( `sleep_rgb_or_color_temp` `color_temp` olarak ayarlandığında kullanılır) Kelvin cinsinden. 😴"
+ },
+ "send_split_delay": {
+ "description": "Parlaklık ve renk ayarını aynı anda desteklemeyen ışıklar için `separate_turn_on_commands` arasındaki gecikme (ms). ⏲️"
+ },
+ "detect_non_ha_changes": {
+ "description": "`light.turn_on` dışındaki durum değişikliklerini algılar ve uyarlamayı durdurur. Bu ayarın çalışması için `take_over_control` etkin olmalıdır. 🕵️ \nDikkat: ⚠️ Bazı ışıklar yanlışlıkla “açık” durumunu bildirebilir, bu da ışıkların beklenmedik şekilde açılmasına neden olabilir. Böyle bir durumda bu özelliği devre dışı bırakın."
+ },
+ "take_over_control": {
+ "description": "Işıklar açıkken ve uyarlanırken başka bir kaynaktan `light.turn_on` çağrılırsa Adaptive Lighting’i devre dışı bırakır. Dikkat: Bu işlem her `interval` süresinde `homeassistant.update_entity` çağrısı yapar! 🔒"
+ },
+ "initial_transition": {
+ "description": "Işıklar `off` durumundan `on` durumuna geçerken ilk geçişin süresi (saniye cinsinden). ⏲️"
+ },
+ "transition": {
+ "description": "Işıklar değişirken geçiş süresi (saniye cinsinden). 🕑"
+ },
+ "sleep_transition": {
+ "description": "“Uyku modu” açılıp kapatıldığında geçiş süresi (saniye cinsinden). 😴"
+ },
+ "entity_id": {
+ "description": "Anahtarın Entity ID’si. 📝"
+ },
+ "max_brightness": {
+ "description": "Maksimum parlaklık yüzdesi.💡"
+ },
+ "min_brightness": {
+ "description": "Minimum parlaklık yüzdesi.💡"
+ },
+ "sleep_rgb_color": {
+ "description": "Uyku modunda RGB renk ( `sleep_rgb_or_color_temp` \"rgb_color\" olarak ayarlandığında kullanılır). 🌈"
+ },
+ "sunrise_time": {
+ "description": "Gün doğumu için sabit bir saat (SS:DD:YY) belirleyin. 🌅"
+ },
+ "sunset_time": {
+ "description": "Gün batımı için sabit bir saat (SS:DD:YY) belirleyin. 🌇"
+ },
+ "use_defaults": {
+ "description": "Bu servis çağrısında belirtilmeyen varsayılan değerleri ayarlar. Seçenekler: \n- `current` (varsayılan, mevcut değerleri korur) \n- `factory` (belgelendirilmiş varsayılanlara sıfırlar) \n- `configuration` (anahtar yapılandırma varsayılanlarına döner) ⚙️"
+ },
+ "min_sunset_time": {
+ "description": "En erken sanal gün batımı saatini (SS:DD:YY) belirleyin; daha geç gün batımlarına izin verir. 🌇"
+ },
+ "max_sunrise_time": {
+ "description": "En geç sanal gün doğumu saatini (SS:DD:YY) belirleyin; daha erken gün doğumlarına izin verir. 🌅"
+ },
+ "include_config_in_attributes": {
+ "description": "`true` olarak ayarlandığında, tüm seçenekleri Home Assistant’ta anahtarın attribute’ları olarak gösterir. 📝"
+ },
+ "sleep_rgb_or_color_temp": {
+ "description": "Uyku modunda `\"rgb_color\"` veya `\"color_temp\"` kullanın. 🌙"
+ },
+ "separate_turn_on_commands": {
+ "description": "Renk ve parlaklık için ayrı `light.turn_on` çağrıları kullanın; bazı ışık türleri için gereklidir. 🔀"
+ },
+ "min_color_temp": {
+ "description": "En yüksek (sıcak) renk sıcaklığı (Kelvin cinsinden). 🔥"
+ },
+ "prefer_rgb_color": {
+ "description": "Mümkün olduğunda ışık renk sıcaklığı yerine RGB renk ayarını tercih edip etmeyeceğini belirler. 🌈"
+ },
+ "turn_on_lights": {
+ "description": "Şu anda kapalı olan ışıkların açılıp açılmayacağını belirler. 🔆"
+ },
+ "adapt_delay": {
+ "description": "Işık açıldıktan sonra Adaptive Lighting’in değişiklikleri uygulamasına kadar bekleme süresi (saniye cinsinden). Titremeyi önlemeye yardımcı olabilir. ⏲️"
+ }
+ },
+ "description": "Anahtardaki tüm ayarları dilediğiniz gibi değiştirebilirsiniz. Buradaki seçeneklerin hepsi, yapılandırma akışındakilerle aynıdır."
+ },
+ "apply": {
+ "fields": {
+ "lights": {
+ "description": "Ayarları bir veya birden fazla ışığa uygula.💡"
+ },
+ "transition": {
+ "description": "Işıklar değişirken geçiş süresi (saniye cinsinden). 🕑"
+ },
+ "entity_id": {
+ "description": "Uygulanacak ayarların bulunduğu anahtarın `entity_id`’si. 📝"
+ },
+ "adapt_brightness": {
+ "description": "Işığın parlaklığının uyarlanıp uyarlanmayacağını belirler. 🌞"
+ },
+ "adapt_color": {
+ "description": "Destekleyen ışıklarda rengin uyarlanıp uyarlanmayacağını belirler. 🌈"
+ },
+ "prefer_rgb_color": {
+ "description": "Mümkün olduğunda ışık renk sıcaklığı yerine RGB renk ayarını tercih edip etmeyeceğini belirler. 🌈"
+ },
+ "turn_on_lights": {
+ "description": "Şu anda kapalı olan ışıkların açılıp açılmayacağını belirler. 🔆"
+ }
+ },
+ "description": "Şu anki Akıllı Işıklandırma ayarlarını ışıklara uygular."
+ },
+ "set_manual_control": {
+ "fields": {
+ "lights": {
+ "description": "Işıkların entity_id(leri). Belirtilmezse, anahtardaki tüm ışıklar seçilir. 💡"
+ },
+ "entity_id": {
+ "description": "Işığın “manuel olarak kontrol edildiğini” işaretlemek veya kaldırmak için kullanılacak anahtarın `entity_id`’si. 📝"
+ },
+ "manual_control": {
+ "description": "Işığı “manuel kontrol” listesinden eklemek (`true`) veya çıkarmak (`false`) için kullanılır. 🔒"
+ }
+ },
+ "description": "Bir ışığın 'manuel olarak kontrol' edilip edilmediğini işaretleyin."
+ }
+ },
+ "config": {
+ "step": {
+ "user": {
+ "title": "Akıllı ışıklandırma örneği için bir ad seçin.",
+ "description": "Her örnek birden fazla ışık içerebilir!"
+ }
+ },
+ "abort": {
+ "already_configured": "Bu cihaz zaten ayarlanmış."
+ }
+ }
+}
diff --git a/custom_components/adaptive_lighting/translations/uk.json b/custom_components/adaptive_lighting/translations/uk.json
index ee12b0e9..749271d5 100644
--- a/custom_components/adaptive_lighting/translations/uk.json
+++ b/custom_components/adaptive_lighting/translations/uk.json
@@ -42,7 +42,9 @@
"adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: На початку вмикання світла. Якщо `true`, освітлення адаптується лише якщо `light.turn_on` викликано без вказання кольору чи яскравості. ❌🌈 Це, наприклад, запобігає адаптації, коли сцена активується. Якщо `false`, освітлення адаптується незалежно від наявності кольору чи яскравості у початковому `service_data`. Потребує ввімкнення `take_over_control`. 🕵️",
"transition_until_sleep": "transition_until_sleep: Коли активовано, адаптивне освітлення буде ставитись до налаштування сну як мінімум, переходячи до цих значень після заходу сонця. 🌙",
"intercept": "intercept: Перехоплювати та адаптувати виклики увімкнення світла (`light.turn_on`), щоб увімкнути миттєву адаптацію кольору та яскравості. 🏎️ Вимкніть для світла, що не підтримує увімкнення світла (`light.turn_on`) з кольором та яскравістю.",
- "include_config_in_attributes": "Включити конфігурацію в атрибути (`include_config_in_attributes`): Показувати всі опції як атрибути на перемикачі в Home Assistant, якщо встановлено значення `true`. 📝"
+ "include_config_in_attributes": "Включити конфігурацію в атрибути (`include_config_in_attributes`): Показувати всі опції як атрибути на перемикачі в Home Assistant, якщо встановлено значення `true`. 📝",
+ "multi_light_intercept": "multi_light_intercept: Перехоплення та адаптація викликів `light.turn_on`, які спрямовані на кілька світильників. ➗⚠️ Це може призвести до розділення одного виклику `light.turn_on` на кілька викликів, наприклад, коли світильники підключені до різних вимикачів. Потрібно ввімкнути `intercept`.",
+ "skip_redundant_commands": "skip_redundant_commands: Пропускати надсилання команд адаптації, цільовий стан яких вже дорівнює відомому стану освітлення. Мінімізує мережевий трафік і покращує швидкість реагування адаптації в деяких ситуаціях. 📉Вимкнути, якщо фізичний стан освітлення не синхронізується із записаним станом HA."
},
"data_description": {
"sunrise_offset": "Змінити час сходу сонця на +/- секунд. ⏰",
@@ -57,7 +59,16 @@
"interval": "Частота адаптації освітлення, у секундах. 🔄",
"sleep_brightness": "Відсоток яскравості світла в режимі сну. 😴",
"sleep_color_temp": "Колірна температура в режимі сну (використовується, коли `sleep_rgb_or_color_temp` має значення `color_temp`) у Кельвінах. 😴",
- "sleep_transition": "Тривалість переходу, коли режим сну \"sleep mode\" увімкнено, у секундах. 😴"
+ "sleep_transition": "Тривалість переходу, коли режим сну \"sleep mode\" увімкнено, у секундах. 😴",
+ "sleep_rgb_color": "Колір RGB у режимі сну (використовується, коли `sleep_rgb_or_color_temp` має значення \"rgb_color\"). 🌈",
+ "sunrise_time": "Встановіть фіксований час (ГГ:ХХ:СС) для сходу сонця. 🌅",
+ "sunset_time": "Встановіть фіксований час (ГГ:ХХ:СС) для заходу сонця. 🌇",
+ "min_sunrise_time": "Встановіть найраніший час віртуального сходу сонця (ГГ:ХХ:СС), враховуючи пізніші сходи. 🌅",
+ "min_sunset_time": "Встановіть найраніший час віртуального заходу сонця (ГГ:ХХ:СС), враховуючи пізніші заходи сонця. 🌇",
+ "max_sunrise_time": "Встановіть найпізніший час віртуального сходу сонця (ГГ:ХХ:СС), враховуючи більш ранні сходи сонця. 🌅",
+ "max_sunset_time": "Встановіть найновіший час віртуального заходу сонця (ГГ:ХХ:СС), враховуючи більш ранні заходи сонця. 🌇",
+ "sleep_rgb_or_color_temp": "Використовуйте `\"rgb_color\"` або `\"color_temp\"` у режимі сну. 🌙",
+ "adapt_delay": "Час очікування (секунди) між увімкненням світла та застосуванням змін системою адаптивного освітлення. Може допомогти уникнути мерехтіння. ⏲️"
}
}
},
@@ -75,6 +86,21 @@
},
"transition": {
"description": "Тривалість переходу, коли світло змінюється, у секундах. 🕑"
+ },
+ "entity_id": {
+ "description": "`entity_id` перемикача з налаштуваннями, які потрібно застосувати. 📝"
+ },
+ "adapt_brightness": {
+ "description": "Чи потрібно адаптувати яскравість світла. 🌞"
+ },
+ "adapt_color": {
+ "description": "Чи адаптувати колір допоміжних ламп. 🌈"
+ },
+ "prefer_rgb_color": {
+ "description": "Чи надавати перевагу налаштуванню кольору RGB над температурою кольору світла, коли це можливо. 🌈"
+ },
+ "turn_on_lights": {
+ "description": "Чи вмикати світло, яке наразі вимкнене. 🔆"
}
}
},
@@ -127,6 +153,45 @@
},
"transition": {
"description": "Тривалість переходу, коли світло змінюється, у секундах. 🕑"
+ },
+ "sleep_rgb_color": {
+ "description": "Колір RGB у режимі сну (використовується, коли `sleep_rgb_or_color_temp` має значення \"rgb_color\"). 🌈"
+ },
+ "sunrise_time": {
+ "description": "Встановіть фіксований час (ГГ:ХХ:СС) для сходу сонця. 🌅"
+ },
+ "sunset_time": {
+ "description": "Встановіть фіксований час (ГГ:ХХ:СС) для заходу сонця. 🌇"
+ },
+ "use_defaults": {
+ "description": "Встановлює значення за замовчуванням, не вказані в цьому виклику служби. Параметри: «current» (за замовчуванням, зберігає поточні значення), «factory» (скидає до задокументованих значень за замовчуванням) або «configuration» (повертає до значень за замовчуванням конфігурації комутатора). ⚙️"
+ },
+ "min_sunset_time": {
+ "description": "Встановіть найраніший час віртуального заходу сонця (ГГ:ХХ:СС), враховуючи пізніші заходи сонця. 🌇"
+ },
+ "max_sunrise_time": {
+ "description": "Встановіть найпізніший час віртуального сходу сонця (ГГ:ХХ:СС), враховуючи більш ранні сходи сонця. 🌅"
+ },
+ "include_config_in_attributes": {
+ "description": "Показувати всі опції як атрибути перемикача в Домашньому помічнику, якщо встановлено значення `true`. 📝"
+ },
+ "sleep_rgb_or_color_temp": {
+ "description": "Використовуйте `\"rgb_color\"` або `\"color_temp\"` у режимі сну. 🌙"
+ },
+ "separate_turn_on_commands": {
+ "description": "Використовуйте окремі виклики `light.turn_on` для кольору та яскравості, що необхідно для деяких типів освітлення. 🔀"
+ },
+ "adapt_delay": {
+ "description": "Час очікування (секунди) між увімкненням світла та застосуванням змін системою адаптивного освітлення. Може допомогти уникнути мерехтіння. ⏲️"
+ },
+ "min_color_temp": {
+ "description": "Найтепліша колірна температура в Кельвінах. 🔥"
+ },
+ "prefer_rgb_color": {
+ "description": "Чи надавати перевагу налаштуванню кольору RGB над температурою кольору світла, коли це можливо. 🌈"
+ },
+ "turn_on_lights": {
+ "description": "Чи вмикати світло, яке наразі вимкнене. 🔆"
}
},
"description": "Змініть будь-які налаштування, які ви бажаєте, у цьому перемикачі. Усі опції тут такі ж, як і в поточному конфігураційному файлі."
@@ -136,6 +201,12 @@
"fields": {
"lights": {
"description": "Ідентифікатор(и) світла (entity_id(s) of lights). Якщо не вказано, вибираються всі лампи у перемикачі. 💡"
+ },
+ "entity_id": {
+ "description": "`entity_id` перемикача, в якому потрібно (зняти) позначку світла як `керованого вручну`. 📝"
+ },
+ "manual_control": {
+ "description": "Додавати (\"true\") чи видаляти (\"false\") світло зі списку \"manual_control\". 🔒"
}
}
}
diff --git a/scripts/setup-dependencies b/scripts/setup-dependencies
index b9e2de1f..92a41411 100755
--- a/scripts/setup-dependencies
+++ b/scripts/setup-dependencies
@@ -6,6 +6,22 @@ if grep -q 'mypy-dev==1.14.0a3' core/requirements_test.txt; then
# mypy-dev==1.14.0a3 seems to not be available anymore, HA 2024.12 is affected
sed -i 's/mypy-dev==1.14.0a3/mypy-dev==1.14.0a7/' core/requirements_test.txt
fi
+if grep -q 'mypy-dev==1.16.0a1' core/requirements_test.txt; then
+ # mypy-dev==1.16.0a1 seems to not be available anymore, HA 2025.2 is affected
+ sed -i 's/mypy-dev==1.16.0a1/mypy-dev==1.16.0a9/' core/requirements_test.txt
+fi
+if grep -q 'mypy-dev==1.16.0a3' core/requirements_test.txt; then
+ # mypy-dev==1.16.0a3 seems to not be available anymore, HA 2025.3 is affected
+ sed -i 's/mypy-dev==1.16.0a3/mypy-dev==1.16.0a9/' core/requirements_test.txt
+fi
+if grep -q 'mypy-dev==1.16.0a7' core/requirements_test.txt; then
+ # mypy-dev==1.16.0a7 seems to not be available anymore, HA 2025.4 is affected
+ sed -i 's/mypy-dev==1.16.0a7/mypy-dev==1.16.0a9/' core/requirements_test.txt
+fi
+if grep -q 'mypy-dev==1.16.0a8' core/requirements_test.txt; then
+ # mypy-dev==1.16.0a8 seems to not be available anymore, HA 2025.5 and 2025.6 is affected
+ sed -i 's/mypy-dev==1.16.0a8/mypy-dev==1.16.0a9/' core/requirements_test.txt
+fi
uv pip install -r core/requirements.txt
uv pip install -r core/requirements_test.txt
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 00000000..cce77d96
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,26 @@
+"""Pytest configuration for adaptive-lighting tests."""
+
+from unittest.mock import patch
+
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def mock_template_deprecation_issue():
+ """Mock the template deprecation issue creation.
+
+ The template component's legacy platform syntax creates deprecation
+ issues that require translations. Since adaptive-lighting tests use
+ template lights as test fixtures (not testing the template integration
+ itself), we mock the issue creation to avoid translation validation errors.
+ """
+ # Patch the create_legacy_template_issue function in the template helpers
+ # to be a no-op when called for the deprecated_legacy_templates issue
+ try:
+ with patch(
+ "homeassistant.components.template.helpers.create_legacy_template_issue",
+ ):
+ yield
+ except (ImportError, ModuleNotFoundError, AttributeError):
+ # Older HA versions don't have this function
+ yield
diff --git a/tests/test_switch.py b/tests/test_switch.py
index cc03fa24..5011ebc6 100644
--- a/tests/test_switch.py
+++ b/tests/test_switch.py
@@ -65,6 +65,7 @@ from homeassistant.components.adaptive_lighting.switch import (
CONF_INTERCEPT,
AdaptiveLightingManager,
AdaptiveSwitch,
+ SimpleSwitch,
_attributes_have_changed,
color_difference_redmean,
create_context,
@@ -82,7 +83,16 @@ from homeassistant.components.light import (
)
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
-from homeassistant.components.template.light import LightTemplate
+
+try:
+ # HA >= 2025.8
+ from homeassistant.components.template.light import (
+ StateLightEntity as LightTemplate,
+ )
+except ImportError:
+ # HA < 2025.8
+ from homeassistant.components.template.light import LightTemplate
+
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import (
ATTR_AREA_ID,
@@ -1000,26 +1010,36 @@ def test_attributes_have_changed():
new_attributes=attrs,
**kwargs,
)
- _LOGGER.debug("Test switch from color_temp to rgb_color")
- assert not _attributes_have_changed(
+ # Test color mode switches - feature added to detect external changes
+ # (e.g., when Hue scenes change light from color_temp to RGB mode)
+ # See: https://github.com/basnijholt/adaptive-lighting/issues/1275
+ #
+ # All mode switches are now detected bidirectionally by checking original
+ # attributes BEFORE conversion in _has_color_mode_changed().
+ _LOGGER.debug(
+ "Test switch from color_temp to rgb_color - should detect mode change",
+ )
+ assert _attributes_have_changed(
old_attributes={ATTR_BRIGHTNESS: 1, ATTR_COLOR_TEMP_KELVIN: 2702},
new_attributes={ATTR_BRIGHTNESS: 1, ATTR_RGB_COLOR: (255, 166, 87)},
**kwargs,
)
- _LOGGER.debug("Test switch from rgb_color to color_temp")
- assert not _attributes_have_changed(
+ _LOGGER.debug(
+ "Test switch from rgb_color to color_temp - should detect mode change",
+ )
+ assert _attributes_have_changed(
old_attributes={ATTR_BRIGHTNESS: 1, ATTR_RGB_COLOR: (255, 166, 87)},
new_attributes={ATTR_BRIGHTNESS: 1, ATTR_COLOR_TEMP_KELVIN: 2702},
**kwargs,
)
- _LOGGER.debug("Test switch from color_temp to color_xy")
- assert not _attributes_have_changed(
+ _LOGGER.debug("Test switch from color_temp to color_xy - should detect mode change")
+ assert _attributes_have_changed(
old_attributes={ATTR_BRIGHTNESS: 1, ATTR_COLOR_TEMP_KELVIN: 2702},
new_attributes={ATTR_BRIGHTNESS: 1, ATTR_XY_COLOR: (0.526, 0.387)},
**kwargs,
)
- _LOGGER.debug("Test switch from color_xy to color_temp")
- assert not _attributes_have_changed(
+ _LOGGER.debug("Test switch from color_xy to color_temp - should detect mode change")
+ assert _attributes_have_changed(
old_attributes={ATTR_BRIGHTNESS: 1, ATTR_XY_COLOR: (0.526, 0.387)},
new_attributes={ATTR_BRIGHTNESS: 1, ATTR_COLOR_TEMP_KELVIN: 2702},
**kwargs,
@@ -2266,3 +2286,175 @@ async def test_brightness_mode(hass, brightness_mode, dark, light):
# After sunrise the brightness should be light_brightness
await patch_time_and_update(after_sunrise)
assert is_approx_equal(switch._settings[ATTR_BRIGHTNESS_PCT], light_brightness)
+
+
+async def test_simple_switch_initial_state_not_none(hass):
+ """Test that SimpleSwitch._state is not None after __init__.
+
+ Regression test for https://github.com/basnijholt/adaptive-lighting/issues/1264
+
+ When an entity is disabled in Home Assistant, async_added_to_hass() is never
+ called. Previously, SimpleSwitch._state was initialized to None and only set
+ to True/False in async_added_to_hass(). This caused an infinite loop in
+ AdaptiveSwitch._setup_listeners() which waits for all SimpleSwitch._state
+ to be not None.
+
+ The fix is to initialize _state to the initial_state value in __init__.
+ """
+ entry = MockConfigEntry(domain=DOMAIN, data={CONF_NAME: DEFAULT_NAME})
+ entry.add_to_hass(hass)
+
+ # Create a SimpleSwitch without calling async_added_to_hass
+ # (simulating a disabled entity)
+ switch = SimpleSwitch(
+ which="Test",
+ initial_state=True,
+ hass=hass,
+ config_entry=entry,
+ icon="mdi:test",
+ )
+
+ # Before the fix: _state would be None, causing infinite loop
+ # After the fix: _state should be the initial_state value
+ assert switch._state is not None, (
+ "SimpleSwitch._state should not be None after __init__. "
+ "This would cause an infinite loop in _setup_listeners when the entity is disabled."
+ )
+ assert switch._state is True # Should be the initial_state value
+
+
+async def test_simple_switch_state_after_async_added_to_hass(hass):
+ """Test that SimpleSwitch._state is properly set after async_added_to_hass.
+
+ This ensures the fix for #1264 doesn't break normal entity initialization.
+ """
+ entry = MockConfigEntry(domain=DOMAIN, data={CONF_NAME: DEFAULT_NAME})
+ entry.add_to_hass(hass)
+
+ # Create switches with different initial states
+ switch_true = SimpleSwitch(
+ which="Test True",
+ initial_state=True,
+ hass=hass,
+ config_entry=entry,
+ icon="mdi:test",
+ )
+ switch_false = SimpleSwitch(
+ which="Test False",
+ initial_state=False,
+ hass=hass,
+ config_entry=entry,
+ icon="mdi:test",
+ )
+
+ # Verify initial state is set correctly
+ assert switch_true._state is True
+ assert switch_false._state is False
+
+ # Call async_added_to_hass (simulating normal entity setup)
+ # Since there's no last state, it should use the initial_state
+ await switch_true.async_added_to_hass()
+ await switch_false.async_added_to_hass()
+
+ # State should still be correct after async_added_to_hass
+ assert switch_true._state is True
+ assert switch_false._state is False
+
+
+def test_attributes_have_changed_light_mode_switch():
+ """Test detection of external light mode changes (color_temp vs rgb vs xy).
+
+ Regression test for https://github.com/basnijholt/adaptive-lighting/issues/1275
+
+ When a user activates a Hue Scene (or similar) via an external app, the light
+ may switch from color_temp mode to RGB/XY mode (or vice versa). This should be
+ detected as an external change so AL doesn't immediately override it.
+
+ The _has_color_mode_changed() function checks the original attributes BEFORE
+ any conversion, enabling bidirectional mode change detection.
+ """
+ context = Context()
+ base_kwargs = {
+ "light": "light.test",
+ "adapt_brightness": True,
+ "context": context,
+ }
+
+ # Test 1: adapt_color=True - all mode changes should be detected
+ kwargs_adapt_color = {**base_kwargs, "adapt_color": True}
+
+ # color_temp → RGB
+ assert _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
+ **kwargs_adapt_color,
+ ), "Should detect color_temp → RGB mode switch"
+
+ # color_temp → XY
+ assert _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_XY_COLOR: (0.64, 0.33)},
+ **kwargs_adapt_color,
+ ), "Should detect color_temp → XY mode switch"
+
+ # RGB → color_temp
+ assert _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
+ **kwargs_adapt_color,
+ ), "Should detect RGB → color_temp mode switch"
+
+ # RGB → XY
+ assert _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_XY_COLOR: (0.64, 0.33)},
+ **kwargs_adapt_color,
+ ), "Should detect RGB → XY mode switch"
+
+ # XY → color_temp
+ assert _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_XY_COLOR: (0.64, 0.33)},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
+ **kwargs_adapt_color,
+ ), "Should detect XY → color_temp mode switch"
+
+ # XY → RGB
+ assert _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_XY_COLOR: (0.64, 0.33)},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
+ **kwargs_adapt_color,
+ ), "Should detect XY → RGB mode switch"
+
+ # No mode change - same type with same values shouldn't be detected
+ assert not _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
+ **kwargs_adapt_color,
+ ), "Same color_temp should not be detected as change"
+
+ assert not _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
+ **kwargs_adapt_color,
+ ), "Same RGB should not be detected as change"
+
+ assert not _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_XY_COLOR: (0.64, 0.33)},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_XY_COLOR: (0.64, 0.33)},
+ **kwargs_adapt_color,
+ ), "Same XY should not be detected as change"
+
+ # Test 2: adapt_color=False - mode changes should NOT be detected
+ kwargs_no_adapt = {**base_kwargs, "adapt_color": False}
+
+ assert not _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
+ **kwargs_no_adapt,
+ ), "Mode change should not be detected when adapt_color=False"
+
+ assert not _attributes_have_changed(
+ old_attributes={ATTR_BRIGHTNESS: 128, ATTR_RGB_COLOR: (255, 0, 0)},
+ new_attributes={ATTR_BRIGHTNESS: 128, ATTR_COLOR_TEMP_KELVIN: 4000},
+ **kwargs_no_adapt,
+ ), "RGB → color_temp should not be detected when adapt_color=False"