Aquarium: PV-Optimized Lighting & Heating (Day) – Load Reduction After Sunset

This automation optimizes your aquarium’s light and heater to make the most of PV surplus during the day and reduce power consumption after sunset. It’s designed for clear, reliable scheduling with sensible fallbacks—so your tank stays stable while your energy bill stays low.

What it does

  • Uses daytime PV to run lighting and maintain temperature efficiently
  • Switches to a night-time eco mode to cut load after sunset
  • Keeps logic simple and transparent (easy to tweak in YAML)

Important: Temperature first

Fish and plants are sensitive to rapid temperature swings. Even small daily spikes or drops can stress the ecosystem. Please:

  • Monitor water temperature with a reliable sensor; set alerts for out-of-range values.
  • Aim for gradual changes (avoid frequent on/off cycles).
  • Improve insulation/cover (tight lid, reduced drafts, stable room temp) to keep heat in.
  • Size the heater appropriately; overspec’d heaters can cause overshoot.

Safety note: Always test changes at a low-risk time and watch the tank closely for a few days. If temperature deviates, prioritize stability over savings by relaxing the eco schedule.

Script:
Go to Settings → Automations & Scenes → Scripts → + Add Script.
ScriptName: script.aquarium_verify_after_30_minutes

Use your own entity IDs. In this example and below:

  • Water temperature: sensor.sonoff_1002215fd9_temperature
  • Aquarium heater (switch): switch.aquarium_heizung_none
  • Aquarium heater power sensor: sensor.aquarium_heizung_leistung
  • Aquarium light 1 (switch): switch.aquarium_licht_none
  • Aquarium light 1 power sensor: sensor.aquarium_licht_leistung
  • Aquarium light 2 (switch): switch.nanoaqua_licht_none
  • Aquarium light 2 power sensor: sensor.nanoaqua_licht_leistung
  • NotifyGroup mobiles: notify.iphones
alias: "Aquarium: Verify after 30 minutes"
description: >
  Prüft 30 Min. nach dem Schalten den gewünschten Zustand und korrigiert ggf. +
  Push
mode: queued
max: 10
sequence:
  - delay: "00:30:00"
  - variables:
      ent: "{{ entity | default('') }}"
      desired: "{{ desired_state | default('') }}"
      reas: "{{ reason | default('verification') }}"
      tries: 5
      wait_s: 5
      pause_s: 8
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ ent != '' and desired in ['on','off'] }}"
        sequence: []
    default:
      - stop: "Verify: missing/invalid parameters"
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ states(ent) == desired }}"
        sequence:
          - stop: "Verify: already in desired state"
  - repeat:
      count: "{{ tries }}"
      sequence:
        - choose:
            - conditions:
                - condition: template
                  value_template: "{{ desired == 'on' }}"
              sequence:
                - action: switch.turn_on
                  target:
                    entity_id: "{{ ent }}"
                  continue_on_error: true
            - conditions:
                - condition: template
                  value_template: "{{ desired == 'off' }}"
              sequence:
                - action: switch.turn_off
                  target:
                    entity_id: "{{ ent }}"
                  continue_on_error: true
        - wait_template: "{{ states(ent) == desired }}"
          timeout: 00:00:{{ wait_s }}
          continue_on_timeout: true
        - choose:
            - conditions:
                - condition: template
                  value_template: "{{ states(ent) == desired }}"
              sequence:
                - stop: "Verify: corrected successfully"
        - delay: 00:00:{{ pause_s }}
  - action: notify.iphones
    data:
      title: "Aquarium: Korrektur fehlgeschlagen ({{ reas }})"
      message: >-
        {{ ent }} ist weiterhin '{{ states(ent) }}', sollte '{{ desired }}'
        sein. Temp: {{ states('sensor.sonoff_1002215fd9_temperature') }}°C |
        Heizung: {{ states('switch.aquarium_heizung_none') }} ({{
        states('sensor.aquarium_heizung_leistung') }} W) | Licht1: {{
        states('switch.aquarium_licht_none') }} ({{
        states('sensor.aquarium_licht_leistung') }} W) | Licht2: {{
        states('switch.nanoaqua_licht_none') }} ({{
        states('sensor.nanoaqua_licht_leistung') }} W)
Code-Sprache: PHP (php)

Automation – light off

alias: "[AQUARIUM] Licht AUS (-60m vor Sonnenuntergang) + 60min Check"
triggers:
  - trigger: sun
    event: sunset
    offset: "-01:00:00"
conditions: []
actions:
  - action: switch.turn_off
    target:
      entity_id:
        - switch.aquarium_licht_none
        - switch.nanoaqua_licht_none
    continue_on_error: true
  - repeat:
      count: 5
      sequence:
        - delay: "00:15:00"
        - choose:
            - conditions:
                - condition: state
                  entity_id: switch.aquarium_licht_none
                  state: "on"
              sequence:
                - action: switch.turn_off
                  target:
                    entity_id: switch.aquarium_licht_none
                  continue_on_error: true
        - choose:
            - conditions:
                - condition: state
                  entity_id: switch.nanoaqua_licht_none
                  state: "on"
              sequence:
                - action: switch.turn_off
                  target:
                    entity_id: switch.nanoaqua_licht_none
                  continue_on_error: true
mode: single
Code-Sprache: JavaScript (javascript)

Heating on

alias: "[AQUARIUM] Heizung EIN (+60m nach Sonnenaufgang)"
description: ""
triggers:
  - event: sunrise
    offset: "01:00:00"
    trigger: sun
conditions: []
actions:
  - target:
      entity_id: switch.aquarium_heizung_none
    action: switch.turn_on
  - data:
      entity: switch.aquarium_heizung_none
      desired_state: "on"
      reason: heater_on
    action: script.aquarium_verify_after_30_minutes
mode: single
Code-Sprache: CSS (css)

Heating off

alias: "[AQUARIUM] Heizung AUS (-30m vor Sonnenuntergang)"
description: ""
triggers:
  - event: sunset
    offset: "-00:30:00"
    trigger: sun
conditions: []
actions:
  - target:
      entity_id: switch.aquarium_heizung_none
    action: switch.turn_off
  - data:
      entity: switch.aquarium_heizung_none
      desired_state: "off"
      reason: heater_off
    action: script.aquarium_verify_after_30_minutes
mode: singleCode-Sprache: CSS (css)

Reconcile

alias: "[AQUARIUM] Reconcile: Zustände an Zeitplan anpassen"
description: Stellt nach Neustart/Offline-Phase die Sollzustände wieder her (robust).
triggers:
  - event: start
    trigger: homeassistant
  - minutes: /15
    trigger: time_pattern
conditions: []
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: >-
              {{ want_heater_on and
              is_state('switch.aquarium_heizung_none','off') }}
        sequence:
          - target:
              entity_id: switch.aquarium_heizung_none
            action: switch.turn_on
      - conditions:
          - condition: template
            value_template: >-
              {{ not want_heater_on and
              is_state('switch.aquarium_heizung_none','on') }}
        sequence:
          - target:
              entity_id: switch.aquarium_heizung_none
            action: switch.turn_off
  - choose:
      - conditions:
          - condition: template
            value_template: >-
              {{ want_lights_on and
              (is_state('switch.aquarium_licht_none','off') or
              is_state('switch.nanoaqua_licht_none','off')) }}
        sequence:
          - target:
              entity_id:
                - switch.aquarium_licht_none
                - switch.nanoaqua_licht_none
            action: switch.turn_on
      - conditions:
          - condition: template
            value_template: >-
              {{ not want_lights_on and
              (is_state('switch.aquarium_licht_none','on') or
              is_state('switch.nanoaqua_licht_none','on')) }}
        sequence:
          - target:
              entity_id:
                - switch.aquarium_licht_none
                - switch.nanoaqua_licht_none
            action: switch.turn_off
mode: restart
variables:
  now_local: "{{ now() }}"
  next_rising: "{{ as_local(as_datetime(state_attr('sun.sun','next_rising'))) }}"
  next_setting: "{{ as_local(as_datetime(state_attr('sun.sun','next_setting'))) }}"
  sunrise_today: |-
    {% set nr = next_rising %} {% if nr.date() == now().date() %}
      {{ nr }}
    {% else %}
      {{ nr - timedelta(days=1) }}
    {% endif %}
  sunset_today: |-
    {% set ns = next_setting %} {% if ns.date() == now().date() %}
      {{ ns }}
    {% else %}
      {{ ns - timedelta(days=1) }}
    {% endif %}
  heater_on_from: "{{ sunrise_today + timedelta(hours=1) }}"
  heater_off_at: "{{ sunset_today - timedelta(minutes=30) }}"
  lights_on_from: "{{ today_at('09:30:00') }}"
  lights_off_at: "{{ sunset_today - timedelta(hours=1) }}"
  want_heater_on: "{{ now_local >= heater_on_from and now_local < heater_off_at }}"
  want_lights_on: "{{ now_local >= lights_on_from and now_local < lights_off_at }}"
Code-Sprache: PHP (php)

Lights on

alias: "[AQUARIUM]  Licht EIN (09:30)"
description: ""
triggers:
  - at: "09:30:00"
    trigger: time
conditions: []
actions:
  - target:
      entity_id:
        - switch.aquarium_licht_none
        - switch.nanoaqua_licht_none
    action: switch.turn_on
  - data:
      entity: switch.aquarium_licht_none
      desired_state: "on"
      reason: lights_on
    action: script.aquarium_verify_after_30_minutes
  - data:
      entity: switch.nanoaqua_licht_none
      desired_state: "on"
      reason: lights_on
    action: script.aquarium_verify_after_30_minutes
mode: single
Code-Sprache: CSS (css)

7. Alarm if water is too cold (this example 22 degrees)

alias: "[AQUARIUM]  Alarm: Temperatur unter 22°C"
description: ""
triggers:
  - entity_id:
      - sensor.sonoff_1002215fd9_temperature
    below: 22
    trigger: numeric_state
    for:
      hours: 0
      minutes: 2
      seconds: 0
conditions: []
actions:
  - data:
      title: "Aquarium: Temperatur-Alarm (<22°C)"
      message: >-
        Temp: {{ states('sensor.sonoff_1002215fd9_temperature') }}°C. Heizung:
        {{ states('switch.aquarium_heizung_none') }} ({{
        states('sensor.aquarium_heizung_leistung') }} W). Licht1: {{
        states('switch.aquarium_licht_none') }} ({{
        states('sensor.aquarium_licht_leistung') }} W). Licht2: {{
        states('switch.nanoaqua_licht_none') }} ({{
        states('sensor.nanoaqua_licht_leistung') }} W).
    action: notify.mobile_app_bananastefan
  - type: turn_on
    device_id: deviceidstromheizung
    entity_id: entityidstromheizung
    domain: switch
mode: single
Code-Sprache: JavaScript (javascript)