initial commit

This commit is contained in:
2022-12-20 21:26:47 +01:00
commit 2962a6db69
722 changed files with 63886 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
# 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/
/secrets.yaml
*.db
*.db-shm
*.db-wal
# Example .gitignore file for your config dir.
# An * ensures that everything will be ignored.
#*
# You can whitelist files/folders with !, these will not be ignored.
!*.yaml
!.gitignore
!*.md
# Ignore folders.
.storage
.cloud
.google.token
# Ensure these YAML files are ignored, otherwise your secret data/credentials will leak.
ip_bans.yaml
secrets.yaml
known_devices.yaml
+145
View File
@@ -0,0 +1,145 @@
#include <string>
#include <iostream>
#define ICON_stop "\U000F04DB"
#define ICON_play "\U000F040A"
#define ICON_pause "\U000F03E4"
std::string playbackStatusToIcon(bool playing, bool paused) {
if (playing) return ICON_play;
else if (paused) return ICON_pause;
else return ICON_stop;
}
#define ICON_moon_first_quarter "\U000F0F61"
#define ICON_moon_full "\U000F0F62"
#define ICON_moon_last_quarter "\U000F0F63"
#define ICON_moon_new "\U000F0F64"
#define ICON_moon_waning_crescent "\U000F0F65"
#define ICON_moon_waning_gibbous "\U000F0F66"
#define ICON_moon_waxing_crescent "\U000F0F67"
#define ICON_moon_waxing_gibbous "\U000F0F68"
std::string moonToIcon(std::string moonPhase)
{
if (moonPhase == "new_moon") return ICON_moon_new;
if (moonPhase == "waxing_crescent") return ICON_moon_waxing_crescent;
if (moonPhase == "first_quarter") return ICON_moon_first_quarter;
if (moonPhase == "waxing_gibbous") return ICON_moon_waxing_gibbous;
if (moonPhase == "full_moon") return ICON_moon_full;
if (moonPhase == "waning_gibbous") return ICON_moon_waning_gibbous;
if (moonPhase == "last_quarter") return ICON_moon_last_quarter;
if (moonPhase == "waning_crescent") return ICON_moon_waning_crescent;
return "";
}
// Map weather states to MDI characters.
std::map<std::string, std::string> weather_icon_map
{
{"cloudy", "\U000F0590"},
{"cloudy-alert", "\U000F0F2F"},
{"cloudy-arrow-right", "\U000F0E6E"},
{"fog", "\U000F0591"},
{"hail", "\U000F0592"},
{"hazy", "\U000F0F30"},
{"hurricane", "\U000F0898"},
{"lightning", "\U000F0593"},
{"lightning-rainy", "\U000F067E"},
{"night", "\U000F0594"},
{"night-partly-cloudy", "\U000F0F31"},
{"partlycloudy", "\U000F0595"},
{"partly-lightning", "\U000F0F32"},
{"partly-rainy", "\U000F0F33"},
{"partly-snowy", "\U000F0F34"},
{"partly-snowy-rainy", "\U000F0F35"},
{"pouring", "\U000F0596"},
{"rainy", "\U000F0597"},
{"snowy", "\U000F0598"},
{"snowy-heavy", "\U000F0F36"},
{"snowy-rainy", "\U000F067F"},
{"sunny", "\U000F0599"},
{"sunny-alert", "\U000F0F37"},
{"sunny-off", "\U000F14E4"},
{"sunset", "\U000F059A"},
{"sunset-down", "\U000F059B"},
{"sunset-up", "\U000F059C"},
{"tornado", "\U000F0F38"},
{"windy", "\U000F059D"},
{"windy-variant", "\U000F059E"},
{"car", "\U000f010b"},
{"trash", "\U000F0819"},
};
std::map<std::string, std::string> moon_icon_map
{
{ "mdi:moon-waxing-crescent", "\U000f0f67" },
{ "mdi:moon-first-quarter", "\U000F0F61" },
{ "mdi:moon-waxing-gibbous", "\U000F0F68" },
{ "mdi:moon-full", "\U000F0F62" },
{ "mdi:moon-waning-gibbous", "\U000F0F66" },
{ "mdi:moon-last-quarter", "\U000F0F63" },
{ "mdi:moon-waning-crescent", "\U000F0F65" },
};
#define ICON_w_clear_night "\U000F0594"
#define ICON_w_cloudy "\U000F0590"
#define ICON_w_fog "\U000F0591"
#define ICON_w_hail "\U000F0592"
#define ICON_w_lightning "\U000F0593"
#define ICON_w_lightning_rainy "\U000F067E"
#define ICON_w_night_partly_cloudy "\U000F0F31"
#define ICON_w_partly_cloudy "\U000F0595"
#define ICON_w_pouring "\U000F0596"
#define ICON_w_rainy "\U000F0597"
#define ICON_w_snowy "\U000F0F36"
#define ICON_w_snowy_rainy "\U000F067F"
#define ICON_w_sunny "\U000F0599"
#define ICON_w_windy "\U000F059D"
#define ICON_w_windy_variant "\U000F059E"
#define ICON_w_exceptional "\U000F0F38"
std::string conditionToIcon(std::string condition, bool daytime)
{
if (condition == "clear") return ICON_w_clear_night;
if (condition == "clear-night") return ICON_w_clear_night;
if (condition == "cloudy") return ICON_w_cloudy;
if (condition == "dust") return ICON_w_fog;
if (condition == "dusty") return ICON_w_fog;
if (condition == "fog") return ICON_w_fog;
if (condition == "frost") return ICON_w_snowy;
if (condition == "hail") return ICON_w_hail;
if (condition == "haze") return ICON_w_fog;
if (condition == "hazy") return ICON_w_fog;
if (condition == "heavy_shower") return ICON_w_rainy;
if (condition == "heavy_showers") return ICON_w_rainy;
if (condition == "light_rain") return ICON_w_rainy;
if (condition == "light_showers") return ICON_w_rainy;
if (condition == "light_shower") return ICON_w_rainy;
if (condition == "lightning") return ICON_w_lightning;
if (condition == "lightning-rainy") return ICON_w_lightning_rainy;
if (condition == "mostly_sunny") return ICON_w_sunny;
if (condition == "night") return ICON_w_clear_night;
if (condition == "partlycloudy" && !daytime) return ICON_w_night_partly_cloudy;
if (condition == "partlycloudy" && daytime) return ICON_w_partly_cloudy;
if (condition == "partly_cloudy" && !daytime) return ICON_w_night_partly_cloudy;
if (condition == "partly_cloudy" && daytime) return ICON_w_partly_cloudy;
if (condition == "pouring") return ICON_w_pouring;
if (condition == "rain") return ICON_w_rainy;
if (condition == "rainy") return ICON_w_rainy;
if (condition == "shower") return ICON_w_rainy;
if (condition == "showers") return ICON_w_rainy;
if (condition == "snow") return ICON_w_snowy;
if (condition == "snowy") return ICON_w_snowy;
if (condition == "snowy-rainy") return ICON_w_snowy_rainy;
if (condition == "storm") return ICON_w_lightning_rainy;
if (condition == "storms") return ICON_w_lightning_rainy;
if (condition == "sunny") return ICON_w_sunny;
if (condition == "wind") return ICON_w_windy;
if (condition == "windy") return ICON_w_windy;
if (condition == "windy-variant") return ICON_w_windy_variant;
if (condition == "exceptional") return ICON_w_exceptional;
return "";
}
+500
View File
@@ -0,0 +1,500 @@
#include "esphome.h"
namespace esphome
{
EHMTX::EHMTX() : PollingComponent(TICKINTERVAL)
{
this->store = new EHMTX_store(this);
this->icon_screen = new EHMTX_screen(this);
this->show_screen = false;
this->show_gauge = false;
this->text_color = Color(240, 240, 240);
this->today_color = Color(240, 240, 240);
this->weekday_color = Color(100, 100, 100);
this->clock_color = Color(240, 240, 240);
this->alarm_color = Color(200, 50, 50);
this->gauge_color = Color(100, 100, 200);
this->gauge_value = 0;
this->icon_count = 0;
this->last_clock_time = 0;
#ifdef USE_EHMTX_SELECT
this->select = NULL;
#endif
}
void EHMTX::force_screen(std::string name)
{
uint8_t icon_id = this->find_icon(name);
if (icon_id < MAXICONS)
{
this->store->force_next_screen(icon_id);
ESP_LOGD(TAG, "force next screen: %s", name.c_str());
}
}
void EHMTX::set_time_format(std::string s)
{
this->time_fmt = s;
}
void EHMTX::set_date_format(std::string s)
{
this->date_fmt = s;
}
void EHMTX::set_indicator_color(int r, int g, int b)
{
this->indicator_color = Color((uint8_t)r & 248, (uint8_t)g & 252, (uint8_t)b & 248);
ESP_LOGD(TAG, "indicator r: %d g: %d b: %d", r, g, b);
}
void EHMTX::set_today_color(int r, int g, int b)
{
this->today_color = Color((uint8_t)r & 248, (uint8_t)g & 252, (uint8_t)b & 248);
ESP_LOGD("EHMTX", "today color r: %d g: %d b: %d", r, g, b);
}
void EHMTX::set_weekday_color(int r, int g, int b)
{
this->weekday_color = Color((uint8_t)r & 248, (uint8_t)g & 252, (uint8_t)b & 248);
ESP_LOGD("EHMTX", "weekday color: %d g: %d b: %d", r, g, b);
}
void EHMTX::set_clock_color(int r, int g, int b)
{
this->clock_color = Color((uint8_t)r & 248, (uint8_t)g & 252, (uint8_t)b & 248);
ESP_LOGD("EHMTX", "clock color r: %d g: %d b: %d", r, g, b);
}
void EHMTX::set_gauge_color(int r, int g, int b)
{
this->gauge_color = Color((uint8_t)r & 248, (uint8_t)g & 252, (uint8_t)b & 248);
ESP_LOGD(TAG, "gauge color r: %d g: %d b: %d", r, g, b);
}
void EHMTX::set_alarm_color(int r, int g, int b)
{
this->alarm_color = Color((uint8_t)r & 248, (uint8_t)g & 252, (uint8_t)b & 248);
ESP_LOGD(TAG, "alarm color r: %d g: %d b: %d", r, g, b);
}
void EHMTX::set_text_color(int r, int g, int b)
{
this->text_color = Color((uint8_t)r & 248, (uint8_t)g & 252, (uint8_t)b & 248);
ESP_LOGD(TAG, "text color r: %d g: %d b: %d", r, g, b);
}
uint8_t EHMTX::find_icon(std::string name)
{
for (uint8_t i = 0; i < this->icon_count; i++)
{
if (strcmp(this->icons[i]->name.c_str(), name.c_str()) == 0)
{
ESP_LOGD(TAG, "icon: %s found id: %d", name.c_str(), i);
return i;
}
}
ESP_LOGD(TAG, "icon: %s not found", name.c_str());
return MAXICONS;
}
void EHMTX::set_indicator_off()
{
this->show_indicator = false;
ESP_LOGD(TAG, "indicator off");
}
void EHMTX::set_indicator_on()
{
this->show_indicator = true;
ESP_LOGD(TAG, "indicator on");
}
void EHMTX::set_gauge_off()
{
this->show_gauge = false;
ESP_LOGD(TAG, "gauge off");
}
void EHMTX::set_gauge_value(uint8_t val)
{
this->show_gauge = false;
if ((val > 0) && (val <= 100))
{
this->show_gauge = true;
this->gauge_value = (uint8_t)(100 - val) * 7 / 100;
ESP_LOGD(TAG, "gauge value: %d => %d",val, this->gauge_value);
}
}
void EHMTX::draw_clock()
{
if (this->clock->now().timestamp > 6000) // valid time
{
time_t ts = this->clock->now().timestamp;
if (!this->show_date or ((this->next_action_time - ts) < this->clock_time))
{
this->display->strftime(this->xoffset + 15, this->yoffset, this->font, this->clock_color, display::TextAlign::BASELINE_CENTER, this->time_fmt.c_str(),
this->clock->now());
}
else
{
this->display->strftime(this->xoffset + 15, this->yoffset, this->font, this->clock_color, display::TextAlign::BASELINE_CENTER, this->date_fmt.c_str(),
this->clock->now());
}
this->draw_day_of_week();
}
else
{
this->display->print(this->xoffset + 15, this->yoffset, this->font, this->alarm_color, display::TextAlign::BASELINE_CENTER, "!t!");
}
}
void EHMTX::setup()
{
#ifdef USE_EHMTX_SELECT
if (this->select != NULL)
{
ESP_LOGD(TAG, "select_component activated");
this->select->traits.set_options(this->select_options);
this->select->parent = this;
}
#endif
}
void EHMTX::update() // called from polling component
{
}
void EHMTX::tick()
{
time_t ts = this->clock->now().timestamp;
if (ts > this->next_action_time)
{
if (this->show_icons)
{
this->next_action_time = ts + this->screen_time;
uint8_t i = this->icon_screen->icon;
++i;
if (i < this->icon_count)
{
int x, y, w, h;
this->display->get_text_bounds(0, 0, this->icons[i]->name.c_str(), this->font, display::TextAlign::LEFT, &x, &y, &w, &h);
this->icon_screen->set_text(this->icons[i]->name, i, w, 1);
ESP_LOGD(TAG, "show all icons icon: %d name: %s", i, this->icons[i]->name.c_str());
}
else
{
this->show_icons = false;
ESP_LOGD(TAG, "show all icons done");
}
}
else
{
this->show_screen = false;
if (!(ts - this->last_clock_time > 60)) // force clock if last time more the 60s old
{
bool has_next_screen = this->store->move_next();
if (has_next_screen)
{
this->show_screen = true;
}
}
if (this->show_screen == false)
{
ESP_LOGD(TAG, "next action: show clock/date for %d/%d sec",this->clock_time, this->screen_time-this->clock_time);
this->last_clock_time = ts;
this->next_action_time = ts + this->screen_time;
}
else
{
ESP_LOGD(TAG, "next action: show screen \"%s\" for %d sec", this->icons[this->store->current()->icon]->name.c_str() ,this->store->current()->display_duration);
this->next_action_time = ts + this->store->current()->display_duration;
for (auto *t : on_next_screen_triggers_)
{
t->process(this->icons[this->store->current()->icon]->name, this->store->current()->text);
}
}
}
}
}
void EHMTX::set_screen_time(uint16_t t)
{
this->screen_time = t;
}
void EHMTX::set_duration(uint8_t t)
{
this->duration = t;
}
void EHMTX::get_status()
{
time_t ts = this->clock->now().timestamp;
ESP_LOGI(TAG, "status time: %d.%d.%d %02d:%02d", this->clock->now().day_of_month,
this->clock->now().month, this->clock->now().year,
this->clock->now().hour, this->clock->now().minute);
ESP_LOGI(TAG, "status brightness: %d (0..255)", this->brightness_);
ESP_LOGI(TAG, "status default duration: %d", this->duration);
ESP_LOGI(TAG, "status date format: %s", this->date_fmt.c_str());
ESP_LOGI(TAG, "status time format: %s", this->time_fmt.c_str());
ESP_LOGI(TAG, "status text_color: RGB(%d,%d,%d)", this->text_color.r, this->text_color.g, this->text_color.b);
ESP_LOGI(TAG, "status alarm_color: RGB(%d,%d,%d)", this->alarm_color.r, this->alarm_color.g, this->alarm_color.b);
if (this->show_indicator)
{
ESP_LOGI(TAG, "status indicator on");
}
else
{
ESP_LOGI(TAG, "status indicator off");
}
this->store->log_status();
for (uint8_t i = 0; i < this->icon_count; i++)
{
ESP_LOGI(TAG, "status icon: %d name: %s", i, this->icons[i]->name.c_str());
}
#ifdef USE_EHMTX_SELECT
ESP_LOGI(TAG, "select enabled");
#endif
}
void EHMTX::set_font(display::Font *font)
{
this->font = font;
}
void EHMTX::set_anim_intervall(uint16_t ai)
{
this->anim_intervall = ai;
}
void EHMTX::set_scroll_intervall(uint16_t si)
{
this->scroll_intervall = si;
}
void EHMTX::del_screen(std::string iname)
{
uint8_t icon = this->find_icon(iname.c_str());
this->store->delete_screen(icon);
}
void EHMTX::add_screen(std::string iconname, std::string text, uint16_t duration, bool alarm)
{
uint8_t icon = this->find_icon(iconname.c_str());
this->internal_add_screen(icon, text, duration, alarm);
ESP_LOGD(TAG, "add_screen icon: %d iconname: %s text: %s duration: %d alarm: %d", icon, iconname.c_str(), text.c_str(), duration, alarm);
}
void EHMTX::internal_add_screen(uint8_t icon, std::string text, uint16_t duration, bool alarm = false)
{
if (icon >= this->icon_count)
{
ESP_LOGD(TAG, "icon %d not found => default: 0", icon);
icon = 0;
}
EHMTX_screen *screen = this->store->find_free_screen(icon);
int x, y, w, h;
this->display->get_text_bounds(0, 0, text.c_str(), this->font, display::TextAlign::LEFT, &x, &y, &w, &h);
screen->alarm = alarm;
screen->set_text(text, icon, w, duration);
}
void EHMTX::set_default_brightness(uint8_t b)
{
this->brightness_ = b;
}
void EHMTX::set_show_date(bool b)
{
this->show_date = b;
if (b)
{
ESP_LOGI(TAG, "show date");
}
else
{
ESP_LOGI(TAG, "don't show date");
}
}
void EHMTX::set_show_day_of_week(bool b)
{
this->show_day_of_week = b;
if (b)
{
ESP_LOGI(TAG, "show day of week");
}
else
{
ESP_LOGI(TAG, "don't show day of week");
}
}
void EHMTX::set_week_start(bool b)
{
this->week_starts_monday = b;
if (b)
{
ESP_LOGI(TAG, "weekstart: monday");
}
else
{
ESP_LOGI(TAG, "weekstart: sunday");
}
}
void EHMTX::set_brightness(uint8_t b)
{
this->brightness_ = b;
float br = (float)b / (float)255;
ESP_LOGI(TAG, "set_brightness %d => %.2f %%", b, 100 * br);
this->display->get_light()->set_correction(br, br, br, br);
}
uint8_t EHMTX::get_brightness()
{
return this->brightness_;
}
std::string EHMTX::get_current()
{
return this->icons[this->store->current()->icon]->name;
}
void EHMTX::set_clock_time(uint16_t t)
{
this->clock_time = t;
}
void EHMTX::set_display(addressable_light::AddressableLightDisplay *disp)
{
this->display = disp;
}
void EHMTX::set_clock(time::RealTimeClock *clock)
{
this->clock = clock;
this->store->clock = clock;
}
void EHMTX::draw_day_of_week()
{
if (this->show_day_of_week)
{
auto dow = this->clock->now().day_of_week - 1; // SUN = 0
for (uint8_t i = 0; i <= 6; i++)
{
if (((!this->week_starts_monday) && (dow == i)) ||
((this->week_starts_monday) && ((dow == (i + 1)) || ((dow == 0 && i == 6)))))
{
this->display->line(2 + i * 4, 7, i * 4 + 4, 7, this->today_color);
}
else
{
this->display->line(2 + i * 4, 7, i * 4 + 4, 7, this->weekday_color);
}
}
}
};
void EHMTX::set_font_offset(int8_t x, int8_t y)
{
this->xoffset = x;
this->yoffset = y;
}
void EHMTX::dump_config()
{
ESP_LOGCONFIG(TAG, "EspHoMatriX %s", EHMTX_VERSION);
ESP_LOGCONFIG(TAG, "Icons: %d of %d", this->icon_count, MAXICONS);
ESP_LOGCONFIG(TAG, "Font offset: x=%d y=%d", this->xoffset, this->yoffset);
ESP_LOGCONFIG(TAG, "Max screens: %d", MAXQUEUE);
ESP_LOGCONFIG(TAG, "Date format: %s", this->date_fmt.c_str());
ESP_LOGCONFIG(TAG, "Time format: %s", this->time_fmt.c_str());
ESP_LOGCONFIG(TAG, "Intervall (ms) scroll: %d anim: %d", this->scroll_intervall, this->anim_intervall);
ESP_LOGCONFIG(TAG, "Displaytime (s) clock: %d screen: %d", this->clock_time, this->screen_time);
if (this->show_day_of_week)
{
ESP_LOGCONFIG(TAG, "show day of week");
}
if (this->show_date)
{
ESP_LOGCONFIG(TAG, "show date");
}
if (this->week_starts_monday)
{
ESP_LOGCONFIG(TAG, "weekstart: monday");
}
else
{
ESP_LOGCONFIG(TAG, "weekstart: sunday");
}
}
#ifdef USE_EHMTX_SELECT
void EHMTX::set_select(esphome::EhmtxSelect *es)
{
this->select = es;
}
#endif
void EHMTX::add_icon(EHMTX_Icon *icon)
{
this->icons[this->icon_count] = icon;
ESP_LOGD(TAG, "add_icon no.: %d name: %s frame_duration: %d ms", this->icon_count, icon->name.c_str(), icon->frame_duration);
this->icon_count++;
#ifdef USE_EHMTX_SELECT
this->select_options.push_back(icon->name);
#endif
}
void EHMTX::show_all_icons()
{
int x, y, w, h;
ESP_LOGD(TAG, "show all icons icon: %s", this->icons[0]->name.c_str());
this->display->get_text_bounds(0, 0, this->icons[0]->name.c_str(), this->font, display::TextAlign::LEFT, &x, &y, &w, &h);
this->icon_screen->set_text(this->icons[0]->name, 0, w, 1);
this->show_icons = true;
}
void EHMTX::draw()
{
if (this->show_icons)
{
this->icon_screen->draw();
}
else
{
if (this->show_screen)
{
this->store->current()->draw();
}
else
{
this->draw_clock();
}
}
if (this->show_indicator)
{
this->display->line(31, 5, 29, 7, this->indicator_color);
this->display->draw_pixel_at(30, 7, this->indicator_color);
this->display->draw_pixel_at(31, 6, this->indicator_color);
this->display->draw_pixel_at(31, 7, this->indicator_color);
}
}
void EHMTXNextScreenTrigger::process(std::string iconname, std::string text)
{
this->trigger(iconname, text);
}
}
+422
View File
@@ -0,0 +1,422 @@
#ifndef EHMTX_H
#define EHMTX_H
#include "esphome.h"
const uint8_t MAXQUEUE = 24;
const uint8_t MAXICONS = 64;
const uint8_t TEXTSCROLLSTART = 8;
const uint8_t TEXTSTARTOFFSET = (32 - 8);
const uint16_t TICKINTERVAL = 1000; // each 1000ms
static const char *const EHMTX_VERSION = "Version: 2022.7.0";
static const char *const TAG = "EHMTX";
namespace esphome
{
class EHMTX_screen;
class EHMTX_store;
class EhmtxSelect;
class EHMTX_Icon;
class EHMTXNextScreenTrigger;
class EHMTX : public PollingComponent
{
protected:
float get_setup_priority() const override { return esphome::setup_priority::AFTER_CONNECTION; }
uint8_t brightness_;
bool week_starts_monday;
bool show_day_of_week;
std::string time_fmt;
std::string date_fmt;
Color indicator_color;
Color clock_color;
Color today_color;
Color weekday_color;
EHMTX_store *store;
std::vector<EHMTXNextScreenTrigger *> on_next_screen_triggers_;
void internal_add_screen(uint8_t icon, std::string text, uint16_t duration, bool alarm);
public:
EHMTX();
Color text_color, alarm_color, gauge_color;
void dump_config();
bool show_screen;
bool show_indicator;
bool show_gauge;
bool show_date;
uint8_t gauge_value;
bool show_icons;
void force_screen(std::string name);
EHMTX_Icon *icons[MAXICONS];
EHMTX_screen *icon_screen;
void add_icon(EHMTX_Icon *icon);
#ifdef USE_EHMTX_SELECT
std::vector<std::string> select_options;
esphome::EhmtxSelect *select;
void set_select(esphome::EhmtxSelect *es);
#endif
addressable_light::AddressableLightDisplay *display;
time::RealTimeClock *clock;
display::Font *font;
int8_t yoffset, xoffset;
uint8_t find_icon(std::string name);
uint16_t duration; // in minutes how long is a screen valid
uint16_t scroll_intervall; // ms to between scrollsteps
uint16_t anim_intervall; // ms to next_frame()
uint16_t clock_time; // seconds display of screen_time - clock_time = date_time
uint16_t screen_time; // seconds display of screen
uint8_t icon_count; // max iconnumber -1
unsigned long last_scroll_time;
unsigned long last_anim_time;
time_t last_clock_time = 0; // starttime clock display
time_t next_action_time = 0; // when is the next screen change
void draw_day_of_week();
void show_all_icons();
void tick();
void draw();
void get_status();
void skip_screen();
std::string get_current();
void set_display(addressable_light::AddressableLightDisplay *disp);
void set_screen_time(uint16_t t);
void set_clock_time(uint16_t t);
void set_show_day_of_week(bool b);
void set_show_date(bool b);
void set_font_offset(int8_t x, int8_t y);
void set_week_start(bool b);
void set_default_brightness(uint8_t b);
void set_brightness(uint8_t b);
uint8_t get_brightness();
void add_screen(std::string icon, std::string text, uint16_t duration, bool alarm);
void del_screen(std::string iname);
void set_clock(time::RealTimeClock *clock);
void set_font(display::Font *font);
void set_anim_intervall(uint16_t intervall);
void set_scroll_intervall(uint16_t intervall);
void set_duration(uint8_t d);
void set_indicator_off();
void set_time_format(std::string s);
void set_date_format(std::string s);
void set_indicator_on();
void set_indicator_color(int r, int g, int b);
void set_gauge_off();
void set_gauge_value(uint8_t v);
void set_gauge_color(int r, int g, int b);
void set_text_color(int r, int g, int b);
void set_clock_color(int r, int g, int b);
void set_today_color(int r, int g, int b);
void set_weekday_color(int r, int g, int b);
void set_alarm_color(int r, int g, int b);
void set_icon_count(uint8_t ic);
void draw_clock();
void add_on_next_screen_trigger(EHMTXNextScreenTrigger *t) { this->on_next_screen_triggers_.push_back(t); }
void setup();
void update();
};
class EHMTX_store
{
protected:
EHMTX_screen *slots[MAXQUEUE];
uint8_t active_slot;
uint8_t force_screen;
uint8_t count_active_screens();
public:
EHMTX_store(EHMTX *config);
void force_next_screen(uint8_t icon_id);
time::RealTimeClock *clock;
EHMTX_screen *find_free_screen(uint8_t icon);
void delete_screen(uint8_t icon);
bool move_next();
EHMTX_screen *current();
void log_status();
};
class EHMTX_screen
{
protected:
uint8_t shiftx_;
uint8_t pixels_;
EHMTX *config_;
public:
uint16_t display_duration;
bool alarm;
time_t endtime;
uint8_t icon;
std::string text;
EHMTX_screen(EHMTX *config);
bool active();
bool is_alarm();
void draw();
void draw_();
bool isfree();
bool update_slot(uint8_t _icon);
void update_screen();
bool del_slot(uint8_t _icon);
void set_text(std::string text, uint8_t icon, uint8_t pixel, uint16_t et);
};
class EHMTXNextScreenTrigger : public Trigger<std::string, std::string>
{
public:
explicit EHMTXNextScreenTrigger(EHMTX *parent) { parent->add_on_next_screen_trigger(this); }
void process(std::string, std::string);
};
template <typename... Ts>
class SetBrightnessAction : public Action<Ts...>
{
public:
SetBrightnessAction(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, brightness)
void play(Ts... x) override
{
auto brightness = this->brightness_.value(x...);
this->parent_->set_brightness(brightness);
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class AddScreenAction : public Action<Ts...>
{
public:
AddScreenAction(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(std::string, icon)
TEMPLATABLE_VALUE(std::string, text)
TEMPLATABLE_VALUE(uint8_t, duration)
TEMPLATABLE_VALUE(bool, alarm)
void play(Ts... x) override
{
auto icon = this->icon_.value(x...);
auto text = this->text_.value(x...);
auto duration = this->duration_.value(x...);
auto alarm = this->alarm_.value(x...);
if (duration)
{
this->parent_->add_screen(icon, text, duration, alarm);
}
else
{
this->parent_->add_screen(icon, text, this->parent_->duration, alarm);
}
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetIndicatorOn : public Action<Ts...>
{
public:
SetIndicatorOn(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, red)
TEMPLATABLE_VALUE(uint8_t, green)
TEMPLATABLE_VALUE(uint8_t, blue)
void play(Ts... x) override
{
this->parent_->set_indicator_on();
this->parent_->set_indicator_color(this->red_.value(x...), this->green_.value(x...), this->blue_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetClockColor : public Action<Ts...>
{
public:
SetClockColor(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, red)
TEMPLATABLE_VALUE(uint8_t, green)
TEMPLATABLE_VALUE(uint8_t, blue)
void play(Ts... x) override
{
this->parent_->set_clock_color(this->red_.value(x...), this->green_.value(x...), this->blue_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetAlarmColor : public Action<Ts...>
{
public:
SetAlarmColor(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, red)
TEMPLATABLE_VALUE(uint8_t, green)
TEMPLATABLE_VALUE(uint8_t, blue)
void play(Ts... x) override
{
this->parent_->set_alarm_color(this->red_.value(x...), this->green_.value(x...), this->blue_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetTodayColor : public Action<Ts...>
{
public:
SetTodayColor(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, red)
TEMPLATABLE_VALUE(uint8_t, green)
TEMPLATABLE_VALUE(uint8_t, blue)
void play(Ts... x) override
{
this->parent_->set_today_color(this->red_.value(x...), this->green_.value(x...), this->blue_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetShowDate : public Action<Ts...>
{
public:
SetShowDate(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, flag)
void play(Ts... x) override
{
this->parent_->set_show_date(this->flag_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetShowDayOfWeek : public Action<Ts...>
{
public:
SetShowDayOfWeek(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, flag)
void play(Ts... x) override
{
this->parent_->set_show_day_of_week(this->flag_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetTextColor : public Action<Ts...>
{
public:
SetTextColor(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, red)
TEMPLATABLE_VALUE(uint8_t, green)
TEMPLATABLE_VALUE(uint8_t, blue)
void play(Ts... x) override
{
this->parent_->set_text_color(this->red_.value(x...), this->green_.value(x...), this->blue_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetWeekdayColor : public Action<Ts...>
{
public:
SetWeekdayColor(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint8_t, red)
TEMPLATABLE_VALUE(uint8_t, green)
TEMPLATABLE_VALUE(uint8_t, blue)
void play(Ts... x) override
{
this->parent_->set_weekday_color(this->red_.value(x...), this->green_.value(x...), this->blue_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class SetIndicatorOff : public Action<Ts...>
{
public:
SetIndicatorOff(EHMTX *parent) : parent_(parent) {}
void play(Ts... x) override
{
this->parent_->set_indicator_off();
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class DeleteScreen : public Action<Ts...>
{
public:
DeleteScreen(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(std::string, icon)
void play(Ts... x) override
{
this->parent_->del_screen(this->icon_.value(x...));
}
protected:
EHMTX *parent_;
};
template <typename... Ts>
class ForceScreen : public Action<Ts...>
{
public:
ForceScreen(EHMTX *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(std::string, icon)
void play(Ts... x) override
{
this->parent_->force_screen(this->icon_.value(x...));
}
protected:
EHMTX *parent_;
};
class EHMTX_Icon : public display::Animation
{
protected:
bool counting_up;
public:
EHMTX_Icon(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, display::ImageType type, std::string icon_name, bool revers, uint16_t frame_duration);
std::string name;
uint16_t frame_duration;
void next_frame();
bool reverse;
};
}
#endif
+37
View File
@@ -0,0 +1,37 @@
#include "esphome.h"
namespace esphome
{
EHMTX_Icon::EHMTX_Icon(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, display::ImageType type, std::string icon_name, bool revers, uint16_t frame_duration)
: Animation(data_start, width, height, animation_frame_count, type)
{
this->name = icon_name;
this->reverse = revers;
this->frame_duration = frame_duration;
this->counting_up = true;
}
void EHMTX_Icon::next_frame()
{
if (this->get_animation_frame_count() > 1)
{
if (this->counting_up)
{
if (this->reverse && (this->get_current_frame() == this->get_animation_frame_count() - 2))
{
this->counting_up = false;
}
Animation::next_frame();
}
else
{
if (this->get_current_frame() == 1) // this->get_animation_frame_count())
{
this->counting_up = true;
}
Animation::prev_frame();
}
}
}
}
+121
View File
@@ -0,0 +1,121 @@
#include "esphome.h"
namespace esphome
{
EHMTX_screen::EHMTX_screen(EHMTX *config)
{
this->config_ = config;
this->endtime = 0;
this->alarm = false;
}
bool EHMTX_screen::is_alarm() { return this->alarm; }
bool EHMTX_screen::del_slot(uint8_t _icon)
{
if (this->icon == _icon)
{
this->endtime = 0;
this->icon = 0;
ESP_LOGD(TAG, "delete screen icon: %d", _icon);
return true;
}
return false;
}
void EHMTX_screen::update_screen()
{
if (millis() - this->config_->last_scroll_time >= this->config_->scroll_intervall && this->pixels_ > TEXTSTARTOFFSET)
{
this->shiftx_++;
if (this->shiftx_ > this->pixels_ + TEXTSTARTOFFSET)
{
this->shiftx_ = 0;
}
this->config_->last_scroll_time = millis();
}
if (millis() - this->config_->last_anim_time >= this->config_->icons[this->icon]->frame_duration)
{
this->config_->icons[this->icon]->next_frame();
this->config_->last_anim_time = millis();
}
}
bool EHMTX_screen::active()
{
if (this->endtime > 0)
{
time_t ts = this->config_->clock->now().timestamp;
if (ts < this->endtime)
{
return true;
}
}
return false;
}
void EHMTX_screen::draw_()
{
int8_t extraoffset = 0;
if (this->pixels_ > TEXTSTARTOFFSET)
{
extraoffset = TEXTSTARTOFFSET;
}
if (this->config_->show_gauge)
{
extraoffset += 2;
}
if (this->alarm)
{
this->config_->display->print(TEXTSCROLLSTART - this->shiftx_ + extraoffset + this->config_->xoffset, this->config_->yoffset, this->config_->font, this->config_->alarm_color, esphome::display::TextAlign::BASELINE_LEFT,
this->text.c_str());
}
else
{
this->config_->display->print(TEXTSCROLLSTART - this->shiftx_ + extraoffset + this->config_->xoffset, this->config_->yoffset, this->config_->font, this->config_->text_color, esphome::display::TextAlign::BASELINE_LEFT,
this->text.c_str());
}
if (this->alarm)
{
this->config_->display->draw_pixel_at(30, 0, this->config_->alarm_color);
this->config_->display->draw_pixel_at(31, 1, this->config_->alarm_color);
this->config_->display->draw_pixel_at(31, 0, this->config_->alarm_color);
}
if (this->config_->show_gauge)
{
this->config_->display->line(0, 7, 0, 0, esphome::display::COLOR_OFF);
this->config_->display->line(0, 7, 0, this->config_->gauge_value, this->config_->gauge_color);
this->config_->display->line(1, 7, 1, 0, esphome::display::COLOR_OFF);
this->config_->display->image(2, 0, this->config_->icons[this->icon]);
this->config_->display->line(10, 0, 10, 7, esphome::display::COLOR_OFF);
}
else
{
this->config_->display->line(8, 0, 8, 7, esphome::display::COLOR_OFF);
this->config_->display->image(0, 0, this->config_->icons[this->icon]);
}
}
void EHMTX_screen::draw()
{
this->draw_();
this->update_screen();
}
void EHMTX_screen::set_text(std::string text, uint8_t icon, uint8_t pixel, uint16_t et)
{
this->text = text;
this->pixels_ = pixel;
this->shiftx_ = 0;
float dd = ceil((2 * (TEXTSTARTOFFSET + pixel) * this->config_->scroll_intervall) / 1000);
this->display_duration = (dd > this->config_->screen_time) ? dd : this->config_->screen_time;
ESP_LOGD(TAG, "display length text: %s pixels %d calculated: %d default: %d", text.c_str(),pixel, this->display_duration, this->config_->screen_time);
this->endtime = this->config_->clock->now().timestamp + et * 60;
this->icon = icon;
}
}
+144
View File
@@ -0,0 +1,144 @@
#include "esphome.h"
namespace esphome
{
EHMTX_store::EHMTX_store(EHMTX *config)
{
for (uint8_t i = 0; i < MAXQUEUE; i++)
{
this->slots[i] = new EHMTX_screen(config);
}
this->active_slot = 0;
}
EHMTX_screen *EHMTX_store::find_free_screen(uint8_t icon)
{
ESP_LOGD(TAG, "findfreeslot for icon: %d", icon);
for (uint8_t i = 0; i < MAXQUEUE; i++)
{
EHMTX_screen *screen = this->slots[i];
if (screen->icon == icon)
{
return screen;
}
}
time_t ts = this->clock->now().timestamp;
for (uint8_t i = 0; i < MAXQUEUE; i++)
{
EHMTX_screen *screen = this->slots[i];
if (screen->endtime <= ts)
{
return screen;
}
}
return this->slots[0];
}
void EHMTX_store::delete_screen(uint8_t icon)
{
for (uint8_t i = 0; i < MAXQUEUE; i++)
{
this->slots[i]->del_slot(icon);
}
}
void EHMTX_store::force_next_screen(uint8_t icon_id)
{
if (icon_id < MAXICONS)
{
this->force_screen = icon_id;
}
}
bool EHMTX_store::move_next()
{
if (this->force_screen < MAXICONS)
{
for (uint8_t slot = 0; slot < MAXQUEUE; slot++)
{
EHMTX_screen *screen = this->slots[slot];
if (screen->active() && screen->icon == this->force_screen)
{
this->force_screen = MAXICONS;
this->active_slot = slot;
return true;
}
}
}
if (this->count_active_screens() == 1)
{
// Find first and only active screen
for (uint8_t slot = 0; slot < MAXQUEUE; slot++)
{
EHMTX_screen *screen = this->slots[slot];
if (screen->active())
{
this->active_slot = slot;
return true;
}
}
}
// Find active screen between active slot and end of array
for (uint8_t slot = (this->active_slot + 1); slot < MAXQUEUE; slot++)
{
EHMTX_screen *screen = this->slots[slot];
if (screen->active())
{
this->active_slot = slot;
return true;
}
}
// Find active screen between 0 and active slot
for (uint8_t slot = 0; slot < this->active_slot; slot++)
{
EHMTX_screen *screen = this->slots[slot];
if (screen->active())
{
this->active_slot = slot;
return true;
}
}
// No active screen found
this->active_slot = 0;
return false;
}
EHMTX_screen *EHMTX_store::current()
{
return this->slots[this->active_slot];
}
uint8_t EHMTX_store::count_active_screens()
{
uint8_t count = 0;
for (uint8_t screen = 0; screen < MAXQUEUE; screen++)
{
if (this->slots[screen]->active())
{
count++;
}
}
return count;
}
void EHMTX_store::log_status()
{
uint8_t status = 0;
time_t ts = this->clock->now().timestamp;
ESP_LOGI(TAG, "status active slot: %d", this->active_slot);
ESP_LOGI(TAG, "status screen count: %d of %d", this->count_active_screens(), MAXQUEUE);
for (uint8_t i = 0; i < MAXQUEUE; i++)
{
if (this->slots[i]->active())
{
EHMTX_screen *screen = this->slots[i];
int td = screen->endtime - ts;
ESP_LOGI(TAG, "status slot %d icon %d text: %s alarm: %d dd: %d sec end: %d sec", i, screen->icon, screen->text.c_str(), screen->alarm, screen->display_duration, td);
}
}
}
}
+609
View File
@@ -0,0 +1,609 @@
from argparse import Namespace
import logging
import io
import requests
from esphome import core, automation
from esphome.components import display, font, time
import esphome.components.image as espImage
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_FILE, CONF_ID, CONF_BRIGHTNESS, CONF_RAW_DATA_ID, CONF_TIME, CONF_DURATION, CONF_TRIGGER_ID
from esphome.core import CORE, HexInt
from esphome.cpp_generator import RawExpression
from .select import EHMTXSelect
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ["display", "light", "api"]
AUTO_LOAD = ["ehmtx"]
IMAGE_TYPE_RGB565 = 4
MAXFRAMES = 20
MAXICONS = 72
ICONWIDTH = 8
ICONHEIGHT = 8
ICONBUFFERSIZE = ICONWIDTH * ICONHEIGHT
ICONSIZE = [ICONWIDTH,ICONHEIGHT]
SVG_START = '<svg width="80px" height="80px" viewBox="0 0 80 80">'
SVG_END = "</svg>"
def rgb888_svg(x,y,r,g,b):
return f"<rect style=\"fill:rgb({r},{g},{b});\" x=\"{x*10}\" y=\"{y*10}\" width=\"10\" height=\"10\"/>"
def rgb565_svg(x,y,r,g,b):
return f"<rect style=\"fill:rgb({(r << 3) | (r >> 2)},{(g << 2) | (g >> 4)},{(b << 3) | (b >> 2)});\" x=\"{x*10}\" y=\"{y*10}\" width=\"10\" height=\"10\"/>"
ehmtx_ns = cg.esphome_ns.namespace("esphome")
EHMTX_ = ehmtx_ns.class_("EHMTX", cg.Component)
Icons_ = ehmtx_ns.class_("EHMTX_Icon")
NextScreenTrigger = ehmtx_ns.class_(
"EHMTXNextScreenTrigger", automation.Trigger.template(cg.std_string)
)
CONF_SHOWCLOCK = "show_clock"
CONF_SHOWSCREEN = "show_screen"
CONF_EHMTX = "ehmtx"
CONF_URL = "url"
CONF_FLAG = "flag"
CONF_LAMEID = "lameid"
CONF_AWTRIXID = "awtrixid"
CONF_ICONS = "icons"
CONF_SHOWDOW = "dayofweek"
CONF_SHOWDATE = "show_date"
CONF_DISPLAY = "display8x32"
CONF_HTML = "html"
CONF_SCROLLINTERVALL = "scroll_intervall"
CONF_ANIMINTERVALL = "anim_intervall"
CONF_FONT_ID = "font_id"
CONF_YOFFSET = "yoffset"
CONF_XOFFSET = "xoffset"
CONF_PINGPONG = "pingpong"
CONF_TIME_FORMAT = "time_format"
CONF_DATE_FORMAT = "date_format"
CONF_SELECT = "ehmtxselect"
CONF_ON_NEXT_SCREEN = "on_next_screen"
CONF_WEEK_ON_MONDAY = "week_start_monday"
CONF_ICON = "icon_name"
CONF_TEXT = "text"
CONF_ALARM = "alarm"
EHMTX_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.declare_id(EHMTX_),
cv.Required(CONF_TIME): cv.use_id(time),
cv.Required(CONF_DISPLAY): cv.use_id(display),
cv.Required(CONF_FONT_ID): cv.use_id(font),
cv.Optional(
CONF_SHOWCLOCK, default="5"
): cv.templatable(cv.positive_int),
cv.Optional(
CONF_SELECT,
): cv.use_id(EHMTXSelect),
cv.Optional(
CONF_YOFFSET, default="6"
): cv.templatable(cv.int_range(min=-32, max=32)),
cv.Optional(
CONF_HTML, default=False
): cv.boolean,
cv.Optional(
CONF_SHOWDATE, default=True
): cv.boolean,
cv.Optional(
CONF_WEEK_ON_MONDAY, default=True
): cv.boolean,
cv.Optional(
CONF_SHOWDOW, default=True
): cv.boolean,
cv.Optional(
CONF_TIME_FORMAT, default="%H:%M"
): cv.string,
cv.Optional(
CONF_DATE_FORMAT, default="%d.%m."
): cv.string,
cv.Optional(
CONF_XOFFSET, default="1"
): cv.templatable(cv.int_range(min=-32, max=32)),
cv.Optional(CONF_SCROLLINTERVALL, default="80"
): cv.templatable(cv.positive_int),
cv.Optional(
CONF_ANIMINTERVALL, default="192"
): cv.templatable(cv.positive_int),
cv.Optional(
CONF_SHOWSCREEN, default="8"
): cv.templatable(cv.positive_int),
cv.Optional(CONF_BRIGHTNESS, default=80): cv.templatable(cv.int_range(min=0, max=255)),
cv.Optional(
CONF_DURATION, default="5"
): cv.templatable(cv.positive_int),
cv.Optional(CONF_ON_NEXT_SCREEN): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(NextScreenTrigger),
}
),
cv.Required(CONF_ICONS): cv.All(
cv.ensure_list(
{
cv.Required(CONF_ID): cv.declare_id(Icons_),
cv.Exclusive(CONF_FILE,"uri"): cv.file_,
cv.Exclusive(CONF_URL,"uri"): cv.url,
cv.Exclusive(CONF_LAMEID,"uri"): cv.string,
cv.Exclusive(CONF_AWTRIXID,"uri"): cv.string,
cv.Optional(
CONF_DURATION, default="0"
): cv.templatable(cv.positive_int),
cv.Optional(
CONF_PINGPONG, default=False
): cv.boolean,
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
}
),
cv.Length(max=MAXICONS),
)})
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, EHMTX_SCHEMA)
ADD_SCREEN_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(EHMTX_),
cv.Required(CONF_ICON): cv.templatable(cv.string),
cv.Required(CONF_TEXT): cv.templatable(cv.string),
cv.Optional(CONF_DURATION): cv.templatable(cv.positive_int),
cv.Optional(CONF_ALARM, default=False): cv.templatable(cv.boolean),
}
)
NextScreenTrigger = ehmtx_ns.class_(
"EHMTXNextScreenTrigger", automation.Trigger.template(cg.std_string)
)
AddScreenAction = ehmtx_ns.class_("AddScreenAction", automation.Action)
@automation.register_action(
"ehmtx.add.screen", AddScreenAction, ADD_SCREEN_ACTION_SCHEMA
)
async def ehmtx_add_screen_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_ICON], args, cg.std_string)
cg.add(var.set_icon(template_))
template_ = await cg.templatable(config[CONF_TEXT], args, cg.std_string)
cg.add(var.set_text(template_))
if CONF_DURATION in config:
template_ = await cg.templatable(config[CONF_DURATION], args, cg.uint8)
cg.add(var.set_duration(template_))
template_ = await cg.templatable(config[CONF_ALARM], args, bool)
cg.add(var.set_alarm(template_))
return var
SET_BRIGHTNESS_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(EHMTX_),
cv.Optional(CONF_BRIGHTNESS, default=80): cv.templatable(cv.int_range(min=0, max=255)),
}
)
SetBrightnessAction = ehmtx_ns.class_("SetBrightnessAction", automation.Action)
@automation.register_action(
"ehmtx.set.brightness", SetBrightnessAction, SET_BRIGHTNESS_ACTION_SCHEMA
)
async def ehmtx_set_brightness_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_BRIGHTNESS], args, cg.int_)
cg.add(var.set_brightness(template_))
return var
SET_COLOR_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(EHMTX_),
cv.Optional(CONF_RED,default=80): cv.templatable(cv.uint8_t,),
cv.Optional(CONF_BLUE,default=80): cv.templatable(cv.uint8_t,),
cv.Optional(CONF_GREEN,default=80): cv.templatable(cv.uint8_t,),
}
)
SetClockColorAction = ehmtx_ns.class_("SetClockColor", automation.Action)
@automation.register_action(
"ehmtx.clock.color", SetClockColorAction, SET_COLOR_ACTION_SCHEMA
)
async def ehmtx_set_clock_color_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_RED], args, cg.int_)
cg.add(var.set_red(template_))
template_ = await cg.templatable(config[CONF_GREEN], args, cg.int_)
cg.add(var.set_green(template_))
template_ = await cg.templatable(config[CONF_BLUE], args, cg.int_)
cg.add(var.set_blue(template_))
return var
SetTextColorAction = ehmtx_ns.class_("SetTextColor", automation.Action)
@automation.register_action(
"ehmtx.text.color", SetTextColorAction, SET_COLOR_ACTION_SCHEMA
)
async def ehmtx_set_text_color_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_RED], args, cg.int_)
cg.add(var.set_red(template_))
template_ = await cg.templatable(config[CONF_GREEN], args, cg.int_)
cg.add(var.set_green(template_))
template_ = await cg.templatable(config[CONF_BLUE], args, cg.int_)
cg.add(var.set_blue(template_))
return var
SetAlarmColorAction = ehmtx_ns.class_("SetAlarmColor", automation.Action)
@automation.register_action(
"ehmtx.alarm.color", SetAlarmColorAction, SET_COLOR_ACTION_SCHEMA
)
async def ehmtx_set_alarm_color_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_RED], args, cg.int_)
cg.add(var.set_red(template_))
template_ = await cg.templatable(config[CONF_GREEN], args, cg.int_)
cg.add(var.set_green(template_))
template_ = await cg.templatable(config[CONF_BLUE], args, cg.int_)
cg.add(var.set_blue(template_))
return var
SET_FLAG_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(EHMTX_),
cv.Optional(CONF_FLAG,default=True): cv.templatable(cv.boolean),
}
)
SetShowDateAction = ehmtx_ns.class_("SetShowDate", automation.Action)
@automation.register_action(
"ehmtx.show.date", SetShowDateAction, SET_FLAG_ACTION_SCHEMA
)
async def ehmtx_show_date_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_FLAG], args, cg.bool_)
cg.add(var.set_flag(template_))
return var
SetShowDayOfWeekAction = ehmtx_ns.class_("SetShowDayOfWeek", automation.Action)
@automation.register_action(
"ehmtx.show.dayofweek", SetShowDayOfWeekAction, SET_FLAG_ACTION_SCHEMA
)
async def ehmtx_show_dayofweek_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_FLAG], args, cg.bool_)
cg.add(var.set_flag(template_))
return var
SetTodayColorAction = ehmtx_ns.class_("SetTodayColor", automation.Action)
@automation.register_action(
"ehmtx.today.color", SetTodayColorAction, SET_COLOR_ACTION_SCHEMA
)
async def ehmtx_set_today_color_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_RED], args, cg.int_)
cg.add(var.set_red(template_))
template_ = await cg.templatable(config[CONF_GREEN], args, cg.int_)
cg.add(var.set_green(template_))
template_ = await cg.templatable(config[CONF_BLUE], args, cg.int_)
cg.add(var.set_blue(template_))
return var
SetWeekdayColorAction = ehmtx_ns.class_("SetWeekdayColor", automation.Action)
@automation.register_action(
"ehmtx.weekday.color", SetWeekdayColorAction, SET_COLOR_ACTION_SCHEMA
)
async def ehmtx_set_week_color_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_RED], args, cg.int_)
cg.add(var.set_red(template_))
template_ = await cg.templatable(config[CONF_GREEN], args, cg.int_)
cg.add(var.set_green(template_))
template_ = await cg.templatable(config[CONF_BLUE], args, cg.int_)
cg.add(var.set_blue(template_))
return var
SetIndicatorOnAction = ehmtx_ns.class_("SetIndicatorOn", automation.Action)
@automation.register_action(
"ehmtx.indicator.on", SetIndicatorOnAction, SET_COLOR_ACTION_SCHEMA
)
async def ehmtx_set_indicator_on_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_RED], args, cg.int_)
cg.add(var.set_red(template_))
template_ = await cg.templatable(config[CONF_GREEN], args, cg.int_)
cg.add(var.set_green(template_))
template_ = await cg.templatable(config[CONF_BLUE], args, cg.int_)
cg.add(var.set_blue(template_))
return var
DELETE_SCREEN_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(EHMTX_),
cv.Required(CONF_ICON): cv.templatable(cv.string),
}
)
DeleteScreenAction = ehmtx_ns.class_("DeleteScreen", automation.Action)
@automation.register_action(
"ehmtx.delete.screen", DeleteScreenAction, DELETE_SCREEN_ACTION_SCHEMA
)
async def ehmtx_delete_screen_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_ICON], args, cg.std_string)
cg.add(var.set_icon(template_))
return var
ForceScreenAction = ehmtx_ns.class_("ForceScreen", automation.Action)
@automation.register_action(
"ehmtx.force.screen", ForceScreenAction, DELETE_SCREEN_ACTION_SCHEMA
)
async def ehmtx_force_screen_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_ICON], args, cg.std_string)
cg.add(var.set_icon(template_))
return var
SetIndicatorOffAction = ehmtx_ns.class_("SetIndicatorOff", automation.Action)
INDICATOR_OFF_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(EHMTX_),
}
)
@automation.register_action(
"ehmtx.indicator.off", SetIndicatorOffAction, INDICATOR_OFF_ACTION_SCHEMA
)
async def ehmtx_set_indicator_off_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
return var
CODEOWNERS = ["@lubeda"]
async def to_code(config):
from PIL import Image
var = cg.new_Pvariable(config[CONF_ID])
html_string = F"<HTML><HEAD><TITLE>{CORE.config_path}</TITLE></HEAD>"
html_string += '''\
<STYLE>
</STYLE><BODY>\
'''
for conf in config[CONF_ICONS]:
if CONF_FILE in conf:
path = CORE.relative_config_path(conf[CONF_FILE])
try:
image = Image.open(path)
except Exception as e:
raise core.EsphomeError(f" ICONS: Could not load image file {path}: {e}")
elif CONF_LAMEID in conf:
r = requests.get("https://developer.lametric.com/content/apps/icon_thumbs/" + conf[CONF_LAMEID], timeout=4.0)
if r.status_code != requests.codes.ok:
raise core.EsphomeError(f" ICONS: Could not download image file {conf[CONF_LAMEID]}: {conf[CONF_ID]}")
image = Image.open(io.BytesIO(r.content))
elif CONF_AWTRIXID in conf:
r = requests.post("https://awtrix.blueforcer.de/icon",json={"reqType":"getIcon","ID":"" + conf[CONF_AWTRIXID] + ""} , timeout=4.0)
if r.status_code != requests.codes.ok:
raise core.EsphomeError(f" ICONS: Could not download awtrix data {conf[CONF_AWTRIXID]}: {conf[CONF_ID]}")
awtrixdata = r.json()
r = requests.get("https://awtrix.blueforcer.de/icons/"+conf[CONF_AWTRIXID], timeout=4.0)
if r.status_code != requests.codes.ok:
raise core.EsphomeError(f" ICONS: Could not download awtrix icon {conf[CONF_URL]}: {conf[CONF_ID]}")
image = Image.open(io.BytesIO(r.content))
elif CONF_URL in conf:
r = requests.get(conf[CONF_URL], timeout=4.0)
if r.status_code != requests.codes.ok:
raise core.EsphomeError(f" ICONS: Could not download image file {conf[CONF_URL]}: {conf[CONF_ID]}")
image = Image.open(io.BytesIO(r.content))
width, height = image.size
if (width != ICONWIDTH) or (height != ICONHEIGHT):
image = image.resize(ICONSIZE)
width, height = image.size
if hasattr(image, 'n_frames'):
frames = min(image.n_frames, MAXFRAMES)
else:
frames = 1
if (conf[CONF_DURATION] == 0):
try:
duration = image.info['duration']
except:
duration = config[CONF_ANIMINTERVALL]
else:
duration = conf[CONF_DURATION]
html_string += F"<BR><B>{conf[CONF_ID]}</B>&nbsp;-&nbsp;({duration} ms):<BR>"
pos = 0
frameIndex = 0
html_string += f"<DIV ID={conf[CONF_ID]}>"
if CONF_AWTRIXID in conf:
if "data" in awtrixdata:
frames = len(awtrixdata["data"])
frameIndex = 0
data = [0 for _ in range(ICONBUFFERSIZE * 2 * frames)]
duration = awtrixdata["tick"]
for frame in awtrixdata["data"]:
if len(frame) != ICONBUFFERSIZE:
raise core.EsphomeError(
f"Unexpected number of pixels in awtrix"
)
i = 0
html_string += SVG_START
for pix in frame:
G = (pix & 0x07e0) >> 5
B = pix & 0x1f
R = (pix & 0xF800) >> 11
x = (i % ICONWIDTH)
y = i//ICONHEIGHT
i += 1
rgb = pix # (R << 11) | (G << 5) | B
html_string += rgb565_svg(x,y,R,G,B)
data[pos] = rgb >> 8
pos += 1
data[pos] = rgb & 255
pos += 1
frameIndex += 1
html_string += SVG_END
else:
frames = 1
i = 0
data = [0 for _ in range(ICONBUFFERSIZE * 2)]
html_string += SVG_START
for pix in awtrixdata:
x = (i % ICONWIDTH)
y = i//ICONHEIGHT
i +=1
rgb = pix
G = (pix & 0x07e0) >> 5
B = pix & 0x1f
R = (pix & 0xF800) >> 11
html_string += rgb565_svg(x,y,R,G,B)
data[pos] = rgb >> 8
pos += 1
data[pos] = rgb & 255
pos += 1
html_string += SVG_END
else:
data = [0 for _ in range(ICONBUFFERSIZE * 2 * frames)]
for frameIndex in range(frames):
html_string += SVG_START
image.seek(frameIndex)
frame = image.convert("RGB")
pixels = list(frame.getdata())
if len(pixels) != ICONBUFFERSIZE:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
)
i = 0
for pix in pixels:
R = pix[0] >> 3
G = pix[1] >> 2
B = pix[2] >> 3
x = (i % ICONWIDTH)
y = i//ICONHEIGHT
i +=1
rgb = (R << 11) | (G << 5) | B
html_string += rgb565_svg(x,y,R,G,B)
data[pos] = rgb >> 8
pos += 1
data[pos] = rgb & 255
pos += 1
html_string += SVG_END
html_string += f"</DIV>"
rhs = [HexInt(x) for x in data]
prog_arr = cg.progmem_array(conf[CONF_RAW_DATA_ID], rhs)
cg.new_Pvariable(
conf[CONF_ID],
prog_arr,
width,
height,
frames,
espImage.IMAGE_TYPE["RGB565"],
str(conf[CONF_ID]),
conf[CONF_PINGPONG],
duration,
)
cg.add(var.add_icon(RawExpression(str(conf[CONF_ID]))))
html_string += "</BODY></HTML>"
if config[CONF_HTML]:
try:
with open(CORE.config_path.replace(".yaml","") + ".html", 'w') as f:
f.truncate()
f.write(html_string)
f.close()
except:
print("Error writing HTML file")
cg.add(var.set_clock_time(config[CONF_SHOWCLOCK]))
cg.add(var.set_default_brightness(config[CONF_BRIGHTNESS]))
cg.add(var.set_screen_time(config[CONF_SHOWSCREEN]))
cg.add(var.set_duration(config[CONF_DURATION]))
cg.add(var.set_scroll_intervall(config[CONF_SCROLLINTERVALL]))
cg.add(var.set_anim_intervall(config[CONF_ANIMINTERVALL]))
cg.add(var.set_week_start(config[CONF_WEEK_ON_MONDAY]))
cg.add(var.set_time_format(config[CONF_TIME_FORMAT]))
cg.add(var.set_date_format(config[CONF_DATE_FORMAT]))
cg.add(var.set_show_day_of_week(config[CONF_SHOWDOW]))
cg.add(var.set_show_date(config[CONF_SHOWDATE]))
cg.add(var.set_font_offset(config[CONF_XOFFSET], config[CONF_YOFFSET]))
disp = await cg.get_variable(config[CONF_DISPLAY])
cg.add(var.set_display(disp))
f = await cg.get_variable(config[CONF_FONT_ID])
cg.add(var.set_font(f))
ehmtxtime = await cg.get_variable(config[CONF_TIME])
cg.add(var.set_clock(ehmtxtime))
if (config.get(CONF_SELECT)):
ehmtxselect = await cg.get_variable(config[CONF_SELECT])
cg.add(var.set_select(ehmtxselect))
for conf in config.get(CONF_ON_NEXT_SCREEN, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [(cg.std_string, "x"), (cg.std_string, "y")], conf)
await cg.register_component(var, config)
@@ -0,0 +1,27 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import select
from esphome.const import (
CONF_ID,
)
CONF_EHMTX = "ehmtx"
select_ns = cg.esphome_ns.namespace("esphome")
EHMTXSelect = select_ns.class_(
"EhmtxSelect", select.Select, cg.PollingComponent
)
CONFIG_SCHEMA = cv.All(
select.SELECT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(EHMTXSelect),
}
).extend(cv.polling_component_schema("30s")),
)
async def to_code(config):
cg.add_define("USE_EHMTX_SELECT")
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await select.register_select(var, config, options=[])
@@ -0,0 +1,33 @@
#include "ehmtx_select.h"
#include "esphome/core/log.h"
namespace esphome {
static const char *const TAG = "ehmtx.select";
void EhmtxSelect::setup() {
this->publish_state("initializing...");
}
void EhmtxSelect::update() {
if (this->parent != NULL) {
std::string value;
value = this->parent->get_current();
this->publish_state(value);
}
}
void EhmtxSelect::dump_config() {
LOG_SELECT(TAG," ", this);
LOG_UPDATE_INTERVAL(this);
}
void EhmtxSelect::control(const std::string &value) {
// value from HA => check if displayable
if (this->parent != NULL) {
ESP_LOGD(TAG, "select control to: %s",value.c_str());
this->parent->force_screen(value.c_str());
}
}
} // namespace esphome
@@ -0,0 +1,22 @@
#pragma once
#include "esphome.h"
namespace esphome {
class EHMTX;
class EhmtxSelect : public select::Select, public PollingComponent {
public:
void setup() override;
void update() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::LATE; }
EHMTX *parent;
protected:
void control(const std::string &value) override;
};
}
+643
View File
@@ -0,0 +1,643 @@
substitutions:
esp_name: "esp32-75epaper-keuken"
esphome:
name: ${esp_name}
comment: ${esp_name}
includes:
- common.h
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
external_components:
- source:
type: git
url: https://github.com/atomicmike/esphome.git
ref: waveshare-color-2022.6
components: [waveshare_epaper]
# Enable Home Assistant API
api:
encryption:
key: !secrets eink_display_api
ota:
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${esp_name} fallback
password: !secret fallback_password
#Include sun
sun:
latitude: !secret home_latitude
longitude: !secret home_longitude
globals:
- id: data_updated
type: bool
restore_value: no
initial_value: 'false'
- id: initial_data_received
type: bool
restore_value: no
initial_value: 'false'
- id: recorded_display_refresh
type: int
restore_value: yes
initial_value: '0'
time:
- platform: homeassistant
id: esptime
on_time_sync:
- then:
- component.update: sunrise
- component.update: sunset
- logger.log: Time has been set and is valid!
on_time:
- minutes: 5
then:
- if:
condition:
lambda: 'return id(data_updated) == true;'
then:
- lambda: 'id(initial_data_received) = true;'
- logger.log: "Sensor data updated: Refreshing display..."
- component.update: eink_display
- lambda: 'id(data_updated) = false;'
- lambda: 'id(recorded_display_refresh) += 1;'
- lambda: 'id(display_last_update).publish_state(id(esptime).now().timestamp);'
else:
- logger.log: "No sensors updated - skipping display refresh."
button:
- platform: template
name: "Refresh screen"
id: refresh
icon: "mdi:emoticon-outline"
on_press:
- logger.log: "Button pressed"
- component.update: eink_display
# deep_sleep:
# run_duration: 15s
# sleep_duration: 1min
# Include custom fonts
font:
- file: 'fonts/GothamRnd-Book.ttf'
id: font_small_book
size: 18
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_large_bold
size: 108
glyphs: ['-',' ', '°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', 'a', 'n']
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_medium_bold
size: 30
# glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'M', 'I', 'N']
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_small_bold
size: 18
# glyphs: ['°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', 'M', 'I', 'N']
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_time
size: 90
glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':']
- file: 'fonts/GothamRnd-Book.ttf'
id: font_weekday
size: 30
- file: 'fonts/GothamRnd-Book.ttf'
id: font_day
size: 65
glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_month
size: 40
- file: fonts/materialdesignicons-webfont.ttf
id: font_wifi
size: 20
glyphs:
- "\U000F091F"
- "\U000F0922"
- "\U000F0925"
- "\U000F0928"
- "\U000F092B"
# Include Material Design Icons font
# Thanks to https://community.home-assistant.io/t/display-materialdesign-icons-on-esphome-attached-to-screen/199790/16
- file: 'fonts/materialdesignicons-webfont.ttf'
id: font_mdi_large
size: 96
glyphs: &mdi-weather-glyphs
- "\U000F0590" # mdi-weather-cloudy
- "\U000F0F2F" # mdi-weather-cloudy-alert
- "\U000F0E6E" # mdi-weather-cloudy-arrow-right
- "\U000F0591" # mdi-weather-fog
- "\U000F0592" # mdi-weather-hail
- "\U000F0F30" # mdi-weather-hazy
- "\U000F0898" # mdi-weather-hurricane
- "\U000F0593" # mdi-weather-lightning
- "\U000F067E" # mdi-weather-lightning-rainy
- "\U000F0594" # mdi-weather-night
- "\U000F0F31" # mdi-weather-night-partly-cloudy
- "\U000F0595" # mdi-weather-partly-cloudy
- "\U000F0F32" # mdi-weather-partly-lightning
- "\U000F0F33" # mdi-weather-partly-rainy
- "\U000F0F34" # mdi-weather-partly-snowy
- "\U000F0F35" # mdi-weather-partly-snowy-rainy
- "\U000F0596" # mdi-weather-pouring
- "\U000F0597" # mdi-weather-rainy
- "\U000F0598" # mdi-weather-snowy
- "\U000F0F36" # mdi-weather-snowy-heavy
- "\U000F067F" # mdi-weather-snowy-rainy
- "\U000F0599" # mdi-weather-sunny
- "\U000F0F37" # mdi-weather-sunny-alert
- "\U000F14E4" # mdi-weather-sunny-off
- "\U000F059A" # mdi-weather-sunset
- "\U000F059B" # mdi-weather-sunset-down
- "\U000F059C" # mdi-weather-sunset-up
- "\U000F0F38" # mdi-weather-tornado
- "\U000F059D" # mdi-weather-windy
- "\U000F059E" # mdi-weather-windy-variant
- "\U000f010b" # mdi-car
- "\U000f0819" # trash
- file: 'fonts/materialdesignicons-webfont.ttf'
id: font_mdi_medlarge
size: 60
glyphs:
- "\U000f0f64" # new moon
- "\U000f0f67" # mdi-moon-waxing-crescent
- "\U000F0F61" # mdi-moon-first-quarter
- "\U000F0F68" # mdi-moon-waxing-gibbous
- "\U000F0F62" # mdi-moon-full
- "\U000F0F66" # mdi-moon-waning-gibbous
- "\U000F0F63" # mdi-moon-last-quarter
- "\U000F0F65" # mdi-moon-waning-crescent
- file: 'fonts/materialdesignicons-webfont.ttf'
id: font_mdi_medium
size: 36
glyphs: *mdi-weather-glyphs
sensor:
- platform: template
name: "${esp_name} Last Update"
device_class: timestamp
id: display_last_update
- platform: homeassistant
entity_id: weather.forecast_home
attribute: temperature
id: weather_temperature
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_temperature_0
id: weather_temperature_0
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_temperature_1
id: weather_temperature_1
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_temperature_2
id: weather_temperature_2
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_temperature_3
id: weather_temperature_3
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: travel_Best_time
id: travel_Best_time
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: travel_ASML_time
id: travel_ASML_time
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: travel_GGD_time
id: travel_GGD_time
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: wifi_signal
id: sensor_wifi_signal
name: "${esp_name} WiFi"
update_interval: 60s
on_value:
- component.update: sensor_wifi_signal_percentage
- platform: template
id: sensor_wifi_signal_percentage
name: "${esp_name} Wi-Fi Signal Percentage"
icon: "mdi:wifi"
unit_of_measurement: "%"
update_interval: never
lambda: |-
if (id(sensor_wifi_signal).state) {
if (id(sensor_wifi_signal).state <= -100 ) {
return 0;
} else if (id(sensor_wifi_signal).state >= -50) {
return 100;
} else {
return 2 * (id(sensor_wifi_signal).state + 100);
}
} else {
return NAN;
}
- platform: template
name: "${esp_name} Recorded Display Refresh"
lambda: 'return id(recorded_display_refresh);'
unit_of_measurement: "refreshes"
text_sensor:
- platform: homeassistant
entity_id: weather.forecast_home
id: weather_state
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_condition_now
id: weather_condition_now
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_condition_0
id: weather_condition_0
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_timestamp_0
id: weather_timestamp_0
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_condition_1
id: weather_condition_1
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_timestamp_1
id: weather_timestamp_1
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_condition_2
id: weather_condition_2
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_timestamp_2
id: weather_timestamp_2
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_condition_3
id: weather_condition_3
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: weather_timestamp_3
id: weather_timestamp_3
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: afval_today
id: afval_today
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: afval_tomorrow
id: afval_tomorrow
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: travel_Best_name
id: travel_Best_name
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: travel_ASML_name
id: travel_ASML_name
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.e_ink_display_data
attribute: travel_GGD_name
id: travel_GGD_name
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.moon_phase
#attribute: icon
id: moon_phase
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.moon
id: moon
internal: true
# Sunrise
- platform: sun
type: sunrise
id: sun_sunrise
format: "%H:%M"
on_value:
then:
- lambda: 'id(data_updated) = true;'
# Sunset
- platform: sun
type: sunset
id: sun_sunset
format: "%H:%M"
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: wifi_info
ssid:
name: "${esp_name} Connected SSID"
id: ssid
icon: mdi:wifi-strength-2
entity_category: diagnostic
bssid:
name: "${esp_name} Connected BSSID"
id: bssid
icon: mdi:wifi-strength-2
entity_category: diagnostic
mac_address:
name: "${esp_name} WiFi Mac Address"
id: macaddress
icon: mdi:wifi-strength-2
entity_category: diagnostic
- platform: sun
id: sunrise
name: Sun Next Sunrise
type: sunrise
format: "%H:%M"
internal: true
update_interval: never
- platform: sun
id: sunset
name: Sun Next Sunset
type: sunset
format: "%H:%M"
internal: true
update_interval: never
# Define colors
# This design is white on black so this is necessary.
color:
- id: color_black
red: 0%
green: 0%
blue: 0%
white: 50%
- id: color_white
red: 0%
green: 0%
blue: 0%
white: 0%
- id: COLOR_RED
red: 100%
green: 0%
blue: 0%
white: 0%
# Pins for Waveshare ePaper ESP Board
spi:
clk_pin: GPIO13
mosi_pin: GPIO14
# Now render everything on the ePaper screen.
display:
- platform: waveshare_epaper
cs_pin: GPIO27
dc_pin: GPIO16
busy_pin: GPIO25
reset_pin: GPIO26
#model: 7.50in-bV2
model: 7.50inv2b
#model: 7.50inv2
#model: 7.50inV2alt
update_interval: 1h
id: eink_display
rotation: 90°
lambda: |-
int xres = it.get_width();
int yres = it.get_height();
#define x_pad 10 // border padding
#define y_pad 10 // border padding
int wifi_x_a = xres-x_pad;
int wifi_y_a = yres-y_pad+2;
// Fill background in black.
it.fill(COLOR_OFF);
// wifi strength
// if (id(sensor_wifi_signal).has_state())
// {
if (id(sensor_wifi_signal).state >= -50) {
//Excellent
it.print(wifi_x_a, wifi_y_a, id(font_wifi), TextAlign::BASELINE_RIGHT, "󰤨");
ESP_LOGI("WiFi", "Excellent");
} else if (id(sensor_wifi_signal).state >= -60) {
//Good
it.print(wifi_x_a, wifi_y_a, id(font_wifi), TextAlign::BASELINE_RIGHT, "󰤥");
ESP_LOGI("WiFi", "Good");
} else if (id(sensor_wifi_signal).state >= -67) {
//Fair
it.print(wifi_x_a, wifi_y_a, id(font_wifi), TextAlign::BASELINE_RIGHT, "󰤢");
ESP_LOGI("WiFi", "Fair");
} else if (id(sensor_wifi_signal).state >= -70) {
//Weak
it.print(wifi_x_a, wifi_y_a, id(font_wifi), TextAlign::BASELINE_RIGHT, "󰤟");
ESP_LOGI("WiFi", "Weak");
} else {
//Unlikely working signal
it.print(wifi_x_a, wifi_y_a, id(font_wifi), TextAlign::BASELINE_RIGHT, "󰤫");
ESP_LOGI("WiFi", "Unlikely");
}
//}
// clock section
int clk_yoffset = 50;
int clk_xoffset = 310;
// Print full weekday name
it.strftime(clk_xoffset, clk_yoffset, id(font_weekday), TextAlign::TOP_RIGHT, "%A", id(esptime).now());
// Print time in HH:MM format 335
it.strftime(clk_xoffset, clk_yoffset+105, id(font_time), TextAlign::BASELINE_RIGHT, "%H:%M", id(esptime).now());
// Print day of the month
it.strftime(clk_xoffset+100, clk_yoffset, id(font_day), TextAlign::TOP_RIGHT, "%d", id(esptime).now());
// Print abbreviated month name
it.strftime(clk_xoffset+100, clk_yoffset+105, id(font_month), TextAlign::BASELINE_RIGHT, "%b", id(esptime).now());
int sry_offset = 200; //sunrise/set y-offset
// Print sunrise
it.printf(60, sry_offset, id(font_mdi_medium), COLOR_RED, TextAlign::CENTER_RIGHT, "\U000F059C");
if(id(sun_sunrise).has_state ()) {
it.printf(65, sry_offset, id(font_small_bold), TextAlign::CENTER_LEFT, "%s", id(sun_sunrise).state.c_str());
}
// Print sunset
it.printf(425, sry_offset, id(font_mdi_medium), COLOR_RED, TextAlign::CENTER_RIGHT, "\U000F059B");
if(id(sun_sunset).has_state ()) {
it.printf(385, sry_offset, id(font_small_bold), TextAlign::CENTER_RIGHT, "%s", id(sun_sunset).state.c_str());
}
// Moonphae
//it.printf(265, sry_offset, id(font_mdi_medlarge), TextAlign::CENTER, "%s", moon_icon_map[id(moon_phase).state.c_str()].c_str());
it.line(30, sry_offset+30, 420, sry_offset+30);
// Weather Section
int wy_offset = sry_offset + 60; //weather y offset
// WEATHER STATE ICON
it.printf(80, wy_offset, id(font_mdi_large), COLOR_RED, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_now).state.c_str()].c_str());
// TEMPERATURE
it.printf(410, wy_offset, id(font_large_bold), TextAlign::TOP_RIGHT, "%2.0f°C", id(weather_temperature).state);
int forecast_yoffset = wy_offset+30+108;
int fcst_xstart = 90;
int fcst_offset = 90;
it.printf(fcst_xstart, forecast_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(weather_timestamp_0).state.c_str());
it.printf(fcst_xstart, forecast_yoffset+20, id(font_mdi_medium), COLOR_RED, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_0).state.c_str()].c_str());
it.printf(fcst_xstart, forecast_yoffset+72, id(font_small_bold), TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_0).state);
fcst_xstart += fcst_offset;
it.printf(fcst_xstart, forecast_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(weather_timestamp_1).state.c_str());
it.printf(fcst_xstart, forecast_yoffset+20, id(font_mdi_medium), COLOR_RED, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_1).state.c_str()].c_str());
it.printf(fcst_xstart, forecast_yoffset+72, id(font_small_bold), TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_1).state);
fcst_xstart += fcst_offset;
it.printf(fcst_xstart, forecast_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(weather_timestamp_2).state.c_str());
it.printf(fcst_xstart, forecast_yoffset+20, id(font_mdi_medium), COLOR_RED, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_2).state.c_str()].c_str());
it.printf(fcst_xstart, forecast_yoffset+72, id(font_small_bold), TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_2).state);
fcst_xstart += fcst_offset;
it.printf(fcst_xstart, forecast_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(weather_timestamp_3).state.c_str());
it.printf(fcst_xstart, forecast_yoffset+20, id(font_mdi_medium), COLOR_RED, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_3).state.c_str()].c_str());
it.printf(fcst_xstart, forecast_yoffset+72, id(font_small_bold), TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_3).state);
it.line(30, forecast_yoffset+102, 420, forecast_yoffset+102);
int drvtime_yoffset = forecast_yoffset+132;
int drvtime_xoffset = 40;
it.printf(drvtime_xoffset, drvtime_yoffset, id(font_mdi_medium), COLOR_RED, TextAlign::TOP_CENTER, "%s", weather_icon_map["car"].c_str());
it.printf(drvtime_xoffset+90, drvtime_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(travel_Best_name).state.c_str());
it.printf(drvtime_xoffset+90, drvtime_yoffset+30, id(font_small_bold), TextAlign::TOP_CENTER, "%2.0f min", id(travel_Best_time).state);
it.printf(drvtime_xoffset+210, drvtime_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(travel_ASML_name).state.c_str());
it.printf(drvtime_xoffset+210, drvtime_yoffset+30, id(font_small_bold), TextAlign::TOP_CENTER, "%2.0f min", id(travel_ASML_time).state);
it.printf(drvtime_xoffset+330, drvtime_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(travel_GGD_name).state.c_str());
it.printf(drvtime_xoffset+330, drvtime_yoffset+30, id(font_small_bold), TextAlign::TOP_CENTER, "%2.0f min", id(travel_GGD_time).state);
it.line(30, drvtime_yoffset+60, 420, drvtime_yoffset+60);
int waste_yoffset = drvtime_yoffset+90;
int waste_xoffset = 60;
it.printf(waste_xoffset, waste_yoffset, id(font_mdi_medium), COLOR_RED, TextAlign::TOP_CENTER, "%s", weather_icon_map["trash"].c_str());
it.printf(waste_xoffset+100, waste_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "Vandaag:");
it.printf(waste_xoffset+260, waste_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "Morgen:");
it.printf(waste_xoffset+95, waste_yoffset+30, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(afval_today).state.c_str());
it.printf(waste_xoffset+255, waste_yoffset+30, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(afval_tomorrow).state.c_str());
// it.line(30, 400, 440, 400);
// it.printf(30, 520, id(font_mdi_medium), color_white, TextAlign::TOP_CENTER, "%s", moon_icon_map[id(moon_phace_icon).state.c_str()].c_str());
captive_portal:
+90
View File
@@ -0,0 +1,90 @@
substitutions:
esp_name: "esp32-aqs1"
esphome:
name: ${esp_name}
comment: ${esp_name}
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret air_quality_woonkamer_api
ota:
password: !secret aqs1_ota_passwoord
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${esp_name} fallback
password: !secret fallback_password
captive_portal:
esp32_ble_tracker:
bluetooth_proxy:
# Example configuration entry
uart:
rx_pin: GPIO19
tx_pin: GPIO26
baud_rate: 9600
i2c:
sda: 21
scl: 22
scan: true
id: bus_a
# spi:
# clk_pin: GPIO18
# mosi_pin: GPIO23
# miso_pin: GPIO12
sensor:
- platform: pmsx003
type: PMSX003
pm_1_0:
name: "Particulate Matter <1.0µm Concentration"
pm_2_5:
name: "Particulate Matter <2.5µm Concentration"
pm_10_0:
name: "Particulate Matter <10.0µm Concentration"
update_interval: 60000ms
- platform: scd30
co2:
name: "Woonkamer CO2"
accuracy_decimals: 1
temperature:
name: "Woonkamer Temperature"
accuracy_decimals: 2
humidity:
name: "Woonkamer Humidity"
accuracy_decimals: 1
temperature_offset: 1.5 °C
address: 0x61
update_interval: 5s
- platform: sgp30
eco2:
name: "Woonkamer eCO2"
accuracy_decimals: 1
tvoc:
name: "Woonkamer TVOC"
accuracy_decimals: 1
store_baseline: yes
address: 0x58
update_interval: 1s
+67
View File
@@ -0,0 +1,67 @@
substitutions:
esp_name: "esp32-m5-bt-proxy"
esphome:
name: ${esp_name}
comment: ${esp_name}
esp32:
board: m5stack-atom
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret moon_light_api
ota:
password: !secret moon_ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${esp_name} fallback
password: !secret fallback_password
# captive_portal:
media_player:
- platform: i2s_audio
id: media_out
name: moon_sound
dac_type: external
i2s_lrclk_pin: GPIO33
i2s_dout_pin: GPIO22
i2s_bclk_pin: GPIO19
mode: mono
binary_sensor:
- platform: gpio
pin:
number: GPIO39
inverted: true
name: Moon_Button
light:
- platform: fastled_clockless
chipset: ws2812b
pin: GPIO25
num_leds: 1
rgb_order: GRB
name: "Moon_Light"
effects:
- random:
name: "Random"
transition_length: 4s
update_interval: 5s
- addressable_rainbow:
name: Rainbow Effect
speed: 10
width: 50
+178
View File
@@ -0,0 +1,178 @@
substitutions:
devicename: esp32-matrix
mypin: GPIO14
board: esp32dev
external_components:
- source:
type: git
url: https://github.com/lubeda/EsphoMaTrix
esphome:
name: $devicename
esp32:
board: $board
binary_sensor:
- platform: status
name: "$devicename Status"
font:
- file: fonts/monobit.ttf
id: ehmtx_font
size: 16
glyphs: |
!?"%()+*=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz€@<>/
# Enable logging
logger:
api:
encryption:
key: !secret matrix_led_api
services:
- service: alarm
variables:
icon_name: string
text: string
then:
lambda: |-
id(rgb8x32)->add_screen(icon_name, text, 7, true);
id(rgb8x32)->force_screen(icon_name);
- service: screen
variables:
icon_name: string
text: string
then:
lambda: |-
id(rgb8x32)->add_screen(icon_name, text, 5, false);
- service: brightness
variables:
brightness: int
then:
lambda: |-
id(rgb8x32)->set_brightness(brightness);
- service: status
then:
lambda: |-
id(rgb8x32)->get_status();
- service: del_screen
variables:
icon_name: string
then:
lambda: |-
id(rgb8x32)->del_screen(icon_name);
- service: indicator_on
variables:
r: int
g: int
b: int
then:
lambda: |-
id(rgb8x32)->set_indicator_color(r,g,b);
id(rgb8x32)->set_indicator_on();
- service: text_color
variables:
r: int
g: int
b: int
then:
lambda: |-
id(rgb8x32)->set_text_color(r,g,b);
- service: alarm_color
variables:
r: int
g: int
b: int
then:
lambda: |-
id(rgb8x32)->set_alarm_color(r,g,b);
- service: indicator_off
then:
lambda: |-
id(rgb8x32)->set_indicator_off();
ota:
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: $devicename
password: "InM2TlqVfJe4"
light:
- platform: neopixelbus
id: ehmtx_light
type: GRB
variant: WS2812
pin: $mypin
num_leds: 256
color_correct: [30%, 30%, 30%]
name: "$devicename Light"
restore_mode: ALWAYS_OFF
on_turn_on:
lambda: |-
id(ehmtx_display)->set_enabled(false);
on_turn_off:
lambda: |-
id(ehmtx_display)->set_enabled(true);
number:
- platform: template
name: "LED brightness"
min_value: 0
max_value: 255
step: 1
lambda: |-
return id(rgb8x32)->get_brightness();
set_action:
lambda: |-
id(rgb8x32)->set_brightness(x);
time:
- platform: homeassistant
id: ehmtx_time
display:
- platform: addressable_light
id: ehmtx_display
addressable_light_id: ehmtx_light
width: 32
height: 8
pixel_mapper: |-
if (x % 2 == 0) {
return (x * 8) + y;
}
return (x * 8) + (7 - y);
rotation: 0°
update_interval: 16ms
auto_clear_enabled: true
lambda: |-
id(rgb8x32)->tick();
id(rgb8x32)->draw();
web_server:
port: 80
ehmtx:
id: rgb8x32
show_clock: 4
show_screen: 6
display8x32: ehmtx_display
time: ehmtx_time
duration: 7
font_id: ehmtx_font
on_next_screen:
lambda: |-
ESP_LOGD("Trigger","Ole it works");
icons:
- file: icons/1350.gif
id: boot
+41
View File
@@ -0,0 +1,41 @@
substitutions:
esp_name: "esp32-s2-aqs-ikea"
esphome:
name: ${esp_name}
comment: ${esp_name}
esp32:
board: esp32-s2-saola-1
framework:
type: esp-idf
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret air_quality_zolder_api
ota:
password: !secret aqs2_ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot in case wifi connection fails
ap:
ssid: ${esp_name} fallback
password: !secret fallback_password
uart:
rx_pin: GPIO18
baud_rate: 9600
sensor:
- platform: pm1006
pm_2_5:
name: "Zolder Particulate Matter 2.5µm Concentration"
+69
View File
@@ -0,0 +1,69 @@
esphome:
name: esp8266-p1-meter
esp8266:
board: esp01_1m
# Enable logging
logger:
baud_rate: 0
level: DEBUG
# Enable Home Assistant API
api:
encryption:
key: "rl7h1lxgaeutnmINJ2mJ3WOWtzUX1AfebHnidDJJdXA="
ota:
password: "122afb0ac402638f10972a8d97256f6f"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Esp8266-P1-Meter"
password: "pKpA1k7rR6K9"
captive_portal:
font:
# gfonts://family[@weight]
- file: "gfonts://Roboto"
id: roboto
size: 20
uart:
rx_pin: GPIO13
baud_rate: 115200
rx_buffer_size: 1700
status_led:
pin: GPIO2
dsmr:
sensor:
- platform: dsmr
energy_delivered_tariff1:
name: Energy Consumed Tariff 1
text_sensor:
- platform: dsmr
identification:
name: "DSMR Identification"
p1_version:
name: "DSMR Version"
# Example configuration entry
i2c:
sda: 4
scl: 5
display:
- platform: ssd1306_i2c
model: "SSD1306 128x64"
address: 0x3C
lambda: |-
it.print(0, 0, id(roboto), "Hello World!");
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+92
View File
@@ -0,0 +1,92 @@
substitutions:
esp_name: "hvac-ir-sensor-display"
esphome:
name: ${esp_name}
comment: ${esp_name}
esp8266:
board: nodemcuv2
# Enable logging
logger:
baud_rate: 0
uart:
rx_pin: GPIO3
tx_pin: GPIO1
baud_rate: 9600
# Enable Home Assistant API
api:
encryption:
key: !secret hvac_woonkamer_api
ota:
password: !secret hvac_woonkamer_ota
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${esp_name} fallback
password: !secret fallback_password
time:
- platform: homeassistant
id: homeassistant_time
remote_transmitter:
pin: GPIO14
carrier_duty_percent: 50%
status_led:
pin: GPIO4
remote_receiver:
id: rcvr
pin:
number: GPIO5
inverted: true
# mode:
# input: true
# pullup: true
tolerance: 55%
dump: all
climate:
- platform: fujitsu_general
name: "Airco Woonkamer"
receiver_id: rcvr
# visual:
# min_temperature: 18
# max_temperature: 25
# temperature_Step: 1
tuya:
status_pin: GPIO16
time_id: homeassistant_time
sensor:
- platform: "tuya"
name: "Temperature"
sensor_datapoint: 101
unit_of_measurement: "°C"
device_class: "temperature"
state_class: "measurement"
filters:
- multiply: 0.1
accuracy_decimals: 1
- platform: "tuya"
name: "humidity"
sensor_datapoint: 102
unit_of_measurement: "%rh"
device_class: "humidity"
state_class: "measurement"
accuracy_decimals: 1
+75
View File
@@ -0,0 +1,75 @@
substitutions:
esp_name: "hvac-ir-slaapkamer"
esphome:
name: ${esp_name}
esp8266:
board: nodemcuv2
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret hvac_slaapkamer_api
ota:
password: !secret hvac_slaapkamer_ota
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${esp_name} fallback
password: !secret fallback_password
i2c:
- id: bus_a
sda: GPIO4
scl: GPIO0
scan: false
remote_transmitter:
pin: GPIO14
carrier_duty_percent: 50%
#status_led:
# pin: GPIO2
remote_receiver:
id: rcvr
pin:
number: GPIO5
inverted: true
# mode:
# input: true
# pullup: true
tolerance: 55%
dump: all
sensor:
- platform: sht3xd
temperature:
name: "Temperatuur slaapkamer (airco)"
id: sht_temp
humidity:
name: "Humidity slaapkamer (airco)"
address: 0x44
update_interval: 60s
climate:
- platform: fujitsu_general
name: "Airco Slaapkamer"
receiver_id: rcvr
sensor: sht_temp
# visual:
# min_temperature: 18
# max_temperature: 25
# temperature_Step: 1
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 443 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

