diff --git a/.all-contributorsrc b/.all-contributorsrc index 091aba91..e2a901fd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1188,6 +1188,42 @@ "contributions": [ "doc" ] + }, + { + "login": "ademuri", + "name": "Adam DeMuri", + "avatar_url": "https://avatars.githubusercontent.com/u/3051618?v=4", + "profile": "https://github.com/ademuri", + "contributions": [ + "code" + ] + }, + { + "login": "NatanDosAnjos", + "name": "Natanael", + "avatar_url": "https://avatars.githubusercontent.com/u/45629905?v=4", + "profile": "https://github.com/NatanDosAnjos", + "contributions": [ + "translation" + ] + }, + { + "login": "Yllelder", + "name": "Yllelder Bamir", + "avatar_url": "https://avatars.githubusercontent.com/u/6941502?v=4", + "profile": "https://github.com/Yllelder", + "contributions": [ + "translation" + ] + }, + { + "login": "Esspel", + "name": "Esspel", + "avatar_url": "https://avatars.githubusercontent.com/u/47383506?v=4", + "profile": "https://github.com/Esspel", + "contributions": [ + "translation" + ] } ], "contributorsPerLine": 7, diff --git a/.github/update-strings.py b/.github/update-strings.py index 94fce459..bfb329a0 100644 --- a/.github/update-strings.py +++ b/.github/update-strings.py @@ -60,6 +60,9 @@ with en_fname.open() as f: en["config"]["step"]["user"] = strings["config"]["step"]["user"] en["options"]["step"]["init"]["data"] = data en["options"]["step"]["init"]["data_description"] = data_description +en["options"]["step"]["init"]["description"] = strings["options"]["step"]["init"][ + "description" +] en["services"] = services_json with en_fname.open("w") as f: diff --git a/.github/workflows/hassfest.yaml b/.github/workflows/hassfest.yaml index 58a152e4..b063de53 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@v6.0.1" + - uses: "actions/checkout@v6.0.2" - uses: home-assistant/actions/hassfest@master diff --git a/.github/workflows/markdown-code-runner.yml b/.github/workflows/markdown-code-runner.yml index 589976cf..efd76831 100644 --- a/.github/workflows/markdown-code-runner.yml +++ b/.github/workflows/markdown-code-runner.yml @@ -13,7 +13,8 @@ jobs: - name: Check out code from GitHub uses: actions/checkout@v6 with: - ref: ${{ github.head_ref }} + repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + ref: ${{ github.head_ref || github.ref }} fetch-depth: 0 - name: Set up Python @@ -24,34 +25,24 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v7 - - name: Run markdown-code-runner + - name: Update generated content + run: ./scripts/update-generated-content + + - name: Check for changes run: | - uv sync --group docs - uv pip install -e . - uv run python docs/run_markdown_code_runner.py - - - name: Run update services.yaml - run: uv run python .github/update-services.py - - - name: Run update strings.json - run: uv run python .github/update-strings.py - - - name: Commit updated files - id: commit - run: | - git add -u . - git config --local user.email "github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - if git diff --quiet && git diff --staged --quiet; then - echo "No changes, skipping commit." - echo "commit_status=skipped" >> $GITHUB_ENV + if [ -n "$(git status --porcelain)" ]; then + if [ "${{ github.event_name }}" == "pull_request" ]; then + echo "::error::Auto-generated files are not up to date. Please run './scripts/update-generated-content' locally and push the changes." + exit 1 + else + echo "Changes detected, committing and pushing..." + git add -u . + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git commit -m "Update auto-generated content" + git pull --rebase + git push + fi else - git commit -m "Update auto-generated content" - echo "commit_status=committed" >> $GITHUB_ENV + echo "No changes detected." fi - - - name: Push changes - if: env.commit_status == 'committed' - run: | - git pull --rebase - git push diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index b8a59b40..dd0e4559 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -38,8 +38,18 @@ jobs: python-version: "3.13" - core-version: "2025.11.3" python-version: "3.13" - - core-version: "dev" + - core-version: "2025.12.5" python-version: "3.13" + - core-version: "2026.1.3" + python-version: "3.13" + - core-version: "2026.2.3" + python-version: "3.13" + - core-version: "2026.3.4" + python-version: "3.14.2" + - core-version: "2026.4.3" + python-version: "3.14.2" + - core-version: "dev" + python-version: "3.14.2" steps: - name: Check out code from GitHub uses: actions/checkout@v6 @@ -61,7 +71,7 @@ jobs: -qq \ --timeout=9 \ --durations=10 \ - --cov="homeassistant" \ + --cov=homeassistant.components.adaptive_lighting \ --cov-report=xml \ -o console_output_style=count \ -p no:sugar \ diff --git a/.gitignore b/.gitignore index eeef9022..f40a1605 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,6 @@ dmypy.json # Home Assistant configuration config/* !config/configuration.yaml + +# Home Assistant core +core/ diff --git a/Dockerfile b/Dockerfile index 353a0748..db75963d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,14 +28,14 @@ RUN ln -s /core /app/core && /app/scripts/setup-symlinks # Install home-assistant/core dependencies RUN mkdir -p /.venv -ENV UV_PROJECT_ENVIRONMENT=/.venv UV_PYTHON=3.13 PATH="/.venv/bin:$PATH" +ENV UV_PROJECT_ENVIRONMENT=/.venv UV_PYTHON=3.14.2 PATH="/.venv/bin:$PATH" RUN uv venv RUN /app/scripts/setup-dependencies WORKDIR /app/core # Make 'custom_components/adaptive_lighting' imports available to tests -ENV PYTHONPATH="${PYTHONPATH}:/app" +ENV PYTHONPATH="/app" ENTRYPOINT ["python3", \ # Enable Python development mode @@ -48,8 +48,8 @@ ENTRYPOINT ["python3", \ "--timeout=9", \ # Print the 10 slowest tests "--durations=10", \ - # Measure code coverage for the 'homeassistant' package - "--cov='homeassistant'", \ + # Measure code coverage for the 'homeassistant.components.adaptive_lighting' component + "--cov=homeassistant.components.adaptive_lighting", \ # Generate an XML report of the code coverage "--cov-report=xml", \ # Generate an HTML report of the code coverage diff --git a/README.md b/README.md index 36ad135c..3a26df24 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-130-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-134-orange.svg?style=flat-square)](#contributors-) # 🌞 Adaptive Lighting: Enhance Your Home's Atmosphere with Smart, Sun-Synchronized Lighting 🌙 @@ -657,6 +657,12 @@ Notice the values of `brightness_mode_time_light` and `brightness_mode_time_dark lenucksi
lenucksi

💻 edgimar
edgimar

💻 Andrei LAZAROV
Andrei LAZAROV

📖 + Adam DeMuri
Adam DeMuri

💻 + Natanael
Natanael

🌍 + Yllelder Bamir
Yllelder Bamir

🌍 + + + Esspel
Esspel

