Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save rocob/972b0299afb273d812e1adbacb1bacc1 to your computer and use it in GitHub Desktop.
Save rocob/972b0299afb273d812e1adbacb1bacc1 to your computer and use it in GitHub Desktop.
ESPHome configuration example to create an animated clock using the Neopixel 60 LED ring
substitutions:
num_leds: "60"
name: "Clock Ring"
esphome:
name: clock_ring
platform: ESP8266
board: d1_mini
on_boot:
priority: -10
then:
- light.turn_on:
id: light_ring
brightness: 50%
effect: Clock
wifi:
ssid: !secret esp_ssid
password: !secret esp_pwd
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "${name} FB AP"
password: !secret esp_fbap
captive_portal:
# Enable logging
logger:
# Enable Home Assistant API
api:
password: !secret esp_api
ota:
password: !secret esp_ota
time:
- platform: homeassistant
id: ha_time
globals:
- id: clock_brightness
type: byte
restore_value: yes
initial_value: '255'
- id: clock_indicators_enabled
type: bool
restore_value: yes
initial_value: 'true'
- id: clock_seconds_enabled
type: bool
restore_value: yes
initial_value: 'true'
binary_sensor:
- platform: status
name: "Status ${name}"
id: status_clock
switch:
- platform: gpio
name: "${name} Alarm"
id: alarm_switch
pin: D0
- platform: template
name: "${name} Indicators"
icon: mdi:progress-clock
lambda: !lambda |-
return id(clock_indicators_enabled);
turn_on_action:
globals.set:
id: clock_indicators_enabled
value: 'true'
turn_off_action:
globals.set:
id: clock_indicators_enabled
value: 'false'
- platform: template
name: "${name} Seconds"
icon: mdi:update
lambda: !lambda |-
return id(clock_seconds_enabled);
turn_on_action:
globals.set:
id: clock_seconds_enabled
value: 'true'
turn_off_action:
globals.set:
id: clock_seconds_enabled
value: 'false'
i2c:
sda: D2
scl: D1
font:
- file: "arial.ttf"
id: my_font
size: 10
- file: "arial.ttf"
id: time_font
size: 16
glyphs: ".:0123456789"
display:
- platform: ssd1306_i2c
model: "SSD1306 64x48"
address: 0x3C
lambda: |-
it.strftime(32, 48, id(time_font), TextAlign::BASELINE_CENTER, "%H:%M:%S", id(ha_time).now());
it.print(0, 0, id(my_font), "${name}");
if (id(alarm_switch).state) {
it.print(24, 18, id(my_font), "ON");
} else {
it.print(22, 18, id(my_font), "OFF");
}
light:
- id: light_ring
internal: False
platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO3
num_leds: "${num_leds}"
# method: ESP8266_DMA
name: "${name} LED"
color_correct: [90%, 90%, 90%]
effects:
- addressable_lambda:
name: "Clock"
update_interval: 32ms
lambda: |-
static boolean initialized;
static ESPColor clock_ring_colors [${num_leds}];
if (initialized == false) {
std::fill_n(clock_ring_colors, it.size(), ESPColor::BLACK);
initialized = true;
}
auto time = id(ha_time).now();
if (!time.is_valid()) {
return;
}
// calculate led indices
int second_idx = (int) ( (time.second * it.size()) / 60);
int minute_idx = (int) ( it.size() * (time.minute * 60 + time.second) / 3600);
int hour_idx = (int) ( it.size() * ( (time.hour % 12) * 60 + time.minute) / 720);
int hour_inc_min_idx = hour_idx + (int) (((float) time.minute / 12) * (it.size() / 60));
// fade out old colors
for (int i = 0; i < it.size(); i++) {
ESPColor old_color = clock_ring_colors[i];
// fade out color
int amount = id(clock_brightness) / 10;
int red = old_color.red;
int green = old_color.green;
int blue = old_color.blue;
int white = old_color.white;
if (red < amount) { red = 0; } else { red -= amount; }
if (green < amount) { green = 0; } else { green -= amount; }
if (blue < amount) { blue = 0; } else { blue -= amount; }
ESPColor new_color = ESPColor(red, green, blue, 0);
clock_ring_colors[i] = new_color;
}
int indicator_brightness = id(clock_brightness) / 3;
ESPColor indicator_color = ESPColor(indicator_brightness, indicator_brightness, indicator_brightness);
// calculate colors
ESPColor second_color = ESPColor(0, 0, id(clock_brightness));
ESPColor minute_color = ESPColor(0, id(clock_brightness), 0);
if (minute_idx == second_idx) {
minute_color += second_color;
}
ESPColor hour_color = ESPColor(id(clock_brightness), 0, 0);
if (hour_inc_min_idx == minute_idx) {
// if seconds are also the same this will already contain the second color
hour_color += minute_color;
} else if (hour_inc_min_idx == second_idx) {
hour_color += second_color;
}
if (id(clock_indicators_enabled)) {
for (int i = 0; i < it.size(); i += (int) ((float) it.size() / 12)) {
clock_ring_colors[i] = indicator_color;
}
}
// set colors
if (id(clock_seconds_enabled)) {
clock_ring_colors[second_idx] = second_color;
}
clock_ring_colors[minute_idx] = minute_color;
clock_ring_colors[hour_inc_min_idx] = hour_color;
// apply colors to light
for (int i = 0; i < it.size(); i++) {
it[i] = clock_ring_colors[i];
}
- addressable_rainbow:
name: "Rainbow Spinner"
speed: 8
width: "${num_leds}"
- addressable_rainbow:
name: "Rainbow Fader"
speed: 3
width: "${num_leds}"
- random:
name: "Random Slow"
transition_length: 30s
update_interval: 30s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment