Merge main into friendly_names_and_types and fix lint issues

This commit is contained in:
Bas Nijholt 2025-11-27 10:33:14 -08:00
commit 4398ef4deb
No known key found for this signature in database
31 changed files with 885 additions and 67 deletions

View file

@ -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,

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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: |

View file

@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -1,7 +1,7 @@
[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/hacs/integration)
![Version](https://img.shields.io/github/v/release/basnijholt/adaptive-lighting?style=for-the-badge)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-116-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-126-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
# 🌞 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
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bittin"><img src="https://avatars.githubusercontent.com/u/43197?v=4?s=100" width="100px;" alt="Luna Jernberg"/><br /><sub><b>Luna Jernberg</b></sub></a><br /><a href="#translation-bittin" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://jeffalwilson.com"><img src="https://avatars.githubusercontent.com/u/1368827?v=4?s=100" width="100px;" alt="Jeff Wilson"/><br /><sub><b>Jeff Wilson</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=jawilson" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TermeHansen"><img src="https://avatars.githubusercontent.com/u/6922018?v=4?s=100" width="100px;" alt="Rasmus Lundsgaard"/><br /><sub><b>Rasmus Lundsgaard</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=TermeHansen" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Tommatheussen"><img src="https://avatars.githubusercontent.com/u/13683094?v=4?s=100" width="100px;" alt="Tom Matheussen"/><br /><sub><b>Tom Matheussen</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=Tommatheussen" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ams2990"><img src="https://avatars.githubusercontent.com/u/488907?v=4?s=100" width="100px;" alt="ams2990"/><br /><sub><b>ams2990</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=ams2990" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DataGhost"><img src="https://avatars.githubusercontent.com/u/3911340?v=4?s=100" width="100px;" alt="DataGhost"/><br /><sub><b>DataGhost</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=DataGhost" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://iamfurkan.com"><img src="https://avatars.githubusercontent.com/u/23127261?v=4?s=100" width="100px;" alt="Furkan Kaya"/><br /><sub><b>Furkan Kaya</b></sub></a><br /><a href="#translation-Wijt" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Rafael4A"><img src="https://avatars.githubusercontent.com/u/32150173?v=4?s=100" width="100px;" alt="Rafael do Amaral Porciuncula"/><br /><sub><b>Rafael do Amaral Porciuncula</b></sub></a><br /><a href="#translation-Rafael4A" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hhjuhl"><img src="https://avatars.githubusercontent.com/u/84127693?v=4?s=100" width="100px;" alt="hhjuhl"/><br /><sub><b>hhjuhl</b></sub></a><br /><a href="#translation-hhjuhl" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Athishbalu"><img src="https://avatars.githubusercontent.com/u/177029556?v=4?s=100" width="100px;" alt="B.Athish"/><br /><sub><b>B.Athish</b></sub></a><br /><a href="#translation-Athishbalu" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/maksim2005UKR"><img src="https://avatars.githubusercontent.com/u/233082001?v=4?s=100" width="100px;" alt="Горпиніч Максим Олександрович"/><br /><sub><b>Горпиніч Максим Олександрович</b></sub></a><br /><a href="#translation-maksim2005UKR" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://plageoj.me"><img src="https://avatars.githubusercontent.com/u/10688301?v=4?s=100" width="100px;" alt="Masayuki Sugahara"/><br /><sub><b>Masayuki Sugahara</b></sub></a><br /><a href="#translation-plageoj" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/therealmate"><img src="https://avatars.githubusercontent.com/u/61843503?v=4?s=100" width="100px;" alt="therealmate"/><br /><sub><b>therealmate</b></sub></a><br /><a href="#translation-therealmate" title="Translation">🌍</a></td>
</tr>
</tbody>
<tfoot>

View file

@ -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:

View file

@ -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:

View file

@ -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"]

View file

@ -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)

View file

@ -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 = (

View file

@ -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"
}

View file

@ -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:

View file

@ -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. 💡"
}
}
}
}
}

View file

@ -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. 🕑"
}
}
}

View file

@ -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. ❄️",

View file

@ -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": "るさの自動調整オプション"
}
}
}

View file

@ -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."
}
}
},

View file

@ -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`. 📝",

View file

@ -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"
}

View file

@ -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 Assistantta anahtarın attributeları 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ı HAdaki 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 Lightingi 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 Lightingin 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 YAMLda 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 entitysi Home Assistantta 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 Lightingi 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 IDsi. 📝"
},
"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 Assistantta anahtarın attributeları 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 Lightingin 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ıı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ış."
}
}
}

View file

@ -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\". 🔒"
}
}
}

View file

@ -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

26
tests/conftest.py Normal file
View file

@ -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

View file

@ -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"