Files
homeassistant-config/esphome/sleepingroom-clock.yaml
T

451 lines
15 KiB
YAML

# Board: ESP32-C3 Super Mini (Generic)
# Definition: definitions/boards/esp32-c3-supermini/manifest.yaml
# Based largely on https://github.com/misterblack1/HU-058_ESPHome by Adrian Black aka "Adrian's Digital Basement"
# AI slop in here
substitutions:
device_description: "ESP32 Geiger counter with radiation dose rate, accumulated dose and MAX7219 seven-segment display"
name: "sleepingroom-clock"
friendly_name: "Schlafzimmeruhr"
esphome:
comment: ${device_description}
name: ${name}
friendly_name: ${friendly_name}
name_add_mac_suffix: false
project:
name: sensor.radiation
version: "1.0"
min_version: 2022.11.0
# Template numbers and selects restore their value by publishing it, which
# does not fire their set_action, so a restored effect would show in the UI
# and do nothing. Push the lot into the component once everything is up.
on_boot:
priority: -100
then:
- lambda: |-
id(panel)->set_effect_speed(id(effect_speed).state);
id(panel)->set_effect_angle(id(effect_angle).state);
id(panel)->set_effect_hue_span(id(effect_hue_span).state);
id(panel)->set_flash_fade(id(fx_flash_fade).state);
std::string sp = id(effect_spread).current_option();
id(panel)->set_effect_spread(sp == "Per Digit" ? 1 : sp == "Per LED" ? 2 : 0);
std::string ef = id(effect_mode).current_option();
id(panel)->set_effect(ef == "Color Cycle" ? 1 : ef == "Flash" ? 2 : 0);
esp32:
variant: esp32c3
flash_size: 4MB
framework:
type: esp-idf
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret apikey
actions:
- action: show_seconds
variables:
duration: int
then:
- lambda: id(panel)->show_seconds(duration);
# Right aligned digits with an optional unit in the rightmost position, so
# 78F and -5C both read correctly. A unit of C or F also lights the degree
# mark. Pass an empty unit to give the number all four positions. There is
# no decimal point on the panel, so round before sending.
- action: show_number
variables:
value: int
unit: string
duration: int
then:
- lambda: id(panel)->show_number(value, unit, duration);
# Every populated LED, white, for a few seconds, then back to the time.
# It ignores color, effects, brightness and the light being off. Duration
# is milliseconds, and 3000 is the button below.
- action: lamp_test
variables:
duration: int
then:
- lambda: id(panel)->lamp_test(duration);
# Per digit color. Digit 0 is the leftmost and 3 the rightmost, and -1
# sets all four at once. Components are 0 to 255 and are not rescaled
# against each other, so a digit can be dimmer than its neighbors as well
# as a different hue. Each digit carries its own annunciator, so the colon
# takes the color of the hour ones digit and the AM mark the hour tens.
# This is runtime state and is not remembered across a reboot.
- action: set_digit_color
variables:
digit: int
red: int
green: int
blue: int
then:
- lambda: |-
id(panel)->set_digit_color(digit, red / 255.0f, green / 255.0f, blue / 255.0f);
# Hand the whole panel back to the Display light, gradients included.
- action: clear_digit_colors
then:
- lambda: id(panel)->clear_digit_colors();
# One LED, by the id on the board layout map. See docs/led-layout.md.
- action: set_position_color
variables:
position: int
red: int
green: int
blue: int
then:
- lambda: |-
id(panel)->set_position_color(position, red / 255.0f, green / 255.0f,
blue / 255.0f);
# A linear ramp between two colors across the whole panel. Angle is in
# degrees, 0 left to right and 90 top to bottom, so -45 runs from the
# bottom left corner to the top right.
# Everything about the running effect in one call, for automations that
# want to set it up atomically rather than nudging five entities.
# mode 0 off, 1 color cycle, 2 flash. spread 0 whole panel, 1 per digit,
# 2 per LED. speed is seconds for one full cycle, which for flash is one
# complete on and off. angle is the direction the spread runs. hue_span is
# how many degrees of the color wheel the panel covers end to end.
# flash_fade is seconds per flash transition, 0 to snap.
- action: set_effect
variables:
mode: int
spread: int
speed: float
angle: float
hue_span: float
flash_fade: float
then:
# Drives the entities rather than the component, so the Home Assistant
# UI ends up showing what the panel is actually doing. Calling the
# component directly would work and leave every slider stale.
#
# Values are clamped here because a number call outside its range is
# dropped rather than clamped, which would silently ignore the whole
# setting.
- lambda: |-
id(effect_speed).make_call().set_value(clamp(speed, 0.2f, 600.0f)).perform();
id(effect_angle).make_call().set_value(clamp(angle, -180.0f, 180.0f)).perform();
id(effect_hue_span).make_call().set_value(clamp(hue_span, 0.0f, 360.0f)).perform();
id(fx_flash_fade).make_call().set_value(clamp(flash_fade, 0.0f, 30.0f)).perform();
id(effect_spread).make_call().set_option(
spread == 1 ? "Per Digit" : spread == 2 ? "Per LED" : "Whole Panel").perform();
id(effect_mode).make_call().set_option(
mode == 1 ? "Color Cycle" : mode == 2 ? "Flash" : "None").perform();
- action: set_gradient
variables:
red1: int
green1: int
blue1: int
red2: int
green2: int
blue2: int
angle: float
then:
- lambda: |-
id(panel)->set_gradient(red1 / 255.0f, green1 / 255.0f, blue1 / 255.0f,
red2 / 255.0f, green2 / 255.0f, blue2 / 255.0f, angle);
ota:
platform: esphome
encryption:
network:
enable_ipv6: true
wifi:
ssid: "Voltage-legacy"
password: !secret voltage_legacy_psk
#use_address: ${name}.home
power_save_mode: high
fast_connect: on
min_auth_mode: WPA2
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Schlafzimmeruhr Fallback Hotspot"
password: !secret fallback_psk
captive_portal:
external_components:
- source:
type: local
path: components
time:
# No timezone on this one, deliberately. Home Assistant pushes its own
# timezone to the device on every time sync, DST rules included, but only
# if this platform has no timezone of its own. Setting one here compiles
# that path out and pins the clock to whatever is written instead.
- platform: homeassistant
id: ha_time
# SNTP is the fallback for a boot with no Home Assistant. It has nowhere to
# learn a timezone from, so this one is spelled out. It is also what the
# display shows for the first few seconds of every boot, until Home
# Assistant answers. Change it if the clock moves.
#- platform: sntp
# id: sntp_time
# timezone: !secret timezone
aip33628:
id: panel
clk_pin: GPIO21 # MCU socket pin 14 (HU-058D) or 16 (HU-058/SE), driver 1
data_pin: GPIO20 # MCU socket pin 5, driver 1
clk2_pin: GPIO10 # MCU socket pin 1, driver 2
data2_pin: GPIO9 # MCU socket pin 2, driver 2
time_id: ha_time
# Power on defaults. The switches below own both after that, and they
# restore their last state, so these only matter for a first ever boot.
twelve_hour: false
blink_colon: true
# Ceiling on IS[3:0], 0 to 15. Fixed, never varied by content. The stock
# firmware ran 0xF and drew 404mA doing it. Drop this if the drivers
# run hotter than you want. See docs/display-map.md.
max_current: 15
light:
- platform: aip33628
id: display
aip33628_id: panel
name: Display
restore_mode: RESTORE_DEFAULT_ON
# Gamma is applied inside the component, because it also has to allow for
# the 2.5mA floor the drivers cannot go below. Leaving it on here as well
# would square it.
gamma_correct: 1.0
# The two front panel buttons. Nothing on the board pulls these nets up. The
# stock 8051 held them high with its own quasi-bidirectional port pullup, and
# that chip is out of the socket, so the ESP32 internal pullup is the only one
# and the lines never see 5V. Switch closed shorts to ground, hence inverted.
#
# No behavior is attached here on purpose. Home Assistant is the control
# surface, so these are just inputs it can bind to. Press and hold patterns
# belong in an automation, not in this file.
binary_sensor:
- platform: gpio
id: button_1
name: Button 1
pin:
number: GPIO8 # MCU socket pin 9, net S1, top button on the case
mode:
input: true
pullup: true
inverted: true
filters:
- delayed_on: 20ms
- delayed_off: 20ms
- platform: gpio
id: button_2
name: Button 2
pin:
number: GPIO7 # MCU socket pin 10, net S2, bottom button on the case
mode:
input: true
pullup: true
inverted: true
filters:
- delayed_on: 20ms
- delayed_off: 20ms
switch:
- platform: template
id: twelve_hour_mode
name: 12 Hour Time
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
turn_on_action:
- lambda: id(panel)->set_twelve_hour(true);
turn_off_action:
- lambda: id(panel)->set_twelve_hour(false);
# Off means a steady colon, not a dark one.
- platform: template
id: colon_blink
name: Blink Colon
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
turn_on_action:
- lambda: id(panel)->set_blink_colon(true);
turn_off_action:
- lambda: id(panel)->set_blink_colon(false);
button:
# Three seconds of every populated LED lit white at half brightness, then
# back to the time with the settings it had before.
- platform: template
name: Lamp Test
on_press:
- lambda: id(panel)->lamp_test(3000);
# A shortcut for the common case, and something to press by hand.
# Anything more elaborate belongs in a Home Assistant automation calling
# the api actions above.
- platform: template
name: Show Seconds
on_press:
- lambda: id(panel)->show_seconds(5000);
# A UI handle for set_digit_color, and the quickest way to see the four
# digits driven independently.
- platform: template
name: Digit Color Demo
on_press:
- lambda: |-
id(panel)->set_digit_color(0, 1.00f, 0.00f, 0.00f);
id(panel)->set_digit_color(1, 1.00f, 0.45f, 0.00f);
id(panel)->set_digit_color(2, 0.00f, 1.00f, 0.25f);
id(panel)->set_digit_color(3, 0.10f, 0.30f, 1.00f);
- platform: template
name: Clear Digit Colors
on_press:
- lambda: id(panel)->clear_digit_colors();
# Green in the bottom left corner running to yellow in the top right, which
# is the sort of thing the stock firmware did. -45 degrees is that diagonal.
- platform: template
name: Diagonal Gradient
on_press:
- lambda: |-
id(panel)->set_gradient(0.00f, 1.00f, 0.10f,
1.00f, 0.85f, 0.00f, -45.0f);
# A UI handle for the show_number action, which otherwise lives only under
# Developer Tools > Actions. Setting either entity redraws immediately and the
# panel falls back to the time ten seconds later on its own.
select:
# Effects run on the device, not in Home Assistant, so a fade stays smooth
# and keeps going if the network drops. Every entity here is a normal one,
# so an automation can drive them, or call the set_effect action to set the
# lot at once.
- platform: template
name: Effect
id: effect_mode
optimistic: true
restore_value: true
initial_option: "None"
options: ["None", "Color Cycle", "Flash"]
set_action:
- lambda: |-
std::string v = x;
id(panel)->set_effect(v == "Color Cycle" ? 1 : v == "Flash" ? 2 : 0);
# Whole Panel moves everything together. Per Digit gives each digit its own
# phase, so the color walks across the four of them. Per LED gives every
# position its own, which is the smooth sweep.
- platform: template
name: Effect Spread
id: effect_spread
optimistic: true
restore_value: true
initial_option: "Whole Panel"
options: ["Whole Panel", "Per Digit", "Per LED"]
set_action:
- lambda: |-
std::string v = x;
id(panel)->set_effect_spread(v == "Per Digit" ? 1 : v == "Per LED" ? 2 : 0);
- platform: template
name: Test Unit
id: test_unit
optimistic: true
restore_value: true
initial_option: "F"
options: ["None", "F", "C"]
set_action:
- lambda: |-
std::string u = x;
if (u == "None") u.clear();
id(panel)->show_number((int) id(test_number).state, u, 10000);
number:
# Seconds for one full cycle. For Flash this is one complete on and off.
- platform: template
name: Effect Speed
id: effect_speed
optimistic: true
restore_value: true
initial_value: 10
min_value: 0.2
max_value: 600
step: 0.2
unit_of_measurement: s
mode: box
set_action:
- lambda: id(panel)->set_effect_speed(x);
# Which way the spread runs. 0 is left to right, 90 top to bottom, 180
# right to left. -45 is the bottom left to top right diagonal and 135 is
# the other one, top right to bottom left.
- platform: template
name: Effect Angle
id: effect_angle
optimistic: true
restore_value: true
initial_value: 0
min_value: -180
max_value: 180
step: 15
unit_of_measurement: deg
mode: box
set_action:
- lambda: id(panel)->set_effect_angle(x);
# How many degrees of the color wheel the panel covers end to end. A full
# 360 packs the whole spectrum across four digits and reads as noise, so
# the useful settings are narrow. 60 is roughly red through orange, 90
# reaches yellow. 0 puts the whole panel on one color that still cycles.
# Only Color Cycle uses this, and only when Spread is not Whole Panel.
- platform: template
name: Effect Hue Span
id: effect_hue_span
optimistic: true
restore_value: true
initial_value: 90
min_value: 0
max_value: 360
step: 5
unit_of_measurement: deg
mode: box
set_action:
- lambda: id(panel)->set_effect_hue_span(x);
# Seconds each flash transition takes, independent of the flash rate. 0
# snaps on and off. 0.1 gives a quick ramp. Anything past half the Effect
# Speed is capped there, which is the point where it becomes a triangle.
- platform: template
name: Flash Fade
id: fx_flash_fade
optimistic: true
restore_value: true
initial_value: 0
min_value: 0
max_value: 30
step: 0.05
unit_of_measurement: s
mode: box
set_action:
- lambda: id(panel)->set_flash_fade(x);
- platform: template
name: Test Number
id: test_number
optimistic: true
restore_value: true
initial_value: 72
min_value: -999
max_value: 9999
step: 1
mode: box
set_action:
- lambda: |-
std::string u = id(test_unit).current_option();
if (u == "None") u.clear();
id(panel)->show_number((int) x, u, 10000);