periodic push

This commit is contained in:
2023-06-29 16:14:36 +02:00
parent c948b95622
commit 831f676068
267 changed files with 17705 additions and 10864 deletions

View File

@@ -0,0 +1,77 @@
substitutions:
device_name: "aqs-woonkamer2"
friendly_name: "AQS-woonkamer2"
comment: "esp32, pm, co2, temp, hum, occup, btprox"
location: "woonkamer"
api_password: !secret air_quality_woonkamer_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret air_quality_woonkamer_ip
pin_status: GPIO2
pin_sda: GPIO21
pin_scl: GPIO22
pin_pm_rx: GPIO18
pin_pm_tx: GPIO19
pin_ld_tx: GPIO17
pin_ld_rx: GPIO16
pin_leds: GPIO23
packages:
board: !include boards/esp32_wroom_arduino.yaml
i2c: !include interfaces/i2c_a.yaml
device_base: !include common/common.yaml
connection: !include common/wifi.yaml
status: !include templates/status.yaml
logger: !include templates/nologger.yaml
bt_proxy: !include templates/ble_proxy.yaml
#sensors
# mmwave: !include sensors/ld2420.yaml
pmsc: !include sensors/pmsx0003.yaml
co2: !include sensors/scd30.yaml
tvoc: !include sensors/sgp30.yaml
light:
- platform: neopixelbus
type: GRBW
variant: ws2812X
pin: ${pin_leds}
num_leds: 3
name: "${device_name}_RGB_Light"
id: RGB_Light
effects:
- random:
name: "Random"
transition_length: 4s
update_interval: 5s
- addressable_rainbow:
name: Rainbow Effect
speed: 100
width: 2
- platform: partition
name: "Top_LED"
segments:
# Use first 10 LEDs from the light with ID light1
- id: RGB_Light
from: 2
to: 2
- platform: partition
name: "Middle_LED"
segments:
# Use first 10 LEDs from the light with ID light1
- id: RGB_Light
from: 1
to: 1
- platform: partition
name: "Bottom_LED"
segments:
# Use first 10 LEDs from the light with ID light1
- id: RGB_Light
from: 0
to: 0

36
esphome/aqs_zolder.yaml Normal file
View File

@@ -0,0 +1,36 @@
substitutions:
device_name: "esp32-s2-aqs-ikea"
friendly_name: "AQS-zolder"
comment: "esp32, pm, co2, display, btprox"
location: "zolder"
api_password: !secret air_quality_zolder_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret air_quality_zolder_ip
pin_pir: GPIO23
pin_status: GPIO2
pin_sda: GPIO21
pin_scl: GPIO22
pin_pm_rx: GPIO18
pin_ld_tx: GPIO17
pin_ld_rx: GPIO16
packages:
board: !include boards/esp32_wroom_arduino.yaml
i2c: !include interfaces/i2c_a.yaml
device_base: !include common/common.yaml
connection: !include common/wifi.yaml
status: !include templates/status.yaml
logger: !include templates/nologger.yaml
bt_proxy: !include templates/ble_proxy.yaml
#sensors
mmwave: !include sensors/ld2410.yaml
pms: !include sensors/pm1006.yaml
co2: !include sensors/scd30.yaml
tvoc: !include sensors/sgp30.yaml
pir: !include sensors/pir_raw.yaml
sensor_wifi: !include sensors/wifi.yaml

2
esphome/boards/esp01.yaml Executable file
View File

@@ -0,0 +1,2 @@
esp8266:
board: esp01_1m

2
esphome/boards/esp12f.yaml Executable file
View File

@@ -0,0 +1,2 @@
esp8266:
board: nodemcuv2

View File

@@ -0,0 +1,4 @@
esp32:
board: m5stack-atom
framework:
type: arduino

View File

@@ -0,0 +1,4 @@
esp32:
board: esp32-s2-saola-1
framework:
type: arduino

View File

@@ -0,0 +1,4 @@
esp32:
board: esp32dev
framework:
type: arduino

0
esphome/common/api.yaml Executable file
View File

9
esphome/common/bluetooth.yaml Executable file
View File

@@ -0,0 +1,9 @@
---
esp32_ble_tracker:
scan_parameters:
interval: 1100ms # default 320ms
window: 1100ms # default 30ms
active: true
bluetooth_proxy:
active: true

4
esphome/common/common.yaml Executable file
View File

@@ -0,0 +1,4 @@
esphome:
name: ${device_name}
comment: ${comment}
friendly_name: ${friendly_name}

19
esphome/common/deepsleep.yaml Executable file
View File

@@ -0,0 +1,19 @@
---
deep_sleep:
id: deep_sleep_control
run_duration: ${run_duration}
sleep_duration: ${sleep_duration}
wakeup_pin:
number: ${pin_wake}
inverted: true
mode:
input: true
# pullup: true
# Wake Button
binary_sensor:
- platform: gpio
name: '${device_name} Wake Button'
pin: ${pin_wake}
internal: true
setup_priority: 1000

0
esphome/common/ota.yaml Executable file
View File

24
esphome/common/waterpump.yaml Executable file
View File

@@ -0,0 +1,24 @@
---
switch:
- platform: gpio
pin: GPIO19
name: 'Water Pump'
id: water
climate:
- platform: bang_bang
visual:
min_temperature: 0
max_temperature: 100
temperature_step: 1
name: ${device_name} Regler
sensor: ${device_name}_moisture
default_target_temperature_low: 20
default_target_temperature_high: 70
heat_action:
- switch.turn_on: water
idle_action:
- switch.turn_off: water

51
esphome/common/wifi.yaml Executable file
View File

@@ -0,0 +1,51 @@
# Enable Home Assistant API
api:
encryption:
key: ${api_password}
ota:
password: ${ota_password}
wifi:
ssid: ${wifi_ssid}
password: ${wifi_password}
manual_ip:
static_ip: ${ip}
gateway: ${gateway}
subnet: ${subnet}
dns1: 192.169.2.15
dns2: 1.1.1.1
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${device_name}
password: ${wifi_password}
captive_portal:
sensor:
- platform: wifi_signal
name: "${device_name} WiFi Signal "
update_interval: 10s
text_sensor:
- platform: wifi_info
ssid:
name: "${device_name} Connected SSID"
id: ssid
icon: mdi:wifi-strength-2
entity_category: diagnostic
bssid:
name: "${device_name} Connected BSSID"
id: bssid
icon: mdi:wifi-strength-2
entity_category: diagnostic
mac_address:
name: "${device_name} WiFi Mac Address"
id: macaddress
icon: mdi:wifi-strength-2
entity_category: diagnostic

23
esphome/common/wifi_nosens.yaml Executable file
View File

@@ -0,0 +1,23 @@
api:
encryption:
key: ${api_password}
ota:
password: ${ota_password}
wifi:
ssid: ${wifi_ssid}
password: ${wifi_password}
manual_ip:
static_ip: ${ip}
gateway: ${gateway}
subnet: ${subnet}
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${device_name}
password: ${wifi_password}
captive_portal:

View File

@@ -1,500 +0,0 @@
#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);
}
}

View File

@@ -1,422 +0,0 @@
#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

View File

@@ -1,37 +0,0 @@
#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();
}
}
}
}

View File

@@ -1,121 +0,0 @@
#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;
}
}

View File

@@ -1,144 +0,0 @@
#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);
}
}
}
}

View File

@@ -1,609 +0,0 @@
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)

View File

@@ -1,27 +0,0 @@
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=[])

View File

@@ -1,33 +0,0 @@
#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

View File

@@ -1,22 +0,0 @@
#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;
};
}

View File

@@ -1,19 +1,27 @@
substitutions:
esp_name: "esp32-75epaper-keuken"
device_name: "esp32-75epaper-keuken"
friendly_name: "Epaper display keuken"
comment: "7,5inch epaper displau keuken"
location: "Keuken"
api_password: !secret eink_display_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret eink_display_ip
packages:
board: !include boards/esp32_wroom_arduino.yaml
connection: !include common/wifi_nosens.yaml
logger: !include templates/logger.yaml
esphome:
name: ${esp_name}
comment: ${esp_name}
name: ${device_name}
comment: ${comment}
friendly_name: ${friendly_name}
includes:
- common.h
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
- include/epaper75.h
external_components:
- source:
@@ -22,25 +30,6 @@ external_components:
ref: waveshare-color-2022.6
components: [waveshare_epaper]
# Enable Home Assistant API
api:
encryption:
key: !secret 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
@@ -198,7 +187,7 @@ font:
sensor:
- platform: template
name: "${esp_name} Last Update"
name: "${device_name} Last Update"
device_class: timestamp
id: display_last_update
@@ -210,66 +199,25 @@ sensor:
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;'
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_temperature_0, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_temperature_1, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_temperature_2, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_temperature_3, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: travel_Best_time, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: travel_ASML_time, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: travel_euretco_time, entity_id: sensor.e_ink_display_data }}
- platform: wifi_signal
id: sensor_wifi_signal
name: "${esp_name} WiFi"
name: "${device_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"
name: "${device_name} Wi-Fi Signal Percentage"
icon: "mdi:wifi"
unit_of_measurement: "%"
update_interval: never
@@ -287,7 +235,7 @@ sensor:
}
- platform: template
name: "${esp_name} Recorded Display Refresh"
name: "${device_name} Recorded Display Refresh"
lambda: 'return id(recorded_display_refresh);'
unit_of_measurement: "refreshes"
@@ -296,110 +244,21 @@ text_sensor:
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;'
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_condition_now, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_condition_0, entity_id: sensor.e_ink_display_data}}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_timestamp_0, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_condition_1, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_timestamp_1, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_condition_2, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_timestamp_2, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_condition_3, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_timestamp_3, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: afval_today, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: afval_tomorrow, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: travel_Best_name, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: travel_ASML_name, entity_id: sensor.e_ink_display_data }}
- !include { file: sensors/homeassistant.yaml, vars: { id: travel_euretco_name, entity_id: sensor.e_ink_display_data }}
- 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
@@ -432,19 +291,19 @@ text_sensor:
- platform: wifi_info
ssid:
name: "${esp_name} Connected SSID"
name: "${device_name} Connected SSID"
id: ssid
icon: mdi:wifi-strength-2
entity_category: diagnostic
bssid:
name: "${esp_name} Connected BSSID"
name: "${device_name} Connected BSSID"
id: bssid
icon: mdi:wifi-strength-2
entity_category: diagnostic
mac_address:
name: "${esp_name} WiFi Mac Address"
name: "${device_name} WiFi Mac Address"
id: macaddress
icon: mdi:wifi-strength-2
entity_category: diagnostic
@@ -455,7 +314,7 @@ text_sensor:
type: sunrise
format: "%H:%M"
internal: true
update_interval: never
update_interval: 12h
- platform: sun
id: sunset
@@ -463,7 +322,7 @@ text_sensor:
type: sunset
format: "%H:%M"
internal: true
update_interval: never
update_interval: 12h
# Define colors
# This design is white on black so this is necessary.
@@ -502,7 +361,7 @@ display:
model: 7.50inv2b
#model: 7.50inv2
#model: 7.50inV2alt
update_interval: 1h
update_interval: 30min
id: eink_display
rotation: 90°
lambda: |-
@@ -619,8 +478,8 @@ display:
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.printf(drvtime_xoffset+330, drvtime_yoffset, id(font_small_bold), TextAlign::TOP_CENTER, "%s", id(travel_euretco_name).state.c_str());
it.printf(drvtime_xoffset+330, drvtime_yoffset+30, id(font_small_bold), TextAlign::TOP_CENTER, "%2.0f min", id(travel_euretco_time).state);
it.line(30, drvtime_yoffset+60, 420, drvtime_yoffset+60);

View File

@@ -1,90 +0,0 @@
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

View File

@@ -1,74 +0,0 @@
substitutions:
esp_name: "esp32-atom-lite"
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 esp32-atomo-lite_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
captive_portal:
bluetooth_proxy:
active: true
binary_sensor:
- platform: gpio
pin:
number: GPIO39
inverted: true
name: ${esp_name}_Button
id: button
on_click:
then:
- if:
condition:
- light.is_on: RGB_Light
then:
- light.turn_off: RGB_Light
else:
- light.turn_on: RGB_Light
light:
- platform: fastled_clockless
chipset: ws2812b
pin: GPIO25
num_leds: 1
rgb_order: GRB
id: RGB_Light
name: "${esp_name}_RGB_Light"
effects:
- random:
name: "Random"
transition_length: 20s
update_interval: 5s
- addressable_rainbow:
name: Rainbow Effect
speed: 100
width: 50

View File

@@ -1,67 +0,0 @@
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

View File

@@ -1,71 +0,0 @@
substitutions:
esp_name: "esp32-s2-aqs-ikea"
esphome:
name: ${esp_name}
comment: ${esp_name}
esp32:
board: esp32-s2-saola-1
framework:
type: arduino
# 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
i2c:
sda: 21
scl: 17
scan: true
id: bus_a
sensor:
- platform: pm1006
pm_2_5:
name: "Zolder Particulate Matter 2.5µm Concentration"
- platform: scd30
co2:
name: "zolder CO2"
accuracy_decimals: 1
temperature:
name: "zolder Temperature"
accuracy_decimals: 2
humidity:
name: "zolder Humidity"
accuracy_decimals: 1
temperature_offset: 1.5 °C
address: 0x61
update_interval: 5s
- platform: sgp30
eco2:
name: "zolder eCO2"
accuracy_decimals: 1
tvoc:
name: "zolder TVOC"
accuracy_decimals: 1
store_baseline: yes
address: 0x58
update_interval: 1s

View File

@@ -1,72 +0,0 @@
substitutions:
esp_name: "esp32-s2-aqs-ikea2"
esphome:
name: ${esp_name}
comment: ${esp_name}
esp32:
board: esp32-s2-saola-1
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "f/PrPgYPV3TbX413d8ad8RgNBwrD7ie7rR6o+jlAiRQ="
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
i2c:
sda: 21
scl: 17
scan: true
id: bus_a
sensor:
- platform: pm1006
pm_2_5:
name: "${esp_name} PM2.5"
- platform: scd30
co2:
name: "${esp_name} CO2"
accuracy_decimals: 1
temperature:
name: "${esp_name} Temperature"
accuracy_decimals: 2
humidity:
name: "${esp_name} Humidity"
accuracy_decimals: 1
temperature_offset: 1.5 °C
address: 0x61
update_interval: 5s
- platform: sgp30
eco2:
name: "${esp_name} eCO2"
accuracy_decimals: 1
tvoc:
name: "${esp_name} TVOC"
accuracy_decimals: 1
store_baseline: yes
address: 0x58
update_interval: 1s

View File

@@ -1,140 +0,0 @@
substitutions:
esp_name: "esp8266-p1-meter"
esphome:
name: ${esp_name}
comment: ${esp_name}
esp8266:
board: esp01_1m
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret esp_P1_api
ota:
password: !secret esp_P1_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:
font:
- file: 'fonts/GothamRnd-Book.ttf'
id: font5
size: 18
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_time
size: 30
glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':']
# uart:
# rx_pin: GPIO13
# baud_rate: 115200
# rx_buffer_size: 1700
status_led:
pin:
number: GPIO2
inverted: true
# 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"
output:
- platform: gpio
pin: GPIO4
id: lcdbacklight
switch:
- platform: output
name: "LCD LED"
output: lcdbacklight
id: switch_lcdbacklight
spi:
clk_pin: GPIO14
mosi_pin: GPIO13
time:
- platform: homeassistant
id: esptime
display:
- platform: st7735
model: INITR_MINI160X80
reset_pin: GPIO16
cs_pin: GPIO5
dc_pin: GPIO15
rotation: 270
device_width: 82
device_height: 161
col_start: 0
row_start: 0
eight_bit_color: true
update_interval: 5s
lambda: |-
//it.rectangle(0, 2, 160, 80, id(my_red));
it.strftime(2, 2, id(font_time), TextAlign::BASELINE_RIGHT, "%H:%M", id(esptime).now());
// this line = the text is centered 80 pixels in from left and 59 pixels down
color:
- id: my_red
red: 100%
green: 100%
blue: 0%
- id: my_yellow
red: 100%
green: 0%
blue: 0%
- id: my_green
red: 100%
green: 0%
blue: 100%
- id: my_blue
red: 0%
green: 100%
blue: 100%
- id: my_cyan
red: 0%
green: 0%
blue: 100%
- id: my_magenta
red: 0%
green: 100%
blue: 0%
- id: my_white
red: 0%
green: 0%
blue: 0%
- id: my_gray
red: 50%
green: 50%
blue: 50%
- id: my_black
red: 100%
green: 100%
blue: 100%

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,92 +0,0 @@
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

View File

@@ -1,75 +1,55 @@
substitutions:
esp_name: "hvac-ir-slaapkamer"
device_name: "hvac-ir-slaapkamer"
friendly_name: "HVAC-IR-RGB slaapkamer"
comment: "esp8266, blue ESP12 module, IR, RGBW"
location: "Slaapkamer"
api_password: !secret hvac_slaapkamer_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret hvac_slaapkamer_ip
pin_ir_tx: GPIO14
pin_ir_rx: GPIO5
pin_button: GPIO13
pin_leds: GPIO03
pin_status: GPIO4
esphome:
name: ${esp_name}
packages:
board: !include boards/esp12f.yaml
ir: !include interfaces/ir.yaml
connection: !include common/wifi.yaml
device_base: !include common/common.yaml
climate: !include templates/climate_nosens.yaml
#status: !include templates/status.yaml
logger: !include templates/nologger.yaml
button: !include templates/button.yaml
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
light:
- platform: neopixelbus
id: leds
method:
type: esp8266_dma
type: GRBW
variant: WS2812X
pin: ${pin_leds}
num_leds: 114
name: "NeoPixel Light"
effects:
- random:
name: "Random"
transition_length: 4s
update_interval: 5s
- addressable_rainbow:
name: Rainbow Effect
speed: 20
width: 17
#GPIO4 - status
#GPIO5 - IR rcvr
#GPIO14 - IR tx
#GPIO13 - button
#GPIO15 - connect to GND
#GPIO03 - WS2812 LEDS

View File

@@ -0,0 +1,34 @@
substitutions:
device_name: "hvac-ir-dsplay"
friendly_name: "IR display sensor woonkamer"
comment: "ESP8266, tuya-hack, display, sensor, IR"
location: "woonkamer"
api_password: !secret hvac_woonkamer_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret hvac_woonkamer_ip
hvac_sensor: tuya_temp
pin_ir_tx: GPIO14
pin_ir_rx: GPIO5
pin_tuya_rx: GPIO3
pin_tuya_tx: GPIO1
pin_tuya_status: GPIO16
pin_button: GPIO13
pin_status: GPIO4
packages:
board: !include boards/esp12f.yaml
ir: !include interfaces/ir.yaml
connection: !include common/wifi.yaml
device_base: !include common/common.yaml
logger: !include templates/nologger.yaml
status: !include templates/status.yaml
button: !include templates/button.yaml
climate: !include templates/climate_sens.yaml
temp: !include sensors/tuya_mcu.yaml
sensor_wifi: !include sensors/wifi.yaml

View File

