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 @@ [![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](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-) # 🌞 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
Luna Jernberg

🌍 Jeff Wilson
Jeff Wilson

💻 Rasmus Lundsgaard
Rasmus Lundsgaard

💻 + Tom Matheussen
Tom Matheussen

💻 + ams2990
ams2990

💻 + DataGhost
DataGhost

💻 + + + Furkan Kaya
Furkan Kaya

🌍 + Rafael do Amaral Porciuncula
Rafael do Amaral Porciuncula

🌍 + hhjuhl
hhjuhl

🌍 + B.Athish
B.Athish

🌍 + Горпиніч Максим Олександрович
Горпиніч Максим Олександрович

🌍 + Masayuki Sugahara
Masayuki Sugahara

🌍 + therealmate
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"