Update devices.yaml and state.json with new device configurations and states
- Refactored friendly names in devices.yaml for consistency and clarity. - Added new devices and updated existing ones in devices.yaml. - Populated state.json with detailed state information for various devices, including brightness, power, and link quality. - Included update information for devices where applicable.
This commit is contained in:
@@ -19,3 +19,44 @@ packages:
|
||||
logger: !include templates/logger.yaml
|
||||
btproxy: !include templates/bt_proxy.yaml
|
||||
|
||||
|
||||
# esp32_ble_tracker:
|
||||
# on_ble_advertise:
|
||||
# then:
|
||||
# - lambda: |-
|
||||
# ESP_LOGD("BLE", "MAC: %s | RSSI: %d", x.address_str().c_str(), x.get_rssi());
|
||||
|
||||
sensor:
|
||||
- platform: xiaomi_hhccjcy01
|
||||
mac_address: "5C:85:7E:13:17:A3"
|
||||
temperature:
|
||||
name: "Xiaomi MiFlora Temperature"
|
||||
moisture:
|
||||
name: "Xiaomi MiFlora Moisture"
|
||||
illuminance:
|
||||
name: "Xiaomi MiFlora Illuminance"
|
||||
conductivity:
|
||||
name: "Xiaomi MiFlora Soil Conductivity"
|
||||
battery_level:
|
||||
name: "Xiaomi MiFlora Battery Level"
|
||||
|
||||
esp32_ble_tracker:
|
||||
on_ble_advertise:
|
||||
then:
|
||||
- lambda: |-
|
||||
// MAC-adres als string
|
||||
std::string mac = x.address_str();
|
||||
|
||||
// RSSI ophalen via getter
|
||||
int rssi = x.get_rssi();
|
||||
|
||||
// Naam ophalen, lege string als onbekend
|
||||
std::string name = x.get_name(); // geen has_value(), gewoon std::string
|
||||
|
||||
// Filter: Xiaomi MAC-prefixes of Flowercare naam
|
||||
if (mac.rfind("A4:C1:38", 0) == 0 ||
|
||||
mac.rfind("62:26:28", 0) == 0 ||
|
||||
mac.rfind("4C:65:A8", 0) == 0 ||
|
||||
name.find("Flower") != std::string::npos) {
|
||||
ESP_LOGD("xiaomi_logger", "Xiaomi device found: %s (%s), RSSI: %d", mac.c_str(), name.c_str(), rssi);
|
||||
}
|
||||
1
esphome/components_local/waveshare_epaper/__init__.py
Normal file
1
esphome/components_local/waveshare_epaper/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@clydebarrow"]
|
||||
229
esphome/components_local/waveshare_epaper/display.py
Normal file
229
esphome/components_local/waveshare_epaper/display.py
Normal file
@@ -0,0 +1,229 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import core, pins
|
||||
from esphome.components import display, spi
|
||||
from esphome.const import (
|
||||
CONF_BUSY_PIN,
|
||||
CONF_DC_PIN,
|
||||
CONF_FULL_UPDATE_EVERY,
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_MODEL,
|
||||
CONF_PAGES,
|
||||
CONF_RESET_DURATION,
|
||||
CONF_RESET_PIN,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
||||
waveshare_epaper_ns = cg.esphome_ns.namespace("waveshare_epaper")
|
||||
WaveshareEPaperBase = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaperBase", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer
|
||||
)
|
||||
WaveshareEPaper = waveshare_epaper_ns.class_("WaveshareEPaper", WaveshareEPaperBase)
|
||||
WaveshareEPaperBWR = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaperBWR", WaveshareEPaperBase
|
||||
)
|
||||
WaveshareEPaperPolled = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaperPolled", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaperTypeA = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaperTypeA", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P7In = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P7In", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P7InB = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P7InB", WaveshareEPaperBWR
|
||||
)
|
||||
WaveshareEPaper2P7InBV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P7InBV2", WaveshareEPaperBWR
|
||||
)
|
||||
WaveshareEPaper2P7InV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P7InV2", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P9InB = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InB", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P9InBV3 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InBV3", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P9InV2R2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InV2R2", WaveshareEPaper
|
||||
)
|
||||
GDEW029T5 = waveshare_epaper_ns.class_("GDEW029T5", WaveshareEPaper)
|
||||
WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InDKE", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper4P2In = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper4P2In", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper4P2InBV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper4P2InBV2", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper5P8In = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper5P8In", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper5P8InV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper5P8InV2", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper7P5In = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5In", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper7P5InBC = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5InBC", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper7P5InBV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5InBV2", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper7P5InBV3 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5InBV3", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper7P5InV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5InV2", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper7P5InV2alt = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5InV2alt", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper7P5InHDB = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5InHDB", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P13InDKE = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P13InDKE", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P13InV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P13InV2", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P13InV3 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P13InV3", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper13P3InK = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper13P3InK", WaveshareEPaper
|
||||
)
|
||||
GDEW0154M09 = waveshare_epaper_ns.class_("GDEW0154M09", WaveshareEPaper)
|
||||
WaveshareEPaper7In5BV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7In5BV2", WaveshareEPaperPolled
|
||||
)
|
||||
|
||||
WaveshareEPaperTypeAModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeAModel")
|
||||
WaveshareEPaperTypeBModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeBModel")
|
||||
|
||||
MODELS = {
|
||||
"1.54in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN),
|
||||
"1.54inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN_V2),
|
||||
"2.13in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN),
|
||||
"2.13inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN_V2),
|
||||
"2.13in-ttgo": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN),
|
||||
"2.13in-ttgo-b1": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B1),
|
||||
"2.13in-ttgo-b73": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B73),
|
||||
"2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74),
|
||||
"2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN),
|
||||
"2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2),
|
||||
"gdew029t5": ("c", GDEW029T5),
|
||||
"2.70in": ("b", WaveshareEPaper2P7In),
|
||||
"2.70in-b": ("b", WaveshareEPaper2P7InB),
|
||||
"2.70in-bv2": ("b", WaveshareEPaper2P7InBV2),
|
||||
"2.70inv2": ("b", WaveshareEPaper2P7InV2),
|
||||
"2.90in-b": ("b", WaveshareEPaper2P9InB),
|
||||
"2.90in-bv3": ("b", WaveshareEPaper2P9InBV3),
|
||||
"2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2),
|
||||
"2.90in-dke": ("c", WaveshareEPaper2P9InDKE),
|
||||
"4.20in": ("b", WaveshareEPaper4P2In),
|
||||
"4.20in-bv2": ("b", WaveshareEPaper4P2InBV2),
|
||||
"5.83in": ("b", WaveshareEPaper5P8In),
|
||||
"5.83inv2": ("b", WaveshareEPaper5P8InV2),
|
||||
"7.50in": ("b", WaveshareEPaper7P5In),
|
||||
"7.50in-bv2": ("b", WaveshareEPaper7P5InBV2),
|
||||
"7.50in-bv2-rb": ("b", WaveshareEPaper7In5BV2),
|
||||
"7.50in-bv3": ("b", WaveshareEPaper7P5InBV3),
|
||||
"7.50in-bc": ("b", WaveshareEPaper7P5InBC),
|
||||
"7.50inv2": ("b", WaveshareEPaper7P5InV2),
|
||||
"7.50inv2alt": ("b", WaveshareEPaper7P5InV2alt),
|
||||
"7.50in-hd-b": ("b", WaveshareEPaper7P5InHDB),
|
||||
"2.13in-ttgo-dke": ("c", WaveshareEPaper2P13InDKE),
|
||||
"2.13inv3": ("c", WaveshareEPaper2P13InV3),
|
||||
"1.54in-m5coreink-m09": ("c", GDEW0154M09),
|
||||
"13.3in-k": ("b", WaveshareEPaper13P3InK),
|
||||
}
|
||||
|
||||
RESET_PIN_REQUIRED_MODELS = ("2.13inv2", "2.13in-ttgo-b74")
|
||||
|
||||
|
||||
def validate_full_update_every_only_types_ac(value):
|
||||
if CONF_FULL_UPDATE_EVERY not in value:
|
||||
return value
|
||||
if MODELS[value[CONF_MODEL]][0] == "b":
|
||||
full_models = []
|
||||
for key, val in sorted(MODELS.items()):
|
||||
if val[0] != "b":
|
||||
full_models.append(key)
|
||||
raise cv.Invalid(
|
||||
"The 'full_update_every' option is only available for models "
|
||||
+ ", ".join(full_models)
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def validate_reset_pin_required(config):
|
||||
if config[CONF_MODEL] in RESET_PIN_REQUIRED_MODELS and CONF_RESET_PIN not in config:
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_RESET_PIN}' is required for model {config[CONF_MODEL]}"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
display.FULL_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WaveshareEPaperBase),
|
||||
cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True),
|
||||
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema,
|
||||
cv.Optional(CONF_FULL_UPDATE_EVERY): cv.int_range(min=1, max=4294967295),
|
||||
cv.Optional(CONF_RESET_DURATION): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(max=core.TimePeriod(milliseconds=500)),
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("1s"))
|
||||
.extend(spi.spi_device_schema()),
|
||||
validate_full_update_every_only_types_ac,
|
||||
validate_reset_pin_required,
|
||||
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
model_type, model = MODELS[config[CONF_MODEL]]
|
||||
if model_type == "a":
|
||||
rhs = WaveshareEPaperTypeA.new(model)
|
||||
var = cg.Pvariable(config[CONF_ID], rhs, WaveshareEPaperTypeA)
|
||||
elif model_type in ("b", "c"):
|
||||
rhs = model.new()
|
||||
var = cg.Pvariable(config[CONF_ID], rhs, model)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
await display.register_display(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
dc = await cg.gpio_pin_expression(config[CONF_DC_PIN])
|
||||
cg.add(var.set_dc_pin(dc))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
if CONF_RESET_PIN in config:
|
||||
reset = await cg.gpio_pin_expression(config[CONF_RESET_PIN])
|
||||
cg.add(var.set_reset_pin(reset))
|
||||
if CONF_BUSY_PIN in config:
|
||||
reset = await cg.gpio_pin_expression(config[CONF_BUSY_PIN])
|
||||
cg.add(var.set_busy_pin(reset))
|
||||
if CONF_FULL_UPDATE_EVERY in config:
|
||||
cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
|
||||
if CONF_RESET_DURATION in config:
|
||||
cg.add(var.set_reset_duration(config[CONF_RESET_DURATION]))
|
||||
186
esphome/components_local/waveshare_epaper/waveshare_213v3.cpp
Normal file
186
esphome/components_local/waveshare_epaper/waveshare_213v3.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "waveshare_epaper.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace waveshare_epaper {
|
||||
|
||||
static const char *const TAG = "waveshare_2.13v3";
|
||||
|
||||
static const uint8_t PARTIAL_LUT[] = {
|
||||
0x32, // cmd
|
||||
0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
|
||||
};
|
||||
|
||||
static const uint8_t FULL_LUT[] = {
|
||||
0x32, // CMD
|
||||
0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x4A, 0x80, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0xF, 0x0, 0x0, 0x2, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
|
||||
};
|
||||
|
||||
static const uint8_t SW_RESET = 0x12;
|
||||
static const uint8_t ACTIVATE = 0x20;
|
||||
static const uint8_t WRITE_BUFFER = 0x24;
|
||||
static const uint8_t WRITE_BASE = 0x26;
|
||||
|
||||
static const uint8_t DRV_OUT_CTL[] = {0x01, 0x27, 0x01, 0x00}; // driver output control
|
||||
static const uint8_t GATEV[] = {0x03, 0x17};
|
||||
static const uint8_t SRCV[] = {0x04, 0x41, 0x0C, 0x32};
|
||||
static const uint8_t SLEEP[] = {0x10, 0x01};
|
||||
static const uint8_t DATA_ENTRY[] = {0x11, 0x03}; // data entry mode
|
||||
static const uint8_t TEMP_SENS[] = {0x18, 0x80}; // Temp sensor
|
||||
static const uint8_t DISPLAY_UPDATE[] = {0x21, 0x00, 0x80}; // Display update control
|
||||
static const uint8_t UPSEQ[] = {0x22, 0xC0};
|
||||
static const uint8_t ON_FULL[] = {0x22, 0xC7};
|
||||
static const uint8_t ON_PARTIAL[] = {0x22, 0x0F};
|
||||
static const uint8_t VCOM[] = {0x2C, 0x36};
|
||||
static const uint8_t CMD5[] = {0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00};
|
||||
static const uint8_t BORDER_PART[] = {0x3C, 0x80}; // border waveform
|
||||
static const uint8_t BORDER_FULL[] = {0x3C, 0x05}; // border waveform
|
||||
static const uint8_t CMD1[] = {0x3F, 0x22};
|
||||
static const uint8_t RAM_X_START[] = {0x44, 0x00, 121 / 8}; // set ram_x_address_start_end
|
||||
static const uint8_t RAM_Y_START[] = {0x45, 0x00, 0x00, 250 - 1, 0}; // set ram_y_address_start_end
|
||||
static const uint8_t RAM_X_POS[] = {0x4E, 0x00}; // set ram_x_address_counter
|
||||
// static const uint8_t RAM_Y_POS[] = {0x4F, 0x00, 0x00}; // set ram_y_address_counter
|
||||
#define SEND(x) this->cmd_data(x, sizeof(x))
|
||||
|
||||
void WaveshareEPaper2P13InV3::write_lut_(const uint8_t *lut) {
|
||||
this->wait_until_idle_();
|
||||
this->cmd_data(lut, sizeof(PARTIAL_LUT));
|
||||
SEND(CMD1);
|
||||
SEND(GATEV);
|
||||
SEND(SRCV);
|
||||
SEND(VCOM);
|
||||
}
|
||||
|
||||
// write the buffer starting on line top, up to line bottom.
|
||||
void WaveshareEPaper2P13InV3::write_buffer_(uint8_t cmd, int top, int bottom) {
|
||||
this->wait_until_idle_();
|
||||
this->set_window_(top, bottom);
|
||||
this->command(cmd);
|
||||
this->start_data_();
|
||||
auto width_bytes = this->get_width_internal() / 8;
|
||||
this->write_array(this->buffer_ + top * width_bytes, (bottom - top) * width_bytes);
|
||||
this->end_data_();
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P13InV3::send_reset_() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(2);
|
||||
this->reset_pin_->digital_write(true);
|
||||
}
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P13InV3::setup() {
|
||||
setup_pins_();
|
||||
delay(20);
|
||||
this->send_reset_();
|
||||
// as a one-off delay this is not worth working around.
|
||||
delay(100); // NOLINT
|
||||
this->wait_until_idle_();
|
||||
this->command(SW_RESET);
|
||||
this->wait_until_idle_();
|
||||
|
||||
SEND(DRV_OUT_CTL);
|
||||
SEND(DATA_ENTRY);
|
||||
SEND(CMD5);
|
||||
this->set_window_(0, this->get_height_internal());
|
||||
SEND(BORDER_FULL);
|
||||
SEND(DISPLAY_UPDATE);
|
||||
SEND(TEMP_SENS);
|
||||
this->wait_until_idle_();
|
||||
this->write_lut_(FULL_LUT);
|
||||
}
|
||||
|
||||
// t and b are y positions, i.e. line numbers.
|
||||
void WaveshareEPaper2P13InV3::set_window_(int t, int b) {
|
||||
uint8_t buffer[3];
|
||||
|
||||
SEND(RAM_X_START);
|
||||
SEND(RAM_Y_START);
|
||||
SEND(RAM_X_POS);
|
||||
buffer[0] = 0x4F;
|
||||
buffer[1] = (uint8_t) t;
|
||||
buffer[2] = (uint8_t) (t >> 8);
|
||||
SEND(buffer);
|
||||
}
|
||||
|
||||
// must implement, but we override setup to have more control
|
||||
void WaveshareEPaper2P13InV3::initialize() {}
|
||||
|
||||
void WaveshareEPaper2P13InV3::partial_update_() {
|
||||
this->send_reset_();
|
||||
this->set_timeout(100, [this] {
|
||||
this->write_lut_(PARTIAL_LUT);
|
||||
SEND(BORDER_PART);
|
||||
SEND(UPSEQ);
|
||||
this->command(ACTIVATE);
|
||||
this->set_timeout(100, [this] {
|
||||
this->wait_until_idle_();
|
||||
this->write_buffer_(WRITE_BUFFER, 0, this->get_height_internal());
|
||||
SEND(ON_PARTIAL);
|
||||
this->command(ACTIVATE); // Activate Display Update Sequence
|
||||
this->is_busy_ = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P13InV3::full_update_() {
|
||||
ESP_LOGI(TAG, "Performing full e-paper update.");
|
||||
this->write_lut_(FULL_LUT);
|
||||
this->write_buffer_(WRITE_BUFFER, 0, this->get_height_internal());
|
||||
this->write_buffer_(WRITE_BASE, 0, this->get_height_internal());
|
||||
SEND(ON_FULL);
|
||||
this->command(ACTIVATE); // don't wait here
|
||||
this->is_busy_ = false;
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P13InV3::display() {
|
||||
if (this->is_busy_ || (this->busy_pin_ != nullptr && this->busy_pin_->digital_read()))
|
||||
return;
|
||||
this->is_busy_ = true;
|
||||
const bool partial = this->at_update_ != 0;
|
||||
this->at_update_ = (this->at_update_ + 1) % this->full_update_every_;
|
||||
if (partial) {
|
||||
this->partial_update_();
|
||||
} else {
|
||||
this->full_update_();
|
||||
}
|
||||
}
|
||||
|
||||
int WaveshareEPaper2P13InV3::get_width_internal() { return 128; }
|
||||
|
||||
int WaveshareEPaper2P13InV3::get_height_internal() { return 250; }
|
||||
|
||||
uint32_t WaveshareEPaper2P13InV3::idle_timeout_() { return 5000; }
|
||||
|
||||
void WaveshareEPaper2P13InV3::dump_config() {
|
||||
LOG_DISPLAY("", "Waveshare E-Paper", this);
|
||||
ESP_LOGCONFIG(TAG, " Model: 2.13inV3");
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||
LOG_PIN(" Busy Pin: ", this->busy_pin_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P13InV3::set_full_update_every(uint32_t full_update_every) {
|
||||
this->full_update_every_ = full_update_every;
|
||||
}
|
||||
|
||||
} // namespace waveshare_epaper
|
||||
} // namespace esphome
|
||||
3194
esphome/components_local/waveshare_epaper/waveshare_epaper.cpp
Normal file
3194
esphome/components_local/waveshare_epaper/waveshare_epaper.cpp
Normal file
File diff suppressed because it is too large
Load Diff
864
esphome/components_local/waveshare_epaper/waveshare_epaper.h
Normal file
864
esphome/components_local/waveshare_epaper/waveshare_epaper.h
Normal file
@@ -0,0 +1,864 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace waveshare_epaper {
|
||||
|
||||
class WaveshareEPaper : public display::DisplayBuffer,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_2MHZ> {
|
||||
public:
|
||||
void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; }
|
||||
float get_setup_priority() const override;
|
||||
void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
|
||||
void set_busy_pin(GPIOPin *busy) { this->busy_pin_ = busy; }
|
||||
void set_reset_duration(uint32_t reset_duration) { this->reset_duration_ = reset_duration; }
|
||||
|
||||
void command(uint8_t value);
|
||||
void data(uint8_t value);
|
||||
void cmd_data(const uint8_t *data, size_t length);
|
||||
|
||||
virtual void display() = 0;
|
||||
virtual void initialize() = 0;
|
||||
virtual void deep_sleep() = 0;
|
||||
|
||||
void update() override;
|
||||
|
||||
void setup() override {
|
||||
this->setup_pins_();
|
||||
this->initialize();
|
||||
}
|
||||
|
||||
void on_safe_shutdown() override;
|
||||
|
||||
display::DisplayType get_display_type() override;
|
||||
|
||||
void fill(Color color) override;
|
||||
|
||||
protected:
|
||||
bool wait_until_idle_();
|
||||
|
||||
void setup_pins_();
|
||||
|
||||
void reset_() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(reset_duration_); // NOLINT
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(20);
|
||||
}
|
||||
}
|
||||
|
||||
virtual int get_width_controller() { return this->get_width_internal(); };
|
||||
|
||||
uint32_t get_buffer_length_();
|
||||
uint32_t reset_duration_{200};
|
||||
|
||||
// Return the list of colors supported by the device
|
||||
virtual std::vector<Color> get_supported_colors() { return {display::COLOR_ON}; }
|
||||
|
||||
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
||||
|
||||
void start_command_();
|
||||
void end_command_();
|
||||
void start_data_();
|
||||
void end_data_();
|
||||
|
||||
GPIOPin *reset_pin_{nullptr};
|
||||
GPIOPin *dc_pin_;
|
||||
GPIOPin *busy_pin_{nullptr};
|
||||
virtual uint32_t idle_timeout_() { return 1000u; } // NOLINT(readability-identifier-naming)
|
||||
};
|
||||
|
||||
class WaveshareEPaperBWR : public WaveshareEPaper {
|
||||
public:
|
||||
std::vector<Color> get_supported_colors() override { return {display::COLOR_ON, Color(255, 0, 0, 0)}; }
|
||||
};
|
||||
|
||||
enum WaveshareEPaperTypeAModel {
|
||||
WAVESHARE_EPAPER_1_54_IN = 0,
|
||||
WAVESHARE_EPAPER_1_54_IN_V2,
|
||||
WAVESHARE_EPAPER_2_13_IN,
|
||||
WAVESHARE_EPAPER_2_13_IN_V2,
|
||||
WAVESHARE_EPAPER_2_9_IN,
|
||||
WAVESHARE_EPAPER_2_9_IN_V2,
|
||||
TTGO_EPAPER_2_13_IN,
|
||||
TTGO_EPAPER_2_13_IN_B73,
|
||||
TTGO_EPAPER_2_13_IN_B1,
|
||||
TTGO_EPAPER_2_13_IN_B74,
|
||||
};
|
||||
|
||||
class WaveshareEPaperTypeA : public WaveshareEPaper {
|
||||
public:
|
||||
WaveshareEPaperTypeA(WaveshareEPaperTypeAModel model);
|
||||
|
||||
void initialize() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
switch (this->model_) {
|
||||
// Models with specific deep sleep command and data
|
||||
case WAVESHARE_EPAPER_1_54_IN:
|
||||
case WAVESHARE_EPAPER_1_54_IN_V2:
|
||||
case WAVESHARE_EPAPER_2_9_IN_V2:
|
||||
case WAVESHARE_EPAPER_2_13_IN_V2:
|
||||
// COMMAND DEEP SLEEP MODE
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
break;
|
||||
// Other models default to simple deep sleep command
|
||||
default:
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x10);
|
||||
break;
|
||||
}
|
||||
if (this->model_ != WAVESHARE_EPAPER_2_13_IN_V2) {
|
||||
// From panel specification:
|
||||
// "After this command initiated, the chip will enter Deep Sleep Mode, BUSY pad will keep output high."
|
||||
this->wait_until_idle_();
|
||||
}
|
||||
}
|
||||
|
||||
void set_full_update_every(uint32_t full_update_every);
|
||||
|
||||
protected:
|
||||
void write_lut_(const uint8_t *lut, uint8_t size);
|
||||
|
||||
void init_display_();
|
||||
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
int get_width_controller() override;
|
||||
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
WaveshareEPaperTypeAModel model_;
|
||||
uint32_t idle_timeout_() override;
|
||||
|
||||
bool deep_sleep_between_updates_{false};
|
||||
};
|
||||
|
||||
enum WaveshareEPaperTypeBModel {
|
||||
WAVESHARE_EPAPER_2_7_IN = 0,
|
||||
WAVESHARE_EPAPER_2_7_IN_B,
|
||||
WAVESHARE_EPAPER_2_7_IN_B_V2,
|
||||
WAVESHARE_EPAPER_4_2_IN,
|
||||
WAVESHARE_EPAPER_4_2_IN_B_V2,
|
||||
WAVESHARE_EPAPER_7_5_IN,
|
||||
WAVESHARE_EPAPER_7_5_INV2,
|
||||
WAVESHARE_EPAPER_7_5_IN_B_V2,
|
||||
WAVESHARE_EPAPER_13_3_IN_K,
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P7In : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P7InB : public WaveshareEPaperBWR {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND VCOM_AND_DATA_INTERVAL_SETTING
|
||||
this->command(0x50);
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07); // deep sleep
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P7InBV2 : public WaveshareEPaperBWR {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class GDEW029T5 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P7InV2 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override { ; }
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class GDEW0154M09 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
void display() override;
|
||||
void dump_config() override;
|
||||
void deep_sleep() override;
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
int get_height_internal() override;
|
||||
|
||||
private:
|
||||
static const uint8_t CMD_DTM1_DATA_START_TRANS = 0x10;
|
||||
static const uint8_t CMD_DTM2_DATA_START_TRANS2 = 0x13;
|
||||
static const uint8_t CMD_DISPLAY_REFRESH = 0x12;
|
||||
static const uint8_t CMD_AUTO_SEQ = 0x17;
|
||||
static const uint8_t DATA_AUTO_PON_DSR_POF_DSLP = 0xA7;
|
||||
static const uint8_t CMD_PSR_PANEL_SETTING = 0x00;
|
||||
static const uint8_t CMD_UNDOCUMENTED_0x4D = 0x4D; // NOLINT
|
||||
static const uint8_t CMD_UNDOCUMENTED_0xAA = 0xaa; // NOLINT
|
||||
static const uint8_t CMD_UNDOCUMENTED_0xE9 = 0xe9; // NOLINT
|
||||
static const uint8_t CMD_UNDOCUMENTED_0xB6 = 0xb6; // NOLINT
|
||||
static const uint8_t CMD_UNDOCUMENTED_0xF3 = 0xf3; // NOLINT
|
||||
static const uint8_t CMD_TRES_RESOLUTION_SETTING = 0x61;
|
||||
static const uint8_t CMD_TCON_TCONSETTING = 0x60;
|
||||
static const uint8_t CMD_CDI_VCOM_DATA_INTERVAL = 0x50;
|
||||
static const uint8_t CMD_POF_POWER_OFF = 0x02;
|
||||
static const uint8_t CMD_DSLP_DEEP_SLEEP = 0x07;
|
||||
static const uint8_t DATA_DSLP_DEEP_SLEEP = 0xA5;
|
||||
static const uint8_t CMD_PWS_POWER_SAVING = 0xe3;
|
||||
static const uint8_t CMD_PON_POWER_ON = 0x04;
|
||||
static const uint8_t CMD_PTL_PARTIAL_WINDOW = 0x90;
|
||||
|
||||
uint8_t *lastbuff_ = nullptr;
|
||||
void reset_();
|
||||
void clear_();
|
||||
void write_init_list_(const uint8_t *list);
|
||||
void init_internal_();
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P9InB : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P9InBV3 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P9InV2R2 : public WaveshareEPaper {
|
||||
public:
|
||||
WaveshareEPaper2P9InV2R2();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override;
|
||||
|
||||
void set_full_update_every(uint32_t full_update_every);
|
||||
|
||||
protected:
|
||||
void write_lut_(const uint8_t *lut, uint8_t size);
|
||||
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
int get_width_controller() override;
|
||||
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
|
||||
private:
|
||||
void reset_();
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P9InDKE : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
}
|
||||
|
||||
void set_full_update_every(uint32_t full_update_every);
|
||||
|
||||
protected:
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper4P2In : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND VCOM AND DATA INTERVAL SETTING
|
||||
this->command(0x50);
|
||||
this->data(0x17); // border floating
|
||||
|
||||
// COMMAND VCM DC SETTING
|
||||
this->command(0x82);
|
||||
// COMMAND PANEL SETTING
|
||||
this->command(0x00);
|
||||
|
||||
delay(100); // NOLINT
|
||||
|
||||
// COMMAND POWER SETTING
|
||||
this->command(0x01);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
delay(100); // NOLINT
|
||||
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper4P2InBV2 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND VCOM AND DATA INTERVAL SETTING
|
||||
this->command(0x50);
|
||||
this->data(0xF7); // border floating
|
||||
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check code
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper5P8In : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper5P8InV2 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND VCOM AND DATA INTERVAL SETTING
|
||||
this->command(0x50);
|
||||
this->data(0x17); // border floating
|
||||
|
||||
// COMMAND VCM DC SETTING
|
||||
this->command(0x82);
|
||||
// COMMAND PANEL SETTING
|
||||
this->command(0x00);
|
||||
|
||||
delay(100); // NOLINT
|
||||
|
||||
// COMMAND POWER SETTING
|
||||
this->command(0x01);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
delay(100); // NOLINT
|
||||
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper7P5In : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper7P5InBV2 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07); // deep sleep
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper7P5InBV3 : public WaveshareEPaper {
|
||||
public:
|
||||
bool wait_until_idle_();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
this->command(0x02); // Power off
|
||||
this->wait_until_idle_();
|
||||
this->command(0x07); // Deep sleep
|
||||
this->data(0xA5);
|
||||
}
|
||||
|
||||
void clear_screen();
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
void reset_() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(200); // NOLINT
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(200); // NOLINT
|
||||
}
|
||||
};
|
||||
|
||||
void init_display_();
|
||||
};
|
||||
|
||||
class WaveshareEPaper7P5InBC : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper7P5InV2 : public WaveshareEPaper {
|
||||
public:
|
||||
bool wait_until_idle_();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND POWER OFF
|
||||
this->command(0x02);
|
||||
this->wait_until_idle_();
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
uint32_t idle_timeout_() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper7P5InV2alt : public WaveshareEPaper7P5InV2 {
|
||||
public:
|
||||
bool wait_until_idle_();
|
||||
void initialize() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void reset_() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(200); // NOLINT
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(2);
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(20);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class WaveshareEPaper7P5InHDB : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// deep sleep
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P13InDKE : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND POWER DOWN
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
// cannot wait until idle here, the device no longer responds
|
||||
}
|
||||
|
||||
void set_full_update_every(uint32_t full_update_every);
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
uint32_t idle_timeout_() override;
|
||||
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P13InV3 : public WaveshareEPaper {
|
||||
public:
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND POWER DOWN
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
// cannot wait until idle here, the device no longer responds
|
||||
}
|
||||
|
||||
void set_full_update_every(uint32_t full_update_every);
|
||||
|
||||
void setup() override;
|
||||
void initialize() override;
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
int get_height_internal() override;
|
||||
uint32_t idle_timeout_() override;
|
||||
|
||||
void write_buffer_(uint8_t cmd, int top, int bottom);
|
||||
void set_window_(int t, int b);
|
||||
void send_reset_();
|
||||
void partial_update_();
|
||||
void full_update_();
|
||||
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
bool is_busy_{false};
|
||||
void write_lut_(const uint8_t *lut);
|
||||
};
|
||||
|
||||
class WaveshareEPaper13P3InK : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
uint32_t idle_timeout_() override;
|
||||
};
|
||||
|
||||
// Generic Waveshare e-paper component that
|
||||
// avoids using any blocking wait to be able to support
|
||||
// big screens that are slow to update
|
||||
class WaveshareEPaperPolled : public WaveshareEPaper {
|
||||
// Will request a display refresh
|
||||
void update() override;
|
||||
|
||||
// Will move the state machine one state at a time
|
||||
// to refresh the display when requested by display()
|
||||
void loop() override;
|
||||
|
||||
// Unused method from parent
|
||||
void initialize() override {}
|
||||
|
||||
protected:
|
||||
// Below are display steps, called one after the other by loop()
|
||||
// Just implement these to support a new device.
|
||||
// Never sleep or wait in a step, the state machine will
|
||||
// handle it.
|
||||
|
||||
// Just after reset, set the power mode and power the driver on
|
||||
virtual void power_on() = 0;
|
||||
|
||||
// Send all the configuration required to display
|
||||
virtual void configure() = 0;
|
||||
|
||||
// Send image data and refresh the display
|
||||
void display() override = 0;
|
||||
|
||||
// Power off the driver
|
||||
virtual void power_off() = 0;
|
||||
|
||||
// Set the screen to deep sleep
|
||||
void deep_sleep() override = 0;
|
||||
|
||||
private:
|
||||
enum class State : uint8_t {
|
||||
SLEEPING,
|
||||
UPDATE_REQUESTED,
|
||||
RESETTING,
|
||||
INITIALIZING,
|
||||
POWERING_ON,
|
||||
CONFIGURING,
|
||||
DISPLAYING,
|
||||
POWERING_OFF,
|
||||
};
|
||||
|
||||
// Set the current state of the display
|
||||
void set_state_(State state);
|
||||
|
||||
// Current state of the display
|
||||
State state_{State::SLEEPING};
|
||||
// Timestamp of last state changed, used to wait between states
|
||||
uint32_t last_state_change_{0};
|
||||
};
|
||||
|
||||
// 7.5 inches screen supporting black and red color with
|
||||
// a v3 label on the back. Called EDP_7in5b_V2 in WaveShare examples.
|
||||
class WaveshareEPaper7In5BV2 : public WaveshareEPaperPolled {
|
||||
public:
|
||||
void dump_config() override;
|
||||
|
||||
void power_on() override;
|
||||
void configure() override;
|
||||
void display() override;
|
||||
void power_off() override;
|
||||
void deep_sleep() override;
|
||||
|
||||
std::vector<Color> get_supported_colors() override { return {display::COLOR_ON, Color(255, 0, 0, 0)}; }
|
||||
|
||||
protected:
|
||||
int get_width_internal() override { return 800; }
|
||||
int get_height_internal() override { return 480; }
|
||||
};
|
||||
|
||||
} // namespace waveshare_epaper
|
||||
} // namespace esphome
|
||||
375
esphome/display_hal.yaml
Normal file
375
esphome/display_hal.yaml
Normal file
@@ -0,0 +1,375 @@
|
||||
substitutions:
|
||||
device_name: "esp32-75epaper-hal"
|
||||
friendly_name: "Epaper display hal"
|
||||
comment: "7.5inch epaper display hal"
|
||||
location: "hal"
|
||||
api_password: !secret eink_display_api
|
||||
ota_password: !secret ota_password
|
||||
wifi_ssid: !secret wifi_ssid
|
||||
wifi_password: !secret wifi_password
|
||||
|
||||
|
||||
# Pins for Waveshare ePaper ESP32-S3
|
||||
pin_spi_clk: GPIO7
|
||||
pin_spi_mosi: GPIO9
|
||||
pin_ep_cs: GPIO2
|
||||
pin_ep_dc: GPIO4
|
||||
pin_ep_busy: GPIO3
|
||||
pin_ep_reset: GPIO1
|
||||
|
||||
packages:
|
||||
board: !include boards/esp32-S3.yaml
|
||||
device_base: !include common/common.yaml
|
||||
connection: !include common/wifi_nosens.yaml
|
||||
logger: !include templates/logger.yaml
|
||||
|
||||
esphome:
|
||||
includes:
|
||||
- include/epaper75.h
|
||||
- include/text_utils.h
|
||||
|
||||
external_components:
|
||||
# Use local patched waveshare component with red/black support
|
||||
- source:
|
||||
type: local
|
||||
path: components_local
|
||||
components: [ waveshare_epaper ]
|
||||
|
||||
sun:
|
||||
latitude: !secret home_latitude
|
||||
longitude: !secret home_longitude
|
||||
|
||||
globals:
|
||||
- id: data_updated
|
||||
type: bool
|
||||
restore_value: no
|
||||
initial_value: 'false'
|
||||
- id: initial_data_received
|
||||
type: bool
|
||||
restore_value: no
|
||||
initial_value: 'false'
|
||||
- id: recorded_display_refresh
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: '0'
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: esptime
|
||||
on_time_sync:
|
||||
- then:
|
||||
- logger.log: "Time synced with Home Assistant"
|
||||
on_time:
|
||||
- minutes: 5
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(data_updated) == true;'
|
||||
then:
|
||||
- lambda: 'id(initial_data_received) = true;'
|
||||
- logger.log: "Sensor data updated: Refreshing display..."
|
||||
- component.update: eink_display
|
||||
- lambda: 'id(data_updated) = false;'
|
||||
- lambda: 'id(recorded_display_refresh) += 1;'
|
||||
- lambda: 'id(display_last_update).publish_state(id(esptime).now().timestamp);'
|
||||
else:
|
||||
- logger.log: "No sensors updated - skipping display refresh."
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: "Hallway Display Refresh"
|
||||
id: refresh
|
||||
icon: "mdi:refresh"
|
||||
on_press:
|
||||
- logger.log: "Hallway display manual refresh"
|
||||
- component.update: eink_display
|
||||
|
||||
# Fonts for display
|
||||
font:
|
||||
- file: 'fonts/GothamRnd-Bold.ttf'
|
||||
id: font_date
|
||||
size: 42
|
||||
glyphs: &default-glyphs
|
||||
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
|
||||
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z','|', '/']
|
||||
|
||||
- file: 'fonts/GothamRnd-Bold.ttf'
|
||||
id: font_temperature
|
||||
size: 48
|
||||
glyphs: *default-glyphs
|
||||
|
||||
- file: 'fonts/GothamRnd-Book.ttf'
|
||||
id: font_weather_condition
|
||||
size: 20
|
||||
glyphs: *default-glyphs
|
||||
|
||||
- file: 'fonts/GothamRnd-Bold.ttf'
|
||||
id: font_section_title
|
||||
size: 20
|
||||
glyphs: *default-glyphs
|
||||
|
||||
- file: 'fonts/GothamRnd-Bold.ttf'
|
||||
id: font_section_header
|
||||
size: 28
|
||||
glyphs: *default-glyphs
|
||||
|
||||
- file: 'fonts/GothamRnd-Book.ttf'
|
||||
id: font_status_text
|
||||
size: 16
|
||||
glyphs: *default-glyphs
|
||||
|
||||
- file: 'fonts/GothamRnd-Book.ttf'
|
||||
id: font_small
|
||||
size: 12
|
||||
glyphs: *default-glyphs
|
||||
|
||||
# Material Design Icons
|
||||
- file: 'fonts/materialdesignicons-webfont.ttf'
|
||||
id: font_mdi_large
|
||||
size: 64
|
||||
glyphs:
|
||||
- "\U000F0590" # mdi-weather-cloudy
|
||||
- "\U000F0595" # mdi-weather-partly-cloudy
|
||||
- "\U000F0599" # mdi-weather-sunny
|
||||
- "\U000F0594" # mdi-weather-night
|
||||
- "\U000F0597" # mdi-weather-rainy
|
||||
- "\U000f010b" # mdi-car
|
||||
- "\U000F0575" # mdi-lock
|
||||
- "\U000F0576" # mdi-lock-open
|
||||
- "\U000F0E84" # mdi-home
|
||||
- "\U000F0048" # mdi-lightning-bolt
|
||||
|
||||
sensor:
|
||||
- platform: template
|
||||
name: "${device_name} Last Update"
|
||||
device_class: timestamp
|
||||
id: display_last_update
|
||||
|
||||
- platform: wifi_signal
|
||||
id: sensor_wifi_signal
|
||||
name: "${device_name} WiFi Signal"
|
||||
update_interval: 60s
|
||||
on_value:
|
||||
- component.update: sensor_wifi_signal_percentage
|
||||
|
||||
- platform: template
|
||||
id: sensor_wifi_signal_percentage
|
||||
name: "${device_name} WiFi Signal Percentage"
|
||||
icon: "mdi:wifi"
|
||||
unit_of_measurement: "%"
|
||||
update_interval: never
|
||||
lambda: |-
|
||||
if (id(sensor_wifi_signal).state) {
|
||||
if (id(sensor_wifi_signal).state <= -100 ) {
|
||||
return 0;
|
||||
} else if (id(sensor_wifi_signal).state >= -50) {
|
||||
return 100;
|
||||
} else {
|
||||
return 2 * (id(sensor_wifi_signal).state + 100);
|
||||
}
|
||||
} else {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
- platform: template
|
||||
name: "${device_name} Display Refresh Count"
|
||||
lambda: 'return id(recorded_display_refresh);'
|
||||
unit_of_measurement: "refreshes"
|
||||
|
||||
# Placeholder sensor - will be overwritten by HA include
|
||||
- platform: template
|
||||
name: "Weather Temperature"
|
||||
id: weather_temp
|
||||
unit_of_measurement: "°C"
|
||||
lambda: 'return 22.0;' # Mock data
|
||||
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_temperature_now, entity_id: sensor.hallway_display_data }}
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: car_charge_level, entity_id: sensor.hallway_display_data }}
|
||||
|
||||
text_sensor:
|
||||
# Pull data from Home Assistant hallway template sensor using reusable include
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: datum, entity_id: sensor.hallway_display_data }}
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: weather_condition_now, entity_id: sensor.hallway_display_data }}
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: door_status, entity_id: sensor.hallway_display_data }}
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: presence_status, entity_id: sensor.hallway_display_data }}
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: car_status, entity_id: sensor.hallway_display_data }}
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: car_range, entity_id: sensor.hallway_display_data }}
|
||||
- !include { file: sensors/homeassistant.yaml, vars: { id: last_change, entity_id: sensor.hallway_display_data }}
|
||||
|
||||
# Primary data source - pulls from Home Assistant hallway template sensor
|
||||
- platform: homeassistant
|
||||
id: display_data_source
|
||||
entity_id: sensor.hallway_display_data
|
||||
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
name: "Door Locked"
|
||||
id: door_locked
|
||||
lambda: 'return true;' # Mock data
|
||||
|
||||
- platform: template
|
||||
name: "People Home"
|
||||
id: people_home
|
||||
lambda: 'return true;' # Mock data
|
||||
|
||||
- platform: template
|
||||
name: "Car Charging"
|
||||
id: car_charging
|
||||
lambda: 'return false;' # Mock data
|
||||
|
||||
# Define colors for e-paper BV2 (3-color: white background, black and red ink)
|
||||
color:
|
||||
- id: color_black
|
||||
red: 0%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
white: 50%
|
||||
- id: color_white
|
||||
red: 0%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
white: 0%
|
||||
- id: color_red
|
||||
red: 100%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
white: 0%
|
||||
|
||||
# SPI configuration
|
||||
spi:
|
||||
clk_pin: ${pin_spi_clk}
|
||||
mosi_pin: ${pin_spi_mosi}
|
||||
|
||||
# Display configuration
|
||||
display:
|
||||
- platform: waveshare_epaper
|
||||
cs_pin: ${pin_ep_cs}
|
||||
dc_pin: ${pin_ep_dc}
|
||||
busy_pin:
|
||||
number: ${pin_ep_busy}
|
||||
inverted: true
|
||||
reset_pin: ${pin_ep_reset}
|
||||
reset_duration: 2ms
|
||||
model: 7.50in-bv2-rb
|
||||
update_interval: 30min
|
||||
id: eink_display
|
||||
rotation: 90°
|
||||
lambda: |-
|
||||
// Display: Waveshare 7.5" native 800x480, with rotation: 90° gives 480x800 (portrait)
|
||||
int w = it.get_width(); // 480 in portrait
|
||||
int h = it.get_height(); // 800 in portrait
|
||||
|
||||
// Fill background white
|
||||
it.fill(COLOR_OFF);
|
||||
|
||||
// ===== LAYOUT VARIABLES =====
|
||||
int margin = 8;
|
||||
int inner_margin = 12;
|
||||
int section_gap = 6;
|
||||
int section_width = w - 2 * margin;
|
||||
|
||||
// Sections sized to fit 800px height
|
||||
int header_y = margin;
|
||||
int header_height = 140;
|
||||
|
||||
int door_y = header_y + header_height + section_gap;
|
||||
int door_height = 110;
|
||||
|
||||
int car_y = door_y + door_height + section_gap;
|
||||
int car_height = 110;
|
||||
|
||||
int presence_y = car_y + car_height + section_gap;
|
||||
int presence_height = 110;
|
||||
|
||||
int energy_y = presence_y + presence_height + section_gap;
|
||||
int footer_height = 45;
|
||||
int footer_y = h - footer_height;
|
||||
int energy_height = footer_y - energy_y - section_gap;
|
||||
|
||||
// ===== HEADER: Date & Weather =====
|
||||
// Date - with fallback
|
||||
if (id(datum).has_state()) {
|
||||
it.printf(w / 2, header_y, id(font_date), TextAlign::TOP_CENTER, "%s", id(datum).state.c_str());
|
||||
} else {
|
||||
it.printf(w / 2, header_y, id(font_date), TextAlign::TOP_CENTER, "Loading...");
|
||||
}
|
||||
|
||||
int weather_y = header_y + 55;
|
||||
int icon_x = margin + 35;
|
||||
it.circle(icon_x, weather_y, 25);
|
||||
|
||||
int temp_col_x = icon_x + 45;
|
||||
if (id(weather_temperature_now).has_state()) {
|
||||
it.printf(temp_col_x, weather_y - 15, id(font_temperature), TextAlign::TOP_LEFT, "%2.0f°C", id(weather_temperature_now).state);
|
||||
} else {
|
||||
it.printf(temp_col_x, weather_y - 15, id(font_temperature), TextAlign::TOP_LEFT, "--°C");
|
||||
}
|
||||
|
||||
if (id(weather_condition_now).has_state()) {
|
||||
it.printf(temp_col_x, weather_y + 25, id(font_weather_condition), TextAlign::TOP_LEFT, "%s", id(weather_condition_now).state.c_str());
|
||||
} else {
|
||||
it.printf(temp_col_x, weather_y + 25, id(font_weather_condition), TextAlign::TOP_LEFT, "---");
|
||||
}
|
||||
|
||||
// Divider line
|
||||
int divider_y = header_y + header_height - 10;
|
||||
it.line(margin, divider_y, w - margin, divider_y);
|
||||
|
||||
// ===== SECTION 1: Door Lock Status =====
|
||||
it.rectangle(margin, door_y, section_width, door_height);
|
||||
it.printf(margin + inner_margin, door_y + 8, id(font_section_header), TextAlign::TOP_LEFT, "[Door]");
|
||||
if (id(door_status).has_state()) {
|
||||
it.printf(margin + inner_margin, door_y + 38, id(font_status_text), TextAlign::TOP_LEFT, "%s", id(door_status).state.c_str());
|
||||
} else {
|
||||
it.printf(margin + inner_margin, door_y + 38, id(font_status_text), TextAlign::TOP_LEFT, "Loading...");
|
||||
}
|
||||
|
||||
// ===== SECTION 2: Presence Status =====
|
||||
it.rectangle(margin, presence_y, section_width, presence_height);
|
||||
it.printf(margin + inner_margin, presence_y + 8, id(font_section_header), TextAlign::TOP_LEFT, "[Presence]");
|
||||
if (id(presence_status).has_state()) {
|
||||
it.printf(margin + inner_margin, presence_y + 38, id(font_status_text), TextAlign::TOP_LEFT, "%s", id(presence_status).state.c_str());
|
||||
} else {
|
||||
it.printf(margin + inner_margin, presence_y + 38, id(font_status_text), TextAlign::TOP_LEFT, "Loading...");
|
||||
}
|
||||
|
||||
// ===== SECTION 3: Car Battery Status =====
|
||||
it.rectangle(margin, car_y, section_width, car_height);
|
||||
it.printf(margin + inner_margin, car_y + 8, id(font_section_header), TextAlign::TOP_LEFT, "[Car]");
|
||||
if (id(car_charge_level).has_state()) {
|
||||
it.printf(margin + inner_margin, car_y + 38, id(font_status_text), TextAlign::TOP_LEFT, "Charge: %2.0f%%", id(car_charge_level).state);
|
||||
} else {
|
||||
it.printf(margin + inner_margin, car_y + 38, id(font_status_text), TextAlign::TOP_LEFT, "Charge: --%");
|
||||
}
|
||||
|
||||
// ===== SECTION 4: Energy Usage =====
|
||||
it.rectangle(margin, energy_y, section_width, energy_height);
|
||||
it.printf(margin + inner_margin, energy_y + 10, id(font_section_header), TextAlign::TOP_LEFT, "[Energy]");
|
||||
|
||||
int graph_left = margin + inner_margin;
|
||||
int graph_top = energy_y + 40;
|
||||
int graph_width = section_width - 2 * inner_margin;
|
||||
int graph_height = energy_height - 50;
|
||||
|
||||
int num_bars = 24;
|
||||
float bar_heights[] = {
|
||||
15, 28, 42, 38, 35, 48, 52, 45, 40, 55, 60, 50,
|
||||
45, 58, 62, 55, 48, 65, 70, 60, 52, 68, 75, 55
|
||||
};
|
||||
int bar_w = (graph_width - 6) / num_bars;
|
||||
|
||||
for (int i = 0; i < num_bars; i++) {
|
||||
int bh = (bar_heights[i] * graph_height) / 80;
|
||||
int bx = graph_left + (i * bar_w) + 2;
|
||||
int by = graph_top + graph_height - bh;
|
||||
it.filled_rectangle(bx, by, bar_w - 1, bh);
|
||||
}
|
||||
|
||||
// ===== FOOTER =====
|
||||
it.rectangle(margin, footer_y, section_width, footer_height);
|
||||
it.printf(w / 2, footer_y + 6, id(font_section_header), TextAlign::TOP_CENTER, "All Systems OK");
|
||||
it.printf(w - margin - inner_margin, footer_y + 20, id(font_small), TextAlign::TOP_RIGHT, "Updated");
|
||||
@@ -1,8 +1,8 @@
|
||||
substitutions:
|
||||
device_name: "hvac-ir-zolder"
|
||||
friendly_name: "IR-blaster Woonkamer"
|
||||
friendly_name: "IR-blaster Slaapkamer"
|
||||
comment: "ESP8266, Tuya-hack, IR"
|
||||
location: "woonkamer"
|
||||
location: "Slaapkamer"
|
||||
api_password: !secret hvac_zolder_api
|
||||
ota_password: !secret ota_password
|
||||
wifi_ssid: !secret wifi_ssid
|
||||
|
||||
@@ -12,4 +12,17 @@ remote_receiver:
|
||||
# input: true
|
||||
# pullup: true
|
||||
tolerance: 55%
|
||||
dump: all
|
||||
dump: all
|
||||
|
||||
# Example configuration entry
|
||||
infrared:
|
||||
# IR transmitter instance
|
||||
- platform: ir_rf_proxy
|
||||
name: IR Proxy Transmitter
|
||||
id: ir_proxy_tx
|
||||
remote_transmitter_id: irtx
|
||||
# IR receiver instance
|
||||
- platform: ir_rf_proxy
|
||||
name: IR Proxy Receiver
|
||||
id: ir_proxy_rx
|
||||
remote_receiver_id: rcvr
|
||||
@@ -1,32 +0,0 @@
|
||||
esphome:
|
||||
name: ttt
|
||||
friendly_name: Ttt
|
||||
|
||||
esp32:
|
||||
board: esp32-c3-devkitm-1
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
|
||||
# Enable Home Assistant API
|
||||
api:
|
||||
encryption:
|
||||
key: "znqjRmfKlS5XOwf4SZ0XlXkLi4VV/EIcLkg7tatYdc0="
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: "c21808863f56526f6cee79d38c0633d2"
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
|
||||
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||
ap:
|
||||
ssid: "Ttt Fallback Hotspot"
|
||||
password: "Ccn8S3iMyrvd"
|
||||
|
||||
captive_portal:
|
||||
|
||||
Reference in New Issue
Block a user