@@ -0,0 +1,35 @@
substitutions:
device_name: "hvac-ir-zolder"
friendly_name: "Airco IR Zolder"
comment: "ESP8266, Tuya-hack, IR"
location: "zolder"
api_password: !secret hvac_zolder_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret hvac_zolder_ip
hvac_sensor: sht_temp
pin_ir_tx: GPIO14
pin_ir_rx: GPIO5
pin_button: GPIO13
pin_leds: GPIO03
pin_status: GPIO4
pin_sda: GPIO4
pin_scl: GPIO0
packages:
board: !include boards/esp12f.yaml
ir: !include interfaces/ir.yaml
i2c_a: !include interfaces/i2c_a.yaml
connection: !include common/wifi.yaml
device_base: !include common/common.yaml
climate: !include templates/climate_sens.yaml
status: !include templates/status.yaml
logger: !include templates/logger.yaml
sht3x: !include sensors/sht3x.yaml
sensor_wifi: !include sensors/wifi.yaml

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
esphome/images/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
esphome/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
esphome/images/weather.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
esphome/images/youtube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

352
esphome/include/bcm500ds.h Executable file
View File

@@ -0,0 +1,352 @@
#include "esphome.h"
// protocol
#define TUYA_COVER_MAX_LEN 640 // Max length of message value
// #define TUYA_COVER_MAX_LEN 256 // Max length of message value
#define TUYA_COVER_BUFFER_LEN 6 // Length of serial buffer for header + type + length
#define TUYA_COVER_HEADER_LEN 2 // Length of fixed header
// enable/disable reversed motor direction
// Normal = header (55AA) + (00060005) + 050100010011 "(55AA00060005050100010011)
// Reversed = header (55AA) + (00060005) + 050100010112 "(55AA00060005050100010112)"
#define TUYA_COVER_DISABLE_REVERSING { 0x69, 0x01, 0x00, 0x01, 0x00 } //dpid = 105, type = bool, len = 1, value = disable
#define TUYA_COVER_ENABLE_REVERSING { 0x69, 0x01, 0x00, 0x01, 0x01 } //dpid = 105, type = bool, len = 1, value = enable
// Curtain commands
// Open = header (55AA) + (00060005) + 6604000100 "(55aa000600056604000100)"
// Close = header (55AA) + (00060005) + 6604000101 "(55aa000600056604000101)"
// Stop = header (55AA) + (00060005) + 6604000102 "(55AA000600056604000102)"
#define TUYA_COVER_OPEN { 0x66, 0x04, 0x00, 0x01, 0x00 } //dpid = 101, type = enum, len = 1, value = OPEN
#define TUYA_COVER_CLOSE { 0x66, 0x04, 0x00, 0x01, 0x01 } //dpid = 101, type = enum, len = 1, value = CLOSE
#define TUYA_COVER_STOP { 0x66, 0x04, 0x00, 0x01, 0x02 } //dpid = 101, type = enum, len = 1, value = STOP
#define TUYA_COVER_SET_POSITION { 0x65, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 } //"65020004000000" //dpid = 2, type = value, len = 4, value = 0x000000 + 1 byte (0x00-0x64)
static const char *TAG = "TUYACOVER";
static const uint16_t TUYA_COVER_HEADER = 0x55AA;
//static const uint16_t TUYA_COVER_VERSION = 0x03;
static const uint16_t TUYA_COVER_VERSION = 0x00;
const uint8_t tuya_cover_enable_reversing[] = TUYA_COVER_ENABLE_REVERSING;
const uint8_t tuya_cover_disable_reversing[] = TUYA_COVER_DISABLE_REVERSING;
const uint8_t tuya_cover_open[] = TUYA_COVER_OPEN;
const uint8_t tuya_cover_close[] = TUYA_COVER_CLOSE;
const uint8_t tuya_cover_stop[] = TUYA_COVER_STOP;
static uint8_t tuya_cover_pos[] = TUYA_COVER_SET_POSITION;
#define HEARTBEAT_INTERVAL_MS 10000
unsigned long previousHeartbeatMillis = 0;
struct TUYACOVERCommand
{
uint16_t header;
uint8_t version;
uint8_t command;
uint16_t length;
uint8_t value[TUYA_COVER_MAX_LEN];
uint8_t checksum;
};
struct TUYACOVERMessage
{
uint8_t dpid;
uint8_t type;
uint16_t len;
uint8_t value[TUYA_COVER_MAX_LEN - 4]; //Subtract dpid, type, len
};
enum TUYACOVERCommandType
{
TUYA_COVER_HEARTBEAT = 0x00,
TUYA_COVER_COMMAND = 0x06,
TUYA_COVER_RESPONSE = 0x07,
TUYA_COVER_QUERY_STATUS = 0x08
};
enum TUYACOVERdpidType
{
TUYA_COVER_DPID_POSITION= 0x65,
TUYA_COVER_DPID_DIRECTION = 0x64,
TUYA_COVER_DPID_UNKNOWN = 0x67,
TUYA_COVER_DPID_ERROR = 0x6E
};
// Variables
TUYACOVERCommand command_{TUYA_COVER_HEADER, TUYA_COVER_VERSION, 0, 0, {}, 0};
uint8_t uart_buffer_[TUYA_COVER_BUFFER_LEN]{0};
// Forward declarations
bool read_command();
void write_command(TUYACOVERCommandType command, const uint8_t *value, uint16_t length);
uint8_t checksum();
/*
* Attempt to read an entire command from the serial UART into the command struct.
* Will fail early if unable to find the two-byte header in the current
* data stream. If the header is found, it will contine to read the complete
* TLV+checksum sequence off the port. If the entire sequence can be read
* and the checksum is valid, it will return true.
*/
bool read_command()
{
// Shift bytes through until we find a valid header
bool valid_header = false;
while (Serial.available() >= 1)
{
uart_buffer_[0] = uart_buffer_[1];
uart_buffer_[1] = Serial.read();
command_.header = (uart_buffer_[0] << 8) + uart_buffer_[1];
if (command_.header == TUYA_COVER_HEADER)
{
valid_header = true;
break;
}
}
// Read the next 4 bytes (Version, Command, Data length)
// Read n bytes (Data length)
// Read the checksum byte
if (valid_header)
{
Serial.readBytes(uart_buffer_ + TUYA_COVER_HEADER_LEN, TUYA_COVER_BUFFER_LEN - TUYA_COVER_HEADER_LEN);
command_.version = uart_buffer_[2];
command_.command = uart_buffer_[3];
command_.length = (uart_buffer_[4] << 8) + uart_buffer_[5];
ESP_LOGV(TAG, "RX: Header = 0x%04X, Version = 0x%02X, Command = 0x%02X, Data length = 0x%04X", command_.header, command_.version, command_.command, command_.length);
if (command_.length < TUYA_COVER_MAX_LEN)
{
Serial.readBytes(command_.value, command_.length);
ESP_LOGV(TAG, "RX_RAW:");
for (size_t i = 0; i < command_.length; i++)
{
ESP_LOGV(TAG, "%02d: 0x%02X", i, command_.value[i]);
}
while (Serial.available() == 0) // Dirty
{
//Wait
}
command_.checksum = Serial.read();
ESP_LOGV(TAG, "RX_CHK: 0x%02X", command_.checksum);
uint8_t calc_checksum = checksum();
if (calc_checksum == command_.checksum)
{
// Clear buffer contents to start with beginning of next command
memset(uart_buffer_, 0, TUYA_COVER_BUFFER_LEN);
return true;
}
else
{
memset(uart_buffer_, 0, TUYA_COVER_BUFFER_LEN);
ESP_LOGE(TAG, "Checksum error: Read = 0x%02X != Calculated = 0x%02X", command_.checksum, calc_checksum);
}
}
else
{
memset(uart_buffer_, 0, TUYA_COVER_BUFFER_LEN);
ESP_LOGE(TAG, "Command length exceeds limit: %d >= %d", command_.length, TUYA_COVER_MAX_LEN);
}
}
// Do not clear buffer to allow for resume in case of reading partway through header RX
return false;
}
/*
* Store the given type, value, and length into the command struct and send
* it out the serial port. Automatically calculates the checksum as well.
*/
void write_command(TUYACOVERCommandType command, const uint8_t *value, uint16_t length)
{
// Copy params into command struct
command_.header = TUYA_COVER_HEADER;
command_.version = TUYA_COVER_VERSION;
command_.command = command;
command_.length = length;
ESP_LOGV(TAG, "TX: Header = 0x%04X, Version = 0x%02X, Command = 0x%02X, Data length = 0x%04X", command_.header, command_.version, command_.command, command_.length);
memcpy(&command_.value, value, length);
ESP_LOGV(TAG, "TX_RAW");
for (size_t i = 0; i < command_.length; i++)
{
ESP_LOGV(TAG, "%02d: 0x%02X", i, command_.value[i]);
}
// Copy struct values into buffer, converting longs to big-endian
uart_buffer_[0] = command_.header >> 8;
uart_buffer_[1] = command_.header & 0xFF;
uart_buffer_[2] = command_.version;
uart_buffer_[3] = command_.command;
uart_buffer_[4] = command_.length >> 8;
uart_buffer_[5] = command_.length & 0xFF;
command_.checksum = checksum();
ESP_LOGV(TAG, "TX_CHK: 0x%02X", command_.checksum);
// Send buffer out via UART
Serial.write(uart_buffer_, TUYA_COVER_BUFFER_LEN);
Serial.write(command_.value, command_.length);
Serial.write(command_.checksum);
// Clear buffer contents to avoid re-reading our own payload
memset(uart_buffer_, 0, TUYA_COVER_BUFFER_LEN);
}
/*
* Calculate checksum from current UART buffer (header+type+length) plus command value.
*/
uint8_t checksum()
{
uint8_t checksum = 0;
for (size_t i = 0; i < TUYA_COVER_BUFFER_LEN; i++)
{
checksum += uart_buffer_[i];
}
for (size_t i = 0; i < command_.length; i++)
{
checksum += command_.value[i];
}
return checksum;
}
class CustomCurtain : public Component, public Cover
{
protected:
public:
void setup() override
{
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = QUERY_STATUS");
write_command(TUYA_COVER_QUERY_STATUS, 0, 0);
}
CoverTraits get_traits() override
{
auto traits = CoverTraits();
traits.set_is_assumed_state(false);
traits.set_supports_position(true);
traits.set_supports_tilt(false);
return traits;
}
void control(const CoverCall &call) override
{
if (call.get_position().has_value())
{
// Write pos (range 0-1) to cover
// Cover pos (range 0x00-0x64) (closed - open)
uint8_t pos = (100-(*call.get_position() * 100));
ESP_LOGV(TAG, "POS = %d", pos);
switch (pos)
{
case 0:
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = CLOSE");
write_command(TUYA_COVER_COMMAND, tuya_cover_close, sizeof(tuya_cover_close));
break;
case 100:
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = OPEN");
write_command(TUYA_COVER_COMMAND, tuya_cover_open, sizeof(tuya_cover_open));
break;
default:
tuya_cover_pos[7] = pos;
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = POS = %d%%", pos);
write_command(TUYA_COVER_COMMAND, tuya_cover_pos, sizeof(tuya_cover_pos));
break;
}
// publish_state only when position is confirmed in loop()
}
if (call.get_stop())
{
// User requested cover stop
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = STOP");
write_command(TUYA_COVER_COMMAND, tuya_cover_stop, sizeof(tuya_cover_stop));
}
}
void loop() override
{
unsigned long currentHeartbeatMillis = millis();
if (currentHeartbeatMillis - previousHeartbeatMillis >= HEARTBEAT_INTERVAL_MS)
{
previousHeartbeatMillis += HEARTBEAT_INTERVAL_MS;
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = HEARTBEAT");
write_command(TUYA_COVER_HEARTBEAT, 0, 0);
}
bool have_message = read_command();
if(have_message && command_.command == TUYA_COVER_HEARTBEAT)
{
ESP_LOGI(TAG, "TUYA_COVER_RESPONSE = %s", (command_.value[0] == 0) ? "FIRST_HEARTBEAT" : "HEARTBEAT");
}
else if (have_message && command_.command == TUYA_COVER_RESPONSE)
{
switch (command_.value[0])
{
case TUYA_COVER_DPID_POSITION:
ESP_LOGI(TAG, "TUYA_COVER_DPID_POSITION = %d%%", command_.value[7]);
this->position = (1-((command_.value[7]) / 100.0f));
this->publish_state();
break;
case TUYA_COVER_DPID_DIRECTION:
ESP_LOGI(TAG, "TUYA_COVER_DPID_DIRECTION = 0x%02X", command_.value[4]);
break;
case TUYA_COVER_DPID_UNKNOWN:
ESP_LOGI(TAG, "TUYA_COVER_DPID_UNKNOWN ENUM = 0x%02X", command_.value[4]);
break;
case TUYA_COVER_DPID_ERROR:
ESP_LOGI(TAG, "TUYA_COVER_DPID_ERROR BITMAP = 0x%02X", command_.value[4]);
break;
default:
break;
}
}
}
};
class CustomAPI : public Component, public CustomAPIDevice
{
public:
void setup() override
{
register_service(&CustomAPI::setStatusReport, "get_status_report");
register_service(&CustomAPI::setMotorNormal, "set_motor_normal");
register_service(&CustomAPI::setMotorReversed, "set_motor_reversed");
register_service(&CustomAPI::sendCommand, "send_command", {"data"});
}
void setStatusReport()
{
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = QUERY_STATUS");
write_command(TUYA_COVER_QUERY_STATUS, 0, 0);
}
void setMotorNormal()
{
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = ENABLE_REVERSING");
write_command(TUYA_COVER_COMMAND, tuya_cover_enable_reversing, sizeof(tuya_cover_enable_reversing));
}
void setMotorReversed()
{
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = ENABLE_REVERSING");
write_command(TUYA_COVER_COMMAND, tuya_cover_disable_reversing, sizeof(tuya_cover_disable_reversing));
}
void sendCommand(std::string data)
{
int i = 0;
uint8_t sum = 0;
while (i < data.length())
{
const char hex[2] = {data[i++], data[i++]};
uint8_t d = strtoul(hex, NULL, 16);
sum += d;
writeByte(d);
}
writeByte(sum);
}
void writeByte(uint8_t data)
{
Serial.write(data);
}
};

