Christmas tree lights

Initial commit
master
woju 2023-04-13 12:25:08 +02:00
commit cd62a024df
15 changed files with 255 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Gitignore settings for ESPHome
# This is an example and may include too much for your use-case.
# You can modify this file to suit your needs.
/.esphome/
/lib/secrets.yaml

20
README Normal file
View File

@ -0,0 +1,20 @@
d1_mini
=======
https://www.wemos.cc/en/latest/d1/d1_mini.html#pin
~/.local/lib/python3.9/site-packages/esphome/components/esp8266/boards.py
GPIO d1_mini
i2c SCL GPIO5 D1
SDA GPIO4 D2
boot mode GPIO0 D3 careful with this pin!
internal led GPIO2 D4,LED
spi CLK GPIO14 D5
MISO GPIO12 D6
MOSI GPIO13 D7
freely usable GPIO16 D0
spi (CS) GPIO15 D8
GPIO1 (TX)
GPIO3 (RX)

View File

@ -0,0 +1,2 @@
platform: status
name: "${node_name} status"

View File

@ -0,0 +1,3 @@
platform: status_led
name: "Status LED"
pin: LED

33
lib/pkg-common.yaml Normal file
View File

@ -0,0 +1,33 @@
esphome:
name: ${node_name}
#mqtt:
# Enable logging
logger:
# Enable Home Assistant API
api:
password: !secret api_password
ota:
password: !secret ota_password
#web_server:
#prometheus:
sensor:
- <<: !include sensor-uptime.yaml
- <<: !include sensor-wifi_signal.yaml
- <<: !include sensor-adc-vcc.yaml
binary_sensor:
- <<: !include binary_sensor-status.yaml
light:
- <<: !include light-status_led.yaml
switch:
- <<: !include switch-restart.yaml
- <<: !include switch-safe_mode.yaml
- <<: !include switch-shutdown.yaml

9
lib/pkg-wifi.yaml Normal file
View File

@ -0,0 +1,9 @@
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "${node_name} Fallback Hotspot"
password: !secret wifi_ap_password
captive_portal:

3
lib/sensor-adc-vcc.yaml Normal file
View File

@ -0,0 +1,3 @@
platform: adc
pin: VCC
name: "${node_name} VCC"

2
lib/sensor-uptime.yaml Normal file
View File

@ -0,0 +1,2 @@
platform: uptime
name: "${node_name} uptime"

View File

@ -0,0 +1,3 @@
platform: wifi_signal
name: "${node_name} WiFi Signal"
update_interval: 60s

2
lib/switch-restart.yaml Normal file
View File

@ -0,0 +1,2 @@
platform: restart
name: "${node_name} restart"

View File

@ -0,0 +1,2 @@
platform: safe_mode
name: "${node_name} restart (safe mode)"

2
lib/switch-shutdown.yaml Normal file
View File

@ -0,0 +1,2 @@
platform: shutdown
name: "${node_name} shutdown"

122
lvng-xmas.yaml Normal file
View File

@ -0,0 +1,122 @@
substitutions:
node_name: lvng-xmas
timezone: Europe/Warsaw
countdown_fill: 'Color(0, 255, 0)'
countdown_empty: 'Color(0, 0, 255)'
countdown_reversed: 'true'
packages:
common: !include lib/pkg-common.yaml
wifi: !include lib/pkg-wifi.yaml
esphome:
platform: ESP8266
board: d1_mini
time:
- platform: sntp
timezone: ${timezone}
id: time_sntp
on_time:
- seconds: 0
minutes: 0
hours: 6
then: &light-turn-on
- light.turn_on:
id: xmas
brightness: 0.9
red: 1.0
green: 1.0
blue: 1.0
effect: Random Twinkle
- seconds: 0
minutes: 0
hours: 0
then:
- if:
condition:
lambda: |-
auto time = id(time_sntp).now();
return time.day_of_month == 1 && time.month == 1;
then: *light-turn-on
else: &light-turn-off
- light.turn_off:
id: xmas
# New Year
- seconds: 0
minutes: 0
hours: 23
days_of_month: 31
months: 12
then:
- light.turn_on:
id: xmas
brightness: 1.0
effect: Countdown to Midnight
#light:
# - name: xmas
# id: xmas
# platform: fastled_clockless
# chipset: WS2811
# pin: D1
# num_leds: 100
light:
- name: xmas
id: xmas
platform: neopixelbus
pin: D1
# WARNING: THIS IS PIN RX
# pin: 3
# method:
# type: esp8266_dma
variant: WS2811
type: RGB
num_leds: 100
effects:
- addressable_twinkle:
twinkle_probability: 10%
- addressable_random_twinkle:
twinkle_probability: 20%
- addressable_scan:
scan_width: 4
- addressable_scan:
name: Scan Fast
scan_width: 5
move_interval: 5ms
- addressable_lambda:
# linear countdown toward midnight
name: Countdown to Midnight
update_interval: 100ms
lambda: !lambda |-
const bool reversed = ${countdown_reversed};
const Color color_fill = ${countdown_fill};
const Color color_empty = ${countdown_empty};
auto time = id(time_sntp).now();
if (time.is_valid()) {
it.all() = color_empty;
if (time.hour >= 12) {
int n = (24 - time.hour) * 60 - time.minute;
//ESP_LOGD("midnight", "time.hour=%d time.minute=%d n=%d it.size()=%d", time.hour, time.minute, n, it.size());
if (!reversed) {
it.range(max(it.size() - 1 - n, 0), it.size() - 1) = color_fill;
} else {
it.range(0, min(n, it.size() - 1)) = color_fill;
}
} else {
//ESP_LOGD("midnight", "time.hour=%d", time.hour);
}
} else {
ESP_LOGD("midnight", "time is invalid");
it.all() = Color(255, 0, 0);
}

1
secrets.yaml Symbolic link
View File

@ -0,0 +1 @@
lib/secrets.yaml

46
xmas-light-command.py Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/python3 -O
import asyncio
import pathlib
import pprint
import typing
import aioesphomeapi
import click
import yaml
def load_secret(secret):
with open(pathlib.Path(__file__).parent / 'secrets.yaml') as file:
return yaml.safe_load(file)[secret]
async def main_async(device, **kwds):
api = aioesphomeapi.APIClient(device, 6053, load_secret('api_password'))
await api.connect(login=True)
entities, _services = await api.list_entities_services()
for entity in entities:
if entity.object_id == 'xmas':
await api.light_command(entity.key, **kwds)
#async def main_async(*args, rgb, **kwds):
# print(f'{rgb=}')
class RGB(typing.NamedTuple):
r: float
g: float
b: float
@classmethod
def fromstr(cls, rgb):
return cls(*map(float, rgb.split(',')))
@click.command()
@click.option('--device', default='lvng-xmas.lan')
@click.option('--effect')
@click.option('--on/--off', 'state', default=None)
@click.option('--rgb', type=RGB.fromstr)
@click.option('--brightness', type=float)
def main(*args, **kwds):
asyncio.run(main_async(*args, **kwds))
if __name__ == '__main__':
main()