1
0
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:
J. Nick Koston
2025-06-23 09:03:16 +02:00
22 changed files with 284 additions and 148 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,
)

View File

@@ -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]);

View File

@@ -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
};

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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):

View File

@@ -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):

View File

@@ -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)

View File

@@ -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):

View File

@@ -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);

View File

@@ -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,
}
)

View File

@@ -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) {

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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)

View File

@@ -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]

View File

@@ -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