Local control of the daytime Smart Control SC20 aquarium LED controller (OEM: aquaLEDs.de UG "Light-Symphony") from Home Assistant — lights, the daycycle schedule, the moon and cloud effects, and diagnostics.
The SC20 has no published API. Everything here was recovered by reverse engineering the
device's own web app and the vendor firmware image, then confirmed against real hardware.
The full protocol reference, including what is proven and what is still a guess, is in
docs/protocol/.
| Entity | What it does |
|---|---|
light.<tank> |
Master light — on/off and brightness, scaling all three channels while keeping their colour ratio |
light.<tank>_white / _blue / _red |
The three channels individually |
select.<tank>_mode |
Daycycle (follow the stored programme) or Manual |
switch.<tank>_moonlight |
Moonlight simulation |
switch.<tank>_cloud_simulation |
Cloud simulation |
switch.<tank>_acclimatisation |
Acclimatisation ramp for a new tank |
number.<tank>_* |
The effect parameters — moonlight levels, cloud frequency, strength and duration, acclimatisation duration and reduction |
time.<tank>_moonlight_start / _end |
The moonlight window, as HH:MM clock times |
update.<tank>_firmware / _web_app |
Whether the controller has an update waiting |
sensor.<tank>_* |
Current mode, live output level (per channel), programmed level, firmware version, uptime, free memory, operating hours, mesh clients |
The controller checks data.daytime.de for itself and reports what it finds, so the
update entities surface its own verdict — Home Assistant makes no request to the vendor.
Firmware and web app are tracked separately because the device updates them independently.
The Install button triggers the controller's own updater (START_FOTA), which
downloads from the vendor, flashes and reboots.
Read this before pressing it. The controller is off the network for several minutes, during which the aquarium light is not under control. There is no cancel. If the write fails, the lamp needs a manual reflash through
http://<host>/updateand the tank stays unlit until you do it. Do not power-cycle the controller during an update, and do not put this in an automation — it is a deliberate, supervised action.The button refuses unless the controller is connected and actually reports something newer than it runs, so it will not pointlessly reflash a current device.
The device needs working internet access to fetch the image; it downloads from
data.daytime.de itself.
Plus services for the lighting programme: set_daycycle, get_daycycle, load_scenario,
save_scenario, preview_curve and set_clock.
Settings → Devices & Services → daytime SC20 → Configure.
The pages follow the controller's own web interface, so settings sit where a user of the vendor app expects them:
| Page | What it holds |
|---|---|
| Daycycle | When the lighting day starts and ends, how long sunrise and sunset take, and the brightness — optionally per colour. This is the vendor app's "easy mode". |
| Moonlight | Night lighting: new-moon and full-moon levels, whether to follow a 30-day lunar cycle, which colours carry it, and the window (which may run past midnight). |
| Cloud simulation | How many clouds a day, how long they last, how much they dim, and how likely each one is to appear. |
| Acclimatisation | The ramp for a newly set-up tank, and its pause switch. |
| Connection | How often to poll. The only page stored in Home Assistant rather than on the lamp. |
Every other page writes straight to the controller and reads its current settings when it opens — these are the lamp's settings, not Home Assistant's, and the vendor app is free to change them behind your back.
The Daycycle page produces the same trapezoid the vendor app does. That is not a guess: feeding a device its own stored settings through this generator reproduces the schedule it is actually running, setpoint for setpoint, and there is a test pinning that. Saving replaces the whole programme, so the previous one is backed up first — and if you had hand-edited an expert curve, the page says so before flattening it.
For a curve the easy-mode shape cannot express, use the set_daycycle service or
load_scenario with a .scen file.
Turning a light on or changing its brightness takes the controller out of its schedule.
That is how the hardware works: manual levels and the daycycle programme are mutually
exclusive modes, and setting a level has no effect unless the controller is in manual mode.
So light.turn_on switches it to manual, and it stays there — the lamp will not resume
the programme on its own.
To hand control back, set select.<tank>_mode to Daycycle. Every light also carries a
mode attribute so you can see which state it is in.
A useful automation pattern is to do a manual override and schedule the return:
# Bright light for a water change, back to normal after an hour
- alias: Water change lighting
triggers:
- trigger: event
event_type: water_change_started
actions:
- action: light.turn_on
target: { entity_id: light.reef_tank }
data: { brightness_pct: 100 }
- delay: "01:00:00"
- action: select.select_option
target: { entity_id: select.reef_tank_mode }
data: { option: daycycle }The brightness reading moves on its own. In daycycle mode the controller reports its
actual output, which includes cloud and moonlight modulation. With cloud simulation
running, the level drifts continuously — that is the tank telling you the truth, not the
integration being unstable. sensor.<tank>_programmed_level shows what the schedule alone
calls for, if you want to compare.
The device has no authentication whatsoever. No password, no token, nothing. Anyone who can reach it on your network has full control of your aquarium lighting, and that is true with or without this integration. Keep it on a trusted network segment.
Add this repository as a custom repository of type Integration, install daytime SC20, restart Home Assistant, then add the integration from Settings → Devices & Services.
Copy custom_components/daytime_sc20/ into your config/custom_components/ directory and
restart Home Assistant.
You need the controller's IP address or hostname. If mDNS works on your network, the device
answers to sc20.local. There is no password to enter.
Everything else is configured afterwards from Configure on the integration's card.
The schedule is a list of up to 30 setpoints, each [minute_of_day, white, blue, red].
Minutes run 0–1440 from midnight and the levels are percentages. The list must be sorted,
must start at minute 0 and must end at minute 1440; the controller interpolates between
setpoints.
- action: daytime_sc20.set_daycycle
data:
config_entry_id: <your entry id>
setpoints:
- [0, 0, 0, 0] # midnight, dark
- [420, 0, 0, 0] # 07:00, still dark
- [430, 0, 0, 80] # 07:10, red dawn
- [480, 100, 100, 100] # 08:00, full daylight
- [1080, 100, 100, 100] # 18:00
- [1140, 0, 0, 80] # 19:00, red dusk
- [1150, 0, 0, 0] # 19:10, dark
- [1440, 0, 0, 0] # midnightThe previous programme is saved to Home Assistant's storage before it is replaced — the controller has no undo and does not acknowledge writes, so this is the only way back.
.scen files from the vendor's website work directly with load_scenario; three of them
are included under docs/protocol/scenarios/.
tools/sc20_probe.py connects to a controller, records every frame, and writes a snapshot
of its configuration. It is how the protocol was confirmed and how to check it again if a
firmware update changes something.
python tools/sc20_probe.py --host 192.168.1.34 # read-only
python tools/sc20_probe.py --host 192.168.1.34 --allow-writes # includes reverted writesRead-only by default. The write phase confirms each command before sending it and always
restores the scheduled mode afterwards, including if it crashes. Captures are written to
docs/protocol/capture/ with SSIDs, MAC addresses, serials and IP addresses scrubbed, so
they are safe to commit.
python -m venv .venv && .venv/bin/pip install aiohttp homeassistant pytest pytest-homeassistant-custom-component ruff
.venv/bin/python -m pytest tests/
.venv/bin/python -m ruff check . && .venv/bin/python -m ruff format --check .The protocol client under custom_components/daytime_sc20/api/ has no Home Assistant
imports and can be used on its own.
One SC20 on firmware revision [23, 15] (webserver 02.3, website 01.5), with three
channels. Other members of this hardware family — the aquaLEDs HC and HC+, and the EHEIM
variants — speak the same protocol but have six channels and a real moonlight channel;
this integration assumes the SC20's three-channel model and does not support them.
Apache License 2.0 — see LICENSE.
Permissive: use, modify and redistribute it, including in closed-source work, provided the notice and attribution are kept. This is also the licence Home Assistant core uses, so the integration could be proposed upstream.
Unofficial, and not affiliated with daytime or aquaLEDs.de UG.
The protocol documentation in docs/protocol/ describes an interface recovered by
observing a device the author owns, for the purpose of interoperability. No vendor
firmware, vendor source or vendor asset is redistributed here — the firmware image is
referenced by URL only, and the three .scen files are the vendor's own public downloads,
which are plain coordinate lists rather than creative works.