🌍 diff --git a/custom_components/adaptive_lighting/config_flow.py b/custom_components/adaptive_lighting/config_flow.py index 5b0dd13c..eb7b4a0a 100644 --- a/custom_components/adaptive_lighting/config_flow.py +++ b/custom_components/adaptive_lighting/config_flow.py @@ -20,6 +20,11 @@ from .switch import validate _LOGGER = logging.getLogger(__name__) +OPTIONS_FLOW_DESCRIPTION_PLACEHOLDERS = { + "webapp_url": "https://basnijholt.github.io/adaptive-lighting", + "docs_url": "https://github.com/basnijholt/adaptive-lighting#readme", +} + class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for Adaptive Lighting.""" @@ -127,7 +132,11 @@ class OptionsFlowHandler(config_entries.OptionsFlow): conf = self.config_entry data = validate(conf) if conf.source == config_entries.SOURCE_IMPORT: - return self.async_show_form(step_id="init", data_schema=None) + return self.async_show_form( + step_id="init", + data_schema=None, + description_placeholders=OPTIONS_FLOW_DESCRIPTION_PLACEHOLDERS, + ) errors: dict[str, str] = {} if user_input is not None: validate_options(user_input, errors) @@ -164,4 +173,5 @@ class OptionsFlowHandler(config_entries.OptionsFlow): step_id="init", data_schema=vol.Schema(options_schema), errors=errors, + description_placeholders=OPTIONS_FLOW_DESCRIPTION_PLACEHOLDERS, ) diff --git a/custom_components/adaptive_lighting/docs_gen.py b/custom_components/adaptive_lighting/docs_gen.py index 8681da4b..9a10a68b 100644 --- a/custom_components/adaptive_lighting/docs_gen.py +++ b/custom_components/adaptive_lighting/docs_gen.py @@ -1,61 +1,12 @@ """Documentation generation utilities for Adaptive Lighting. -Provides functions to extract sections from README.md and transform -content for the documentation site. Used by markdown-code-runner -to generate documentation pages from README content. +Provides functions to transform content for the documentation site. +Used by markdown-code-runner to generate documentation pages from README content. """ from __future__ import annotations import re -from pathlib import Path - -# Path to README relative to this module -_MODULE_DIR = Path(__file__).parent -README_PATH = _MODULE_DIR.parent.parent / "README.md" - - -def readme_section(section_name: str, *, strip_heading: bool = True) -> str: - """Extract a marked section from README.md. - - Sections are marked with HTML comments: - - content - - - Args: - section_name: The name of the section to extract - strip_heading: If True, remove the first heading from the section - - Returns: - The content between the section markers - - Raises: - ValueError: If the section is not found in README.md - - """ - content = README_PATH.read_text() - - start_marker = f"" - end_marker = f"" - - start_idx = content.find(start_marker) - if start_idx == -1: - msg = f"Section '{section_name}' not found in README.md" - raise ValueError(msg) - - end_idx = content.find(end_marker, start_idx) - if end_idx == -1: - msg = f"End marker for section '{section_name}' not found" - raise ValueError(msg) - - section = content[start_idx + len(start_marker) : end_idx].strip() - - if strip_heading: - # Remove first heading (# or ## or ###) - section = re.sub(r"^#{1,3}\s+[^\n]+\n+", "", section, count=1) - - return _transform_readme_links(section) def _transform_readme_links(content: str) -> str: diff --git a/custom_components/adaptive_lighting/strings.json b/custom_components/adaptive_lighting/strings.json index 4b18ea06..cf816d4f 100644 --- a/custom_components/adaptive_lighting/strings.json +++ b/custom_components/adaptive_lighting/strings.json @@ -24,7 +24,7 @@ "step": { "init": { "title": "Adaptive Lighting options", - "description": "Configure an Adaptive Lighting component. Option names align with the YAML settings. If you've defined this entry in YAML, no options will appear here. For interactive graphs that demonstrate parameter effects, visit [this web app](https://basnijholt.github.io/adaptive-lighting). For further details, see the [official documentation](https://github.com/basnijholt/adaptive-lighting#readme).", + "description": "Configure an Adaptive Lighting component. Option names align with the YAML settings. If you've defined this entry in YAML, no options will appear here. For interactive graphs that demonstrate parameter effects, visit [this web app]({webapp_url}). For further details, see the [official documentation]({docs_url}).", "data": { "lights": "lights: List of light entity_ids to be controlled (may be empty). 🌟", "interval": "interval", diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index 3923e056..91049970 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -1356,7 +1356,10 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): data.context.id, ) light = service_data[ATTR_ENTITY_ID] - self.manager.last_service_data[light] = service_data + self.manager.last_service_data[light] = { + **self.manager.last_service_data.get(light, {}), + **service_data, + } await self.hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, diff --git a/custom_components/adaptive_lighting/translations/bg.json b/custom_components/adaptive_lighting/translations/bg.json index b88bbaae..f8f6f7e9 100644 --- a/custom_components/adaptive_lighting/translations/bg.json +++ b/custom_components/adaptive_lighting/translations/bg.json @@ -18,7 +18,7 @@ "step": { "init": { "title": "Настройки на Адаптивно осветление", - "description": "Конфигурирайте компонент за Адаптивно осветление. Имената на опциите съвпадат с настройките на YAML. Ако сте дефинирали този запис в YAML, няма да се появят опции тук. За интерактивни графики, които демонстрират ефектите на параметрите, посетете [това уеб приложение](https://basnijholt.github.io/adaptive-lighting). За повече подробности, вижте [официалната документация](https://github.com/basnijholt/adaptive-lighting#readme).", + "description": "Конфигурирайте компонент за Адаптивно осветление. Имената на опциите съвпадат с настройките на YAML. Ако сте дефинирали този запис в YAML, няма да се появят опции тук. За интерактивни графики, които демонстрират ефектите на параметрите, посетете [това уеб приложение]({webapp_url}). За повече подробности, вижте [официалната документация]({docs_url}).", "data": { "lights": "lights: Списък от entity_ids на лампи за контрол (може да е празен). 🌟", "interval": "интервал", diff --git a/custom_components/adaptive_lighting/translations/ca.json b/custom_components/adaptive_lighting/translations/ca.json index 48f409d2..4291d8c2 100644 --- a/custom_components/adaptive_lighting/translations/ca.json +++ b/custom_components/adaptive_lighting/translations/ca.json @@ -46,7 +46,7 @@ "intercept": "intercept: Intercepta i adapta les crides `light.turn_on` per permetre canvis instantanis de color i brillantor. 🏎️ Inhabilita-ho per a llums que no admeten `light.turn_on` amb color i brillantor.", "skip_redundant_commands": "skip_redundant_commands: Evita l'enviament de d'ordres d'adaptació als objectius on el seu estat ja és el conegut del llum. Minimitza el trànsit de la xarxa i millora la resposta de l'adaptació en alguns casos. 📉 Inhabilita-ho si l'estat físic del llum queda desincronitzat amb l'estat registrat a Home Assistant." }, - "description": "Configura un component d'Adaptive Lighting. Els noms de les opcions s'alineen amb la configuració de YAML. Si has definit aquesta entrada a YAML, aquí no apareixerà cap opció. Per veure gràfics interactius que demostren efectes de paràmetres, visita [aquesta aplicació web] (https://basnijholt.github.io/adaptive-lighting). Per a més detalls, pots veure la [documentació oficial] (https://github.com/basnijholt/adaptive-lighting#readme)." + "description": "Configura un component d'Adaptive Lighting. Els noms de les opcions s'alineen amb la configuració de YAML. Si has definit aquesta entrada a YAML, aquí no apareixerà cap opció. Per veure gràfics interactius que demostren efectes de paràmetres, visita [aquesta aplicació web]({webapp_url}). Per a més detalls, pots veure la [documentació oficial]({docs_url})." } }, "error": { diff --git a/custom_components/adaptive_lighting/translations/de.json b/custom_components/adaptive_lighting/translations/de.json index c5a0f0e2..c7592dc5 100644 --- a/custom_components/adaptive_lighting/translations/de.json +++ b/custom_components/adaptive_lighting/translations/de.json @@ -25,7 +25,7 @@ "step": { "init": { "title": "Optionen für Adaptive Beleuchtung", - "description": "Alle Einstellungen für eine Adaptive Lighting Komponente. Die Optionsnamen entsprechen den YAML-Einstellungen. Es werden keine Optionen angezeigt, wenn dieser Eintrag in YAML konfiguriert wurde. Interaktive Diagramme zur Veranschaulichung der Auswirkungen der Parameter finden Sie unter [dieser Webanwendung](https://basnijholt.github.io/adaptive-lighting). Weitere Details finden Sie in der [offiziellen Dokumentation](https://github.com/basnijholt/adaptive-lighting#readme).", + "description": "Alle Einstellungen für eine Adaptive Lighting Komponente. Die Optionsnamen entsprechen den YAML-Einstellungen. Es werden keine Optionen angezeigt, wenn dieser Eintrag in YAML konfiguriert wurde. Interaktive Diagramme zur Veranschaulichung der Auswirkungen der Parameter finden Sie unter [dieser Webanwendung]({webapp_url}). Weitere Details finden Sie in der [offiziellen Dokumentation]({docs_url}).", "data": { "lights": "Lichter", "initial_transition": "initial_transition, wenn Lichter von 'off' zu 'on' wechseln oder wenn 'sleep_state' wechselt", diff --git a/custom_components/adaptive_lighting/translations/en.json b/custom_components/adaptive_lighting/translations/en.json index e39898fd..43bb2105 100644 --- a/custom_components/adaptive_lighting/translations/en.json +++ b/custom_components/adaptive_lighting/translations/en.json @@ -25,7 +25,7 @@ "step": { "init": { "title": "Adaptive Lighting options", - "description": "Configure an Adaptive Lighting component. Option names align with the YAML settings. If you've defined this entry in YAML, no options will appear here. For interactive graphs that demonstrate parameter effects, visit [this web app](https://basnijholt.github.io/adaptive-lighting). For further details, see the [official documentation](https://github.com/basnijholt/adaptive-lighting#readme).", + "description": "Configure an Adaptive Lighting component. Option names align with the YAML settings. If you've defined this entry in YAML, no options will appear here. For interactive graphs that demonstrate parameter effects, visit [this web app]({webapp_url}). For further details, see the [official documentation]({docs_url}).", "data": { "lights": "lights: List of light entity_ids to be controlled (may be empty). 🌟", "interval": "interval", diff --git a/custom_components/adaptive_lighting/translations/es.json b/custom_components/adaptive_lighting/translations/es.json index 66d6a7a8..eff8172d 100644 --- a/custom_components/adaptive_lighting/translations/es.json +++ b/custom_components/adaptive_lighting/translations/es.json @@ -46,7 +46,7 @@ "skip_redundant_commands": "skip_redundant_commands: Evitar mandar comandos de adaptación a luces cuyo estado ya sea el esperado. Reduce tráfico en la red y mejora la respuesta de la adaptación en ciertas situaciones. 📉Deshabilitar si el estado real de las luces se desincroniza con el estado registrado en Home Assistant.", "take_over_control": "take_over_control: Deshabilita Adaptive Lighting si otra fuente llama`light.turn_on` mientras las luces están encendidas y adaptándose. Cuidado, esto llama`homeassistant.update_entity` cada `interval`! 🔒" }, - "description": "Configura un componente Adaptive Lighting. Los nombres de las opciones se asemejan a las disponibles en la configuración YAML. Si has definido esta entrada en YAML, no aparecerá ninguna opción aquí. Para gráficos interactivos que demuestran los efectos de los parámetros, visita [esta web app](https://basnijholt.github.io/adaptive-lighting). Para más detalles, ver la [documentación oficial](https://github.com/basnijholt/adaptive-lighting#readme)." + "description": "Configura un componente Adaptive Lighting. Los nombres de las opciones se asemejan a las disponibles en la configuración YAML. Si has definido esta entrada en YAML, no aparecerá ninguna opción aquí. Para gráficos interactivos que demuestran los efectos de los parámetros, visita [esta web app]({webapp_url}). Para más detalles, ver la [documentación oficial]({docs_url})." } }, "error": { diff --git a/custom_components/adaptive_lighting/translations/fi.json b/custom_components/adaptive_lighting/translations/fi.json index 6fd5f528..65233d1b 100644 --- a/custom_components/adaptive_lighting/translations/fi.json +++ b/custom_components/adaptive_lighting/translations/fi.json @@ -161,7 +161,7 @@ "sleep_rgb_or_color_temp": "Käytä joko `\"rgb_color\"` tai `\"color_temp\"` lepotilassa. 🌙", "max_sunset_time": "Aseta viimeisin virtuaalinen auringonlaskuaika (TT:MM:SS), jotta aikaisemmat auringonlaskut ovat mahdollisia. 🌇" }, - "description": "Määritä Adaptive Lighting -komponentti. Vaihtoehtojen nimet vastaavat YAML-asetuksia. Jos olet määrittänyt tämän merkinnän YAML:ssa, tässä ei näy vaihtoehtoja. Interaktiiviset kaaviot, jotka esittelevät parametrien vaikutuksia, on [tässä verkkosovelluksessa](https://basnijholt.github.io/adaptive-lighting). Lisätietoja löytyy [virallisesta dokumentaatiosta](https://github.com/basnijholt/adaptive-lighting#readme).", + "description": "Määritä Adaptive Lighting -komponentti. Vaihtoehtojen nimet vastaavat YAML-asetuksia. Jos olet määrittänyt tämän merkinnän YAML:ssa, tässä ei näy vaihtoehtoja. Interaktiiviset kaaviot, jotka esittelevät parametrien vaikutuksia, on [tässä verkkosovelluksessa]({webapp_url}). Lisätietoja löytyy [virallisesta dokumentaatiosta]({docs_url}).", "data": { "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: Kun valot sytytetään ensimmäisen kerran. Jos asetuksena on \"true\", Adaptive Lighting mukautuu vain, jos \"light.turn_on\" kutsutaan määrittelemättä väriä tai kirkkautta. ❌🌈 Tämä esimerkiksi estää mukautumisen näkymää aktivoitaessa. Jos \"false\", Adaptive Lighting mukautuu riippumatta siitä, onko alkuperäisessä \"service_data\"-arvossa väri tai kirkkaus. Vaatii \"take_over_control\":n käyttöönoton. 🕵️", "multi_light_intercept": "multi_light_intercept: sieppaa ja mukauta light.turn_on-kutsut, jotka kohdistuvat useisiin valoihin. ➗⚠️ Tämä saattaa johtaa yksittäisen light.turn_on-kutsun jakamiseen useiksi kutsuiksi, esimerkiksi kun valot ovat eri kytkimissä. Vaadi `intercept`:n käyttöönotto.", diff --git a/custom_components/adaptive_lighting/translations/fr.json b/custom_components/adaptive_lighting/translations/fr.json index c749ba6b..b816a008 100644 --- a/custom_components/adaptive_lighting/translations/fr.json +++ b/custom_components/adaptive_lighting/translations/fr.json @@ -18,7 +18,7 @@ "step": { "init": { "title": "Options d'éclairage adaptatif", - "description": "Configurer un composant d'éclairage adaptatif. Les noms correspondent aux paramètres YAML. Si vous avez défini cette entrée en YAML, aucune option n'apparaît ici. Pour les graphiques interactifs qui montrent les effets des paramètres, visiter [cette application web](https://basnijholt.github.io/adaptive-lighting). Pour plus de détail, voir la [documentation](https://github.com/basnijholt/adaptive-lighting#readme)", + "description": "Configurer un composant d'éclairage adaptatif. Les noms correspondent aux paramètres YAML. Si vous avez défini cette entrée en YAML, aucune option n'apparaît ici. Pour les graphiques interactifs qui montrent les effets des paramètres, visiter [cette application web]({webapp_url}). Pour plus de détail, voir la [documentation]({docs_url})", "data": { "lights": "lights : Liste d'\"entity_ids\" de lumières à controller (peu être vide). 🌟", "initial_transition": "initial_transition : Transition (en secondes) lorsque l'état d'une lampe passe d'« éteinte » à « allumée ».", diff --git a/custom_components/adaptive_lighting/translations/hu.json b/custom_components/adaptive_lighting/translations/hu.json index d349699e..d6f49af9 100644 --- a/custom_components/adaptive_lighting/translations/hu.json +++ b/custom_components/adaptive_lighting/translations/hu.json @@ -45,7 +45,7 @@ "include_config_in_attributes": "include_config_in_attributes: A kapcsoló összes opciójának attribútumként való megjelenítése a Home Assistantben, ha a beállítás értéke `igaz`. 📝" }, "title": "Adaptív világítás beállításai", - "description": "Egy Adaptív világítás komponens konfigurálása. Az opciók nevei a YAML-beállításokhoz igazodnak. Ha ezt a bejegyzést YAML-ben definiálta, itt nem jelennek meg beállítások. A paraméterek hatásait bemutató interaktív grafikonokért látogasson el [erre a webes alkalmazásra](https://basnijholt.github.io/adaptive-lighting). További részletekért olvasd el a [hivatalos dokumentációt](https://github.com/basnijholt/adaptive-lighting#readme)." + "description": "Egy Adaptív világítás komponens konfigurálása. Az opciók nevei a YAML-beállításokhoz igazodnak. Ha ezt a bejegyzést YAML-ben definiálta, itt nem jelennek meg beállítások. A paraméterek hatásait bemutató interaktív grafikonokért látogasson el [erre a webes alkalmazásra]({webapp_url}). További részletekért olvasd el a [hivatalos dokumentációt]({docs_url})." } }, "error": { diff --git a/custom_components/adaptive_lighting/translations/id.json b/custom_components/adaptive_lighting/translations/id.json index 00876915..6e670c1a 100644 --- a/custom_components/adaptive_lighting/translations/id.json +++ b/custom_components/adaptive_lighting/translations/id.json @@ -179,7 +179,7 @@ "include_config_in_attributes": "include_config_in_attributes: Tampilkan semua opsi sebagai atribut pada sakelar di Home Assistant ketika diatur ke `true`. 📝" }, "title": "Opsi Pencahayaan Adaptif", - "description": "Konfigurasikan komponen Pencahayaan Adaptif. Nama opsi selaras dengan pengaturan YAML. Jika Anda telah menentukan entri ini di YAML, tidak ada opsi yang akan muncul di sini. Untuk grafik interaktif yang menunjukkan efek parameter, kunjungi [aplikasi web ini](https://basnijholt.github.io/adaptive-lighting). Untuk detail lebih lanjut, lihat [dokumentasi resmi](https://github.com/basnijholt/adaptive-lighting#readme)." + "description": "Konfigurasikan komponen Pencahayaan Adaptif. Nama opsi selaras dengan pengaturan YAML. Jika Anda telah menentukan entri ini di YAML, tidak ada opsi yang akan muncul di sini. Untuk grafik interaktif yang menunjukkan efek parameter, kunjungi [aplikasi web ini]({webapp_url}). Untuk detail lebih lanjut, lihat [dokumentasi resmi]({docs_url})." } }, "error": { diff --git a/custom_components/adaptive_lighting/translations/ko.json b/custom_components/adaptive_lighting/translations/ko.json index 8a628f70..41807efb 100644 --- a/custom_components/adaptive_lighting/translations/ko.json +++ b/custom_components/adaptive_lighting/translations/ko.json @@ -18,7 +18,7 @@ "step": { "init": { "title": "적응형 조명 옵션", - "description": "적응형 조명 구성요소를 구성합니다. 옵션 이름은 YAML 설정과 일치합니다. 이 항목을 YAML에서 정의한 경우 여기에 옵션이 표시되지 않습니다. 매개변수 효과를 시연하는 인터랙티브 그래프는 [이 웹 앱](https://basnijholt.github.io/adaptive-lighting)에서 확인할 수 있습니다. 자세한 내용은 [공식 문서](https://github.com/basnijholt/adaptive-lighting#readme)를 참조하세요.", + "description": "적응형 조명 구성요소를 구성합니다. 옵션 이름은 YAML 설정과 일치합니다. 이 항목을 YAML에서 정의한 경우 여기에 옵션이 표시되지 않습니다. 매개변수 효과를 시연하는 인터랙티브 그래프는 [이 웹 앱]({webapp_url})에서 확인할 수 있습니다. 자세한 내용은 [공식 문서]({docs_url})를 참조하세요.", "data": { "lights": "조명: 제어될 조명 entity_ids의 목록 (비어 있을 수 있음). 🌟", "interval": "간격", diff --git a/custom_components/adaptive_lighting/translations/nl.json b/custom_components/adaptive_lighting/translations/nl.json index d287ef0c..98a1b51b 100644 --- a/custom_components/adaptive_lighting/translations/nl.json +++ b/custom_components/adaptive_lighting/translations/nl.json @@ -18,7 +18,7 @@ "step": { "init": { "title": "Adaptieve verlichting instellingen", - "description": "Alle instellingen voor een Adaptieve verlichting component. De optienamen komen overeen met de YAML-instellingen. Er worden geen opties weergegeven als u het item `adaptive_lighting` hebt gedefinieerd in uw YAML-configuratie.\nVoor een demonstratie met interactieve grafieken, parameters en effecten, bezoek [deze web applicatie](https://basnijholt.github.io/adaptive-lighting). Voor verdere details, bekijk de [officiële documentatie](https://github.com/basnijholt/adaptive-lighting#readme).", + "description": "Alle instellingen voor een Adaptieve verlichting component. De optienamen komen overeen met de YAML-instellingen. Er worden geen opties weergegeven als u het item `adaptive_lighting` hebt gedefinieerd in uw YAML-configuratie.\nVoor een demonstratie met interactieve grafieken, parameters en effecten, bezoek [deze web applicatie]({webapp_url}). Voor verdere details, bekijk de [officiële documentatie]({docs_url}).", "data": { "lights": "Lampen: lijst van `light` entiteiten om te bedienen (kan leeg zijn). 🌟", "initial_transition": "initial_transition: Wanneer lichten van 'uit' naar 'aan' gaan. (seconden)", diff --git a/custom_components/adaptive_lighting/translations/pl.json b/custom_components/adaptive_lighting/translations/pl.json index bebb4b18..28b22d3a 100644 --- a/custom_components/adaptive_lighting/translations/pl.json +++ b/custom_components/adaptive_lighting/translations/pl.json @@ -18,7 +18,7 @@ "step": { "init": { "title": "Opcje adaptacyjnego oświetlenia", - "description": "Konfiguracja komponentu Adaptacyjnego oświetlenia. Nazwy opcji odpowiadają ustawieniom YAML. Żadne opcje nie są wyświetlane, jeśli są zdefiniowane w konfiguracji YAML. Aby zobaczyć interaktywne wykresy demonstrujące działanie parametrów odwiedź [tą aplikację webową](https://basnijholt.github.io/adaptive-lighting). Aby zobaczyć więcej szczegółów odwiedź [oficjalną dokumentację](https://github.com/basnijholt/adaptive-lighting#readme).", + "description": "Konfiguracja komponentu Adaptacyjnego oświetlenia. Nazwy opcji odpowiadają ustawieniom YAML. Żadne opcje nie są wyświetlane, jeśli są zdefiniowane w konfiguracji YAML. Aby zobaczyć interaktywne wykresy demonstrujące działanie parametrów odwiedź [tą aplikację webową]({webapp_url}). Aby zobaczyć więcej szczegółów odwiedź [oficjalną dokumentację]({docs_url}).", "data": { "lights": "lights: Lista `entity_id`, które mają być kontrolowane (może być pusta). 🌟", "initial_transition": "initial_transition: When lights turn 'off' to 'on'. (sekund)", diff --git a/custom_components/adaptive_lighting/translations/pt.json b/custom_components/adaptive_lighting/translations/pt.json index a491cc34..cc94ff24 100644 --- a/custom_components/adaptive_lighting/translations/pt.json +++ b/custom_components/adaptive_lighting/translations/pt.json @@ -72,7 +72,7 @@ "brightness_mode": "Brilho que irá ser usado. Possíveis valores são `default`, `linear` e `tanh`(usa `brightness_mode_time_dark` e `brightness_mode_time_light`). 📈" }, "title": "Opções da Iluminação Adaptativa", - "description": "Configure um componente da Iluminação Adaptativa. O nome das opções são as mesmas que as do YML. Se você já definiu essa configuração no YAML, nenhuma opção vai aparecer aqui. Para acessar um gráfico que demonstra o efeito dos parâmetros, acesse [esse app](https://basnijholt.github.io/adaptive-lighting). Para mais detalhes, veja a [documentação oficial](https://github.com/basnijholt/adaptive-lighting#readme).", + "description": "Configure um componente da Iluminação Adaptativa. O nome das opções são as mesmas que as do YML. Se você já definiu essa configuração no YAML, nenhuma opção vai aparecer aqui. Para acessar um gráfico que demonstra o efeito dos parâmetros, acesse [esse app]({webapp_url}). Para mais detalhes, veja a [documentação oficial]({docs_url}).", "data": { "lights": "lights: Lista das entity_ids das luzes para serem controladas (pode ser vazia). 🌟", "min_brightness": "min_brightness: Percentagem minima de brilho. 💡", diff --git a/custom_components/adaptive_lighting/translations/sk.json b/custom_components/adaptive_lighting/translations/sk.json index e1cf499f..0dbc8d7b 100644 --- a/custom_components/adaptive_lighting/translations/sk.json +++ b/custom_components/adaptive_lighting/translations/sk.json @@ -45,7 +45,7 @@ "send_split_delay": "Pauza (v ms) medzi príkazmi pri zapnutom `separate_turn_on_commands` pre svetlá, ktoré nepodporujú súčasné nastavenie jasu a teploty svetla. ⏲️" }, "title": "Nastavenia Adaptívneho osvetlenia", - "description": "Nastavte komponentu Adaptívneho osvetlenia. Názvy nastavení sú zhodné s názvami v súbore YAML. Ak ste túto položku nastavili už v YAML, tak tu sa nezobrazia žiadne možnosti nastavenia. Interaktívne grafy, ktoré zobrazujú vplyv nastavení, nájdete na [tejto webovej aplikácii](https://basnijholt.github.io/adaptive-lighting). Ďalšie informácie nájdete v [oficiálnej dokumentácii](https://github.com/basnijholt/adaptive-lighting#readme)." + "description": "Nastavte komponentu Adaptívneho osvetlenia. Názvy nastavení sú zhodné s názvami v súbore YAML. Ak ste túto položku nastavili už v YAML, tak tu sa nezobrazia žiadne možnosti nastavenia. Interaktívne grafy, ktoré zobrazujú vplyv nastavení, nájdete na [tejto webovej aplikácii]({webapp_url}). Ďalšie informácie nájdete v [oficiálnej dokumentácii]({docs_url})." } }, "error": { diff --git a/custom_components/adaptive_lighting/translations/sl.json b/custom_components/adaptive_lighting/translations/sl.json index dc78acaa..011f1d89 100644 --- a/custom_components/adaptive_lighting/translations/sl.json +++ b/custom_components/adaptive_lighting/translations/sl.json @@ -44,7 +44,7 @@ "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)." + "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]({webapp_url}). Za dodatne podrobnosti glejte [uradno dokumentacijo]({docs_url})." } } }, diff --git a/custom_components/adaptive_lighting/translations/ta.json b/custom_components/adaptive_lighting/translations/ta.json index 92c552e5..50d5ecbc 100644 --- a/custom_components/adaptive_lighting/translations/ta.json +++ b/custom_components/adaptive_lighting/translations/ta.json @@ -149,7 +149,7 @@ "step": { "init": { "title": "தகவமைப்பு விளக்கு விருப்பங்கள்", - "description": "தகவமைப்பு விளக்கு கூறுகளை உள்ளமைக்கவும். விருப்பப் பெயர்கள் YAML அமைப்புகளுடன் சீரமைக்கப்படுகின்றன. இந்த உள்ளீட்டை நீங்கள் YAML இல் வரையறுத்திருந்தால், இங்கே எந்த விருப்பங்களும் தோன்றாது. அளவுரு விளைவுகளை நிரூபிக்கும் ஊடாடும் வரைபடங்களுக்கு, [இந்த வலை பயன்பாடு] (https://basnijholt.github.io/adaptive-lighting) ஐப் பார்வையிடவும். மேலும் விவரங்களுக்கு, [அதிகாரப்பூர்வ ஆவணங்கள்] (https://github.com/basnijholt/adaptive-lighting#readme) ஐப் பார்க்கவும்.", + "description": "தகவமைப்பு விளக்கு கூறுகளை உள்ளமைக்கவும். விருப்பப் பெயர்கள் YAML அமைப்புகளுடன் சீரமைக்கப்படுகின்றன. இந்த உள்ளீட்டை நீங்கள் YAML இல் வரையறுத்திருந்தால், இங்கே எந்த விருப்பங்களும் தோன்றாது. அளவுரு விளைவுகளை நிரூபிக்கும் ஊடாடும் வரைபடங்களுக்கு, [இந்த வலை பயன்பாடு]({webapp_url}) ஐப் பார்வையிடவும். மேலும் விவரங்களுக்கு, [அதிகாரப்பூர்வ ஆவணங்கள்]({docs_url}) ஐப் பார்க்கவும்.", "data": { "lights": "விளக்குகள்: கட்டுப்படுத்தப்பட வேண்டிய ஒளி நிறுவனம்_டுகளின் பட்டியல் (காலியாக இருக்கலாம்). .", "min_brightness": "min_brightness: குறைந்தபட்ச ஒளி விழுக்காடு. .", diff --git a/custom_components/adaptive_lighting/translations/tr.json b/custom_components/adaptive_lighting/translations/tr.json index 3b5c2d6e..00f1459f 100644 --- a/custom_components/adaptive_lighting/translations/tr.json +++ b/custom_components/adaptive_lighting/translations/tr.json @@ -46,7 +46,7 @@ "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." + "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ı]({webapp_url}) ziyaret edebilirsiniz. Daha fazla bilgi için [resmi dokümantasyona]({docs_url}) bakın." } }, "error": { diff --git a/custom_components/adaptive_lighting/translations/ur.json b/custom_components/adaptive_lighting/translations/ur.json index 67f3d3e0..1f04e5f6 100644 --- a/custom_components/adaptive_lighting/translations/ur.json +++ b/custom_components/adaptive_lighting/translations/ur.json @@ -179,7 +179,7 @@ "include_config_in_attributes": "include_config_in_attributes: 'سچ' پر سیٹ ہونے پر ہوم اسسٹنٹ میں سوئچ پر خصوصیات کے طور پر تمام اختیارات دکھائیں۔ 📝" }, "title": "مطابقت پذیر روشنی کے اختیارات", - "description": "ایک مطابقت پذیر لائٹنگ جزو تشکیل دیں۔ آپشن کے نام YAML کی ترتیبات کے ساتھ مطابقت رکھتے ہیں۔ اگر آپ نے YAML میں اس اندراج کی وضاحت کی ہے تو ، یہاں کوئی آپشن ظاہر نہیں ہوگا۔ انٹرایکٹو گراف کے لئے جو پیرامیٹر کے اثرات کو ظاہر کرتے ہیں ، ملاحظہ کریں [اس ویب ایپ] (https://basnijholt.github.io/adaptive-lighting)۔ مزید تفصیلات کے لئے ، [سرکاری دستاویزات] (https://github.com/basnijholt/adaptive-lighting#readme) ملاحظہ کریں۔" + "description": "ایک مطابقت پذیر لائٹنگ جزو تشکیل دیں۔ آپشن کے نام YAML کی ترتیبات کے ساتھ مطابقت رکھتے ہیں۔ اگر آپ نے YAML میں اس اندراج کی وضاحت کی ہے تو ، یہاں کوئی آپشن ظاہر نہیں ہوگا۔ انٹرایکٹو گراف کے لئے جو پیرامیٹر کے اثرات کو ظاہر کرتے ہیں ، ملاحظہ کریں [اس ویب ایپ]({webapp_url})۔ مزید تفصیلات کے لئے ، [سرکاری دستاویزات]({docs_url}) ملاحظہ کریں۔" } }, "error": { diff --git a/custom_components/adaptive_lighting/translations/zh-Hans.json b/custom_components/adaptive_lighting/translations/zh-Hans.json index 0d501731..1fd9d3da 100644 --- a/custom_components/adaptive_lighting/translations/zh-Hans.json +++ b/custom_components/adaptive_lighting/translations/zh-Hans.json @@ -18,7 +18,7 @@ "step": { "init": { "title": "自适应照明选项", - "description": "配置自适应照明组件。选项名称与YAML设置对齐。如果在YAML中定义了此条目,则此处不会显示任何选项。有关演示参数影响的交互式图表,请访问[此Web应用程序](https://basnijholt.github.io/adaptive-lighting)。有关更多详细信息,请参阅[官方文档](https://github.com/basnijholt/adaptive-lighting#readme)。", + "description": "配置自适应照明组件。选项名称与YAML设置对齐。如果在YAML中定义了此条目,则此处不会显示任何选项。有关演示参数影响的交互式图表,请访问[此Web应用程序]({webapp_url})。有关更多详细信息,请参阅[官方文档]({docs_url})。", "data": { "lights": "lights:要控制的灯光实体ID列表(可以为空)。🌟", "interval": "频率(interval)", diff --git a/docs/advanced/brightness-modes.md b/docs/advanced/brightness-modes.md index b735a583..10408fb2 100644 --- a/docs/advanced/brightness-modes.md +++ b/docs/advanced/brightness-modes.md @@ -19,8 +19,7 @@ Adaptive Lighting supports three brightness modes: ## Detailed Explanation - - + @@ -90,8 +89,7 @@ adaptive_lighting: These graphs show how brightness changes throughout the day based on calculated values: - - + diff --git a/docs/advanced/manual-control.md b/docs/advanced/manual-control.md index 157e081e..36720f3c 100644 --- a/docs/advanced/manual-control.md +++ b/docs/advanced/manual-control.md @@ -9,8 +9,7 @@ Adaptive Lighting is designed to work seamlessly with manual adjustments, detect ## How It Works - - + diff --git a/docs/automation-examples.md b/docs/automation-examples.md index e8ed0e7d..e2d136f6 100644 --- a/docs/automation-examples.md +++ b/docs/automation-examples.md @@ -7,8 +7,8 @@ icon: lucide/bot Real-world automation examples showing how to integrate Adaptive Lighting with your Home Assistant setup. - - + + diff --git a/docs/configuration.md b/docs/configuration.md index 622c1416..21c7028e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -85,8 +85,8 @@ All configuration options are listed below with their default values. These opti ## Full Configuration Example - - + + diff --git a/docs/index.md b/docs/index.md index 28820274..bc3acebe 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,8 +26,8 @@ By automatically adapting the settings of your lights throughout the day, Adapti ## Features - - + + diff --git a/docs/see-also.md b/docs/see-also.md index 8c1a2613..5b06fcae 100644 --- a/docs/see-also.md +++ b/docs/see-also.md @@ -9,8 +9,8 @@ Resources, tutorials, and related projects for Adaptive Lighting. ## Tutorials & Articles - - + + diff --git a/docs/services.md b/docs/services.md index 5b0ccf58..306aa7c3 100644 --- a/docs/services.md +++ b/docs/services.md @@ -112,13 +112,11 @@ data: ## adaptive_lighting.change_switch_settings - - + + -#### `adaptive_lighting.change_switch_settings` - `adaptive_lighting.change_switch_settings` (new in 1.7.0) Change any of the above configuration options of Adaptive Lighting (such as `sunrise_time` or `prefer_rgb_color`) with a service call directly from your script/automation. > [!WARNING] diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 4e33e24c..22374949 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -9,8 +9,8 @@ This guide covers common issues and their solutions when using Adaptive Lighting ## Enable Debug Logging - - + + @@ -30,8 +30,8 @@ After the issue occurs, create a new issue report with the log (`/config/home-as ## Common Problems & Solutions - - + + diff --git a/pyproject.toml b/pyproject.toml index 7021ac48..bdaf9acf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ requires-python = ">=3.12" docs = [ "astral", "homeassistant", - "markdown-code-runner", + "markdown-code-runner>=2.7.0", "markdown-gfm-admonition", "pandas", "shinylive", diff --git a/scripts/setup-dependencies b/scripts/setup-dependencies index 7722c58d..d69bce3c 100755 --- a/scripts/setup-dependencies +++ b/scripts/setup-dependencies @@ -9,6 +9,30 @@ sed -i '/^mypy-dev/d' core/requirements_test.txt uv pip install -r core/requirements.txt uv pip install -r core/requirements_test.txt + +# HA 2026.4+ imports aiohasupervisor from tests/components/conftest.py +# but pins it in requirements_test_all.txt instead of requirements_test.txt. +aiohasupervisor_req="" +if [[ -f core/requirements_test_all.txt ]]; then + aiohasupervisor_req="$(grep -m1 '^aiohasupervisor' core/requirements_test_all.txt || true)" +fi +if [[ -n "${aiohasupervisor_req}" ]]; then + uv pip install "${aiohasupervisor_req}" +fi + +# HA 2026.4+ validates service translations through translations/en.json. +# The core checkout keeps English source strings in strings.json, so seed en.json +# in the temporary CI checkout before tests load integrations. +for strings_file in core/homeassistant/components/*/strings.json; do + [[ -f "${strings_file}" ]] || continue + translations_dir="$(dirname "${strings_file}")/translations" + en_translation="${translations_dir}/en.json" + if [[ ! -f "${en_translation}" ]]; then + mkdir -p "${translations_dir}" + cp "${strings_file}" "${en_translation}" + fi +done + uv pip install -e core/ uv pip install ulid-transform # this is in Adaptive-lighting's manifest.json uv pip install $(python test_dependencies.py) diff --git a/scripts/setup-devcontainer b/scripts/setup-devcontainer index fb5bf472..31ad774e 100755 --- a/scripts/setup-devcontainer +++ b/scripts/setup-devcontainer @@ -15,7 +15,7 @@ pip install \ pip cache purge -uv venv --clear --python 3.13 +uv venv --clear --python 3.14.2 ./scripts/setup-dependencies ./scripts/setup-symlinks uv run pre-commit install-hooks diff --git a/scripts/update-generated-content b/scripts/update-generated-content new file mode 100755 index 00000000..1fed3178 --- /dev/null +++ b/scripts/update-generated-content @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -ex + +cd "$(dirname "$0")/.." + +uv sync --group docs +uv pip install -e . +uv run python docs/run_markdown_code_runner.py +uv run python .github/update-services.py +uv run python .github/update-strings.py diff --git a/scripts/update-test-matrix.py b/scripts/update-test-matrix.py index 1d06d992..c37b7f25 100755 --- a/scripts/update-test-matrix.py +++ b/scripts/update-test-matrix.py @@ -82,9 +82,13 @@ def get_python_version(ha_version: str) -> str: """Determine Python version based on HA Core version.""" parts = ha_version.split(".") year, month = int(parts[0]), int(parts[1]) - # 2024.x and 2025.1 use Python 3.12, 2025.2+ use Python 3.13 + # 2024.x and 2025.1 use Python 3.12. + # 2025.2 through 2026.2 use Python 3.13. + # 2026.3+ uses Python 3.14. if year == 2024 or (year == 2025 and month == 1): return "3.12" + if year > 2026 or (year == 2026 and month >= 3): + return "3.14.2" return "3.13" @@ -97,7 +101,7 @@ def generate_matrix_yaml(versions: list[str]) -> str: lines.append(f' python-version: "{python_ver}"') # Add dev version lines.append(' - core-version: "dev"') - lines.append(' python-version: "3.13"') + lines.append(' python-version: "3.14.2"') return "\n".join(lines) diff --git a/tests/test_switch.py b/tests/test_switch.py index 5ba3cb60..470e0e25 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -9,7 +9,7 @@ from collections import OrderedDict from copy import deepcopy from random import randint from typing import Any -from unittest.mock import Mock, patch +from unittest.mock import AsyncMock, Mock, patch import homeassistant.util.dt as dt_util import pytest @@ -242,15 +242,25 @@ async def setup_lights(hass: HomeAssistant, with_group: bool = False): await lights[0].async_turn_on() await lights[1].async_turn_on() + for light in lights[2:]: + await light.async_turn_off() for light in lights: - light._attr_brightness = 255 + set_light_brightness(light, 255) light._attr_color_temp = 250 + light.async_write_ha_state() assert all(hass.states.get(light.entity_id) is not None for light in lights) return lights +def set_light_brightness(light: LightTemplate, brightness: int) -> None: + """Set brightness across Home Assistant template light internals.""" + if hasattr(light, "_brightness"): + light._brightness = brightness + light._attr_brightness = brightness + + async def setup_lights_and_switch( hass, extra_conf=None, @@ -352,14 +362,14 @@ def create_transition_events( async def test_adaptive_lighting_switches(hass): """Test switches created for adaptive_lighting integration.""" - entry, _ = await setup_switch(hass, {}) + entry, switch = await setup_switch(hass, {}) assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 4 assert set(hass.states.async_entity_ids(SWITCH_DOMAIN)) == { - ENTITY_SWITCH, - ENTITY_SLEEP_MODE_SWITCH, - ENTITY_ADAPT_COLOR_SWITCH, - ENTITY_ADAPT_BRIGHTNESS_SWITCH, + switch.entity_id, + switch.sleep_mode_switch.entity_id, + switch.adapt_color_switch.entity_id, + switch.adapt_brightness_switch.entity_id, } assert ATTR_ADAPTIVE_LIGHTING_MANAGER in hass.data[DOMAIN] assert entry.entry_id in hass.data[DOMAIN] @@ -500,7 +510,7 @@ async def test_light_settings(hass): await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: ENTITY_SLEEP_MODE_SWITCH}, + {ATTR_ENTITY_ID: switch.sleep_mode_switch.entity_id}, blocking=True, ) await hass.async_block_till_done() @@ -520,7 +530,7 @@ async def test_light_settings(hass): await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: ENTITY_SLEEP_MODE_SWITCH}, + {ATTR_ENTITY_ID: switch.sleep_mode_switch.entity_id}, blocking=True, ) await hass.async_block_till_done() @@ -726,7 +736,7 @@ async def test_manual_control( ), manual_control # Check that toggling (sleep mode) switch resets manual control - for entity_id in [ENTITY_SWITCH, ENTITY_SLEEP_MODE_SWITCH]: + for entity_id in [switch.entity_id, switch.sleep_mode_switch.entity_id]: await change_manual_control(True) assert manual_control[ENTITY_LIGHT_1] await turn_switch(False, entity_id) @@ -806,7 +816,7 @@ async def test_manual_control( DOMAIN, SERVICE_APPLY, { - ATTR_ENTITY_ID: ENTITY_SWITCH, + ATTR_ENTITY_ID: switch.entity_id, CONF_LIGHTS: [ENTITY_LIGHT_1], CONF_TURN_ON_LIGHTS: True, }, @@ -1058,7 +1068,7 @@ async def test_apply_service(hass): DOMAIN, SERVICE_APPLY, { - ATTR_ENTITY_ID: ENTITY_SWITCH, + ATTR_ENTITY_ID: switch.entity_id, CONF_LIGHTS: [entity_id], CONF_TURN_ON_LIGHTS: True, **kwargs, @@ -1275,7 +1285,7 @@ async def test_state_change_handlers(hass): await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: ENTITY_SLEEP_MODE_SWITCH}, + {ATTR_ENTITY_ID: switch.sleep_mode_switch.entity_id}, blocking=True, ) await hass.async_block_till_done() @@ -1647,7 +1657,7 @@ async def test_change_switch_settings_service(hass): DOMAIN, SERVICE_CHANGE_SWITCH_SETTINGS, { - ATTR_ENTITY_ID: ENTITY_SWITCH, + ATTR_ENTITY_ID: switch.entity_id, **kwargs, }, blocking=True, @@ -1696,7 +1706,7 @@ async def test_change_switch_settings_service(hass): async def test_cancellable_service_calls_task(hass): """Test the creation and execution of the task that wraps adaptation service calls.""" - (light, *_) = await setup_lights(hass) + light, *_ = await setup_lights(hass) _, switch = await setup_switch(hass, {CONF_SEPARATE_TURN_ON_COMMANDS: True}) context = switch.create_context("test") @@ -2914,3 +2924,76 @@ async def test_adapt_only_on_bare_turn_on_respects_pause_changed_mode(hass, inte f"With take_over_control_mode=PAUSE_CHANGED and only brightness marked " f"as manually controlled, color_temp should still be adapted." ) + + +async def test_detect_non_ha_changes_with_separate_turn_on_commands(hass): + """Regression test for detect_non_ha_changes with separate_turn_on_commands. + + With separate_turn_on_commands=True, each adaptation cycle makes two sequential + light.turn_on calls (brightness, then color). If the second call overwrites + last_service_data instead of merging, brightness is dropped — and + _attributes_have_changed silently skips the brightness comparison, so a direct + Zigbee brightness change is never detected as manual control. + """ + switch, (light, *_) = await setup_lights_and_switch( + hass, + { + CONF_SEPARATE_TURN_ON_COMMANDS: True, + CONF_DETECT_NON_HA_CHANGES: True, + CONF_TAKE_OVER_CONTROL: True, + }, + ) + + context = switch.create_context("test") + + async def update(force: bool = False): + await switch._update_attrs_and_maybe_adapt_lights( + context=context, + force=force, + transition=0, + ) + await hass.async_block_till_done() + + await update(force=True) + + last_sd = switch.manager.last_service_data.get(ENTITY_LIGHT_1) + assert last_sd is not None, "last_service_data not set after force adapt" + assert ( + ATTR_BRIGHTNESS in last_sd + ), f"brightness missing from last_service_data after split calls: {last_sd}" + assert ( + ATTR_COLOR_TEMP_KELVIN in last_sd or ATTR_RGB_COLOR in last_sd + ), f"color missing from last_service_data after split calls: {last_sd}" + + al_brightness = light.brightness + assert al_brightness is not None + switch.manager.manual_control[ENTITY_LIGHT_1] = LightControlAttributes.NONE + + manual_brightness = ( + al_brightness - 120 if al_brightness >= 120 else al_brightness + 120 + ) + set_light_brightness(light, manual_brightness) + + async def _flush_attr_state(hass, entity_id): + """Mimic a ZHA attribute report: write current hardware state to HA.""" + light.async_write_ha_state() + + with patch( + "homeassistant.components.adaptive_lighting.switch.async_update_entity", + new=AsyncMock(side_effect=_flush_attr_state), + ): + await update(force=False) + + assert LightControlAttributes.BRIGHTNESS in switch.manager.manual_control.get( + ENTITY_LIGHT_1, + LightControlAttributes.NONE, + ), ( + f"manual_control={switch.manager.manual_control.get(ENTITY_LIGHT_1)}, " + f"last_service_data={switch.manager.last_service_data.get(ENTITY_LIGHT_1)}" + ) + + await update(force=False) + + assert ( + light.brightness == manual_brightness + ), f"AL overrode manual brightness {manual_brightness} with {al_brightness}" diff --git a/uv.lock b/uv.lock index 5dd33777..57564ac4 100644 --- a/uv.lock +++ b/uv.lock @@ -109,7 +109,7 @@ dev = [ docs = [ { name = "astral" }, { name = "homeassistant" }, - { name = "markdown-code-runner" }, + { name = "markdown-code-runner", specifier = ">=2.7.0" }, { name = "markdown-gfm-admonition" }, { name = "pandas" }, { name = "shinylive" }, @@ -2790,11 +2790,11 @@ wheels = [ [[package]] name = "markdown-code-runner" -version = "2.4.0" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/91/6b7030f873b0c49f38131c7a5ac9432238cf1e1cd47104c05b07cf5c32ed/markdown_code_runner-2.4.0.tar.gz", hash = "sha256:7d6229c437f0c71e5c0442585663af4bfe4beacddf884a64ea8c09fd5dbc31dd", size = 19072, upload-time = "2025-08-23T19:07:41.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/d1/2a7753e05dcc711552721048a34341b63a6a2b09e70ac738c7a4c81ca414/markdown_code_runner-2.7.0.tar.gz", hash = "sha256:b36f9314839c0db3ee5e5d644e1b4471454af8f624eb15bd5a5d68dbc6430afe", size = 99832, upload-time = "2026-01-24T21:20:04.772Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/3c/8857763070aa0f39591b012f92ab40bd5fe906914085e925eb117e28f306/markdown_code_runner-2.4.0-py3-none-any.whl", hash = "sha256:5be62e75ab35188b37f2d440c19b6683df68615ea24bed4955d95a745e56b483", size = 12292, upload-time = "2025-08-23T19:07:40.972Z" }, + { url = "https://files.pythonhosted.org/packages/cf/92/9d7fa0567cf3d38b551bb1a30c177285e0e42ae2b10c16946af7e8a38e73/markdown_code_runner-2.7.0-py3-none-any.whl", hash = "sha256:fad5d5fbad9c49687141ab9c39ed7e054f857b97aa79d11f031de157afd21c20", size = 14692, upload-time = "2026-01-24T21:20:03.382Z" }, ] [[package]]