ESPHome

Outdoor lamp on a sun schedule, no hub needed

An ESP32 turns a relay or a MOSFET dimmer on from sun elevation, with a late-night cutoff, and keeps doing it when Wi-Fi or Home Assistant is down.

Difficulty · IntermediateSetup · 45–90 minutesESPHome · 2026.10
Get the pack · from $19Have us set it up

Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.

A lamp that asks Home Assistant for sunset stops when the hub or the Wi-Fi does. This firmware asks the sun for its elevation on the ESP32 itself. SNTP and, when it is reachable, Home Assistant time both set the clock. After a reboot the relay and the dimmer come back to the last state, then the schedule takes over again once the clock and the sun reading are valid.

Latitude, longitude, and timezone in the file are placeholders (0.0, 0.0, UTC). Set them before the first flash. Do not publish the real coordinates.

Mains stays on a relay that is listed for that load, in an enclosure, wired by someone qualified. A 12 V strip can use the MOSFET pin instead. Do not put mains on the MOSFET.

What problem it solves

You want an outdoor lamp on around dusk and off around dawn, plus an optional late-night gap, without a hub in the decision.

How it works

The board is any ESP32 that matches esp32dev. ESPHome computes sun elevation from the substitutions. A template select is the mode. Template numbers are the thresholds. Both outputs follow one decision: a GPIO relay (pack default GPIO4, active-low) and a 1000 Hz LEDC dimmer (pack default GPIO16) driving a monochromatic light.

  1. Mode — On forces both outputs on. Off forces them off. Auto uses the schedule. The choice is restored after a reboot.
  2. Sun — in Auto, the lamp turns on when elevation is at or below On at or below (pack default −0.8°, near the horizon). It turns off when elevation is at or above Off at or above (same default). If the two numbers overlap, the lamp holds its current state instead of chattering.
  3. Clock — elevation is ignored until the clock year is 2024 or later and the sun sensor is a real number. Until then the decision text reads waiting for time or waiting for sun, and the outputs stay where restore left them.
  4. Night gap — Night off hour (0–23, pack default 23) and Morning on hour (0–23, or 24 to disable, which is the default) define a stretch where Auto forces the lamp off even if the sun is still down. A morning hour of 24 with an off hour of 12 or later means “off from that hour until noon.” Set both hours inside 0–23 to wrap across midnight when the off hour is later than the morning hour.
  5. Dim level — 5–100%. The relay is only on or off. The dimmer uses this percent. Wire the output you actually have; the unused one can stay unconnected.
  6. When it runs — on boot, every 60 seconds, when either time source syncs, and when you change a number or the mode. A text sensor named Lamp decision shows on, off, sun, day, night cutoff, or hold.

There is no web page on the device. Wi-Fi credentials and the API key stay in secrets.yaml.

Hardware notes

  • Board. Classic ESP32, ESPHome board esp32dev, ESP-IDF. An ESP32-C6 or C3 Super Mini needs a different board: line and a pin check before you flash this file.
  • Relay. GPIO4, inverted: true, because many 5 V modules are active-low. If the lamp comes on when the log says off, flip relay_inverted. The module’s JD-VCC jumper and a separate coil supply are the usual way to keep coil current off the ESP32’s 3.3 V pin. The ESP32 ground and the coil ground still have to meet.
  • Mains. A hobby relay module is not a mains enclosure. Use a relay rated for the lamp voltage and current, a box, strain relief, and a person who is allowed to wire it. This pack does not include a mains diagram.
  • Dimmer. IRLZ44N or another MOSFET that is actually on at 3.3 V. Low side of a 12 V strip: drain to the strip negative, source to ground, gate toward GPIO16. About 220 Ω between the pin and the gate, and about 10 kΩ from gate to source, so the strip stays off while the pin floats at boot. Common ground with the ESP32. 12 V never touches a GPIO.
  • Placeholders. latitude: "0.0" and longitude: "0.0" are the Gulf of Guinea, not a house. Elevation there is not your dusk. Set both, and timezone, in substitutions before you trust Auto.

Snippet

The schedule lives in the script in the pack. The substitutions you must edit first are these.

substitutions:
  device_name: outdoor-lamp
  latitude: "0.0"
  longitude: "0.0"
  timezone: UTC
  relay_pin: GPIO4
  relay_inverted: "true"
  dimmer_pin: GPIO16

Setup outline

  1. Copy esphome/secrets.example.yaml into your ESPHome secrets and fill Wi-Fi, a new API key, OTA, and the fallback hotspot password.
  2. Paste the device YAML. Set latitude, longitude, and timezone. Leave the mode on Auto.
  3. Validate, then flash over USB. Confirm Lamp decision leaves waiting for time once SNTP syncs.
  4. Set mode to On and Off and confirm the relay (or the strip) follows. Set Auto again.
  5. Nudge On at or below above the live elevation and confirm the lamp comes on. Put the numbers back to about −0.8 unless you want civil twilight instead of the horizon.
  6. If you want the lamp dark after 23:00 even in winter, leave Night off hour at 23 and set Morning on hour to the hour it may turn on again (6 is a common choice). 24 means that morning gate is disabled.

Troubleshooting

  • It stays on waiting for time. SNTP never synced and Home Assistant time is not connected. Check Wi-Fi and the timezone string (America/Denver, not an offset pasted from a browser).
  • It stays on waiting for sun. The clock is valid but elevation is still empty. Give the sun sensor one 60 second update after time sync.
  • The lamp is on in the middle of the day. Off at or above is below the current elevation, or the mode is On. Read Lamp decision. day means the sun threshold turned it off. sun means it is supposed to be on.
  • It never turns off after midnight. Night off hour is 24, which disables the gap, or Morning on hour is 24 and the off hour is in the morning. The text sensor should say night cutoff inside the gap.
  • Relay is backwards. Flip relay_inverted and reflash. Do not “fix” that by swapping mains conductors.
  • Dimmer barely lights. The MOSFET is not logic-level, or the dim level is still at the minimum of 5%. A plain IRF520 module will not turn on from 3.3 V.
  • A reboot left the lamp on until the clock synced. That is restore. Auto does not change the outputs until time and elevation are both valid.

Credits

Based on ESPhome-outdoor-lamp-controller. That project schedules an outdoor lamp from the sun, with twilight presets and a group of lamps that stay in step. This pack is a single lamp: local elevation, a mode select, a night gap, and one relay plus one PWM dimmer. It does not sync a group, watch a wall switch, or use a daylight sensor.

Changelog

v1.0 · Oct 30, 2026: First publish.