HA-grow-lights

Hello all

New to HA, and this is my first attempt at an automation/blueprint.

Also given I’m NOT a programmer, this script was written entirely with the help of ChatGPT.

Premiss of the project: Fully automate lighting and heating of my grow room. (heating script to come).

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.
Or you can visit GitHub

blueprint:
  name: Grow Lights – Solar Noon Cycle
  description: >
    Automatically controls grow lights based on a grow start date.
    Veg weeks 1–4 run 18/6, Flower week 5+ runs 12/12.
    Lights are centered on solar noon with safe fallback to clock noon.
    This automation requires helper input_datetime.grow_start_date.
    Set the date you started your grow. Grow is based on a 12 week cycle.
    Select the light switch you wish to control.
  domain: automation

  input:
    grow_start_date:
      name: Grow Start Date
      selector:
        entity:
          domain: input_datetime

    grow_light_switch:
      name: Grow Light Switch
      selector:
        entity:
          domain: switch

mode: single

trigger:
  - platform: time_pattern
    minutes: "/1"
  - platform: homeassistant
    event: start

variables:
  start_date: !input grow_start_date
  light_switch: !input grow_light_switch

  now_ts: "{{ now().timestamp() }}"

  prev_noon: "{{ state_attr('sun.sun','previous_noon') }}"
  next_noon: "{{ state_attr('sun.sun','next_noon') }}"

  noon_ts: >
    {% if prev_noon is not none and next_noon is not none %}
      {{ as_timestamp(prev_noon if now_ts < as_timestamp(next_noon) else next_noon) }}
    {% else %}
      {{ as_timestamp(today_at('12:00')) }}
    {% endif %}

  grow_days: >
    {% if states(start_date) not in ['unknown','unavailable',''] %}
      {{ (now().date()
          - strptime(states(start_date).split(' ')[0], '%Y-%m-%d').date()).days }}
    {% else %}
      -1
    {% endif %}

  grow_week: "{{ (grow_days // 7) + 1 if grow_days >= 0 else 0 }}"

  light_hours: "{{ 18 if grow_week < 5 else 12 }}"
  half_window: "{{ (light_hours / 2) * 3600 }}"

  lights_on_ts: "{{ noon_ts - half_window }}"
  lights_off_ts: "{{ noon_ts + half_window }}"

action:
  - choose:
      # Turn lights ON
      - conditions:
          - condition: template
            value_template: >
              {{ now_ts >= lights_on_ts and now_ts < lights_off_ts }}
        sequence:
          - service: switch.turn_on
            target:
              entity_id: !input grow_light_switch

      # Turn lights OFF
      - conditions:
          - condition: template
            value_template: >
              {{ now_ts < lights_on_ts or now_ts >= lights_off_ts }}
        sequence:
          - service: switch.turn_off
            target:
              entity_id: !input grow_light_switch

Helper required:
input_datetime.grow_start_date

Also ensure you have sun.sun installed (found this out by accident).

To configure the automation simply select your date helper and the light switch you wish to control. Everything else is done for you.

Grow cycles are based on a 12 week cycle. 4 veg and 8 flower.

The lighting cycle is centered on midday. So a 18/6 cycle is 0300-2100 and a 12/12 cycle is 0600-1800.

Here are a couple of templates that may also be helpful.
These should be added to your templates.yaml

Show grow week

      # ───────────── Grow Week ─────────────
      - name: "Grow Week"
        icon: mdi:calendar-week
        state: >
          {% set start = states('input_datetime.grow_start_date') %}
          {% if start not in ['unknown','unavailable',''] %}
            {% set start_date = strptime(start.split(' ')[0], '%Y-%m-%d') %}
            {% set days = (now().date() - start_date.date()).days %}
            {{ (days // 7) + 1 if days >= 0 else 0 }}
          {% else %}
            unknown
          {% endif %}

Grow light countdown. Show how far away the next lighting cycle is.

      # ───────────── Grow Lights Countdown ─────────────
      - name: "Grow Lights – Countdown"
        icon: mdi:timer-outline
        state: >
          {% set start_date = states('input_datetime.grow_start_date') %}
          {% set light_state = states('switch.relayboard_lights') %}
          {% if start_date in ['unknown','unavailable',''] or light_state in ['unknown','unavailable'] %}
            unavailable
          {% else %}
            {% set now_ts = as_timestamp(now()) %}
            {# Use fixed local noon today #}
            {% set noon = now().replace(hour=12, minute=0, second=0, microsecond=0) %}
            {% set noon_ts = as_timestamp(noon) %}
            {% set grow_days = (now().date() - strptime(start_date.split(' ')[0], '%Y-%m-%d').date()).days %}
            {% set grow_week = (grow_days // 7) + 1 if grow_days >= 0 else 0 %}
            {% set light_hours = 18 if grow_week < 5 else 12 %}
            {% set half_window = (light_hours / 2) * 3600 %}
            {% set lights_on_ts = noon_ts - half_window %}
            {% set lights_off_ts = noon_ts + half_window %}

            {% if light_state == 'on' %}
              {% set remaining = max(0, lights_off_ts - now_ts) %}
            {% else %}
              {% if now_ts < lights_on_ts %}
                {% set remaining = lights_on_ts - now_ts %}
              {% else %}
                {% set remaining = (lights_on_ts + 24*3600) - now_ts %}
              {% endif %}
            {% endif %}

            {% set hours = (remaining // 3600) | int %}
            {% set minutes = ((remaining % 3600) // 60) | int %}
            {{ "%02d:%02d" | format(hours, minutes) }}
          {% endif %}

I hope that someone finds this helpful.

Regards

Fred

1 Like