280
esphome/include/ld2410_uart.h Executable file
View File

@@ -0,0 +1,280 @@
#include "esphome.h"
#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1)
class LD2410 : public PollingComponent, public UARTDevice
{
public:
LD2410(UARTComponent *parent) : UARTDevice(parent) {}
BinarySensor *hasTarget = new BinarySensor();
BinarySensor *hasMovingTarget = new BinarySensor();
BinarySensor *hasStillTarget = new BinarySensor();
BinarySensor *lastCommandSuccess = new BinarySensor();
Sensor *movingTargetDistance = new Sensor();
Sensor *movingTargetEnergy = new Sensor();
Sensor *stillTargetDistance = new Sensor();
Sensor *stillTargetEnergy = new Sensor();
Sensor *detectDistance = new Sensor();
Number *maxMovingDistanceRange;
Number *maxStillDistanceRange;
int movingSensitivities[9] = {0};
int stillSensitivities[9] = {0};
Number *noneDuration;
long lastPeriodicMillis = millis();
void setNumbers(Number *maxMovingDistanceRange_, Number *maxStillDistanceRange_, Number *noneDuration_){
maxMovingDistanceRange = maxMovingDistanceRange_;
maxStillDistanceRange = maxStillDistanceRange_;
noneDuration = noneDuration_;
}
void sendCommand(char *commandStr, char *commandValue, int commandValueLen)
{
lastCommandSuccess->publish_state(false);
// frame start bytes
write_byte(0xFD);
write_byte(0xFC);
write_byte(0xFB);
write_byte(0xFA);
// length bytes
int len = 2;
if (commandValue != nullptr)
len += commandValueLen;
write_byte(lowByte(len));
write_byte(highByte(len));
// command string bytes
write_byte(commandStr[0]);
write_byte(commandStr[1]);
// command value bytes
if (commandValue != nullptr)
{
for (int i = 0; i < commandValueLen; i++)
{
write_byte(commandValue[i]);
}
}
// frame end bytes
write_byte(0x04);
write_byte(0x03);
write_byte(0x02);
write_byte(0x01);
delay(50);
}
int twoByteToInt(char firstByte, char secondByte)
{
return (int16_t)(secondByte << 8) + firstByte;
}
void handlePeriodicData(char *buffer, int len)
{
if (len < 12)
return; // 4 frame start bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame end bytes
if (buffer[0] != 0xF4 || buffer[1] != 0xF3 || buffer[2] != 0xF2 || buffer[3] != 0xF1)
return; // check 4 frame start bytes
if (buffer[7] != 0xAA || buffer[len - 6] != 0x55 || buffer[len - 5] != 0x00)
return; // data head=0xAA, data end=0x55, crc=0x00
/*
Data Type: 6th byte
0x01: Engineering mode
0x02: Normal mode
*/
char dataType = buffer[5];
/*
Target states: 9th byte
0x00 = No target
0x01 = Moving targets
0x02 = Still targets
0x03 = Moving+Still targets
*/
char stateByte = buffer[8];
hasTarget->publish_state(stateByte != 0x00);
/*
Reduce data update rate to prevent home assistant database size glow fast
*/
long currentMillis = millis();
if (currentMillis - lastPeriodicMillis < 1000)
return;
lastPeriodicMillis = currentMillis;
hasMovingTarget->publish_state(CHECK_BIT(stateByte, 0));
hasStillTarget->publish_state(CHECK_BIT(stateByte, 1));
/*
Moving target distance: 10~11th bytes
Moving target energy: 12th byte
Still target distance: 13~14th bytes
Still target energy: 15th byte
Detect distance: 16~17th bytes
*/
int newMovingTargetDistance = twoByteToInt(buffer[9], buffer[10]);
if (movingTargetDistance->get_state() != newMovingTargetDistance)
movingTargetDistance->publish_state(newMovingTargetDistance);
int newMovingTargetEnergy = buffer[11];
if (movingTargetEnergy->get_state() != newMovingTargetEnergy)
movingTargetEnergy->publish_state(newMovingTargetEnergy);
int newStillTargetDistance = twoByteToInt(buffer[12], buffer[13]);
if (stillTargetDistance->get_state() != newStillTargetDistance)
stillTargetDistance->publish_state(newStillTargetDistance);
int newStillTargetEnergy = buffer[14];
if (stillTargetEnergy->get_state() != newStillTargetEnergy)
stillTargetEnergy->publish_state(buffer[14]);
int newDetectDistance = twoByteToInt(buffer[15], buffer[16]);
if (detectDistance->get_state() != newDetectDistance)
detectDistance->publish_state(newDetectDistance);
if (dataType == 0x01)
{ // engineering mode
// todo: support engineering mode data
}
}
void handleACKData(char *buffer, int len)
{
if (len < 10)
return;
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA)
return; // check 4 frame start bytes
if (buffer[7] != 0x01)
return;
if (twoByteToInt(buffer[8], buffer[9]) != 0x00)
{
lastCommandSuccess->publish_state(false);
return;
}
lastCommandSuccess->publish_state(true);
switch (buffer[6])
{
case 0x61: // Query parameters response
{
if (buffer[10] != 0xAA)
return; // value head=0xAA
/*
Moving distance range: 13th byte
Still distance range: 14th byte
*/
maxMovingDistanceRange->publish_state(buffer[12]);
maxStillDistanceRange->publish_state(buffer[13]);
/*
Moving Sensitivities: 15~23th bytes
Still Sensitivities: 24~32th bytes
*/
for (int i = 0; i < 9; i++)
{
movingSensitivities[i] = buffer[14 + i];
}
for (int i = 0; i < 9; i++)
{
stillSensitivities[i] = buffer[23 + i];
}
/*
None Duration: 33~34th bytes
*/
noneDuration->publish_state(twoByteToInt(buffer[32], buffer[33]));
}
break;
default:
break;
}
}
void readline(int readch, char *buffer, int len)
{
static int pos = 0;
if (readch >= 0)
{
if (pos < len - 1)
{
buffer[pos++] = readch;
buffer[pos] = 0;
}
else
{
pos = 0;
}
if (pos >= 4)
{
if (buffer[pos - 4] == 0xF8 && buffer[pos - 3] == 0xF7 && buffer[pos - 2] == 0xF6 && buffer[pos - 1] == 0xF5)
{
handlePeriodicData(buffer, pos);
pos = 0; // Reset position index ready for next time
}
else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 && buffer[pos - 1] == 0x01)
{
handleACKData(buffer, pos);
pos = 0; // Reset position index ready for next time
}
}
}
return;
}
void setConfigMode(bool enable)
{
char cmd[2] = {enable ? 0xFF : 0xFE, 0x00};
char value[2] = {0x01, 0x00};
sendCommand(cmd, enable ? value : nullptr, 2);
}
void queryParameters()
{
char cmd_query[2] = {0x61, 0x00};
sendCommand(cmd_query, nullptr, 0);
}
void setup() override
{
set_update_interval(15000);
}
void loop() override
{
const int max_line_length = 80;
static char buffer[max_line_length];
while (available())
{
readline(read(), buffer, max_line_length);
}
}
void setEngineeringMode(bool enable)
{
char cmd[2] = {enable ? 0x62 : 0x63, 0x00};
sendCommand(cmd, nullptr, 0);
}
void setMaxDistancesAndNoneDuration(int maxMovingDistanceRange, int maxStillDistanceRange, int noneDuration)
{
char cmd[2] = {0x60, 0x00};
char value[18] = {0x00, 0x00, lowByte(maxMovingDistanceRange), highByte(maxMovingDistanceRange), 0x00, 0x00, 0x01, 0x00, lowByte(maxStillDistanceRange), highByte(maxStillDistanceRange), 0x00, 0x00, 0x02, 0x00, lowByte(noneDuration), highByte(noneDuration), 0x00, 0x00};
sendCommand(cmd, value, 18);
queryParameters();
}
void factoryReset()
{
char cmd[2] = {0xA2, 0x00};
sendCommand(cmd, nullptr, 0);
}
void reboot()
{
char cmd[2] = {0xA3, 0x00};
sendCommand(cmd, nullptr, 0);
// not need to exit config mode because the ld2410 will reboot automatically
}
void setBaudrate(int index)
{
char cmd[2] = {0xA1, 0x00};
char value[2] = {index, 0x00};
sendCommand(cmd, value, 2);
}
void update()
{
}
};

