mirror of
https://github.com/esphome/esphome.git
synced 2024-11-26 17:05:21 +01:00
commit
839fe49e61
25 changed files with 229 additions and 128 deletions
2
.github/workflows/ci-docker.yml
vendored
2
.github/workflows/ci-docker.yml
vendored
|
@ -25,7 +25,7 @@ jobs:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up env variables
|
- name: Set up env variables
|
||||||
run: |
|
run: |
|
||||||
base_version="2.4.1"
|
base_version="2.6.0"
|
||||||
|
|
||||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
|
2
.github/workflows/release-dev.yml
vendored
2
.github/workflows/release-dev.yml
vendored
|
@ -190,7 +190,7 @@ jobs:
|
||||||
echo "::set-env name=TAG::${TAG}"
|
echo "::set-env name=TAG::${TAG}"
|
||||||
- name: Set up env variables
|
- name: Set up env variables
|
||||||
run: |
|
run: |
|
||||||
base_version="2.4.1"
|
base_version="2.6.0"
|
||||||
|
|
||||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -210,7 +210,7 @@ jobs:
|
||||||
echo "::set-env name=TAG::${TAG}"
|
echo "::set-env name=TAG::${TAG}"
|
||||||
- name: Set up env variables
|
- name: Set up env variables
|
||||||
run: |
|
run: |
|
||||||
base_version="2.4.1"
|
base_version="2.6.0"
|
||||||
|
|
||||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
@ -226,7 +226,7 @@ jobs:
|
||||||
cache_tag="beta"
|
cache_tag="beta"
|
||||||
else
|
else
|
||||||
cache_tag="latest"
|
cache_tag="latest"
|
||||||
end
|
fi
|
||||||
|
|
||||||
# Set env variables so these values don't need to be calculated again
|
# Set env variables so these values don't need to be calculated again
|
||||||
echo "::set-env name=BUILD_FROM::${build_from}"
|
echo "::set-env name=BUILD_FROM::${build_from}"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ARG BUILD_FROM=esphome/esphome-base-amd64:2.4.1
|
ARG BUILD_FROM=esphome/esphome-base-amd64:2.6.0
|
||||||
FROM ${BUILD_FROM}
|
FROM ${BUILD_FROM}
|
||||||
|
|
||||||
# First install requirements to leverage caching when requirements don't change
|
# First install requirements to leverage caching when requirements don't change
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM esphome/esphome-base-amd64:2.4.1
|
FROM esphome/esphome-base-amd64:2.6.0
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM esphome/esphome-lint-base:2.4.1
|
FROM esphome/esphome-lint-base:2.6.0
|
||||||
|
|
||||||
COPY requirements.txt requirements_test.txt /
|
COPY requirements.txt requirements_test.txt /
|
||||||
RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt
|
RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt
|
||||||
|
|
|
@ -12,4 +12,4 @@ def to_code(config):
|
||||||
cg.add_library('AsyncTCP-esphome', '1.1.1')
|
cg.add_library('AsyncTCP-esphome', '1.1.1')
|
||||||
elif CORE.is_esp8266:
|
elif CORE.is_esp8266:
|
||||||
# https://github.com/OttoWinter/ESPAsyncTCP
|
# https://github.com/OttoWinter/ESPAsyncTCP
|
||||||
cg.add_library('ESPAsyncTCP-esphome', '1.2.2')
|
cg.add_library('ESPAsyncTCP-esphome', '1.2.3')
|
||||||
|
|
|
@ -204,31 +204,33 @@ void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign al
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
this->print(x, y, font, color, align, buffer);
|
this->print(x, y, font, color, align, buffer);
|
||||||
}
|
}
|
||||||
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) {
|
void DisplayBuffer::image(int x, int y, Image *image, Color color_on, Color color_off) {
|
||||||
if (image->get_type() == BINARY) {
|
switch (image->get_type()) {
|
||||||
|
case IMAGE_TYPE_BINARY:
|
||||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
||||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
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_on : color_off);
|
||||||
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() == GRAYSCALE) {
|
break;
|
||||||
|
case IMAGE_TYPE_GRAYSCALE:
|
||||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
||||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
||||||
this->draw_pixel_at(x + img_x, y + img_y, image->get_grayscale_pixel(img_x, img_y));
|
this->draw_pixel_at(x + img_x, y + img_y, image->get_grayscale_pixel(img_x, img_y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (image->get_type() == RGB) {
|
break;
|
||||||
|
case IMAGE_TYPE_RGB24:
|
||||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
||||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
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));
|
this->draw_pixel_at(x + img_x, y + img_y, image->get_color_pixel(img_x, img_y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1,
|
void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1,
|
||||||
int *width, int *height) {
|
int *width, int *height) {
|
||||||
int x_offset, baseline;
|
int x_offset, baseline;
|
||||||
|
@ -463,15 +465,14 @@ Color Image::get_grayscale_pixel(int x, int y) const {
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||||
return 0;
|
return 0;
|
||||||
const uint32_t pos = (x + y * this->width_);
|
const uint32_t pos = (x + y * this->width_);
|
||||||
return Color(pgm_read_byte(this->data_start_ + pos) << 24);
|
const uint8_t gray = pgm_read_byte(this->data_start_ + pos);
|
||||||
|
return Color(gray | gray << 8 | gray << 16 | gray << 24);
|
||||||
}
|
}
|
||||||
int Image::get_width() const { return this->width_; }
|
int Image::get_width() const { return this->width_; }
|
||||||
int Image::get_height() const { return this->height_; }
|
int Image::get_height() const { return this->height_; }
|
||||||
ImageType Image::get_type() const { return this->type_; }
|
ImageType Image::get_type() const { return this->type_; }
|
||||||
Image::Image(const uint8_t *data_start, int width, int height)
|
Image::Image(const uint8_t *data_start, int width, int height, ImageType type)
|
||||||
: width_(width), height_(height), data_start_(data_start) {}
|
: width_(width), height_(height), type_(type), 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) {}
|
DisplayPage::DisplayPage(const display_writer_t &writer) : writer_(writer) {}
|
||||||
void DisplayPage::show() { this->parent_->show_page(this); }
|
void DisplayPage::show() { this->parent_->show_page(this); }
|
||||||
|
|
|
@ -68,7 +68,7 @@ extern const Color COLOR_OFF;
|
||||||
/// Turn the pixel ON.
|
/// Turn the pixel ON.
|
||||||
extern const Color COLOR_ON;
|
extern const Color COLOR_ON;
|
||||||
|
|
||||||
enum ImageType { BINARY = 0, GRAYSCALE = 1, RGB = 2 };
|
enum ImageType { IMAGE_TYPE_BINARY = 0, IMAGE_TYPE_GRAYSCALE = 1, IMAGE_TYPE_RGB24 = 2 };
|
||||||
|
|
||||||
enum DisplayRotation {
|
enum DisplayRotation {
|
||||||
DISPLAY_ROTATION_0_DEGREES = 0,
|
DISPLAY_ROTATION_0_DEGREES = 0,
|
||||||
|
@ -262,9 +262,15 @@ class DisplayBuffer {
|
||||||
__attribute__((format(strftime, 5, 0)));
|
__attribute__((format(strftime, 5, 0)));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Draw the `image` with the top-left corner at [x,y] to the screen.
|
/** 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);
|
* @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 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, Image *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
||||||
|
|
||||||
/** Get the text bounds of the given string.
|
/** Get the text bounds of the given string.
|
||||||
*
|
*
|
||||||
|
@ -381,8 +387,7 @@ class Font {
|
||||||
|
|
||||||
class Image {
|
class Image {
|
||||||
public:
|
public:
|
||||||
Image(const uint8_t *data_start, int width, int height);
|
Image(const uint8_t *data_start, int width, int height, ImageType type);
|
||||||
Image(const uint8_t *data_start, int width, int height, int type);
|
|
||||||
bool get_pixel(int x, int y) const;
|
bool get_pixel(int x, int y) const;
|
||||||
Color get_color_pixel(int x, int y) const;
|
Color get_color_pixel(int x, int y) const;
|
||||||
Color get_grayscale_pixel(int x, int y) const;
|
Color get_grayscale_pixel(int x, int y) const;
|
||||||
|
@ -393,7 +398,7 @@ class Image {
|
||||||
protected:
|
protected:
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
ImageType type_{BINARY};
|
ImageType type_;
|
||||||
const uint8_t *data_start_;
|
const uint8_t *data_start_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,20 @@ from esphome import core
|
||||||
from esphome.components import display, font
|
from esphome.components import display, font
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.const import CONF_FILE, CONF_ID, CONF_RESIZE, CONF_TYPE
|
from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE
|
||||||
from esphome.core import CORE, HexInt
|
from esphome.core import CORE, HexInt
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEPENDENCIES = ['display']
|
DEPENDENCIES = ['display']
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
ImageType = {'binary': 0, 'grayscale': 1, 'rgb': 2}
|
|
||||||
|
ImageType = display.display_ns.enum('ImageType')
|
||||||
|
IMAGE_TYPE = {
|
||||||
|
'BINARY': ImageType.IMAGE_TYPE_BINARY,
|
||||||
|
'GRAYSCALE': ImageType.IMAGE_TYPE_GRAYSCALE,
|
||||||
|
'RGB24': ImageType.IMAGE_TYPE_RGB24,
|
||||||
|
}
|
||||||
|
|
||||||
Image_ = display.display_ns.class_('Image')
|
Image_ = display.display_ns.class_('Image')
|
||||||
|
|
||||||
|
@ -21,7 +27,7 @@ IMAGE_SCHEMA = cv.Schema({
|
||||||
cv.Required(CONF_ID): cv.declare_id(Image_),
|
cv.Required(CONF_ID): cv.declare_id(Image_),
|
||||||
cv.Required(CONF_FILE): cv.file_,
|
cv.Required(CONF_FILE): cv.file_,
|
||||||
cv.Optional(CONF_RESIZE): cv.dimensions,
|
cv.Optional(CONF_RESIZE): cv.dimensions,
|
||||||
cv.Optional(CONF_TYPE): cv.string,
|
cv.Optional(CONF_TYPE, default='BINARY'): cv.enum(IMAGE_TYPE, upper=True),
|
||||||
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
|
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -37,12 +43,17 @@ def to_code(config):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise core.EsphomeError(f"Could not load image file {path}: {e}")
|
raise core.EsphomeError(f"Could not load image file {path}: {e}")
|
||||||
|
|
||||||
|
width, height = image.size
|
||||||
|
|
||||||
if CONF_RESIZE in config:
|
if CONF_RESIZE in config:
|
||||||
image.thumbnail(config[CONF_RESIZE])
|
image.thumbnail(config[CONF_RESIZE])
|
||||||
|
|
||||||
if CONF_TYPE in config:
|
|
||||||
if config[CONF_TYPE].startswith('GRAYSCALE'):
|
|
||||||
width, height = image.size
|
width, height = image.size
|
||||||
|
else:
|
||||||
|
if width > 500 or height > 500:
|
||||||
|
_LOGGER.warning("The image you requested is very big. Please consider using"
|
||||||
|
" the resize parameter.")
|
||||||
|
|
||||||
|
if config[CONF_TYPE] == 'GRAYSCALE':
|
||||||
image = image.convert('L', dither=Image.NONE)
|
image = image.convert('L', dither=Image.NONE)
|
||||||
pixels = list(image.getdata())
|
pixels = list(image.getdata())
|
||||||
data = [0 for _ in range(height * width)]
|
data = [0 for _ in range(height * width)]
|
||||||
|
@ -50,11 +61,8 @@ def to_code(config):
|
||||||
for pix in pixels:
|
for pix in pixels:
|
||||||
data[pos] = pix
|
data[pos] = pix
|
||||||
pos += 1
|
pos += 1
|
||||||
rhs = [HexInt(x) for x in data]
|
|
||||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
elif config[CONF_TYPE] == 'RGB24':
|
||||||
cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, ImageType['grayscale'])
|
|
||||||
elif config[CONF_TYPE].startswith('RGB'):
|
|
||||||
width, height = image.size
|
|
||||||
image = image.convert('RGB')
|
image = image.convert('RGB')
|
||||||
pixels = list(image.getdata())
|
pixels = list(image.getdata())
|
||||||
data = [0 for _ in range(height * width * 3)]
|
data = [0 for _ in range(height * width * 3)]
|
||||||
|
@ -66,15 +74,9 @@ def to_code(config):
|
||||||
pos += 1
|
pos += 1
|
||||||
data[pos] = pix[2]
|
data[pos] = pix[2]
|
||||||
pos += 1
|
pos += 1
|
||||||
rhs = [HexInt(x) for x in data]
|
|
||||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
elif config[CONF_TYPE] == 'BINARY':
|
||||||
cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, ImageType['rgb'])
|
|
||||||
else:
|
|
||||||
image = image.convert('1', dither=Image.NONE)
|
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
|
width8 = ((width + 7) // 8) * 8
|
||||||
data = [0 for _ in range(height * width8 // 8)]
|
data = [0 for _ in range(height * width8 // 8)]
|
||||||
for y in range(height):
|
for y in range(height):
|
||||||
|
@ -86,4 +88,5 @@ def to_code(config):
|
||||||
|
|
||||||
rhs = [HexInt(x) for x in data]
|
rhs = [HexInt(x) for x in data]
|
||||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||||
cg.new_Pvariable(config[CONF_ID], prog_arr, width, height)
|
cg.new_Pvariable(config[CONF_ID], prog_arr, width, height,
|
||||||
|
IMAGE_TYPE[config[CONF_TYPE]])
|
||||||
|
|
|
@ -18,14 +18,14 @@ ParallelScript = script_ns.class_('ParallelScript', Script)
|
||||||
|
|
||||||
CONF_SINGLE = 'single'
|
CONF_SINGLE = 'single'
|
||||||
CONF_RESTART = 'restart'
|
CONF_RESTART = 'restart'
|
||||||
CONF_QUEUE = 'queue'
|
CONF_QUEUED = 'queued'
|
||||||
CONF_PARALLEL = 'parallel'
|
CONF_PARALLEL = 'parallel'
|
||||||
CONF_MAX_RUNS = 'max_runs'
|
CONF_MAX_RUNS = 'max_runs'
|
||||||
|
|
||||||
SCRIPT_MODES = {
|
SCRIPT_MODES = {
|
||||||
CONF_SINGLE: SingleScript,
|
CONF_SINGLE: SingleScript,
|
||||||
CONF_RESTART: RestartScript,
|
CONF_RESTART: RestartScript,
|
||||||
CONF_QUEUE: QueueingScript,
|
CONF_QUEUED: QueueingScript,
|
||||||
CONF_PARALLEL: ParallelScript,
|
CONF_PARALLEL: ParallelScript,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ SCRIPT_MODES = {
|
||||||
def check_max_runs(value):
|
def check_max_runs(value):
|
||||||
if CONF_MAX_RUNS not in value:
|
if CONF_MAX_RUNS not in value:
|
||||||
return value
|
return value
|
||||||
if value[CONF_MODE] not in [CONF_QUEUE, CONF_PARALLEL]:
|
if value[CONF_MODE] not in [CONF_QUEUED, CONF_PARALLEL]:
|
||||||
raise cv.Invalid("The option 'max_runs' is only valid in 'queue' and 'parallel' mode.",
|
raise cv.Invalid("The option 'max_runs' is only valid in 'queue' and 'parallel' mode.",
|
||||||
path=[CONF_MAX_RUNS])
|
path=[CONF_MAX_RUNS])
|
||||||
return value
|
return value
|
||||||
|
@ -65,7 +65,7 @@ def to_code(config):
|
||||||
if CONF_MAX_RUNS in conf:
|
if CONF_MAX_RUNS in conf:
|
||||||
cg.add(trigger.set_max_runs(conf[CONF_MAX_RUNS]))
|
cg.add(trigger.set_max_runs(conf[CONF_MAX_RUNS]))
|
||||||
|
|
||||||
if conf[CONF_MODE] == CONF_QUEUE:
|
if conf[CONF_MODE] == CONF_QUEUED:
|
||||||
yield cg.register_component(trigger, conf)
|
yield cg.register_component(trigger, conf)
|
||||||
|
|
||||||
triggers.append((trigger, conf))
|
triggers.append((trigger, conf))
|
||||||
|
|
|
@ -33,7 +33,7 @@ void QueueingScript::execute() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Script '%s' queueing new instance (mode: queue)", this->name_.c_str());
|
ESP_LOGD(TAG, "Script '%s' queueing new instance (mode: queued)", this->name_.c_str());
|
||||||
this->num_runs_++;
|
this->num_runs_++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ def setup_ssd1036(var, config):
|
||||||
reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN])
|
reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN])
|
||||||
cg.add(var.set_reset_pin(reset))
|
cg.add(var.set_reset_pin(reset))
|
||||||
if CONF_BRIGHTNESS in config:
|
if CONF_BRIGHTNESS in config:
|
||||||
cg.add(var.set_brightness(config[CONF_BRIGHTNESS]))
|
cg.add(var.init_brightness(config[CONF_BRIGHTNESS]))
|
||||||
if CONF_EXTERNAL_VCC in config:
|
if CONF_EXTERNAL_VCC in config:
|
||||||
cg.add(var.set_external_vcc(config[CONF_EXTERNAL_VCC]))
|
cg.add(var.set_external_vcc(config[CONF_EXTERNAL_VCC]))
|
||||||
if CONF_LAMBDA in config:
|
if CONF_LAMBDA in config:
|
||||||
|
|
|
@ -5,7 +5,11 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ssd1306_base {
|
namespace ssd1306_base {
|
||||||
|
|
||||||
static const char *TAG = "sd1306";
|
static const char *TAG = "ssd1306";
|
||||||
|
|
||||||
|
static const uint8_t BLACK = 0;
|
||||||
|
static const uint8_t WHITE = 1;
|
||||||
|
static const uint8_t SSD1306_MAX_CONTRAST = 255;
|
||||||
|
|
||||||
static const uint8_t SSD1306_COMMAND_DISPLAY_OFF = 0xAE;
|
static const uint8_t SSD1306_COMMAND_DISPLAY_OFF = 0xAE;
|
||||||
static const uint8_t SSD1306_COMMAND_DISPLAY_ON = 0xAF;
|
static const uint8_t SSD1306_COMMAND_DISPLAY_ON = 0xAF;
|
||||||
|
@ -69,27 +73,6 @@ void SSD1306::setup() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->command(SSD1306_COMMAND_SET_CONTRAST);
|
|
||||||
switch (this->model_) {
|
|
||||||
case SSD1306_MODEL_128_32:
|
|
||||||
case SH1106_MODEL_128_32:
|
|
||||||
this->command(0x8F);
|
|
||||||
break;
|
|
||||||
case SSD1306_MODEL_128_64:
|
|
||||||
case SH1106_MODEL_128_64:
|
|
||||||
case SSD1306_MODEL_64_48:
|
|
||||||
case SH1106_MODEL_64_48:
|
|
||||||
this->command(int(255 * (this->brightness_)));
|
|
||||||
break;
|
|
||||||
case SSD1306_MODEL_96_16:
|
|
||||||
case SH1106_MODEL_96_16:
|
|
||||||
if (this->external_vcc_)
|
|
||||||
this->command(0x10);
|
|
||||||
else
|
|
||||||
this->command(0xAF);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->command(SSD1306_COMMAND_SET_PRE_CHARGE);
|
this->command(SSD1306_COMMAND_SET_PRE_CHARGE);
|
||||||
if (this->external_vcc_)
|
if (this->external_vcc_)
|
||||||
this->command(0x22);
|
this->command(0x22);
|
||||||
|
@ -104,7 +87,12 @@ void SSD1306::setup() {
|
||||||
|
|
||||||
this->command(SSD1306_COMMAND_DEACTIVATE_SCROLL);
|
this->command(SSD1306_COMMAND_DEACTIVATE_SCROLL);
|
||||||
|
|
||||||
this->command(SSD1306_COMMAND_DISPLAY_ON);
|
set_brightness(this->brightness_);
|
||||||
|
|
||||||
|
this->fill(BLACK); // clear display - ensures we do not see garbage at power-on
|
||||||
|
this->display(); // ...write buffer, which actually clears the display's memory
|
||||||
|
|
||||||
|
this->turn_on();
|
||||||
}
|
}
|
||||||
void SSD1306::display() {
|
void SSD1306::display() {
|
||||||
if (this->is_sh1106_()) {
|
if (this->is_sh1106_()) {
|
||||||
|
@ -140,6 +128,22 @@ void SSD1306::update() {
|
||||||
this->do_update_();
|
this->do_update_();
|
||||||
this->display();
|
this->display();
|
||||||
}
|
}
|
||||||
|
void SSD1306::set_brightness(float brightness) {
|
||||||
|
// validation
|
||||||
|
this->brightness_ = clamp(brightness, 0, 1);
|
||||||
|
// now write the new brightness level to the display
|
||||||
|
this->command(SSD1306_COMMAND_SET_CONTRAST);
|
||||||
|
this->command(int(SSD1306_MAX_CONTRAST * (this->brightness_)));
|
||||||
|
}
|
||||||
|
bool SSD1306::is_on() { return this->is_on_; }
|
||||||
|
void SSD1306::turn_on() {
|
||||||
|
this->command(SSD1306_COMMAND_DISPLAY_ON);
|
||||||
|
this->is_on_ = true;
|
||||||
|
}
|
||||||
|
void SSD1306::turn_off() {
|
||||||
|
this->command(SSD1306_COMMAND_DISPLAY_OFF);
|
||||||
|
this->is_on_ = false;
|
||||||
|
}
|
||||||
int SSD1306::get_height_internal() {
|
int SSD1306::get_height_internal() {
|
||||||
switch (this->model_) {
|
switch (this->model_) {
|
||||||
case SSD1306_MODEL_128_32:
|
case SSD1306_MODEL_128_32:
|
||||||
|
@ -178,7 +182,6 @@ int SSD1306::get_width_internal() {
|
||||||
size_t SSD1306::get_buffer_length_() {
|
size_t SSD1306::get_buffer_length_() {
|
||||||
return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u;
|
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, Color 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)
|
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -29,8 +29,11 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer {
|
||||||
void set_model(SSD1306Model model) { this->model_ = model; }
|
void set_model(SSD1306Model model) { this->model_ = model; }
|
||||||
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
|
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
|
||||||
void set_external_vcc(bool external_vcc) { this->external_vcc_ = external_vcc; }
|
void set_external_vcc(bool external_vcc) { this->external_vcc_ = external_vcc; }
|
||||||
void set_brightness(float brightness) { this->brightness_ = brightness; }
|
void init_brightness(float brightness) { this->brightness_ = brightness; }
|
||||||
|
void set_brightness(float brightness);
|
||||||
|
bool is_on();
|
||||||
|
void turn_on();
|
||||||
|
void turn_off();
|
||||||
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
||||||
void fill(Color color) override;
|
void fill(Color color) override;
|
||||||
|
|
||||||
|
@ -51,6 +54,7 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer {
|
||||||
SSD1306Model model_{SSD1306_MODEL_128_64};
|
SSD1306Model model_{SSD1306_MODEL_128_64};
|
||||||
GPIOPin *reset_pin_{nullptr};
|
GPIOPin *reset_pin_{nullptr};
|
||||||
bool external_vcc_{false};
|
bool external_vcc_{false};
|
||||||
|
bool is_on_{false};
|
||||||
float brightness_{1.0};
|
float brightness_{1.0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
#include "sys/time.h"
|
#include "sys/time.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "errno.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace time {
|
namespace time {
|
||||||
|
@ -20,8 +21,18 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) {
|
||||||
struct timeval timev {
|
struct timeval timev {
|
||||||
.tv_sec = static_cast<time_t>(epoch), .tv_usec = 0,
|
.tv_sec = static_cast<time_t>(epoch), .tv_usec = 0,
|
||||||
};
|
};
|
||||||
|
ESP_LOGVV(TAG, "Got epoch %u", epoch);
|
||||||
timezone tz = {0, 0};
|
timezone tz = {0, 0};
|
||||||
settimeofday(&timev, &tz);
|
int ret = settimeofday(&timev, &tz);
|
||||||
|
if (ret == EINVAL) {
|
||||||
|
// Some ESP8266 frameworks abort when timezone parameter is not NULL
|
||||||
|
// while ESP32 expects it not to be NULL
|
||||||
|
ret = settimeofday(&timev, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
ESP_LOGW(TAG, "setimeofday() failed with code %d", ret);
|
||||||
|
}
|
||||||
|
|
||||||
auto time = this->now();
|
auto time = this->now();
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
|
@ -232,7 +232,7 @@ uint8_t TM1637Display::print(uint8_t start_pos, const char* str) {
|
||||||
uint8_t pos = start_pos;
|
uint8_t pos = start_pos;
|
||||||
for (; *str != '\0'; str++) {
|
for (; *str != '\0'; str++) {
|
||||||
uint8_t data = TM1637_UNKNOWN_CHAR;
|
uint8_t data = TM1637_UNKNOWN_CHAR;
|
||||||
if (*str >= ' ' && *str <= '}')
|
if (*str >= ' ' && *str <= '~')
|
||||||
data = pgm_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']);
|
data = pgm_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']);
|
||||||
|
|
||||||
if (data == TM1637_UNKNOWN_CHAR) {
|
if (data == TM1637_UNKNOWN_CHAR) {
|
||||||
|
|
|
@ -22,8 +22,8 @@ void Tuya::loop() {
|
||||||
void Tuya::dump_config() {
|
void Tuya::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Tuya:");
|
ESP_LOGCONFIG(TAG, "Tuya:");
|
||||||
if (this->init_state_ != TuyaInitState::INIT_DONE) {
|
if (this->init_state_ != TuyaInitState::INIT_DONE) {
|
||||||
ESP_LOGCONFIG(TAG, " Configuration will be reported when setup is complete. Current init_state: %u", // NOLINT
|
ESP_LOGCONFIG(TAG, " Configuration will be reported when setup is complete. Current init_state: %u",
|
||||||
this->init_state_);
|
static_cast<uint8_t>(this->init_state_));
|
||||||
ESP_LOGCONFIG(TAG, " If no further output is received, confirm that this is a supported Tuya device.");
|
ESP_LOGCONFIG(TAG, " If no further output is received, confirm that this is a supported Tuya device.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,11 +41,11 @@ void WhirlpoolClimate::transmit_state() {
|
||||||
remote_state[18] = 0x08;
|
remote_state[18] = 0x08;
|
||||||
|
|
||||||
auto powered_on = this->mode != climate::CLIMATE_MODE_OFF;
|
auto powered_on = this->mode != climate::CLIMATE_MODE_OFF;
|
||||||
if (powered_on != this->powered_on_assumed_) {
|
if (powered_on != this->powered_on_assumed) {
|
||||||
// Set power toggle command
|
// Set power toggle command
|
||||||
remote_state[2] = 4;
|
remote_state[2] = 4;
|
||||||
remote_state[15] = 1;
|
remote_state[15] = 1;
|
||||||
this->powered_on_assumed_ = powered_on;
|
this->powered_on_assumed = powered_on;
|
||||||
}
|
}
|
||||||
switch (this->mode) {
|
switch (this->mode) {
|
||||||
case climate::CLIMATE_MODE_AUTO:
|
case climate::CLIMATE_MODE_AUTO:
|
||||||
|
@ -215,14 +215,14 @@ bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||||
|
|
||||||
if (powered_on) {
|
if (powered_on) {
|
||||||
this->mode = climate::CLIMATE_MODE_OFF;
|
this->mode = climate::CLIMATE_MODE_OFF;
|
||||||
this->powered_on_assumed_ = false;
|
this->powered_on_assumed = false;
|
||||||
} else {
|
} else {
|
||||||
this->powered_on_assumed_ = true;
|
this->powered_on_assumed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set received mode
|
// Set received mode
|
||||||
if (powered_on_assumed_) {
|
if (powered_on_assumed) {
|
||||||
auto mode = remote_state[3] & 0x7;
|
auto mode = remote_state[3] & 0x7;
|
||||||
ESP_LOGV(TAG, "Mode: %02X", mode);
|
ESP_LOGV(TAG, "Mode: %02X", mode);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ class WhirlpoolClimate : public climate_ir::ClimateIR {
|
||||||
void setup() override {
|
void setup() override {
|
||||||
climate_ir::ClimateIR::setup();
|
climate_ir::ClimateIR::setup();
|
||||||
|
|
||||||
this->powered_on_assumed_ = this->mode != climate::CLIMATE_MODE_OFF;
|
this->powered_on_assumed = this->mode != climate::CLIMATE_MODE_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Override control to change settings of the climate device.
|
/// Override control to change settings of the climate device.
|
||||||
|
@ -39,15 +39,15 @@ class WhirlpoolClimate : public climate_ir::ClimateIR {
|
||||||
|
|
||||||
void set_model(Model model) { this->model_ = model; }
|
void set_model(Model model) { this->model_ = model; }
|
||||||
|
|
||||||
|
// used to track when to send the power toggle command
|
||||||
|
bool powered_on_assumed;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Transmit via IR the state of this climate controller.
|
/// Transmit via IR the state of this climate controller.
|
||||||
void transmit_state() override;
|
void transmit_state() override;
|
||||||
/// Handle received IR Buffer
|
/// Handle received IR Buffer
|
||||||
bool on_receive(remote_base::RemoteReceiveData data) override;
|
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||||
|
|
||||||
// used to track when to send the power toggle command
|
|
||||||
bool powered_on_assumed_;
|
|
||||||
|
|
||||||
bool send_swing_cmd_{false};
|
bool send_swing_cmd_{false};
|
||||||
Model model_;
|
Model model_;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
MAJOR_VERSION = 1
|
MAJOR_VERSION = 1
|
||||||
MINOR_VERSION = 15
|
MINOR_VERSION = 15
|
||||||
PATCH_VERSION = '0b3'
|
PATCH_VERSION = '0b4'
|
||||||
__short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}'
|
__short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}'
|
||||||
__version__ = f'{__short_version__}.{PATCH_VERSION}'
|
__version__ = f'{__short_version__}.{PATCH_VERSION}'
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ ARDUINO_VERSION_ESP32 = {
|
||||||
# See also https://github.com/platformio/platform-espressif8266/releases
|
# See also https://github.com/platformio/platform-espressif8266/releases
|
||||||
ARDUINO_VERSION_ESP8266 = {
|
ARDUINO_VERSION_ESP8266 = {
|
||||||
'dev': 'https://github.com/platformio/platform-espressif8266.git',
|
'dev': 'https://github.com/platformio/platform-espressif8266.git',
|
||||||
|
'2.7.4': 'espressif8266@2.6.2',
|
||||||
|
'2.7.3': 'espressif8266@2.6.1',
|
||||||
'2.7.2': 'espressif8266@2.6.0',
|
'2.7.2': 'espressif8266@2.6.0',
|
||||||
'2.7.1': 'espressif8266@2.5.3',
|
'2.7.1': 'espressif8266@2.5.3',
|
||||||
'2.7.0': 'espressif8266@2.5.0',
|
'2.7.0': 'espressif8266@2.5.0',
|
||||||
|
|
|
@ -44,7 +44,7 @@ validate_platform = cv.one_of(*ESP_PLATFORMS, upper=True)
|
||||||
|
|
||||||
PLATFORMIO_ESP8266_LUT = {
|
PLATFORMIO_ESP8266_LUT = {
|
||||||
**ARDUINO_VERSION_ESP8266,
|
**ARDUINO_VERSION_ESP8266,
|
||||||
'RECOMMENDED': ARDUINO_VERSION_ESP8266['2.7.2'],
|
'RECOMMENDED': ARDUINO_VERSION_ESP8266['2.7.4'],
|
||||||
'LATEST': 'espressif8266',
|
'LATEST': 'espressif8266',
|
||||||
'DEV': ARDUINO_VERSION_ESP8266['dev'],
|
'DEV': ARDUINO_VERSION_ESP8266['dev'],
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ lib_deps =
|
||||||
ESPAsyncWebServer-esphome@1.2.7
|
ESPAsyncWebServer-esphome@1.2.7
|
||||||
FastLED@3.3.2
|
FastLED@3.3.2
|
||||||
NeoPixelBus-esphome@2.5.7
|
NeoPixelBus-esphome@2.5.7
|
||||||
ESPAsyncTCP-esphome@1.2.2
|
ESPAsyncTCP-esphome@1.2.3
|
||||||
1655@1.0.2 ; TinyGPSPlus (has name conflict)
|
1655@1.0.2 ; TinyGPSPlus (has name conflict)
|
||||||
6865@1.0.0 ; TM1651 Battery Display
|
6865@1.0.0 ; TM1651 Battery Display
|
||||||
6306@1.0.3 ; HM3301
|
6306@1.0.3 ; HM3301
|
||||||
|
|
59
script/bump-docker-base-version.py
Executable file
59
script/bump-docker-base-version.py
Executable file
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def sub(path, pattern, repl, expected_count=1):
|
||||||
|
with open(path) as fh:
|
||||||
|
content = fh.read()
|
||||||
|
content, count = re.subn(pattern, repl, content, flags=re.MULTILINE)
|
||||||
|
if expected_count is not None:
|
||||||
|
assert count == expected_count, f"Pattern {pattern} replacement failed!"
|
||||||
|
with open(path, "wt") as fh:
|
||||||
|
fh.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
def write_version(version: str):
|
||||||
|
for p in [
|
||||||
|
".github/workflows/ci-docker.yml",
|
||||||
|
".github/workflows/release-dev.yml",
|
||||||
|
".github/workflows/release.yml"
|
||||||
|
]:
|
||||||
|
sub(
|
||||||
|
p,
|
||||||
|
r'base_version=".*"',
|
||||||
|
f'base_version="{version}"'
|
||||||
|
)
|
||||||
|
|
||||||
|
sub(
|
||||||
|
"docker/Dockerfile",
|
||||||
|
r"ARG BUILD_FROM=esphome/esphome-base-amd64:.*",
|
||||||
|
f"ARG BUILD_FROM=esphome/esphome-base-amd64:{version}"
|
||||||
|
)
|
||||||
|
sub(
|
||||||
|
"docker/Dockerfile.dev",
|
||||||
|
r"FROM esphome/esphome-base-amd64:.*",
|
||||||
|
f"FROM esphome/esphome-base-amd64:{version}"
|
||||||
|
)
|
||||||
|
sub(
|
||||||
|
"docker/Dockerfile.lint",
|
||||||
|
r"FROM esphome/esphome-lint-base:.*",
|
||||||
|
f"FROM esphome/esphome-lint-base:{version}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('new_version', type=str)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
version = args.new_version
|
||||||
|
print(f"Bumping to {version}")
|
||||||
|
write_version(version)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main() or 0)
|
|
@ -292,6 +292,21 @@ text_sensor:
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: my_script
|
- id: my_script
|
||||||
|
mode: single
|
||||||
|
then:
|
||||||
|
- lambda: 'ESP_LOGD("main", "Hello World!");'
|
||||||
|
- id: my_script_queued
|
||||||
|
mode: queued
|
||||||
|
max_runs: 2
|
||||||
|
then:
|
||||||
|
- lambda: 'ESP_LOGD("main", "Hello World!");'
|
||||||
|
- id: my_script_parallel
|
||||||
|
mode: parallel
|
||||||
|
max_runs: 2
|
||||||
|
then:
|
||||||
|
- lambda: 'ESP_LOGD("main", "Hello World!");'
|
||||||
|
- id: my_script_restart
|
||||||
|
mode: restart
|
||||||
then:
|
then:
|
||||||
- lambda: 'ESP_LOGD("main", "Hello World!");'
|
- lambda: 'ESP_LOGD("main", "Hello World!");'
|
||||||
|
|
||||||
|
@ -316,5 +331,3 @@ interval:
|
||||||
- logger.log: "Interval Run"
|
- logger.log: "Interval Run"
|
||||||
|
|
||||||
display:
|
display:
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue