mirror of
https://github.com/esphome/esphome.git
synced 2025-09-20 20:22:27 +01:00
Merge remote-tracking branch 'upstream/logger_disable_loop' into integration
This commit is contained in:
@@ -522,6 +522,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
|
||||
}
|
||||
|
||||
void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
|
||||
this->scan_result_ = &scan_result;
|
||||
for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
|
||||
this->address_[i] = scan_result.bda[i];
|
||||
this->address_type_ = static_cast<esp_ble_addr_type_t>(scan_result.ble_addr_type);
|
||||
|
@@ -85,6 +85,9 @@ class ESPBTDevice {
|
||||
|
||||
const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
|
||||
|
||||
// Exposed through a function for use in lambdas
|
||||
const BLEScanResult &get_scan_result() const { return *scan_result_; }
|
||||
|
||||
bool resolve_irk(const uint8_t *irk) const;
|
||||
|
||||
optional<ESPBLEiBeacon> get_ibeacon() const {
|
||||
@@ -111,6 +114,7 @@ class ESPBTDevice {
|
||||
std::vector<ESPBTUUID> service_uuids_{};
|
||||
std::vector<ServiceData> manufacturer_datas_{};
|
||||
std::vector<ServiceData> service_datas_{};
|
||||
const BLEScanResult *scan_result_{nullptr};
|
||||
};
|
||||
|
||||
class ESP32BLETracker;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from collections.abc import MutableMapping
|
||||
import functools
|
||||
import hashlib
|
||||
from itertools import accumulate
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
@@ -468,8 +469,9 @@ class EFont:
|
||||
|
||||
|
||||
class GlyphInfo:
|
||||
def __init__(self, data_len, advance, offset_x, offset_y, width, height):
|
||||
self.data_len = data_len
|
||||
def __init__(self, glyph, data, advance, offset_x, offset_y, width, height):
|
||||
self.glyph = glyph
|
||||
self.bitmap_data = data
|
||||
self.advance = advance
|
||||
self.offset_x = offset_x
|
||||
self.offset_y = offset_y
|
||||
@@ -477,6 +479,62 @@ class GlyphInfo:
|
||||
self.height = height
|
||||
|
||||
|
||||
def glyph_to_glyphinfo(glyph, font, size, bpp):
|
||||
scale = 256 // (1 << bpp)
|
||||
if not font.is_scalable:
|
||||
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
||||
if size in sizes:
|
||||
font.select_size(sizes.index(size))
|
||||
else:
|
||||
font.set_pixel_sizes(size, 0)
|
||||
flags = FT_LOAD_RENDER
|
||||
if bpp != 1:
|
||||
flags |= FT_LOAD_NO_BITMAP
|
||||
else:
|
||||
flags |= FT_LOAD_TARGET_MONO
|
||||
font.load_char(glyph, flags)
|
||||
width = font.glyph.bitmap.width
|
||||
height = font.glyph.bitmap.rows
|
||||
buffer = font.glyph.bitmap.buffer
|
||||
pitch = font.glyph.bitmap.pitch
|
||||
glyph_data = [0] * ((height * width * bpp + 7) // 8)
|
||||
src_mode = font.glyph.bitmap.pixel_mode
|
||||
pos = 0
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if src_mode == ft_pixel_mode_mono:
|
||||
pixel = (
|
||||
(1 << bpp) - 1
|
||||
if buffer[y * pitch + x // 8] & (1 << (7 - x % 8))
|
||||
else 0
|
||||
)
|
||||
else:
|
||||
pixel = buffer[y * pitch + x] // scale
|
||||
for bit_num in range(bpp):
|
||||
if pixel & (1 << (bpp - bit_num - 1)):
|
||||
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
|
||||
pos += 1
|
||||
ascender = pt_to_px(font.size.ascender)
|
||||
if ascender == 0:
|
||||
if not font.is_scalable:
|
||||
ascender = size
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Unable to determine ascender of font %s %s",
|
||||
font.family_name,
|
||||
font.style_name,
|
||||
)
|
||||
return GlyphInfo(
|
||||
glyph,
|
||||
glyph_data,
|
||||
pt_to_px(font.glyph.metrics.horiAdvance),
|
||||
font.glyph.bitmap_left,
|
||||
ascender - font.glyph.bitmap_top,
|
||||
width,
|
||||
height,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
"""
|
||||
Collect all glyph codepoints, construct a map from a codepoint to a font file.
|
||||
@@ -506,98 +564,47 @@ async def to_code(config):
|
||||
|
||||
codepoints = list(point_set)
|
||||
codepoints.sort(key=functools.cmp_to_key(glyph_comparator))
|
||||
glyph_args = {}
|
||||
data = []
|
||||
bpp = config[CONF_BPP]
|
||||
scale = 256 // (1 << bpp)
|
||||
size = config[CONF_SIZE]
|
||||
# create the data array for all glyphs
|
||||
for codepoint in codepoints:
|
||||
font = point_font_map[codepoint]
|
||||
if not font.is_scalable:
|
||||
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
||||
if size in sizes:
|
||||
font.select_size(sizes.index(size))
|
||||
else:
|
||||
font.set_pixel_sizes(size, 0)
|
||||
flags = FT_LOAD_RENDER
|
||||
if bpp != 1:
|
||||
flags |= FT_LOAD_NO_BITMAP
|
||||
else:
|
||||
flags |= FT_LOAD_TARGET_MONO
|
||||
font.load_char(codepoint, flags)
|
||||
width = font.glyph.bitmap.width
|
||||
height = font.glyph.bitmap.rows
|
||||
buffer = font.glyph.bitmap.buffer
|
||||
pitch = font.glyph.bitmap.pitch
|
||||
glyph_data = [0] * ((height * width * bpp + 7) // 8)
|
||||
src_mode = font.glyph.bitmap.pixel_mode
|
||||
pos = 0
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if src_mode == ft_pixel_mode_mono:
|
||||
pixel = (
|
||||
(1 << bpp) - 1
|
||||
if buffer[y * pitch + x // 8] & (1 << (7 - x % 8))
|
||||
else 0
|
||||
)
|
||||
else:
|
||||
pixel = buffer[y * pitch + x] // scale
|
||||
for bit_num in range(bpp):
|
||||
if pixel & (1 << (bpp - bit_num - 1)):
|
||||
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
|
||||
pos += 1
|
||||
ascender = pt_to_px(font.size.ascender)
|
||||
if ascender == 0:
|
||||
if not font.is_scalable:
|
||||
ascender = size
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Unable to determine ascender of font %s", config[CONF_FILE]
|
||||
)
|
||||
glyph_args[codepoint] = GlyphInfo(
|
||||
len(data),
|
||||
pt_to_px(font.glyph.metrics.horiAdvance),
|
||||
font.glyph.bitmap_left,
|
||||
ascender - font.glyph.bitmap_top,
|
||||
width,
|
||||
height,
|
||||
)
|
||||
data += glyph_data
|
||||
|
||||
rhs = [HexInt(x) for x in data]
|
||||
glyph_args = [
|
||||
glyph_to_glyphinfo(x, point_font_map[x], size, bpp) for x in codepoints
|
||||
]
|
||||
rhs = [HexInt(x) for x in flatten([x.bitmap_data for x in glyph_args])]
|
||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||
|
||||
# Create the glyph table that points to data in the above array.
|
||||
glyph_initializer = []
|
||||
for codepoint in codepoints:
|
||||
glyph_initializer.append(
|
||||
cg.StructInitializer(
|
||||
GlyphData,
|
||||
(
|
||||
"a_char",
|
||||
cg.RawExpression(
|
||||
f"(const uint8_t *){cpp_string_escape(codepoint)}"
|
||||
),
|
||||
),
|
||||
(
|
||||
"data",
|
||||
cg.RawExpression(
|
||||
f"{str(prog_arr)} + {str(glyph_args[codepoint].data_len)}"
|
||||
),
|
||||
),
|
||||
("advance", glyph_args[codepoint].advance),
|
||||
("offset_x", glyph_args[codepoint].offset_x),
|
||||
("offset_y", glyph_args[codepoint].offset_y),
|
||||
("width", glyph_args[codepoint].width),
|
||||
("height", glyph_args[codepoint].height),
|
||||
)
|
||||
glyph_initializer = [
|
||||
cg.StructInitializer(
|
||||
GlyphData,
|
||||
(
|
||||
"a_char",
|
||||
cg.RawExpression(f"(const uint8_t *){cpp_string_escape(x.glyph)}"),
|
||||
),
|
||||
(
|
||||
"data",
|
||||
cg.RawExpression(f"{str(prog_arr)} + {str(y - len(x.bitmap_data))}"),
|
||||
),
|
||||
("advance", x.advance),
|
||||
("offset_x", x.offset_x),
|
||||
("offset_y", x.offset_y),
|
||||
("width", x.width),
|
||||
("height", x.height),
|
||||
)
|
||||
for (x, y) in zip(
|
||||
glyph_args, list(accumulate([len(x.bitmap_data) for x in glyph_args]))
|
||||
)
|
||||
]
|
||||
|
||||
glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer)
|
||||
|
||||
font_height = pt_to_px(base_font.size.height)
|
||||
ascender = pt_to_px(base_font.size.ascender)
|
||||
descender = abs(pt_to_px(base_font.size.descender))
|
||||
g = glyph_to_glyphinfo("x", base_font, size, bpp)
|
||||
xheight = g.height if len(g.bitmap_data) > 1 else 0
|
||||
g = glyph_to_glyphinfo("X", base_font, size, bpp)
|
||||
capheight = g.height if len(g.bitmap_data) > 1 else 0
|
||||
if font_height == 0:
|
||||
if not base_font.is_scalable:
|
||||
font_height = size
|
||||
@@ -610,5 +617,8 @@ async def to_code(config):
|
||||
len(glyph_initializer),
|
||||
ascender,
|
||||
font_height,
|
||||
descender,
|
||||
xheight,
|
||||
capheight,
|
||||
bpp,
|
||||
)
|
||||
|
@@ -45,8 +45,15 @@ void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
|
||||
*height = this->glyph_data_->height;
|
||||
}
|
||||
|
||||
Font::Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp)
|
||||
: baseline_(baseline), height_(height), bpp_(bpp) {
|
||||
Font::Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
||||
uint8_t bpp)
|
||||
: baseline_(baseline),
|
||||
height_(height),
|
||||
descender_(descender),
|
||||
linegap_(height - baseline - descender),
|
||||
xheight_(xheight),
|
||||
capheight_(capheight),
|
||||
bpp_(bpp) {
|
||||
glyphs_.reserve(data_nr);
|
||||
for (int i = 0; i < data_nr; ++i)
|
||||
glyphs_.emplace_back(&data[i]);
|
||||
|
@@ -50,11 +50,17 @@ class Font
|
||||
public:
|
||||
/** Construct the font with the given glyphs.
|
||||
*
|
||||
* @param glyphs A vector of glyphs, must be sorted lexicographically.
|
||||
* @param data A vector of glyphs, must be sorted lexicographically.
|
||||
* @param data_nr The number of glyphs in data.
|
||||
* @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).
|
||||
* @param height The y-offset from the top of the text to the bottom.
|
||||
* @param descender The y-offset from the baseline to the lowest stroke in the font (e.g. from letters like g or p).
|
||||
* @param xheight The height of lowercase letters, usually measured at the "x" glyph.
|
||||
* @param capheight The height of capital letters, usually measured at the "X" glyph.
|
||||
* @param bpp The bits per pixel used for this font. Used to read data out of the glyph bitmaps.
|
||||
*/
|
||||
Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp = 1);
|
||||
Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
||||
uint8_t bpp = 1);
|
||||
|
||||
int match_next_glyph(const uint8_t *str, int *match_length);
|
||||
|
||||
@@ -65,6 +71,11 @@ class Font
|
||||
#endif
|
||||
inline int get_baseline() { return this->baseline_; }
|
||||
inline int get_height() { return this->height_; }
|
||||
inline int get_ascender() { return this->baseline_; }
|
||||
inline int get_descender() { return this->descender_; }
|
||||
inline int get_linegap() { return this->linegap_; }
|
||||
inline int get_xheight() { return this->xheight_; }
|
||||
inline int get_capheight() { return this->capheight_; }
|
||||
inline int get_bpp() { return this->bpp_; }
|
||||
|
||||
const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
||||
@@ -73,6 +84,10 @@ class Font
|
||||
std::vector<Glyph, RAMAllocator<Glyph>> glyphs_;
|
||||
int baseline_;
|
||||
int height_;
|
||||
int descender_;
|
||||
int linegap_;
|
||||
int xheight_;
|
||||
int capheight_;
|
||||
uint8_t bpp_; // bits per pixel
|
||||
};
|
||||
|
||||
|
@@ -46,8 +46,8 @@ void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const ch
|
||||
bool message_sent = false;
|
||||
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
||||
// For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
|
||||
message_sent = this->log_buffer_->send_message_thread_safe(static_cast<uint8_t>(level), tag,
|
||||
static_cast<uint16_t>(line), current_task, format, args);
|
||||
message_sent =
|
||||
this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), current_task, format, args);
|
||||
if (message_sent) {
|
||||
// Enable logger loop to process the buffered message
|
||||
// This is safe to call from any context including ISRs
|
||||
@@ -151,7 +151,7 @@ void Logger::init_log_buffer(size_t total_buffer_size) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESPHOME_TASK_LOG_BUFFER)
|
||||
#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
|
||||
void Logger::loop() {
|
||||
#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
|
||||
if (this->uart_ == UART_SELECTION_USB_CDC) {
|
||||
|
@@ -107,7 +107,7 @@ class Logger : public Component {
|
||||
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
||||
void init_log_buffer(size_t total_buffer_size);
|
||||
#endif
|
||||
#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESPHOME_TASK_LOG_BUFFER)
|
||||
#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
|
||||
void loop() override;
|
||||
#endif
|
||||
/// Manually set the baud rate for serial, set to 0 to disable.
|
||||
@@ -359,7 +359,7 @@ class Logger : public Component {
|
||||
this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
|
||||
}
|
||||
|
||||
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
||||
#ifdef USE_ESP32
|
||||
// Disable loop when task buffer is empty (with USB CDC check)
|
||||
inline void disable_loop_when_buffer_empty_() {
|
||||
// Thread safety note: This is safe even if another task calls enable_loop_soon_any_context()
|
||||
|
@@ -454,9 +454,13 @@ def container_validator(schema, widget_type: WidgetType):
|
||||
"""
|
||||
|
||||
def validator(value):
|
||||
result = schema
|
||||
if w_sch := widget_type.schema:
|
||||
result = result.extend(w_sch)
|
||||
if isinstance(w_sch, dict):
|
||||
w_sch = cv.Schema(w_sch)
|
||||
# order is important here to preserve extras
|
||||
result = w_sch.extend(schema)
|
||||
else:
|
||||
result = schema
|
||||
ltype = df.TYPE_NONE
|
||||
if value and (layout := value.get(df.CONF_LAYOUT)):
|
||||
if not isinstance(layout, dict):
|
||||
|
@@ -3,7 +3,6 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj
|
||||
|
||||
from .defines import (
|
||||
CONF_STYLE_DEFINITIONS,
|
||||
@@ -13,12 +12,13 @@ from .defines import (
|
||||
literal,
|
||||
)
|
||||
from .helpers import add_lv_use
|
||||
from .lvcode import LambdaContext, LocalVariable, lv, lv_assign, lv_variable
|
||||
from .lvcode import LambdaContext, LocalVariable, lv
|
||||
from .schemas import ALL_STYLES, FULL_STYLE_SCHEMA, STYLE_REMAP
|
||||
from .types import ObjUpdateAction, lv_lambda_t, lv_obj_t, lv_obj_t_ptr, lv_style_t
|
||||
from .types import ObjUpdateAction, lv_obj_t, lv_style_t
|
||||
from .widgets import (
|
||||
Widget,
|
||||
add_widgets,
|
||||
collect_parts,
|
||||
set_obj_properties,
|
||||
theme_widget_map,
|
||||
wait_for_widgets,
|
||||
@@ -37,12 +37,18 @@ async def style_set(svar, style):
|
||||
lv.call(f"style_set_{remapped_prop}", svar, literal(value))
|
||||
|
||||
|
||||
async def create_style(style, id_name):
|
||||
style_id = ID(id_name, True, lv_style_t)
|
||||
svar = cg.new_Pvariable(style_id)
|
||||
lv.style_init(svar)
|
||||
await style_set(svar, style)
|
||||
return svar
|
||||
|
||||
|
||||
async def styles_to_code(config):
|
||||
"""Convert styles to C__ code."""
|
||||
for style in config.get(CONF_STYLE_DEFINITIONS, ()):
|
||||
svar = cg.new_Pvariable(style[CONF_ID])
|
||||
lv.style_init(svar)
|
||||
await style_set(svar, style)
|
||||
await create_style(style, style[CONF_ID].id)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
@@ -68,16 +74,18 @@ async def theme_to_code(config):
|
||||
if theme := config.get(CONF_THEME):
|
||||
add_lv_use(CONF_THEME)
|
||||
for w_name, style in theme.items():
|
||||
if not isinstance(style, dict):
|
||||
continue
|
||||
|
||||
lname = "lv_theme_apply_" + w_name
|
||||
apply = lv_variable(lv_lambda_t, lname)
|
||||
theme_widget_map[w_name] = apply
|
||||
ow = Widget.create("obj", MockObj(ID("obj")), obj_spec)
|
||||
async with LambdaContext([(lv_obj_t_ptr, "obj")], where=w_name) as context:
|
||||
await set_obj_properties(ow, style)
|
||||
lv_assign(apply, await context.get_lambda())
|
||||
# Work around Python 3.10 bug with nested async comprehensions
|
||||
# With Python 3.11 this could be simplified
|
||||
styles = {}
|
||||
for part, states in collect_parts(style).items():
|
||||
styles[part] = {
|
||||
state: await create_style(
|
||||
props,
|
||||
"_lv_theme_style_" + w_name + "_" + part + "_" + state,
|
||||
)
|
||||
for state, props in states.items()
|
||||
}
|
||||
theme_widget_map[w_name] = styles
|
||||
|
||||
|
||||
async def add_top_layer(lv_component, config):
|
||||
|
@@ -6,7 +6,7 @@ from esphome.config_validation import Invalid
|
||||
from esphome.const import CONF_DEFAULT, CONF_GROUP, CONF_ID, CONF_STATE, CONF_TYPE
|
||||
from esphome.core import ID, TimePeriod
|
||||
from esphome.coroutine import FakeAwaitable
|
||||
from esphome.cpp_generator import CallExpression, MockObj
|
||||
from esphome.cpp_generator import MockObj
|
||||
|
||||
from ..defines import (
|
||||
CONF_FLEX_ALIGN_CROSS,
|
||||
@@ -453,7 +453,17 @@ async def widget_to_code(w_cnfig, w_type: WidgetType, parent):
|
||||
|
||||
w = Widget.create(wid, var, spec, w_cnfig)
|
||||
if theme := theme_widget_map.get(w_type):
|
||||
lv_add(CallExpression(theme, w.obj))
|
||||
for part, states in theme.items():
|
||||
part = "LV_PART_" + part.upper()
|
||||
for state, style in states.items():
|
||||
state = "LV_STATE_" + state.upper()
|
||||
if state == "LV_STATE_DEFAULT":
|
||||
lv_state = literal(part)
|
||||
elif part == "LV_PART_MAIN":
|
||||
lv_state = literal(state)
|
||||
else:
|
||||
lv_state = join_enums((state, part))
|
||||
lv.obj_add_style(w.obj, style, lv_state)
|
||||
await set_obj_properties(w, w_cnfig)
|
||||
await add_widgets(w, w_cnfig)
|
||||
await spec.to_code(w, w_cnfig)
|
||||
|
@@ -1,8 +1,15 @@
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, CONF_VALUE
|
||||
|
||||
from ..defines import BAR_MODES, CONF_ANIMATED, CONF_INDICATOR, CONF_MAIN, literal
|
||||
from ..lv_validation import animated, get_start_value, lv_float
|
||||
from ..defines import (
|
||||
BAR_MODES,
|
||||
CONF_ANIMATED,
|
||||
CONF_INDICATOR,
|
||||
CONF_MAIN,
|
||||
CONF_START_VALUE,
|
||||
literal,
|
||||
)
|
||||
from ..lv_validation import animated, lv_int
|
||||
from ..lvcode import lv
|
||||
from ..types import LvNumber, NumberType
|
||||
from . import Widget
|
||||
@@ -10,22 +17,30 @@ from . import Widget
|
||||
# Note this file cannot be called "bar.py" because that name is disallowed.
|
||||
|
||||
CONF_BAR = "bar"
|
||||
BAR_MODIFY_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_VALUE): lv_float,
|
||||
cv.Optional(CONF_ANIMATED, default=True): animated,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def validate_bar(config):
|
||||
if config.get(CONF_MODE) != "LV_BAR_MODE_RANGE" and CONF_START_VALUE in config:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_START_VALUE} is only allowed when {CONF_MODE} is set to 'RANGE'"
|
||||
)
|
||||
if (CONF_MIN_VALUE in config) != (CONF_MAX_VALUE in config):
|
||||
raise cv.Invalid(
|
||||
f"If either {CONF_MIN_VALUE} or {CONF_MAX_VALUE} is set, both must be set"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
BAR_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_VALUE): lv_float,
|
||||
cv.Optional(CONF_MIN_VALUE, default=0): cv.int_,
|
||||
cv.Optional(CONF_MAX_VALUE, default=100): cv.int_,
|
||||
cv.Optional(CONF_MODE, default="NORMAL"): BAR_MODES.one_of,
|
||||
cv.Optional(CONF_VALUE): lv_int,
|
||||
cv.Optional(CONF_START_VALUE): lv_int,
|
||||
cv.Optional(CONF_MIN_VALUE): lv_int,
|
||||
cv.Optional(CONF_MAX_VALUE): lv_int,
|
||||
cv.Optional(CONF_MODE): BAR_MODES.one_of,
|
||||
cv.Optional(CONF_ANIMATED, default=True): animated,
|
||||
}
|
||||
)
|
||||
).add_extra(validate_bar)
|
||||
|
||||
|
||||
class BarType(NumberType):
|
||||
@@ -35,17 +50,23 @@ class BarType(NumberType):
|
||||
LvNumber("lv_bar_t"),
|
||||
parts=(CONF_MAIN, CONF_INDICATOR),
|
||||
schema=BAR_SCHEMA,
|
||||
modify_schema=BAR_MODIFY_SCHEMA,
|
||||
)
|
||||
|
||||
async def to_code(self, w: Widget, config):
|
||||
var = w.obj
|
||||
if mode := config.get(CONF_MODE):
|
||||
lv.bar_set_mode(var, literal(mode))
|
||||
is_animated = literal(config[CONF_ANIMATED])
|
||||
if CONF_MIN_VALUE in config:
|
||||
lv.bar_set_range(var, config[CONF_MIN_VALUE], config[CONF_MAX_VALUE])
|
||||
lv.bar_set_mode(var, literal(config[CONF_MODE]))
|
||||
value = await get_start_value(config)
|
||||
if value is not None:
|
||||
lv.bar_set_value(var, value, literal(config[CONF_ANIMATED]))
|
||||
lv.bar_set_range(
|
||||
var,
|
||||
await lv_int.process(config[CONF_MIN_VALUE]),
|
||||
await lv_int.process(config[CONF_MAX_VALUE]),
|
||||
)
|
||||
if value := await lv_int.process(config.get(CONF_VALUE)):
|
||||
lv.bar_set_value(var, value, is_animated)
|
||||
if start_value := await lv_int.process(config.get(CONF_START_VALUE)):
|
||||
lv.bar_set_start_value(var, start_value, is_animated)
|
||||
|
||||
@property
|
||||
def animated(self):
|
||||
|
@@ -963,13 +963,12 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool
|
||||
uint16_t ret = 0;
|
||||
uint8_t c = 0;
|
||||
uint8_t nr_of_ff_bytes = 0;
|
||||
uint64_t start;
|
||||
bool exit_flag = false;
|
||||
bool ff_flag = false;
|
||||
|
||||
start = App.get_loop_component_start_time();
|
||||
const uint32_t start = millis();
|
||||
|
||||
while ((timeout == 0 && this->available()) || App.get_loop_component_start_time() - start <= timeout) {
|
||||
while ((timeout == 0 && this->available()) || millis() - start <= timeout) {
|
||||
if (!this->available()) {
|
||||
App.feed_wdt();
|
||||
delay(1);
|
||||
@@ -1038,7 +1037,7 @@ void Nextion::add_no_result_to_queue_(const std::string &variable_name) {
|
||||
nextion_queue->component = new nextion::NextionComponentBase;
|
||||
nextion_queue->component->set_variable_name(variable_name);
|
||||
|
||||
nextion_queue->queue_time = App.get_loop_component_start_time();
|
||||
nextion_queue->queue_time = millis();
|
||||
|
||||
this->nextion_queue_.push_back(nextion_queue);
|
||||
|
||||
|
@@ -46,7 +46,7 @@ def set_sdkconfig_options(config):
|
||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PANID", config[CONF_PAN_ID])
|
||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", config[CONF_CHANNEL])
|
||||
add_idf_sdkconfig_option(
|
||||
"CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{config[CONF_NETWORK_KEY]:X}"
|
||||
"CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{config[CONF_NETWORK_KEY]:X}".lower()
|
||||
)
|
||||
|
||||
if network_name := config.get(CONF_NETWORK_NAME):
|
||||
@@ -54,14 +54,14 @@ def set_sdkconfig_options(config):
|
||||
|
||||
if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None:
|
||||
add_idf_sdkconfig_option(
|
||||
"CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}"
|
||||
"CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}".lower()
|
||||
)
|
||||
if (mesh_local_prefix := config.get(CONF_MESH_LOCAL_PREFIX)) is not None:
|
||||
add_idf_sdkconfig_option(
|
||||
"CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix:X}"
|
||||
"CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix}".lower()
|
||||
)
|
||||
if (pskc := config.get(CONF_PSKC)) is not None:
|
||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}")
|
||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}".lower())
|
||||
|
||||
if CONF_FORCE_DATASET in config:
|
||||
if config[CONF_FORCE_DATASET]:
|
||||
@@ -98,7 +98,7 @@ _CONNECTION_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_EXT_PAN_ID): cv.hex_int,
|
||||
cv.Optional(CONF_NETWORK_NAME): cv.string_strict,
|
||||
cv.Optional(CONF_PSKC): cv.hex_int,
|
||||
cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.hex_int,
|
||||
cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.ipv6network,
|
||||
}
|
||||
)
|
||||
|
||||
|
@@ -137,7 +137,7 @@ void OpenThreadSrpComponent::setup() {
|
||||
// Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this
|
||||
// component
|
||||
this->mdns_services_ = this->mdns_->get_services();
|
||||
ESP_LOGW(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size());
|
||||
ESP_LOGD(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size());
|
||||
for (const auto &service : this->mdns_services_) {
|
||||
otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
|
||||
if (!entry) {
|
||||
@@ -185,11 +185,11 @@ void OpenThreadSrpComponent::setup() {
|
||||
if (error != OT_ERROR_NONE) {
|
||||
ESP_LOGW(TAG, "Failed to add service: %s", otThreadErrorToString(error));
|
||||
}
|
||||
ESP_LOGW(TAG, "Added service: %s", full_service.c_str());
|
||||
ESP_LOGD(TAG, "Added service: %s", full_service.c_str());
|
||||
}
|
||||
|
||||
otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr);
|
||||
ESP_LOGW(TAG, "Finished SRP setup");
|
||||
ESP_LOGD(TAG, "Finished SRP setup");
|
||||
}
|
||||
|
||||
void *OpenThreadSrpComponent::pool_alloc_(size_t size) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
# Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9
|
||||
import binascii
|
||||
import ipaddress
|
||||
|
||||
from esphome.const import CONF_CHANNEL
|
||||
|
||||
@@ -37,6 +38,12 @@ def parse_tlv(tlv) -> dict:
|
||||
if tag in TLV_TYPES:
|
||||
if tag == 3:
|
||||
output[TLV_TYPES[tag]] = val.decode("utf-8")
|
||||
elif tag == 7:
|
||||
mesh_local_prefix = binascii.hexlify(val).decode("utf-8")
|
||||
mesh_local_prefix_str = f"{mesh_local_prefix}0000000000000000"
|
||||
ipv6_bytes = bytes.fromhex(mesh_local_prefix_str)
|
||||
ipv6_address = ipaddress.IPv6Address(ipv6_bytes)
|
||||
output[TLV_TYPES[tag]] = f"{ipv6_address}/64"
|
||||
else:
|
||||
output[TLV_TYPES[tag]] = int.from_bytes(val)
|
||||
return output
|
||||
|
@@ -6,7 +6,7 @@ from esphome.components.esp32 import (
|
||||
only_on_variant,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.const import CONF_DEVICES, CONF_ID
|
||||
from esphome.cpp_types import Component
|
||||
|
||||
AUTO_LOAD = ["bytebuffer"]
|
||||
@@ -16,7 +16,6 @@ usb_host_ns = cg.esphome_ns.namespace("usb_host")
|
||||
USBHost = usb_host_ns.class_("USBHost", Component)
|
||||
USBClient = usb_host_ns.class_("USBClient", Component)
|
||||
|
||||
CONF_DEVICES = "devices"
|
||||
CONF_VID = "vid"
|
||||
CONF_PID = "pid"
|
||||
CONF_ENABLE_HUBS = "enable_hubs"
|
||||
|
@@ -3,7 +3,15 @@
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from ipaddress import AddressValueError, IPv4Address, ip_address
|
||||
from ipaddress import (
|
||||
AddressValueError,
|
||||
IPv4Address,
|
||||
IPv4Network,
|
||||
IPv6Address,
|
||||
IPv6Network,
|
||||
ip_address,
|
||||
ip_network,
|
||||
)
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
@@ -1176,6 +1184,14 @@ def ipv4address(value):
|
||||
return address
|
||||
|
||||
|
||||
def ipv6address(value):
|
||||
try:
|
||||
address = IPv6Address(value)
|
||||
except AddressValueError as exc:
|
||||
raise Invalid(f"{value} is not a valid IPv6 address") from exc
|
||||
return address
|
||||
|
||||
|
||||
def ipv4address_multi_broadcast(value):
|
||||
address = ipv4address(value)
|
||||
if not (address.is_multicast or (address == IPv4Address("255.255.255.255"))):
|
||||
@@ -1193,6 +1209,33 @@ def ipaddress(value):
|
||||
return address
|
||||
|
||||
|
||||
def ipv4network(value):
|
||||
"""Validate that the value is a valid IPv4 network."""
|
||||
try:
|
||||
network = IPv4Network(value, strict=False)
|
||||
except ValueError as exc:
|
||||
raise Invalid(f"{value} is not a valid IPv4 network") from exc
|
||||
return network
|
||||
|
||||
|
||||
def ipv6network(value):
|
||||
"""Validate that the value is a valid IPv6 network."""
|
||||
try:
|
||||
network = IPv6Network(value, strict=False)
|
||||
except ValueError as exc:
|
||||
raise Invalid(f"{value} is not a valid IPv6 network") from exc
|
||||
return network
|
||||
|
||||
|
||||
def ipnetwork(value):
|
||||
"""Validate that the value is a valid IP network."""
|
||||
try:
|
||||
network = ip_network(value, strict=False)
|
||||
except ValueError as exc:
|
||||
raise Invalid(f"{value} is not a valid IP network") from exc
|
||||
return network
|
||||
|
||||
|
||||
def _valid_topic(value):
|
||||
"""Validate that this is a valid topic name/filter."""
|
||||
if value is None: # Used to disable publishing and subscribing
|
||||
|
@@ -217,6 +217,7 @@ CONF_DEST = "dest"
|
||||
CONF_DEVICE = "device"
|
||||
CONF_DEVICE_CLASS = "device_class"
|
||||
CONF_DEVICE_FACTOR = "device_factor"
|
||||
CONF_DEVICES = "devices"
|
||||
CONF_DIELECTRIC_CONSTANT = "dielectric_constant"
|
||||
CONF_DIMENSIONS = "dimensions"
|
||||
CONF_DIO_PIN = "dio_pin"
|
||||
|
@@ -17,7 +17,6 @@
|
||||
|
||||
// logger
|
||||
#define ESPHOME_LOG_LEVEL ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
#define USE_ESPHOME_TASK_LOG_BUFFER
|
||||
|
||||
// Feature flags
|
||||
#define USE_ALARM_CONTROL_PANEL
|
||||
@@ -131,6 +130,8 @@
|
||||
|
||||
// ESP32-specific feature flags
|
||||
#ifdef USE_ESP32
|
||||
#define USE_ESPHOME_TASK_LOG_BUFFER
|
||||
|
||||
#define USE_BLUETOOTH_PROXY
|
||||
#define USE_CAPTIVE_PORTAL
|
||||
#define USE_ESP32_BLE
|
||||
|
@@ -5,7 +5,7 @@ import fnmatch
|
||||
import functools
|
||||
import inspect
|
||||
from io import BytesIO, TextIOBase, TextIOWrapper
|
||||
from ipaddress import _BaseAddress
|
||||
from ipaddress import _BaseAddress, _BaseNetwork
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
@@ -621,6 +621,7 @@ ESPHomeDumper.add_multi_representer(str, ESPHomeDumper.represent_stringify)
|
||||
ESPHomeDumper.add_multi_representer(int, ESPHomeDumper.represent_int)
|
||||
ESPHomeDumper.add_multi_representer(float, ESPHomeDumper.represent_float)
|
||||
ESPHomeDumper.add_multi_representer(_BaseAddress, ESPHomeDumper.represent_stringify)
|
||||
ESPHomeDumper.add_multi_representer(_BaseNetwork, ESPHomeDumper.represent_stringify)
|
||||
ESPHomeDumper.add_multi_representer(MACAddress, ESPHomeDumper.represent_stringify)
|
||||
ESPHomeDumper.add_multi_representer(TimePeriod, ESPHomeDumper.represent_stringify)
|
||||
ESPHomeDumper.add_multi_representer(Lambda, ESPHomeDumper.represent_lambda)
|
||||
|
@@ -728,12 +728,15 @@ lvgl:
|
||||
value: 30
|
||||
max_value: 100
|
||||
min_value: 10
|
||||
start_value: 20
|
||||
mode: range
|
||||
on_click:
|
||||
then:
|
||||
- lvgl.bar.update:
|
||||
id: bar_id
|
||||
value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
||||
start_value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
||||
mode: symmetrical
|
||||
- logger.log:
|
||||
format: "bar value %f"
|
||||
args: [x]
|
||||
|
@@ -8,4 +8,6 @@ openthread:
|
||||
pan_id: 0x8f28
|
||||
ext_pan_id: 0xd63e8e3e495ebbc3
|
||||
pskc: 0xc23a76e98f1a6483639b1ac1271e2e27
|
||||
mesh_local_prefix: fd53:145f:ed22:ad81::/64
|
||||
force_dataset: true
|
||||
|
||||
|
Reference in New Issue
Block a user