mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
parent
cbc03aae80
commit
81f6750211
16 changed files with 148 additions and 76 deletions
|
@ -79,6 +79,50 @@ Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const {
|
|||
return color_off;
|
||||
}
|
||||
}
|
||||
#ifdef USE_LVGL
|
||||
lv_img_dsc_t *Image::get_lv_img_dsc() {
|
||||
// lazily construct lvgl image_dsc.
|
||||
if (this->dsc_.data != this->data_start_) {
|
||||
this->dsc_.data = this->data_start_;
|
||||
this->dsc_.header.always_zero = 0;
|
||||
this->dsc_.header.reserved = 0;
|
||||
this->dsc_.header.w = this->width_;
|
||||
this->dsc_.header.h = this->height_;
|
||||
this->dsc_.data_size = image_type_to_width_stride(this->dsc_.header.w * this->dsc_.header.h, this->get_type());
|
||||
switch (this->get_type()) {
|
||||
case IMAGE_TYPE_BINARY:
|
||||
this->dsc_.header.cf = LV_IMG_CF_ALPHA_1BIT;
|
||||
break;
|
||||
|
||||
case IMAGE_TYPE_GRAYSCALE:
|
||||
this->dsc_.header.cf = LV_IMG_CF_ALPHA_8BIT;
|
||||
break;
|
||||
|
||||
case IMAGE_TYPE_RGB24:
|
||||
this->dsc_.header.cf = LV_IMG_CF_RGB888;
|
||||
break;
|
||||
|
||||
case IMAGE_TYPE_RGB565:
|
||||
#if LV_COLOR_DEPTH == 16
|
||||
this->dsc_.header.cf = this->has_transparency() ? LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED : LV_IMG_CF_TRUE_COLOR;
|
||||
#else
|
||||
this->dsc_.header.cf = LV_IMG_CF_RGB565;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_RGBA:
|
||||
#if LV_COLOR_DEPTH == 32
|
||||
this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR;
|
||||
#else
|
||||
this->dsc_.header.cf = LV_IMG_CF_RGBA8888;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
return &this->dsc_;
|
||||
}
|
||||
#endif // USE_LVGL
|
||||
|
||||
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;
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
#pragma once
|
||||
#include "esphome/core/color.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
#include "esphome/components/display/display.h"
|
||||
|
||||
#ifdef USE_LVGL
|
||||
// required for clang-tidy
|
||||
#ifndef LV_CONF_H
|
||||
#define LV_CONF_SKIP 1 // NOLINT
|
||||
#endif // LV_CONF_H
|
||||
|
||||
#include <lvgl.h>
|
||||
#endif // USE_LVGL
|
||||
|
||||
namespace esphome {
|
||||
namespace image {
|
||||
|
@ -37,7 +46,7 @@ class Image : public display::BaseImage {
|
|||
Color get_pixel(int x, int y, Color color_on = display::COLOR_ON, Color color_off = display::COLOR_OFF) const;
|
||||
int get_width() const override;
|
||||
int get_height() const override;
|
||||
const uint8_t *get_data_start() { return this->data_start_; }
|
||||
const uint8_t *get_data_start() const { return this->data_start_; }
|
||||
ImageType get_type() const;
|
||||
|
||||
void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override;
|
||||
|
@ -45,6 +54,9 @@ class Image : public display::BaseImage {
|
|||
void set_transparency(bool transparent) { transparent_ = transparent; }
|
||||
bool has_transparency() const { return transparent_; }
|
||||
|
||||
#ifdef USE_LVGL
|
||||
lv_img_dsc_t *get_lv_img_dsc();
|
||||
#endif
|
||||
protected:
|
||||
bool get_binary_pixel_(int x, int y) const;
|
||||
Color get_rgb24_pixel_(int x, int y) const;
|
||||
|
@ -57,6 +69,9 @@ class Image : public display::BaseImage {
|
|||
ImageType type_;
|
||||
const uint8_t *data_start_;
|
||||
bool transparent_;
|
||||
#ifdef USE_LVGL
|
||||
lv_img_dsc_t dsc_{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
|
|
@ -53,7 +53,7 @@ from .types import (
|
|||
lv_style_t,
|
||||
lvgl_ns,
|
||||
)
|
||||
from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties
|
||||
from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties, styles_used
|
||||
from .widgets.animimg import animimg_spec
|
||||
from .widgets.arc import arc_spec
|
||||
from .widgets.button import button_spec
|
||||
|
@ -280,6 +280,8 @@ async def to_code(config):
|
|||
|
||||
for comp in helpers.lvgl_components_required:
|
||||
CORE.add_define(f"USE_LVGL_{comp.upper()}")
|
||||
if "transform_angle" in styles_used:
|
||||
add_define("LV_COLOR_SCREEN_TRANSP", "1")
|
||||
for use in helpers.lv_uses:
|
||||
add_define(f"LV_USE_{use.upper()}")
|
||||
lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME)
|
||||
|
|
|
@ -452,6 +452,7 @@ CONF_OFFSET_Y = "offset_y"
|
|||
CONF_ONE_CHECKED = "one_checked"
|
||||
CONF_ONE_LINE = "one_line"
|
||||
CONF_ON_SELECT = "on_select"
|
||||
CONF_OPA = "opa"
|
||||
CONF_NEXT = "next"
|
||||
CONF_PAD_ROW = "pad_row"
|
||||
CONF_PAD_COLUMN = "pad_column"
|
||||
|
|
|
@ -31,7 +31,6 @@ from .defines import (
|
|||
literal,
|
||||
)
|
||||
from .helpers import esphome_fonts_used, lv_fonts_used, requires_component
|
||||
from .lvcode import lv_expr
|
||||
from .types import lv_font_t, lv_gradient_t, lv_img_t
|
||||
|
||||
opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER")
|
||||
|
@ -330,7 +329,7 @@ def image_validator(value):
|
|||
lv_image = LValidator(
|
||||
image_validator,
|
||||
lv_img_t,
|
||||
retmapper=lambda x: lv_expr.img_from(MockObj(x)),
|
||||
retmapper=lambda x: MockObj(x, "->").get_lv_img_dsc(),
|
||||
requires="image",
|
||||
)
|
||||
lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal)
|
||||
|
|
|
@ -356,49 +356,6 @@ bool lv_is_pre_initialise() {
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef USE_LVGL_IMAGE
|
||||
lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) {
|
||||
if (img_dsc == nullptr)
|
||||
img_dsc = new lv_img_dsc_t(); // NOLINT
|
||||
img_dsc->header.always_zero = 0;
|
||||
img_dsc->header.reserved = 0;
|
||||
img_dsc->header.w = src->get_width();
|
||||
img_dsc->header.h = src->get_height();
|
||||
img_dsc->data = src->get_data_start();
|
||||
img_dsc->data_size = image_type_to_width_stride(img_dsc->header.w * img_dsc->header.h, src->get_type());
|
||||
switch (src->get_type()) {
|
||||
case image::IMAGE_TYPE_BINARY:
|
||||
img_dsc->header.cf = LV_IMG_CF_ALPHA_1BIT;
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_GRAYSCALE:
|
||||
img_dsc->header.cf = LV_IMG_CF_ALPHA_8BIT;
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_RGB24:
|
||||
img_dsc->header.cf = LV_IMG_CF_RGB888;
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_RGB565:
|
||||
#if LV_COLOR_DEPTH == 16
|
||||
img_dsc->header.cf = src->has_transparency() ? LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED : LV_IMG_CF_TRUE_COLOR;
|
||||
#else
|
||||
img_dsc->header.cf = LV_IMG_CF_RGB565;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_RGBA:
|
||||
#if LV_COLOR_DEPTH == 32
|
||||
img_dsc->header.cf = LV_IMG_CF_TRUE_COLOR;
|
||||
#else
|
||||
img_dsc->header.cf = LV_IMG_CF_RGBA8888;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return img_dsc;
|
||||
}
|
||||
#endif // USE_LVGL_IMAGE
|
||||
|
||||
#ifdef USE_LVGL_ANIMIMG
|
||||
void lv_animimg_stop(lv_obj_t *obj) {
|
||||
auto *animg = (lv_animimg_t *) obj;
|
||||
|
|
|
@ -102,10 +102,6 @@ class FontEngine {
|
|||
lv_font_t lv_font_{};
|
||||
};
|
||||
#endif // USE_LVGL_FONT
|
||||
#ifdef USE_LVGL_IMAGE
|
||||
lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc = nullptr);
|
||||
#endif // USE_LVGL_IMAGE
|
||||
|
||||
#ifdef USE_LVGL_ANIMIMG
|
||||
void lv_animimg_stop(lv_obj_t *obj);
|
||||
#endif // USE_LVGL_ANIMIMG
|
||||
|
|
|
@ -52,6 +52,7 @@ from ..types import LV_STATE, LvType, WidgetType, lv_coord_t, lv_obj_t, lv_obj_t
|
|||
EVENT_LAMB = "event_lamb__"
|
||||
|
||||
theme_widget_map = {}
|
||||
styles_used = set()
|
||||
|
||||
|
||||
class LvScrActType(WidgetType):
|
||||
|
@ -158,6 +159,7 @@ class Widget:
|
|||
def set_style(self, prop, value, state):
|
||||
if value is None:
|
||||
return
|
||||
styles_used.add(prop)
|
||||
lv.call(f"obj_set_style_{prop}", self.obj, value, state)
|
||||
|
||||
def __type_base(self):
|
||||
|
|
|
@ -2,13 +2,12 @@ from esphome import automation
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_DURATION, CONF_ID
|
||||
from esphome.cpp_generator import MockObj
|
||||
|
||||
from ..automation import action_to_code
|
||||
from ..defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC
|
||||
from ..helpers import lvgl_components_required
|
||||
from ..lv_validation import lv_image, lv_milliseconds
|
||||
from ..lvcode import lv, lv_expr
|
||||
from ..lvcode import lv
|
||||
from ..types import LvType, ObjUpdateAction, void_ptr
|
||||
from . import Widget, WidgetType, get_widgets
|
||||
from .img import CONF_IMAGE
|
||||
|
@ -63,7 +62,7 @@ class AnimimgType(WidgetType):
|
|||
if CONF_SRC in config:
|
||||
for x in config[CONF_SRC]:
|
||||
await cg.get_variable(x)
|
||||
srcs = [lv_expr.img_from(MockObj(x)) for x in config[CONF_SRC]]
|
||||
srcs = [await lv_image.process(x) for x in config[CONF_SRC]]
|
||||
src_id = cg.static_const_array(config[CONF_SRC_LIST_ID], srcs)
|
||||
count = len(config[CONF_SRC])
|
||||
lv.animimg_set_src(w.obj, src_id, count)
|
||||
|
@ -73,7 +72,7 @@ class AnimimgType(WidgetType):
|
|||
lv.animimg_start(w.obj)
|
||||
|
||||
def get_uses(self):
|
||||
return CONF_IMAGE, CONF_LABEL
|
||||
return "img", CONF_IMAGE, CONF_LABEL
|
||||
|
||||
|
||||
animimg_spec = AnimimgType()
|
||||
|
|
|
@ -20,6 +20,7 @@ from ..defines import (
|
|||
CONF_END_VALUE,
|
||||
CONF_INDICATOR,
|
||||
CONF_MAIN,
|
||||
CONF_OPA,
|
||||
CONF_PIVOT_X,
|
||||
CONF_PIVOT_Y,
|
||||
CONF_SRC,
|
||||
|
@ -35,10 +36,11 @@ from ..lv_validation import (
|
|||
lv_color,
|
||||
lv_float,
|
||||
lv_image,
|
||||
opacity,
|
||||
requires_component,
|
||||
size,
|
||||
)
|
||||
from ..lvcode import LocalVariable, lv, lv_assign, lv_expr
|
||||
from ..lvcode import LocalVariable, lv, lv_assign, lv_expr, lv_obj
|
||||
from ..types import LvType, ObjUpdateAction
|
||||
from . import Widget, WidgetType, get_widgets
|
||||
from .arc import CONF_ARC
|
||||
|
@ -76,6 +78,7 @@ INDICATOR_LINE_SCHEMA = cv.Schema(
|
|||
cv.Optional(CONF_COLOR, default=0): lv_color,
|
||||
cv.Optional(CONF_R_MOD, default=0): size,
|
||||
cv.Optional(CONF_VALUE): lv_float,
|
||||
cv.Optional(CONF_OPA): opacity,
|
||||
}
|
||||
)
|
||||
INDICATOR_IMG_SCHEMA = cv.Schema(
|
||||
|
@ -84,6 +87,7 @@ INDICATOR_IMG_SCHEMA = cv.Schema(
|
|||
cv.Required(CONF_PIVOT_X): pixels,
|
||||
cv.Required(CONF_PIVOT_Y): pixels,
|
||||
cv.Optional(CONF_VALUE): lv_float,
|
||||
cv.Optional(CONF_OPA): opacity,
|
||||
}
|
||||
)
|
||||
INDICATOR_ARC_SCHEMA = cv.Schema(
|
||||
|
@ -94,6 +98,7 @@ INDICATOR_ARC_SCHEMA = cv.Schema(
|
|||
cv.Exclusive(CONF_VALUE, CONF_VALUE): lv_float,
|
||||
cv.Exclusive(CONF_START_VALUE, CONF_VALUE): lv_float,
|
||||
cv.Optional(CONF_END_VALUE): lv_float,
|
||||
cv.Optional(CONF_OPA): opacity,
|
||||
}
|
||||
)
|
||||
INDICATOR_TICKS_SCHEMA = cv.Schema(
|
||||
|
@ -218,9 +223,7 @@ class MeterType(WidgetType):
|
|||
for indicator in scale_conf.get(CONF_INDICATORS, ()):
|
||||
(t, v) = next(iter(indicator.items()))
|
||||
iid = v[CONF_ID]
|
||||
ivar = cg.new_variable(
|
||||
iid, cg.nullptr, type_=lv_meter_indicator_t_ptr
|
||||
)
|
||||
ivar = cg.Pvariable(iid, cg.nullptr, type_=lv_meter_indicator_t)
|
||||
# Enable getting the meter to which this belongs.
|
||||
wid = Widget.create(iid, var, obj_spec, v)
|
||||
wid.obj = ivar
|
||||
|
@ -268,9 +271,7 @@ class MeterType(WidgetType):
|
|||
v[CONF_PIVOT_Y],
|
||||
),
|
||||
)
|
||||
start_value = await get_start_value(v)
|
||||
end_value = await get_end_value(v)
|
||||
set_indicator_values(var, ivar, start_value, end_value)
|
||||
await set_indicator_values(var, ivar, v)
|
||||
|
||||
|
||||
meter_spec = MeterType()
|
||||
|
@ -285,21 +286,22 @@ meter_spec = MeterType()
|
|||
cv.Exclusive(CONF_VALUE, CONF_VALUE): lv_float,
|
||||
cv.Exclusive(CONF_START_VALUE, CONF_VALUE): lv_float,
|
||||
cv.Optional(CONF_END_VALUE): lv_float,
|
||||
cv.Optional(CONF_OPA): opacity,
|
||||
}
|
||||
),
|
||||
)
|
||||
async def indicator_update_to_code(config, action_id, template_arg, args):
|
||||
widget = await get_widgets(config)
|
||||
start_value = await get_start_value(config)
|
||||
end_value = await get_end_value(config)
|
||||
|
||||
async def set_value(w: Widget):
|
||||
set_indicator_values(w.var, w.obj, start_value, end_value)
|
||||
await set_indicator_values(w.var, w.obj, config)
|
||||
|
||||
return await action_to_code(widget, set_value, action_id, template_arg, args)
|
||||
|
||||
|
||||
def set_indicator_values(meter, indicator, start_value, end_value):
|
||||
async def set_indicator_values(meter, indicator, config):
|
||||
start_value = await get_start_value(config)
|
||||
end_value = await get_end_value(config)
|
||||
if start_value is not None:
|
||||
if end_value is None:
|
||||
lv.meter_set_indicator_value(meter, indicator, start_value)
|
||||
|
@ -307,3 +309,6 @@ def set_indicator_values(meter, indicator, start_value, end_value):
|
|||
lv.meter_set_indicator_start_value(meter, indicator, start_value)
|
||||
if end_value is not None:
|
||||
lv.meter_set_indicator_end_value(meter, indicator, end_value)
|
||||
if opa := config.get(CONF_OPA):
|
||||
lv_assign(indicator.opa, await opacity.process(opa))
|
||||
lv_obj.invalidate(meter)
|
||||
|
|
|
@ -2,13 +2,13 @@ spi:
|
|||
- id: spi_main_lcd
|
||||
clk_pin: 16
|
||||
mosi_pin: 17
|
||||
miso_pin: 15
|
||||
miso_pin: 32
|
||||
|
||||
display:
|
||||
- platform: ili9xxx
|
||||
id: main_lcd
|
||||
model: ili9342
|
||||
cs_pin: 12
|
||||
cs_pin: 14
|
||||
dc_pin: 13
|
||||
reset_pin: 21
|
||||
invert_colors: true
|
||||
|
|
|
@ -8,8 +8,8 @@ display:
|
|||
- platform: ili9xxx
|
||||
id: main_lcd
|
||||
model: ili9342
|
||||
cs_pin: 8
|
||||
dc_pin: 9
|
||||
cs_pin: 3
|
||||
dc_pin: 11
|
||||
reset_pin: 10
|
||||
invert_colors: true
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ display:
|
|||
- platform: ili9xxx
|
||||
id: main_lcd
|
||||
model: ili9342
|
||||
cs_pin: 8
|
||||
dc_pin: 9
|
||||
cs_pin: 3
|
||||
dc_pin: 11
|
||||
reset_pin: 10
|
||||
invert_colors: true
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@ spi:
|
|||
- id: spi_main_lcd
|
||||
clk_pin: 16
|
||||
mosi_pin: 17
|
||||
miso_pin: 15
|
||||
miso_pin: 18
|
||||
|
||||
display:
|
||||
- platform: ili9xxx
|
||||
id: main_lcd
|
||||
model: ili9342
|
||||
cs_pin: 12
|
||||
cs_pin: 19
|
||||
dc_pin: 13
|
||||
reset_pin: 21
|
||||
invert_colors: true
|
||||
|
|
|
@ -608,6 +608,58 @@ lvgl:
|
|||
id: tabview_id
|
||||
index: 0
|
||||
animated: true
|
||||
- meter:
|
||||
height: 200px
|
||||
width: 200px
|
||||
indicator:
|
||||
bg_color: 0xFF
|
||||
radius: 0
|
||||
bg_opa: TRANSP
|
||||
text_color: 0xFFFFFF
|
||||
scales:
|
||||
- ticks:
|
||||
width: 1
|
||||
count: 61
|
||||
length: 20
|
||||
color: 0xFFFFFF
|
||||
range_from: 0
|
||||
range_to: 60
|
||||
angle_range: 360
|
||||
rotation: 270
|
||||
indicators:
|
||||
- line:
|
||||
opa: 50%
|
||||
id: minute_hand
|
||||
color: 0xFF0000
|
||||
r_mod: -1
|
||||
width: 3
|
||||
-
|
||||
angle_range: 330
|
||||
rotation: 300
|
||||
range_from: 1
|
||||
range_to: 12
|
||||
ticks:
|
||||
width: 1
|
||||
count: 12
|
||||
length: 1
|
||||
major:
|
||||
stride: 1
|
||||
width: 4
|
||||
length: 8
|
||||
color: 0xC0C0C0
|
||||
label_gap: 6
|
||||
- angle_range: 360
|
||||
rotation: 270
|
||||
range_from: 0
|
||||
range_to: 720
|
||||
indicators:
|
||||
- line:
|
||||
id: hour_hand
|
||||
value: 180
|
||||
width: 4
|
||||
color: 0xA0A0A0
|
||||
r_mod: -20
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: space16
|
||||
|
|
|
@ -10,7 +10,7 @@ sensor:
|
|||
- platform: rotary_encoder
|
||||
name: "Rotary Encoder"
|
||||
id: encoder
|
||||
pin_a: 2
|
||||
pin_a: 3
|
||||
pin_b: 1
|
||||
internal: true
|
||||
|
||||
|
|
Loading…
Reference in a new issue