5
esphome/interfaces/i2c_a.yaml Executable file
View File

@@ -0,0 +1,5 @@
i2c:
- id: bus_a
sda: ${pin_sda}
scl: ${pin_scl}
scan: false

15
esphome/interfaces/ir.yaml Executable file
View File

@@ -0,0 +1,15 @@
remote_transmitter:
id: irtx
pin: ${pin_ir_tx}
carrier_duty_percent: 50%
remote_receiver:
id: rcvr
pin:
number: ${pin_ir_rx}
inverted: true
# mode:
# input: true
# pullup: true
tolerance: 55%
dump: all

11
esphome/interfaces/uart_ld.yaml Executable file
View File

@@ -0,0 +1,11 @@
id: uart_ld
tx_pin: ${pin_ld_tx}
rx_pin: ${pin_ld_rx}
baud_rate: 256000
parity: NONE
stop_bits: 1
debug:
direction: BOTH
dummy_receiver: false
after:
delimiter: [0xF8,0xF7,0xF6,0xF5]

View File

@@ -0,0 +1,11 @@
id: uart_ld
tx_pin: ${pin_ld_tx}
rx_pin: ${pin_ld_rx}
baud_rate: 115200
parity: NONE
stop_bits: 1
debug:
direction: BOTH
# dummy_receiver: false
# after:
# delimiter: [0xF8,0xF7,0xF6,0xF5]

View File

@@ -0,0 +1,3 @@
id: uart_pm
rx_pin: ${pin_pm_rx}
baud_rate: 9600

View File

@@ -0,0 +1,4 @@
id: uart_pm
rx_pin: ${pin_pm_rx}
tx_pin: ${pin_pm_tx}
baud_rate: 9600

View File

@@ -0,0 +1,4 @@
id: uart_tuya
rx_pin: ${pin_tuya_rx}
tx_pin: ${pin_tuya_tx}
baud_rate: 9600

View File

@@ -0,0 +1,4 @@
id: uart_curtain
rx_pin: ${pin_curtain_rx}
tx_pin: ${pin_curtain_tx}
baud_rate: 9600

95
esphome/m5-atom-echo.yaml Normal file
View File

@@ -0,0 +1,95 @@
substitutions:
device_name: "m5-atom-echo"
friendly_name: "M5 atom echo"
comment: "esp32, mic, dac, RGB, Btn"
api_password: !secret esp32-atom-echo_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret esp32-atom-echo_ip
pin_lrclk: GPIO33
pin_bclk: GPIO19
pin_i2sdin: GPIO23
pin_i2sdout: GPIO22
pin_button: GPIO39
pin_LEDS: GPIO27
packages:
board: !include boards/m5atom.yaml
connection: !include common/wifi.yaml
device_base: !include common/common.yaml
logger: !include templates/logger.yaml
i2s_audio:
i2s_lrclk_pin: ${pin_lrclk}
i2s_bclk_pin: ${pin_bclk}
microphone:
- platform: i2s_audio
id: atom_mic
i2s_din_pin: ${pin_i2sdin}
voice_assistant:
microphone: atom_mic
on_tts_start:
then:
- light.turn_on:
id: led
brightness: 100%
red: 0
green: 100%
blue: 0
on_tts_end:
then:
- light.turn_off:
id: led
on_error:
then:
- light.turn_on:
id: led
brightness: 100%
red: 100%
green: 0%
blue: 0
- delay: 5s
- light.turn_off:
id: led
media_player:
- platform: i2s_audio
id: media_out
name: "mediaplayer"
dac_type: external
i2s_dout_pin: ${pin_i2sdout}
mode: mono
binary_sensor:
- platform: gpio
pin:
number: ${pin_button}
inverted: true
name: "button"
on_press:
- voice_assistant.start:
on_release:
- voice_assistant.stop:
light:
- platform: neopixelbus
type: GRB
variant: ws2812X
pin: ${pin_LEDS}
num_leds: 1
name: "RGB_Light"
id: led
effects:
- random:
name: "Random"
transition_length: 4s
update_interval: 5s
- addressable_rainbow:
name: Rainbow Effect
speed: 10
width: 50

View File

@@ -0,0 +1,34 @@
substitutions:
device_name: "esp32-aqs1"
friendly_name: "AQS-woonkamer_oud"
comment: "esp32, pm, co2, display, btprox"
location: "prullenbak"
api_password: !secret air_quality_woonkamer_oud_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret air_quality_woonkamer_oud_ip
pin_pm_rx: GPIO19
pin_pm_tx: GPIO26
pin_sda: GPIO21
pin_scl: GPIO22
packages:
board: !include boards/esp32_wroom_arduino.yaml
i2c: !include interfaces/i2c_a.yaml
device_base: !include common/common.yaml
connection: !include common/wifi.yaml
logger: !include templates/logger.yaml
bt_proxy: !include templates/ble_proxy.yaml
pmsc: !include sensors/pmsx0003.yaml
co2: !include sensors/scd30.yaml
tvoc: !include sensors/sgp30.yaml
#display pins
# spi:
# clk_pin: GPIO18
# mosi_pin: GPIO23
# miso_pin: GPIO12

View File

@@ -0,0 +1,92 @@
substitutions:
esp_name: "ir-004-c3"
external_components:
- source: github://Jorre05/remote_receiver
components: [ remote_receiver ]
esphome:
name: ${esp_name}
comment: ${esp_name}
esp32:
variant: ESP32C3
board: esp32dev
framework:
type: esp-idf
sdkconfig_options:
CONFIG_BT_BLE_50_FEATURES_SUPPORTED: y
CONFIG_BT_BLE_42_FEATURES_SUPPORTED: y
CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10"
# 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
#bluetooth_proxy:
time:
- platform: homeassistant
id: homeassistant_time
remote_transmitter:
pin: GPIO3
carrier_duty_percent: 50%
status_led:
pin: GPIO18
remote_receiver:
id: rcvr
pin:
number: GPIO19 #ESP32-C3 #GPIO5 ESP8266
inverted: true
# mode:
# input: true
# pullup: true
tolerance: 55%
dump: all
climate:
- platform: fujitsu_general
name: "Airco"
receiver_id: rcvr
# binary_sensor:
# - platform: gpio
# name: "Button"
# id: button
# pin:
# number: GPIO5
# mode: INPUT_PULLUP
# inverted: True
#GPIO4 - status (ESP32-C3 = GPIO18)
#GPIO5 - IR rcvr. (ESP32-C3 = GPIO19)
#GPIO14 - IR tx (ESP32-C3 = GPIO3)
#GPIO13 - button. (ESP32-C3 = GPIO5)
#GPIO2 - strapping to 3v3 (ESP32-C3 specific)

95
esphome/plant-sensor-1.yaml Executable file
View File

@@ -0,0 +1,95 @@
substitutions:
device_name: "plant_sensor1"
friendly_name: "plant sensor 1"
comment: "esp32, plant"
location: "tuin"
api_password: !secret plant_sensor1_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret plant_sensor1
#project specific params
project_version: '1.0'
update_interval: 30min
loglevel: DEBUG
moisture_min: '2.82'
moisture_max: '1.39'
conductivity_min: '0.075'
conductivity_max: '0.25'
# Uncomment run_duration and sleep_duration if you want to use deepsleep
# set how long to stay awake - NOT less then 10sec
run_duration: 11s
# set how long to sleep in minutes
sleep_duration: 10min
pin_power: GPIO4
pin_sda: GPIO25
pin_scl: GPIO26
pin_wake: GPIO35
pin_vbatt: GPIO33
pin_fertilizer: GPIO34
pin_soil: GPIO32
esphome:
name: '${device_name}'
comment: '${comment}'
on_boot:
priority: 240
then:
- wait_until:
condition:
wifi.connected:
timeout: 10s
on_shutdown:
then:
- switch.turn_off: spower
esp32:
board: lolin_d32
packages:
connection: !include common/wifi.yaml
i2c: !include interfaces/i2c_a.yaml
logger: !include templates/nologger.yaml
bluetooth: !include common/bluetooth.yaml
deepsleep: !include common/deepsleep.yaml
#sensors
plantsensors: !include sensors/plantsensors.yaml
bme280: !include sensors/bme280.yaml
battery: !include sensors/battery.yaml
time:
- platform: homeassistant
button:
- platform: restart
name: 'Restart - ${device_name}'
switch:
# Power Switch
- platform: gpio
name: '${device_name} Sensor Power switch'
pin:
number: 4
mode: INPUT_PULLUP
id: spower
restore_mode: ALWAYS_ON
internal: true
setup_priority: 1000
# packages:
# text_sensors: !include common/text_sensors.yaml
# dht: !include common/dht.yaml
# plantsensors: !include common/plantsensors.yaml
# waterpump: !include common/waterpump.yaml
# bluetooth: !include common/bluetooth.yaml
# Battery only works for 12 hours with deepsleep!
# bme280: !include common/bme280.yaml
# deepsleep: !include common/deepsleep.yaml
# battery: !include common/battery.yaml

View File

@@ -0,0 +1,56 @@
substitutions:
device_name: "esp32-atom-lite"
friendly_name: "RGBLED moon woonkamer"
comment: "esp32, RGBled, BT proxy"
location: "Woonkamer"
api_password: !secret esp32-atomo-lite_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret esp32-atomo-lite_ip
pin_button: GPIO39
pin_leds: GPIO25
packages:
board: !include boards/esp32_m5atom.yaml
connection: !include common/wifi.yaml
device_base: !include common/common.yaml
bt_proxy: !include templates/ble_proxy.yaml
logger: !include templates/nologger.yaml
binary_sensor:
- platform: gpio
pin:
number: ${pin_button}
inverted: true
name: ${device_name}_Button
id: button
on_click:
then:
- if:
condition:
- light.is_on: RGB_Light
then:
- light.turn_off: RGB_Light
else:
- light.turn_on: RGB_Light
light:
- platform: neopixelbus
type: GRB
variant: ws2812X
pin: ${pin_leds}
num_leds: 1
name: "${device_name}_RGB_Light"
id: RGB_Light
effects:
- random:
name: "Random"
transition_length: 4s
update_interval: 5s
- addressable_rainbow:
name: Rainbow Effect
speed: 100
width: 50

36
esphome/sensors/battery.yaml Executable file
View File

@@ -0,0 +1,36 @@
---
sensor:
# Batterie volt
- platform: adc
pin: ${pin_vbatt}
name: '${device_name} Battery Voltage'
id: '${device_name}_voltage'
attenuation: 11db
unit_of_measurement: 'V'
icon: 'mdi:battery-high'
device_class: 'voltage'
state_class: 'measurement'
accuracy_decimals: 2
filters:
- multiply: 2
- calibrate_linear:
# Map 0.0 (from sensor) to 0.0 (true value)
- 0.0 -> 0.0
- 4.0 -> 4.0
update_interval: ${update_interval}
# Battery %
- platform: adc
pin: ${pin_vbatt}
name: '${device_name} Battery'
id: '${device_name}_percent'
attenuation: 11db
accuracy_decimals: 2
unit_of_measurement: '%'
filters:
- multiply: 2
- calibrate_linear:
# Map 0.0 (from sensor) to 0.0 (true value)
- 3.18 -> 0.0
- 4.20 -> 100.0
update_interval: ${update_interval}

20
esphome/sensors/bme280.yaml Executable file
View File

@@ -0,0 +1,20 @@
---
sensor:
- platform: bme280
i2c_id: bus_a
temperature:
name: '${device_name} BME280 Temperature'
oversampling: 1x
# filters: # uncomment after calibration
# - offset: -2.3 # offset in °C for the measured temperature
pressure:
name: '${device_name} BME280 Pressure'
# filters: # uncomment after calibration
# - offset: -2.3 # offset in °C for the measured temperature
humidity:
name: '${device_name} BME280 Humidity'
# filters: # uncomment after calibration
# - offset: -2.3 # offset in °C for the measured temperature
address: 0x77
update_interval: ${update_interval}
setup_priority: -300

26
esphome/sensors/dht.yaml Executable file
View File

@@ -0,0 +1,26 @@
---
sensor:
# DHT
- platform: dht
pin: 16
model: dht11
temperature:
name: '${device_name} DHT Temperature'
unit_of_measurement: '°C'
icon: 'mdi:thermometer'
device_class: 'temperature'
state_class: 'measurement'
accuracy_decimals: 1
# filters: # uncomment after calibration
# - offset: -2.3 # offset in °C for the measured temperature
humidity:
name: '${device_name} DHT Humidity'
unit_of_measurement: '%'
icon: 'mdi:cloud-percent'
device_class: 'humidity'
state_class: 'measurement'
accuracy_decimals: 1
# filters: # uncomment after calibration
# - offset: -2.3 # offset in °C for the measured temperature
setup_priority: -100
update_interval: ${update_interval}

View File

@@ -0,0 +1,7 @@
platform: homeassistant
entity_id: ${entity_id}
attribute: ${id}
id: ${id}
on_value:
then:
- lambda: 'id(data_updated) = true;'

50
esphome/sensors/ld2410.yaml Executable file
View File

@@ -0,0 +1,50 @@
uart:
- !include ../interfaces/uart_ld.yaml
# Example configuration entry
ld2410:
uart_id: uart_ld
timeout: 1s
max_move_distance : 6m
max_still_distance: 3m
# g0_move_threshold: 10
# g0_still_threshold: 20
# g1_move_threshold: 10
# g1_still_threshold: 20
# g2_move_threshold: 20
# g2_still_threshold: 21
# g3_move_threshold: 30
# g3_still_threshold: 31
# g4_move_threshold: 40
# g4_still_threshold: 41
# g5_move_threshold: 50
# g5_still_threshold: 51
# g6_move_threshold: 60
# g6_still_threshold: 61
# g7_move_threshold: 70
# g7_still_threshold: 71
# g8_move_threshold: 80
# g8_still_threshold: 81
sensor:
- platform: ld2410
moving_distance:
name : ${device_name} Moving Distance
still_distance:
name: ${device_name} Still Distance
moving_energy:
name: ${device_name} Move Energy
still_energy:
name: ${device_name} Still Energy
detection_distance:
name: ${device_name} Detection Distance
binary_sensor:
- platform: ld2410
has_target:
name: ${device_name} Presence
has_moving_target:
name: ${device_name} Moving Target
has_still_target:
name: ${device_name} Still Target

58
esphome/sensors/ld2420.yaml Executable file
View File

@@ -0,0 +1,58 @@
uart:
- !include ../interfaces/uart_ld2420.yaml
external_components:
- source: github://pr#4847
components: [ ld2420 ]
refresh: 0s
# Example configuration entry
ld2420:
uart_id: uart_ld
presence_time_window: 120s
detection_gate_min: 1
detection_gate_max: 12
g0_move_threshold: 60000
g0_still_threshold: 40000
g1_move_threshold: 30000
g1_still_threshold: 20000
g2_move_threshold: 400
g2_still_threshold: 200
g3_move_threshold: 250
g3_still_threshold: 200
g4_move_threshold: 250
g4_still_threshold: 200
g5_move_threshold: 250
g5_still_threshold: 200
g6_move_threshold: 250
g6_still_threshold: 200
g7_move_threshold: 250
g7_still_threshold: 150
g8_move_threshold: 250
g8_still_threshold: 150
g9_move_threshold: 250
g9_still_threshold: 100
g10_move_threshold: 250
g10_still_threshold: 100
g11_move_threshold: 250
g11_still_threshold: 100
g12_move_threshold: 250
g12_still_threshold: 100
g13_move_threshold: 200
g13_still_threshold: 100
g14_move_threshold: 200
g14_still_threshold: 100
g15_move_threshold: 200
g15_still_threshold: 100
sensor:
- platform: ld2420
moving_distance:
name : ${device_name} Moving Distance
binary_sensor:
- platform: ld2420
has_target:
name: ${device_name} Presence

6
esphome/sensors/pir_raw.yaml Executable file
View File

@@ -0,0 +1,6 @@
binary_sensor:
- platform: gpio
name: "PIR_raw"
pin: ${pin_pir}
icon: mdi:motion-sensor
device_class: motion

View File

@@ -0,0 +1,53 @@
---
sensor:
# Soil humidity %
- platform: adc
pin: ${pin_soil}
name: '${device_name} Soil Moisture'
id: ${device_name}_moisture
icon: 'mdi:watering-can'
update_interval: ${update_interval}
attenuation: 11db
accuracy_decimals: 2 # comment for calibration
unit_of_measurement: '%' # comment for calibration
device_class: 'moisture'
state_class: 'measurement'
# unit_of_measurement: "V" # uncomment for calibration
filters: # comment when calibrating
- calibrate_linear: # comment when calibrating
# Map 0.0 (from sensor) to 0.0 (true value)
- ${moisture_min} -> 0.0 # comment when calibrating
- ${moisture_max} -> 100.0 # comment when calibrating
# Fertilizer sensor
- platform: adc
pin: ${pin_fertilizer}
name: '${device_name} Soil Conductivity'
icon: 'mdi:flower'
update_interval: ${update_interval}
accuracy_decimals: 2 # comment for calibration
unit_of_measurement: 'µS/cm' # comment when calibrating
# device_class: '' # for now home assistant does not support this class
state_class: 'measurement'
# unit_of_measurement: "V" # uncomment for raw data
filters: # comment when calibrating
- calibrate_linear: # comment when calibrating
# Map 0.0 (from sensor) to 0.0 (true value)
- ${conductivity_min} -> 0.0 # comment when calibrating
- ${conductivity_max} -> 100.0 # comment when calibrating
# Lux sensor
- platform: bh1750
i2c_id: bus_a
name: '${device_name} BH1750 Illuminance'
address: 0x23
unit_of_measurement: 'lx'
icon: 'mdi:white-balance-sunny'
device_class: 'illuminance'
state_class: 'measurement'
setup_priority: -300
update_interval: ${update_interval}
# convert to illuminance
filters:
- lambda: |-
return x * 200.0;

