Add documentation site with Zensical framework

Create comprehensive documentation site for adaptive-lighting.nijho.lt:

- Add zensical.toml configuration with Material theme (amber/orange)
- Create docs_gen.py module for extracting README sections via markers
- Add section markers to README.md for content reuse
- Create documentation pages:
  - index.md: Home with features overview
  - getting-started.md: Installation and quick setup
  - configuration.md: Auto-generated config options table
  - services.md: Auto-generated service documentation
  - automation-examples.md: Real-world automation recipes
  - troubleshooting.md: Common issues and solutions
  - see-also.md: External resources and links
  - advanced/brightness-modes.md: Brightness mode deep dive
  - advanced/manual-control.md: Manual control system docs
  - advanced/sleep-mode.md: Sleep mode configuration
- Add GitHub Actions workflow for building and deploying to Pages
- Add custom CSS with sun-themed styling
- Add CNAME for custom domain

Uses markdown-code-runner to auto-generate content from code schemas
and extract README sections for single-source documentation.
This commit is contained in:
Bas Nijholt 2026-01-12 09:52:16 -08:00
commit f5877076ce
18 changed files with 2159 additions and 0 deletions

82
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,82 @@
name: Documentation
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'README.md'
- 'zensical.toml'
- 'custom_components/adaptive_lighting/_docs_helpers.py'
- 'custom_components/adaptive_lighting/docs_gen.py'
- 'custom_components/adaptive_lighting/const.py'
- '.github/workflows/docs.yml'
pull_request:
paths:
- 'docs/**'
- 'README.md'
- 'zensical.toml'
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: |
uv pip install --system zensical markdown-code-runner
uv pip install --system pandas voluptuous homeassistant
- name: Set up PYTHONPATH
run: |
echo "PYTHONPATH=$GITHUB_WORKSPACE/custom_components:$GITHUB_WORKSPACE" >> $GITHUB_ENV
- name: Run markdown-code-runner on docs
run: |
# Process all markdown files with CODE blocks
for f in docs/*.md docs/**/*.md; do
if [ -f "$f" ]; then
echo "Processing $f..."
markdown-code-runner "$f" || echo "Warning: Failed to process $f"
fi
done
- name: Build documentation
run: zensical build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./site
deploy:
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View file

