Skip to content

Repository files navigation

daytime SC20 for Home Assistant

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/.

What you get

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

Firmware updates

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>/update and 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.

Configuring it

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.

Read this before you use it

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.

Installation

HACS

Add this repository as a custom repository of type Integration, install daytime SC20, restart Home Assistant, then add the integration from Settings → Devices & Services.

Manual

Copy custom_components/daytime_sc20/ into your config/custom_components/ directory and restart Home Assistant.

Setup

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.

Working with the lighting programme

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]       # midnight

The 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/.

Reverse-engineering tools

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 writes

Read-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.

Development

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.

Tested against

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.

Licence

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.

Provenance

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.

About

Home Assistant integration for the daytime SC20 aquarium LED controller, with its reverse-engineered protocol documented

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages