From 491f7e96f0b3ab26b84b4a6fd65cf4323a40bddb Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 28 Jun 2020 16:37:36 -0500 Subject: [PATCH 1/5] Add support for ST7789V display module (as on TTGO T-Display) (#1050) * TFT-LCD ST7789V of ESP32 TTGO. This patch allows you to use TFT-LCD ST7789V of ESP32 TTGO * Lots of polish and a few tweaks * Add test * Add color to core, take 1 * Where did those tabs come from? * Fix lines too long * Added color component * Linted * Rebase, SPI fix, test * Shuffle bits * One more thing...oops * Image type fix...oops * Make display_buffer use Color * Fix BGR/RGB, remove predefined colors * Fix all the things * renamed colors to color * migrate max7219 Co-authored-by: musk95 Co-authored-by: Guillermo Ruffino --- esphome/components/color/__init__.py | 23 ++ esphome/components/display/display_buffer.cpp | 82 ++++-- esphome/components/display/display_buffer.h | 47 +-- esphome/components/image/__init__.py | 71 +++-- .../components/max7219digit/max7219digit.cpp | 4 +- .../components/max7219digit/max7219digit.h | 2 +- esphome/components/pcd8544/pcd_8544.cpp | 8 +- esphome/components/pcd8544/pcd_8544.h | 4 +- .../components/ssd1306_base/ssd1306_base.cpp | 8 +- .../components/ssd1306_base/ssd1306_base.h | 4 +- .../components/ssd1325_base/ssd1325_base.cpp | 8 +- .../components/ssd1325_base/ssd1325_base.h | 4 +- esphome/components/st7789v/__init__.py | 3 + esphome/components/st7789v/display.py | 44 +++ esphome/components/st7789v/st7789v.cpp | 274 ++++++++++++++++++ esphome/components/st7789v/st7789v.h | 151 ++++++++++ .../waveshare_epaper/waveshare_epaper.cpp | 8 +- .../waveshare_epaper/waveshare_epaper.h | 4 +- esphome/const.py | 1 + esphome/core/color.h | 161 ++++++++++ tests/test1.yaml | 17 ++ 21 files changed, 843 insertions(+), 85 deletions(-) create mode 100644 esphome/components/color/__init__.py create mode 100644 esphome/components/st7789v/__init__.py create mode 100644 esphome/components/st7789v/display.py create mode 100644 esphome/components/st7789v/st7789v.cpp create mode 100644 esphome/components/st7789v/st7789v.h create mode 100644 esphome/core/color.h diff --git a/esphome/components/color/__init__.py b/esphome/components/color/__init__.py new file mode 100644 index 0000000000..3e2e7b2c07 --- /dev/null +++ b/esphome/components/color/__init__.py @@ -0,0 +1,23 @@ +from esphome import config_validation as cv +from esphome import codegen as cg +from esphome.const import CONF_BLUE, CONF_GREEN, CONF_ID, CONF_RED, CONF_WHITE + +ColorStruct = cg.esphome_ns.struct('Color') + +MULTI_CONF = True +CONFIG_SCHEMA = cv.Schema({ + cv.Required(CONF_ID): cv.declare_id(ColorStruct), + cv.Optional(CONF_RED, default=0.0): cv.percentage, + cv.Optional(CONF_GREEN, default=0.0): cv.percentage, + cv.Optional(CONF_BLUE, default=0.0): cv.percentage, + cv.Optional(CONF_WHITE, default=0.0): cv.percentage, +}).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + cg.variable(config[CONF_ID], cg.StructInitializer( + ColorStruct, + ('r', config[CONF_RED]), + ('g', config[CONF_GREEN]), + ('b', config[CONF_BLUE]), + ('w', config[CONF_WHITE]))) diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 453e114338..cd28e45071 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -7,8 +7,8 @@ namespace display { static const char *TAG = "display"; -const uint8_t COLOR_OFF = 0; -const uint8_t COLOR_ON = 1; +const Color COLOR_OFF = 0; +const Color COLOR_ON = 1; void DisplayBuffer::init_internal_(uint32_t buffer_length) { this->buffer_ = new uint8_t[buffer_length]; @@ -18,7 +18,7 @@ void DisplayBuffer::init_internal_(uint32_t buffer_length) { } this->clear(); } -void DisplayBuffer::fill(int color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); } +void DisplayBuffer::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); } void DisplayBuffer::clear() { this->fill(COLOR_OFF); } int DisplayBuffer::get_width() { switch (this->rotation_) { @@ -43,7 +43,7 @@ int DisplayBuffer::get_height() { } } void DisplayBuffer::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; } -void HOT DisplayBuffer::draw_pixel_at(int x, int y, int color) { +void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) { switch (this->rotation_) { case DISPLAY_ROTATION_0_DEGREES: break; @@ -63,7 +63,7 @@ void HOT DisplayBuffer::draw_pixel_at(int x, int y, int color) { this->draw_absolute_pixel_internal(x, y, color); App.feed_wdt(); } -void HOT DisplayBuffer::line(int x1, int y1, int x2, int y2, int color) { +void HOT DisplayBuffer::line(int x1, int y1, int x2, int y2, Color color) { const int32_t dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1; const int32_t dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1; int32_t err = dx + dy; @@ -83,29 +83,29 @@ void HOT DisplayBuffer::line(int x1, int y1, int x2, int y2, int color) { } } } -void HOT DisplayBuffer::horizontal_line(int x, int y, int width, int color) { +void HOT DisplayBuffer::horizontal_line(int x, int y, int width, Color color) { // Future: Could be made more efficient by manipulating buffer directly in certain rotations. for (int i = x; i < x + width; i++) this->draw_pixel_at(i, y, color); } -void HOT DisplayBuffer::vertical_line(int x, int y, int height, int color) { +void HOT DisplayBuffer::vertical_line(int x, int y, int height, Color color) { // Future: Could be made more efficient by manipulating buffer directly in certain rotations. for (int i = y; i < y + height; i++) this->draw_pixel_at(x, i, color); } -void DisplayBuffer::rectangle(int x1, int y1, int width, int height, int color) { +void DisplayBuffer::rectangle(int x1, int y1, int width, int height, Color color) { this->horizontal_line(x1, y1, width, color); this->horizontal_line(x1, y1 + height - 1, width, color); this->vertical_line(x1, y1, height, color); this->vertical_line(x1 + width - 1, y1, height, color); } -void DisplayBuffer::filled_rectangle(int x1, int y1, int width, int height, int color) { +void DisplayBuffer::filled_rectangle(int x1, int y1, int width, int height, Color color) { // Future: Use vertical_line and horizontal_line methods depending on rotation to reduce memory accesses. for (int i = y1; i < y1 + height; i++) { this->horizontal_line(x1, i, width, color); } } -void HOT DisplayBuffer::circle(int center_x, int center_xy, int radius, int color) { +void HOT DisplayBuffer::circle(int center_x, int center_xy, int radius, Color color) { int dx = -radius; int dy = 0; int err = 2 - 2 * radius; @@ -128,7 +128,7 @@ void HOT DisplayBuffer::circle(int center_x, int center_xy, int radius, int colo } } while (dx <= 0); } -void DisplayBuffer::filled_circle(int center_x, int center_y, int radius, int color) { +void DisplayBuffer::filled_circle(int center_x, int center_y, int radius, Color color) { int dx = -int32_t(radius); int dy = 0; int err = 2 - 2 * radius; @@ -155,7 +155,7 @@ void DisplayBuffer::filled_circle(int center_x, int center_y, int radius, int co } while (dx <= 0); } -void DisplayBuffer::print(int x, int y, Font *font, int color, TextAlign align, const char *text) { +void DisplayBuffer::print(int x, int y, Font *font, Color color, TextAlign align, const char *text) { int x_start, y_start; int width, height; this->get_text_bounds(x, y, text, font, align, &x_start, &y_start, &width, &height); @@ -197,16 +197,34 @@ void DisplayBuffer::print(int x, int y, Font *font, int color, TextAlign align, i += match_length; } } -void DisplayBuffer::vprintf_(int x, int y, Font *font, int color, TextAlign align, const char *format, va_list arg) { +void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg) { char buffer[256]; int ret = vsnprintf(buffer, sizeof(buffer), format, arg); if (ret > 0) this->print(x, y, font, color, align, buffer); } -void DisplayBuffer::image(int x, int y, Image *image) { - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? COLOR_ON : COLOR_OFF); +void DisplayBuffer::image(int x, int y, Image *image) { this->image(x, y, COLOR_ON, image); } +void DisplayBuffer::image(int x, int y, Color color, Image *image, bool invert) { + if (image->get_type() == BINARY) { + for (int img_x = 0; img_x < image->get_width(); img_x++) { + for (int img_y = 0; img_y < image->get_height(); img_y++) { + if (invert) + this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? COLOR_OFF : color); + else + this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? color : COLOR_OFF); + } + } + } else if (image->get_type() == GRAYSCALE4) { + for (int img_x = 0; img_x < image->get_width(); img_x++) { + for (int img_y = 0; img_y < image->get_height(); img_y++) { + this->draw_pixel_at(x + img_x, y + img_y, image->get_grayscale4_pixel(img_x, img_y)); + } + } + } else if (image->get_type() == RGB565) { + for (int img_x = 0; img_x < image->get_width(); img_x++) { + for (int img_y = 0; img_y < image->get_height(); img_y++) { + this->draw_pixel_at(x + img_x, y + img_y, image->get_color_pixel(img_x, img_y)); + } } } } @@ -248,7 +266,7 @@ void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, break; } } -void DisplayBuffer::print(int x, int y, Font *font, int color, const char *text) { +void DisplayBuffer::print(int x, int y, Font *font, Color color, const char *text) { this->print(x, y, font, color, TextAlign::TOP_LEFT, text); } void DisplayBuffer::print(int x, int y, Font *font, TextAlign align, const char *text) { @@ -257,13 +275,13 @@ void DisplayBuffer::print(int x, int y, Font *font, TextAlign align, const char void DisplayBuffer::print(int x, int y, Font *font, const char *text) { this->print(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, text); } -void DisplayBuffer::printf(int x, int y, Font *font, int color, TextAlign align, const char *format, ...) { +void DisplayBuffer::printf(int x, int y, Font *font, Color color, TextAlign align, const char *format, ...) { va_list arg; va_start(arg, format); this->vprintf_(x, y, font, color, align, format, arg); va_end(arg); } -void DisplayBuffer::printf(int x, int y, Font *font, int color, const char *format, ...) { +void DisplayBuffer::printf(int x, int y, Font *font, Color color, const char *format, ...) { va_list arg; va_start(arg, format); this->vprintf_(x, y, font, color, TextAlign::TOP_LEFT, format, arg); @@ -306,14 +324,14 @@ void DisplayBuffer::do_update_() { } } #ifdef USE_TIME -void DisplayBuffer::strftime(int x, int y, Font *font, int color, TextAlign align, const char *format, +void DisplayBuffer::strftime(int x, int y, Font *font, Color color, TextAlign align, const char *format, time::ESPTime time) { char buffer[64]; size_t ret = time.strftime(buffer, sizeof(buffer), format); if (ret > 0) this->print(x, y, font, color, align, buffer); } -void DisplayBuffer::strftime(int x, int y, Font *font, int color, const char *format, time::ESPTime time) { +void DisplayBuffer::strftime(int x, int y, Font *font, Color color, const char *format, time::ESPTime time) { this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time); } void DisplayBuffer::strftime(int x, int y, Font *font, TextAlign align, const char *format, time::ESPTime time) { @@ -431,10 +449,30 @@ bool Image::get_pixel(int x, int y) const { const uint32_t pos = x + y * width_8; return pgm_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); } +int Image::get_color_pixel(int x, int y) const { + if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) + return 0; + + const uint32_t pos = (x + y * this->width_) * 2; + int color = (pgm_read_byte(this->data_start_ + pos) << 8) + (pgm_read_byte(this->data_start_ + pos + 1)); + return color; +} +int Image::get_grayscale4_pixel(int x, int y) const { + if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) + return 0; + const uint32_t pos = (x + y * this->width_) / 2; + // 2 = number of pixels per byte, 4 = pixel shift + uint8_t shift = (x % 2) * 4; + int color = (pgm_read_byte(this->data_start_ + pos) >> shift) & 0x0f; + return color; +} int Image::get_width() const { return this->width_; } int Image::get_height() const { return this->height_; } +ImageType Image::get_type() const { return this->type_; } Image::Image(const uint8_t *data_start, int width, int height) : width_(width), height_(height), data_start_(data_start) {} +Image::Image(const uint8_t *data_start, int width, int height, int type) + : width_(width), height_(height), type_((ImageType) type), data_start_(data_start) {} DisplayPage::DisplayPage(const display_writer_t &writer) : writer_(writer) {} void DisplayPage::show() { this->parent_->show_page(this); } diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index b12fad8c8a..969a5d80cd 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/automation.h" +#include "esphome/core/color.h" #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" @@ -63,9 +64,11 @@ enum class TextAlign { }; /// Turn the pixel OFF. -extern const uint8_t COLOR_OFF; +extern const Color COLOR_OFF; /// Turn the pixel ON. -extern const uint8_t COLOR_ON; +extern const Color COLOR_ON; + +enum ImageType { BINARY = 0, GRAYSCALE4 = 1, RGB565 = 2 }; enum DisplayRotation { DISPLAY_ROTATION_0_DEGREES = 0, @@ -91,7 +94,7 @@ using display_writer_t = std::function; class DisplayBuffer { public: /// Fill the entire screen with the given color. - virtual void fill(int color); + virtual void fill(Color color); /// Clear the entire screen by filling it with OFF pixels. void clear(); @@ -100,29 +103,29 @@ class DisplayBuffer { /// Get the height of the image in pixels with rotation applied. int get_height(); /// Set a single pixel at the specified coordinates to the given color. - void draw_pixel_at(int x, int y, int color = COLOR_ON); + void draw_pixel_at(int x, int y, Color color = COLOR_ON); /// Draw a straight line from the point [x1,y1] to [x2,y2] with the given color. - void line(int x1, int y1, int x2, int y2, int color = COLOR_ON); + void line(int x1, int y1, int x2, int y2, Color color = COLOR_ON); /// Draw a horizontal line from the point [x,y] to [x+width,y] with the given color. - void horizontal_line(int x, int y, int width, int color = COLOR_ON); + void horizontal_line(int x, int y, int width, Color color = COLOR_ON); /// Draw a vertical line from the point [x,y] to [x,y+width] with the given color. - void vertical_line(int x, int y, int height, int color = COLOR_ON); + void vertical_line(int x, int y, int height, Color color = COLOR_ON); /// Draw the outline of a rectangle with the top left point at [x1,y1] and the bottom right point at /// [x1+width,y1+height]. - void rectangle(int x1, int y1, int width, int height, int color = COLOR_ON); + void rectangle(int x1, int y1, int width, int height, Color color = COLOR_ON); /// Fill a rectangle with the top left point at [x1,y1] and the bottom right point at [x1+width,y1+height]. - void filled_rectangle(int x1, int y1, int width, int height, int color = COLOR_ON); + void filled_rectangle(int x1, int y1, int width, int height, Color color = COLOR_ON); /// Draw the outline of a circle centered around [center_x,center_y] with the radius radius with the given color. - void circle(int center_x, int center_xy, int radius, int color = COLOR_ON); + void circle(int center_x, int center_xy, int radius, Color color = COLOR_ON); /// Fill a circle centered around [center_x,center_y] with the radius radius with the given color. - void filled_circle(int center_x, int center_y, int radius, int color = COLOR_ON); + void filled_circle(int center_x, int center_y, int radius, Color color = COLOR_ON); /** Print `text` with the anchor point at [x,y] with `font`. * @@ -133,7 +136,7 @@ class DisplayBuffer { * @param align The alignment of the text. * @param text The text to draw. */ - void print(int x, int y, Font *font, int color, TextAlign align, const char *text); + void print(int x, int y, Font *font, Color color, TextAlign align, const char *text); /** Print `text` with the top left at [x,y] with `font`. * @@ -143,7 +146,7 @@ class DisplayBuffer { * @param color The color to draw the text with. * @param text The text to draw. */ - void print(int x, int y, Font *font, int color, const char *text); + void print(int x, int y, Font *font, Color color, const char *text); /** Print `text` with the anchor point at [x,y] with `font`. * @@ -174,7 +177,7 @@ class DisplayBuffer { * @param format The format to use. * @param ... The arguments to use for the text formatting. */ - void printf(int x, int y, Font *font, int color, TextAlign align, const char *format, ...) + void printf(int x, int y, Font *font, Color color, TextAlign align, const char *format, ...) __attribute__((format(printf, 7, 8))); /** Evaluate the printf-format `format` and print the result with the top left at [x,y] with `font`. @@ -186,7 +189,7 @@ class DisplayBuffer { * @param format The format to use. * @param ... The arguments to use for the text formatting. */ - void printf(int x, int y, Font *font, int color, const char *format, ...) __attribute__((format(printf, 6, 7))); + void printf(int x, int y, Font *font, Color color, const char *format, ...) __attribute__((format(printf, 6, 7))); /** Evaluate the printf-format `format` and print the result with the anchor point at [x,y] with `font`. * @@ -220,7 +223,7 @@ class DisplayBuffer { * @param format The strftime format to use. * @param time The time to format. */ - void strftime(int x, int y, Font *font, int color, TextAlign align, const char *format, time::ESPTime time) + void strftime(int x, int y, Font *font, Color color, TextAlign align, const char *format, time::ESPTime time) __attribute__((format(strftime, 7, 0))); /** Evaluate the strftime-format `format` and print the result with the top left at [x,y] with `font`. @@ -232,7 +235,7 @@ class DisplayBuffer { * @param format The strftime format to use. * @param time The time to format. */ - void strftime(int x, int y, Font *font, int color, const char *format, time::ESPTime time) + void strftime(int x, int y, Font *font, Color color, const char *format, time::ESPTime time) __attribute__((format(strftime, 6, 0))); /** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`. @@ -261,6 +264,7 @@ class DisplayBuffer { /// Draw the `image` with the top-left corner at [x,y] to the screen. void image(int x, int y, Image *image); + void image(int x, int y, Color color, Image *image, bool invert = false); /** Get the text bounds of the given string. * @@ -290,9 +294,9 @@ class DisplayBuffer { void set_rotation(DisplayRotation rotation); protected: - void vprintf_(int x, int y, Font *font, int color, TextAlign align, const char *format, va_list arg); + void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg); - virtual void draw_absolute_pixel_internal(int x, int y, int color) = 0; + virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0; virtual int get_height_internal() = 0; @@ -378,13 +382,18 @@ class Font { class Image { public: Image(const uint8_t *data_start, int width, int height); + Image(const uint8_t *data_start, int width, int height, int type); bool get_pixel(int x, int y) const; + int get_color_pixel(int x, int y) const; + int get_grayscale4_pixel(int x, int y) const; int get_width() const; int get_height() const; + ImageType get_type() const; protected: int width_; int height_; + ImageType type_{BINARY}; const uint8_t *data_start_; }; diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index d0a41a7379..62379b11bb 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -4,13 +4,15 @@ from esphome import core from esphome.components import display, font import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_FILE, CONF_ID, CONF_RESIZE +from esphome.const import CONF_FILE, CONF_ID, CONF_RESIZE, CONF_TYPE from esphome.core import CORE, HexInt _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['display'] MULTI_CONF = True +ImageType = {'binary': 0, 'grayscale4': 1, 'rgb565': 2} + Image_ = display.display_ns.class_('Image') CONF_RAW_DATA_ID = 'raw_data_id' @@ -19,6 +21,7 @@ IMAGE_SCHEMA = cv.Schema({ cv.Required(CONF_ID): cv.declare_id(Image_), cv.Required(CONF_FILE): cv.file_, cv.Optional(CONF_RESIZE): cv.dimensions, + cv.Optional(CONF_TYPE): cv.string, cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), }) @@ -37,20 +40,54 @@ def to_code(config): if CONF_RESIZE in config: image.thumbnail(config[CONF_RESIZE]) - image = image.convert('1', dither=Image.NONE) - width, height = image.size - if width > 500 or height > 500: - _LOGGER.warning("The image you requested is very big. Please consider using the resize " - "parameter") - width8 = ((width + 7) // 8) * 8 - data = [0 for _ in range(height * width8 // 8)] - for y in range(height): - for x in range(width): - if image.getpixel((x, y)): - continue - pos = x + y * width8 - data[pos // 8] |= 0x80 >> (pos % 8) + if CONF_TYPE in config: + if config[CONF_TYPE].startswith('GRAYSCALE4'): + width, height = image.size + image = image.convert('L', dither=Image.NONE) + pixels = list(image.getdata()) + data = [0 for _ in range(height * width // 2)] + pos = 0 + for pixnum, pix in enumerate(pixels): + pixshift = (pixnum % 2) * 4 + data[pos] |= (pix >> 4) << pixshift + if pixshift != 0: + pos += 1 + rhs = [HexInt(x) for x in data] + prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, ImageType['grayscale4']) + elif config[CONF_TYPE].startswith('RGB565'): + width, height = image.size + image = image.convert('RGB') + pixels = list(image.getdata()) + data = [0 for _ in range(height * width * 2)] + pos = 0 + for pix in pixels: + r = (pix[0] >> 3) & 0x1F + g = (pix[1] >> 2) & 0x3F + b = (pix[2] >> 3) & 0x1F + p = (r << 11) + (g << 5) + b + data[pos] = (p >> 8) & 0xFF + pos += 1 + data[pos] = p & 0xFF + pos += 1 + rhs = [HexInt(x) for x in data] + prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, ImageType['rgb565']) + else: + image = image.convert('1', dither=Image.NONE) + width, height = image.size + if width > 500 or height > 500: + _LOGGER.warning("The image you requested is very big. Please consider using" + " the resize parameter.") + width8 = ((width + 7) // 8) * 8 + data = [0 for _ in range(height * width8 // 8)] + for y in range(height): + for x in range(width): + if image.getpixel((x, y)): + continue + pos = x + y * width8 + data[pos // 8] |= 0x80 >> (pos % 8) - rhs = [HexInt(x) for x in data] - prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.new_Pvariable(config[CONF_ID], prog_arr, width, height) + rhs = [HexInt(x) for x in data] + prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + cg.new_Pvariable(config[CONF_ID], prog_arr, width, height) diff --git a/esphome/components/max7219digit/max7219digit.cpp b/esphome/components/max7219digit/max7219digit.cpp index b19f05143f..dff1d27370 100644 --- a/esphome/components/max7219digit/max7219digit.cpp +++ b/esphome/components/max7219digit/max7219digit.cpp @@ -123,7 +123,7 @@ int MAX7219Component::get_width_internal() { return this->num_chips_ * 8; } size_t MAX7219Component::get_buffer_length_() { return this->num_chips_ * 8; } -void HOT MAX7219Component::draw_absolute_pixel_internal(int x, int y, int color) { +void HOT MAX7219Component::draw_absolute_pixel_internal(int x, int y, Color color) { if (x + 1 > this->max_displaybuffer_.size()) { // Extend the display buffer in case required this->max_displaybuffer_.resize(x + 1, this->bckgrnd_); } @@ -134,7 +134,7 @@ void HOT MAX7219Component::draw_absolute_pixel_internal(int x, int y, int color) uint16_t pos = x; // X is starting at 0 top left uint8_t subpos = y; // Y is starting at 0 top left - if (color == 1) { + if (color.is_on()) { this->max_displaybuffer_[pos] |= (1 << subpos); } else { this->max_displaybuffer_[pos] &= ~(1 << subpos); diff --git a/esphome/components/max7219digit/max7219digit.h b/esphome/components/max7219digit/max7219digit.h index b032f33b58..dfd61e84e5 100644 --- a/esphome/components/max7219digit/max7219digit.h +++ b/esphome/components/max7219digit/max7219digit.h @@ -40,7 +40,7 @@ class MAX7219Component : public PollingComponent, void turn_on_off(bool on_off); - void draw_absolute_pixel_internal(int x, int y, int color) override; + void draw_absolute_pixel_internal(int x, int y, Color color) override; int get_height_internal() override; int get_width_internal() override; diff --git a/esphome/components/pcd8544/pcd_8544.cpp b/esphome/components/pcd8544/pcd_8544.cpp index ed9d1bbd43..e47d71e8af 100644 --- a/esphome/components/pcd8544/pcd_8544.cpp +++ b/esphome/components/pcd8544/pcd_8544.cpp @@ -85,14 +85,14 @@ void HOT PCD8544::display() { this->command(this->PCD8544_SETYADDR); } -void HOT PCD8544::draw_absolute_pixel_internal(int x, int y, int color) { +void HOT PCD8544::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) { return; } uint16_t pos = x + (y / 8) * this->get_width_internal(); uint8_t subpos = y % 8; - if (color) { + if (color.is_on()) { this->buffer_[pos] |= (1 << subpos); } else { this->buffer_[pos] &= ~(1 << subpos); @@ -117,8 +117,8 @@ void PCD8544::update() { this->display(); } -void PCD8544::fill(int color) { - uint8_t fill = color ? 0xFF : 0x00; +void PCD8544::fill(Color color) { + uint8_t fill = color.is_on() ? 0xFF : 0x00; for (uint32_t i = 0; i < this->get_buffer_length_(); i++) this->buffer_[i] = fill; } diff --git a/esphome/components/pcd8544/pcd_8544.h b/esphome/components/pcd8544/pcd_8544.h index a1c247bf7b..4c590b402c 100644 --- a/esphome/components/pcd8544/pcd_8544.h +++ b/esphome/components/pcd8544/pcd_8544.h @@ -43,7 +43,7 @@ class PCD8544 : public PollingComponent, void update() override; - void fill(int color) override; + void fill(Color color) override; void setup() override { this->setup_pins_(); @@ -51,7 +51,7 @@ class PCD8544 : public PollingComponent, } protected: - void draw_absolute_pixel_internal(int x, int y, int color) override; + void draw_absolute_pixel_internal(int x, int y, Color color) override; void setup_pins_(); diff --git a/esphome/components/ssd1306_base/ssd1306_base.cpp b/esphome/components/ssd1306_base/ssd1306_base.cpp index d60f7dc985..2c1e5c6de8 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.cpp +++ b/esphome/components/ssd1306_base/ssd1306_base.cpp @@ -179,20 +179,20 @@ size_t SSD1306::get_buffer_length_() { return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u; } -void HOT SSD1306::draw_absolute_pixel_internal(int x, int y, int color) { +void HOT SSD1306::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; uint16_t pos = x + (y / 8) * this->get_width_internal(); uint8_t subpos = y & 0x07; - if (color) { + if (color.is_on()) { this->buffer_[pos] |= (1 << subpos); } else { this->buffer_[pos] &= ~(1 << subpos); } } -void SSD1306::fill(int color) { - uint8_t fill = color ? 0xFF : 0x00; +void SSD1306::fill(Color color) { + uint8_t fill = color.is_on() ? 0xFF : 0x00; for (uint32_t i = 0; i < this->get_buffer_length_(); i++) this->buffer_[i] = fill; } diff --git a/esphome/components/ssd1306_base/ssd1306_base.h b/esphome/components/ssd1306_base/ssd1306_base.h index 8adf3c1b87..3e46ef9cc7 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.h +++ b/esphome/components/ssd1306_base/ssd1306_base.h @@ -32,7 +32,7 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { void set_brightness(float brightness) { this->brightness_ = brightness; } float get_setup_priority() const override { return setup_priority::PROCESSOR; } - void fill(int color) override; + void fill(Color color) override; protected: virtual void command(uint8_t value) = 0; @@ -41,7 +41,7 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { bool is_sh1106_() const; - void draw_absolute_pixel_internal(int x, int y, int color) override; + void draw_absolute_pixel_internal(int x, int y, Color color) override; int get_height_internal() override; int get_width_internal() override; diff --git a/esphome/components/ssd1325_base/ssd1325_base.cpp b/esphome/components/ssd1325_base/ssd1325_base.cpp index 22dc51e790..044582804f 100644 --- a/esphome/components/ssd1325_base/ssd1325_base.cpp +++ b/esphome/components/ssd1325_base/ssd1325_base.cpp @@ -144,20 +144,20 @@ size_t SSD1325::get_buffer_length_() { return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u; } -void HOT SSD1325::draw_absolute_pixel_internal(int x, int y, int color) { +void HOT SSD1325::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; uint16_t pos = x + (y / 8) * this->get_width_internal(); uint8_t subpos = y % 8; - if (color) { + if (color.is_on()) { this->buffer_[pos] |= (1 << subpos); } else { this->buffer_[pos] &= ~(1 << subpos); } } -void SSD1325::fill(int color) { - uint8_t fill = color ? 0xFF : 0x00; +void SSD1325::fill(Color color) { + uint8_t fill = color.is_on() ? 0xFF : 0x00; for (uint32_t i = 0; i < this->get_buffer_length_(); i++) this->buffer_[i] = fill; } diff --git a/esphome/components/ssd1325_base/ssd1325_base.h b/esphome/components/ssd1325_base/ssd1325_base.h index e796b85a33..b5b28dfbae 100644 --- a/esphome/components/ssd1325_base/ssd1325_base.h +++ b/esphome/components/ssd1325_base/ssd1325_base.h @@ -28,14 +28,14 @@ class SSD1325 : public PollingComponent, public display::DisplayBuffer { void set_external_vcc(bool external_vcc) { this->external_vcc_ = external_vcc; } float get_setup_priority() const override { return setup_priority::PROCESSOR; } - void fill(int color) override; + void fill(Color color) override; protected: virtual void command(uint8_t value) = 0; virtual void write_display_data() = 0; void init_reset_(); - void draw_absolute_pixel_internal(int x, int y, int color) override; + void draw_absolute_pixel_internal(int x, int y, Color color) override; int get_height_internal() override; int get_width_internal() override; diff --git a/esphome/components/st7789v/__init__.py b/esphome/components/st7789v/__init__.py new file mode 100644 index 0000000000..dc85fa6b76 --- /dev/null +++ b/esphome/components/st7789v/__init__.py @@ -0,0 +1,3 @@ +import esphome.codegen as cg + +st7789v_ns = cg.esphome_ns.namespace('st7789v') diff --git a/esphome/components/st7789v/display.py b/esphome/components/st7789v/display.py new file mode 100644 index 0000000000..4723daf0e4 --- /dev/null +++ b/esphome/components/st7789v/display.py @@ -0,0 +1,44 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import display, spi +from esphome.const import CONF_BACKLIGHT_PIN, CONF_BRIGHTNESS, CONF_CS_PIN, CONF_DC_PIN, CONF_ID, \ + CONF_LAMBDA, CONF_RESET_PIN +from . import st7789v_ns + +DEPENDENCIES = ['spi'] + +ST7789V = st7789v_ns.class_('ST7789V', cg.PollingComponent, spi.SPIDevice, + display.DisplayBuffer) +ST7789VRef = ST7789V.operator('ref') + +CONFIG_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(ST7789V), + cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_BACKLIGHT_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, +}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema()) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield spi.register_spi_device(var, config) + + dc = yield cg.gpio_pin_expression(config[CONF_DC_PIN]) + cg.add(var.set_dc_pin(dc)) + + reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) + cg.add(var.set_reset_pin(reset)) + + bl = yield cg.gpio_pin_expression(config[CONF_BACKLIGHT_PIN]) + cg.add(var.set_backlight_pin(bl)) + + if CONF_LAMBDA in config: + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + cg.add(var.set_writer(lambda_)) + + yield display.register_display(var, config) diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp new file mode 100644 index 0000000000..284f2342fc --- /dev/null +++ b/esphome/components/st7789v/st7789v.cpp @@ -0,0 +1,274 @@ +#include "st7789v.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace st7789v { + +static const char *TAG = "st7789v"; + +void ST7789V::setup() { + ESP_LOGCONFIG(TAG, "Setting up SPI ST7789V..."); + this->spi_setup(); + this->dc_pin_->setup(); // OUTPUT + + this->init_reset_(); + + this->write_command_(ST7789_SLPOUT); // Sleep out + delay(120); // NOLINT + + this->write_command_(ST7789_NORON); // Normal display mode on + + // *** display and color format setting *** + this->write_command_(ST7789_MADCTL); + this->write_data_(ST7789_MADCTL_COLOR_ORDER); + + // JLX240 display datasheet + this->write_command_(0xB6); + this->write_data_(0x0A); + this->write_data_(0x82); + + this->write_command_(ST7789_COLMOD); + this->write_data_(0x55); + delay(10); + + // *** ST7789V Frame rate setting *** + this->write_command_(ST7789_PORCTRL); + this->write_data_(0x0c); + this->write_data_(0x0c); + this->write_data_(0x00); + this->write_data_(0x33); + this->write_data_(0x33); + + this->write_command_(ST7789_GCTRL); // Voltages: VGH / VGL + this->write_data_(0x35); + + // *** ST7789V Power setting *** + this->write_command_(ST7789_VCOMS); + this->write_data_(0x28); // JLX240 display datasheet + + this->write_command_(ST7789_LCMCTRL); + this->write_data_(0x0C); + + this->write_command_(ST7789_VDVVRHEN); + this->write_data_(0x01); + this->write_data_(0xFF); + + this->write_command_(ST7789_VRHS); // voltage VRHS + this->write_data_(0x10); + + this->write_command_(ST7789_VDVS); + this->write_data_(0x20); + + this->write_command_(ST7789_FRCTRL2); + this->write_data_(0x0f); + + this->write_command_(ST7789_PWCTRL1); + this->write_data_(0xa4); + this->write_data_(0xa1); + + // *** ST7789V gamma setting *** + this->write_command_(ST7789_PVGAMCTRL); + this->write_data_(0xd0); + this->write_data_(0x00); + this->write_data_(0x02); + this->write_data_(0x07); + this->write_data_(0x0a); + this->write_data_(0x28); + this->write_data_(0x32); + this->write_data_(0x44); + this->write_data_(0x42); + this->write_data_(0x06); + this->write_data_(0x0e); + this->write_data_(0x12); + this->write_data_(0x14); + this->write_data_(0x17); + + this->write_command_(ST7789_NVGAMCTRL); + this->write_data_(0xd0); + this->write_data_(0x00); + this->write_data_(0x02); + this->write_data_(0x07); + this->write_data_(0x0a); + this->write_data_(0x28); + this->write_data_(0x31); + this->write_data_(0x54); + this->write_data_(0x47); + this->write_data_(0x0e); + this->write_data_(0x1c); + this->write_data_(0x17); + this->write_data_(0x1b); + this->write_data_(0x1e); + + this->write_command_(ST7789_INVON); + + // Clear display - ensures we do not see garbage at power-on + this->draw_filled_rect_(0, 0, 239, 319, 0x0000); + + delay(120); // NOLINT + + this->write_command_(ST7789_DISPON); // Display on + delay(120); // NOLINT + + backlight_(true); + + this->init_internal_(this->get_buffer_length_()); + memset(this->buffer_, 0x00, this->get_buffer_length_()); +} + +void ST7789V::dump_config() { + LOG_DISPLAY("", "SPI ST7789V", this); + LOG_PIN(" CS Pin: ", this->cs_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" B/L Pin: ", this->backlight_pin_); + LOG_UPDATE_INTERVAL(this); +} + +float ST7789V::get_setup_priority() const { return setup_priority::PROCESSOR; } + +void ST7789V::update() { + this->do_update_(); + this->write_display_data(); +} + +void ST7789V::loop() {} + +void ST7789V::write_display_data() { + uint16_t x1 = 52; // _offsetx + uint16_t x2 = 186; // _offsetx + uint16_t y1 = 40; // _offsety + uint16_t y2 = 279; // _offsety + + this->enable(); + + // set column(x) address + this->dc_pin_->digital_write(false); + this->write_byte(ST7789_CASET); + this->dc_pin_->digital_write(true); + this->write_addr_(x1, x2); + // set page(y) address + this->dc_pin_->digital_write(false); + this->write_byte(ST7789_RASET); + this->dc_pin_->digital_write(true); + this->write_addr_(y1, y2); + // write display memory + this->dc_pin_->digital_write(false); + this->write_byte(ST7789_RAMWR); + this->dc_pin_->digital_write(true); + + this->write_array(this->buffer_, this->get_buffer_length_()); + + this->disable(); +} + +void ST7789V::init_reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(true); + delay(1); + // Trigger Reset + this->reset_pin_->digital_write(false); + delay(10); + // Wake up + this->reset_pin_->digital_write(true); + } +} + +void ST7789V::backlight_(bool onoff) { + if (this->backlight_pin_ != nullptr) { + this->backlight_pin_->setup(); + this->backlight_pin_->digital_write(onoff); + } +} + +void ST7789V::write_command_(uint8_t value) { + this->enable(); + this->dc_pin_->digital_write(false); + this->write_byte(value); + this->dc_pin_->digital_write(true); + this->disable(); +} + +void ST7789V::write_data_(uint8_t value) { + this->dc_pin_->digital_write(true); + this->enable(); + this->write_byte(value); + this->disable(); +} + +void ST7789V::write_addr_(uint16_t addr1, uint16_t addr2) { + static uint8_t BYTE[4]; + BYTE[0] = (addr1 >> 8) & 0xFF; + BYTE[1] = addr1 & 0xFF; + BYTE[2] = (addr2 >> 8) & 0xFF; + BYTE[3] = addr2 & 0xFF; + + this->dc_pin_->digital_write(true); + this->write_array(BYTE, 4); +} + +void ST7789V::write_color_(uint16_t color, uint16_t size) { + static uint8_t BYTE[1024]; + int index = 0; + for (int i = 0; i < size; i++) { + BYTE[index++] = (color >> 8) & 0xFF; + BYTE[index++] = color & 0xFF; + } + + this->dc_pin_->digital_write(true); + return write_array(BYTE, size * 2); +} + +int ST7789V::get_height_internal() { + return 240; // 320; +} + +int ST7789V::get_width_internal() { + return 135; // 240; +} + +size_t ST7789V::get_buffer_length_() { + return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) * 2; +} + +// Draw a filled rectangle +// x1: Start X coordinate +// y1: Start Y coordinate +// x2: End X coordinate +// y2: End Y coordinate +// color: color +void ST7789V::draw_filled_rect_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { + // ESP_LOGD(TAG,"offset(x)=%d offset(y)=%d",dev->_offsetx,dev->_offsety); + this->enable(); + this->dc_pin_->digital_write(false); + this->write_byte(ST7789_CASET); // set column(x) address + this->dc_pin_->digital_write(true); + this->write_addr_(x1, x2); + + this->dc_pin_->digital_write(false); + this->write_byte(ST7789_RASET); // set Page(y) address + this->dc_pin_->digital_write(true); + this->write_addr_(y1, y2); + this->dc_pin_->digital_write(false); + this->write_byte(ST7789_RAMWR); // begin a write to memory + this->dc_pin_->digital_write(true); + for (int i = x1; i <= x2; i++) { + uint16_t size = y2 - y1 + 1; + this->write_color_(color, size); + } + this->disable(); +} + +void HOT ST7789V::draw_absolute_pixel_internal(int x, int y, Color color) { + if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) + return; + + auto color565 = color.to_rgb_565(); + + uint16_t pos = (x + y * this->get_width_internal()) * 2; + this->buffer_[pos++] = (color565 >> 8) & 0xff; + this->buffer_[pos] = color565 & 0xff; +} + +} // namespace st7789v +} // namespace esphome diff --git a/esphome/components/st7789v/st7789v.h b/esphome/components/st7789v/st7789v.h new file mode 100644 index 0000000000..0e17e65fd7 --- /dev/null +++ b/esphome/components/st7789v/st7789v.h @@ -0,0 +1,151 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/spi/spi.h" +#include "esphome/components/display/display_buffer.h" + +namespace esphome { +namespace st7789v { + +static const uint8_t BLACK = 0; +static const uint8_t WHITE = 1; + +static const uint8_t ST7789_NOP = 0x00; // No Operation +static const uint8_t ST7789_SWRESET = 0x01; // Software Reset +static const uint8_t ST7789_RDDID = 0x04; // Read Display ID +static const uint8_t ST7789_RDDST = 0x09; // Read Display Status +static const uint8_t ST7789_RDDPM = 0x0A; // Read Display Power Mode +static const uint8_t ST7789_RDDMADCTL = 0x0B; // Read Display MADCTL +static const uint8_t ST7789_RDDCOLMOD = 0x0C; // Read Display Pixel Format +static const uint8_t ST7789_RDDIM = 0x0D; // Read Display Image Mode +static const uint8_t ST7789_RDDSM = 0x0E; // Read Display Signal Mod +static const uint8_t ST7789_RDDSDR = 0x0F; // Read Display Self-Diagnostic Resul +static const uint8_t ST7789_SLPIN = 0x10; // Sleep in +static const uint8_t ST7789_SLPOUT = 0x11; // Sleep Out +static const uint8_t ST7789_PTLON = 0x12; // Partial Display Mode O +static const uint8_t ST7789_NORON = 0x13; // Normal Display Mode O +static const uint8_t ST7789_INVOFF = 0x20; // Display Inversion Off +static const uint8_t ST7789_INVON = 0x21; // Display Inversion O +static const uint8_t ST7789_GAMSET = 0x26; // Gamma Set +static const uint8_t ST7789_DISPOFF = 0x28; // Display Off +static const uint8_t ST7789_DISPON = 0x29; // Display On +static const uint8_t ST7789_CASET = 0x2A; // Column Address Set +static const uint8_t ST7789_RASET = 0x2B; // Row Address Set +static const uint8_t ST7789_RAMWR = 0x2C; // Memory Write +static const uint8_t ST7789_RAMRD = 0x2E; // Memory Read +static const uint8_t ST7789_PTLAR = 0x30; // Partial Area +static const uint8_t ST7789_VSCRDEF = 0x33; // Vertical Scrolling Definitio +static const uint8_t ST7789_TEOFF = 0x34; // Tearing Effect Line OFF +static const uint8_t ST7789_TEON = 0x35; // Tearing Effect Line On +static const uint8_t ST7789_MADCTL = 0x36; // Memory Data Access Control +static const uint8_t ST7789_VSCSAD = 0x37; // Vertical Scroll Start Address of RAM +static const uint8_t ST7789_IDMOFF = 0x38; // Idle Mode Off +static const uint8_t ST7789_IDMON = 0x39; // Idle mode on +static const uint8_t ST7789_COLMOD = 0x3A; // Interface Pixel Format +static const uint8_t ST7789_WRMEMC = 0x3C; // Write Memory Continue +static const uint8_t ST7789_RDMEMC = 0x3E; // Read Memory Continue +static const uint8_t ST7789_STE = 0x44; // Set Tear Scanline +static const uint8_t ST7789_GSCAN = 0x45; // Get Scanlin +static const uint8_t ST7789_WRDISBV = 0x51; // Write Display Brightness +static const uint8_t ST7789_RDDISBV = 0x52; // Read Display Brightness Value +static const uint8_t ST7789_WRCTRLD = 0x53; // Write CTRL Display +static const uint8_t ST7789_RDCTRLD = 0x54; // Read CTRL Value Display +static const uint8_t ST7789_WRCACE = 0x55; // Write Content Adaptive Brightness Control and Color Enhancement +static const uint8_t ST7789_RDCABC = 0x56; // Read Content Adaptive Brightness Control +static const uint8_t ST7789_WRCABCMB = 0x5E; // Write CABC Minimum Brightnes +static const uint8_t ST7789_RDCABCMB = 0x5F; // Read CABC Minimum Brightnes +static const uint8_t ST7789_RDABCSDR = 0x68; // Read Automatic Brightness Control Self-Diagnostic Result +static const uint8_t ST7789_RDID1 = 0xDA; // Read ID1 +static const uint8_t ST7789_RDID2 = 0xDB; // Read ID2 +static const uint8_t ST7789_RDID3 = 0xDC; // Read ID3 +static const uint8_t ST7789_RAMCTRL = 0xB0; // RAM Control +static const uint8_t ST7789_RGBCTRL = 0xB1; // RGB Interface Contro +static const uint8_t ST7789_PORCTRL = 0xB2; // Porch Setting +static const uint8_t ST7789_FRCTRL1 = 0xB3; // Frame Rate Control 1 (In partial mode/ idle colors) +static const uint8_t ST7789_PARCTRL = 0xB5; // Partial mode Contro +static const uint8_t ST7789_GCTRL = 0xB7; // Gate Contro +static const uint8_t ST7789_GTADJ = 0xB8; // Gate On Timing Adjustmen +static const uint8_t ST7789_DGMEN = 0xBA; // Digital Gamma Enable +static const uint8_t ST7789_VCOMS = 0xBB; // VCOMS Setting +static const uint8_t ST7789_LCMCTRL = 0xC0; // LCM Control +static const uint8_t ST7789_IDSET = 0xC1; // ID Code Settin +static const uint8_t ST7789_VDVVRHEN = 0xC2; // VDV and VRH Command Enabl +static const uint8_t ST7789_VRHS = 0xC3; // VRH Set +static const uint8_t ST7789_VDVS = 0xC4; // VDV Set +static const uint8_t ST7789_VCMOFSET = 0xC5; // VCOMS Offset Set +static const uint8_t ST7789_FRCTRL2 = 0xC6; // Frame Rate Control in Normal Mode +static const uint8_t ST7789_CABCCTRL = 0xC7; // CABC Control +static const uint8_t ST7789_REGSEL1 = 0xC8; // Register Value Selection 1 +static const uint8_t ST7789_REGSEL2 = 0xCA; // Register Value Selection +static const uint8_t ST7789_PWMFRSEL = 0xCC; // PWM Frequency Selection +static const uint8_t ST7789_PWCTRL1 = 0xD0; // Power Control 1 +static const uint8_t ST7789_VAPVANEN = 0xD2; // Enable VAP/VAN signal output +static const uint8_t ST7789_CMD2EN = 0xDF; // Command 2 Enable +static const uint8_t ST7789_PVGAMCTRL = 0xE0; // Positive Voltage Gamma Control +static const uint8_t ST7789_NVGAMCTRL = 0xE1; // Negative Voltage Gamma Control +static const uint8_t ST7789_DGMLUTR = 0xE2; // Digital Gamma Look-up Table for Red +static const uint8_t ST7789_DGMLUTB = 0xE3; // Digital Gamma Look-up Table for Blue +static const uint8_t ST7789_GATECTRL = 0xE4; // Gate Control +static const uint8_t ST7789_SPI2EN = 0xE7; // SPI2 Enable +static const uint8_t ST7789_PWCTRL2 = 0xE8; // Power Control 2 +static const uint8_t ST7789_EQCTRL = 0xE9; // Equalize time control +static const uint8_t ST7789_PROMCTRL = 0xEC; // Program Mode Contro +static const uint8_t ST7789_PROMEN = 0xFA; // Program Mode Enabl +static const uint8_t ST7789_NVMSET = 0xFC; // NVM Setting +static const uint8_t ST7789_PROMACT = 0xFE; // Program action + +// Flags for ST7789_MADCTL +static const uint8_t ST7789_MADCTL_MY = 0x80; +static const uint8_t ST7789_MADCTL_MX = 0x40; +static const uint8_t ST7789_MADCTL_MV = 0x20; +static const uint8_t ST7789_MADCTL_ML = 0x10; +static const uint8_t ST7789_MADCTL_RGB = 0x00; +static const uint8_t ST7789_MADCTL_BGR = 0x08; +static const uint8_t ST7789_MADCTL_MH = 0x04; +static const uint8_t ST7789_MADCTL_SS = 0x02; +static const uint8_t ST7789_MADCTL_GS = 0x01; + +static const uint8_t ST7789_MADCTL_COLOR_ORDER = ST7789_MADCTL_BGR; + +class ST7789V : public PollingComponent, + public display::DisplayBuffer, + public spi::SPIDevice { + public: + void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; } + void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } + void set_backlight_pin(GPIOPin *backlight_pin) { this->backlight_pin_ = backlight_pin; } + + // ========== INTERNAL METHODS ========== + // (In most use cases you won't need these) + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void update() override; + void loop() override; + + void write_display_data(); + + protected: + GPIOPin *dc_pin_; + GPIOPin *reset_pin_{nullptr}; + GPIOPin *backlight_pin_{nullptr}; + + void init_reset_(); + void backlight_(bool onoff); + void write_command_(uint8_t value); + void write_data_(uint8_t value); + void write_addr_(uint16_t addr1, uint16_t addr2); + void write_color_(uint16_t color, uint16_t size); + + int get_height_internal() override; + int get_width_internal() override; + size_t get_buffer_length_(); + + void draw_filled_rect_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); + + void draw_absolute_pixel_internal(int x, int y, Color color) override; +}; + +} // namespace st7789v +} // namespace esphome diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index c145fb361c..331adffb5e 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -115,20 +115,20 @@ void WaveshareEPaper::update() { this->do_update_(); this->display(); } -void WaveshareEPaper::fill(int color) { +void WaveshareEPaper::fill(Color color) { // flip logic - const uint8_t fill = color ? 0x00 : 0xFF; + const uint8_t fill = color.is_on() ? 0x00 : 0xFF; for (uint32_t i = 0; i < this->get_buffer_length_(); i++) this->buffer_[i] = fill; } -void HOT WaveshareEPaper::draw_absolute_pixel_internal(int x, int y, int color) { +void HOT WaveshareEPaper::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) return; const uint32_t pos = (x + y * this->get_width_internal()) / 8u; const uint8_t subpos = x & 0x07; // flip logic - if (!color) + if (!color.is_on()) this->buffer_[pos] |= 0x80 >> subpos; else this->buffer_[pos] &= ~(0x80 >> subpos); diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 1ed7475350..18748e05ca 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -26,7 +26,7 @@ class WaveshareEPaper : public PollingComponent, void update() override; - void fill(int color) override; + void fill(Color color) override; void setup() override { this->setup_pins_(); @@ -36,7 +36,7 @@ class WaveshareEPaper : public PollingComponent, void on_safe_shutdown() override; protected: - void draw_absolute_pixel_internal(int x, int y, int color) override; + void draw_absolute_pixel_internal(int x, int y, Color color) override; bool wait_until_idle_(); diff --git a/esphome/const.py b/esphome/const.py index 9626a2e00a..8b727b615c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -49,6 +49,7 @@ CONF_AUTOMATION_ID = 'automation_id' CONF_AVAILABILITY = 'availability' CONF_AWAY = 'away' CONF_AWAY_CONFIG = 'away_config' +CONF_BACKLIGHT_PIN = 'backlight_pin' CONF_BATTERY_LEVEL = 'battery_level' CONF_BATTERY_VOLTAGE = 'battery_voltage' CONF_BAUD_RATE = 'baud_rate' diff --git a/esphome/core/color.h b/esphome/core/color.h new file mode 100644 index 0000000000..b19340a1d3 --- /dev/null +++ b/esphome/core/color.h @@ -0,0 +1,161 @@ +#pragma once + +#include "component.h" +#include "helpers.h" + +namespace esphome { + +inline static uint8_t esp_scale8(uint8_t i, uint8_t scale) { return (uint16_t(i) * (1 + uint16_t(scale))) / 256; } + +struct Color { + union { + struct { + union { + uint8_t r; + uint8_t red; + }; + union { + uint8_t g; + uint8_t green; + }; + union { + uint8_t b; + uint8_t blue; + }; + union { + uint8_t w; + uint8_t white; + }; + }; + uint8_t raw[4]; + uint32_t raw_32; + }; + inline Color() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT + inline Color(float red, float green, float blue) ALWAYS_INLINE : r(uint8_t(red * 255)), + g(uint8_t(green * 255)), + b(uint8_t(blue * 255)), + w(0) {} + inline Color(float red, float green, float blue, float white) ALWAYS_INLINE : r(uint8_t(red * 255)), + g(uint8_t(green * 255)), + b(uint8_t(blue * 255)), + w(uint8_t(white * 255)) {} + inline Color(uint32_t colorcode) ALWAYS_INLINE : r((colorcode >> 16) & 0xFF), + g((colorcode >> 8) & 0xFF), + b((colorcode >> 0) & 0xFF), + w((colorcode >> 24) & 0xFF) {} + inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; } + inline Color &operator=(const Color &rhs) ALWAYS_INLINE { + this->r = rhs.r; + this->g = rhs.g; + this->b = rhs.b; + this->w = rhs.w; + return *this; + } + inline Color &operator=(uint32_t colorcode) ALWAYS_INLINE { + this->w = (colorcode >> 24) & 0xFF; + this->r = (colorcode >> 16) & 0xFF; + this->g = (colorcode >> 8) & 0xFF; + this->b = (colorcode >> 0) & 0xFF; + return *this; + } + inline uint8_t &operator[](uint8_t x) ALWAYS_INLINE { return this->raw[x]; } + inline Color operator*(uint8_t scale) const ALWAYS_INLINE { + return Color(esp_scale8(this->red, scale), esp_scale8(this->green, scale), esp_scale8(this->blue, scale), + esp_scale8(this->white, scale)); + } + inline Color &operator*=(uint8_t scale) ALWAYS_INLINE { + this->red = esp_scale8(this->red, scale); + this->green = esp_scale8(this->green, scale); + this->blue = esp_scale8(this->blue, scale); + this->white = esp_scale8(this->white, scale); + return *this; + } + inline Color operator*(const Color &scale) const ALWAYS_INLINE { + return Color(esp_scale8(this->red, scale.red), esp_scale8(this->green, scale.green), + esp_scale8(this->blue, scale.blue), esp_scale8(this->white, scale.white)); + } + inline Color &operator*=(const Color &scale) ALWAYS_INLINE { + this->red = esp_scale8(this->red, scale.red); + this->green = esp_scale8(this->green, scale.green); + this->blue = esp_scale8(this->blue, scale.blue); + this->white = esp_scale8(this->white, scale.white); + return *this; + } + inline Color operator+(const Color &add) const ALWAYS_INLINE { + Color ret; + if (uint8_t(add.r + this->r) < this->r) + ret.r = 255; + else + ret.r = this->r + add.r; + if (uint8_t(add.g + this->g) < this->g) + ret.g = 255; + else + ret.g = this->g + add.g; + if (uint8_t(add.b + this->b) < this->b) + ret.b = 255; + else + ret.b = this->b + add.b; + if (uint8_t(add.w + this->w) < this->w) + ret.w = 255; + else + ret.w = this->w + add.w; + return ret; + } + inline Color &operator+=(const Color &add) ALWAYS_INLINE { return *this = (*this) + add; } + inline Color operator+(uint8_t add) const ALWAYS_INLINE { return (*this) + Color(add, add, add, add); } + inline Color &operator+=(uint8_t add) ALWAYS_INLINE { return *this = (*this) + add; } + inline Color operator-(const Color &subtract) const ALWAYS_INLINE { + Color ret; + if (subtract.r > this->r) + ret.r = 0; + else + ret.r = this->r - subtract.r; + if (subtract.g > this->g) + ret.g = 0; + else + ret.g = this->g - subtract.g; + if (subtract.b > this->b) + ret.b = 0; + else + ret.b = this->b - subtract.b; + if (subtract.w > this->w) + ret.w = 0; + else + ret.w = this->w - subtract.w; + return ret; + } + inline Color &operator-=(const Color &subtract) ALWAYS_INLINE { return *this = (*this) - subtract; } + inline Color operator-(uint8_t subtract) const ALWAYS_INLINE { + return (*this) - Color(subtract, subtract, subtract, subtract); + } + inline Color &operator-=(uint8_t subtract) ALWAYS_INLINE { return *this = (*this) - subtract; } + static Color random_color() { + float r = float(random_uint32()) / float(UINT32_MAX); + float g = float(random_uint32()) / float(UINT32_MAX); + float b = float(random_uint32()) / float(UINT32_MAX); + float w = float(random_uint32()) / float(UINT32_MAX); + return Color(r, g, b, w); + } + Color fade_to_white(uint8_t amnt) { return Color(1, 1, 1, 1) - (*this * amnt); } + Color fade_to_black(uint8_t amnt) { return *this * amnt; } + Color lighten(uint8_t delta) { return *this + delta; } + Color darken(uint8_t delta) { return *this - delta; } + + uint32_t to_rgb_565() const { + uint32_t color565 = + (esp_scale8(this->red, 31) << 11) | (esp_scale8(this->green, 63) << 5) | (esp_scale8(this->blue, 31) << 0); + return color565; + } + uint32_t to_bgr_565() const { + uint32_t color565 = + (esp_scale8(this->blue, 31) << 11) | (esp_scale8(this->green, 63) << 5) | (esp_scale8(this->red, 31) << 0); + return color565; + } + uint32_t to_grayscale4() const { + uint32_t gs4 = esp_scale8(this->white, 15); + return gs4; + } +}; +static const Color COLOR_BLACK(0, 0, 0); +static const Color COLOR_WHITE(1, 1, 1); +}; // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index 8f06de2e40..ec09a00208 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1507,6 +1507,16 @@ interval: id(btn_left)->set_threshold(btn_left_state * 0.9); +color: + - id: kbx_red + red: 100% + green: 1% + blue: 2% + - id: kbx_blue + red: 0% + green: 1% + blue: 100% + display: - platform: lcd_gpio dimensions: 18x4 @@ -1591,6 +1601,13 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); +- platform: st7789v + cs_pin: GPIO5 + dc_pin: GPIO16 + reset_pin: GPIO23 + backlight_pin: GPIO4 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); tm1651: id: tm1651_battery From 83a12d980edafa4511baf963c79b6a05fea87a94 Mon Sep 17 00:00:00 2001 From: rspaargaren Date: Sun, 28 Jun 2020 23:47:43 +0200 Subject: [PATCH 2/5] Vl53 long range (#1055) * Update vl53l0x_sensor.cpp Changed values for long range support * added true / false option for long range detection * debug missing , * further debug option long_range * Travis updates * added travis tests * Travis removed space should be good now * update to trigger travis again * added old test files for PR * added vl5310x sensor for compile testing * fix variable names Co-authored-by: Guillermo Ruffino --- esphome/components/vl53l0x/sensor.py | 7 ++++++- esphome/components/vl53l0x/vl53l0x_sensor.cpp | 21 +++++++++++++++---- esphome/components/vl53l0x/vl53l0x_sensor.h | 2 ++ tests/test3.yaml | 4 ++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/esphome/components/vl53l0x/sensor.py b/esphome/components/vl53l0x/sensor.py index 6740d53e13..209016fe40 100644 --- a/esphome/components/vl53l0x/sensor.py +++ b/esphome/components/vl53l0x/sensor.py @@ -10,15 +10,20 @@ VL53L0XSensor = vl53l0x_ns.class_('VL53L0XSensor', sensor.Sensor, cg.PollingComp i2c.I2CDevice) CONF_SIGNAL_RATE_LIMIT = 'signal_rate_limit' +CONF_LONG_RANGE = 'long_range' + CONFIG_SCHEMA = sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ cv.GenerateID(): cv.declare_id(VL53L0XSensor), cv.Optional(CONF_SIGNAL_RATE_LIMIT, default=0.25): cv.float_range( - min=0.0, max=512.0, min_included=False, max_included=False) + min=0.0, max=512.0, min_included=False, max_included=False), + cv.Optional(CONF_LONG_RANGE, default=False): cv.boolean, }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x29)) def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + cg.add(var.set_signal_rate_limit(config[CONF_SIGNAL_RATE_LIMIT])) + cg.add(var.set_long_range(config[CONF_LONG_RANGE])) yield sensor.register_sensor(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.cpp b/esphome/components/vl53l0x/vl53l0x_sensor.cpp index 231bed99ac..8ce822352f 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.cpp +++ b/esphome/components/vl53l0x/vl53l0x_sensor.cpp @@ -33,7 +33,8 @@ void VL53L0XSensor::setup() { reg(0xFF) = 0x00; reg(0x80) = 0x00; reg(0x60) |= 0x12; - + if (this->long_range_) + this->signal_rate_limit_ = 0.1; auto rate_value = static_cast(signal_rate_limit_ * 128); write_byte_16(0x44, rate_value); @@ -104,7 +105,11 @@ void VL53L0XSensor::setup() { reg(0x48) = 0x00; reg(0x30) = 0x20; reg(0xFF) = 0x00; - reg(0x30) = 0x09; + if (this->long_range_) { + reg(0x30) = 0x07; // WAS 0x09 + } else { + reg(0x30) = 0x09; + } reg(0x54) = 0x00; reg(0x31) = 0x04; reg(0x32) = 0x03; @@ -116,7 +121,11 @@ void VL53L0XSensor::setup() { reg(0x51) = 0x00; reg(0x52) = 0x96; reg(0x56) = 0x08; - reg(0x57) = 0x30; + if (this->long_range_) { + reg(0x57) = 0x50; // was 0x30 + } else { + reg(0x57) = 0x30; + } reg(0x61) = 0x00; reg(0x62) = 0x00; reg(0x64) = 0x00; @@ -153,7 +162,11 @@ void VL53L0XSensor::setup() { reg(0x44) = 0x00; reg(0x45) = 0x20; reg(0x47) = 0x08; - reg(0x48) = 0x28; + if (this->long_range_) { + reg(0x48) = 0x48; // was 0x28 + } else { + reg(0x48) = 0x28; + } reg(0x67) = 0x00; reg(0x70) = 0x04; reg(0x71) = 0x01; diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.h b/esphome/components/vl53l0x/vl53l0x_sensor.h index 1825383cee..4939a9806c 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.h +++ b/esphome/components/vl53l0x/vl53l0x_sensor.h @@ -29,6 +29,7 @@ class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c void loop() override; void set_signal_rate_limit(float signal_rate_limit) { signal_rate_limit_ = signal_rate_limit; } + void set_long_range(bool long_range) { long_range_ = long_range; } protected: uint32_t get_measurement_timing_budget_() { @@ -247,6 +248,7 @@ class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c } float signal_rate_limit_; + bool long_range_; uint32_t measurement_timing_budget_us_; bool initiated_read_{false}; bool waiting_for_interrupt_{false}; diff --git a/tests/test3.yaml b/tests/test3.yaml index edbf472657..9c51894e12 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -217,6 +217,10 @@ sensor: - platform: apds9960 type: proximity name: APDS9960 Proximity + - platform: vl53l0x + name: "VL53L0x Distance" + address: 0x29 + update_interval: 60s - platform: apds9960 type: clear name: APDS9960 Clear From c041cc483c9308011986c7a25e7e0ad593a0c09e Mon Sep 17 00:00:00 2001 From: vxider Date: Mon, 29 Jun 2020 05:50:20 +0800 Subject: [PATCH 3/5] fix shunt voltage / current / power reading in INA3221 (#1101) * fix shunt voltage / current / power reading in INA3221 * support nagetive shunt voltage reading * fix loss of precision --- esphome/components/ina3221/ina3221.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ina3221/ina3221.cpp b/esphome/components/ina3221/ina3221.cpp index 3bd568f37d..a0334064ff 100644 --- a/esphome/components/ina3221/ina3221.cpp +++ b/esphome/components/ina3221/ina3221.cpp @@ -100,7 +100,7 @@ void INA3221Component::update() { this->status_set_warning(); return; } - const float shunt_voltage_v = int16_t(raw) * 40.0f / 1000000.0f; + const float shunt_voltage_v = int16_t(raw) * 40.0f / 8.0f / 1000000.0f; if (channel.shunt_voltage_sensor_ != nullptr) channel.shunt_voltage_sensor_->publish_state(shunt_voltage_v); current_a = shunt_voltage_v / channel.shunt_resistance_; From 78633c57682d5c062d3743b6b55208513e9cbf79 Mon Sep 17 00:00:00 2001 From: vxider Date: Mon, 29 Jun 2020 05:59:22 +0800 Subject: [PATCH 4/5] fix calibration (#1103) --- esphome/components/ina219/ina219.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ina219/ina219.cpp b/esphome/components/ina219/ina219.cpp index 1150f7c661..44d6501e36 100644 --- a/esphome/components/ina219/ina219.cpp +++ b/esphome/components/ina219/ina219.cpp @@ -120,7 +120,7 @@ void INA219Component::setup() { } this->calibration_lsb_ = lsb; - auto calibration = uint32_t(0.04096f / (0.0001 * lsb * this->shunt_resistance_ohm_)); + auto calibration = uint32_t(0.04096f / (0.000001 * lsb * this->shunt_resistance_ohm_)); ESP_LOGV(TAG, " Using LSB=%u calibration=%u", lsb, calibration); if (!this->write_byte_16(INA219_REGISTER_CALIBRATION, calibration)) { this->mark_failed(); From 2f0722598404f5f6935b294f3e4668660d15ef2e Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Mon, 29 Jun 2020 00:44:15 +0200 Subject: [PATCH 5/5] Fix: Component script not stopped in certain situations (#1004) * Move stop/is_running implementation to Action base class Try to fix issue #1105. Until now if an UpdateComponentAction, a LambdaAction, or any action that can contain those inside their "else" or "then" action lists, resulted in a call to script.stop on the script that contains them, the script would continue running, because they didn't implement a stop() method. Basically only the asynchronous ones did: DelayAction, WaitUntilAction and ScriptWaitAction. With this change num_running_ in Action replaces DelayAction::num_running_ and WaitUntilAction::triggered_ to provide the same is_running logic to other actions. * Make some Action methods protected Apparently play()/stop() etc. are not meant to be called directly by users of the class and if they're called directly that would not give the expected result for the classes that have an empty play(). Make all methods except play_complex, stop_comples and is_running protected. While there also make RemoteTransmitterActionBase::encode protected. * lint * format Co-authored-by: Guillermo Ruffino --- .../components/api/homeassistant_service.h | 1 + esphome/components/binary_sensor/automation.h | 1 + esphome/components/cover/automation.h | 16 ++-- esphome/components/dfplayer/dfplayer.h | 6 +- esphome/components/display/display_buffer.h | 5 +- esphome/components/esp8266_pwm/esp8266_pwm.h | 1 - esphome/components/fan/automation.h | 3 - esphome/components/mhz19/mhz19.h | 3 + esphome/components/mqtt/mqtt_client.h | 1 + esphome/components/output/automation.h | 1 + esphome/components/pid/pid_climate.h | 8 +- esphome/components/remote_base/jvc_protocol.h | 1 + esphome/components/remote_base/lg_protocol.h | 1 + esphome/components/remote_base/nec_protocol.h | 1 + .../remote_base/panasonic_protocol.h | 1 + .../components/remote_base/pioneer_protocol.h | 1 + esphome/components/remote_base/rc5_protocol.h | 1 + esphome/components/remote_base/remote_base.h | 8 +- .../components/remote_base/samsung_protocol.h | 1 + .../components/remote_base/sony_protocol.h | 1 + .../rotary_encoder/rotary_encoder.h | 1 + esphome/components/script/script.h | 19 ++-- esphome/components/sensor/automation.h | 1 + esphome/components/servo/servo.h | 2 + esphome/components/switch/automation.h | 1 + esphome/components/text_sensor/automation.h | 1 + esphome/core/automation.h | 56 +++++++----- esphome/core/base_automation.h | 87 ++++++++----------- 28 files changed, 121 insertions(+), 109 deletions(-) diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index d68dac3b61..8a72765195 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -29,6 +29,7 @@ template class HomeAssistantServiceCallAction : public Action void add_variable(std::string key, T value) { this->variables_.push_back(TemplatableKeyValuePair(key, value)); } + void play(Ts... x) override { HomeassistantServiceResponse resp; resp.service = this->service_.value(x...); diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index e9ff37446d..6b0321628c 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -137,6 +137,7 @@ template class BinarySensorPublishAction : public Action public: explicit BinarySensorPublishAction(BinarySensor *sensor) : sensor_(sensor) {} TEMPLATABLE_VALUE(bool, state) + void play(Ts... x) override { auto val = this->state_.value(x...); this->sensor_->publish_state(val); diff --git a/esphome/components/cover/automation.h b/esphome/components/cover/automation.h index a8eb0cdf99..0092f987f2 100644 --- a/esphome/components/cover/automation.h +++ b/esphome/components/cover/automation.h @@ -41,6 +41,10 @@ template class ControlAction : public Action { public: explicit ControlAction(Cover *cover) : cover_(cover) {} + TEMPLATABLE_VALUE(bool, stop) + TEMPLATABLE_VALUE(float, position) + TEMPLATABLE_VALUE(float, tilt) + void play(Ts... x) override { auto call = this->cover_->make_call(); if (this->stop_.has_value()) @@ -52,10 +56,6 @@ template class ControlAction : public Action { call.perform(); } - TEMPLATABLE_VALUE(bool, stop) - TEMPLATABLE_VALUE(float, position) - TEMPLATABLE_VALUE(float, tilt) - protected: Cover *cover_; }; @@ -63,6 +63,10 @@ template class ControlAction : public Action { template class CoverPublishAction : public Action { public: CoverPublishAction(Cover *cover) : cover_(cover) {} + TEMPLATABLE_VALUE(float, position) + TEMPLATABLE_VALUE(float, tilt) + TEMPLATABLE_VALUE(CoverOperation, current_operation) + void play(Ts... x) override { if (this->position_.has_value()) this->cover_->position = this->position_.value(x...); @@ -73,10 +77,6 @@ template class CoverPublishAction : public Action { this->cover_->publish_state(); } - TEMPLATABLE_VALUE(float, position) - TEMPLATABLE_VALUE(float, tilt) - TEMPLATABLE_VALUE(CoverOperation, current_operation) - protected: Cover *cover_; }; diff --git a/esphome/components/dfplayer/dfplayer.h b/esphome/components/dfplayer/dfplayer.h index 22ca11c3be..cb9686bb64 100644 --- a/esphome/components/dfplayer/dfplayer.h +++ b/esphome/components/dfplayer/dfplayer.h @@ -104,7 +104,6 @@ class DFPlayer : public uart::UARTDevice, public Component { #define DFPLAYER_SIMPLE_ACTION(ACTION_CLASS, ACTION_METHOD) \ template class ACTION_CLASS : public Action, public Parented { \ - public: \ void play(Ts... x) override { this->parent_->ACTION_METHOD(); } \ }; @@ -115,6 +114,7 @@ template class PlayFileAction : public Action, public Par public: TEMPLATABLE_VALUE(uint16_t, file) TEMPLATABLE_VALUE(boolean, loop) + void play(Ts... x) override { auto file = this->file_.value(x...); auto loop = this->loop_.value(x...); @@ -131,6 +131,7 @@ template class PlayFolderAction : public Action, public P TEMPLATABLE_VALUE(uint16_t, folder) TEMPLATABLE_VALUE(uint16_t, file) TEMPLATABLE_VALUE(boolean, loop) + void play(Ts... x) override { auto folder = this->folder_.value(x...); auto file = this->file_.value(x...); @@ -146,6 +147,7 @@ template class PlayFolderAction : public Action, public P template class SetDeviceAction : public Action, public Parented { public: TEMPLATABLE_VALUE(Device, device) + void play(Ts... x) override { auto device = this->device_.value(x...); this->parent_->set_device(device); @@ -155,6 +157,7 @@ template class SetDeviceAction : public Action, public Pa template class SetVolumeAction : public Action, public Parented { public: TEMPLATABLE_VALUE(uint8_t, volume) + void play(Ts... x) override { auto volume = this->volume_.value(x...); this->parent_->set_volume(volume); @@ -164,6 +167,7 @@ template class SetVolumeAction : public Action, public Pa template class SetEqAction : public Action, public Parented { public: TEMPLATABLE_VALUE(EqPreset, eq) + void play(Ts... x) override { auto eq = this->eq_.value(x...); this->parent_->set_eq(eq); diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 969a5d80cd..4b84e90a08 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -400,6 +400,7 @@ class Image { template class DisplayPageShowAction : public Action { public: TEMPLATABLE_VALUE(DisplayPage *, page) + void play(Ts... x) override { auto *page = this->page_.value(x...); if (page != nullptr) { @@ -411,18 +412,18 @@ template class DisplayPageShowAction : public Action { template class DisplayPageShowNextAction : public Action { public: DisplayPageShowNextAction(DisplayBuffer *buffer) : buffer_(buffer) {} + void play(Ts... x) override { this->buffer_->show_next_page(); } - protected: DisplayBuffer *buffer_; }; template class DisplayPageShowPrevAction : public Action { public: DisplayPageShowPrevAction(DisplayBuffer *buffer) : buffer_(buffer) {} + void play(Ts... x) override { this->buffer_->show_prev_page(); } - protected: DisplayBuffer *buffer_; }; diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.h b/esphome/components/esp8266_pwm/esp8266_pwm.h index b6839985b0..51b74f48ba 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.h +++ b/esphome/components/esp8266_pwm/esp8266_pwm.h @@ -43,7 +43,6 @@ template class SetFrequencyAction : public Action { this->parent_->update_frequency(freq); } - protected: ESP8266PWM *parent_; }; diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index dfa72a3ea6..d96ed994e8 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -25,7 +25,6 @@ template class TurnOnAction : public Action { call.perform(); } - protected: FanState *state_; }; @@ -35,7 +34,6 @@ template class TurnOffAction : public Action { void play(Ts... x) override { this->state_->turn_off().perform(); } - protected: FanState *state_; }; @@ -45,7 +43,6 @@ template class ToggleAction : public Action { void play(Ts... x) override { this->state_->toggle().perform(); } - protected: FanState *state_; }; diff --git a/esphome/components/mhz19/mhz19.h b/esphome/components/mhz19/mhz19.h index 2201fc87f0..151351be4c 100644 --- a/esphome/components/mhz19/mhz19.h +++ b/esphome/components/mhz19/mhz19.h @@ -37,6 +37,7 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice { template class MHZ19CalibrateZeroAction : public Action { public: MHZ19CalibrateZeroAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} + void play(Ts... x) override { this->mhz19_->calibrate_zero(); } protected: @@ -46,6 +47,7 @@ template class MHZ19CalibrateZeroAction : public Action { template class MHZ19ABCEnableAction : public Action { public: MHZ19ABCEnableAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} + void play(Ts... x) override { this->mhz19_->abc_enable(); } protected: @@ -55,6 +57,7 @@ template class MHZ19ABCEnableAction : public Action { template class MHZ19ABCDisableAction : public Action { public: MHZ19ABCDisableAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} + void play(Ts... x) override { this->mhz19_->abc_disable(); } protected: diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 6f14b0c92c..2bbebff845 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -316,6 +316,7 @@ template class MQTTPublishJsonAction : public Action { TEMPLATABLE_VALUE(bool, retain) void set_payload(std::function payload) { this->payload_ = payload; } + void play(Ts... x) override { auto f = std::bind(&MQTTPublishJsonAction::encode_, this, x..., std::placeholders::_1); auto topic = this->topic_.value(x...); diff --git a/esphome/components/output/automation.h b/esphome/components/output/automation.h index 8c8a5ab61b..51c2849702 100644 --- a/esphome/components/output/automation.h +++ b/esphome/components/output/automation.h @@ -33,6 +33,7 @@ template class SetLevelAction : public Action { SetLevelAction(FloatOutput *output) : output_(output) {} TEMPLATABLE_VALUE(float, level) + void play(Ts... x) override { this->output_->set_level(this->level_.value(x...)); } protected: diff --git a/esphome/components/pid/pid_climate.h b/esphome/components/pid/pid_climate.h index 8f379c47b4..3dae92af5f 100644 --- a/esphome/components/pid/pid_climate.h +++ b/esphome/components/pid/pid_climate.h @@ -71,6 +71,10 @@ template class PIDAutotuneAction : public Action { public: PIDAutotuneAction(PIDClimate *parent) : parent_(parent) {} + void set_noiseband(float noiseband) { noiseband_ = noiseband; } + void set_positive_output(float positive_output) { positive_output_ = positive_output; } + void set_negative_output(float negative_output) { negative_output_ = negative_output; } + void play(Ts... x) { auto tuner = make_unique(); tuner->set_noiseband(this->noiseband_); @@ -79,10 +83,6 @@ template class PIDAutotuneAction : public Action { this->parent_->start_autotune(std::move(tuner)); } - void set_noiseband(float noiseband) { noiseband_ = noiseband; } - void set_positive_output(float positive_output) { positive_output_ = positive_output; } - void set_negative_output(float negative_output) { negative_output_ = negative_output; } - protected: float noiseband_; float positive_output_; diff --git a/esphome/components/remote_base/jvc_protocol.h b/esphome/components/remote_base/jvc_protocol.h index 8a216f5348..fc40a6a874 100644 --- a/esphome/components/remote_base/jvc_protocol.h +++ b/esphome/components/remote_base/jvc_protocol.h @@ -23,6 +23,7 @@ DECLARE_REMOTE_PROTOCOL(JVC) template class JVCAction : public RemoteTransmitterActionBase { public: TEMPLATABLE_VALUE(uint32_t, data) + void encode(RemoteTransmitData *dst, Ts... x) override { JVCData data{}; data.data = this->data_.value(x...); diff --git a/esphome/components/remote_base/lg_protocol.h b/esphome/components/remote_base/lg_protocol.h index b810115f58..6267560443 100644 --- a/esphome/components/remote_base/lg_protocol.h +++ b/esphome/components/remote_base/lg_protocol.h @@ -26,6 +26,7 @@ template class LGAction : public RemoteTransmitterActionBasedata_.value(x...); diff --git a/esphome/components/remote_base/nec_protocol.h b/esphome/components/remote_base/nec_protocol.h index c794991eab..593a3efe17 100644 --- a/esphome/components/remote_base/nec_protocol.h +++ b/esphome/components/remote_base/nec_protocol.h @@ -25,6 +25,7 @@ template class NECAction : public RemoteTransmitterActionBaseaddress_.value(x...); diff --git a/esphome/components/remote_base/panasonic_protocol.h b/esphome/components/remote_base/panasonic_protocol.h index b13bd3e92d..eae97a8a14 100644 --- a/esphome/components/remote_base/panasonic_protocol.h +++ b/esphome/components/remote_base/panasonic_protocol.h @@ -26,6 +26,7 @@ template class PanasonicAction : public RemoteTransmitterActionB public: TEMPLATABLE_VALUE(uint16_t, address) TEMPLATABLE_VALUE(uint32_t, command) + void encode(RemoteTransmitData *dst, Ts... x) override { PanasonicData data{}; data.address = this->address_.value(x...); diff --git a/esphome/components/remote_base/pioneer_protocol.h b/esphome/components/remote_base/pioneer_protocol.h index f93e51a033..4cac4f9f32 100644 --- a/esphome/components/remote_base/pioneer_protocol.h +++ b/esphome/components/remote_base/pioneer_protocol.h @@ -25,6 +25,7 @@ template class PioneerAction : public RemoteTransmitterActionBas public: TEMPLATABLE_VALUE(uint16_t, rc_code_1) TEMPLATABLE_VALUE(uint16_t, rc_code_2) + void encode(RemoteTransmitData *dst, Ts... x) override { PioneerData data{}; data.rc_code_1 = this->rc_code_1_.value(x...); diff --git a/esphome/components/remote_base/rc5_protocol.h b/esphome/components/remote_base/rc5_protocol.h index 2e1da74d9f..589c8d42de 100644 --- a/esphome/components/remote_base/rc5_protocol.h +++ b/esphome/components/remote_base/rc5_protocol.h @@ -26,6 +26,7 @@ template class RC5Action : public RemoteTransmitterActionBaseaddress_.value(x...); diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index 250b59e55e..916fe29c1f 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -323,6 +323,9 @@ template class RemoteTransmitterActionBase : public Actionparent_ = parent; } + TEMPLATABLE_VALUE(uint32_t, send_times); + TEMPLATABLE_VALUE(uint32_t, send_wait); + void play(Ts... x) override { auto call = this->parent_->transmit(); this->encode(call.get_data(), x...); @@ -331,12 +334,9 @@ template class RemoteTransmitterActionBase : public Action class SamsungAction : public RemoteTransmitterActionBase { public: TEMPLATABLE_VALUE(uint32_t, data) + void encode(RemoteTransmitData *dst, Ts... x) override { SamsungData data{}; data.data = this->data_.value(x...); diff --git a/esphome/components/remote_base/sony_protocol.h b/esphome/components/remote_base/sony_protocol.h index 9f0bcdf82f..aecc8ab91c 100644 --- a/esphome/components/remote_base/sony_protocol.h +++ b/esphome/components/remote_base/sony_protocol.h @@ -26,6 +26,7 @@ template class SonyAction : public RemoteTransmitterActionBasedata_.value(x...); diff --git a/esphome/components/rotary_encoder/rotary_encoder.h b/esphome/components/rotary_encoder/rotary_encoder.h index 4220645478..f0e47dfe0a 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.h +++ b/esphome/components/rotary_encoder/rotary_encoder.h @@ -74,6 +74,7 @@ template class RotaryEncoderSetValueAction : public Actionencoder_->set_value(this->value_.value(x...)); } protected: diff --git a/esphome/components/script/script.h b/esphome/components/script/script.h index 3b97327da8..8495014f00 100644 --- a/esphome/components/script/script.h +++ b/esphome/components/script/script.h @@ -53,41 +53,34 @@ template class ScriptWaitAction : public Action, public C public: ScriptWaitAction(Script *script) : script_(script) {} - void play(Ts... x) { /* ignore - see play_complex */ - } - void play_complex(Ts... x) override { + this->num_running_++; // Check if we can continue immediately. if (!this->script_->is_running()) { - this->triggered_ = false; - this->play_next(x...); + this->play_next_(x...); return; } this->var_ = std::make_tuple(x...); - this->triggered_ = true; this->loop(); } - void stop() override { this->triggered_ = false; } - void loop() override { - if (!this->triggered_) + if (this->num_running_ == 0) return; if (this->script_->is_running()) return; - this->triggered_ = false; - this->play_next_tuple(this->var_); + this->play_next_tuple_(this->var_); } float get_setup_priority() const override { return setup_priority::DATA; } - bool is_running() override { return this->triggered_ || this->is_running_next(); } + void play(Ts... x) override { /* ignore - see play_complex */ + } protected: Script *script_; - bool triggered_{false}; std::tuple var_{}; }; diff --git a/esphome/components/sensor/automation.h b/esphome/components/sensor/automation.h index 079077dba0..c70fb93963 100644 --- a/esphome/components/sensor/automation.h +++ b/esphome/components/sensor/automation.h @@ -25,6 +25,7 @@ template class SensorPublishAction : public Action { public: SensorPublishAction(Sensor *sensor) : sensor_(sensor) {} TEMPLATABLE_VALUE(float, state) + void play(Ts... x) override { this->sensor_->publish_state(this->state_.value(x...)); } protected: diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index a37188740c..b864efc877 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -64,6 +64,7 @@ template class ServoWriteAction : public Action { public: ServoWriteAction(Servo *servo) : servo_(servo) {} TEMPLATABLE_VALUE(float, value) + void play(Ts... x) override { this->servo_->write(this->value_.value(x...)); } protected: @@ -73,6 +74,7 @@ template class ServoWriteAction : public Action { template class ServoDetachAction : public Action { public: ServoDetachAction(Servo *servo) : servo_(servo) {} + void play(Ts... x) override { this->servo_->detach(); } protected: diff --git a/esphome/components/switch/automation.h b/esphome/components/switch/automation.h index 90bdabf0f4..579daf4d24 100644 --- a/esphome/components/switch/automation.h +++ b/esphome/components/switch/automation.h @@ -73,6 +73,7 @@ template class SwitchPublishAction : public Action { public: SwitchPublishAction(Switch *a_switch) : switch_(a_switch) {} TEMPLATABLE_VALUE(bool, state) + void play(Ts... x) override { this->switch_->publish_state(this->state_.value(x...)); } protected: diff --git a/esphome/components/text_sensor/automation.h b/esphome/components/text_sensor/automation.h index 496efb1cc3..6810d10b13 100644 --- a/esphome/components/text_sensor/automation.h +++ b/esphome/components/text_sensor/automation.h @@ -30,6 +30,7 @@ template class TextSensorPublishAction : public Action { public: TextSensorPublishAction(TextSensor *sensor) : sensor_(sensor) {} TEMPLATABLE_VALUE(std::string, state) + void play(Ts... x) override { this->sensor_->publish_state(this->state_.value(x...)); } protected: diff --git a/esphome/core/automation.h b/esphome/core/automation.h index cbe96a749e..02bd8bb299 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -75,45 +75,55 @@ template class ActionList; template class Action { public: - virtual void play(Ts... x) = 0; virtual void play_complex(Ts... x) { + this->num_running_++; this->play(x...); - this->play_next(x...); + this->play_next_(x...); } - void play_next(Ts... x) { - if (this->next_ != nullptr) { - this->next_->play_complex(x...); + virtual void stop_complex() { + if (num_running_) { + this->stop(); + this->num_running_ = 0; + } + this->stop_next_(); + } + virtual bool is_running() { return this->num_running_ > 0 || this->is_running_next_(); } + + protected: + friend ActionList; + + virtual void play(Ts... x) = 0; + void play_next_(Ts... x) { + if (this->num_running_ > 0) { + this->num_running_--; + if (this->next_ != nullptr) { + this->next_->play_complex(x...); + } } } - virtual void stop() {} - virtual void stop_complex() { - this->stop(); - this->stop_next(); + template void play_next_tuple_(const std::tuple &tuple, seq) { + this->play_next_(std::get(tuple)...); } - void stop_next() { + void play_next_tuple_(const std::tuple &tuple) { + this->play_next_tuple_(tuple, typename gens::type()); + } + + virtual void stop() {} + void stop_next_() { if (this->next_ != nullptr) { this->next_->stop_complex(); } } - virtual bool is_running() { return this->is_running_next(); } - bool is_running_next() { + + bool is_running_next_() { if (this->next_ == nullptr) return false; return this->next_->is_running(); } - void play_next_tuple(const std::tuple &tuple) { - this->play_next_tuple_(tuple, typename gens::type()); - } - - protected: - friend ActionList; - - template void play_next_tuple_(const std::tuple &tuple, seq) { - this->play_next(std::get(tuple)...); - } - Action *next_ = nullptr; + + int num_running_{0}; }; template class ActionList { diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index add3df0bb5..d2656290bc 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -108,34 +108,23 @@ template class DelayAction : public Action, public Compon TEMPLATABLE_VALUE(uint32_t, delay) - void stop() override { - this->cancel_timeout(""); - this->num_running_ = 0; - } - - void play(Ts... x) override { /* ignore - see play_complex */ - } - void play_complex(Ts... x) override { - auto f = std::bind(&DelayAction::delay_end_, this, x...); + auto f = std::bind(&DelayAction::play_next_, this, x...); this->num_running_++; this->set_timeout(this->delay_.value(x...), f); } float get_setup_priority() const override { return setup_priority::HARDWARE; } - bool is_running() override { return this->num_running_ > 0 || this->is_running_next(); } - - protected: - void delay_end_(Ts... x) { - this->num_running_--; - this->play_next(x...); + void play(Ts... x) override { /* ignore - see play_complex */ } - int num_running_{0}; + + void stop() override { this->cancel_timeout(""); } }; template class LambdaAction : public Action { public: explicit LambdaAction(std::function &&f) : f_(std::move(f)) {} + void play(Ts... x) override { this->f_(x...); } protected: @@ -148,41 +137,40 @@ template class IfAction : public Action { void add_then(const std::vector *> &actions) { this->then_.add_actions(actions); - this->then_.add_action(new LambdaAction([this](Ts... x) { this->play_next(x...); })); + this->then_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); } void add_else(const std::vector *> &actions) { this->else_.add_actions(actions); - this->else_.add_action(new LambdaAction([this](Ts... x) { this->play_next(x...); })); - } - - void play(Ts... x) override { /* ignore - see play_complex */ + this->else_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); } void play_complex(Ts... x) override { + this->num_running_++; bool res = this->condition_->check(x...); if (res) { if (this->then_.empty()) { - this->play_next(x...); - } else { + this->play_next_(x...); + } else if (this->num_running_ > 0) { this->then_.play(x...); } } else { if (this->else_.empty()) { - this->play_next(x...); - } else { + this->play_next_(x...); + } else if (this->num_running_ > 0) { this->else_.play(x...); } } } + void play(Ts... x) override { /* ignore - see play_complex */ + } + void stop() override { this->then_.stop(); this->else_.stop(); } - bool is_running() override { return this->then_.is_running() || this->else_.is_running() || this->is_running_next(); } - protected: Condition *condition_; ActionList then_; @@ -196,37 +184,40 @@ template class WhileAction : public Action { void add_then(const std::vector *> &actions) { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](Ts... x) { - if (this->condition_->check_tuple(this->var_)) { + if (this->num_running_ > 0 && this->condition_->check_tuple(this->var_)) { // play again - this->then_.play_tuple(this->var_); + if (this->num_running_ > 0) { + this->then_.play_tuple(this->var_); + } } else { // condition false, play next - this->play_next_tuple(this->var_); + this->play_next_tuple_(this->var_); } })); } - void play(Ts... x) override { /* ignore - see play_complex */ - } - void play_complex(Ts... x) override { + this->num_running_++; // Store loop parameters this->var_ = std::make_tuple(x...); // Initial condition check if (!this->condition_->check_tuple(this->var_)) { // If new condition check failed, stop loop if running this->then_.stop(); - this->play_next_tuple(this->var_); + this->play_next_tuple_(this->var_); return; } - this->then_.play_tuple(this->var_); + if (this->num_running_ > 0) { + this->then_.play_tuple(this->var_); + } + } + + void play(Ts... x) override { /* ignore - see play_complex */ } void stop() override { this->then_.stop(); } - bool is_running() override { return this->then_.is_running() || this->is_running_next(); } - protected: Condition *condition_; ActionList then_; @@ -237,48 +228,44 @@ template class WaitUntilAction : public Action, public Co public: WaitUntilAction(Condition *condition) : condition_(condition) {} - void play(Ts... x) { /* ignore - see play_complex */ - } - void play_complex(Ts... x) override { + this->num_running_++; // Check if we can continue immediately. if (this->condition_->check(x...)) { - this->triggered_ = false; - this->play_next(x...); + if (this->num_running_ > 0) { + this->play_next_(x...); + } return; } this->var_ = std::make_tuple(x...); - this->triggered_ = true; this->loop(); } - void stop() override { this->triggered_ = false; } - void loop() override { - if (!this->triggered_) + if (this->num_running_ == 0) return; if (!this->condition_->check_tuple(this->var_)) { return; } - this->triggered_ = false; - this->play_next_tuple(this->var_); + this->play_next_tuple_(this->var_); } float get_setup_priority() const override { return setup_priority::DATA; } - bool is_running() override { return this->triggered_ || this->is_running_next(); } + void play(Ts... x) override { /* ignore - see play_complex */ + } protected: Condition *condition_; - bool triggered_{false}; std::tuple var_{}; }; template class UpdateComponentAction : public Action { public: UpdateComponentAction(PollingComponent *component) : component_(component) {} + void play(Ts... x) override { this->component_->update(); } protected: