Merge branch 'main' into renovate/pytz-2025.x

This commit is contained in:
Bas Nijholt 2026-04-24 11:37:08 -07:00 committed by GitHub
commit 62f46e54bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 284 additions and 154 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

3
.gitignore vendored
View file

@ -135,3 +135,6 @@ dmypy.json
# Home Assistant configuration
config/*
!config/configuration.yaml
# Home Assistant core
core/

View file

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

View file

@ -1,7 +1,7 @@
[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/hacs/integration)
![Version](https://img.shields.io/github/v/release/basnijholt/adaptive-lighting?style=for-the-badge)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-130-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-134-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
# 🌞 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
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lenucksi"><img src="https://avatars.githubusercontent.com/u/2451899?v=4?s=100" width="100px;" alt="lenucksi"/><br /><sub><b>lenucksi</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=lenucksi" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://gitlab.com/edgimar"><img src="https://avatars.githubusercontent.com/u/393850?v=4?s=100" width="100px;" alt="edgimar"/><br /><sub><b>edgimar</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=edgimar" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/andrei-lazarov"><img src="https://avatars.githubusercontent.com/u/51081857?v=4?s=100" width="100px;" alt="Andrei LAZAROV"/><br /><sub><b>Andrei LAZAROV</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=andrei-lazarov" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ademuri"><img src="https://avatars.githubusercontent.com/u/3051618?v=4?s=100" width="100px;" alt="Adam DeMuri"/><br /><sub><b>Adam DeMuri</b></sub></a><br /><a href="https://github.com/basnijholt/adaptive-lighting/commits?author=ademuri" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NatanDosAnjos"><img src="https://avatars.githubusercontent.com/u/45629905?v=4?s=100" width="100px;" alt="Natanael"/><br /><sub><b>Natanael</b></sub></a><br /><a href="#translation-NatanDosAnjos" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Yllelder"><img src="https://avatars.githubusercontent.com/u/6941502?v=4?s=100" width="100px;" alt="Yllelder Bamir"/><br /><sub><b>Yllelder Bamir</b></sub></a><br /><a href="#translation-Yllelder" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Esspel"><img src="https://avatars.githubusercontent.com/u/47383506?v=4?s=100" width="100px;" alt="Esspel"/><br /><sub><b>Esspel</b></sub></a><br /><a href="#translation-Esspel" title="Translation">🌍</a></td>
</tr>
</tbody>
<tfoot>

View file

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

View file

@ -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:
<!-- SECTION:section_name:START -->
content
<!-- SECTION:section_name:END -->
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"<!-- SECTION:{section_name}:START -->"
end_marker = f"<!-- SECTION:{section_name}:END -->"
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:

View file

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

View file

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

View file

@ -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": "интервал",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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": "간격",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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: குறைந்தபட்ச ஒளி விழுக்காடு. .",

View file

@ -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 Lightingin değişiklikleri uygulamasına kadar bekleme süresi (saniye cinsinden). Titremeyi önlemeye yardımcı olabilir. ⏲️"
},
"description": "Bir Adaptive Lighting bileşenini yapılandırın. Seçenek adları YAML ayarlarıyla uyumludur. Eğer bu girdiyi YAMLda tanımladıysanız, burada seçenekler görünmez. \nParametrelerin etkilerini gösteren etkileşimli grafikler için [bu web uygulamasını](https://basnijholt.github.io/adaptive-lighting) ziyaret edebilirsiniz. Daha fazla bilgi için [resmi dokümantasyona](https://github.com/basnijholt/adaptive-lighting#readme) bakın."
"description": "Bir Adaptive Lighting bileşenini yapılandırın. Seçenek adları YAML ayarlarıyla uyumludur. Eğer bu girdiyi YAMLda tanımladıysanız, burada seçenekler görünmez. \nParametrelerin etkilerini gösteren etkileşimli grafikler için [bu web uygulamasını]({webapp_url}) ziyaret edebilirsiniz. Daha fazla bilgi için [resmi dokümantasyona]({docs_url}) bakın."
}
},
"error": {

View file

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

View file

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

View file

@ -19,8 +19,7 @@ Adaptive Lighting supports three brightness modes:
## Detailed Explanation
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("brightness-modes")) -->
<!-- print(include_section("../../README.md", "brightness-modes", strip_heading=True)) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
@ -90,8 +89,7 @@ adaptive_lighting:
These graphs show how brightness changes throughout the day based on calculated values:
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("graphs")) -->
<!-- print(include_section("../../README.md", "graphs", strip_heading=True)) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->

View file

@ -9,8 +9,7 @@ Adaptive Lighting is designed to work seamlessly with manual adjustments, detect
## How It Works
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("manual-control")) -->
<!-- print(include_section("../../README.md", "manual-control", strip_heading=True)) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->

View file

@ -7,8 +7,8 @@ icon: lucide/bot
Real-world automation examples showing how to integrate Adaptive Lighting with your Home Assistant setup.
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("automation-examples")) -->
<!-- from adaptive_lighting.docs_gen import _transform_readme_links -->
<!-- print(_transform_readme_links(include_section("../README.md", "automation-examples", strip_heading=True))) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->

View file

@ -85,8 +85,8 @@ All configuration options are listed below with their default values. These opti
## Full Configuration Example
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("config-example-full", strip_heading=False)) -->
<!-- from adaptive_lighting.docs_gen import _transform_readme_links -->
<!-- print(_transform_readme_links(include_section("../README.md", "config-example-full"))) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->

View file

@ -26,8 +26,8 @@ By automatically adapting the settings of your lights throughout the day, Adapti
## Features
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("features")) -->
<!-- from adaptive_lighting.docs_gen import _transform_readme_links -->
<!-- print(_transform_readme_links(include_section("../README.md", "features", strip_heading=True))) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->

View file

@ -9,8 +9,8 @@ Resources, tutorials, and related projects for Adaptive Lighting.
## Tutorials & Articles
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("see-also")) -->
<!-- from adaptive_lighting.docs_gen import _transform_readme_links -->
<!-- print(_transform_readme_links(include_section("../README.md", "see-also", strip_heading=True))) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->

View file

@ -112,13 +112,11 @@ data:
## adaptive_lighting.change_switch_settings
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("change-switch-settings")) -->
<!-- from adaptive_lighting.docs_gen import _transform_readme_links -->
<!-- print(_transform_readme_links(include_section("../README.md", "change-switch-settings", strip_heading=True))) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
#### `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]

View file

@ -9,8 +9,8 @@ This guide covers common issues and their solutions when using Adaptive Lighting
## Enable Debug Logging
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("troubleshooting-intro")) -->
<!-- from adaptive_lighting.docs_gen import _transform_readme_links -->
<!-- print(_transform_readme_links(include_section("../README.md", "troubleshooting-intro", strip_heading=True))) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
@ -30,8 +30,8 @@ After the issue occurs, create a new issue report with the log (`/config/home-as
## Common Problems & Solutions
<!-- CODE:START -->
<!-- from adaptive_lighting.docs_gen import readme_section -->
<!-- print(readme_section("common-problems")) -->
<!-- from adaptive_lighting.docs_gen import _transform_readme_links -->
<!-- print(_transform_readme_links(include_section("../README.md", "common-problems", strip_heading=True))) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

8
uv.lock generated
View file

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