@ -24,6 +24,7 @@ https://github.com/basnijholt/adaptive-lighting/assets/6897215/68908f7d-fbf1-499
[[ToC](#books-table-of-contents)]
<!-- SECTION:features:START -->
## :bulb: Features
When initially turning on a light that is controlled by Adaptive Lighting, the `light.turn_on` service call is intercepted, and the light's brightness and color are automatically adjusted based on the sun's position.
@ -35,7 +36,9 @@ Adaptive Lighting provides four switches (using "living_room" as an example comp
- `switch.adaptive_lighting_sleep_mode_living_room`: Activate "sleep mode" 😴 and set custom sleep_brightness and sleep_color_temp.
- `switch.adaptive_lighting_adapt_brightness_living_room`: Enable or disable brightness adaptation 🔆 for supported lights.
- `switch.adaptive_lighting_adapt_color_living_room`: Enable or disable color adaptation 🌈 for supported lights.
<!-- SECTION:features:END -->
<!-- SECTION:manual-control:START -->
### :control_knobs: Regain Manual Control
Adaptive Lighting is designed to automatically detect when you or another source (e.g., automation) manually changes light settings 🕹️.
@ -46,6 +49,7 @@ Additionally, enabling `detect_non_ha_changes` allows Adaptive Lighting to detec
The `adaptive_lighting.manual_control` event is fired when a light is marked as "manually controlled," allowing for integration with automations 🤖.
> ⚠️ **_Caution: Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Disable `detect_non_ha_changes` if you encounter such issues._**
<!-- SECTION:manual-control:END -->
## :books: Table of Contents
@ -149,6 +153,7 @@ The YAML and frontend configuration methods support all of the options listed be
<!-- OUTPUT:END -->
<!-- SECTION:config-example-full:START -->
Full example:
```yaml
@ -175,6 +180,7 @@ adaptive_lighting:
only_once: false
```
<!-- SECTION:config-example-full:END -->
### :hammer_and_wrench: Services
@ -218,6 +224,8 @@ adaptive_lighting:
| `manual_control` | Whether to add ("true") or remove ("false") all adapted attributes of the light from the "manual_control" list, or the name of an attribute for selective addition. 🔒 | ❌ | bool or one of `['brightness', 'color']` |
<!-- OUTPUT:END -->
<!-- SECTION:change-switch-settings:START -->
#### `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.
@ -237,7 +245,9 @@ The following keys are disallowed:
| `lights` | You may call `adaptive_lighting.apply` with your lights or create a new config instead. |
| `name` | You can rename your switch's display name in Home Assistant's UI. |
| `interval` | The interval is used only once when the config loads. A config change and restart are required. |
<!-- SECTION:change-switch-settings:END -->
<!-- SECTION:automation-examples:START -->
## :robot: Automation examples
<details>
@ -336,6 +346,7 @@ iphone_carly_wakeup:
```
</details>
<!-- SECTION:automation-examples:END -->
## Additional Information
@ -343,8 +354,10 @@ For more details on adding the integration and setting options, refer to the [do
Adaptive Lighting was initially inspired by @claytonjn's [hass-circadian\_lighting](https://github.com/claytonjn/hass-circadian_lighting), but has since been entirely rewritten and expanded with new features.
<!-- SECTION:troubleshooting:START -->
## :sos: Troubleshooting
<!-- SECTION:troubleshooting-intro:START -->
Encountering issues? Enable debug logging in your `configuration.yaml`:
```yaml
@ -355,7 +368,9 @@ logger:
```
After the issue occurs, create a new issue report with the log (`/config/home-assistant.log`).
<!-- SECTION:troubleshooting-intro:END -->
<!-- SECTION:common-problems:START -->
### :exclamation: Common Problems & Solutions
#### :bulb: Lights Not Responding or Turning On by Themselves
@ -413,7 +428,10 @@ These lights are known to exhibit disadvantageous behaviour due to firmware bugs
- Ikea Tradfri bulbs/drivers (and related Ikea smart light products)
- Unsupported simultaneous transition of brightness and color: When receiving such a command, they switch the brightness instantly and only transition the color. To get smooth transitions of both brightness and color, enable `separate_turn_on_commands`.
- Unresponsiveness during color transitions: No other commands are processed during an ongoing color transition, e.g., turn-off commands are ignored and lights stay on despite being reported as off to Home Assistant. The default config with long transitions thus results in long periods of unresponsiveness. To work around this, disable transitions by setting `transition` to `0`, and increase the adaptation frequency by setting `interval` to a short time, e.g., `15` seconds, to retain the impression of smooth continuous adaptations. Keeping the `initial_transition` is recommended for a smooth fade-in (lights are usually not turned off momentarily after being turned on, in which case a short period of unresponsiveness is tolerable).
<!-- SECTION:common-problems:END -->
<!-- SECTION:troubleshooting:END -->
<!-- SECTION:graphs:START -->
## :bar_chart: Graphs!
These graphs were generated using the values calculated by the Adaptive Lighting sensor/switch(es).
@ -428,7 +446,9 @@ These graphs were generated using the values calculated by the Adaptive Lighting
### While using `transition_until_sleep: true`
![image](https://user-images.githubusercontent.com/2219836/228949675-f9699624-8abc-466c-bb04-250ce0f495b8.png)
<!-- SECTION:graphs:END -->
<!-- SECTION:brightness-modes:START -->
### Custom brightness ramps using `brightness_mode` with `"linear"` and `"tanh"`
<details>
@ -454,12 +474,15 @@ Notice the values of `brightness_mode_time_light` and `brightness_mode_time_dark
![image](https://github.com/basnijholt/adaptive-lighting/assets/6897215/3dcbdc42-63c4-49df-8651-d2fae53dd08d)
> Check out the interactive webapp on https://basnijholt.github.io/adaptive-lighting/ to play with the parameters and see how the brightness changes!
<!-- SECTION:brightness-modes:END -->
<!-- SECTION:see-also:START -->
## :eyes: See also
- [*Sleep better with Adaptive Lighting in Home Assistant*](https://wartner.io/sleep-better-with-adaptive-lightning-in-home-assistant/) by Florian Wartner on 2023-02-23 (blog post 📜)
- [*Automatic smart light brightness and color based on the sun*](https://www.youtube.com/watch?v=Rg3zI1Oyk3c) by Home Automation Guy on 2022-08-31 (YouTube video 📺)
- [*Adaptive Lighting Blew My Mind in Home Assistant - How to set it up*](https://www.youtube.com/watch?v=c1cnccmgl3k) by Smart Home Junkie on 2022-06-26 (YouTube video 📺)
<!-- SECTION:see-also:END -->
## :busts_in_silhouette: Contributors

View file

@ -0,0 +1,152 @@
"""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.
"""
from __future__ import annotations
import re
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
pass
# 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 = False) -> 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:
"""Transform README internal links to docs site links.
Converts anchors like #configuration to proper doc page links.
"""
# Map README anchors to doc pages
link_map = {
"#gear-configuration": "configuration.md",
"#memo-options": "configuration.md#all-options",
"#hammer_and_wrench-services": "services.md",
"#adaptive_lightingapply": "services.md#adaptive_lightingapply",
"#adaptive_lightingset_manual_control": "services.md#adaptive_lightingset_manual_control",
"#adaptive_lightingchange_switch_settings": "services.md#adaptive_lightingchange_switch_settings",
"#robot-automation-examples": "automation-examples.md",
"#sos-troubleshooting": "troubleshooting.md",
"#exclamation-common-problems--solutions": "troubleshooting.md#common-problems-solutions",
"#bar_chart-graphs": "advanced/brightness-modes.md#graphs",
"#bulb-features": "index.md#features",
"#control_knobs-regain-manual-control": "advanced/manual-control.md",
"#eyes-see-also": "see-also.md",
}
for old_link, new_link in link_map.items():
content = content.replace(f"]({old_link})", f"]({new_link})")
# Also handle the ToC link pattern [[ToC](#...)]
content = re.sub(r"\[\[ToC\]\([^)]+\)\]", "", content)
return content
def get_feature_list() -> str:
"""Get the features section formatted for docs."""
return readme_section("features", strip_heading=True)
def get_manual_control_intro() -> str:
"""Get the manual control introduction section."""
return readme_section("manual-control", strip_heading=True)
def get_automation_examples() -> str:
"""Get all automation examples from README."""
return readme_section("automation-examples", strip_heading=True)
def get_troubleshooting() -> str:
"""Get the troubleshooting section."""
return readme_section("troubleshooting", strip_heading=True)
def get_troubleshooting_intro() -> str:
"""Get just the troubleshooting intro (debug logging)."""
return readme_section("troubleshooting-intro", strip_heading=True)
def get_common_problems() -> str:
"""Get the common problems section."""
return readme_section("common-problems", strip_heading=True)
def get_brightness_modes() -> str:
"""Get the brightness modes explanation with graphs."""
return readme_section("brightness-modes", strip_heading=True)
def get_graphs() -> str:
"""Get the graphs section."""
return readme_section("graphs", strip_heading=True)
def get_sleep_mode_intro() -> str:
"""Get the sleep mode introduction."""
return readme_section("sleep-mode", strip_heading=True)
def get_see_also() -> str:
"""Get the see also section."""
return readme_section("see-also", strip_heading=True)
def get_config_example_full() -> str:
"""Get the full configuration example."""
return readme_section("config-example-full", strip_heading=False)
def get_change_switch_settings_docs() -> str:
"""Get the change_switch_settings service documentation."""
return readme_section("change-switch-settings", strip_heading=True)

1
docs/CNAME Normal file
View file

@ -0,0 +1 @@
adaptive-lighting.nijho.lt

View file

@ -0,0 +1,126 @@
---
icon: lucide/trending-up
---
# Brightness Modes
Enhance your control over brightness transitions during sunrise and sunset with the `brightness_mode` option.
## Available Modes
Adaptive Lighting supports three brightness modes:
| Mode | Description |
|------|-------------|
| `default` | Standard behavior based on sun position |
| `linear` | Linear ramp between min/max brightness |
| `tanh` | Smooth S-curve using hyperbolic tangent |
## Detailed Explanation
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_brightness_modes -->
<!-- print(get_brightness_modes()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
<details>
<summary>Enhance your control over brightness transitions during sunrise and sunset with <code>brightness_mode</code> (click here to learn more 🧠).</summary>
With Adaptive Lighting, you can set a `brightness_mode` to specify how the brightness changes during sunrise and sunset. The `brightness_mode` can be set to `"default"` ([as illustrated in other graphs above](#high_brightness-brightness)), `"linear"`, or `"tanh"`. If you choose to deviate from the `"default"` mode, you can adjust `brightness_mode_time_dark` and `brightness_mode_time_light` to further customize the lighting transitions.
When `brightness_mode` is set to `"linear"`:
- During **_sunset_**, the brightness begins to gradually decrease from `max_brightness` starting at `time=sunset_time - brightness_mode_time_light`, until it reaches `min_brightness` at `time=sunset_time + brightness_mode_time_dark`.
- During **_sunrise_**, the brightness begins to gradually increase from `min_brightness` starting at `time=sunrise_time - brightness_mode_time_dark`, until it reaches `max_brightness` at `time=sunrise_time + brightness_mode_time_light`.
When `brightness_mode` is set to `"tanh"`, it uses the smooth transition of a [hyperbolic tangent function](https://mathworld.wolfram.com/HyperbolicTangent.html):
- During **_sunset_**, the brightness starts to decrease from 95% of `max_brightness` starting at `time=sunset_time - brightness_mode_time_light`, until it reaches 5% of `min_brightness` at `time=sunset_time + brightness_mode_time_dark`.
- During **_sunrise_**, the brightness starts to increase from 5% of `min_brightness` starting at `time=sunrise_time - brightness_mode_time_dark`, until it reaches 95% of `max_brightness` at `time=sunrise_time + brightness_mode_time_light`.
</details>
Notice the values of `brightness_mode_time_light` and `brightness_mode_time_dark` in the text box.
![image](https://github.com/basnijholt/adaptive-lighting/assets/6897215/15143580-13cd-4ab2-a603-89f2b7830afd)
![image](https://github.com/basnijholt/adaptive-lighting/assets/6897215/f61fdac9-6d47-48c9-84ed-cbb451d5de5d)
![image](https://github.com/basnijholt/adaptive-lighting/assets/6897215/e5fc5d27-3c37-4e3d-93d1-6e7cf4b48e7c)
![image](https://github.com/basnijholt/adaptive-lighting/assets/6897215/3dcbdc42-63c4-49df-8651-d2fae53dd08d)
> Check out the interactive webapp on https://basnijholt.github.io/adaptive-lighting/ to play with the parameters and see how the brightness changes!
<!-- OUTPUT:END -->
## Configuration Parameters
When using `linear` or `tanh` modes, you can fine-tune the transition with these parameters:
| Parameter | Default | Description |
|-----------|---------|-------------|
| `brightness_mode_time_dark` | 900 (15 min) | Duration to ramp brightness before/after sunrise/sunset |
| `brightness_mode_time_light` | 3600 (1 hour) | Duration to ramp brightness after/before sunrise/sunset |
## Example Configurations
### Quick Transition (Linear)
```yaml
adaptive_lighting:
- name: "Quick transitions"
lights:
- light.living_room
brightness_mode: linear
brightness_mode_time_dark: 600 # 10 minutes
brightness_mode_time_light: 1800 # 30 minutes
```
### Smooth Transition (Tanh)
```yaml
adaptive_lighting:
- name: "Smooth transitions"
lights:
- light.bedroom
brightness_mode: tanh
brightness_mode_time_dark: 1200 # 20 minutes
brightness_mode_time_light: 3600 # 1 hour
```
## Graphs
These graphs show how brightness changes throughout the day based on calculated values:
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_graphs -->
<!-- print(get_graphs()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
These graphs were generated using the values calculated by the Adaptive Lighting sensor/switch(es).
### :sunny: Sun Position
![cl_percent|690x131](https://community-home-assistant-assets.s3.dualstack.us-west-2.amazonaws.com/original/3X/6/5/657ff98beb65a94598edeb4bdfd939095db1a22c.PNG)
### :thermometer: Color Temperature
![cl_color_temp|690x129](https://community-home-assistant-assets.s3.dualstack.us-west-2.amazonaws.com/original/3X/5/9/59e84263cbecd8e428cb08777a0413672c48dfcd.PNG)
### :high_brightness: Brightness
![cl_brightness|690x130](https://community-home-assistant-assets.s3.dualstack.us-west-2.amazonaws.com/original/3X/5/8/58ebd994b62a8b1abfb3497a5288d923ff4e2330.PNG)
### While using `transition_until_sleep: true`
![image](https://user-images.githubusercontent.com/2219836/228949675-f9699624-8abc-466c-bb04-250ce0f495b8.png)
<!-- OUTPUT:END -->
## Interactive Simulator
The best way to understand brightness modes is to experiment with the interactive simulator:
<div style="text-align: center; margin: 2rem 0;">
<a href="https://basnijholt.github.io/adaptive-lighting" class="simulator-link">
🌞 Try the Simulator 🌛
</a>
</div>
Adjust the `brightness_mode`, `brightness_mode_time_dark`, and `brightness_mode_time_light` parameters to see how they affect the brightness curve in real-time.

View file

@ -0,0 +1,163 @@
---
icon: lucide/hand
---
# Manual Control
Adaptive Lighting is designed to work seamlessly with manual adjustments, detecting when you or another source changes light settings and pausing adaptation accordingly.
## How It Works
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_manual_control_intro -->
<!-- print(get_manual_control_intro()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
Adaptive Lighting is designed to automatically detect when you or another source (e.g., automation) manually changes light settings 🕹️.
When this occurs, the affected light is marked as "manually controlled," and Adaptive Lighting will not make further adjustments until the light is turned off and back on or reset using the `adaptive_lighting.set_manual_control` service call.
This feature is available when `take_over_control` is enabled.
Additionally, enabling `detect_non_ha_changes` allows Adaptive Lighting to detect all state changes, including those made outside of Home Assistant, by comparing the light's state to its previously used settings.
The `adaptive_lighting.manual_control` event is fired when a light is marked as "manually controlled," allowing for integration with automations 🤖.
> ⚠️ **_Caution: Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Disable `detect_non_ha_changes` if you encounter such issues._**
<!-- OUTPUT:END -->
## Configuration Options
### take_over_control
When enabled (default: `true`), Adaptive Lighting detects `light.turn_on` service calls that specify brightness or color values. If such a call is detected for a light that's already on, that light is marked as "manually controlled".
```yaml
adaptive_lighting:
- name: "With manual control detection"
lights:
- light.living_room
take_over_control: true # default
```
### take_over_control_mode
Controls how adaptation pauses when manual changes are detected:
| Mode | Behavior |
|------|----------|
| `pause_all` | Pause both brightness and color adaptation (default) |
| `pause_changed` | Only pause adaptation of the changed attribute |
```yaml
adaptive_lighting:
- name: "Selective pause"
lights:
- light.living_room
take_over_control: true
take_over_control_mode: pause_changed # Only pause changed attributes
```
### detect_non_ha_changes
When enabled, Adaptive Lighting detects state changes made outside of Home Assistant by comparing the light's current state to its previously applied settings.
!!! warning "Use with caution"
Some lights may falsely report an "on" state, which could result in lights turning on unexpectedly. Disable this option if you encounter such issues.
```yaml
adaptive_lighting:
- name: "Detect external changes"
lights:
- light.living_room
take_over_control: true
detect_non_ha_changes: true
```
### autoreset_control_seconds
Automatically resets the manual control flag after a specified number of seconds. Set to `0` to disable (default).
```yaml
adaptive_lighting:
- name: "Auto-reset after 2 hours"
lights:
- light.living_room
take_over_control: true
autoreset_control_seconds: 7200 # 2 hours
```
### adapt_only_on_bare_turn_on
When enabled, Adaptive Lighting only adapts lights when `light.turn_on` is called without specifying brightness or color. This is useful when you want scenes to work without interference.
```yaml
adaptive_lighting:
- name: "Respect scenes"
lights:
- light.living_room
take_over_control: true
adapt_only_on_bare_turn_on: true
```
## Checking Manual Control Status
You can see which lights are marked as manually controlled by checking the switch attributes:
1. Go to **Developer Tools** → **States**
2. Find your Adaptive Lighting switch (e.g., `switch.adaptive_lighting_living_room`)
3. Look at the `manual_control` attribute - it lists all manually controlled lights
## Resetting Manual Control
### Via Service Call
```yaml
service: adaptive_lighting.set_manual_control
data:
entity_id: switch.adaptive_lighting_living_room
lights:
- light.floor_lamp
manual_control: false # Resume adaptation
```
### By Turning Light Off and On
Simply turning a light off and then back on will reset its manual control status.
### Via Automation
See [Automation Examples](../automation-examples.md) for automation recipes that automatically reset manual control.
## Events
When a light is marked as manually controlled, Adaptive Lighting fires an event:
**Event type:** `adaptive_lighting.manual_control`
**Event data:**
```yaml
entity_id: light.living_room
switch: switch.adaptive_lighting_living_room
```
You can use this event to trigger automations:
```yaml
automation:
- alias: "Notify on manual control"
trigger:
platform: event
event_type: adaptive_lighting.manual_control
action:
- service: notify.mobile_app
data:
message: "{{ trigger.event.data.entity_id }} was manually adjusted"
```
## Best Practices
1. **Use Zigbee groups** when controlling multiple bulbs together - this ensures consistent manual control detection
2. **Set reasonable autoreset times** if you want lights to eventually resume adaptation
3. **Use `pause_changed` mode** if you only adjust brightness or color individually
4. **Disable `detect_non_ha_changes`** if you experience unexpected light turn-ons

202
docs/advanced/sleep-mode.md Normal file
View file

@ -0,0 +1,202 @@
---
icon: lucide/moon
---
# Sleep Mode
Sleep mode is a special operating mode that sets your lights to minimal brightness and very warm color, perfect for winding down at night without disrupting your circadian rhythm.
## Overview
When sleep mode is activated, Adaptive Lighting overrides normal sun-based calculations with fixed sleep settings:
| Setting | Default | Description |
|---------|---------|-------------|
| `sleep_brightness` | 1% | Very dim lighting |
| `sleep_color_temp` | 1000K | Very warm (candlelight) |
| `sleep_rgb_color` | `[255, 56, 0]` | Deep orange/red |
| `sleep_transition` | 1 second | Quick transition |
## Activating Sleep Mode
Each Adaptive Lighting configuration creates a sleep mode switch:
```
switch.adaptive_lighting_sleep_mode_<name>
```
Turn it on to activate sleep mode:
```yaml
service: switch.turn_on
target:
entity_id: switch.adaptive_lighting_sleep_mode_living_room
```
## Configuration Options
### sleep_brightness
Sets the brightness level during sleep mode (1-100%).
```yaml
adaptive_lighting:
- name: "Bedroom"
lights:
- light.bedroom
sleep_brightness: 1 # Barely visible
```
### sleep_rgb_or_color_temp
Choose whether to use color temperature or RGB color in sleep mode:
| Value | Description |
|-------|-------------|
| `color_temp` | Use `sleep_color_temp` (default) |
| `rgb_color` | Use `sleep_rgb_color` |
### sleep_color_temp
The color temperature in Kelvin during sleep mode (when using `color_temp` mode).
```yaml
adaptive_lighting:
- name: "Bedroom"
lights:
- light.bedroom
sleep_rgb_or_color_temp: color_temp
sleep_color_temp: 1000 # Very warm
```
### sleep_rgb_color
The RGB color during sleep mode (when using `rgb_color` mode). Useful for bulbs that don't support very low color temperatures.
```yaml
adaptive_lighting:
- name: "Bedroom"
lights:
- light.bedroom
sleep_rgb_or_color_temp: rgb_color
sleep_rgb_color: [255, 56, 0] # Deep orange/red
```
### sleep_transition
The transition duration when entering or exiting sleep mode.
```yaml
adaptive_lighting:
- name: "Bedroom"
lights:
- light.bedroom
sleep_transition: 10 # Slow 10-second transition
```
### transition_until_sleep
When enabled, Adaptive Lighting treats sleep settings as the minimum values and gradually transitions to them after sunset. This creates a natural wind-down effect.
```yaml
adaptive_lighting:
- name: "Evening wind-down"
lights:
- light.living_room
transition_until_sleep: true
sleep_brightness: 10
sleep_color_temp: 2000
```
## Automation Examples
### Enable Sleep Mode at Bedtime
```yaml
automation:
- alias: "Bedtime - enable sleep mode"
trigger:
- platform: time
at: "22:00:00"
action:
- service: switch.turn_on
target:
entity_id:
- switch.adaptive_lighting_sleep_mode_bedroom
- switch.adaptive_lighting_sleep_mode_hallway
```
### Sync with Input Boolean
```yaml
input_boolean:
sleep_mode:
name: "Sleep Mode"
icon: mdi:sleep
automation:
- alias: "Toggle sleep mode switches"
trigger:
- platform: state
entity_id: input_boolean.sleep_mode
action:
- service: "switch.turn_{{ states('input_boolean.sleep_mode') }}"
target:
entity_id:
- switch.adaptive_lighting_sleep_mode_bedroom
- switch.adaptive_lighting_sleep_mode_living_room
```
### Disable Sleep Mode at Wake Time
```yaml
automation:
- alias: "Morning - disable sleep mode"
trigger:
- platform: time
at: "07:00:00"
action:
- service: switch.turn_off
target:
entity_id:
- switch.adaptive_lighting_sleep_mode_bedroom
- switch.adaptive_lighting_sleep_mode_living_room
```
### Motion-Activated Night Light
```yaml
automation:
- alias: "Hallway night light"
trigger:
- platform: state
entity_id: binary_sensor.hallway_motion
to: "on"
condition:
- condition: time
after: "22:00:00"
before: "06:00:00"
action:
- service: switch.turn_on
target:
entity_id: switch.adaptive_lighting_sleep_mode_hallway
- service: light.turn_on
target:
entity_id: light.hallway
- wait_for_trigger:
- platform: state
entity_id: binary_sensor.hallway_motion
to: "off"
for: "00:02:00"
- service: light.turn_off
target:
entity_id: light.hallway
```
## Best Practices
1. **Use very low brightness** (1-5%) for true sleep mode
2. **Use warm colors** (1000-2000K) to minimize blue light exposure
3. **Consider RGB mode** if your bulbs can't achieve very warm temperatures
4. **Set up automations** to automatically enable/disable sleep mode
5. **Use `transition_until_sleep`** for a gradual evening wind-down

BIN
docs/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,203 @@
/* Adaptive Lighting Documentation Custom Styles */
/* Sun-themed color adjustments */
:root {
--md-primary-fg-color: #FF9800;
--md-primary-fg-color--light: #FFB74D;
--md-primary-fg-color--dark: #F57C00;
--md-accent-fg-color: #FFC107;
}
[data-md-color-scheme="slate"] {
--md-primary-fg-color: #FFB74D;
--md-accent-fg-color: #FFD54F;
}
/* Hero section on index page */
.md-content__inner > h1:first-child {
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
/* Logo in header - make it slightly larger */
.md-header__button.md-logo img {
height: 1.8rem;
}
/* Feature grid for homepage */
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.feature-card {
padding: 1.25rem;
border-radius: 0.5rem;
background: var(--md-code-bg-color);
border: 1px solid var(--md-default-fg-color--lightest);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.feature-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.feature-card h3 {
margin-top: 0;
font-size: 1.1rem;
}
.feature-card p {
margin-bottom: 0;
font-size: 0.9rem;
color: var(--md-default-fg-color--light);
}
/* Configuration table improvements */
.md-typeset table:not([class]) {
font-size: 0.85rem;
}
.md-typeset table:not([class]) th {
background-color: var(--md-primary-fg-color);
color: white;
}
[data-md-color-scheme="slate"] .md-typeset table:not([class]) th {
background-color: var(--md-primary-fg-color--dark);
}
/* Code block in table cells */
.md-typeset table:not([class]) code {
font-size: 0.8rem;
padding: 0.1rem 0.3rem;
}
/* Admonition styling - sun theme */
.md-typeset .admonition.tip,
.md-typeset details.tip {
border-color: #FFC107;
}
.md-typeset .admonition.tip > .admonition-title,
.md-typeset details.tip > summary {
background-color: rgba(255, 193, 7, 0.1);
}
.md-typeset .admonition.tip > .admonition-title::before,
.md-typeset details.tip > summary::before {
background-color: #FFC107;
}
/* Service section styling */
.service-section {
margin: 2rem 0;
padding: 1.5rem;
background: var(--md-code-bg-color);
border-radius: 0.5rem;
border-left: 4px solid var(--md-primary-fg-color);
}
.service-section h2 {
margin-top: 0;
}
/* Badge styling for status indicators */
.badge {
display: inline-block;
padding: 0.2rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.badge-required {
background-color: #f44336;
color: white;
}
.badge-optional {
background-color: #4caf50;
color: white;
}
/* Responsive video container */
.video-container {
position: relative;
width: 100%;
padding-bottom: 56.25%;
margin: 1rem 0;
}
.video-container iframe,
.video-container video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 0.5rem;
}
/* Simulator link button styling */
.simulator-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
background: linear-gradient(135deg, #FF9800, #FFC107);
color: white !important;
border-radius: 2rem;
font-weight: 600;
text-decoration: none !important;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.simulator-link:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 152, 0, 0.4);
}
/* Hide doctoc generated content in docs (we use mkdocs nav) */
.md-typeset [id^="doctoc"] {
display: none;
}
/* Details/collapsible sections - match README style */
.md-typeset details {
margin: 1rem 0;
}
.md-typeset details summary {
cursor: pointer;
font-weight: 500;
}
/* Emoji in headings - ensure proper sizing */
.md-typeset h1 .twemoji,
.md-typeset h2 .twemoji,
.md-typeset h3 .twemoji {
height: 1em;
vertical-align: -0.1em;
}
/* Graph images */
.md-typeset img[alt*="graph"],
.md-typeset img[alt*="brightness"],
.md-typeset img[alt*="color_temp"] {
border-radius: 0.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* Contributors section - hide in docs, link to README instead */
.contributors-section {
text-align: center;
padding: 2rem;
background: var(--md-code-bg-color);
border-radius: 0.5rem;
margin: 2rem 0;
}

236
docs/automation-examples.md Normal file
View file

@ -0,0 +1,236 @@
---
icon: lucide/bot
---
# Automation Examples
Real-world automation examples showing how to integrate Adaptive Lighting with your Home Assistant setup.
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_automation_examples -->
<!-- print(get_automation_examples()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
<details>
<summary>Reset the <code>manual_control</code> status of a light after an hour.</summary>
```yaml
- alias: "Adaptive lighting: reset manual_control after 1 hour"
mode: parallel
trigger:
platform: event
event_type: adaptive_lighting.manual_control
variables:
light: "{{ trigger.event.data.entity_id }}"
switch: "{{ trigger.event.data.switch }}"
action:
- delay: "01:00:00"
- condition: template
value_template: "{{ light in state_attr(switch, 'manual_control') }}"
- service: adaptive_lighting.set_manual_control
data:
entity_id: "{{ switch }}"
lights: "{{ light }}"
manual_control: false
```
</details>
<details>
<summary>Toggle multiple Adaptive Lighting switches to "sleep mode" using an <code>input_boolean.sleep_mode</code>.</summary>
```yaml
- alias: "Adaptive lighting: toggle 'sleep mode'"
trigger:
- platform: state
entity_id: input_boolean.sleep_mode
- platform: homeassistant
event: start # in case the states aren't properly restored
variables:
sleep_mode: "{{ states('input_boolean.sleep_mode') }}"
action:
service: "switch.turn_{{ sleep_mode }}"
entity_id:
- switch.adaptive_lighting_sleep_mode_living_room
- switch.adaptive_lighting_sleep_mode_bedroom
```
Set your sunrise and sunset time based on your alarm. The below script sets sunset_time exactly 12 hours after the custom sunrise time.
```yaml
iphone_carly_wakeup:
alias: iPhone Carly Wakeup
sequence:
- condition: state
entity_id: input_boolean.carly_iphone_wakeup
state: "off"
- service: input_datetime.set_datetime
target:
entity_id: input_datetime.carly_iphone_wakeup
data:
time: '{{ now().strftime("%H:%M:%S") }}'
- service: input_boolean.turn_on
target:
entity_id: input_boolean.carly_iphone_wakeup
- repeat:
count: >
{{ (states.switch
| map(attribute="entity_id")
| select(">","switch.adaptive_lighting_al_")
| select("<", "switch.adaptive_lighting_al_z")
| join(",")
).split(",") | length }}
sequence:
- service: adaptive_lighting.change_switch_settings
data:
entity_id: switch.adaptive_lighting_al_den_ceilingfan_lights
sunrise_time: '{{ now().strftime("%H:%M:%S") }}'
sunset_time: >
{{ (as_timestamp(now()) + 12*60*60) | timestamp_custom("%H:%M:%S") }}
- service: script.turn_on
target:
entity_id: script.run_wakeup_routine
- service: input_boolean.turn_off
target:
entity_id:
- input_boolean.carly_iphone_winddown
- input_boolean.carly_iphone_bedtime
- service: input_datetime.set_datetime
target:
entity_id: input_datetime.wakeup_time
data:
time: '{{ now().strftime("%H:%M:%S") }}'
- service: script.adaptive_lighting_disable_sleep_mode
mode: queued
icon: mdi:weather-sunset
max: 10
```
</details>
<!-- OUTPUT:END -->
---
## Additional Examples
### Enable Sleep Mode at Bedtime
```yaml
automation:
- alias: "Enable sleep mode at bedtime"
trigger:
- platform: time
at: "22:00:00"
action:
- service: switch.turn_on
target:
entity_id:
- switch.adaptive_lighting_sleep_mode_bedroom
- switch.adaptive_lighting_sleep_mode_living_room
```
### Disable Sleep Mode in the Morning
```yaml
automation:
- alias: "Disable sleep mode in the morning"
trigger:
- platform: time
at: "07:00:00"
action:
- service: switch.turn_off
target:
entity_id:
- switch.adaptive_lighting_sleep_mode_bedroom
- switch.adaptive_lighting_sleep_mode_living_room
```
### Pause Adaptation During Movie Mode
```yaml
automation:
- alias: "Movie mode - pause adaptation"
trigger:
- platform: state
entity_id: input_boolean.movie_mode
to: "on"
action:
- service: switch.turn_off
target:
entity_id: switch.adaptive_lighting_living_room
- service: light.turn_on
target:
entity_id: light.living_room
data:
brightness_pct: 10
color_temp_kelvin: 2200
- alias: "Movie mode - resume adaptation"
trigger:
- platform: state
entity_id: input_boolean.movie_mode
to: "off"
action:
- service: switch.turn_on
target:
entity_id: switch.adaptive_lighting_living_room
```
### Sync Sleep Mode with Bedtime Routine
```yaml
automation:
- alias: "Bedtime routine activates sleep mode"
trigger:
- platform: state
entity_id: input_boolean.bedtime
to: "on"
action:
- service: switch.turn_on
target:
entity_id:
- switch.adaptive_lighting_sleep_mode_bedroom
- switch.adaptive_lighting_sleep_mode_hallway
- service: light.turn_on
target:
entity_id: light.bedroom
```
### Reset Manual Control When Leaving Home
```yaml
automation:
- alias: "Reset manual control when leaving"
trigger:
- platform: state
entity_id: person.your_name
from: "home"
action:
- service: adaptive_lighting.set_manual_control
data:
entity_id: switch.adaptive_lighting_living_room
manual_control: false
```
### Adjust Settings Based on Weather
```yaml
automation:
- alias: "Cloudy day - increase brightness"
trigger:
- platform: state
entity_id: weather.home
condition:
- condition: state
entity_id: weather.home
state: "cloudy"
action:
- service: adaptive_lighting.change_switch_settings
data:
entity_id: switch.adaptive_lighting_living_room
min_brightness: 40
max_brightness: 100
```

145
docs/configuration.md Normal file
View file

@ -0,0 +1,145 @@
---
icon: lucide/settings
---
# Configuration
Adaptive Lighting supports configuration through both YAML and the Home Assistant UI, with identical option names in both methods.
## Basic Configuration
The minimal configuration requires only adding the integration to your `configuration.yaml`:
```yaml
adaptive_lighting:
```
You can then configure everything through the UI at **Settings****Devices & Services****Adaptive Lighting****Configure**.
## YAML Configuration
For YAML configuration, you can specify lights and options directly:
```yaml
adaptive_lighting:
- name: "Living Room"
lights:
- light.living_room_ceiling
- light.living_room_lamp
```
## All Options
All configuration options are listed below with their default values. These options work identically in both YAML and the UI.
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from _docs_helpers import generate_config_markdown_table -->
<!-- print(generate_config_markdown_table()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- OUTPUT:END -->
## Full Configuration Example
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_config_example_full -->
<!-- print(get_config_example_full()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- OUTPUT:END -->
## Multiple Configurations
You can create multiple Adaptive Lighting configurations for different areas or use cases:
```yaml
adaptive_lighting:
- name: "Daytime Spaces"
lights:
- light.living_room
- light.kitchen
- light.office
min_brightness: 30
max_brightness: 100
- name: "Bedroom"
lights:
- light.bedroom_ceiling
- light.bedroom_lamp
min_brightness: 5
max_brightness: 80
sleep_brightness: 1
sleep_color_temp: 1000
- name: "Night Lights"
lights:
- light.hallway_night
- light.bathroom_night
min_brightness: 1
max_brightness: 20
```
## Option Categories
### Brightness Settings
| Option | Description |
|--------|-------------|
| `min_brightness` | Minimum brightness (1-100%) |
| `max_brightness` | Maximum brightness (1-100%) |
| `brightness_mode` | How brightness changes (`default`, `linear`, `tanh`) |
| `brightness_mode_time_dark` | Ramp duration before/after sunrise/sunset |
| `brightness_mode_time_light` | Ramp duration after/before sunrise/sunset |
See [Brightness Modes](advanced/brightness-modes.md) for detailed explanations.
### Color Temperature Settings
| Option | Description |
|--------|-------------|
| `min_color_temp` | Warmest temperature in Kelvin |
| `max_color_temp` | Coldest temperature in Kelvin |
| `prefer_rgb_color` | Use RGB instead of color temperature |
### Sleep Mode Settings
| Option | Description |
|--------|-------------|
| `sleep_brightness` | Brightness in sleep mode (1-100%) |
| `sleep_color_temp` | Color temperature in sleep mode |
| `sleep_rgb_color` | RGB color for sleep mode |
| `sleep_transition` | Transition duration for sleep mode |
| `transition_until_sleep` | Gradually transition to sleep settings after sunset |
See [Sleep Mode](advanced/sleep-mode.md) for more details.
### Manual Control Settings
| Option | Description |
|--------|-------------|
| `take_over_control` | Detect manual changes and pause adaptation |
| `detect_non_ha_changes` | Detect changes made outside Home Assistant |
| `autoreset_control_seconds` | Auto-reset manual control after this many seconds |
See [Manual Control](advanced/manual-control.md) for detailed behavior.
### Timing Settings
| Option | Description |
|--------|-------------|
| `sunrise_time` | Fixed sunrise time (overrides actual) |
| `sunset_time` | Fixed sunset time (overrides actual) |
| `sunrise_offset` | Offset from actual sunrise in seconds |
| `sunset_offset` | Offset from actual sunset in seconds |
| `min_sunrise_time` / `max_sunrise_time` | Constrain virtual sunrise |
| `min_sunset_time` / `max_sunset_time` | Constrain virtual sunset |
### Transition Settings
| Option | Description |
|--------|-------------|
| `transition` | Duration for regular transitions (seconds) |
| `initial_transition` | Duration when lights first turn on |
| `interval` | How often to adapt lights (seconds) |

117
docs/getting-started.md Normal file
View file

@ -0,0 +1,117 @@
---
icon: lucide/rocket
---
# Getting Started
This guide will help you install and configure Adaptive Lighting for the first time.
## Prerequisites
- [Home Assistant](https://www.home-assistant.io/) 2024.12.0 or newer
- [HACS](https://hacs.xyz/) (Home Assistant Community Store) installed
## Installation
### Via HACS (Recommended)
1. Open HACS in your Home Assistant instance
2. Click on **Integrations**
3. Click the **+ Explore & Download Repositories** button
4. Search for "Adaptive Lighting"
5. Click **Download**
6. Restart Home Assistant
Or use this button to open HACS directly:
[![Open your Home Assistant instance and open the Adaptive Lighting integration inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=basnijholt&repository=adaptive-lighting&category=integration)
### Manual Installation
1. Download the latest release from [GitHub](https://github.com/basnijholt/adaptive-lighting/releases)
2. Extract the `adaptive_lighting` folder to your `config/custom_components/` directory
3. Restart Home Assistant
## Configuration
### Step 1: Add to configuration.yaml
Add the following to your `configuration.yaml`:
```yaml
adaptive_lighting:
```
!!! note
This entry is required even if you plan to configure everything through the UI.
### Step 2: Restart Home Assistant
Restart Home Assistant for the changes to take effect.
### Step 3: Add the Integration
1. Go to **Settings** → **Devices & Services**
2. Click **+ Add Integration**
3. Search for "Adaptive Lighting"
4. Follow the setup wizard to select your lights
### Step 4: Configure Your Lights
You can configure Adaptive Lighting in two ways:
=== "Via UI"
1. Go to **Settings** → **Devices & Services**
2. Find Adaptive Lighting and click **Configure**
3. Adjust settings as needed
=== "Via YAML"
```yaml
adaptive_lighting:
- name: "Living Room"
lights:
- light.living_room_ceiling
- light.living_room_lamp
min_brightness: 20
max_brightness: 100
min_color_temp: 2200
max_color_temp: 5500
```
## Basic Configuration Example
Here's a simple configuration to get you started:
```yaml
adaptive_lighting:
- name: "Main Lights"
lights:
- light.living_room
- light.bedroom
- light.kitchen
transition: 30
min_brightness: 10
max_brightness: 100
min_color_temp: 2000
max_color_temp: 5500
```
## Verifying Installation
After configuration, you should see new switches in Home Assistant:
- `switch.adaptive_lighting_main_lights`
- `switch.adaptive_lighting_sleep_mode_main_lights`
- `switch.adaptive_lighting_adapt_brightness_main_lights`
- `switch.adaptive_lighting_adapt_color_main_lights`
Turn on `switch.adaptive_lighting_main_lights` to start adapting your lights!
## Next Steps
- [Configuration Reference](configuration.md) - Explore all available options
- [Services](services.md) - Learn about service calls for automations
- [Automation Examples](automation-examples.md) - See real-world automation recipes
- [Troubleshooting](troubleshooting.md) - Common issues and solutions

88
docs/index.md Normal file
View file

@ -0,0 +1,88 @@
---
icon: lucide/sun
hide:
- navigation
---
# Adaptive Lighting
**Enhance Your Home's Atmosphere with Smart, Sun-Synchronized Lighting**
<div style="text-align: center; margin: 2rem 0;">
<img src="assets/logo.png" alt="Adaptive Lighting Logo" width="200" />
</div>
[Adaptive Lighting](https://github.com/basnijholt/adaptive-lighting) is a custom component for [Home Assistant](https://www.home-assistant.io/) that intelligently adjusts the brightness and color of your lights based on the sun's position, while still allowing for manual control.
<div style="text-align: center; margin: 2rem 0;">
<a href="https://my.home-assistant.io/redirect/hacs_repository/?owner=basnijholt&repository=adaptive-lighting&category=integration" class="md-button md-button--primary">
Install via HACS
</a>
<a href="https://basnijholt.github.io/adaptive-lighting" class="md-button">
Try the Simulator
</a>
</div>
By automatically adapting the settings of your lights throughout the day, Adaptive Lighting helps maintain your natural circadian rhythm, which can lead to improved sleep, mood, and overall well-being. Experience cooler color temperatures at noon, gradually transitioning to warmer colors at sunset and sunrise.
## Features
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_feature_list -->
<!-- print(get_feature_list()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
When initially turning on a light that is controlled by Adaptive Lighting, the `light.turn_on` service call is intercepted, and the light's brightness and color are automatically adjusted based on the sun's position.
After that, the light's brightness and color are automatically adjusted at a regular interval.
Adaptive Lighting provides four switches (using "living_room" as an example component name):
- `switch.adaptive_lighting_living_room`: Turn Adaptive Lighting on or off and view current light settings through its attributes.
- `switch.adaptive_lighting_sleep_mode_living_room`: Activate "sleep mode" 😴 and set custom sleep_brightness and sleep_color_temp.
- `switch.adaptive_lighting_adapt_brightness_living_room`: Enable or disable brightness adaptation 🔆 for supported lights.
- `switch.adaptive_lighting_adapt_color_living_room`: Enable or disable color adaptation 🌈 for supported lights.
<!-- OUTPUT:END -->
## Quick Start
1. **Install via HACS**: Search for "Adaptive Lighting" in the [Home Assistant Community Store](https://hacs.xyz/)
2. **Add to configuration**: Add `adaptive_lighting:` to your `configuration.yaml`
3. **Configure**: Go to **Settings****Devices & Services****Add Integration** → **Adaptive Lighting**
4. **Select your lights**: Choose which lights to control and enjoy automatic adaptation!
```yaml
# Minimal configuration.yaml entry
adaptive_lighting:
lights:
- light.living_room
```
!!! tip "Using the UI exclusively?"
Even if you plan to configure everything through the UI, the `adaptive_lighting:` entry must still be present in your `configuration.yaml`.
[Get Started →](getting-started.md){ .md-button .md-button--primary }
[View All Options →](configuration.md){ .md-button }
## How It Works
Adaptive Lighting provides four switches for each configuration (using "living_room" as an example):
| Switch | Purpose |
|--------|---------|
| `switch.adaptive_lighting_living_room` | Main on/off control |
| `switch.adaptive_lighting_sleep_mode_living_room` | Activate sleep mode |
| `switch.adaptive_lighting_adapt_brightness_living_room` | Enable/disable brightness adaptation |
| `switch.adaptive_lighting_adapt_color_living_room` | Enable/disable color adaptation |
## Interactive Simulator
Visualize how Adaptive Lighting will work with your settings using the interactive simulator:
<div style="text-align: center; margin: 2rem 0;">
<a href="https://basnijholt.github.io/adaptive-lighting" class="simulator-link">
🌞 Adaptive Lighting Simulator 🌛
</a>
</div>

View file

@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Run markdown-code-runner on all documentation files.
This script processes all Markdown files in the docs/ directory that contain
CODE blocks, executing them and updating the OUTPUT sections.
Usage:
python docs/run_markdown_code_runner.py
"""
from __future__ import annotations
import subprocess
import sys
from pathlib import Path
# Add the custom_components to the path for imports
REPO_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(REPO_ROOT / "custom_components"))
sys.path.insert(0, str(REPO_ROOT))
def find_markdown_files() -> list[Path]:
"""Find all Markdown files in the docs directory."""
docs_dir = REPO_ROOT / "docs"
return list(docs_dir.rglob("*.md"))
def has_code_blocks(file_path: Path) -> bool:
"""Check if a Markdown file has CODE blocks."""
content = file_path.read_text()
return "<!-- CODE:START -->" in content or "```python markdown-code-runner" in content
def run_markdown_code_runner(file_path: Path) -> bool:
"""Run markdown-code-runner on a single file."""
try:
result = subprocess.run(
["markdown-code-runner", str(file_path)],
capture_output=True,
text=True,
check=True,
env={
**subprocess.os.environ,
"PYTHONPATH": f"{REPO_ROOT / 'custom_components'}:{REPO_ROOT}",
},
)
return True
except subprocess.CalledProcessError as e:
print(f" Error: {e.stderr}")
return False
except FileNotFoundError:
print(" Error: markdown-code-runner not found. Install with: pip install markdown-code-runner")
return False
def main() -> int:
"""Process all Markdown files with CODE blocks."""
print("Finding Markdown files with CODE blocks...")
markdown_files = find_markdown_files()
files_with_code = [f for f in markdown_files if has_code_blocks(f)]
if not files_with_code:
print("No files with CODE blocks found.")
return 0
print(f"Found {len(files_with_code)} files with CODE blocks:")
success_count = 0
failure_count = 0
for file_path in sorted(files_with_code):
relative_path = file_path.relative_to(REPO_ROOT)
print(f"\nProcessing {relative_path}...")
if run_markdown_code_runner(file_path):
print(f" ✓ Success")
success_count += 1
else:
print(f" ✗ Failed")
failure_count += 1
print(f"\n{'='*50}")
print(f"Results: {success_count} succeeded, {failure_count} failed")
return 1 if failure_count > 0 else 0
if __name__ == "__main__":
sys.exit(main())

74
docs/see-also.md Normal file
View file

@ -0,0 +1,74 @@
---
icon: lucide/external-link
---
# See Also
Resources, tutorials, and related projects for Adaptive Lighting.
## Tutorials & Articles
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_see_also -->
<!-- print(get_see_also()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
- [*Sleep better with Adaptive Lighting in Home Assistant*](https://wartner.io/sleep-better-with-adaptive-lightning-in-home-assistant/) by Florian Wartner on 2023-02-23 (blog post 📜)
- [*Automatic smart light brightness and color based on the sun*](https://www.youtube.com/watch?v=Rg3zI1Oyk3c) by Home Automation Guy on 2022-08-31 (YouTube video 📺)
- [*Adaptive Lighting Blew My Mind in Home Assistant - How to set it up*](https://www.youtube.com/watch?v=c1cnccmgl3k) by Smart Home Junkie on 2022-06-26 (YouTube video 📺)
<!-- OUTPUT:END -->
## Interactive Tools
### Adaptive Lighting Simulator
Visualize how different settings affect your lighting throughout the day:
<div style="text-align: center; margin: 2rem 0;">
<a href="https://basnijholt.github.io/adaptive-lighting" class="simulator-link">
🌞 Open the Simulator 🌛
</a>
</div>
The simulator lets you:
- Adjust all configuration parameters in real-time
- See how brightness and color temperature change throughout the day
- Visualize the effects of different `brightness_mode` settings
- Test sleep mode transitions
## Related Projects
### Circadian Lighting
Adaptive Lighting was initially inspired by [hass-circadian_lighting](https://github.com/claytonjn/hass-circadian_lighting) by @claytonjn, but has since been entirely rewritten and expanded with many new features.
## Official Documentation
- [Home Assistant Documentation (PR Preview)](https://deploy-preview-14877--home-assistant-docs.netlify.app/integrations/adaptive_lighting/)
- [Home Assistant Community Store (HACS)](https://hacs.xyz/)
## Community
- [Home Assistant Community Forums](https://community.home-assistant.io/)
- [Home Assistant Discord](https://discord.gg/home-assistant)
- [Reddit r/homeassistant](https://www.reddit.com/r/homeassistant/)
## Contributing
Interested in contributing to Adaptive Lighting?
- [GitHub Repository](https://github.com/basnijholt/adaptive-lighting)
- [Issue Tracker](https://github.com/basnijholt/adaptive-lighting/issues)
- [Translation via Weblate](https://hosted.weblate.org/engage/adaptive-lighting/)
### Translation
Help translate Adaptive Lighting into your language on [Hosted Weblate](https://hosted.weblate.org/engage/adaptive-lighting/). No programming knowledge required!
<a href="https://hosted.weblate.org/engage/adaptive-lighting/">
<img src="https://hosted.weblate.org/widget/adaptive-lighting/multi-auto.svg" alt="Translation status" />
</a>

166
docs/services.md Normal file
View file

@ -0,0 +1,166 @@
---
icon: lucide/zap
---
# Services
Adaptive Lighting provides three services for programmatic control, allowing you to integrate with automations and scripts.
## adaptive_lighting.apply
Applies the current Adaptive Lighting settings to lights on demand. Useful for forcing an immediate update or applying settings to lights that aren't in the regular adaptation cycle.
### Parameters
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from _docs_helpers import generate_apply_markdown_table -->
<!-- print(generate_apply_markdown_table()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- OUTPUT:END -->
### Example Usage
```yaml
# Apply current settings to specific lights
service: adaptive_lighting.apply
data:
entity_id: switch.adaptive_lighting_living_room
lights:
- light.floor_lamp
- light.desk_lamp
turn_on_lights: false
```
```yaml
# Force apply with custom transition
service: adaptive_lighting.apply
data:
entity_id: switch.adaptive_lighting_bedroom
transition: 5
adapt_brightness: true
adapt_color: true
```
---
## adaptive_lighting.set_manual_control
Marks or unmarks a light as "manually controlled". When a light is marked as manually controlled, Adaptive Lighting will not adjust it until the manual control flag is cleared.
### Parameters
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from _docs_helpers import generate_set_manual_control_markdown_table -->
<!-- print(generate_set_manual_control_markdown_table()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- OUTPUT:END -->
### Example Usage
```yaml
# Remove manual control from a light (resume adaptation)
service: adaptive_lighting.set_manual_control
data:
entity_id: switch.adaptive_lighting_living_room
lights:
- light.floor_lamp
manual_control: false
```
```yaml
# Mark a light as manually controlled (pause adaptation)
service: adaptive_lighting.set_manual_control
data:
entity_id: switch.adaptive_lighting_living_room
lights:
- light.floor_lamp
manual_control: true
```
```yaml
# Only pause brightness adaptation, continue color adaptation
service: adaptive_lighting.set_manual_control
data:
entity_id: switch.adaptive_lighting_living_room
lights:
- light.floor_lamp
manual_control: brightness
```
---
## adaptive_lighting.change_switch_settings
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_change_switch_settings_docs -->
<!-- print(get_change_switch_settings_docs()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- OUTPUT:END -->
### Example Usage
```yaml
# Temporarily change color temperature range
service: adaptive_lighting.change_switch_settings
data:
entity_id: switch.adaptive_lighting_living_room
min_color_temp: 2500
max_color_temp: 4000
```
```yaml
# Override sunrise time for the day
service: adaptive_lighting.change_switch_settings
data:
entity_id: switch.adaptive_lighting_bedroom
sunrise_time: "07:00:00"
use_defaults: current
```
```yaml
# Reset to configuration defaults
service: adaptive_lighting.change_switch_settings
data:
entity_id: switch.adaptive_lighting_living_room
use_defaults: configuration
```
---
## Events
Adaptive Lighting also fires events that you can use in automations.
### adaptive_lighting.manual_control
Fired when a light is marked as "manually controlled" due to a detected manual change.
**Event Data:**
| Attribute | Description |
|-----------|-------------|
| `entity_id` | The light that was marked as manually controlled |
| `switch` | The Adaptive Lighting switch entity |
### Example Automation
```yaml
automation:
- alias: "Log manual control events"
trigger:
platform: event
event_type: adaptive_lighting.manual_control
action:
- service: notify.mobile_app
data:
title: "Adaptive Lighting"
message: "{{ trigger.event.data.entity_id }} was manually controlled"
```
See [Automation Examples](automation-examples.md) for more use cases.

164
docs/troubleshooting.md Normal file
View file

@ -0,0 +1,164 @@
---
icon: lucide/life-buoy
---
# Troubleshooting
This guide covers common issues and their solutions when using Adaptive Lighting.
## Enable Debug Logging
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_troubleshooting_intro -->
<!-- print(get_troubleshooting_intro()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
Encountering issues? Enable debug logging in your `configuration.yaml`:
```yaml
logger:
default: warning
logs:
custom_components.adaptive_lighting: debug
```
After the issue occurs, create a new issue report with the log (`/config/home-assistant.log`).
<!-- OUTPUT:END -->
## Common Problems & Solutions
<!-- CODE:START -->
<!-- import sys; sys.path.insert(0, 'custom_components/adaptive_lighting') -->
<!-- from docs_gen import get_common_problems -->
<!-- print(get_common_problems()) -->
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
#### :bulb: Lights Not Responding or Turning On by Themselves
Adaptive Lighting sends more commands to lights than a typical human user would. If your light control network is unhealthy, you may experience:
- Laggy manual commands (e.g., turning lights on or off).
- Unresponsive lights.
- Home Assistant reporting incorrect light states, causing Adaptive Lighting to inadvertently turn lights back on.
Most issues that appear to be caused by Adaptive Lighting are actually due to unrelated problems.
Addressing these issues will significantly improve your Home Assistant experience.
In case lights are suddenly turning on by themselves, this is most likely due to the light incorrectly reporting an "on" state to Home Assistant, leading to an undesired Adaptive Lighting action.
To prevent adapting in cases *where the state of the light is suddenly "on" and only adapt if there is an associated `light.turn_on` service call*, set `detect_non_ha_changes: false`.
#### :signal_strength: WiFi Networks
Ensure your light bulbs have a strong WiFi connection. If the signal strength is less than -70dBm, the connection may be weak and prone to dropping messages.
#### :spider_web: Zigbee, Z-Wave, and Other Mesh Networks
Mesh networks typically require powered devices to act as routers, relaying messages back to the central coordinator (the radio connected to Home Assistant).
Philips lights usually function as routers, while Ikea, Sengled, and generic Tuya bulbs often do not.
If devices become unresponsive or fail to respond to commands, Adaptive Lighting can exacerbate the issue.
Use network maps (available in ZHA, zigbee2mqtt, deCONZ, and ZWaveJS UI) to evaluate your network health.
Smart plugs can be an affordable way to add more routers to your network.
For most Zigbee networks, **using groups is essential for optimal performance**.
For example, if you want to use Adaptive Lighting in a hallway with six bulbs, adding each bulb individually to the Adaptive Lighting configuration could overwhelm the network with commands.
Instead, create a group in your Zigbee software (not a regular Home Assistant group) and add that single group to the Adaptive Lighting configuration.
This sends a single broadcast command to adjust all bulbs, improving response times and keeping the bulbs in sync.
As a rule of thumb, if you always control lights together (e.g., bulbs in a ceiling fixture), they should be in a Zigbee group.
Expose only the group (not individual bulbs) in Home Assistant Dashboards and external systems like Google Home or Apple HomeKit.
> :warning: **If you control lights individually, `manual_control` cannot behave correctly! If you need to control lights individually as well, use a [Home Assistant Light Group](https://www.home-assistant.io/integrations/group/).**
#### :rainbow: Light Colors Not Matching
Bulbs from different manufacturers or models may have varying color temperature specifications. For instance, if you have two Adaptive Lighting configurations—one with only Philips Hue White Ambiance bulbs and another with a mix of Philips Hue White Ambiance and Sengled bulbs—the Philips Hue bulbs may appear to have different color temperatures despite having identical settings.
To resolve this:
1. Include only bulbs of the same make and model in a single Adaptive Lighting configuration.
2. Rearrange bulbs so that different color temperatures are not visible simultaneously.
#### :bulb: Bulb-Specific Issues
These lights are known to exhibit disadvantageous behaviour due to firmware bugs, insufficient functionality, or hardware limitations:
- [Sengled Z01-A19NAE26](https://www.zigbee2mqtt.io/devices/Z01-A19NAE26.html#sengled-z01-a19nae26)
- Unexpected turn-ons: If Adaptive Lighting sends a long transition time (like the default 45 seconds), and the bulb is turned off during that time, it may turn back on after approximately 10 seconds to continue the transition command. Since the bulb is turning itself on, there will be no obvious trigger in Home Assistant or other logs indicating the cause of the light turning on. To fix this, set a much shorter `transition` time, such as 1 second.
- Heat sensitivity: Additionally, these bulbs may perform poorly in enclosed "dome" style ceiling lights, particularly when hot. While most LEDs (even non-smart ones) state in the fine print that they do not support working in enclosed fixtures, in practice, more expensive bulbs like Philips Hue generally perform better. To resolve this issue, move the problematic bulbs to open-air fixtures.
- Ikea Tradfri bulbs/drivers (and related Ikea smart light products)
- Unsupported simultaneous transition of brightness and color: When receiving such a command, they switch the brightness instantly and only transition the color. To get smooth transitions of both brightness and color, enable `separate_turn_on_commands`.
- Unresponsiveness during color transitions: No other commands are processed during an ongoing color transition, e.g., turn-off commands are ignored and lights stay on despite being reported as off to Home Assistant. The default config with long transitions thus results in long periods of unresponsiveness. To work around this, disable transitions by setting `transition` to `0`, and increase the adaptation frequency by setting `interval` to a short time, e.g., `15` seconds, to retain the impression of smooth continuous adaptations. Keeping the `initial_transition` is recommended for a smooth fade-in (lights are usually not turned off momentarily after being turned on, in which case a short period of unresponsiveness is tolerable).
<!-- OUTPUT:END -->
---
## Additional Troubleshooting Tips
### Lights Flickering
If your lights flicker when Adaptive Lighting adjusts them:
1. **Increase transition time**: Set `transition: 60` or higher
2. **Reduce adaptation frequency**: Set `interval: 120` or higher
3. **Enable `skip_redundant_commands`**: This reduces unnecessary commands
```yaml
adaptive_lighting:
- name: "Anti-flicker"
lights:
- light.problem_light
transition: 60
interval: 120
skip_redundant_commands: true
```
### Lights Not Adapting
If lights aren't being adapted:
1. Check that the main switch is on: `switch.adaptive_lighting_<name>`
2. Check that the light isn't marked as manually controlled (check switch attributes)
3. Verify the light is in the configuration's `lights` list
4. Check debug logs for errors
### Color Temperature Looks Wrong
Different bulb manufacturers have varying color temperature implementations:
1. **Group similar bulbs**: Only include bulbs of the same make/model in one configuration
2. **Adjust the range**: Some bulbs may need narrower `min_color_temp`/`max_color_temp` ranges
3. **Use RGB instead**: Set `prefer_rgb_color: true` for more consistent colors
### Slow Response to Changes
If adaptation seems delayed:
1. **Reduce interval**: Set `interval: 30` for faster updates
2. **Enable intercept**: Set `intercept: true` for instant adaptation on turn-on
3. **Check network**: Ensure your light network is healthy (see WiFi/Zigbee sections above)
### Integration Not Loading
If Adaptive Lighting doesn't load:
1. Check that `adaptive_lighting:` is in your `configuration.yaml`
2. Verify the integration is installed correctly in `custom_components/`
3. Check the Home Assistant logs for errors
4. Ensure you're running a compatible Home Assistant version (2024.12.0+)
## Getting Help
If you're still having issues:
1. Enable debug logging and capture the relevant logs
2. Open an issue on [GitHub](https://github.com/basnijholt/adaptive-lighting/issues)
3. Include:
- Your configuration (redact sensitive info)
- Debug logs
- Home Assistant version
- Description of expected vs actual behavior

126
zensical.toml Normal file
View file

@ -0,0 +1,126 @@
# Adaptive Lighting Documentation Site Configuration
# Built with Zensical - https://zensical.org
[project]
name = "Adaptive Lighting"
site_url = "https://adaptive-lighting.nijho.lt"
repo_url = "https://github.com/basnijholt/adaptive-lighting"
repo_name = "basnijholt/adaptive-lighting"
edit_uri = "edit/main/docs/"
copyright = "Copyright &copy; 2020-2026 Bas Nijholt"
[theme]
name = "material"
custom_dir = "docs/overrides"
logo = "assets/logo.png"
favicon = "assets/logo.png"
icon = { repo = "fontawesome/brands/github" }
[[theme.palette]]
scheme = "default"
primary = "amber"
accent = "orange"
toggle = { icon = "lucide/sun", name = "Switch to dark mode" }
[[theme.palette]]
scheme = "slate"
primary = "amber"
accent = "orange"
toggle = { icon = "lucide/moon", name = "Switch to light mode" }
[theme.features]
content = [
"content.action.edit",
"content.action.view",
"content.code.copy",
"content.code.annotate",
"content.tabs.link",
"content.tooltips",
]
navigation = [
"navigation.instant",
"navigation.instant.prefetch",
"navigation.tracking",
"navigation.tabs",
"navigation.sections",
"navigation.top",
"navigation.footer",
]
search = [
"search.highlight",
"search.suggest",
]
[[nav]]
Home = "index.md"
[[nav]]
"Getting Started" = "getting-started.md"
[[nav]]
Configuration = "configuration.md"
[[nav]]
Services = "services.md"
[[nav]]
"Automation Examples" = "automation-examples.md"
[[nav]]
Troubleshooting = "troubleshooting.md"
[[nav]]
Advanced = [
{ "Brightness Modes" = "advanced/brightness-modes.md" },
{ "Manual Control" = "advanced/manual-control.md" },
{ "Sleep Mode" = "advanced/sleep-mode.md" },
]
[[nav]]
"See Also" = "see-also.md"
[markdown_extensions.toc]
permalink = true
toc_depth = 3
[markdown_extensions.admonition]
[markdown_extensions.pymdownx_details]
[markdown_extensions.pymdownx_superfences]
[markdown_extensions.pymdownx_highlight]
anchor_linenums = true
line_spans = "__span"
pygments_lang_class = true
[markdown_extensions.pymdownx_inlinehilite]
[markdown_extensions.pymdownx_tabbed]
alternate_style = true
slugify = { "!!python/object/apply:pymdownx.slugs.slugify" = { kwds = { case = "lower" } } }
[markdown_extensions.pymdownx_emoji]
emoji_index = "!!python/name:material.extensions.emoji.twemoji"
emoji_generator = "!!python/name:material.extensions.emoji.to_svg"
[markdown_extensions.attr_list]
[markdown_extensions.md_in_html]
[markdown_extensions.tables]
[markdown_extensions.def_list]
[extra]
[[extra.social]]
icon = "fontawesome/brands/github"
link = "https://github.com/basnijholt/adaptive-lighting"
[[extra.social]]
icon = "fontawesome/brands/discord"
link = "https://discord.gg/home-assistant"
[plugins.search]
[plugins.tags]