Compare commits
1 commit
feature/fi
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d3228dd8c1 |
19 changed files with 0 additions and 1900 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -3,11 +3,3 @@
|
||||||
# You can modify this file to suit your needs.
|
# You can modify this file to suit your needs.
|
||||||
/.esphome/
|
/.esphome/
|
||||||
/secrets.yaml
|
/secrets.yaml
|
||||||
/secrets.*.yaml
|
|
||||||
!/secrets.example.yaml
|
|
||||||
/.pio
|
|
||||||
/.vscode
|
|
||||||
__pycache__
|
|
||||||
/_ignored
|
|
||||||
esp32_test.code-workspace
|
|
||||||
case/d1_mini_esp32.FCStd1
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,153 +0,0 @@
|
||||||
import logging
|
|
||||||
|
|
||||||
from esphome import core
|
|
||||||
from esphome.components import display, font, time, light, color
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.cpp_generator as cpp
|
|
||||||
|
|
||||||
from esphome.const import (
|
|
||||||
CONF_ID,
|
|
||||||
CONF_NAME,
|
|
||||||
CONF_RAW_DATA_ID,
|
|
||||||
CONF_TYPE,
|
|
||||||
CONF_TIME_ID,
|
|
||||||
CONF_SEGMENTS,
|
|
||||||
CONF_ADDRESSABLE_LIGHT_ID,
|
|
||||||
CONF_HOUR,
|
|
||||||
CONF_HOURS,
|
|
||||||
CONF_MINUTE,
|
|
||||||
CONF_MINUTES,
|
|
||||||
CONF_BRIGHTNESS,
|
|
||||||
CONF_UPDATE_INTERVAL,
|
|
||||||
)
|
|
||||||
from esphome.core import CORE, HexInt
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
CONF_DISPLAY_ID = "display_id"
|
|
||||||
|
|
||||||
CONF_LINE_START_X = "x1"
|
|
||||||
CONF_LINE_START_Y = "y1"
|
|
||||||
|
|
||||||
CONF_LINE_END_X = "x2"
|
|
||||||
CONF_LINE_END_Y = "y2"
|
|
||||||
|
|
||||||
CONF_LINE = "line"
|
|
||||||
|
|
||||||
CONF_COLOR_ON = "color_on"
|
|
||||||
CONF_COLOR_OFF = "color_off"
|
|
||||||
|
|
||||||
DATA_VECTOR_SEGMENTS_HOUR = "data_vector_segments_hour"
|
|
||||||
DATA_VECTOR_SEGMENTS_MINUTE = "data_vector_segments_minute"
|
|
||||||
DATA_VECTOR_SEGMENTS = "data_vector_segments_ptr"
|
|
||||||
|
|
||||||
CONF_WORDCLOCK_STATIC_SEGMENTS = "static_segments"
|
|
||||||
CONF_HOUR_OFFSET = "hour_offset"
|
|
||||||
CONF_FADE_LENGTH = "fade_length"
|
|
||||||
|
|
||||||
DEPENDENCIES = ["display", "time"]
|
|
||||||
MULTI_CONF = False
|
|
||||||
|
|
||||||
int8 = cg.global_ns.namespace("int8_t")
|
|
||||||
wordclock_ns = cg.esphome_ns.namespace("wordclock")
|
|
||||||
SegmentCoords = wordclock_ns.struct("SegmentCoords")
|
|
||||||
Wordclock = wordclock_ns.class_(
|
|
||||||
"Wordclock", cg.PollingComponent
|
|
||||||
)
|
|
||||||
|
|
||||||
WORDCLOCK_SEGMENT_SCHEMA = {
|
|
||||||
cv.Required(CONF_NAME): cv.string_strict,
|
|
||||||
cv.Required(CONF_LINE): {
|
|
||||||
cv.Required(CONF_LINE_START_X): cv.int_,
|
|
||||||
cv.Required(CONF_LINE_START_Y): cv.int_,
|
|
||||||
cv.Required(CONF_LINE_END_X): cv.int_,
|
|
||||||
cv.Optional(CONF_LINE_END_Y): cv.int_,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
WORDCLOCK_HOUR_SCHEMA = {
|
|
||||||
cv.Required(CONF_HOUR): cv.uint16_t,
|
|
||||||
cv.Required(CONF_SEGMENTS): cv.ensure_list(cv.string_strict),
|
|
||||||
}
|
|
||||||
|
|
||||||
WORDCLOCK_MINUTE_SCHEMA = {
|
|
||||||
cv.Required(CONF_MINUTE): cv.uint16_t,
|
|
||||||
cv.Required(CONF_HOUR_OFFSET): cv.int_range(-1,1),
|
|
||||||
cv.Required(CONF_SEGMENTS): cv.ensure_list(cv.string_strict),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WORDCLOCK_SCHEMA = cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(Wordclock),
|
|
||||||
cv.Required(CONF_DISPLAY_ID): cv.use_id(
|
|
||||||
display.DisplayBuffer
|
|
||||||
),
|
|
||||||
cv.Required(CONF_TIME_ID): cv.use_id(
|
|
||||||
time.RealTimeClock
|
|
||||||
),
|
|
||||||
cv.Required(CONF_ADDRESSABLE_LIGHT_ID): cv.use_id(
|
|
||||||
light.AddressableLightState
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
|
|
||||||
cv.Optional(CONF_COLOR_ON): cv.use_id(color.ColorStruct),
|
|
||||||
cv.Optional(CONF_COLOR_OFF): cv.use_id(color.ColorStruct),
|
|
||||||
cv.Optional(CONF_WORDCLOCK_STATIC_SEGMENTS): cv.ensure_list(cv.string_strict),
|
|
||||||
cv.Required(CONF_SEGMENTS): cv.ensure_list(WORDCLOCK_SEGMENT_SCHEMA),
|
|
||||||
cv.Required(CONF_MINUTES): cv.ensure_list(WORDCLOCK_MINUTE_SCHEMA),
|
|
||||||
cv.Required(CONF_HOURS): cv.ensure_list(WORDCLOCK_HOUR_SCHEMA),
|
|
||||||
cv.Optional(
|
|
||||||
CONF_FADE_LENGTH, default="700ms"
|
|
||||||
): cv.positive_time_period_milliseconds,
|
|
||||||
cv.Optional(
|
|
||||||
CONF_UPDATE_INTERVAL, default="16ms"
|
|
||||||
): cv.positive_time_period_milliseconds,
|
|
||||||
cv.GenerateID(DATA_VECTOR_SEGMENTS): cv.declare_id(cg.std_vector.template(cg.uint16)),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = WORDCLOCK_SCHEMA
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
wrapped_display = await cg.get_variable(config[CONF_DISPLAY_ID])
|
|
||||||
wrapped_time = await cg.get_variable(config[CONF_TIME_ID])
|
|
||||||
wrapped_light_state = await cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID], wrapped_time, wrapped_display, wrapped_light_state)
|
|
||||||
|
|
||||||
if CONF_COLOR_ON in config:
|
|
||||||
wrapped_color_on = await cg.get_variable(config[CONF_COLOR_ON])
|
|
||||||
cg.add(var.set_on_color(wrapped_color_on))
|
|
||||||
if CONF_COLOR_OFF in config:
|
|
||||||
wrapped_color_off = await cg.get_variable(config[CONF_COLOR_OFF])
|
|
||||||
cg.add(var.set_off_color(wrapped_color_off))
|
|
||||||
|
|
||||||
cg.add(var.setup_transitions(config[CONF_FADE_LENGTH]))
|
|
||||||
cg.add(var.set_brightness(int(config[CONF_BRIGHTNESS] * 255)))
|
|
||||||
|
|
||||||
SEGMENT_MAP = dict()
|
|
||||||
for idx, segment in enumerate(config[CONF_SEGMENTS]):
|
|
||||||
SEGMENT_MAP[segment[CONF_NAME]] = idx
|
|
||||||
line = segment[CONF_LINE]
|
|
||||||
|
|
||||||
x1 = line[CONF_LINE_START_X]
|
|
||||||
y1 = line[CONF_LINE_START_Y]
|
|
||||||
x2 = line[CONF_LINE_END_X]
|
|
||||||
y2 = line.get(CONF_LINE_END_Y, y1)
|
|
||||||
|
|
||||||
exp = cg.StructInitializer(SegmentCoords, ("x1", x1), ("y1", y1), ("x2", x2), ("y2", y2),)
|
|
||||||
cg.add(var.add_segment(exp))
|
|
||||||
|
|
||||||
if CONF_WORDCLOCK_STATIC_SEGMENTS in config:
|
|
||||||
for segment_name in config[CONF_WORDCLOCK_STATIC_SEGMENTS]:
|
|
||||||
cg.add(var.add_static(SEGMENT_MAP[segment_name]))
|
|
||||||
|
|
||||||
for idx, hour in enumerate(config[CONF_HOURS]):
|
|
||||||
segment_ids = [SEGMENT_MAP[a] for a in hour[CONF_SEGMENTS]]
|
|
||||||
cg.add(var.add_hour(hour[CONF_HOUR], cg.std_vector.template(cg.uint16).new(segment_ids)))
|
|
||||||
|
|
||||||
for idx, minute in enumerate(config[CONF_MINUTES]):
|
|
||||||
segment_ids = [SEGMENT_MAP[a] for a in minute[CONF_SEGMENTS]]
|
|
||||||
cg.add(var.add_minute(minute[CONF_MINUTE], minute[CONF_HOUR_OFFSET], cg.std_vector.template(cg.uint16).new(segment_ids)))
|
|
||||||
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
|
|
||||||
#include "wordclock.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace wordclock {
|
|
||||||
|
|
||||||
void Wordclock::setup() {
|
|
||||||
this->valid_time = this->time->now().is_valid();
|
|
||||||
if (!this->valid_time) {
|
|
||||||
this->start_idle_animation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wordclock::update() {
|
|
||||||
esphome::time::ESPTime time = this->time->now();
|
|
||||||
|
|
||||||
if (!time.is_valid()) {
|
|
||||||
if (this->valid_time) {
|
|
||||||
ESP_LOGD("wordclock.cpp", "time is not valid [%02i:%02i:%02i]", time.hour, time.minute, time.second);
|
|
||||||
this->start_idle_animation();
|
|
||||||
this->valid_time = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!this->valid_time) {
|
|
||||||
ESP_LOGD("wordclock.cpp", "time is valid [%02i:%02i:%02i]", time.hour, time.minute, time.second);
|
|
||||||
this->end_idle_animation();
|
|
||||||
this->valid_time = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool dirty = false;
|
|
||||||
|
|
||||||
int8_t minute = this->find_minute(time.minute);
|
|
||||||
int8_t hour = this->find_hour((time.hour + this->minutes->at(minute).hour_offset) % 24);
|
|
||||||
|
|
||||||
if (hour != this->current_hour) {
|
|
||||||
this->current_hour = hour;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
if (minute != this->current_minute) {
|
|
||||||
this->current_minute = minute;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirty) {
|
|
||||||
this->previous_segments->clear();
|
|
||||||
|
|
||||||
for (uint16_t segment_idx : *this->current_segments) {
|
|
||||||
this->previous_segments->push_back(segment_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->current_segments->clear();
|
|
||||||
|
|
||||||
for (uint16_t segment_idx : *this->static_segments) {
|
|
||||||
this->current_segments->push_back(segment_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint16_t segment_idx : *(this->minutes->at(minute).segments)) {
|
|
||||||
this->current_segments->push_back(segment_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint16_t segment_idx : *(this->hours->at(hour).segments)) {
|
|
||||||
this->current_segments->push_back(segment_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(this->current_segments->begin(), this->current_segments->end());
|
|
||||||
|
|
||||||
this->find_difference(this->previous_segments, this->current_segments);
|
|
||||||
|
|
||||||
this->on_transformer->reset();
|
|
||||||
this->off_transformer->reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->current_position = 0;
|
|
||||||
uint8_t transition_progress = this->on_transformer->apply().value_or(255);
|
|
||||||
Color removed_color = this->on_color.gradient(this->off_color, transition_progress);
|
|
||||||
|
|
||||||
for (uint16_t segment_idx : *this->removed_segments) {
|
|
||||||
this->disable_segment_effect(segment_idx, this->off_color, transition_progress);
|
|
||||||
}
|
|
||||||
for (uint16_t segment_idx : *this->added_segments) {
|
|
||||||
this->enable_segment_effect(segment_idx, this->off_color, transition_progress);
|
|
||||||
}
|
|
||||||
for (uint16_t segment_idx : *this->staying_segments) {
|
|
||||||
this->apply_segment_effect(segment_idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wordclock::find_difference(std::vector<uint16_t> *a_vec, std::vector<uint16_t> *b_vec) {
|
|
||||||
this->added_segments->clear();
|
|
||||||
this->removed_segments->clear();
|
|
||||||
this->staying_segments->clear();
|
|
||||||
|
|
||||||
auto a = a_vec->begin();
|
|
||||||
auto b = b_vec->begin();
|
|
||||||
|
|
||||||
while (!(a == a_vec->end() && b == b_vec->end())) {
|
|
||||||
if (a == a_vec->end()) { this->added_segments-> push_back(*b); b++; }
|
|
||||||
else if (b == b_vec->end()) { this->removed_segments->push_back(*a); a++; }
|
|
||||||
else if (*a > *b) { this->added_segments-> push_back(*b); b++; }
|
|
||||||
else if (*a < *b) { this->removed_segments->push_back(*a); a++; }
|
|
||||||
else if (*a == *b) { this->staying_segments->push_back(*a); a++; b++; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Color Wordclock::get_next_color_base_(uint32_t position, uint32_t speed, uint16_t width, const Color ¤t_color) {
|
|
||||||
esphome::light::ESPHSVColor hsv;
|
|
||||||
hsv.value = 255;
|
|
||||||
hsv.saturation = 240;
|
|
||||||
hsv.hue = ((millis() * speed + (position * (0xFFFF / width))) % 0xFFFF) >> 8;
|
|
||||||
return hsv.to_rgb();
|
|
||||||
}
|
|
||||||
|
|
||||||
Color Wordclock::get_next_color(uint32_t position, const Color ¤t_color) {
|
|
||||||
if (this->effect_active) {
|
|
||||||
uint32_t speed_ = 3;
|
|
||||||
uint16_t width_ = 100; // Original width_ = 50
|
|
||||||
return get_next_color_base_(position, speed_, width_, current_color);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Color(this->on_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t Wordclock::find_hour(uint8_t target_value) {
|
|
||||||
std::vector<Hour> *elements = this->hours;
|
|
||||||
for (int i = 0; i < elements->size(); i++) {
|
|
||||||
if (elements->at(i).hour == target_value) {
|
|
||||||
return i;
|
|
||||||
} else if (elements->at(i).hour > target_value) {
|
|
||||||
return i - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return elements->size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t Wordclock::find_minute(uint8_t target_value) {
|
|
||||||
std::vector<Minute> *elements = this->minutes;
|
|
||||||
for (int i = 0; i < elements->size(); i++) {
|
|
||||||
if (elements->at(i).minute == target_value) {
|
|
||||||
return i;
|
|
||||||
} else if (elements->at(i).minute > target_value) {
|
|
||||||
return i - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return elements->size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wordclock::setup_transitions(uint32_t milliseconds) {
|
|
||||||
this->on_transformer->setup(0, 255, milliseconds);
|
|
||||||
this->off_transformer->setup(255, 0, milliseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
Wordclock::Wordclock(
|
|
||||||
esphome::time::RealTimeClock *time,
|
|
||||||
esphome::addressable_light::AddressableLightDisplay *display,
|
|
||||||
esphome::light::AddressableLightState *light_state) {
|
|
||||||
|
|
||||||
this->time = time;
|
|
||||||
this->display = display;
|
|
||||||
this->light_state = light_state;
|
|
||||||
|
|
||||||
this->light_state->set_default_transition_length(1);
|
|
||||||
|
|
||||||
this->minutes = new std::vector<Minute>();
|
|
||||||
this->hours = new std::vector<Hour>();
|
|
||||||
|
|
||||||
this->segments = new std::vector<SegmentCoords>();
|
|
||||||
this->static_segments = new std::vector<uint16_t>();
|
|
||||||
|
|
||||||
this->previous_segments = new std::vector<uint16_t>();
|
|
||||||
this->current_segments = new std::vector<uint16_t>();
|
|
||||||
|
|
||||||
this->added_segments = new std::vector<uint16_t>();
|
|
||||||
this->removed_segments = new std::vector<uint16_t>();
|
|
||||||
this->staying_segments = new std::vector<uint16_t>();
|
|
||||||
|
|
||||||
this->on_transformer = new BrightnessTransitionTransformer();
|
|
||||||
this->off_transformer = new BrightnessTransitionTransformer();
|
|
||||||
}
|
|
||||||
} // namespace wordclock
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,223 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "esphome.h"
|
|
||||||
// #include "esphome/components/sensor/sensor.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace wordclock {
|
|
||||||
|
|
||||||
struct Minute {
|
|
||||||
uint8_t minute;
|
|
||||||
uint8_t hour_offset;
|
|
||||||
std::vector<uint16_t> *segments;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Hour {
|
|
||||||
uint8_t hour;
|
|
||||||
std::vector<uint16_t> *segments;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SegmentCoords {
|
|
||||||
uint16_t x1;
|
|
||||||
uint16_t y1;
|
|
||||||
uint16_t x2;
|
|
||||||
uint16_t y2;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BrightnessTransitionTransformer {
|
|
||||||
public:
|
|
||||||
virtual ~BrightnessTransitionTransformer() = default;
|
|
||||||
|
|
||||||
void setup(const uint8_t start_values, const uint8_t target_values, uint32_t length) {
|
|
||||||
this->start_time_ = millis();
|
|
||||||
this->length_ = length;
|
|
||||||
this->start_values_ = start_values;
|
|
||||||
this->target_values_ = target_values;
|
|
||||||
this->start();
|
|
||||||
}
|
|
||||||
virtual bool is_finished() { return this->get_progress_() >= 1.0f; }
|
|
||||||
virtual void start() {}
|
|
||||||
// virtual optional<uint8_t> apply() = 0;
|
|
||||||
virtual optional<uint8_t> apply() {
|
|
||||||
float v = BrightnessTransitionTransformer::smoothed_progress(this->get_progress_());
|
|
||||||
return esphome::lerp(v, this->start_values_, this->target_values_);
|
|
||||||
}
|
|
||||||
virtual void stop() {}
|
|
||||||
void reset() {
|
|
||||||
this->start_time_ = millis();
|
|
||||||
}
|
|
||||||
const uint8_t &get_start_values() const { return this->start_values_; }
|
|
||||||
const uint8_t &get_target_values() const { return this->target_values_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The progress of this transition, on a scale of 0 to 1.
|
|
||||||
float get_progress_() {
|
|
||||||
uint32_t now = esphome::millis();
|
|
||||||
if (now < this->start_time_)
|
|
||||||
return 0.0f;
|
|
||||||
if (now >= this->start_time_ + this->length_)
|
|
||||||
return 1.0f;
|
|
||||||
return clamp((now - this->start_time_) / float(this->length_), 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static float smoothed_progress(float x) { return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); }
|
|
||||||
|
|
||||||
uint32_t start_time_;
|
|
||||||
uint32_t length_;
|
|
||||||
uint8_t start_values_;
|
|
||||||
uint8_t target_values_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Wordclock : public esphome::PollingComponent {
|
|
||||||
public:
|
|
||||||
Wordclock(
|
|
||||||
esphome::time::RealTimeClock *time,
|
|
||||||
esphome::addressable_light::AddressableLightDisplay *display,
|
|
||||||
esphome::light::AddressableLightState *light_state
|
|
||||||
);
|
|
||||||
|
|
||||||
void setup();
|
|
||||||
void update();
|
|
||||||
void set_brightness(uint8_t brightness) {
|
|
||||||
this->brightness = brightness;
|
|
||||||
}
|
|
||||||
bool get_effect_active() { return this->effect_active; }
|
|
||||||
void set_effect_active(bool x) { this->effect_active = x; }
|
|
||||||
|
|
||||||
bool get_effect_width() { return this->effect_width; }
|
|
||||||
void set_effect_width(bool x) { this->effect_width = x; }
|
|
||||||
|
|
||||||
bool get_effect_speed() { return this->effect_speed; }
|
|
||||||
void set_effect_speed(bool x) { this->effect_speed = x; }
|
|
||||||
|
|
||||||
void set_on_color(Color on_color) {
|
|
||||||
this->on_color = on_color;
|
|
||||||
}
|
|
||||||
void set_off_color(Color off_color) {
|
|
||||||
this->off_color = off_color;
|
|
||||||
}
|
|
||||||
void setup_transitions(uint32_t milliseconds);
|
|
||||||
|
|
||||||
void add_segment(SegmentCoords segment) {
|
|
||||||
this->segments->push_back(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_static(uint16_t segment_id) {
|
|
||||||
this->static_segments->push_back(segment_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_hour(uint8_t hour, std::vector<uint16_t> *segments_ptr) {
|
|
||||||
this->hours->push_back(Hour{
|
|
||||||
.hour = hour,
|
|
||||||
.segments = segments_ptr,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_minute(uint8_t minute, uint8_t hour_offset, std::vector<uint16_t> *segments_ptr) {
|
|
||||||
this->minutes->push_back(Minute{
|
|
||||||
.minute = minute,
|
|
||||||
.hour_offset = hour_offset,
|
|
||||||
.segments = segments_ptr,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Color on_color {Color(0xFFFFFF)};
|
|
||||||
Color off_color {Color(0x000000)};
|
|
||||||
uint8_t brightness{255};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// ESPHome Components
|
|
||||||
esphome::time::RealTimeClock *time;
|
|
||||||
esphome::light::AddressableLightState *light_state;
|
|
||||||
esphome::addressable_light::AddressableLightDisplay *display;
|
|
||||||
|
|
||||||
// Time related state
|
|
||||||
bool valid_time{false};
|
|
||||||
int8_t current_hour{-1};
|
|
||||||
int8_t current_minute{-1};
|
|
||||||
|
|
||||||
// Segments Configuration
|
|
||||||
std::vector<SegmentCoords> *segments;
|
|
||||||
std::vector<uint16_t> *static_segments;
|
|
||||||
std::vector<Minute> *minutes;
|
|
||||||
std::vector<Hour> *hours;
|
|
||||||
|
|
||||||
BrightnessTransitionTransformer *off_transformer;
|
|
||||||
BrightnessTransitionTransformer *on_transformer;
|
|
||||||
|
|
||||||
std::vector<uint16_t> *added_segments;
|
|
||||||
std::vector<uint16_t> *removed_segments;
|
|
||||||
std::vector<uint16_t> *staying_segments;
|
|
||||||
|
|
||||||
std::vector<uint16_t> *current_segments;
|
|
||||||
std::vector<uint16_t> *previous_segments;
|
|
||||||
|
|
||||||
uint32_t current_position{0};
|
|
||||||
bool effect_active{true};
|
|
||||||
uint32_t effect_speed{12};
|
|
||||||
uint16_t effect_width{50};
|
|
||||||
Color get_next_color_base_(uint32_t position, uint32_t speed, uint16_t width, const Color ¤t_color);
|
|
||||||
Color get_next_color(uint32_t position, const Color ¤t_color);
|
|
||||||
|
|
||||||
|
|
||||||
// Utils
|
|
||||||
int8_t find_hour(uint8_t target_value);
|
|
||||||
int8_t find_minute(uint8_t target_value);
|
|
||||||
void find_difference(std::vector<uint16_t> *a_vec, std::vector<uint16_t> *b_vec);
|
|
||||||
|
|
||||||
void segment_effect_base(uint16_t segment_id, bool to_effect, Color base_color, uint8_t transition_progress) {
|
|
||||||
SegmentCoords s = this->segments->at(segment_id);
|
|
||||||
int x1 = s.x1; int y1 = s.y1; int x2 = s.x2; int y2 = s.y2;
|
|
||||||
Color color_to_draw;
|
|
||||||
Color effect_color;
|
|
||||||
|
|
||||||
const int32_t dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
|
|
||||||
const int32_t dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
|
|
||||||
int32_t err = dx + dy;
|
|
||||||
while (true) {
|
|
||||||
effect_color = this->get_next_color(x1 + y1, Color(0)) * this->brightness;
|
|
||||||
if (to_effect) {
|
|
||||||
color_to_draw = base_color.gradient(effect_color, transition_progress);
|
|
||||||
} else {
|
|
||||||
color_to_draw = effect_color.gradient(base_color, transition_progress);
|
|
||||||
}
|
|
||||||
this->display->draw_pixel_at(x1, y1, color_to_draw);
|
|
||||||
if (x1 == x2 && y1 == y2) break;
|
|
||||||
int32_t e2 = 2 * err;
|
|
||||||
if (e2 >= dy) {
|
|
||||||
err += dy;
|
|
||||||
x1 += sx;
|
|
||||||
}
|
|
||||||
if (e2 <= dx) {
|
|
||||||
err += dx;
|
|
||||||
y1 += sy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_segment_effect(uint16_t segment_id, Color off_color, uint8_t transition_progress) {
|
|
||||||
segment_effect_base(segment_id, true, off_color, transition_progress);
|
|
||||||
}
|
|
||||||
void disable_segment_effect(uint16_t segment_id, Color off_color, uint8_t transition_progress) {
|
|
||||||
segment_effect_base(segment_id, false, off_color, transition_progress);
|
|
||||||
}
|
|
||||||
void apply_segment_effect(uint16_t segment_id) {
|
|
||||||
segment_effect_base(segment_id, true, Color(0), 255);
|
|
||||||
}
|
|
||||||
void start_idle_animation() {
|
|
||||||
this->display->fill(this->off_color);
|
|
||||||
this->display->set_enabled(false);
|
|
||||||
this->light_state->turn_on().set_effect("random_twinkle").perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
void end_idle_animation() {
|
|
||||||
this->light_state->turn_off().perform();
|
|
||||||
this->display->set_enabled(true);
|
|
||||||
this->display->fill(this->off_color);
|
|
||||||
this->previous_segments->clear();
|
|
||||||
this->current_segments->clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace wordclock
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
# Wordclock Development Log
|
|
||||||
|
|
||||||
|
|
||||||
## Feature "make speed and width of rainbow effect user-configurable" [2023-06-10 17:13]
|
|
||||||
|
|
||||||
[17:16] Step - add attributes, getters, and setters for effect-speed and -width.
|
|
||||||
- [x] add attr/funcs to header file.
|
|
||||||
- [x] add inputs to yaml.
|
|
||||||
-
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
esphome:
|
|
||||||
name: "display-thing"
|
|
||||||
|
|
||||||
esp8266:
|
|
||||||
board: d1_mini
|
|
||||||
framework:
|
|
||||||
version: recommended
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
ap:
|
|
||||||
ssid: "${devicename}"
|
|
||||||
password: !secret ap_password
|
|
||||||
manual_ip:
|
|
||||||
static_ip: !secret manualip_static_ip
|
|
||||||
gateway: !secret manualip_gateway
|
|
||||||
subnet: !secret manualip_subnet
|
|
||||||
dns1: 1.1.1.1
|
|
||||||
dns2: 1.0.0.1
|
|
||||||
|
|
||||||
api:
|
|
||||||
|
|
||||||
ota:
|
|
||||||
password: "${devicename}"
|
|
||||||
|
|
||||||
logger:
|
|
||||||
# esp8266_store_log_strings_in_flash: false
|
|
||||||
# web_server:
|
|
||||||
# port: 80
|
|
||||||
|
|
||||||
light:
|
|
||||||
- name: NeoPixel Strip 1
|
|
||||||
id: neopixel_strip_1
|
|
||||||
platform: neopixelbus
|
|
||||||
type: GRB
|
|
||||||
variant: WS2812
|
|
||||||
pin: GPIO3
|
|
||||||
num_leds: 199
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
default_transition_length: 0s
|
|
||||||
color_correct: [100%, 100%, 100%]
|
|
||||||
method:
|
|
||||||
type: esp8266_dma
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: addressable_light
|
|
||||||
id: led_matrix_display
|
|
||||||
addressable_light_id: neopixel_strip_1
|
|
||||||
width: 18
|
|
||||||
height: 11
|
|
||||||
rotation: 270°
|
|
||||||
update_interval: 16ms
|
|
||||||
pixel_mapper: |-
|
|
||||||
int mapping_even[] = {0, 2, 4, 6,8,10,11,13,15,17, 16,16,16,16, 16,16,16,16};
|
|
||||||
int mapping_odd[] = {17,15,13,11,9, 7, 6, 4, 2, 0, 16,16,16,16, 16,16,16,16};
|
|
||||||
if (x > 9) return -1;
|
|
||||||
if (y % 2 == 0) {
|
|
||||||
return (y * 18 + mapping_even[x]); // mapping_even[y];
|
|
||||||
} else {
|
|
||||||
return (y * 18 + mapping_odd[x]); // mapping_odd[y];
|
|
||||||
//return (y * 18 + 17 - x); // mapping_odd[y];
|
|
||||||
}
|
|
||||||
//if (x % 2 == 0) {
|
|
||||||
// return (x * 8) + y;
|
|
||||||
//}
|
|
||||||
//return (x * 8) + (7 - y);
|
|
||||||
lambda: |-
|
|
||||||
Color red = Color(0xFF0000);
|
|
||||||
Color green = Color(0x00FF00);
|
|
||||||
Color blue = Color(0x0000FF);
|
|
||||||
Color turquoise = Color(0x00FFFF);
|
|
||||||
it.draw_pixel_at(0,0, green);
|
|
||||||
it.draw_pixel_at(10,0, red);
|
|
||||||
it.draw_pixel_at(0,9, turquoise);
|
|
||||||
it.draw_pixel_at(10,9, blue);
|
|
||||||
//it.line(0,0, 11,0, red);
|
|
||||||
//it.line(0,1, 11,1, green);
|
|
||||||
//it.line(0,2, 11,2, blue);
|
|
||||||
//it.line(0,3, 11,3, red);
|
|
||||||
//it.line(0,4, 11,4, green);
|
|
||||||
//it.line(0,5, 11,5, blue);
|
|
||||||
//it.line(0,6, 11,6, red);
|
|
||||||
//it.line(0,7, 11,7, green);
|
|
||||||
//it.line(0,8, 11,8, blue);
|
|
||||||
//it.line(0,9, 11,9, red);
|
|
||||||
//it.line(0,10, 9,10, green);
|
|
||||||
//it.line(0,11, 9,11, blue);
|
|
||||||
// it.line(5,14, 10,14, green);
|
|
||||||
//it.rectangle(0, 0, 17, 10, red);
|
|
||||||
//it.rectangle(1, 1, 16, 9, green);
|
|
||||||
//it.rectangle(2, 2, 15, 8, blue);
|
|
||||||
//it.rectangle(3, 3, 14, 7, red);
|
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: sntp
|
|
||||||
id: current_time
|
|
||||||
timezone: !secret timezone
|
|
||||||
64
flake.lock
generated
64
flake.lock
generated
|
|
@ -1,64 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1683777345,
|
|
||||||
"narHash": "sha256-V2p/A4RpEGqEZussOnHYMU6XglxBJGCODdzoyvcwig8=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "635a306fc8ede2e34cb3dd0d6d0a5d49362150ed",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs",
|
|
||||||
"systems": "systems",
|
|
||||||
"utils": "utils"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1680978846,
|
|
||||||
"narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "x86_64-linux",
|
|
||||||
"rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "x86_64-linux",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": [
|
|
||||||
"systems"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681202837,
|
|
||||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
||||||
67
flake.nix
67
flake.nix
|
|
@ -1,67 +0,0 @@
|
||||||
{
|
|
||||||
description = "EspHome Word Clock";
|
|
||||||
inputs = {
|
|
||||||
systems.url = "github:nix-systems/x86_64-linux";
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
|
||||||
# nixpkgs.url = "github:NixOS/nixpkgs/22.11";
|
|
||||||
utils.url = "github:numtide/flake-utils";
|
|
||||||
utils.inputs.systems.follows = "systems";
|
|
||||||
};
|
|
||||||
outputs = { self, nixpkgs, utils, ... }: utils.lib.eachDefaultSystem (
|
|
||||||
system:
|
|
||||||
let pkgs = import nixpkgs { inherit system; };
|
|
||||||
in {
|
|
||||||
|
|
||||||
# This block here is used when running `nix develop`
|
|
||||||
devShells.default = pkgs.mkShell rec {
|
|
||||||
# Update the name to something that suites your project.
|
|
||||||
name = "esphome-wordclock";
|
|
||||||
|
|
||||||
# build environment dependencies
|
|
||||||
packages = with pkgs; [
|
|
||||||
esphome
|
|
||||||
];
|
|
||||||
|
|
||||||
# Setting up the environment variables you need during development.
|
|
||||||
|
|
||||||
# Todo figure out why I can't use clang on Asahi but can on Darwin
|
|
||||||
# Use "clang++" for most systems but OSX Asahi requires g++ for some reason or a runtime error occurs
|
|
||||||
shellHook = let
|
|
||||||
# This is for an icon that is used below for the command line input below
|
|
||||||
icon = "f121";
|
|
||||||
in ''
|
|
||||||
export PS1="$(echo -e '\u${icon}') {\[$(tput sgr0)\]\[\033[38;5;228m\]\w\[$(tput sgr0)\]\[\033[38;5;15m\]} (${name}) \\$ \[$(tput sgr0)\]"
|
|
||||||
# export COMPILER="clang++"
|
|
||||||
#export COMPILER="g++"
|
|
||||||
echo "test";
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
# This is used when running `nix build`
|
|
||||||
# packages.default = pkgs.llvmPackages_14.stdenv.mkDerivation rec {
|
|
||||||
packages.default = pkgs.esphome.stdenv.mkDerivation rec {
|
|
||||||
name = "esphome-wordclock";
|
|
||||||
version = "2.0.0";
|
|
||||||
src = self;
|
|
||||||
|
|
||||||
# buildInputs = [ pkgs.esphome ];
|
|
||||||
|
|
||||||
# buildPhase = "COMPILER='clang++' make";
|
|
||||||
|
|
||||||
# installPhase = ''
|
|
||||||
# mkdir -p $out/bin;
|
|
||||||
# install -t $out/bin worm
|
|
||||||
# '';
|
|
||||||
|
|
||||||
# meta = with inputs.utils.lib; {
|
|
||||||
# homepage = "https://github.com/icecreammatt/ssu-cs315-worm";
|
|
||||||
# description = ''
|
|
||||||
# Terminal CLI Worm Game
|
|
||||||
# '';
|
|
||||||
# };
|
|
||||||
};
|
|
||||||
# packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;
|
|
||||||
|
|
||||||
# packages.x86_64-linux.default = self.packages.x86_64-linux.hello;
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
154
image_test.yaml
154
image_test.yaml
|
|
@ -1,154 +0,0 @@
|
||||||
esphome:
|
|
||||||
name: "display-thing"
|
|
||||||
|
|
||||||
esp8266:
|
|
||||||
board: d1_mini
|
|
||||||
framework:
|
|
||||||
version: recommended
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: local
|
|
||||||
path: components
|
|
||||||
components: [ wordclock, addressable_light ]
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
ap:
|
|
||||||
ssid: "${devicename}"
|
|
||||||
password: !secret ap_password
|
|
||||||
manual_ip:
|
|
||||||
static_ip: !secret manualip_static_ip
|
|
||||||
gateway: !secret manualip_gateway
|
|
||||||
subnet: !secret manualip_subnet
|
|
||||||
dns1: 1.1.1.1
|
|
||||||
dns2: 1.0.0.1
|
|
||||||
|
|
||||||
api:
|
|
||||||
|
|
||||||
# ota:
|
|
||||||
# password: "${devicename}"
|
|
||||||
|
|
||||||
logger:
|
|
||||||
# esp8266_store_log_strings_in_flash: false
|
|
||||||
web_server:
|
|
||||||
port: 80
|
|
||||||
|
|
||||||
light:
|
|
||||||
- name: NeoPixel Strip 1
|
|
||||||
id: neopixel_strip_1
|
|
||||||
platform: neopixelbus
|
|
||||||
type: GRB
|
|
||||||
variant: WS2812
|
|
||||||
pin: GPIO3
|
|
||||||
num_leds: 199
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
default_transition_length: 0s
|
|
||||||
color_correct: [100%, 100%, 100%]
|
|
||||||
method:
|
|
||||||
type: esp8266_dma
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: addressable_light
|
|
||||||
id: led_matrix_display
|
|
||||||
addressable_light_id: neopixel_strip_1
|
|
||||||
width: 18
|
|
||||||
height: 11
|
|
||||||
rotation: 270°
|
|
||||||
update_interval: 16ms
|
|
||||||
auto_clear_enabled: false
|
|
||||||
pixel_mapper: |-
|
|
||||||
int mapping_even[] = {0, 2, 4, 6,8,10,11,13,15,17, 16,16,16,16, 16,16,16,16};
|
|
||||||
int mapping_odd[] = {17,15,13,11,9, 7, 6, 4, 2, 0, 16,16,16,16, 16,16,16,16};
|
|
||||||
if (x > 9) return -1;
|
|
||||||
if (y % 2 == 0) {
|
|
||||||
return (y * 18 + mapping_even[x]); // mapping_even[y];
|
|
||||||
} else {
|
|
||||||
return (y * 18 + mapping_odd[x]); // mapping_odd[y];
|
|
||||||
}
|
|
||||||
# lambda: |-
|
|
||||||
# it.fill(Color(0));
|
|
||||||
# it.image(0, 0, id(foo));
|
|
||||||
# //it.line(0,0,5,5, Color(0x00FF00));
|
|
||||||
|
|
||||||
# image:
|
|
||||||
# - id: foo
|
|
||||||
# file: ./test.png
|
|
||||||
# type: RGB24
|
|
||||||
# resize: 18x11
|
|
||||||
|
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: sntp
|
|
||||||
id: current_time
|
|
||||||
timezone: !secret timezone
|
|
||||||
|
|
||||||
|
|
||||||
wordclock:
|
|
||||||
time_id: current_time
|
|
||||||
display_id: led_matrix_display
|
|
||||||
static_segments: ["IT", "IS"]
|
|
||||||
segments:
|
|
||||||
- {name: "IT", line: {x1: 0, x2: 1, y1: 0}}
|
|
||||||
- {name: "IS", line: {x1: 3, x2: 4, y1: 0}}
|
|
||||||
- {name: "AM", line: {x1: 7, x2: 8, y1: 0}}
|
|
||||||
- {name: "PM", line: {x1: 9, x2: 10, y1: 0}}
|
|
||||||
- {name: "QUARTER", line: {x1: 2, x2: 8, y1: 1}}
|
|
||||||
- {name: "TWENTY", line: {x1: 0, x2: 5, y1: 2}}
|
|
||||||
- {name: "FIVE_MINUTES", line: {x1: 6, x2: 9, y1: 2}}
|
|
||||||
- {name: "HALF", line: {x1: 0, x2: 3, y1: 3}}
|
|
||||||
- {name: "TEN_MINUTES", line: {x1: 5, x2: 7, y1: 3}}
|
|
||||||
- {name: "TO", line: {x1: 9, x2: 10, y1: 3}}
|
|
||||||
- {name: "PAST", line: {x1: 0, x2: 3, y1: 4}}
|
|
||||||
- {name: "NINE", line: {x1: 7, x2: 10, y1: 4}}
|
|
||||||
- {name: "ONE", line: {x1: 0, x2: 2, y1: 5}}
|
|
||||||
- {name: "SIX", line: {x1: 3, x2: 5, y1: 5}}
|
|
||||||
- {name: "THREE", line: {x1: 6, x2: 10, y1: 5}}
|
|
||||||
- {name: "FOUR", line: {x1: 0, x2: 3, y1: 6}}
|
|
||||||
- {name: "FIVE", line: {x1: 4, x2: 7, y1: 6}}
|
|
||||||
- {name: "TWO", line: {x1: 8, x2: 10, y1: 6}}
|
|
||||||
- {name: "EIGHT", line: {x1: 0, x2: 4, y1: 7}}
|
|
||||||
- {name: "ELEVEN", line: {x1: 5, x2: 10, y1: 7}}
|
|
||||||
- {name: "SEVEN", line: {x1: 0, x2: 4, y1: 8}}
|
|
||||||
- {name: "TWELVE", line: {x1: 5, x2: 10, y1: 8}}
|
|
||||||
- {name: "TEN", line: {x1: 0, x2: 2, y1: 9}}
|
|
||||||
- {name: "OCLOCK", line: {x1: 5, x2: 10, y1: 9}}
|
|
||||||
minutes:
|
|
||||||
- {minute: 0, segments: ["OCLOCK"]}
|
|
||||||
- {minute: 5, segments: ["FIVE_MINUTES", "PAST"]}
|
|
||||||
- {minute: 10, segments: ["TEN_MINUTES", "PAST"]}
|
|
||||||
- {minute: 15, segments: ["QUARTER", "PAST"]}
|
|
||||||
- {minute: 20, segments: ["TWENTY", "PAST"]}
|
|
||||||
- {minute: 25, segments: ["TWENTY", "FIVE_MINUTES", "PAST"]}
|
|
||||||
- {minute: 30, segments: ["HALF", "PAST"]}
|
|
||||||
- {minute: 35, segments: ["TWENTY", "FIVE_MINUTES", "TO"]}
|
|
||||||
- {minute: 40, segments: ["TWENTY", "TO"]}
|
|
||||||
- {minute: 45, segments: ["QUARTER", "TO"]}
|
|
||||||
- {minute: 50, segments: ["TEN_MINUTES", "TO"]}
|
|
||||||
- {minute: 55, segments: ["FIVE_MINUTES", "TO"]}
|
|
||||||
hours:
|
|
||||||
- {hour: 0, segments: ["TWELVE", "AM"]}
|
|
||||||
- {hour: 1, segments: ["ONE", "AM"]}
|
|
||||||
- {hour: 2, segments: ["TWO", "AM"]}
|
|
||||||
- {hour: 3, segments: ["THREE", "AM"]}
|
|
||||||
- {hour: 4, segments: ["FOUR", "AM"]}
|
|
||||||
- {hour: 5, segments: ["FIVE", "AM"]}
|
|
||||||
- {hour: 6, segments: ["SIX", "AM"]}
|
|
||||||
- {hour: 7, segments: ["SEVEN", "AM"]}
|
|
||||||
- {hour: 8, segments: ["EIGHT", "AM"]}
|
|
||||||
- {hour: 9, segments: ["NINE", "AM"]}
|
|
||||||
- {hour: 10, segments: ["TEN", "AM"]}
|
|
||||||
- {hour: 11, segments: ["ELEVEN", "AM"]}
|
|
||||||
- {hour: 12, segments: ["TWELVE", "PM"]}
|
|
||||||
- {hour: 13, segments: ["ONE", "PM"]}
|
|
||||||
- {hour: 14, segments: ["TWO", "PM"]}
|
|
||||||
- {hour: 15, segments: ["THREE", "PM"]}
|
|
||||||
- {hour: 16, segments: ["FOUR", "PM"]}
|
|
||||||
- {hour: 17, segments: ["FIVE", "PM"]}
|
|
||||||
- {hour: 18, segments: ["SIX", "PM"]}
|
|
||||||
- {hour: 19, segments: ["SEVEN", "PM"]}
|
|
||||||
- {hour: 20, segments: ["EIGHT", "PM"]}
|
|
||||||
- {hour: 21, segments: ["NINE", "PM"]}
|
|
||||||
- {hour: 22, segments: ["TEN", "PM"]}
|
|
||||||
- {hour: 23, segments: ["ELEVEN", "PM"]}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
wifi_password: secret
|
|
||||||
wifi_ssid: example
|
|
||||||
manualip_static_ip: 192.168.1.177
|
|
||||||
manualip_gateway: 192.168.1.1
|
|
||||||
manualip_subnet: 255.255.255.0
|
|
||||||
timezone: Europe/Zurich
|
|
||||||
ap_password: "password for internal fallback accesspoint"
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
esphome:
|
|
||||||
name: "wordclock"
|
|
||||||
|
|
||||||
esp8266:
|
|
||||||
board: d1_mini
|
|
||||||
framework:
|
|
||||||
version: recommended
|
|
||||||
# esp32:
|
|
||||||
# board: ttgo-t7-v13-mini32
|
|
||||||
# framework:
|
|
||||||
# type: arduino
|
|
||||||
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: local
|
|
||||||
path: components
|
|
||||||
components: [ wordclock ]
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
ap:
|
|
||||||
ssid: "WordClock Configuration"
|
|
||||||
password: !secret ap_password
|
|
||||||
manual_ip:
|
|
||||||
static_ip: !secret manualip_static_ip
|
|
||||||
gateway: !secret manualip_gateway
|
|
||||||
subnet: !secret manualip_subnet
|
|
||||||
dns1: 1.1.1.1
|
|
||||||
dns2: 1.0.0.1
|
|
||||||
|
|
||||||
captive_portal:
|
|
||||||
|
|
||||||
api:
|
|
||||||
|
|
||||||
ota:
|
|
||||||
password: "wordclock"
|
|
||||||
|
|
||||||
logger:
|
|
||||||
|
|
||||||
select:
|
|
||||||
- platform: template
|
|
||||||
name: "Template select"
|
|
||||||
restore_value: true
|
|
||||||
set_action:
|
|
||||||
- lambda: |-
|
|
||||||
return id(wordclock_1)->set_effect_active(false);
|
|
||||||
optimistic: true
|
|
||||||
options:
|
|
||||||
- effect
|
|
||||||
- color
|
|
||||||
initial_option: effect
|
|
||||||
|
|
||||||
web_server:
|
|
||||||
port: 80
|
|
||||||
version: 1
|
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: sntp
|
|
||||||
id: current_time
|
|
||||||
timezone: !secret timezone
|
|
||||||
|
|
||||||
light:
|
|
||||||
- name: NeoPixel Strip 1
|
|
||||||
id: neopixel_strip_1
|
|
||||||
platform: neopixelbus
|
|
||||||
type: GRB
|
|
||||||
variant: WS2812
|
|
||||||
pin: GPIO3
|
|
||||||
# pin: GPIO22
|
|
||||||
num_leds: 198
|
|
||||||
restore_mode: ALWAYS_OFF
|
|
||||||
method:
|
|
||||||
type: esp8266_dma
|
|
||||||
# type: esp32_i2s
|
|
||||||
|
|
||||||
effects:
|
|
||||||
- addressable_random_twinkle:
|
|
||||||
name: "random_twinkle"
|
|
||||||
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: addressable_light
|
|
||||||
id: led_matrix_display
|
|
||||||
addressable_light_id: neopixel_strip_1
|
|
||||||
width: 18
|
|
||||||
height: 11
|
|
||||||
rotation: 270°
|
|
||||||
update_interval: 16ms
|
|
||||||
auto_clear_enabled: false
|
|
||||||
pixel_mapper: |-
|
|
||||||
int mapping_even[] = {0, 2, 4, 6,8,10,11,13,15,17, 16,16,16,16, 16,16,16,16};
|
|
||||||
int mapping_odd[] = {17,15,13,11,9, 7, 6, 4, 2, 0, 16,16,16,16, 16,16,16,16};
|
|
||||||
if (x > 9) return -1;
|
|
||||||
if (y % 2 == 0) {
|
|
||||||
return (y * 18 + mapping_even[x]);
|
|
||||||
} else {
|
|
||||||
return (y * 18 + mapping_odd[x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
color:
|
|
||||||
- id: col_on
|
|
||||||
red: 90%
|
|
||||||
green: 50%
|
|
||||||
blue: 0%
|
|
||||||
- id: col_off
|
|
||||||
red: 0%
|
|
||||||
green: 0%
|
|
||||||
blue: 0%
|
|
||||||
|
|
||||||
wordclock:
|
|
||||||
id: wordclock_1
|
|
||||||
time_id: current_time
|
|
||||||
display_id: led_matrix_display
|
|
||||||
addressable_light_id: neopixel_strip_1
|
|
||||||
brightness: 100%
|
|
||||||
color_on: col_on
|
|
||||||
color_off: col_off
|
|
||||||
update_interval: 16ms
|
|
||||||
static_segments: ["IT", "IS"]
|
|
||||||
segments:
|
|
||||||
- {name: "IT", line: {x1: 0, x2: 1, y1: 0}}
|
|
||||||
- {name: "IS", line: {x1: 3, x2: 4, y1: 0}}
|
|
||||||
- {name: "AM", line: {x1: 7, x2: 8, y1: 0}}
|
|
||||||
- {name: "PM", line: {x1: 9, x2: 10, y1: 0}}
|
|
||||||
- {name: "QUARTER", line: {x1: 2, x2: 8, y1: 1}}
|
|
||||||
- {name: "TWENTY", line: {x1: 0, x2: 5, y1: 2}}
|
|
||||||
- {name: "FIVE_MINUTES", line: {x1: 6, x2: 9, y1: 2}}
|
|
||||||
- {name: "HALF", line: {x1: 0, x2: 3, y1: 3}}
|
|
||||||
- {name: "TEN_MINUTES", line: {x1: 5, x2: 7, y1: 3}}
|
|
||||||
- {name: "TO", line: {x1: 9, x2: 10, y1: 3}}
|
|
||||||
- {name: "PAST", line: {x1: 0, x2: 3, y1: 4}}
|
|
||||||
- {name: "NINE", line: {x1: 7, x2: 10, y1: 4}}
|
|
||||||
- {name: "ONE", line: {x1: 0, x2: 2, y1: 5}}
|
|
||||||
- {name: "SIX", line: {x1: 3, x2: 5, y1: 5}}
|
|
||||||
- {name: "THREE", line: {x1: 6, x2: 10, y1: 5}}
|
|
||||||
- {name: "FOUR", line: {x1: 0, x2: 3, y1: 6}}
|
|
||||||
- {name: "FIVE", line: {x1: 4, x2: 7, y1: 6}}
|
|
||||||
- {name: "TWO", line: {x1: 8, x2: 10, y1: 6}}
|
|
||||||
- {name: "EIGHT", line: {x1: 0, x2: 4, y1: 7}}
|
|
||||||
- {name: "ELEVEN", line: {x1: 5, x2: 10, y1: 7}}
|
|
||||||
- {name: "SEVEN", line: {x1: 0, x2: 4, y1: 8}}
|
|
||||||
- {name: "TWELVE", line: {x1: 5, x2: 10, y1: 8}}
|
|
||||||
- {name: "TEN", line: {x1: 0, x2: 2, y1: 9}}
|
|
||||||
- {name: "OCLOCK", line: {x1: 5, x2: 10, y1: 9}}
|
|
||||||
minutes:
|
|
||||||
- {minute: 0, hour_offset: 0, segments: ["OCLOCK"]}
|
|
||||||
- {minute: 5, hour_offset: 0, segments: ["FIVE_MINUTES", "PAST"]}
|
|
||||||
- {minute: 10, hour_offset: 0, segments: ["TEN_MINUTES", "PAST"]}
|
|
||||||
- {minute: 15, hour_offset: 0, segments: ["QUARTER", "PAST"]}
|
|
||||||
- {minute: 20, hour_offset: 0, segments: ["TWENTY", "PAST"]}
|
|
||||||
- {minute: 25, hour_offset: 0, segments: ["TWENTY", "FIVE_MINUTES", "PAST"]}
|
|
||||||
- {minute: 30, hour_offset: 0, segments: ["HALF", "PAST"]}
|
|
||||||
- {minute: 35, hour_offset: 1, segments: ["TWENTY", "FIVE_MINUTES", "TO"]}
|
|
||||||
- {minute: 40, hour_offset: 1, segments: ["TWENTY", "TO"]}
|
|
||||||
- {minute: 45, hour_offset: 1, segments: ["QUARTER", "TO"]}
|
|
||||||
- {minute: 50, hour_offset: 1, segments: ["TEN_MINUTES", "TO"]}
|
|
||||||
- {minute: 55, hour_offset: 1, segments: ["FIVE_MINUTES", "TO"]}
|
|
||||||
hours:
|
|
||||||
- {hour: 0, segments: "TWELVE"}
|
|
||||||
- {hour: 1, segments: "ONE"}
|
|
||||||
- {hour: 2, segments: "TWO"}
|
|
||||||
- {hour: 3, segments: "THREE"}
|
|
||||||
- {hour: 4, segments: "FOUR"}
|
|
||||||
- {hour: 5, segments: "FIVE"}
|
|
||||||
- {hour: 6, segments: "SIX"}
|
|
||||||
- {hour: 7, segments: "SEVEN"}
|
|
||||||
- {hour: 8, segments: "EIGHT"}
|
|
||||||
- {hour: 9, segments: "NINE"}
|
|
||||||
- {hour: 10, segments: "TEN"}
|
|
||||||
- {hour: 11, segments: "ELEVEN"}
|
|
||||||
- {hour: 12, segments: "TWELVE"}
|
|
||||||
- {hour: 13, segments: "ONE"}
|
|
||||||
- {hour: 14, segments: "TWO"}
|
|
||||||
- {hour: 15, segments: "THREE"}
|
|
||||||
- {hour: 16, segments: "FOUR"}
|
|
||||||
- {hour: 17, segments: "FIVE"}
|
|
||||||
- {hour: 18, segments: "SIX"}
|
|
||||||
- {hour: 19, segments: "SEVEN"}
|
|
||||||
- {hour: 20, segments: "EIGHT"}
|
|
||||||
- {hour: 21, segments: "NINE"}
|
|
||||||
- {hour: 22, segments: "TEN"}
|
|
||||||
- {hour: 23, segments: "ELEVEN"}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,327 +0,0 @@
|
||||||
esphome:
|
|
||||||
name: "wordclock"
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
# board: ttgo-t7-v13-mini32
|
|
||||||
board: wemos_d1_mini32
|
|
||||||
framework:
|
|
||||||
type: arduino
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: local
|
|
||||||
path: components
|
|
||||||
components: [ wordclock ]
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
ap:
|
|
||||||
ssid: "WordClock Configuration"
|
|
||||||
password: !secret ap_password
|
|
||||||
manual_ip:
|
|
||||||
static_ip: !secret manualip_static_ip
|
|
||||||
gateway: !secret manualip_gateway
|
|
||||||
subnet: !secret manualip_subnet
|
|
||||||
dns1: 1.1.1.1
|
|
||||||
dns2: 1.0.0.1
|
|
||||||
|
|
||||||
captive_portal:
|
|
||||||
|
|
||||||
# api:
|
|
||||||
|
|
||||||
ota:
|
|
||||||
password: "wordclock"
|
|
||||||
|
|
||||||
logger:
|
|
||||||
|
|
||||||
switch:
|
|
||||||
- platform: template
|
|
||||||
name: "5 Rainbow ON/OFF"
|
|
||||||
# lambda: |-
|
|
||||||
# return id(wordclock_1)->get_effect_active();
|
|
||||||
restore_state: true
|
|
||||||
optimistic: true
|
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
|
||||||
turn_on_action:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->set_effect_active(true);
|
|
||||||
turn_off_action:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->set_effect_active(false);
|
|
||||||
|
|
||||||
# on_...:
|
|
||||||
# - globals.set:
|
|
||||||
# id: my_global_var
|
|
||||||
# value: '10'
|
|
||||||
|
|
||||||
|
|
||||||
# output:
|
|
||||||
# - platform: template
|
|
||||||
# id: dummy_out
|
|
||||||
# type: float
|
|
||||||
# write_action:
|
|
||||||
# - lambda: |-
|
|
||||||
# return id(wordclock_1)->set_effect_active(true);
|
|
||||||
# Example configuration entry
|
|
||||||
globals:
|
|
||||||
- id: my_global_int
|
|
||||||
type: int
|
|
||||||
restore_value: no
|
|
||||||
initial_value: '0'
|
|
||||||
# Example for global string variable
|
|
||||||
- id: my_global_string
|
|
||||||
type: std::string
|
|
||||||
restore_value: no # Strings cannot be saved/restored
|
|
||||||
initial_value: '"Global value is"'
|
|
||||||
|
|
||||||
number:
|
|
||||||
- platform: template
|
|
||||||
name: "1 Red"
|
|
||||||
optimistic: true
|
|
||||||
restore_value: true
|
|
||||||
initial_value: 255
|
|
||||||
# lambda: |-
|
|
||||||
# return id(wordclock_1)->on_color.red;
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->on_color.red = x;
|
|
||||||
set_action:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->on_color.red = x;
|
|
||||||
min_value: 0
|
|
||||||
max_value: 255
|
|
||||||
step: 1
|
|
||||||
- platform: template
|
|
||||||
name: "2 Green"
|
|
||||||
optimistic: true
|
|
||||||
restore_value: true
|
|
||||||
initial_value: 255
|
|
||||||
# lambda: |-
|
|
||||||
# return id(wordclock_1)->on_color.green;
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->on_color.green = x;
|
|
||||||
set_action:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->on_color.green = x;
|
|
||||||
min_value: 0
|
|
||||||
max_value: 255
|
|
||||||
step: 1
|
|
||||||
- platform: template
|
|
||||||
name: "3 Blue"
|
|
||||||
optimistic: true
|
|
||||||
restore_value: true
|
|
||||||
initial_value: 255
|
|
||||||
# lambda: |-
|
|
||||||
# return id(wordclock_1)->on_color.blue;
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->on_color.blue = x;
|
|
||||||
set_action:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->on_color.blue = x;
|
|
||||||
min_value: 0
|
|
||||||
max_value: 255
|
|
||||||
step: 1
|
|
||||||
- platform: template
|
|
||||||
name: "4 Brightness"
|
|
||||||
optimistic: true
|
|
||||||
restore_value: true
|
|
||||||
initial_value: 255
|
|
||||||
# lambda: |-
|
|
||||||
# return id(wordclock_1)->brightness;
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->brightness = x;
|
|
||||||
set_action:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->brightness = x;
|
|
||||||
min_value: 0
|
|
||||||
max_value: 255
|
|
||||||
step: 1
|
|
||||||
- platform: template
|
|
||||||
name: "6 Rainbow Speed"
|
|
||||||
optimistic: true
|
|
||||||
restore_value: true
|
|
||||||
initial_value: 12
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->set_effect_speed(x);
|
|
||||||
set_action:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->set_effect_speed(x);
|
|
||||||
min_value: 1
|
|
||||||
max_value: 4096
|
|
||||||
step: 1
|
|
||||||
- platform: template
|
|
||||||
name: "7 Rainbow Width"
|
|
||||||
optimistic: true
|
|
||||||
restore_value: true
|
|
||||||
initial_value: 50
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->set_effect_width(x);
|
|
||||||
set_action:
|
|
||||||
- lambda: |-
|
|
||||||
id(wordclock_1)->set_effect_width(x);
|
|
||||||
min_value: 1
|
|
||||||
max_value: 65535
|
|
||||||
step: 1
|
|
||||||
|
|
||||||
# select:
|
|
||||||
# - platform: template
|
|
||||||
# name: "Rainbow ON/OFF"
|
|
||||||
# restore_value: true
|
|
||||||
# set_action:
|
|
||||||
# - lambda: |-
|
|
||||||
# return id(wordclock_1)->set_color_value(x);
|
|
||||||
# optimistic: true
|
|
||||||
# options:
|
|
||||||
# - effect
|
|
||||||
# - color
|
|
||||||
# initial_option: effect
|
|
||||||
# - platform: template
|
|
||||||
# name: "Template select"
|
|
||||||
# restore_value: true
|
|
||||||
# set_action:
|
|
||||||
# - lambda: |-
|
|
||||||
# return id(wordclock_1)->set_color_value(x);
|
|
||||||
# optimistic: true
|
|
||||||
# options:
|
|
||||||
# - effect
|
|
||||||
# - color
|
|
||||||
# initial_option: effect
|
|
||||||
|
|
||||||
|
|
||||||
web_server:
|
|
||||||
port: 80
|
|
||||||
version: 2
|
|
||||||
local: true
|
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: sntp
|
|
||||||
id: current_time
|
|
||||||
timezone: !secret timezone
|
|
||||||
|
|
||||||
|
|
||||||
light:
|
|
||||||
- name: NeoPixel Strip 1
|
|
||||||
id: neopixel_strip_1
|
|
||||||
platform: neopixelbus
|
|
||||||
type: GRB
|
|
||||||
variant: WS2812
|
|
||||||
pin: GPIO22
|
|
||||||
num_leds: 198
|
|
||||||
restore_mode: ALWAYS_OFF
|
|
||||||
internal: true
|
|
||||||
method:
|
|
||||||
type: esp32_i2s
|
|
||||||
effects:
|
|
||||||
- addressable_random_twinkle:
|
|
||||||
name: "random_twinkle"
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: addressable_light
|
|
||||||
id: led_matrix_display
|
|
||||||
addressable_light_id: neopixel_strip_1
|
|
||||||
width: 18
|
|
||||||
height: 11
|
|
||||||
rotation: 270°
|
|
||||||
update_interval: 16ms
|
|
||||||
auto_clear_enabled: false
|
|
||||||
pixel_mapper: |-
|
|
||||||
int mapping_even[] = {0, 2, 4, 6,8,10,11,13,15,17, 16,16,16,16, 16,16,16,16};
|
|
||||||
int mapping_odd[] = {17,15,13,11,9, 7, 6, 4, 2, 0, 16,16,16,16, 16,16,16,16};
|
|
||||||
if (x > 9) return -1;
|
|
||||||
if (y % 2 == 0) {
|
|
||||||
return (y * 18 + mapping_even[x]);
|
|
||||||
} else {
|
|
||||||
return (y * 18 + mapping_odd[x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
color:
|
|
||||||
- id: col_on
|
|
||||||
red: 90%
|
|
||||||
green: 50%
|
|
||||||
blue: 0%
|
|
||||||
- id: col_off
|
|
||||||
red: 0%
|
|
||||||
green: 0%
|
|
||||||
blue: 0%
|
|
||||||
|
|
||||||
wordclock:
|
|
||||||
id: wordclock_1
|
|
||||||
time_id: current_time
|
|
||||||
display_id: led_matrix_display
|
|
||||||
addressable_light_id: neopixel_strip_1
|
|
||||||
brightness: 100%
|
|
||||||
color_on: col_on
|
|
||||||
color_off: col_off
|
|
||||||
update_interval: 16ms
|
|
||||||
static_segments: ["IT", "IS"]
|
|
||||||
segments:
|
|
||||||
- {name: "IT", line: {x1: 0, x2: 1, y1: 0}}
|
|
||||||
- {name: "IS", line: {x1: 3, x2: 4, y1: 0}}
|
|
||||||
- {name: "AM", line: {x1: 7, x2: 8, y1: 0}}
|
|
||||||
- {name: "PM", line: {x1: 9, x2: 10, y1: 0}}
|
|
||||||
- {name: "QUARTER", line: {x1: 2, x2: 8, y1: 1}}
|
|
||||||
- {name: "TWENTY", line: {x1: 0, x2: 5, y1: 2}}
|
|
||||||
- {name: "FIVE_MINUTES", line: {x1: 6, x2: 9, y1: 2}}
|
|
||||||
- {name: "HALF", line: {x1: 0, x2: 3, y1: 3}}
|
|
||||||
- {name: "TEN_MINUTES", line: {x1: 5, x2: 7, y1: 3}}
|
|
||||||
- {name: "TO", line: {x1: 9, x2: 10, y1: 3}}
|
|
||||||
- {name: "PAST", line: {x1: 0, x2: 3, y1: 4}}
|
|
||||||
- {name: "NINE", line: {x1: 7, x2: 10, y1: 4}}
|
|
||||||
- {name: "ONE", line: {x1: 0, x2: 2, y1: 5}}
|
|
||||||
- {name: "SIX", line: {x1: 3, x2: 5, y1: 5}}
|
|
||||||
- {name: "THREE", line: {x1: 6, x2: 10, y1: 5}}
|
|
||||||
- {name: "FOUR", line: {x1: 0, x2: 3, y1: 6}}
|
|
||||||
- {name: "FIVE", line: {x1: 4, x2: 7, y1: 6}}
|
|
||||||
- {name: "TWO", line: {x1: 8, x2: 10, y1: 6}}
|
|
||||||
- {name: "EIGHT", line: {x1: 0, x2: 4, y1: 7}}
|
|
||||||
- {name: "ELEVEN", line: {x1: 5, x2: 10, y1: 7}}
|
|
||||||
- {name: "SEVEN", line: {x1: 0, x2: 4, y1: 8}}
|
|
||||||
- {name: "TWELVE", line: {x1: 5, x2: 10, y1: 8}}
|
|
||||||
- {name: "TEN", line: {x1: 0, x2: 2, y1: 9}}
|
|
||||||
- {name: "OCLOCK", line: {x1: 5, x2: 10, y1: 9}}
|
|
||||||
minutes:
|
|
||||||
- {minute: 0, hour_offset: 0, segments: ["OCLOCK"]}
|
|
||||||
- {minute: 5, hour_offset: 0, segments: ["FIVE_MINUTES", "PAST"]}
|
|
||||||
- {minute: 10, hour_offset: 0, segments: ["TEN_MINUTES", "PAST"]}
|
|
||||||
- {minute: 15, hour_offset: 0, segments: ["QUARTER", "PAST"]}
|
|
||||||
- {minute: 20, hour_offset: 0, segments: ["TWENTY", "PAST"]}
|
|
||||||
- {minute: 25, hour_offset: 0, segments: ["TWENTY", "FIVE_MINUTES", "PAST"]}
|
|
||||||
- {minute: 30, hour_offset: 0, segments: ["HALF", "PAST"]}
|
|
||||||
- {minute: 35, hour_offset: 1, segments: ["TWENTY", "FIVE_MINUTES", "TO"]}
|
|
||||||
- {minute: 40, hour_offset: 1, segments: ["TWENTY", "TO"]}
|
|
||||||
- {minute: 45, hour_offset: 1, segments: ["QUARTER", "TO"]}
|
|
||||||
- {minute: 50, hour_offset: 1, segments: ["TEN_MINUTES", "TO"]}
|
|
||||||
- {minute: 55, hour_offset: 1, segments: ["FIVE_MINUTES", "TO"]}
|
|
||||||
hours:
|
|
||||||
- {hour: 0, segments: "TWELVE"}
|
|
||||||
- {hour: 1, segments: "ONE"}
|
|
||||||
- {hour: 2, segments: "TWO"}
|
|
||||||
- {hour: 3, segments: "THREE"}
|
|
||||||
- {hour: 4, segments: "FOUR"}
|
|
||||||
- {hour: 5, segments: "FIVE"}
|
|
||||||
- {hour: 6, segments: "SIX"}
|
|
||||||
- {hour: 7, segments: "SEVEN"}
|
|
||||||
- {hour: 8, segments: "EIGHT"}
|
|
||||||
- {hour: 9, segments: "NINE"}
|
|
||||||
- {hour: 10, segments: "TEN"}
|
|
||||||
- {hour: 11, segments: "ELEVEN"}
|
|
||||||
- {hour: 12, segments: "TWELVE"}
|
|
||||||
- {hour: 13, segments: "ONE"}
|
|
||||||
- {hour: 14, segments: "TWO"}
|
|
||||||
- {hour: 15, segments: "THREE"}
|
|
||||||
- {hour: 16, segments: "FOUR"}
|
|
||||||
- {hour: 17, segments: "FIVE"}
|
|
||||||
- {hour: 18, segments: "SIX"}
|
|
||||||
- {hour: 19, segments: "SEVEN"}
|
|
||||||
- {hour: 20, segments: "EIGHT"}
|
|
||||||
- {hour: 21, segments: "NINE"}
|
|
||||||
- {hour: 22, segments: "TEN"}
|
|
||||||
- {hour: 23, segments: "ELEVEN"}
|
|
||||||
|
|
||||||
|
|
||||||
BIN
test.png
BIN
test.png
Binary file not shown.
|
Before Width: | Height: | Size: 780 B |
228
wordclock.h
228
wordclock.h
|
|
@ -1,228 +0,0 @@
|
||||||
#include "esphome.h"
|
|
||||||
#include <FastLED.h>
|
|
||||||
|
|
||||||
// By now only loosely based on https://github.com/leinich/ha-wordclock-esphome
|
|
||||||
|
|
||||||
// esphome dependencies:
|
|
||||||
// needs: esphome time --> id: current_time
|
|
||||||
// needs: esphome fastled --> id: fastledlight
|
|
||||||
|
|
||||||
///// Random Stuff /////
|
|
||||||
|
|
||||||
#ifndef WORDCLOCK_NUM_LEDS
|
|
||||||
#define WORDCLOCK_NUM_LEDS 198
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WORDCLOCK_DATA_PIN
|
|
||||||
#define WORDCLOCK_DATA_PIN 22
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* NOTE:
|
|
||||||
This section is about mapping LED indices to
|
|
||||||
positions in the grid.
|
|
||||||
|
|
||||||
On EVEN rows, the LED strip is running along the
|
|
||||||
matrix indices, but the physical positions of the
|
|
||||||
LEDs on the strip did not align with the letter
|
|
||||||
cutouts.
|
|
||||||
|
|
||||||
On ODD rows, the string is running backwards, because
|
|
||||||
it is snaking its way back and forth. */
|
|
||||||
int mapping_even[] = {0, 2, 4, 6,8,10,11,13,15,17};
|
|
||||||
int mapping_odd[] = {17,15,13,11,9, 7, 6, 4, 2, 0};
|
|
||||||
int map_coords_to_strip(int row, int column) {
|
|
||||||
if (column % 2) {
|
|
||||||
return (10 - column) * 18 + mapping_odd[row];
|
|
||||||
} else {
|
|
||||||
return (10 - column) * 18 + mapping_even [row];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///// Word Table /////
|
|
||||||
int WORD_IT_IS[5][2] = {{4,0}, {0,0}, {0,1}, {0,3}, {0,4}};
|
|
||||||
|
|
||||||
int WORD_QUARTER[8][2] = {{7,0}, {1,2}, {1,3}, {1,4}, {1,5}, {1,6}, {1,7}, {1,8}};
|
|
||||||
int WORD_TWENTY[7][2] = {{6,0}, {2,0}, {2,1}, {2,2}, {2,3}, {2,4}, {2,5}};
|
|
||||||
int WORD_FIVE_MINUTES[5][2] = {{4,0}, {2,6}, {2,7}, {2,8}, {2,9}};
|
|
||||||
int WORD_HALF[5][2] = {{4,0}, {3,0}, {3,1}, {3,2}, {3,3}};
|
|
||||||
int WORD_TEN_MINUTES[4][2] = {{3,0}, {3,5}, {3,6}, {3,7}};
|
|
||||||
|
|
||||||
int WORD_TO[3][2] = {{2,0}, {3,9}, {3,10}};
|
|
||||||
int WORD_PAST[5][2] = {{4,0}, {4,0}, {4,1}, {4,2}, {4,3}};
|
|
||||||
|
|
||||||
int WORD_NINE[5][2] = {{4,0}, {4,7}, {4,8}, {4,9}, {4,10}};
|
|
||||||
int WORD_ONE[4][2] = {{3,0}, {5,0}, {5,1}, {5,2}};
|
|
||||||
int WORD_SIX[4][2] = {{3,0}, {5,3}, {5,4}, {5,5}};
|
|
||||||
int WORD_THREE[6][2] = {{5,0}, {5,6}, {5,7}, {5,8}, {5,9}, {5,10}};
|
|
||||||
int WORD_FOUR[5][2] = {{4,0}, {6,0}, {6,1}, {6,2}, {6,3}};
|
|
||||||
int WORD_FIVE[5][2] = {{4,0}, {6,4}, {6,5}, {6,6}, {6,7}};
|
|
||||||
int WORD_TWO[4][2] = {{3,0}, {6,8}, {6,9}, {6,10}};
|
|
||||||
int WORD_EIGHT[6][2] = {{5,0}, {7,0}, {7,1}, {7,2}, {7,3}, {7,4}};
|
|
||||||
int WORD_ELEVEN[7][2] = {{6,0}, {7,5}, {7,6}, {7,7}, {7,8}, {7,9}, {7,10}};
|
|
||||||
int WORD_SEVEN[6][2] = {{5,0}, {8,0}, {8,1}, {8,2}, {8,3}, {8,4}};
|
|
||||||
int WORD_TWELFE[7][2] = {{6,0}, {8,5}, {8,6}, {8,7}, {8,8}, {8,9}, {8,10}};
|
|
||||||
int WORD_TEN[4][2] = {{3,0}, {9,0}, {9,1}, {9,2}};
|
|
||||||
|
|
||||||
int WORD_OCLOCK[7][2] = {{6,0}, {9,5}, {9,6}, {9,7}, {9,8}, {9,9}, {9,10}};
|
|
||||||
|
|
||||||
// TODO: It's probably unnecessary to have the "API" in CustomAPIDevice, since this
|
|
||||||
// component doesn't actually register any services anymore.
|
|
||||||
class Wordclock : public Component, public CustomAPIDevice {
|
|
||||||
public:
|
|
||||||
CRGB leds[WORDCLOCK_NUM_LEDS];
|
|
||||||
int hour = -1;
|
|
||||||
int minute = -1;
|
|
||||||
int second = -1;
|
|
||||||
int red = 124;
|
|
||||||
int green = 124;
|
|
||||||
int blue = 124;
|
|
||||||
int brightness = 50;
|
|
||||||
|
|
||||||
void setup() override {
|
|
||||||
FastLED.addLeds<NEOPIXEL, WORDCLOCK_DATA_PIN>(leds, WORDCLOCK_NUM_LEDS);
|
|
||||||
FastLED.setBrightness(brightness);
|
|
||||||
clear_all_leds();
|
|
||||||
FastLED.show();
|
|
||||||
// TODO: Set up some kind of initialization sequence. But it should be based on an effect or similarly supporting the
|
|
||||||
// cooperative multithreading. delay() calls are uncalled for.
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_all_leds() {
|
|
||||||
for(int i = 0; i < WORDCLOCK_NUM_LEDS; i++) {
|
|
||||||
leds[i].setRGB(0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_word(const int word[][2], const CRGB& c) {
|
|
||||||
for (int i=1; i < word[0][0] + 1; i++) {
|
|
||||||
leds[map_coords_to_strip(word[i][0], word[i][1])].setRGB(c.r, c.g, c.b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_minutes(int minutes, const CRGB& color) {
|
|
||||||
int five_minute_chunk = minutes / 5;
|
|
||||||
|
|
||||||
switch (five_minute_chunk)
|
|
||||||
{
|
|
||||||
case 0: // sharp
|
|
||||||
display_word(WORD_OCLOCK, color); ESP_LOGD("minute", "oclock "); break;
|
|
||||||
case 1: // five past
|
|
||||||
display_word(WORD_FIVE_MINUTES, color); ESP_LOGD("minute", "five past "); break;
|
|
||||||
case 2: // ten past
|
|
||||||
display_word(WORD_TEN_MINUTES, color); ESP_LOGD("minute", "ten past "); break;
|
|
||||||
case 3: // quarter past
|
|
||||||
display_word(WORD_QUARTER, color); ESP_LOGD("minute", "quarter past "); break;
|
|
||||||
case 4: // twenty past
|
|
||||||
display_word(WORD_TWENTY, color); ESP_LOGD("minute", "twenty past "); break;
|
|
||||||
case 5: // twenty five past
|
|
||||||
display_word(WORD_TWENTY, color); display_word(WORD_FIVE_MINUTES, color);
|
|
||||||
ESP_LOGD("minute", "twenty five past "); break;
|
|
||||||
case 6: // half past
|
|
||||||
display_word(WORD_HALF, color); ESP_LOGD("minute", "half past "); break;
|
|
||||||
case 7: // twenty five to
|
|
||||||
display_word(WORD_TWENTY, color); display_word(WORD_FIVE_MINUTES, color);
|
|
||||||
ESP_LOGD("minute", "twenty five to "); break;
|
|
||||||
case 8: // twenty to
|
|
||||||
display_word(WORD_TWENTY, color); ESP_LOGD("minute", "twenty to "); break;
|
|
||||||
case 9: // quarter to
|
|
||||||
display_word(WORD_QUARTER, color); ESP_LOGD("minute", "quarter to "); break;
|
|
||||||
case 10: // ten to
|
|
||||||
display_word(WORD_TEN_MINUTES, color); ESP_LOGD("minute", "ten to "); break;
|
|
||||||
case 11: // five to
|
|
||||||
display_word(WORD_FIVE_MINUTES, color); ESP_LOGD("minute", "five to "); break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (five_minute_chunk > 6) {
|
|
||||||
display_word(WORD_TO, color);
|
|
||||||
} else if (five_minute_chunk > 0) {
|
|
||||||
display_word(WORD_PAST, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_hour(int hour, int minutes, const CRGB& color) {
|
|
||||||
int five_minute_chunk = minutes / 5;
|
|
||||||
if (five_minute_chunk > 6) {
|
|
||||||
hour += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (hour % 12)
|
|
||||||
{
|
|
||||||
case 0: // twelve
|
|
||||||
display_word(WORD_TWELFE, color); ESP_LOGD("hour", "twelve "); break;
|
|
||||||
case 1: // one
|
|
||||||
display_word(WORD_ONE, color); ESP_LOGD("hour", "one "); break;
|
|
||||||
case 2: // two
|
|
||||||
display_word(WORD_TWO, color); ESP_LOGD("hour", "two "); break;
|
|
||||||
case 3: // three
|
|
||||||
display_word(WORD_THREE, color); ESP_LOGD("hour", "three "); break;
|
|
||||||
case 4: // four
|
|
||||||
display_word(WORD_FOUR, color); ESP_LOGD("hour", "four "); break;
|
|
||||||
case 5: // five
|
|
||||||
display_word(WORD_FIVE, color); ESP_LOGD("hour", "five "); break;
|
|
||||||
case 6: // six
|
|
||||||
display_word(WORD_SIX, color); ESP_LOGD("hour", "six "); break;
|
|
||||||
case 7: // seven
|
|
||||||
display_word(WORD_SEVEN, color); ESP_LOGD("hour", "seven "); break;
|
|
||||||
case 8: // eight
|
|
||||||
display_word(WORD_EIGHT, color); ESP_LOGD("hour", "eight "); break;
|
|
||||||
case 9: // nine
|
|
||||||
display_word(WORD_NINE, color); ESP_LOGD("hour", "nine "); break;
|
|
||||||
case 10: // ten
|
|
||||||
display_word(WORD_TEN, color); ESP_LOGD("hour", "ten "); break;
|
|
||||||
case 11: // eleven
|
|
||||||
display_word(WORD_ELEVEN, color); ESP_LOGD("hour", "eleven "); break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_time(int hour, int minutes, const CRGB& color) {
|
|
||||||
display_word(WORD_IT_IS, color);
|
|
||||||
display_hour(hour, minutes, color);
|
|
||||||
display_minutes(minutes, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() override {
|
|
||||||
auto time = id(current_time).now();
|
|
||||||
// https://www.esphome.io/api/classesphome_1_1light_1_1_light_color_values.html LightColorValues Class
|
|
||||||
auto fastledlight2 = id(fastledlight).current_values;
|
|
||||||
//convert float 0.0 till 1.0 into int 0 till 255
|
|
||||||
red = (int) (fastledlight2.get_red() * 125);
|
|
||||||
green = (int) (fastledlight2.get_green() * 125);
|
|
||||||
blue = (int) (fastledlight2.get_blue() * 125);
|
|
||||||
brightness = 0;
|
|
||||||
//check if light is on and set brightness
|
|
||||||
if (fastledlight2.get_state() > 0 ) {
|
|
||||||
brightness = (int) (fastledlight2.get_brightness()*125);
|
|
||||||
} else {
|
|
||||||
// ESP_LOGD("loop", "fastledlight state off - b: %i rgb %i %i %i", brightness, red, green, blue); delay(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
FastLED.setBrightness(brightness);
|
|
||||||
FastLED.show();
|
|
||||||
//check if valid time. Blink red,green,blue until valid time is present
|
|
||||||
if (time.is_valid() == false) {
|
|
||||||
ESP_LOGD("loop", "time is not valid");
|
|
||||||
// do something to show that the clock isn't dead. Maybe I can instantiate and effect and use that for this.
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (time.second != second) {
|
|
||||||
second = time.second;
|
|
||||||
ESP_LOGD("loop", "time is now [%02i:%02i:%02i]", time.hour, time.minute, time.second);
|
|
||||||
}
|
|
||||||
if(time.hour != hour || time.minute != minute) {
|
|
||||||
hour = time.hour;
|
|
||||||
minute = time.minute;
|
|
||||||
if (hour >= 0 && time.is_valid() == true){
|
|
||||||
|
|
||||||
clear_all_leds();
|
|
||||||
display_time(time.hour, time.minute, CRGB(red, green, blue));
|
|
||||||
FastLED.show();
|
|
||||||
|
|
||||||
ESP_LOGE("loop", "Update Time: %i:%i Brightness: %i RGB: %i-%i-%i", time.hour, time.minute, brightness, red, green, blue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
esphome:
|
|
||||||
name: "${devicename}"
|
|
||||||
platformio_options:
|
|
||||||
build_flags:
|
|
||||||
- -DWORDCLOCK_DATA_PIN=22
|
|
||||||
- -DWORDCLOCK_NUM_LEDS=198
|
|
||||||
includes:
|
|
||||||
- wordclock.h
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: local
|
|
||||||
path: components
|
|
||||||
components: [ wordcl ]
|
|
||||||
|
|
||||||
esp8266:
|
|
||||||
board: d1_mini
|
|
||||||
framework:
|
|
||||||
version: recommended
|
|
||||||
|
|
||||||
# esp32:
|
|
||||||
# board: ttgo-t7-v13-mini32
|
|
||||||
# framework:
|
|
||||||
# type: arduino
|
|
||||||
|
|
||||||
substitutions:
|
|
||||||
devicename: wordclock
|
|
||||||
friendly_name: "Wordclock"
|
|
||||||
light_friendly_name: "Wordclock Light"
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
ap:
|
|
||||||
ssid: "${devicename}"
|
|
||||||
password: !secret ap_password
|
|
||||||
manual_ip:
|
|
||||||
static_ip: !secret manualip_static_ip
|
|
||||||
gateway: !secret manualip_gateway
|
|
||||||
subnet: !secret manualip_subnet
|
|
||||||
dns1: 1.1.1.1
|
|
||||||
dns2: 1.0.0.1
|
|
||||||
|
|
||||||
api:
|
|
||||||
|
|
||||||
ota:
|
|
||||||
password: "${devicename}"
|
|
||||||
|
|
||||||
logger:
|
|
||||||
# esp8266_store_log_strings_in_flash: false
|
|
||||||
web_server:
|
|
||||||
port: 80
|
|
||||||
|
|
||||||
light:
|
|
||||||
- platform: fastled_clockless
|
|
||||||
id: fastledlight
|
|
||||||
chipset: WS2812
|
|
||||||
pin: 23
|
|
||||||
num_leds: 198
|
|
||||||
rgb_order: GRB
|
|
||||||
name: ${light_friendly_name}
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: sntp
|
|
||||||
id: current_time
|
|
||||||
timezone: !secret timezone
|
|
||||||
|
|
||||||
custom_component:
|
|
||||||
- lambda: |-
|
|
||||||
auto wordclock = new Wordclock();
|
|
||||||
return {wordclock};
|
|
||||||
components:
|
|
||||||
- id: wordclock
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
esphome:
|
|
||||||
name: "wordclock"
|
|
||||||
|
|
||||||
esp8266:
|
|
||||||
board: d1_mini
|
|
||||||
framework:
|
|
||||||
version: recommended
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: local
|
|
||||||
path: components
|
|
||||||
components: [ wordcl ]
|
|
||||||
|
|
||||||
# substitutions:
|
|
||||||
# devicename: wordclock
|
|
||||||
# friendly_name: "Wordclock"
|
|
||||||
# light_friendly_name: "Wordclock Light"
|
|
||||||
|
|
||||||
# wifi:
|
|
||||||
# ssid: !secret wifi_ssid
|
|
||||||
# password: !secret wifi_password
|
|
||||||
# ap:
|
|
||||||
# ssid: "${devicename}"
|
|
||||||
# password: !secret ap_password
|
|
||||||
# manual_ip:
|
|
||||||
# static_ip: !secret manualip_static_ip
|
|
||||||
# gateway: !secret manualip_gateway
|
|
||||||
# subnet: !secret manualip_subnet
|
|
||||||
# dns1: 1.1.1.1
|
|
||||||
# dns2: 1.0.0.1
|
|
||||||
|
|
||||||
# api:
|
|
||||||
|
|
||||||
# ota:
|
|
||||||
# password: "${devicename}"
|
|
||||||
|
|
||||||
# logger:
|
|
||||||
# # esp8266_store_log_strings_in_flash: false
|
|
||||||
# web_server:
|
|
||||||
# port: 80
|
|
||||||
|
|
||||||
wordclock:
|
|
||||||
|
|
||||||
light:
|
|
||||||
- name: neopixel
|
|
||||||
id: neopixel_1
|
|
||||||
platform: neopixelbus
|
|
||||||
type: GRB
|
|
||||||
variant: WS2812
|
|
||||||
pin: GPIO3
|
|
||||||
num_leds: 30
|
|
||||||
default_transition_length: 0.2s
|
|
||||||
# restore_mode: RESTORE_DEFAULT_ON
|
|
||||||
method:
|
|
||||||
type: esp8266_dma
|
|
||||||
on_turn_on:
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: neopixel_1
|
|
||||||
brightness: 35%
|
|
||||||
effect: rainbow
|
|
||||||
effects:
|
|
||||||
# - random:
|
|
||||||
# - pulse:
|
|
||||||
# - strobe:
|
|
||||||
# - flicker:
|
|
||||||
- addressable_rainbow:
|
|
||||||
name: rainbow
|
|
||||||
# - addressable_color_wipe:
|
|
||||||
# - addressable_scan:
|
|
||||||
# - addressable_twinkle:
|
|
||||||
# - addressable_random_twinkle:
|
|
||||||
# - addressable_fireworks:
|
|
||||||
# - addressable_flicker:
|
|
||||||
# - wled:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# time:
|
|
||||||
# - platform: sntp
|
|
||||||
# id: current_time
|
|
||||||
# timezone: !secret timezone
|
|
||||||
|
|
||||||
# - light.turn_on:
|
|
||||||
# id: light_1
|
|
||||||
# brightness: 100%
|
|
||||||
# effect: addressable_rainbow
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
esphome:
|
|
||||||
name: wordclock-livingroom
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
board: ttgo-t7-v13-mini32
|
|
||||||
framework:
|
|
||||||
type: arduino
|
|
||||||
|
|
||||||
# Enable logging
|
|
||||||
logger:
|
|
||||||
|
|
||||||
# Enable Home Assistant API
|
|
||||||
api:
|
|
||||||
password: ""
|
|
||||||
|
|
||||||
ota:
|
|
||||||
password: ""
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
ssid: "wifithing"
|
|
||||||
password: "lostandfound1"
|
|
||||||
|
|
||||||
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
|
||||||
ap:
|
|
||||||
ssid: "Wordclock-Livingroom"
|
|
||||||
password: "wGfGBPnJcbzE"
|
|
||||||
|
|
||||||
captive_portal:
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue