mirror of
https://github.com/esphome/esphome.git
synced 2025-11-02 16:11:53 +00:00
Compare commits
23 Commits
2023.6.0b1
...
2023.6.0b5
Author | SHA1 | Date | |
---|---|---|---|
|
a40fa22055 | ||
|
2047bba4f7 | ||
|
a064ab5c2b | ||
|
0c37d06936 | ||
|
d919373853 | ||
|
6c00be0a63 | ||
|
0671287b80 | ||
|
867b4719d1 | ||
|
fc544dc389 | ||
|
501fe83710 | ||
|
5513b0e121 | ||
|
959e7745a6 | ||
|
76e947651d | ||
|
5ba04eb620 | ||
|
2d32e89b87 | ||
|
88c13768e3 | ||
|
29f4430658 | ||
|
b558a1c9dd | ||
|
a4ef26749b | ||
|
6aa3092be0 | ||
|
abca47f36f | ||
|
407b5e199e | ||
|
2ffd430b0b |
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -49,9 +49,11 @@ jobs:
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Set up python environment
|
||||
env:
|
||||
ESPHOME_NO_VENV: 1
|
||||
run: |
|
||||
script/setup
|
||||
pip install setuptools wheel twine
|
||||
pip install twine
|
||||
- name: Build
|
||||
run: python setup.py sdist bdist_wheel
|
||||
- name: Upload
|
||||
|
@@ -94,3 +94,5 @@ async def to_code(config):
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))
|
||||
|
69
esphome/components/display/animation.cpp
Normal file
69
esphome/components/display/animation.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "animation.h"
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type)
|
||||
: Image(data_start, width, height, type),
|
||||
animation_data_start_(data_start),
|
||||
current_frame_(0),
|
||||
animation_frame_count_(animation_frame_count),
|
||||
loop_start_frame_(0),
|
||||
loop_end_frame_(animation_frame_count_),
|
||||
loop_count_(0),
|
||||
loop_current_iteration_(1) {}
|
||||
void Animation::set_loop(uint32_t start_frame, uint32_t end_frame, int count) {
|
||||
loop_start_frame_ = std::min(start_frame, animation_frame_count_);
|
||||
loop_end_frame_ = std::min(end_frame, animation_frame_count_);
|
||||
loop_count_ = count;
|
||||
loop_current_iteration_ = 1;
|
||||
}
|
||||
|
||||
uint32_t Animation::get_animation_frame_count() const { return this->animation_frame_count_; }
|
||||
int Animation::get_current_frame() const { return this->current_frame_; }
|
||||
void Animation::next_frame() {
|
||||
this->current_frame_++;
|
||||
if (loop_count_ && this->current_frame_ == loop_end_frame_ &&
|
||||
(this->loop_current_iteration_ < loop_count_ || loop_count_ < 0)) {
|
||||
this->current_frame_ = loop_start_frame_;
|
||||
this->loop_current_iteration_++;
|
||||
}
|
||||
if (this->current_frame_ >= animation_frame_count_) {
|
||||
this->loop_current_iteration_ = 1;
|
||||
this->current_frame_ = 0;
|
||||
}
|
||||
|
||||
this->update_data_start_();
|
||||
}
|
||||
void Animation::prev_frame() {
|
||||
this->current_frame_--;
|
||||
if (this->current_frame_ < 0) {
|
||||
this->current_frame_ = this->animation_frame_count_ - 1;
|
||||
}
|
||||
|
||||
this->update_data_start_();
|
||||
}
|
||||
|
||||
void Animation::set_frame(int frame) {
|
||||
unsigned abs_frame = abs(frame);
|
||||
|
||||
if (abs_frame < this->animation_frame_count_) {
|
||||
if (frame >= 0) {
|
||||
this->current_frame_ = frame;
|
||||
} else {
|
||||
this->current_frame_ = this->animation_frame_count_ - abs_frame;
|
||||
}
|
||||
}
|
||||
|
||||
this->update_data_start_();
|
||||
}
|
||||
|
||||
void Animation::update_data_start_() {
|
||||
const uint32_t image_size = image_type_to_width_stride(this->width_, this->type_) * this->height_;
|
||||
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
|
||||
}
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
37
esphome/components/display/animation.h
Normal file
37
esphome/components/display/animation.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "image.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
class Animation : public Image {
|
||||
public:
|
||||
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type);
|
||||
|
||||
uint32_t get_animation_frame_count() const;
|
||||
int get_current_frame() const;
|
||||
void next_frame();
|
||||
void prev_frame();
|
||||
|
||||
/** Selects a specific frame within the animation.
|
||||
*
|
||||
* @param frame If possitive, advance to the frame. If negative, recede to that frame from the end frame.
|
||||
*/
|
||||
void set_frame(int frame);
|
||||
|
||||
void set_loop(uint32_t start_frame, uint32_t end_frame, int count);
|
||||
|
||||
protected:
|
||||
void update_data_start_();
|
||||
|
||||
const uint8_t *animation_data_start_;
|
||||
int current_frame_;
|
||||
uint32_t animation_frame_count_;
|
||||
uint32_t loop_start_frame_;
|
||||
uint32_t loop_end_frame_;
|
||||
int loop_count_;
|
||||
int loop_current_iteration_;
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
@@ -7,6 +7,10 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "animation.h"
|
||||
#include "image.h"
|
||||
#include "font.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
@@ -15,25 +19,6 @@ static const char *const TAG = "display";
|
||||
const Color COLOR_OFF(0, 0, 0, 255);
|
||||
const Color COLOR_ON(255, 255, 255, 255);
|
||||
|
||||
static int image_type_to_bpp(ImageType type) {
|
||||
switch (type) {
|
||||
case IMAGE_TYPE_BINARY:
|
||||
return 1;
|
||||
case IMAGE_TYPE_GRAYSCALE:
|
||||
return 8;
|
||||
case IMAGE_TYPE_RGB565:
|
||||
return 16;
|
||||
case IMAGE_TYPE_RGB24:
|
||||
return 24;
|
||||
case IMAGE_TYPE_RGBA:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int image_type_to_width_stride(int width, ImageType type) { return (width * image_type_to_bpp(type) + 7u) / 8u; }
|
||||
|
||||
void Rect::expand(int16_t horizontal, int16_t vertical) {
|
||||
if (this->is_set() && (this->w >= (-2 * horizontal)) && (this->h >= (-2 * vertical))) {
|
||||
this->x = this->x - horizontal;
|
||||
@@ -326,6 +311,37 @@ void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign al
|
||||
}
|
||||
|
||||
void DisplayBuffer::image(int x, int y, BaseImage *image, Color color_on, Color color_off) {
|
||||
this->image(x, y, image, ImageAlign::TOP_LEFT, color_on, color_off);
|
||||
}
|
||||
|
||||
void DisplayBuffer::image(int x, int y, BaseImage *image, ImageAlign align, Color color_on, Color color_off) {
|
||||
auto x_align = ImageAlign(int(align) & (int(ImageAlign::HORIZONTAL_ALIGNMENT)));
|
||||
auto y_align = ImageAlign(int(align) & (int(ImageAlign::VERTICAL_ALIGNMENT)));
|
||||
|
||||
switch (x_align) {
|
||||
case ImageAlign::RIGHT:
|
||||
x -= image->get_width();
|
||||
break;
|
||||
case ImageAlign::CENTER_HORIZONTAL:
|
||||
x -= image->get_width() / 2;
|
||||
break;
|
||||
case ImageAlign::LEFT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (y_align) {
|
||||
case ImageAlign::BOTTOM:
|
||||
y -= image->get_height();
|
||||
break;
|
||||
case ImageAlign::CENTER_VERTICAL:
|
||||
y -= image->get_height() / 2;
|
||||
break;
|
||||
case ImageAlign::TOP:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
image->draw(x, y, this, color_on, color_off);
|
||||
}
|
||||
|
||||
@@ -505,286 +521,6 @@ Rect DisplayBuffer::get_clipping() {
|
||||
return this->clipping_rectangle_.back();
|
||||
}
|
||||
}
|
||||
bool Glyph::get_pixel(int x, int y) const {
|
||||
const int x_data = x - this->glyph_data_->offset_x;
|
||||
const int y_data = y - this->glyph_data_->offset_y;
|
||||
if (x_data < 0 || x_data >= this->glyph_data_->width || y_data < 0 || y_data >= this->glyph_data_->height)
|
||||
return false;
|
||||
const uint32_t width_8 = ((this->glyph_data_->width + 7u) / 8u) * 8u;
|
||||
const uint32_t pos = x_data + y_data * width_8;
|
||||
return progmem_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u));
|
||||
}
|
||||
const char *Glyph::get_char() const { return this->glyph_data_->a_char; }
|
||||
bool Glyph::compare_to(const char *str) const {
|
||||
// 1 -> this->char_
|
||||
// 2 -> str
|
||||
for (uint32_t i = 0;; i++) {
|
||||
if (this->glyph_data_->a_char[i] == '\0')
|
||||
return true;
|
||||
if (str[i] == '\0')
|
||||
return false;
|
||||
if (this->glyph_data_->a_char[i] > str[i])
|
||||
return false;
|
||||
if (this->glyph_data_->a_char[i] < str[i])
|
||||
return true;
|
||||
}
|
||||
// this should not happen
|
||||
return false;
|
||||
}
|
||||
int Glyph::match_length(const char *str) const {
|
||||
for (uint32_t i = 0;; i++) {
|
||||
if (this->glyph_data_->a_char[i] == '\0')
|
||||
return i;
|
||||
if (str[i] != this->glyph_data_->a_char[i])
|
||||
return 0;
|
||||
}
|
||||
// this should not happen
|
||||
return 0;
|
||||
}
|
||||
void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
|
||||
*x1 = this->glyph_data_->offset_x;
|
||||
*y1 = this->glyph_data_->offset_y;
|
||||
*width = this->glyph_data_->width;
|
||||
*height = this->glyph_data_->height;
|
||||
}
|
||||
int Font::match_next_glyph(const char *str, int *match_length) {
|
||||
int lo = 0;
|
||||
int hi = this->glyphs_.size() - 1;
|
||||
while (lo != hi) {
|
||||
int mid = (lo + hi + 1) / 2;
|
||||
if (this->glyphs_[mid].compare_to(str)) {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid - 1;
|
||||
}
|
||||
}
|
||||
*match_length = this->glyphs_[lo].match_length(str);
|
||||
if (*match_length <= 0)
|
||||
return -1;
|
||||
return lo;
|
||||
}
|
||||
void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
|
||||
*baseline = this->baseline_;
|
||||
*height = this->height_;
|
||||
int i = 0;
|
||||
int min_x = 0;
|
||||
bool has_char = false;
|
||||
int x = 0;
|
||||
while (str[i] != '\0') {
|
||||
int match_length;
|
||||
int glyph_n = this->match_next_glyph(str + i, &match_length);
|
||||
if (glyph_n < 0) {
|
||||
// Unknown char, skip
|
||||
if (!this->get_glyphs().empty())
|
||||
x += this->get_glyphs()[0].glyph_data_->width;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const Glyph &glyph = this->glyphs_[glyph_n];
|
||||
if (!has_char) {
|
||||
min_x = glyph.glyph_data_->offset_x;
|
||||
} else {
|
||||
min_x = std::min(min_x, x + glyph.glyph_data_->offset_x);
|
||||
}
|
||||
x += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
|
||||
|
||||
i += match_length;
|
||||
has_char = true;
|
||||
}
|
||||
*x_offset = min_x;
|
||||
*width = x - min_x;
|
||||
}
|
||||
Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : baseline_(baseline), height_(height) {
|
||||
glyphs_.reserve(data_nr);
|
||||
for (int i = 0; i < data_nr; ++i)
|
||||
glyphs_.emplace_back(&data[i]);
|
||||
}
|
||||
|
||||
void Image::draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) {
|
||||
switch (type_) {
|
||||
case IMAGE_TYPE_BINARY: {
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
if (this->get_binary_pixel_(img_x, img_y)) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color_on);
|
||||
} else if (!this->transparent_) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color_off);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_TYPE_GRAYSCALE:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_grayscale_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IMAGE_TYPE_RGB565:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_rgb565_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IMAGE_TYPE_RGB24:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_rgb24_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IMAGE_TYPE_RGBA:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_rgba_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const {
|
||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||
return color_off;
|
||||
switch (this->type_) {
|
||||
case IMAGE_TYPE_BINARY:
|
||||
return this->get_binary_pixel_(x, y) ? color_on : color_off;
|
||||
case IMAGE_TYPE_GRAYSCALE:
|
||||
return this->get_grayscale_pixel_(x, y);
|
||||
case IMAGE_TYPE_RGB565:
|
||||
return this->get_rgb565_pixel_(x, y);
|
||||
case IMAGE_TYPE_RGB24:
|
||||
return this->get_rgb24_pixel_(x, y);
|
||||
case IMAGE_TYPE_RGBA:
|
||||
return this->get_rgba_pixel_(x, y);
|
||||
default:
|
||||
return color_off;
|
||||
}
|
||||
}
|
||||
bool Image::get_binary_pixel_(int x, int y) const {
|
||||
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
|
||||
const uint32_t pos = x + y * width_8;
|
||||
return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
|
||||
}
|
||||
Color Image::get_rgba_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_) * 4;
|
||||
return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
||||
progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3));
|
||||
}
|
||||
Color Image::get_rgb24_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_) * 3;
|
||||
Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
||||
progmem_read_byte(this->data_start_ + pos + 2));
|
||||
if (color.b == 1 && color.r == 0 && color.g == 0 && transparent_) {
|
||||
// (0, 0, 1) has been defined as transparent color for non-alpha images.
|
||||
// putting blue == 1 as a first condition for performance reasons (least likely value to short-cut the if)
|
||||
color.w = 0;
|
||||
} else {
|
||||
color.w = 0xFF;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
Color Image::get_rgb565_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_) * 2;
|
||||
uint16_t rgb565 =
|
||||
progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1);
|
||||
auto r = (rgb565 & 0xF800) >> 11;
|
||||
auto g = (rgb565 & 0x07E0) >> 5;
|
||||
auto b = rgb565 & 0x001F;
|
||||
Color color = Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2));
|
||||
if (rgb565 == 0x0020 && transparent_) {
|
||||
// darkest green has been defined as transparent color for transparent RGB565 images.
|
||||
color.w = 0;
|
||||
} else {
|
||||
color.w = 0xFF;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
Color Image::get_grayscale_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_);
|
||||
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
|
||||
uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF;
|
||||
return Color(gray, gray, gray, alpha);
|
||||
}
|
||||
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, ImageType type)
|
||||
: width_(width), height_(height), type_(type), data_start_(data_start) {}
|
||||
|
||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type)
|
||||
: Image(data_start, width, height, type),
|
||||
animation_data_start_(data_start),
|
||||
current_frame_(0),
|
||||
animation_frame_count_(animation_frame_count),
|
||||
loop_start_frame_(0),
|
||||
loop_end_frame_(animation_frame_count_),
|
||||
loop_count_(0),
|
||||
loop_current_iteration_(1) {}
|
||||
void Animation::set_loop(uint32_t start_frame, uint32_t end_frame, int count) {
|
||||
loop_start_frame_ = std::min(start_frame, animation_frame_count_);
|
||||
loop_end_frame_ = std::min(end_frame, animation_frame_count_);
|
||||
loop_count_ = count;
|
||||
loop_current_iteration_ = 1;
|
||||
}
|
||||
|
||||
uint32_t Animation::get_animation_frame_count() const { return this->animation_frame_count_; }
|
||||
int Animation::get_current_frame() const { return this->current_frame_; }
|
||||
void Animation::next_frame() {
|
||||
this->current_frame_++;
|
||||
if (loop_count_ && this->current_frame_ == loop_end_frame_ &&
|
||||
(this->loop_current_iteration_ < loop_count_ || loop_count_ < 0)) {
|
||||
this->current_frame_ = loop_start_frame_;
|
||||
this->loop_current_iteration_++;
|
||||
}
|
||||
if (this->current_frame_ >= animation_frame_count_) {
|
||||
this->loop_current_iteration_ = 1;
|
||||
this->current_frame_ = 0;
|
||||
}
|
||||
|
||||
this->update_data_start_();
|
||||
}
|
||||
void Animation::prev_frame() {
|
||||
this->current_frame_--;
|
||||
if (this->current_frame_ < 0) {
|
||||
this->current_frame_ = this->animation_frame_count_ - 1;
|
||||
}
|
||||
|
||||
this->update_data_start_();
|
||||
}
|
||||
|
||||
void Animation::set_frame(int frame) {
|
||||
unsigned abs_frame = abs(frame);
|
||||
|
||||
if (abs_frame < this->animation_frame_count_) {
|
||||
if (frame >= 0) {
|
||||
this->current_frame_ = frame;
|
||||
} else {
|
||||
this->current_frame_ = this->animation_frame_count_ - abs_frame;
|
||||
}
|
||||
}
|
||||
|
||||
this->update_data_start_();
|
||||
}
|
||||
|
||||
void Animation::update_data_start_() {
|
||||
const uint32_t image_size = image_type_to_width_stride(this->width_, this->type_) * this->height_;
|
||||
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
|
||||
}
|
||||
|
||||
DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {}
|
||||
void DisplayPage::show() { this->parent_->show_page(this); }
|
||||
|
@@ -16,6 +16,10 @@
|
||||
#include "esphome/components/qr_code/qr_code.h"
|
||||
#endif
|
||||
|
||||
#include "animation.h"
|
||||
#include "font.h"
|
||||
#include "image.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
@@ -70,17 +74,52 @@ enum class TextAlign {
|
||||
BOTTOM_RIGHT = BOTTOM | RIGHT,
|
||||
};
|
||||
|
||||
/// Turn the pixel OFF.
|
||||
extern const Color COLOR_OFF;
|
||||
/// Turn the pixel ON.
|
||||
extern const Color COLOR_ON;
|
||||
/** ImageAlign is used to tell the display class how to position a image. By default
|
||||
* the coordinates you enter for the image() functions take the upper left corner of the image
|
||||
* as the "anchor" point. You can customize this behavior to, for example, make the coordinates
|
||||
* refer to the *center* of the image.
|
||||
*
|
||||
* All image alignments consist of an X and Y-coordinate alignment. For the alignment along the X-axis
|
||||
* these options are allowed:
|
||||
*
|
||||
* - LEFT (x-coordinate of anchor point is on left)
|
||||
* - CENTER_HORIZONTAL (x-coordinate of anchor point is in the horizontal center of the image)
|
||||
* - RIGHT (x-coordinate of anchor point is on right)
|
||||
*
|
||||
* For the Y-Axis alignment these options are allowed:
|
||||
*
|
||||
* - TOP (y-coordinate of anchor is on the top of the image)
|
||||
* - CENTER_VERTICAL (y-coordinate of anchor is in the vertical center of the image)
|
||||
* - BOTTOM (y-coordinate of anchor is on the bottom of the image)
|
||||
*
|
||||
* These options are then combined to create combined TextAlignment options like:
|
||||
* - TOP_LEFT (default)
|
||||
* - CENTER (anchor point is in the middle of the image bounds)
|
||||
* - ...
|
||||
*/
|
||||
enum class ImageAlign {
|
||||
TOP = 0x00,
|
||||
CENTER_VERTICAL = 0x01,
|
||||
BOTTOM = 0x02,
|
||||
|
||||
enum ImageType {
|
||||
IMAGE_TYPE_BINARY = 0,
|
||||
IMAGE_TYPE_GRAYSCALE = 1,
|
||||
IMAGE_TYPE_RGB24 = 2,
|
||||
IMAGE_TYPE_RGB565 = 3,
|
||||
IMAGE_TYPE_RGBA = 4,
|
||||
LEFT = 0x00,
|
||||
CENTER_HORIZONTAL = 0x04,
|
||||
RIGHT = 0x08,
|
||||
|
||||
TOP_LEFT = TOP | LEFT,
|
||||
TOP_CENTER = TOP | CENTER_HORIZONTAL,
|
||||
TOP_RIGHT = TOP | RIGHT,
|
||||
|
||||
CENTER_LEFT = CENTER_VERTICAL | LEFT,
|
||||
CENTER = CENTER_VERTICAL | CENTER_HORIZONTAL,
|
||||
CENTER_RIGHT = CENTER_VERTICAL | RIGHT,
|
||||
|
||||
BOTTOM_LEFT = BOTTOM | LEFT,
|
||||
BOTTOM_CENTER = BOTTOM | CENTER_HORIZONTAL,
|
||||
BOTTOM_RIGHT = BOTTOM | RIGHT,
|
||||
|
||||
HORIZONTAL_ALIGNMENT = LEFT | CENTER_HORIZONTAL | RIGHT,
|
||||
VERTICAL_ALIGNMENT = TOP | CENTER_VERTICAL | BOTTOM
|
||||
};
|
||||
|
||||
enum DisplayType {
|
||||
@@ -123,8 +162,6 @@ class Rect {
|
||||
void info(const std::string &prefix = "rect info:");
|
||||
};
|
||||
|
||||
class BaseImage;
|
||||
class Font;
|
||||
class DisplayBuffer;
|
||||
class DisplayPage;
|
||||
class DisplayOnPageChangeTrigger;
|
||||
@@ -311,12 +348,23 @@ class DisplayBuffer {
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param image The image to draw
|
||||
* @param image The image to draw.
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
* @param color_off The color to replace in binary images for the off bits.
|
||||
*/
|
||||
void image(int x, int y, BaseImage *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
||||
|
||||
/** Draw the `image` at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param image The image to draw.
|
||||
* @param align The alignment of the image.
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
* @param color_off The color to replace in binary images for the off bits.
|
||||
*/
|
||||
void image(int x, int y, BaseImage *image, ImageAlign align, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
||||
|
||||
#ifdef USE_GRAPH
|
||||
/** Draw the `graph` with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
@@ -475,123 +523,6 @@ class DisplayPage {
|
||||
DisplayPage *next_{nullptr};
|
||||
};
|
||||
|
||||
struct GlyphData {
|
||||
const char *a_char;
|
||||
const uint8_t *data;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
class Glyph {
|
||||
public:
|
||||
Glyph(const GlyphData *data) : glyph_data_(data) {}
|
||||
|
||||
bool get_pixel(int x, int y) const;
|
||||
|
||||
const char *get_char() const;
|
||||
|
||||
bool compare_to(const char *str) const;
|
||||
|
||||
int match_length(const char *str) const;
|
||||
|
||||
void scan_area(int *x1, int *y1, int *width, int *height) const;
|
||||
|
||||
protected:
|
||||
friend Font;
|
||||
friend DisplayBuffer;
|
||||
|
||||
const GlyphData *glyph_data_;
|
||||
};
|
||||
|
||||
class Font {
|
||||
public:
|
||||
/** Construct the font with the given glyphs.
|
||||
*
|
||||
* @param glyphs A vector of glyphs, must be sorted lexicographically.
|
||||
* @param baseline The y-offset from the top of the text to the baseline.
|
||||
* @param bottom The y-offset from the top of the text to the bottom (i.e. height).
|
||||
*/
|
||||
Font(const GlyphData *data, int data_nr, int baseline, int height);
|
||||
|
||||
int match_next_glyph(const char *str, int *match_length);
|
||||
|
||||
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height);
|
||||
inline int get_baseline() { return this->baseline_; }
|
||||
inline int get_height() { return this->height_; }
|
||||
|
||||
const std::vector<Glyph, ExternalRAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
||||
|
||||
protected:
|
||||
std::vector<Glyph, ExternalRAMAllocator<Glyph>> glyphs_;
|
||||
int baseline_;
|
||||
int height_;
|
||||
};
|
||||
|
||||
class BaseImage {
|
||||
public:
|
||||
virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0;
|
||||
virtual int get_width() const = 0;
|
||||
virtual int get_height() const = 0;
|
||||
};
|
||||
|
||||
class Image : public BaseImage {
|
||||
public:
|
||||
Image(const uint8_t *data_start, int width, int height, ImageType type);
|
||||
Color get_pixel(int x, int y, Color color_on = COLOR_ON, Color color_off = COLOR_OFF) const;
|
||||
int get_width() const override;
|
||||
int get_height() const override;
|
||||
ImageType get_type() const;
|
||||
|
||||
void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) override;
|
||||
|
||||
void set_transparency(bool transparent) { transparent_ = transparent; }
|
||||
bool has_transparency() const { return transparent_; }
|
||||
|
||||
protected:
|
||||
bool get_binary_pixel_(int x, int y) const;
|
||||
Color get_rgb24_pixel_(int x, int y) const;
|
||||
Color get_rgba_pixel_(int x, int y) const;
|
||||
Color get_rgb565_pixel_(int x, int y) const;
|
||||
Color get_grayscale_pixel_(int x, int y) const;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
ImageType type_;
|
||||
const uint8_t *data_start_;
|
||||
bool transparent_;
|
||||
};
|
||||
|
||||
class Animation : public Image {
|
||||
public:
|
||||
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type);
|
||||
|
||||
uint32_t get_animation_frame_count() const;
|
||||
int get_current_frame() const;
|
||||
void next_frame();
|
||||
void prev_frame();
|
||||
|
||||
/** Selects a specific frame within the animation.
|
||||
*
|
||||
* @param frame If possitive, advance to the frame. If negative, recede to that frame from the end frame.
|
||||
*/
|
||||
void set_frame(int frame);
|
||||
|
||||
void set_loop(uint32_t start_frame, uint32_t end_frame, int count);
|
||||
|
||||
protected:
|
||||
void update_data_start_();
|
||||
|
||||
const uint8_t *animation_data_start_;
|
||||
int current_frame_;
|
||||
uint32_t animation_frame_count_;
|
||||
uint32_t loop_start_frame_;
|
||||
uint32_t loop_end_frame_;
|
||||
int loop_count_;
|
||||
int loop_current_iteration_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayPageShowAction : public Action<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(DisplayPage *, page)
|
||||
|
105
esphome/components/display/font.cpp
Normal file
105
esphome/components/display/font.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "font.h"
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
bool Glyph::get_pixel(int x, int y) const {
|
||||
const int x_data = x - this->glyph_data_->offset_x;
|
||||
const int y_data = y - this->glyph_data_->offset_y;
|
||||
if (x_data < 0 || x_data >= this->glyph_data_->width || y_data < 0 || y_data >= this->glyph_data_->height)
|
||||
return false;
|
||||
const uint32_t width_8 = ((this->glyph_data_->width + 7u) / 8u) * 8u;
|
||||
const uint32_t pos = x_data + y_data * width_8;
|
||||
return progmem_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u));
|
||||
}
|
||||
const char *Glyph::get_char() const { return this->glyph_data_->a_char; }
|
||||
bool Glyph::compare_to(const char *str) const {
|
||||
// 1 -> this->char_
|
||||
// 2 -> str
|
||||
for (uint32_t i = 0;; i++) {
|
||||
if (this->glyph_data_->a_char[i] == '\0')
|
||||
return true;
|
||||
if (str[i] == '\0')
|
||||
return false;
|
||||
if (this->glyph_data_->a_char[i] > str[i])
|
||||
return false;
|
||||
if (this->glyph_data_->a_char[i] < str[i])
|
||||
return true;
|
||||
}
|
||||
// this should not happen
|
||||
return false;
|
||||
}
|
||||
int Glyph::match_length(const char *str) const {
|
||||
for (uint32_t i = 0;; i++) {
|
||||
if (this->glyph_data_->a_char[i] == '\0')
|
||||
return i;
|
||||
if (str[i] != this->glyph_data_->a_char[i])
|
||||
return 0;
|
||||
}
|
||||
// this should not happen
|
||||
return 0;
|
||||
}
|
||||
void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
|
||||
*x1 = this->glyph_data_->offset_x;
|
||||
*y1 = this->glyph_data_->offset_y;
|
||||
*width = this->glyph_data_->width;
|
||||
*height = this->glyph_data_->height;
|
||||
}
|
||||
int Font::match_next_glyph(const char *str, int *match_length) {
|
||||
int lo = 0;
|
||||
int hi = this->glyphs_.size() - 1;
|
||||
while (lo != hi) {
|
||||
int mid = (lo + hi + 1) / 2;
|
||||
if (this->glyphs_[mid].compare_to(str)) {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid - 1;
|
||||
}
|
||||
}
|
||||
*match_length = this->glyphs_[lo].match_length(str);
|
||||
if (*match_length <= 0)
|
||||
return -1;
|
||||
return lo;
|
||||
}
|
||||
void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
|
||||
*baseline = this->baseline_;
|
||||
*height = this->height_;
|
||||
int i = 0;
|
||||
int min_x = 0;
|
||||
bool has_char = false;
|
||||
int x = 0;
|
||||
while (str[i] != '\0') {
|
||||
int match_length;
|
||||
int glyph_n = this->match_next_glyph(str + i, &match_length);
|
||||
if (glyph_n < 0) {
|
||||
// Unknown char, skip
|
||||
if (!this->get_glyphs().empty())
|
||||
x += this->get_glyphs()[0].glyph_data_->width;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const Glyph &glyph = this->glyphs_[glyph_n];
|
||||
if (!has_char) {
|
||||
min_x = glyph.glyph_data_->offset_x;
|
||||
} else {
|
||||
min_x = std::min(min_x, x + glyph.glyph_data_->offset_x);
|
||||
}
|
||||
x += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
|
||||
|
||||
i += match_length;
|
||||
has_char = true;
|
||||
}
|
||||
*x_offset = min_x;
|
||||
*width = x - min_x;
|
||||
}
|
||||
Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : baseline_(baseline), height_(height) {
|
||||
glyphs_.reserve(data_nr);
|
||||
for (int i = 0; i < data_nr; ++i)
|
||||
glyphs_.emplace_back(&data[i]);
|
||||
}
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
66
esphome/components/display/font.h
Normal file
66
esphome/components/display/font.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/datatypes.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
class DisplayBuffer;
|
||||
class Font;
|
||||
|
||||
struct GlyphData {
|
||||
const char *a_char;
|
||||
const uint8_t *data;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
class Glyph {
|
||||
public:
|
||||
Glyph(const GlyphData *data) : glyph_data_(data) {}
|
||||
|
||||
bool get_pixel(int x, int y) const;
|
||||
|
||||
const char *get_char() const;
|
||||
|
||||
bool compare_to(const char *str) const;
|
||||
|
||||
int match_length(const char *str) const;
|
||||
|
||||
void scan_area(int *x1, int *y1, int *width, int *height) const;
|
||||
|
||||
protected:
|
||||
friend Font;
|
||||
friend DisplayBuffer;
|
||||
|
||||
const GlyphData *glyph_data_;
|
||||
};
|
||||
|
||||
class Font {
|
||||
public:
|
||||
/** Construct the font with the given glyphs.
|
||||
*
|
||||
* @param glyphs A vector of glyphs, must be sorted lexicographically.
|
||||
* @param baseline The y-offset from the top of the text to the baseline.
|
||||
* @param bottom The y-offset from the top of the text to the bottom (i.e. height).
|
||||
*/
|
||||
Font(const GlyphData *data, int data_nr, int baseline, int height);
|
||||
|
||||
int match_next_glyph(const char *str, int *match_length);
|
||||
|
||||
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height);
|
||||
inline int get_baseline() { return this->baseline_; }
|
||||
inline int get_height() { return this->height_; }
|
||||
|
||||
const std::vector<Glyph, ExternalRAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
||||
|
||||
protected:
|
||||
std::vector<Glyph, ExternalRAMAllocator<Glyph>> glyphs_;
|
||||
int baseline_;
|
||||
int height_;
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
135
esphome/components/display/image.cpp
Normal file
135
esphome/components/display/image.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "image.h"
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "display_buffer.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
void Image::draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) {
|
||||
switch (type_) {
|
||||
case IMAGE_TYPE_BINARY: {
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
if (this->get_binary_pixel_(img_x, img_y)) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color_on);
|
||||
} else if (!this->transparent_) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color_off);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_TYPE_GRAYSCALE:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_grayscale_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IMAGE_TYPE_RGB565:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_rgb565_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IMAGE_TYPE_RGB24:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_rgb24_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IMAGE_TYPE_RGBA:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_rgba_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const {
|
||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||
return color_off;
|
||||
switch (this->type_) {
|
||||
case IMAGE_TYPE_BINARY:
|
||||
return this->get_binary_pixel_(x, y) ? color_on : color_off;
|
||||
case IMAGE_TYPE_GRAYSCALE:
|
||||
return this->get_grayscale_pixel_(x, y);
|
||||
case IMAGE_TYPE_RGB565:
|
||||
return this->get_rgb565_pixel_(x, y);
|
||||
case IMAGE_TYPE_RGB24:
|
||||
return this->get_rgb24_pixel_(x, y);
|
||||
case IMAGE_TYPE_RGBA:
|
||||
return this->get_rgba_pixel_(x, y);
|
||||
default:
|
||||
return color_off;
|
||||
}
|
||||
}
|
||||
bool Image::get_binary_pixel_(int x, int y) const {
|
||||
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
|
||||
const uint32_t pos = x + y * width_8;
|
||||
return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
|
||||
}
|
||||
Color Image::get_rgba_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_) * 4;
|
||||
return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
||||
progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3));
|
||||
}
|
||||
Color Image::get_rgb24_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_) * 3;
|
||||
Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
||||
progmem_read_byte(this->data_start_ + pos + 2));
|
||||
if (color.b == 1 && color.r == 0 && color.g == 0 && transparent_) {
|
||||
// (0, 0, 1) has been defined as transparent color for non-alpha images.
|
||||
// putting blue == 1 as a first condition for performance reasons (least likely value to short-cut the if)
|
||||
color.w = 0;
|
||||
} else {
|
||||
color.w = 0xFF;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
Color Image::get_rgb565_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_) * 2;
|
||||
uint16_t rgb565 =
|
||||
progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1);
|
||||
auto r = (rgb565 & 0xF800) >> 11;
|
||||
auto g = (rgb565 & 0x07E0) >> 5;
|
||||
auto b = rgb565 & 0x001F;
|
||||
Color color = Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2));
|
||||
if (rgb565 == 0x0020 && transparent_) {
|
||||
// darkest green has been defined as transparent color for transparent RGB565 images.
|
||||
color.w = 0;
|
||||
} else {
|
||||
color.w = 0xFF;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
Color Image::get_grayscale_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_);
|
||||
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
|
||||
uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF;
|
||||
return Color(gray, gray, gray, alpha);
|
||||
}
|
||||
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, ImageType type)
|
||||
: width_(width), height_(height), type_(type), data_start_(data_start) {}
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
75
esphome/components/display/image.h
Normal file
75
esphome/components/display/image.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include "esphome/core/color.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
enum ImageType {
|
||||
IMAGE_TYPE_BINARY = 0,
|
||||
IMAGE_TYPE_GRAYSCALE = 1,
|
||||
IMAGE_TYPE_RGB24 = 2,
|
||||
IMAGE_TYPE_RGB565 = 3,
|
||||
IMAGE_TYPE_RGBA = 4,
|
||||
};
|
||||
|
||||
inline int image_type_to_bpp(ImageType type) {
|
||||
switch (type) {
|
||||
case IMAGE_TYPE_BINARY:
|
||||
return 1;
|
||||
case IMAGE_TYPE_GRAYSCALE:
|
||||
return 8;
|
||||
case IMAGE_TYPE_RGB565:
|
||||
return 16;
|
||||
case IMAGE_TYPE_RGB24:
|
||||
return 24;
|
||||
case IMAGE_TYPE_RGBA:
|
||||
return 32;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int image_type_to_width_stride(int width, ImageType type) { return (width * image_type_to_bpp(type) + 7u) / 8u; }
|
||||
|
||||
/// Turn the pixel OFF.
|
||||
extern const Color COLOR_OFF;
|
||||
/// Turn the pixel ON.
|
||||
extern const Color COLOR_ON;
|
||||
|
||||
class DisplayBuffer;
|
||||
|
||||
class BaseImage {
|
||||
public:
|
||||
virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0;
|
||||
virtual int get_width() const = 0;
|
||||
virtual int get_height() const = 0;
|
||||
};
|
||||
|
||||
class Image : public BaseImage {
|
||||
public:
|
||||
Image(const uint8_t *data_start, int width, int height, ImageType type);
|
||||
Color get_pixel(int x, int y, Color color_on = COLOR_ON, Color color_off = COLOR_OFF) const;
|
||||
int get_width() const override;
|
||||
int get_height() const override;
|
||||
ImageType get_type() const;
|
||||
|
||||
void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) override;
|
||||
|
||||
void set_transparency(bool transparent) { transparent_ = transparent; }
|
||||
bool has_transparency() const { return transparent_; }
|
||||
|
||||
protected:
|
||||
bool get_binary_pixel_(int x, int y) const;
|
||||
Color get_rgb24_pixel_(int x, int y) const;
|
||||
Color get_rgba_pixel_(int x, int y) const;
|
||||
Color get_rgb565_pixel_(int x, int y) const;
|
||||
Color get_grayscale_pixel_(int x, int y) const;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
ImageType type_;
|
||||
const uint8_t *data_start_;
|
||||
bool transparent_;
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
@@ -151,11 +151,13 @@ void FujitsuGeneralClimate::transmit_state() {
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_LOW);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_QUIET:
|
||||
SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_SILENT);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
default:
|
||||
SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_AUTO);
|
||||
break;
|
||||
// TODO Quiet / Silent
|
||||
}
|
||||
|
||||
// Set swing
|
||||
@@ -345,8 +347,9 @@ bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
const uint8_t recv_fan_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_FAN_NIBBLE);
|
||||
ESP_LOGV(TAG, "Received fan mode %X", recv_fan_mode);
|
||||
switch (recv_fan_mode) {
|
||||
// TODO No Quiet / Silent in ESPH
|
||||
case FUJITSU_GENERAL_FAN_SILENT:
|
||||
this->fan_mode = climate::CLIMATE_FAN_QUIET;
|
||||
break;
|
||||
case FUJITSU_GENERAL_FAN_LOW:
|
||||
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||
break;
|
||||
|
@@ -52,7 +52,7 @@ class FujitsuGeneralClimate : public climate_ir::ClimateIR {
|
||||
FujitsuGeneralClimate()
|
||||
: ClimateIR(FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1.0f, true, true,
|
||||
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
|
||||
climate::CLIMATE_FAN_HIGH},
|
||||
climate::CLIMATE_FAN_HIGH, climate::CLIMATE_FAN_QUIET},
|
||||
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL,
|
||||
climate::CLIMATE_SWING_BOTH}) {}
|
||||
|
||||
|
@@ -9,11 +9,42 @@ static const char *const TAG = "growatt_solar";
|
||||
static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04;
|
||||
static const uint8_t MODBUS_REGISTER_COUNT[] = {33, 95}; // indexed with enum GrowattProtocolVersion
|
||||
|
||||
void GrowattSolar::loop() {
|
||||
// If update() was unable to send we retry until we can send.
|
||||
if (!this->waiting_to_update_)
|
||||
return;
|
||||
update();
|
||||
}
|
||||
|
||||
void GrowattSolar::update() {
|
||||
// If our last send has had no reply yet, and it wasn't that long ago, do nothing.
|
||||
uint32_t now = millis();
|
||||
if (now - this->last_send_ < this->get_update_interval() / 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The bus might be slow, or there might be other devices, or other components might be talking to our device.
|
||||
if (this->waiting_for_response()) {
|
||||
this->waiting_to_update_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this->waiting_to_update_ = false;
|
||||
this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT[this->protocol_version_]);
|
||||
this->last_send_ = millis();
|
||||
}
|
||||
|
||||
void GrowattSolar::on_modbus_data(const std::vector<uint8_t> &data) {
|
||||
// Other components might be sending commands to our device. But we don't get called with enough
|
||||
// context to know what is what. So if we didn't do a send, we ignore the data.
|
||||
if (!this->last_send_)
|
||||
return;
|
||||
this->last_send_ = 0;
|
||||
|
||||
// Also ignore the data if the message is too short. Otherwise we will publish invalid values.
|
||||
if (data.size() < MODBUS_REGISTER_COUNT[this->protocol_version_] * 2)
|
||||
return;
|
||||
|
||||
auto publish_1_reg_sensor_state = [&](sensor::Sensor *sensor, size_t i, float unit) -> void {
|
||||
if (sensor == nullptr)
|
||||
return;
|
||||
|
@@ -19,6 +19,7 @@ enum GrowattProtocolVersion {
|
||||
|
||||
class GrowattSolar : public PollingComponent, public modbus::ModbusDevice {
|
||||
public:
|
||||
void loop() override;
|
||||
void update() override;
|
||||
void on_modbus_data(const std::vector<uint8_t> &data) override;
|
||||
void dump_config() override;
|
||||
@@ -55,6 +56,9 @@ class GrowattSolar : public PollingComponent, public modbus::ModbusDevice {
|
||||
}
|
||||
|
||||
protected:
|
||||
bool waiting_to_update_;
|
||||
uint32_t last_send_;
|
||||
|
||||
struct GrowattPhase {
|
||||
sensor::Sensor *voltage_sensor_{nullptr};
|
||||
sensor::Sensor *current_sensor_{nullptr};
|
||||
|
@@ -44,6 +44,8 @@ MODELS = {
|
||||
"ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI),
|
||||
"ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI),
|
||||
"ST7796": ili9XXX_ns.class_("ILI9XXXST7796", ili9XXXSPI),
|
||||
"S3BOX": ili9XXX_ns.class_("ILI9XXXS3Box", ili9XXXSPI),
|
||||
"S3BOX_LITE": ili9XXX_ns.class_("ILI9XXXS3BoxLite", ili9XXXSPI),
|
||||
}
|
||||
|
||||
COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
|
||||
|
@@ -421,5 +421,28 @@ void ILI9XXXST7796::initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
// 24_TFT rotated display
|
||||
void ILI9XXXS3Box::initialize() {
|
||||
this->init_lcd_(INITCMD_S3BOX);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 320;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 240;
|
||||
}
|
||||
}
|
||||
|
||||
// 24_TFT rotated display
|
||||
void ILI9XXXS3BoxLite::initialize() {
|
||||
this->init_lcd_(INITCMD_S3BOXLITE);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 320;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 240;
|
||||
}
|
||||
this->invert_display_(true);
|
||||
}
|
||||
|
||||
} // namespace ili9xxx
|
||||
} // namespace esphome
|
||||
|
@@ -134,5 +134,15 @@ class ILI9XXXST7796 : public ILI9XXXDisplay {
|
||||
void initialize() override;
|
||||
};
|
||||
|
||||
class ILI9XXXS3Box : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
};
|
||||
|
||||
class ILI9XXXS3BoxLite : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
};
|
||||
|
||||
} // namespace ili9xxx
|
||||
} // namespace esphome
|
||||
|
@@ -169,6 +169,66 @@ static const uint8_t PROGMEM INITCMD_ST7796[] = {
|
||||
0x00 // End of list
|
||||
};
|
||||
|
||||
static const uint8_t PROGMEM INITCMD_S3BOX[] = {
|
||||
0xEF, 3, 0x03, 0x80, 0x02,
|
||||
0xCF, 3, 0x00, 0xC1, 0x30,
|
||||
0xED, 4, 0x64, 0x03, 0x12, 0x81,
|
||||
0xE8, 3, 0x85, 0x00, 0x78,
|
||||
0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02,
|
||||
0xF7, 1, 0x20,
|
||||
0xEA, 2, 0x00, 0x00,
|
||||
ILI9XXX_PWCTR1 , 1, 0x23, // Power control VRH[5:0]
|
||||
ILI9XXX_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0]
|
||||
ILI9XXX_VMCTR1 , 2, 0x3e, 0x28, // VCM control
|
||||
ILI9XXX_VMCTR2 , 1, 0x86, // VCM control2
|
||||
ILI9XXX_MADCTL , 1, 0xC8, // Memory Access Control
|
||||
ILI9XXX_VSCRSADD, 1, 0x00, // Vertical scroll zero
|
||||
ILI9XXX_PIXFMT , 1, 0x55,
|
||||
ILI9XXX_FRMCTR1 , 2, 0x00, 0x18,
|
||||
ILI9XXX_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control
|
||||
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||
ILI9XXX_GAMMASET , 1, 0x01, // Gamma curve selected
|
||||
ILI9XXX_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x0E, 0x09, 0x00,
|
||||
ILI9XXX_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0x36, 0x0F,
|
||||
ILI9XXX_SLPOUT , 0x80, // Exit Sleep
|
||||
ILI9XXX_DISPON , 0x80, // Display on
|
||||
0x00 // End of list
|
||||
};
|
||||
|
||||
static const uint8_t PROGMEM INITCMD_S3BOXLITE[] = {
|
||||
0xEF, 3, 0x03, 0x80, 0x02,
|
||||
0xCF, 3, 0x00, 0xC1, 0x30,
|
||||
0xED, 4, 0x64, 0x03, 0x12, 0x81,
|
||||
0xE8, 3, 0x85, 0x00, 0x78,
|
||||
0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02,
|
||||
0xF7, 1, 0x20,
|
||||
0xEA, 2, 0x00, 0x00,
|
||||
ILI9XXX_PWCTR1 , 1, 0x23, // Power control VRH[5:0]
|
||||
ILI9XXX_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0]
|
||||
ILI9XXX_VMCTR1 , 2, 0x3e, 0x28, // VCM control
|
||||
ILI9XXX_VMCTR2 , 1, 0x86, // VCM control2
|
||||
ILI9XXX_MADCTL , 1, 0x40, // Memory Access Control
|
||||
ILI9XXX_VSCRSADD, 1, 0x00, // Vertical scroll zero
|
||||
ILI9XXX_PIXFMT , 1, 0x55,
|
||||
ILI9XXX_FRMCTR1 , 2, 0x00, 0x18,
|
||||
ILI9XXX_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control
|
||||
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||
ILI9XXX_GAMMASET , 1, 0x01, // Gamma curve selected
|
||||
ILI9XXX_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x0E, 0x09, 0x00,
|
||||
ILI9XXX_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0x36, 0x0F,
|
||||
ILI9XXX_SLPOUT , 0x80, // Exit Sleep
|
||||
ILI9XXX_DISPON , 0x80, // Display on
|
||||
0x00 // End of list
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace ili9xxx
|
||||
} // namespace esphome
|
||||
|
@@ -29,13 +29,10 @@ void PCA9685Output::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up PCA9685OutputComponent...");
|
||||
|
||||
ESP_LOGV(TAG, " Resetting devices...");
|
||||
uint8_t address_tmp = this->address_;
|
||||
this->set_i2c_address(0x00);
|
||||
if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->set_i2c_address(address_tmp);
|
||||
|
||||
if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) {
|
||||
this->mark_failed();
|
||||
|
@@ -87,6 +87,30 @@ void SPIComponent::setup() {
|
||||
return;
|
||||
}
|
||||
#endif // USE_ESP32
|
||||
#ifdef USE_RP2040
|
||||
static uint8_t spi_bus_num = 0;
|
||||
if (spi_bus_num >= 2) {
|
||||
use_hw_spi = false;
|
||||
}
|
||||
if (use_hw_spi) {
|
||||
SPIClassRP2040 *spi;
|
||||
if (spi_bus_num == 0) {
|
||||
spi = &SPI;
|
||||
} else {
|
||||
spi = &SPI1;
|
||||
}
|
||||
spi_bus_num++;
|
||||
|
||||
if (miso_pin != -1)
|
||||
spi->setRX(miso_pin);
|
||||
if (mosi_pin != -1)
|
||||
spi->setTX(mosi_pin);
|
||||
spi->setSCK(clk_pin);
|
||||
this->hw_spi_ = spi;
|
||||
this->hw_spi_->begin();
|
||||
return;
|
||||
}
|
||||
#endif // USE_RP2040
|
||||
#endif // USE_SPI_ARDUINO_BACKEND
|
||||
|
||||
if (this->miso_ != nullptr) {
|
||||
|
@@ -15,6 +15,7 @@ VBus = vbus_ns.class_("VBus", uart.UARTDevice, cg.Component)
|
||||
CONF_VBUS_ID = "vbus_id"
|
||||
|
||||
CONF_DELTASOL_BS_PLUS = "deltasol_bs_plus"
|
||||
CONF_DELTASOL_BS_2009 = "deltasol_bs_2009"
|
||||
CONF_DELTASOL_C = "deltasol_c"
|
||||
CONF_DELTASOL_CS2 = "deltasol_cs2"
|
||||
CONF_DELTASOL_CS_PLUS = "deltasol_cs_plus"
|
||||
|
@@ -18,12 +18,14 @@ from .. import (
|
||||
VBus,
|
||||
CONF_VBUS_ID,
|
||||
CONF_DELTASOL_BS_PLUS,
|
||||
CONF_DELTASOL_BS_2009,
|
||||
CONF_DELTASOL_C,
|
||||
CONF_DELTASOL_CS2,
|
||||
CONF_DELTASOL_CS_PLUS,
|
||||
)
|
||||
|
||||
DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusBSensor", cg.Component)
|
||||
DeltaSol_BS_2009 = vbus_ns.class_("DeltaSolBS2009BSensor", cg.Component)
|
||||
DeltaSol_C = vbus_ns.class_("DeltaSolCBSensor", cg.Component)
|
||||
DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2BSensor", cg.Component)
|
||||
DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusBSensor", cg.Component)
|
||||
@@ -42,6 +44,7 @@ CONF_COLLECTOR_FROST = "collector_frost"
|
||||
CONF_TUBE_COLLECTOR = "tube_collector"
|
||||
CONF_RECOOLING = "recooling"
|
||||
CONF_HQM = "hqm"
|
||||
CONF_FROST_PROTECTION_ACTIVE = "frost_protection_active"
|
||||
|
||||
CONFIG_SCHEMA = cv.typed_schema(
|
||||
{
|
||||
@@ -87,6 +90,33 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||
),
|
||||
}
|
||||
),
|
||||
CONF_DELTASOL_BS_2009: cv.COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DeltaSol_BS_2009),
|
||||
cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
|
||||
cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_PROBLEM,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_PROBLEM,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_PROBLEM,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_PROBLEM,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_FROST_PROTECTION_ACTIVE
|
||||
): binary_sensor.binary_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
}
|
||||
),
|
||||
CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DeltaSol_C),
|
||||
@@ -222,6 +252,28 @@ async def to_code(config):
|
||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HQM])
|
||||
cg.add(var.set_hqm_bsensor(sens))
|
||||
|
||||
elif config[CONF_MODEL] == CONF_DELTASOL_BS_2009:
|
||||
cg.add(var.set_command(0x0100))
|
||||
cg.add(var.set_source(0x427B))
|
||||
cg.add(var.set_dest(0x0010))
|
||||
if CONF_SENSOR1_ERROR in config:
|
||||
sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR])
|
||||
cg.add(var.set_s1_error_bsensor(sens))
|
||||
if CONF_SENSOR2_ERROR in config:
|
||||
sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR])
|
||||
cg.add(var.set_s2_error_bsensor(sens))
|
||||
if CONF_SENSOR3_ERROR in config:
|
||||
sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR])
|
||||
cg.add(var.set_s3_error_bsensor(sens))
|
||||
if CONF_SENSOR4_ERROR in config:
|
||||
sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR])
|
||||
cg.add(var.set_s4_error_bsensor(sens))
|
||||
if CONF_FROST_PROTECTION_ACTIVE in config:
|
||||
sens = await binary_sensor.new_binary_sensor(
|
||||
config[CONF_FROST_PROTECTION_ACTIVE]
|
||||
)
|
||||
cg.add(var.set_frost_protection_active_bsensor(sens))
|
||||
|
||||
elif config[CONF_MODEL] == CONF_DELTASOL_C:
|
||||
cg.add(var.set_command(0x0100))
|
||||
cg.add(var.set_source(0x4212))
|
||||
|
@@ -50,6 +50,28 @@ void DeltaSolBSPlusBSensor::handle_message(std::vector<uint8_t> &message) {
|
||||
this->hqm_bsensor_->publish_state(message[15] & 0x20);
|
||||
}
|
||||
|
||||
void DeltaSolBS2009BSensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Deltasol BS 2009:");
|
||||
LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_);
|
||||
LOG_BINARY_SENSOR(" ", "Sensor 2 Error", this->s2_error_bsensor_);
|
||||
LOG_BINARY_SENSOR(" ", "Sensor 3 Error", this->s3_error_bsensor_);
|
||||
LOG_BINARY_SENSOR(" ", "Sensor 4 Error", this->s4_error_bsensor_);
|
||||
LOG_BINARY_SENSOR(" ", "Frost Protection Active", this->frost_protection_active_bsensor_);
|
||||
}
|
||||
|
||||
void DeltaSolBS2009BSensor::handle_message(std::vector<uint8_t> &message) {
|
||||
if (this->s1_error_bsensor_ != nullptr)
|
||||
this->s1_error_bsensor_->publish_state(message[20] & 1);
|
||||
if (this->s2_error_bsensor_ != nullptr)
|
||||
this->s2_error_bsensor_->publish_state(message[20] & 2);
|
||||
if (this->s3_error_bsensor_ != nullptr)
|
||||
this->s3_error_bsensor_->publish_state(message[20] & 4);
|
||||
if (this->s4_error_bsensor_ != nullptr)
|
||||
this->s4_error_bsensor_->publish_state(message[20] & 8);
|
||||
if (this->frost_protection_active_bsensor_ != nullptr)
|
||||
this->frost_protection_active_bsensor_->publish_state(message[25] & 1);
|
||||
}
|
||||
|
||||
void DeltaSolCBSensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Deltasol C:");
|
||||
LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_);
|
||||
|
@@ -39,6 +39,27 @@ class DeltaSolBSPlusBSensor : public VBusListener, public Component {
|
||||
void handle_message(std::vector<uint8_t> &message) override;
|
||||
};
|
||||
|
||||
class DeltaSolBS2009BSensor : public VBusListener, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; }
|
||||
void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; }
|
||||
void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; }
|
||||
void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; }
|
||||
void set_frost_protection_active_bsensor(binary_sensor::BinarySensor *bsensor) {
|
||||
this->frost_protection_active_bsensor_ = bsensor;
|
||||
}
|
||||
|
||||
protected:
|
||||
binary_sensor::BinarySensor *s1_error_bsensor_{nullptr};
|
||||
binary_sensor::BinarySensor *s2_error_bsensor_{nullptr};
|
||||
binary_sensor::BinarySensor *s3_error_bsensor_{nullptr};
|
||||
binary_sensor::BinarySensor *s4_error_bsensor_{nullptr};
|
||||
binary_sensor::BinarySensor *frost_protection_active_bsensor_{nullptr};
|
||||
|
||||
void handle_message(std::vector<uint8_t> &message) override;
|
||||
};
|
||||
|
||||
class DeltaSolCBSensor : public VBusListener, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
|
@@ -33,12 +33,14 @@ from .. import (
|
||||
VBus,
|
||||
CONF_VBUS_ID,
|
||||
CONF_DELTASOL_BS_PLUS,
|
||||
CONF_DELTASOL_BS_2009,
|
||||
CONF_DELTASOL_C,
|
||||
CONF_DELTASOL_CS2,
|
||||
CONF_DELTASOL_CS_PLUS,
|
||||
)
|
||||
|
||||
DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusSensor", cg.Component)
|
||||
DeltaSol_BS_2009 = vbus_ns.class_("DeltaSolBS2009Sensor", cg.Component)
|
||||
DeltaSol_C = vbus_ns.class_("DeltaSolCSensor", cg.Component)
|
||||
DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2Sensor", cg.Component)
|
||||
DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusSensor", cg.Component)
|
||||
@@ -142,6 +144,87 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||
),
|
||||
}
|
||||
),
|
||||
CONF_DELTASOL_BS_2009: cv.COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DeltaSol_BS_2009),
|
||||
cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
|
||||
cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_PERCENT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_PERCENT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HOUR,
|
||||
icon=ICON_TIMER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_DURATION,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HOUR,
|
||||
icon=ICON_TIMER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_DURATION,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT_HOURS,
|
||||
icon=ICON_RADIATOR,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TIME): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MINUTE,
|
||||
icon=ICON_TIMER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_DURATION,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_VERSION): sensor.sensor_schema(
|
||||
accuracy_decimals=2,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
}
|
||||
),
|
||||
CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DeltaSol_C),
|
||||
@@ -437,6 +520,44 @@ async def to_code(config):
|
||||
sens = await sensor.new_sensor(config[CONF_VERSION])
|
||||
cg.add(var.set_version_sensor(sens))
|
||||
|
||||
elif config[CONF_MODEL] == CONF_DELTASOL_BS_2009:
|
||||
cg.add(var.set_command(0x0100))
|
||||
cg.add(var.set_source(0x427B))
|
||||
cg.add(var.set_dest(0x0010))
|
||||
if CONF_TEMPERATURE_1 in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1])
|
||||
cg.add(var.set_temperature1_sensor(sens))
|
||||
if CONF_TEMPERATURE_2 in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2])
|
||||
cg.add(var.set_temperature2_sensor(sens))
|
||||
if CONF_TEMPERATURE_3 in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3])
|
||||
cg.add(var.set_temperature3_sensor(sens))
|
||||
if CONF_TEMPERATURE_4 in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4])
|
||||
cg.add(var.set_temperature4_sensor(sens))
|
||||
if CONF_PUMP_SPEED_1 in config:
|
||||
sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1])
|
||||
cg.add(var.set_pump_speed1_sensor(sens))
|
||||
if CONF_PUMP_SPEED_2 in config:
|
||||
sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2])
|
||||
cg.add(var.set_pump_speed2_sensor(sens))
|
||||
if CONF_OPERATING_HOURS_1 in config:
|
||||
sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1])
|
||||
cg.add(var.set_operating_hours1_sensor(sens))
|
||||
if CONF_OPERATING_HOURS_2 in config:
|
||||
sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2])
|
||||
cg.add(var.set_operating_hours2_sensor(sens))
|
||||
if CONF_HEAT_QUANTITY in config:
|
||||
sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY])
|
||||
cg.add(var.set_heat_quantity_sensor(sens))
|
||||
if CONF_TIME in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TIME])
|
||||
cg.add(var.set_time_sensor(sens))
|
||||
if CONF_VERSION in config:
|
||||
sens = await sensor.new_sensor(config[CONF_VERSION])
|
||||
cg.add(var.set_version_sensor(sens))
|
||||
|
||||
elif config[CONF_MODEL] == CONF_DELTASOL_C:
|
||||
cg.add(var.set_command(0x0100))
|
||||
cg.add(var.set_source(0x4212))
|
||||
|
@@ -57,6 +57,47 @@ void DeltaSolBSPlusSensor::handle_message(std::vector<uint8_t> &message) {
|
||||
this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f);
|
||||
}
|
||||
|
||||
void DeltaSolBS2009Sensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Deltasol BS 2009:");
|
||||
LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature 2", this->temperature2_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature 3", this->temperature3_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature 4", this->temperature4_sensor_);
|
||||
LOG_SENSOR(" ", "Pump Speed 1", this->pump_speed1_sensor_);
|
||||
LOG_SENSOR(" ", "Pump Speed 2", this->pump_speed2_sensor_);
|
||||
LOG_SENSOR(" ", "Operating Hours 1", this->operating_hours1_sensor_);
|
||||
LOG_SENSOR(" ", "Operating Hours 2", this->operating_hours2_sensor_);
|
||||
LOG_SENSOR(" ", "Heat Quantity", this->heat_quantity_sensor_);
|
||||
LOG_SENSOR(" ", "System Time", this->time_sensor_);
|
||||
LOG_SENSOR(" ", "FW Version", this->version_sensor_);
|
||||
}
|
||||
|
||||
void DeltaSolBS2009Sensor::handle_message(std::vector<uint8_t> &message) {
|
||||
if (this->temperature1_sensor_ != nullptr)
|
||||
this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f);
|
||||
if (this->temperature2_sensor_ != nullptr)
|
||||
this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f);
|
||||
if (this->temperature3_sensor_ != nullptr)
|
||||
this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f);
|
||||
if (this->temperature4_sensor_ != nullptr)
|
||||
this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f);
|
||||
if (this->pump_speed1_sensor_ != nullptr)
|
||||
this->pump_speed1_sensor_->publish_state(message[8]);
|
||||
if (this->pump_speed2_sensor_ != nullptr)
|
||||
this->pump_speed2_sensor_->publish_state(message[12]);
|
||||
if (this->operating_hours1_sensor_ != nullptr)
|
||||
this->operating_hours1_sensor_->publish_state(get_u16(message, 10));
|
||||
if (this->operating_hours2_sensor_ != nullptr)
|
||||
this->operating_hours2_sensor_->publish_state(get_u16(message, 18));
|
||||
if (this->heat_quantity_sensor_ != nullptr) {
|
||||
this->heat_quantity_sensor_->publish_state(get_u16(message, 28) + get_u16(message, 30) * 1000);
|
||||
}
|
||||
if (this->time_sensor_ != nullptr)
|
||||
this->time_sensor_->publish_state(get_u16(message, 22));
|
||||
if (this->version_sensor_ != nullptr)
|
||||
this->version_sensor_->publish_state(get_u16(message, 32) * 0.01f);
|
||||
}
|
||||
|
||||
void DeltaSolCSensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Deltasol C:");
|
||||
LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_);
|
||||
@@ -166,9 +207,9 @@ void DeltaSolCSPlusSensor::handle_message(std::vector<uint8_t> &message) {
|
||||
if (this->heat_quantity_sensor_ != nullptr)
|
||||
this->heat_quantity_sensor_->publish_state((get_u16(message, 30) << 16) + get_u16(message, 28));
|
||||
if (this->time_sensor_ != nullptr)
|
||||
this->time_sensor_->publish_state(get_u16(message, 12));
|
||||
this->time_sensor_->publish_state(get_u16(message, 22));
|
||||
if (this->version_sensor_ != nullptr)
|
||||
this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f);
|
||||
this->version_sensor_->publish_state(get_u16(message, 32) * 0.01f);
|
||||
if (this->flow_rate_sensor_ != nullptr)
|
||||
this->flow_rate_sensor_->publish_state(get_u16(message, 38));
|
||||
}
|
||||
|
@@ -37,6 +37,37 @@ class DeltaSolBSPlusSensor : public VBusListener, public Component {
|
||||
void handle_message(std::vector<uint8_t> &message) override;
|
||||
};
|
||||
|
||||
class DeltaSolBS2009Sensor : public VBusListener, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; }
|
||||
void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; }
|
||||
void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; }
|
||||
void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; }
|
||||
void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; }
|
||||
void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; }
|
||||
void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; }
|
||||
void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; }
|
||||
void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; }
|
||||
void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; }
|
||||
void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; }
|
||||
|
||||
protected:
|
||||
sensor::Sensor *temperature1_sensor_{nullptr};
|
||||
sensor::Sensor *temperature2_sensor_{nullptr};
|
||||
sensor::Sensor *temperature3_sensor_{nullptr};
|
||||
sensor::Sensor *temperature4_sensor_{nullptr};
|
||||
sensor::Sensor *pump_speed1_sensor_{nullptr};
|
||||
sensor::Sensor *pump_speed2_sensor_{nullptr};
|
||||
sensor::Sensor *operating_hours1_sensor_{nullptr};
|
||||
sensor::Sensor *operating_hours2_sensor_{nullptr};
|
||||
sensor::Sensor *heat_quantity_sensor_{nullptr};
|
||||
sensor::Sensor *time_sensor_{nullptr};
|
||||
sensor::Sensor *version_sensor_{nullptr};
|
||||
|
||||
void handle_message(std::vector<uint8_t> &message) override;
|
||||
};
|
||||
|
||||
class DeltaSolCSensor : public VBusListener, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
|
@@ -81,6 +81,37 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def build_index_html(config) -> str:
|
||||
html = "<!DOCTYPE html><html><head><meta charset=UTF-8><link rel=icon href=data:>"
|
||||
css_include = config.get(CONF_CSS_INCLUDE)
|
||||
js_include = config.get(CONF_JS_INCLUDE)
|
||||
if css_include:
|
||||
html += "<link rel=stylesheet href=/0.css>"
|
||||
if config[CONF_CSS_URL]:
|
||||
html += f'<link rel=stylesheet href="{config[CONF_CSS_URL]}">'
|
||||
html += "</head><body>"
|
||||
if js_include:
|
||||
html += "<script type=module src=/0.js></script>"
|
||||
html += "<esp-app></esp-app>"
|
||||
if config[CONF_JS_URL]:
|
||||
html += f'<script src="{config[CONF_JS_URL]}"></script>'
|
||||
html += "</body></html>"
|
||||
return html
|
||||
|
||||
|
||||
def add_resource_as_progmem(resource_name: str, content: str) -> None:
|
||||
"""Add a resource to progmem."""
|
||||
content_encoded = content.encode("utf-8")
|
||||
content_encoded_size = len(content_encoded)
|
||||
bytes_as_int = ", ".join(str(x) for x in content_encoded)
|
||||
uint8_t = f"const uint8_t ESPHOME_WEBSERVER_{resource_name}[{content_encoded_size}] PROGMEM = {{{bytes_as_int}}}"
|
||||
size_t = (
|
||||
f"const size_t ESPHOME_WEBSERVER_{resource_name}_SIZE = {content_encoded_size}"
|
||||
)
|
||||
cg.add_global(cg.RawExpression(uint8_t))
|
||||
cg.add_global(cg.RawExpression(size_t))
|
||||
|
||||
|
||||
@coroutine_with_priority(40.0)
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
||||
@@ -89,13 +120,17 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add_define("USE_WEBSERVER")
|
||||
version = config[CONF_VERSION]
|
||||
|
||||
cg.add(paren.set_port(config[CONF_PORT]))
|
||||
cg.add_define("USE_WEBSERVER")
|
||||
cg.add_define("USE_WEBSERVER_PORT", config[CONF_PORT])
|
||||
cg.add_define("USE_WEBSERVER_VERSION", config[CONF_VERSION])
|
||||
cg.add(var.set_css_url(config[CONF_CSS_URL]))
|
||||
cg.add(var.set_js_url(config[CONF_JS_URL]))
|
||||
cg.add_define("USE_WEBSERVER_VERSION", version)
|
||||
if version == 2:
|
||||
add_resource_as_progmem("INDEX_HTML", build_index_html(config))
|
||||
else:
|
||||
cg.add(var.set_css_url(config[CONF_CSS_URL]))
|
||||
cg.add(var.set_js_url(config[CONF_JS_URL]))
|
||||
cg.add(var.set_allow_ota(config[CONF_OTA]))
|
||||
if CONF_AUTH in config:
|
||||
cg.add(paren.set_auth_username(config[CONF_AUTH][CONF_USERNAME]))
|
||||
@@ -103,13 +138,13 @@ async def to_code(config):
|
||||
if CONF_CSS_INCLUDE in config:
|
||||
cg.add_define("USE_WEBSERVER_CSS_INCLUDE")
|
||||
path = CORE.relative_config_path(config[CONF_CSS_INCLUDE])
|
||||
with open(file=path, encoding="utf-8") as myfile:
|
||||
cg.add(var.set_css_include(myfile.read()))
|
||||
with open(file=path, encoding="utf-8") as css_file:
|
||||
add_resource_as_progmem("CSS_INCLUDE", css_file.read())
|
||||
if CONF_JS_INCLUDE in config:
|
||||
cg.add_define("USE_WEBSERVER_JS_INCLUDE")
|
||||
path = CORE.relative_config_path(config[CONF_JS_INCLUDE])
|
||||
with open(file=path, encoding="utf-8") as myfile:
|
||||
cg.add(var.set_js_include(myfile.read()))
|
||||
with open(file=path, encoding="utf-8") as js_file:
|
||||
add_resource_as_progmem("JS_INCLUDE", js_file.read())
|
||||
cg.add(var.set_include_internal(config[CONF_INCLUDE_INTERNAL]))
|
||||
if CONF_LOCAL in config and config[CONF_LOCAL]:
|
||||
cg.add_define("USE_WEBSERVER_LOCAL")
|
||||
|
@@ -90,10 +90,17 @@ WebServer::WebServer(web_server_base::WebServerBase *base)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_WEBSERVER_VERSION == 1
|
||||
void WebServer::set_css_url(const char *css_url) { this->css_url_ = css_url; }
|
||||
void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; }
|
||||
void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; }
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_CSS_INCLUDE
|
||||
void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; }
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER_JS_INCLUDE
|
||||
void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; }
|
||||
#endif
|
||||
|
||||
void WebServer::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up web server...");
|
||||
@@ -159,20 +166,14 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
||||
response->addHeader("Content-Encoding", "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
#else
|
||||
#elif USE_WEBSERVER_VERSION == 1
|
||||
void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("text/html");
|
||||
// All content is controlled and created by user - so allowing all origins is fine here.
|
||||
stream->addHeader("Access-Control-Allow-Origin", "*");
|
||||
#if USE_WEBSERVER_VERSION == 1
|
||||
const std::string &title = App.get_name();
|
||||
stream->print(F("<!DOCTYPE html><html lang=\"en\"><head><meta charset=UTF-8><meta "
|
||||
"name=viewport content=\"width=device-width, initial-scale=1,user-scalable=no\"><title>"));
|
||||
stream->print(title.c_str());
|
||||
stream->print(F("</title>"));
|
||||
#else
|
||||
stream->print(F("<!DOCTYPE html><html><head><meta charset=UTF-8><link rel=icon href=data:>"));
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER_CSS_INCLUDE
|
||||
stream->print(F("<link rel=\"stylesheet\" href=\"/0.css\">"));
|
||||
#endif
|
||||
@@ -182,7 +183,6 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
||||
stream->print(F("\">"));
|
||||
}
|
||||
stream->print(F("</head><body>"));
|
||||
#if USE_WEBSERVER_VERSION == 1
|
||||
stream->print(F("<article class=\"markdown-body\"><h1>"));
|
||||
stream->print(title.c_str());
|
||||
stream->print(F("</h1>"));
|
||||
@@ -308,49 +308,40 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
||||
"type=\"file\" name=\"update\"><input type=\"submit\" value=\"Update\"></form>"));
|
||||
}
|
||||
stream->print(F("<h2>Debug Log</h2><pre id=\"log\"></pre>"));
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER_JS_INCLUDE
|
||||
if (this->js_include_ != nullptr) {
|
||||
stream->print(F("<script type=\"module\" src=\"/0.js\"></script>"));
|
||||
}
|
||||
#endif
|
||||
#if USE_WEBSERVER_VERSION == 2
|
||||
stream->print(F("<esp-app></esp-app>"));
|
||||
#endif
|
||||
if (strlen(this->js_url_) > 0) {
|
||||
stream->print(F("<script src=\""));
|
||||
stream->print(this->js_url_);
|
||||
stream->print(F("\"></script>"));
|
||||
}
|
||||
#if USE_WEBSERVER_VERSION == 1
|
||||
stream->print(F("</article></body></html>"));
|
||||
#else
|
||||
stream->print(F("</body></html>"));
|
||||
#endif
|
||||
|
||||
request->send(stream);
|
||||
}
|
||||
#elif USE_WEBSERVER_VERSION == 2
|
||||
void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
||||
AsyncWebServerResponse *response =
|
||||
request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE);
|
||||
request->send(response);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_CSS_INCLUDE
|
||||
void WebServer::handle_css_request(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("text/css");
|
||||
if (this->css_include_ != nullptr) {
|
||||
stream->print(this->css_include_);
|
||||
}
|
||||
|
||||
request->send(stream);
|
||||
AsyncWebServerResponse *response =
|
||||
request->beginResponse_P(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE);
|
||||
request->send(response);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_JS_INCLUDE
|
||||
void WebServer::handle_js_request(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("text/javascript");
|
||||
if (this->js_include_ != nullptr) {
|
||||
stream->addHeader("Access-Control-Allow-Origin", "*");
|
||||
stream->print(this->js_include_);
|
||||
}
|
||||
|
||||
request->send(stream);
|
||||
AsyncWebServerResponse *response =
|
||||
request->beginResponse_P(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE);
|
||||
request->send(response);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -14,6 +14,22 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#endif
|
||||
|
||||
#if USE_WEBSERVER_VERSION == 2
|
||||
extern const uint8_t ESPHOME_WEBSERVER_INDEX_HTML[] PROGMEM;
|
||||
extern const size_t ESPHOME_WEBSERVER_INDEX_HTML_SIZE;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_CSS_INCLUDE
|
||||
extern const uint8_t ESPHOME_WEBSERVER_CSS_INCLUDE[] PROGMEM;
|
||||
extern const size_t ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_JS_INCLUDE
|
||||
extern const uint8_t ESPHOME_WEBSERVER_JS_INCLUDE[] PROGMEM;
|
||||
extern const size_t ESPHOME_WEBSERVER_JS_INCLUDE_SIZE;
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace web_server {
|
||||
|
||||
@@ -40,6 +56,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
public:
|
||||
WebServer(web_server_base::WebServerBase *base);
|
||||
|
||||
#if USE_WEBSERVER_VERSION == 1
|
||||
/** Set the URL to the CSS <link> that's sent to each client. Defaults to
|
||||
* https://esphome.io/_static/webserver-v1.min.css
|
||||
*
|
||||
@@ -47,24 +64,29 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
*/
|
||||
void set_css_url(const char *css_url);
|
||||
|
||||
/** Set local path to the script that's embedded in the index page. Defaults to
|
||||
*
|
||||
* @param css_include Local path to web server script.
|
||||
*/
|
||||
void set_css_include(const char *css_include);
|
||||
|
||||
/** Set the URL to the script that's embedded in the index page. Defaults to
|
||||
* https://esphome.io/_static/webserver-v1.min.js
|
||||
*
|
||||
* @param js_url The url to the web server script.
|
||||
*/
|
||||
void set_js_url(const char *js_url);
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_CSS_INCLUDE
|
||||
/** Set local path to the script that's embedded in the index page. Defaults to
|
||||
*
|
||||
* @param css_include Local path to web server script.
|
||||
*/
|
||||
void set_css_include(const char *css_include);
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_JS_INCLUDE
|
||||
/** Set local path to the script that's embedded in the index page. Defaults to
|
||||
*
|
||||
* @param js_include Local path to web server script.
|
||||
*/
|
||||
void set_js_include(const char *js_include);
|
||||
#endif
|
||||
|
||||
/** Determine whether internal components should be displayed on the web server.
|
||||
* Defaults to false.
|
||||
@@ -240,10 +262,16 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
web_server_base::WebServerBase *base_;
|
||||
AsyncEventSource events_{"/events"};
|
||||
ListEntitiesIterator entities_iterator_;
|
||||
#if USE_WEBSERVER_VERSION == 1
|
||||
const char *css_url_{nullptr};
|
||||
const char *css_include_{nullptr};
|
||||
const char *js_url_{nullptr};
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER_CSS_INCLUDE
|
||||
const char *css_include_{nullptr};
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER_JS_INCLUDE
|
||||
const char *js_include_{nullptr};
|
||||
#endif
|
||||
bool include_internal_{false};
|
||||
bool allow_ota_{true};
|
||||
#ifdef USE_ESP32
|
||||
|
@@ -83,6 +83,7 @@ class WebServerBase : public Component {
|
||||
return;
|
||||
}
|
||||
this->server_ = std::make_shared<AsyncWebServer>(this->port_);
|
||||
// All content is controlled and created by user - so allowing all origins is fine here.
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||
this->server_->begin();
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2023.6.0b1"
|
||||
__version__ = "2023.6.0b5"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||
|
@@ -99,7 +99,7 @@ void Application::loop() {
|
||||
|
||||
if (this->dump_config_at_ < this->components_.size()) {
|
||||
if (this->dump_config_at_ == 0) {
|
||||
ESP_LOGI(TAG, "ESPHome version " ESPHOME_VERSION " compiled on %s", this->compilation_time_.c_str());
|
||||
ESP_LOGI(TAG, "ESPHome version " ESPHOME_VERSION " compiled on %s", this->compilation_time_);
|
||||
#ifdef ESPHOME_PROJECT_NAME
|
||||
ESP_LOGI(TAG, "Project " ESPHOME_PROJECT_NAME " version " ESPHOME_PROJECT_VERSION);
|
||||
#endif
|
||||
|
@@ -56,7 +56,7 @@ namespace esphome {
|
||||
|
||||
class Application {
|
||||
public:
|
||||
void pre_setup(const std::string &name, const std::string &friendly_name, const std::string &comment,
|
||||
void pre_setup(const std::string &name, const std::string &friendly_name, const char *comment,
|
||||
const char *compilation_time, bool name_add_mac_suffix) {
|
||||
arch_init();
|
||||
this->name_add_mac_suffix_ = name_add_mac_suffix;
|
||||
@@ -154,11 +154,11 @@ class Application {
|
||||
/// Get the friendly name of this Application set by pre_setup().
|
||||
const std::string &get_friendly_name() const { return this->friendly_name_; }
|
||||
/// Get the comment of this Application set by pre_setup().
|
||||
const std::string &get_comment() const { return this->comment_; }
|
||||
std::string get_comment() const { return this->comment_; }
|
||||
|
||||
bool is_name_add_mac_suffix_enabled() const { return this->name_add_mac_suffix_; }
|
||||
|
||||
const std::string &get_compilation_time() const { return this->compilation_time_; }
|
||||
std::string get_compilation_time() const { return this->compilation_time_; }
|
||||
|
||||
/** Set the target interval with which to run the loop() calls.
|
||||
* If the loop() method takes longer than the target interval, ESPHome won't
|
||||
@@ -376,8 +376,8 @@ class Application {
|
||||
|
||||
std::string name_;
|
||||
std::string friendly_name_;
|
||||
std::string comment_;
|
||||
std::string compilation_time_;
|
||||
const char *comment_{nullptr};
|
||||
const char *compilation_time_{nullptr};
|
||||
bool name_add_mac_suffix_;
|
||||
uint32_t last_loop_{0};
|
||||
uint32_t loop_interval_{16};
|
||||
|
@@ -9,7 +9,7 @@ pyserial==3.5
|
||||
platformio==6.1.7 # When updating platformio, also update Dockerfile
|
||||
esptool==4.6
|
||||
click==8.1.3
|
||||
esphome-dashboard==20230516.0
|
||||
esphome-dashboard==20230621.0
|
||||
aioesphomeapi==14.0.0
|
||||
zeroconf==0.63.0
|
||||
|
||||
|
@@ -5,12 +5,13 @@ set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ]; then
|
||||
if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ] && [ ! "$ESPHOME_NO_VENV" ]; then
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
fi
|
||||
|
||||
pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||
pip3 install setuptools wheel
|
||||
pip3 install --no-use-pep517 -e .
|
||||
|
||||
pre-commit install
|
||||
|
@@ -695,6 +695,13 @@ image:
|
||||
file: mdi:alert-outline
|
||||
type: BINARY
|
||||
|
||||
graph:
|
||||
- id: my_graph
|
||||
sensor: ha_hello_world_temperature
|
||||
duration: 1h
|
||||
width: 100
|
||||
height: 100
|
||||
|
||||
cap1188:
|
||||
id: cap1188_component
|
||||
address: 0x29
|
||||
|
Reference in New Issue
Block a user