8
esphome/sensors/pm1006.yaml Executable file
View File

@@ -0,0 +1,8 @@
uart:
- !include ../interfaces/uart_pm.yaml
sensor:
- platform: pm1006
pm_2_5:
name: "${location} Particulate Matter 2.5µm Concentration"
uart_id: uart_pm

14
esphome/sensors/pmsx0003.yaml Executable file
View File

@@ -0,0 +1,14 @@
uart:
- !include ../interfaces/uart_pmx.yaml
sensor:
- platform: pmsx003
uart_id: uart_pm
type: PMSX003
pm_1_0:
name: ${device_name} <1.0µm
pm_2_5:
name: ${device_name} <2.5µm
pm_10_0:
name: ${device_name} <10.0µm
update_interval: 30000ms

14
esphome/sensors/scd30.yaml Executable file
View File

@@ -0,0 +1,14 @@
sensor:
- platform: scd30
co2:
name: "${device_name} CO2"
accuracy_decimals: 1
temperature:
name: "${device_name} Temperature"
accuracy_decimals: 2
humidity:
name: "${device_name} Humidity"
accuracy_decimals: 1
temperature_offset: 1.5 °C
address: 0x61
update_interval: 5s

11
esphome/sensors/sgp30.yaml Executable file
View File

@@ -0,0 +1,11 @@
sensor:
- platform: sgp30
eco2:
name: "${device_name} eCO2"
accuracy_decimals: 1
tvoc:
name: "${device_name} TVOC"
accuracy_decimals: 1
store_baseline: yes
address: 0x58
update_interval: 1s

9
esphome/sensors/sht3x.yaml Executable file
View File

@@ -0,0 +1,9 @@
sensor:
- platform: sht3xd
temperature:
name: Temperatuur ${device_name}
id: sht_temp
humidity:
name: luchtvochtigheid ${device_name}
address: 0x44
update_interval: 60s

31
esphome/sensors/tuya_mcu.yaml Executable file
View File

@@ -0,0 +1,31 @@
uart:
- !include ../interfaces/uart_tuya.yaml
time:
- platform: homeassistant
id: homeassistant_time
tuya:
status_pin: ${pin_tuya_status}
time_id: homeassistant_time
uart_id: uart_tuya
sensor:
- platform: "tuya"
name: "Temperature ${device_name}"
id: tuya_temp
sensor_datapoint: 101
unit_of_measurement: "°C"
device_class: "temperature"
state_class: "measurement"
filters:
- multiply: 0.1
accuracy_decimals: 1
- platform: "tuya"
name: "humidity ${device_name}"
sensor_datapoint: 102
unit_of_measurement: "%rh"
device_class: "humidity"
state_class: "measurement"
accuracy_decimals: 1

0
esphome/sensors/wifi.yaml Executable file
View File

View File

@@ -0,0 +1,2 @@
bluetooth_proxy:
active: true

8
esphome/templates/button.yaml Executable file
View File

@@ -0,0 +1,8 @@
binary_sensor:
- platform: gpio
name: "Button"
id: button
pin:
number: ${pin_button}
mode: INPUT_PULLUP
inverted: True

View File

@@ -0,0 +1,23 @@
remote_transmitter:
pin: ${pin_ir_tx}
carrier_duty_percent: 50%
remote_receiver:
id: rcvr
pin:
number: ${pin_ir_rx}
inverted: true
# mode:
# input: true
# pullup: true
tolerance: 55%
dump: all
climate:
- platform: fujitsu_general
name: "Airco"
receiver_id: rcvr
visual:
min_temperature: 18
max_temperature: 25
temperature_step: 1

View File

@@ -0,0 +1,9 @@
climate:
- platform: fujitsu_general
name: "Airco"
receiver_id: rcvr
sensor: ${hvac_sensor}
visual:
min_temperature: 18
max_temperature: 25
temperature_step: 1

2
esphome/templates/logger.yaml Executable file
View File

@@ -0,0 +1,2 @@
# logging
logger:

View File

@@ -0,0 +1,3 @@
# disable logging
logger:
baud_rate: 0

2
esphome/templates/status.yaml Executable file
View File

@@ -0,0 +1,2 @@
status_led:
pin: ${pin_status}

474
esphome/ttgo-display.yaml Normal file
View File

@@ -0,0 +1,474 @@
# ESPHome code for the LilyGO TTGO Display
# Copyright 2023 by Smart Home Junkie
#
# Visit my website at https://www.smarthomejunkie.net
# Watch the tutorial for this display and code at https://youtu.be/LJCeelAzlS0
substitutions:
esp_name: "ttgo-display"
esphome:
name: ${esp_name}
comment: ${esp_name}
# Enable Home Assistant API
api:
encryption:
key: !secret ttgo
# Define board type
esp32:
board: esp32dev
# Enable logging
logger:
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:
spi:
clk_pin: GPIO18
mosi_pin: GPIO19
# Define the rotate variable. This indicates if the pages should be rotated or not
globals:
- id: rotate
type: boolean
initial_value: "true"
# Define time sensor
time:
- platform: homeassistant
id: esptime
# Define binary sensors
binary_sensor:
- platform: gpio # Short Press button 0
pin:
number: GPIO0
inverted: true
mode:
input: true
pullup: true
name: "Short Press Button 0"
id: short_press_button_0
on_click:
min_length: 1ms
max_length: 1000ms
then:
- display.page.show_previous: my_display
- component.update: my_display
- platform: gpio # Long Press button 0
pin:
number: GPIO0
inverted: true
id: button_2
on_click:
min_length: 1001ms
max_length: 5000ms
then:
- switch.toggle: backlight
- platform: gpio # Short Press button 1
pin:
number: GPIO35
inverted: true
name: "Short Press Button 1"
id: short_press_button_1
on_click:
min_length: 1ms
max_length: 1000ms
then:
- display.page.show_next: my_display
- component.update: my_display
- platform: gpio # Long Press button 1
pin:
number: GPIO35
inverted: true
name: "Long Press Button 1"
id: long_press_button_1
on_click:
min_length: 1001ms
max_length: 5000ms
then:
- if:
condition:
lambda: |-
return id(rotate);
then:
globals.set:
id: rotate
value: "false"
else:
globals.set:
id: rotate
value: "true"
# Allow dimmable control of the backlight (pin GPIO4) - Currently not working
output:
- platform: ledc
pin: GPIO4
id: gpio4
light:
- platform: monochromatic
output: gpio4
name: "Backlight"
switch:
- platform: gpio
pin: GPIO4
id: backlight
internal: true
# Define all the numeric sensors used
sensor:
- platform: homeassistant
entity_id: sensor.subscriptions_short
id: subscriptions
- platform: homeassistant
entity_id: sensor.views
id: views
- platform: homeassistant
entity_id: sensor.ttgo_display_data
attribute: netto_power
id: nettopower
- platform: homeassistant
entity_id: sensor.Temperatuur_Zolder_Werkkamer
id: office_temperature
- platform: homeassistant
entity_id: sensor.humidity_zolder_werkkamer
id: office_humidity
- platform: homeassistant
entity_id: sensor.office_multi_sensor_pressure
id: office_pressure
- platform: homeassistant
entity_id: sensor.office_light_sensor
id: office_light_sensor
- platform: homeassistant
entity_id: sensor.ttgo_display_data
attribute: temperature
id: outside_temperature
- platform: homeassistant
entity_id: sensor.ttgo_display_data
attribute: wind_speed
id: wind_speed
- platform: homeassistant
entity_id: sensor.bitcoin
id: bitcoin
# Define all the string sensors used
text_sensor:
- platform: homeassistant
entity_id: sensor.ttgo_display_data
attribute: weather_condition_0
id: weather_condition
filters:
- to_upper:
# Define fonts. Use either Google fonts or stored fonts. Stored fonts are stored in esphome/fonts/
font:
- file:
type: gfonts
family: Lato
weight: 400
id: lato
size: 20
- file:
type: gfonts
family: Lato
weight: 700
id: latobold
size: 24
- file:
type: gfonts
family: Lato
weight: 900
id: latoblack
size: 30
- file:
type: gfonts
family: Lato
weight: 900
id: latoblackheading1
size: 50
- file: "fonts/bitcoin/Ubuntu-BoldItalic.ttf"
id: bitcoin_font
size: 30
# Define colors
color:
- id: RED
red: 100%
green: 0%
blue: 0%
- id: GREEN
red: 0%
green: 100%
blue: 0%
- id: BLUE
red: 0%
green: 0%
blue: 100%
- id: YELLOW
red: 100%
green: 100%
blue: 0%
- id: WHITE
red: 100%
green: 100%
blue: 100%
- id: ORANGE
red: 100%
green: 67%
blue: 20%
# Define all the images used. Store the images in the images folder within the esphome folder (esphome/images/)
image:
- file: "images/youtube.png"
id: youtube_image
resize: 80x80
type: RGB24
- file: "images/logo.jpg"
id: logo
resize: 120x120
type: RGB24
- file: "images/electricity-icon.png"
id: electricity_image
resize: 80x80
type: RGB24
- file: "images/bitcoin_logo.png"
id: bitcoin_logo
resize: 80x80
type: RGB24
- file: "images/wind_icon.png"
id: wind_icon
resize: 30x30
type: RGB24
- file: "images/thermometer_icon.png"
id: thermometer_icon
resize: 30x30
type: RGB24
# Define animations
animation:
- file: "images/weather.gif"
id: weather_animation
resize: 80x80
type: RGB24
# Define the graphs for sensors that you want to show on the display
graph:
- id: office_temperature_graph
sensor: office_temperature
duration: 4h
width: 220
height: 90
x_grid: 1h
y_grid: 5
min_range: 5
max_range: 35
min_value: 5
max_value: 35
color: GREEN
- id: office_humidity_graph
sensor: office_humidity
duration: 4h
width: 220
height: 90
x_grid: 1h
y_grid: 25
min_range: 1
max_range: 100
min_value: 1
max_value: 100
color: BLUE
- id: office_pressure_graph
sensor: office_pressure
duration: 4h
width: 220
height: 90
x_grid: 1h
y_grid: 100.0
color: YELLOW
- id: office_light_sensor_graph
duration: 4h
width: 220
height: 90
x_grid: 1h
traces:
- sensor: office_light_sensor
color: ORANGE
line_type: SOLID
line_thickness: 5
# Define qr code locations
qr_code:
- id: smarthomejunkie_website
value: smarthomejunkie.net
# Set up the display. This is the main part of the code
display:
- platform: st7789v
model: TTGO_TDISPLAY_135x240
backlight_pin: GPIO4
cs_pin: GPIO5
dc_pin: GPIO16
reset_pin: GPIO23
rotation: 90°
update_interval: 1s
id: my_display
pages: # Define the pages
# - id: showintro
# lambda: |-
# it.image(0, 10, id(logo));
# it.printf(135, 10, id(latoblack), WHITE, "SMART");
# it.printf(140, 50, id(latoblack), WHITE, "HOME");
# it.printf(130, 90, id(latoblack), WHITE, "JUNKIE");
- id: showtime
lambda: |-
it.strftime(45, 20, id(latoblack), "%d-%m-%Y", id(esptime).now());
it.strftime(25, 55, id(latoblackheading1), "%H:%M:%S", id(esptime).now());
# - id: showsubscribers
# lambda: |-
# it.printf(0,0,id(latoblack), WHITE, "SUBSCRIBERS");
# it.image(0, 40, id(youtube_image));
# if (id(subscriptions).has_state()) {
# it.printf(95, 60, id(latoblack), WHITE, "%.1fK", id(subscriptions).state);
# } else {
# it.printf(95, 65, id(lato), WHITE, "LOADING...");
# }
# - id: showviews
# lambda: |-
# it.printf(0,0,id(latoblack), WHITE, "VIEWS");
# it.image(0, 40, id(youtube_image));
# if (id(views).has_state()) {
# it.printf(95, 60, id(latoblack), WHITE, "%.0f", id(views).state);
# } else {
# it.printf(95, 65, id(lato), WHITE, "LOADING...");
# }
- id: shownettopower
lambda: |-
it.printf(0,0,id(latoblack), WHITE, "NETTO POWER");
it.image(0, 40, id(electricity_image));
if (id(nettopower).has_state()) {
if (id(nettopower).state > -1000) {
it.printf(95, 60, id(latoblack), WHITE, "%.0f Watt", id(nettopower).state);
} else {
it.printf(95, 60, id(latobold), WHITE, "%.0f Watt", id(nettopower).state);
}
} else {
it.printf(95, 65, id(lato), WHITE, "LOADING...");
}
# - id: showbitcoin
# lambda: |-
# it.printf(0,0,id(bitcoin_font), WHITE, "bitcoin");
# it.image(0, 40, id(bitcoin_logo));
# if (id(bitcoin).has_state()) {
# it.printf(95, 60, id(bitcoin_font), WHITE, "%.0f", id(bitcoin).state);
# } else {
# it.printf(95, 65, id(lato), WHITE, "LOADING...");
# }
- id: show_office_temperature_graph
lambda: |-
if (id(office_temperature).has_state()) {
it.printf(0,0,id(latoblack), WHITE, "TEMP: %.1f °C", id(office_temperature).state);
it.graph(10, 40, id(office_temperature_graph));
} else {
it.printf(0,0,id(latoblack), WHITE, "TEMPERATURE");
it.printf(80, 65, id(lato), WHITE, "LOADING...");
}
- id: show_office_humidity_graph
lambda: |-
if (id(office_humidity).has_state()) {
it.printf(0,0,id(latoblack), WHITE, "HUM: %.0f %%", id(office_humidity).state);
it.graph(10, 40, id(office_humidity_graph));
} else {
it.printf(0,0,id(latoblack), WHITE, "HUMIDITY");
it.printf(80, 65, id(lato), WHITE, "LOADING...");
}
# - id: show_office_pressure_graph
# lambda: |-
# if (id(office_pressure).has_state()) {
# it.printf(0,0,id(latoblack), WHITE, "PRS: %.0f hPA", id(office_pressure).state);
# it.graph(10, 40, id(office_pressure_graph));
# } else {
# it.printf(0,0,id(latoblack), WHITE, "PRESSURE");
# it.printf(80, 65, id(lato), WHITE, "LOADING...");
# }
# - id: show_office_light_sensor_graph
# lambda: |-
# if (id(office_light_sensor).has_state()) {
# it.printf(0,0,id(latoblack), WHITE, "LUX: %.0f lx", id(office_light_sensor).state);
# it.graph(10, 40, id(office_light_sensor_graph));
# } else {
# it.printf(0,0,id(latoblack), WHITE, "LUX");
# it.printf(80, 65, id(lato), WHITE, "LOADING...");
# }
- id: show_weather
lambda: |-
id(weather_animation).next_frame();
it.image(0, 0, id(weather_animation), COLOR_ON, COLOR_OFF);
if (id(weather_condition).state == "CLOUDY" ||
id(weather_condition).state == "RAINY" ||
id(weather_condition).state == "FOG" ||
id(weather_condition).state == "HAIL" ||
id(weather_condition).state == "SNOWY" ||
id(weather_condition).state == "SUNNY" ||
id(weather_condition).state == "WINDY" ) {
it.printf(110,0,id(latoblack), WHITE,"%s",id(weather_condition).state.c_str());
}
if (id(weather_condition).state == "LIGTNING" ||
id(weather_condition).state == "POURING" ) {
it.printf(110,0,id(latobold), WHITE,"%s",id(weather_condition).state.c_str());
}
if (id(weather_condition).state == "PARTLYCLOUDY") {
it.printf(110,0,id(latoblack), WHITE,"PARTLY");
it.printf(110,35,id(latoblack), WHITE,"CLOUDY");
}
if (id(weather_condition).state == "SNOWY-RAIN") {
it.printf(110,0,id(latoblack), WHITE,"SNOWY");
it.printf(110,35,id(latoblack), WHITE,"RAIN");
}
if (id(weather_condition).state == "LIGHTNING-RAINY") {
it.printf(110,0,id(latobold), WHITE,"LIGHTNING");
it.printf(110,35,id(latobold), WHITE,"RAINY");
}
if (id(weather_condition).state == "WINDY-VARIANT") {
it.printf(110,0,id(latoblack), WHITE,"WINDY");
}
if (id(weather_condition).state == "CLEAR-NIGHT") {
it.printf(110,0,id(latoblack), WHITE,"CLEAR");
it.printf(110,35,id(latoblack), WHITE,"NIGHT");
}
if (id(weather_condition).state == "EXCEPTIONAL") {
it.printf(110,0,id(latobold), WHITE,"EXCEPTIONAL");
}
it.image(0, 100, id(thermometer_icon));
it.printf(35,100,id(latobold), WHITE,"%.1f °C", id(outside_temperature).state);
it.image(110, 100, id(wind_icon));
it.printf(145,100,id(latobold), WHITE,"%.2f m/s", id(wind_speed).state);
# - id: showqrcode
# lambda: |-
# it.qr_code(60, 5, id(smarthomejunkie_website), WHITE, 5);
# Define the cycle interval of the pages.
interval:
- interval: 5s
then:
if:
condition:
lambda: 'return id(rotate);'
then:
- display.page.show_next: my_display
- component.update: my_display

39
esphome/watervalve.yaml Normal file
View File

@@ -0,0 +1,39 @@
substitutions:
device_name: "watervalve"
friendly_name: "WaterValve Garage"
comment: "ESP8266, tuya-hack"
location: "Garage"
api_password: !secret watervalve_api
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret watervalve_ip
pin_status: GPIO0
pin_motor: GPIO13
pin_button: GPIO12
packages:
board: !include boards/esp12f.yaml
connection: !include common/wifi.yaml
device_base: !include common/common.yaml
status: !include templates/status.yaml
logger: !include templates/logger.yaml
switch:
- platform: gpio
id: valveMotor
pin: ${pin_motor}
name: "Valve"
icon: mdi:pipe-valve
binary_sensor:
- platform: gpio
name: "Button"
pin: ${pin_button}
on_click:
min_length: 50ms
max_length: 1000ms
then:
- switch.toggle: valveMotor

42
esphome/zemismart-curtain.yaml Executable file
View File

@@ -0,0 +1,42 @@
substitutions:
device_name: "zemismart-curtain"
friendly_name: "zemismart curtain"
comment: "esp8266 tuya mcu curtain"
location: "overloop"
api_password: !secret zemismart_curtain
ota_password: !secret ota_password
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
gateway: !secret ip_gateway
subnet: !secret ip_subnet
ip: !secret zemismart_curtain_ip
pin_curtain_rx: GPIO13
pin_curtain_tx: GPIO15
packages:
board: !include boards/esp01.yaml
device_base: !include common/common.yaml
connection: !include common/wifi.yaml
logger: !include templates/nologger.yaml
uart:
- !include interfaces/uart_tywe1s.yaml
esphome:
includes:
- include/bcm500ds.h
cover:
- platform: custom
lambda: |-
auto curtain = new CustomCurtain();
App.register_component(curtain);
return {curtain};
covers:
- name: Curtain
device_class: curtain
custom_component:
- lambda: |-
return { new CustomAPI() };