+95
View File
@@ -0,0 +1,95 @@
substitutions:
esp_name: "vw-bus"
esphome:
name: ${esp_name}
comment: ${esp_name}
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret vw_bus_api
ota:
password: !secret vw_bus_ota
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${esp_name} fallback
password: !secret fallback_password
captive_portal:
output:
- platform: ledc
pin: GPIO5
id: INTERIOUR
- platform: ledc
pin: GPIO18
id: DIM_FL
- platform: ledc
pin: GPIO27
id: DIM_FR
- platform: ledc
pin: GPIO4
id: BLINK_FL
- platform: ledc
pin: GPIO17
id: BLINK_FR
- platform: ledc
pin: GPIO14
id: DIM_RL
- platform: ledc
pin: GPIO26
id: DIM_RR
light:
- platform: monochromatic
output: INTERIOUR
name: "Interiour lights"
- platform: monochromatic
output: DIM_FL
name: "Dim FL"
- platform: monochromatic
output: DIM_FR
name: "Dim FR"
- platform: monochromatic
output: DIM_RL
name: "Dim RL"
effects:
- pulse:
transition_length: 0.3s
update_interval: 0.5s
- platform: monochromatic
output: DIM_RR
name: "Dim RR"
effects:
- pulse:
transition_length: 0.3s
update_interval: 0.5s
- platform: monochromatic
output: BLINK_FL
name: "Blink FL"
effects:
- pulse:
transition_length: 0.3s
update_interval: 0.5s
- platform: monochromatic
output: BLINK_FR
name: "Blink FR"
effects:
- pulse:
transition_length: 0.3s
update_interval: 0.5s