mirror of
https://github.com/esphome/esphome.git
synced 2025-09-26 07:02:21 +01:00
Merge remote-tracking branch 'upstream/dev' into api_reboot
This commit is contained in:
@@ -28,6 +28,12 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
// Read a maximum of 5 messages per loop iteration to prevent starving other components.
|
||||||
|
// This is a balance between API responsiveness and allowing other components to run.
|
||||||
|
// Since each message could contain multiple protobuf messages when using packet batching,
|
||||||
|
// this limits the number of messages processed, not the number of TCP packets.
|
||||||
|
static constexpr uint8_t MAX_MESSAGES_PER_LOOP = 5;
|
||||||
|
|
||||||
static const char *const TAG = "api.connection";
|
static const char *const TAG = "api.connection";
|
||||||
static const int ESP32_CAMERA_STOP_STREAM = 5000;
|
static const int ESP32_CAMERA_STOP_STREAM = 5000;
|
||||||
|
|
||||||
@@ -109,12 +115,16 @@ void APIConnection::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32_t now = App.get_loop_component_start_time();
|
||||||
// Check if socket has data ready before attempting to read
|
// Check if socket has data ready before attempting to read
|
||||||
if (this->helper_->is_socket_ready()) {
|
if (this->helper_->is_socket_ready()) {
|
||||||
|
// Read up to MAX_MESSAGES_PER_LOOP messages per loop to improve throughput
|
||||||
|
for (uint8_t message_count = 0; message_count < MAX_MESSAGES_PER_LOOP; message_count++) {
|
||||||
ReadPacketBuffer buffer;
|
ReadPacketBuffer buffer;
|
||||||
err = this->helper_->read_packet(&buffer);
|
err = this->helper_->read_packet(&buffer);
|
||||||
if (err == APIError::WOULD_BLOCK) {
|
if (err == APIError::WOULD_BLOCK) {
|
||||||
// pass
|
// No more data available
|
||||||
|
break;
|
||||||
} else if (err != APIError::OK) {
|
} else if (err != APIError::OK) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
|
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
|
||||||
@@ -127,7 +137,7 @@ void APIConnection::loop() {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
this->last_traffic_ = App.get_loop_component_start_time();
|
this->last_traffic_ = now;
|
||||||
// read a packet
|
// read a packet
|
||||||
if (buffer.data_len > 0) {
|
if (buffer.data_len > 0) {
|
||||||
this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
|
this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
|
||||||
@@ -138,6 +148,7 @@ void APIConnection::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Process deferred batch if scheduled
|
// Process deferred batch if scheduled
|
||||||
if (this->deferred_batch_.batch_scheduled &&
|
if (this->deferred_batch_.batch_scheduled &&
|
||||||
@@ -152,7 +163,6 @@ void APIConnection::loop() {
|
|||||||
|
|
||||||
static uint8_t max_ping_retries = 60;
|
static uint8_t max_ping_retries = 60;
|
||||||
static uint16_t ping_retry_interval = 1000;
|
static uint16_t ping_retry_interval = 1000;
|
||||||
const uint32_t now = App.get_loop_component_start_time();
|
|
||||||
if (this->sent_ping_) {
|
if (this->sent_ping_) {
|
||||||
// Disconnect if not responded within 2.5*keepalive
|
// Disconnect if not responded within 2.5*keepalive
|
||||||
if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
|
if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
|
||||||
|
@@ -274,12 +274,21 @@ APIError APINoiseFrameHelper::init() {
|
|||||||
}
|
}
|
||||||
/// Run through handshake messages (if in that phase)
|
/// Run through handshake messages (if in that phase)
|
||||||
APIError APINoiseFrameHelper::loop() {
|
APIError APINoiseFrameHelper::loop() {
|
||||||
|
// During handshake phase, process as many actions as possible until we can't progress
|
||||||
|
// socket_->ready() stays true until next main loop, but state_action() will return
|
||||||
|
// WOULD_BLOCK when no more data is available to read
|
||||||
|
while (state_ != State::DATA && this->socket_->ready()) {
|
||||||
APIError err = state_action_();
|
APIError err = state_action_();
|
||||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
if (err == APIError::WOULD_BLOCK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!this->tx_buf_.empty()) {
|
if (!this->tx_buf_.empty()) {
|
||||||
err = try_send_tx_buf_();
|
APIError err = try_send_tx_buf_();
|
||||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@@ -522,6 +522,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
|
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++)
|
for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
|
||||||
this->address_[i] = scan_result.bda[i];
|
this->address_[i] = scan_result.bda[i];
|
||||||
this->address_type_ = static_cast<esp_ble_addr_type_t>(scan_result.ble_addr_type);
|
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_; }
|
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;
|
bool resolve_irk(const uint8_t *irk) const;
|
||||||
|
|
||||||
optional<ESPBLEiBeacon> get_ibeacon() const {
|
optional<ESPBLEiBeacon> get_ibeacon() const {
|
||||||
@@ -111,6 +114,7 @@ class ESPBTDevice {
|
|||||||
std::vector<ESPBTUUID> service_uuids_{};
|
std::vector<ESPBTUUID> service_uuids_{};
|
||||||
std::vector<ServiceData> manufacturer_datas_{};
|
std::vector<ServiceData> manufacturer_datas_{};
|
||||||
std::vector<ServiceData> service_datas_{};
|
std::vector<ServiceData> service_datas_{};
|
||||||
|
const BLEScanResult *scan_result_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ESP32BLETracker;
|
class ESP32BLETracker;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
import functools
|
import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
|
from itertools import accumulate
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -468,8 +469,9 @@ class EFont:
|
|||||||
|
|
||||||
|
|
||||||
class GlyphInfo:
|
class GlyphInfo:
|
||||||
def __init__(self, data_len, advance, offset_x, offset_y, width, height):
|
def __init__(self, glyph, data, advance, offset_x, offset_y, width, height):
|
||||||
self.data_len = data_len
|
self.glyph = glyph
|
||||||
|
self.bitmap_data = data
|
||||||
self.advance = advance
|
self.advance = advance
|
||||||
self.offset_x = offset_x
|
self.offset_x = offset_x
|
||||||
self.offset_y = offset_y
|
self.offset_y = offset_y
|
||||||
@@ -477,6 +479,62 @@ class GlyphInfo:
|
|||||||
self.height = height
|
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):
|
async def to_code(config):
|
||||||
"""
|
"""
|
||||||
Collect all glyph codepoints, construct a map from a codepoint to a font file.
|
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 = list(point_set)
|
||||||
codepoints.sort(key=functools.cmp_to_key(glyph_comparator))
|
codepoints.sort(key=functools.cmp_to_key(glyph_comparator))
|
||||||
glyph_args = {}
|
|
||||||
data = []
|
|
||||||
bpp = config[CONF_BPP]
|
bpp = config[CONF_BPP]
|
||||||
scale = 256 // (1 << bpp)
|
|
||||||
size = config[CONF_SIZE]
|
size = config[CONF_SIZE]
|
||||||
# create the data array for all glyphs
|
# create the data array for all glyphs
|
||||||
for codepoint in codepoints:
|
glyph_args = [
|
||||||
font = point_font_map[codepoint]
|
glyph_to_glyphinfo(x, point_font_map[x], size, bpp) for x in codepoints
|
||||||
if not font.is_scalable:
|
]
|
||||||
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
rhs = [HexInt(x) for x in flatten([x.bitmap_data for x in glyph_args])]
|
||||||
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]
|
|
||||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||||
|
|
||||||
# Create the glyph table that points to data in the above array.
|
# Create the glyph table that points to data in the above array.
|
||||||
glyph_initializer = []
|
glyph_initializer = [
|
||||||
for codepoint in codepoints:
|
|
||||||
glyph_initializer.append(
|
|
||||||
cg.StructInitializer(
|
cg.StructInitializer(
|
||||||
GlyphData,
|
GlyphData,
|
||||||
(
|
(
|
||||||
"a_char",
|
"a_char",
|
||||||
cg.RawExpression(
|
cg.RawExpression(f"(const uint8_t *){cpp_string_escape(x.glyph)}"),
|
||||||
f"(const uint8_t *){cpp_string_escape(codepoint)}"
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"data",
|
"data",
|
||||||
cg.RawExpression(
|
cg.RawExpression(f"{str(prog_arr)} + {str(y - len(x.bitmap_data))}"),
|
||||||
f"{str(prog_arr)} + {str(glyph_args[codepoint].data_len)}"
|
|
||||||
),
|
),
|
||||||
),
|
("advance", x.advance),
|
||||||
("advance", glyph_args[codepoint].advance),
|
("offset_x", x.offset_x),
|
||||||
("offset_x", glyph_args[codepoint].offset_x),
|
("offset_y", x.offset_y),
|
||||||
("offset_y", glyph_args[codepoint].offset_y),
|
("width", x.width),
|
||||||
("width", glyph_args[codepoint].width),
|
("height", x.height),
|
||||||
("height", glyph_args[codepoint].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)
|
glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer)
|
||||||
|
|
||||||
font_height = pt_to_px(base_font.size.height)
|
font_height = pt_to_px(base_font.size.height)
|
||||||
ascender = pt_to_px(base_font.size.ascender)
|
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 font_height == 0:
|
||||||
if not base_font.is_scalable:
|
if not base_font.is_scalable:
|
||||||
font_height = size
|
font_height = size
|
||||||
@@ -610,5 +617,8 @@ async def to_code(config):
|
|||||||
len(glyph_initializer),
|
len(glyph_initializer),
|
||||||
ascender,
|
ascender,
|
||||||
font_height,
|
font_height,
|
||||||
|
descender,
|
||||||
|
xheight,
|
||||||
|
capheight,
|
||||||
bpp,
|
bpp,
|
||||||
)
|
)
|
||||||
|
@@ -45,8 +45,15 @@ void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
|
|||||||
*height = this->glyph_data_->height;
|
*height = this->glyph_data_->height;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp)
|
Font::Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
||||||
: baseline_(baseline), height_(height), bpp_(bpp) {
|
uint8_t bpp)
|
||||||
|
: baseline_(baseline),
|
||||||
|
height_(height),
|
||||||
|
descender_(descender),
|
||||||
|
linegap_(height - baseline - descender),
|
||||||
|
xheight_(xheight),
|
||||||
|
capheight_(capheight),
|
||||||
|
bpp_(bpp) {
|
||||||
glyphs_.reserve(data_nr);
|
glyphs_.reserve(data_nr);
|
||||||
for (int i = 0; i < data_nr; ++i)
|
for (int i = 0; i < data_nr; ++i)
|
||||||
glyphs_.emplace_back(&data[i]);
|
glyphs_.emplace_back(&data[i]);
|
||||||
|
@@ -50,11 +50,17 @@ class Font
|
|||||||
public:
|
public:
|
||||||
/** Construct the font with the given glyphs.
|
/** 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 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);
|
int match_next_glyph(const uint8_t *str, int *match_length);
|
||||||
|
|
||||||
@@ -65,6 +71,11 @@ class Font
|
|||||||
#endif
|
#endif
|
||||||
inline int get_baseline() { return this->baseline_; }
|
inline int get_baseline() { return this->baseline_; }
|
||||||
inline int get_height() { return this->height_; }
|
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_; }
|
inline int get_bpp() { return this->bpp_; }
|
||||||
|
|
||||||
const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
||||||
@@ -73,6 +84,10 @@ class Font
|
|||||||
std::vector<Glyph, RAMAllocator<Glyph>> glyphs_;
|
std::vector<Glyph, RAMAllocator<Glyph>> glyphs_;
|
||||||
int baseline_;
|
int baseline_;
|
||||||
int height_;
|
int height_;
|
||||||
|
int descender_;
|
||||||
|
int linegap_;
|
||||||
|
int xheight_;
|
||||||
|
int capheight_;
|
||||||
uint8_t bpp_; // bits per pixel
|
uint8_t bpp_; // bits per pixel
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -184,7 +184,9 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(Logger),
|
cv.GenerateID(): cv.declare_id(Logger),
|
||||||
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
|
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
|
||||||
cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes,
|
cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.All(
|
||||||
|
cv.validate_bytes, cv.int_range(min=160, max=65535)
|
||||||
|
),
|
||||||
cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean,
|
cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean,
|
||||||
cv.SplitDefault(
|
cv.SplitDefault(
|
||||||
CONF_TASK_LOG_BUFFER_SIZE,
|
CONF_TASK_LOG_BUFFER_SIZE,
|
||||||
|
@@ -24,7 +24,7 @@ static const char *const TAG = "logger";
|
|||||||
// - Messages are serialized through main loop for proper console output
|
// - Messages are serialized through main loop for proper console output
|
||||||
// - Fallback to emergency console logging only if ring buffer is full
|
// - Fallback to emergency console logging only if ring buffer is full
|
||||||
// - WITHOUT task log buffer: Only emergency console output, no callbacks
|
// - WITHOUT task log buffer: Only emergency console output, no callbacks
|
||||||
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
|
void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args) { // NOLINT
|
||||||
if (level > this->level_for(tag))
|
if (level > this->level_for(tag))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -46,8 +46,8 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
|
|||||||
bool message_sent = false;
|
bool message_sent = false;
|
||||||
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
||||||
// For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
|
// 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,
|
message_sent =
|
||||||
static_cast<uint16_t>(line), current_task, format, args);
|
this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), current_task, format, args);
|
||||||
#endif // USE_ESPHOME_TASK_LOG_BUFFER
|
#endif // USE_ESPHOME_TASK_LOG_BUFFER
|
||||||
|
|
||||||
// Emergency console logging for non-main tasks when ring buffer is full or disabled
|
// Emergency console logging for non-main tasks when ring buffer is full or disabled
|
||||||
@@ -58,7 +58,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
|
|||||||
// Maximum size for console log messages (includes null terminator)
|
// Maximum size for console log messages (includes null terminator)
|
||||||
static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144;
|
static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144;
|
||||||
char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
|
char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
|
||||||
int buffer_at = 0; // Initialize buffer position
|
uint16_t buffer_at = 0; // Initialize buffer position
|
||||||
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at,
|
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at,
|
||||||
MAX_CONSOLE_LOG_MSG_SIZE);
|
MAX_CONSOLE_LOG_MSG_SIZE);
|
||||||
this->write_msg_(console_buffer);
|
this->write_msg_(console_buffer);
|
||||||
@@ -69,7 +69,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Implementation for all other platforms
|
// Implementation for all other platforms
|
||||||
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
|
void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args) { // NOLINT
|
||||||
if (level > this->level_for(tag) || global_recursion_guard_)
|
if (level > this->level_for(tag) || global_recursion_guard_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
|
|||||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||||
// Implementation for ESP8266 with flash string support.
|
// Implementation for ESP8266 with flash string support.
|
||||||
// Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266.
|
// Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266.
|
||||||
void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format,
|
void Logger::log_vprintf_(uint8_t level, const char *tag, int line, const __FlashStringHelper *format,
|
||||||
va_list args) { // NOLINT
|
va_list args) { // NOLINT
|
||||||
if (level > this->level_for(tag) || global_recursion_guard_)
|
if (level > this->level_for(tag) || global_recursion_guard_)
|
||||||
return;
|
return;
|
||||||
@@ -122,7 +122,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
|
|||||||
}
|
}
|
||||||
#endif // USE_STORE_LOG_STR_IN_FLASH
|
#endif // USE_STORE_LOG_STR_IN_FLASH
|
||||||
|
|
||||||
inline int Logger::level_for(const char *tag) {
|
inline uint8_t Logger::level_for(const char *tag) {
|
||||||
auto it = this->log_levels_.find(tag);
|
auto it = this->log_levels_.find(tag);
|
||||||
if (it != this->log_levels_.end())
|
if (it != this->log_levels_.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
@@ -195,13 +195,13 @@ void Logger::loop() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
|
void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
|
||||||
void Logger::set_log_level(const std::string &tag, int log_level) { this->log_levels_[tag] = log_level; }
|
void Logger::set_log_level(const std::string &tag, uint8_t log_level) { this->log_levels_[tag] = log_level; }
|
||||||
|
|
||||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
|
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
|
||||||
UARTSelection Logger::get_uart() const { return this->uart_; }
|
UARTSelection Logger::get_uart() const { return this->uart_; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Logger::add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback) {
|
void Logger::add_on_log_callback(std::function<void(uint8_t, const char *, const char *)> &&callback) {
|
||||||
this->log_callback_.add(std::move(callback));
|
this->log_callback_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; }
|
float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; }
|
||||||
@@ -230,7 +230,7 @@ void Logger::dump_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::set_log_level(int level) {
|
void Logger::set_log_level(uint8_t level) {
|
||||||
if (level > ESPHOME_LOG_LEVEL) {
|
if (level > ESPHOME_LOG_LEVEL) {
|
||||||
level = ESPHOME_LOG_LEVEL;
|
level = ESPHOME_LOG_LEVEL;
|
||||||
ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
|
ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
|
||||||
|
@@ -61,7 +61,7 @@ static const char *const LOG_LEVEL_LETTERS[] = {
|
|||||||
*
|
*
|
||||||
* Advanced configuration (pin selection, etc) is not supported.
|
* Advanced configuration (pin selection, etc) is not supported.
|
||||||
*/
|
*/
|
||||||
enum UARTSelection {
|
enum UARTSelection : uint8_t {
|
||||||
#ifdef USE_LIBRETINY
|
#ifdef USE_LIBRETINY
|
||||||
UART_SELECTION_DEFAULT = 0,
|
UART_SELECTION_DEFAULT = 0,
|
||||||
UART_SELECTION_UART0,
|
UART_SELECTION_UART0,
|
||||||
@@ -129,10 +129,10 @@ class Logger : public Component {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Set the default log level for this logger.
|
/// Set the default log level for this logger.
|
||||||
void set_log_level(int level);
|
void set_log_level(uint8_t level);
|
||||||
/// Set the log level of the specified tag.
|
/// Set the log level of the specified tag.
|
||||||
void set_log_level(const std::string &tag, int log_level);
|
void set_log_level(const std::string &tag, uint8_t log_level);
|
||||||
int get_log_level() { return this->current_level_; }
|
uint8_t get_log_level() { return this->current_level_; }
|
||||||
|
|
||||||
// ========== INTERNAL METHODS ==========
|
// ========== INTERNAL METHODS ==========
|
||||||
// (In most use cases you won't need these)
|
// (In most use cases you won't need these)
|
||||||
@@ -140,19 +140,20 @@ class Logger : public Component {
|
|||||||
void pre_setup();
|
void pre_setup();
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
inline int level_for(const char *tag);
|
inline uint8_t level_for(const char *tag);
|
||||||
|
|
||||||
/// Register a callback that will be called for every log message sent
|
/// Register a callback that will be called for every log message sent
|
||||||
void add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback);
|
void add_on_log_callback(std::function<void(uint8_t, const char *, const char *)> &&callback);
|
||||||
|
|
||||||
// add a listener for log level changes
|
// add a listener for log level changes
|
||||||
void add_listener(std::function<void(int)> &&callback) { this->level_callback_.add(std::move(callback)); }
|
void add_listener(std::function<void(uint8_t)> &&callback) { this->level_callback_.add(std::move(callback)); }
|
||||||
|
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
void log_vprintf_(int level, const char *tag, int line, const char *format, va_list args); // NOLINT
|
void log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args); // NOLINT
|
||||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||||
void log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format, va_list args); // NOLINT
|
void log_vprintf_(uint8_t level, const char *tag, int line, const __FlashStringHelper *format,
|
||||||
|
va_list args); // NOLINT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -160,8 +161,9 @@ class Logger : public Component {
|
|||||||
|
|
||||||
// Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
|
// Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
|
||||||
// It's the caller's responsibility to initialize buffer_at (typically to 0)
|
// It's the caller's responsibility to initialize buffer_at (typically to 0)
|
||||||
inline void HOT format_log_to_buffer_with_terminator_(int level, const char *tag, int line, const char *format,
|
inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format,
|
||||||
va_list args, char *buffer, int *buffer_at, int buffer_size) {
|
va_list args, char *buffer, uint16_t *buffer_at,
|
||||||
|
uint16_t buffer_size) {
|
||||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||||
this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
|
this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
|
||||||
#else
|
#else
|
||||||
@@ -180,7 +182,7 @@ class Logger : public Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper to format and send a log message to both console and callbacks
|
// Helper to format and send a log message to both console and callbacks
|
||||||
inline void HOT log_message_to_buffer_and_send_(int level, const char *tag, int line, const char *format,
|
inline void HOT log_message_to_buffer_and_send_(uint8_t level, const char *tag, int line, const char *format,
|
||||||
va_list args) {
|
va_list args) {
|
||||||
// Format to tx_buffer and prepare for output
|
// Format to tx_buffer and prepare for output
|
||||||
this->tx_buffer_at_ = 0; // Initialize buffer position
|
this->tx_buffer_at_ = 0; // Initialize buffer position
|
||||||
@@ -194,11 +196,12 @@ class Logger : public Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the body of the log message to the buffer
|
// Write the body of the log message to the buffer
|
||||||
inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, int *buffer_at, int buffer_size) {
|
inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, uint16_t *buffer_at,
|
||||||
|
uint16_t buffer_size) {
|
||||||
// Calculate available space
|
// Calculate available space
|
||||||
const int available = buffer_size - *buffer_at;
|
if (*buffer_at >= buffer_size)
|
||||||
if (available <= 0)
|
|
||||||
return;
|
return;
|
||||||
|
const uint16_t available = buffer_size - *buffer_at;
|
||||||
|
|
||||||
// Determine copy length (minimum of remaining capacity and string length)
|
// Determine copy length (minimum of remaining capacity and string length)
|
||||||
const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available;
|
const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available;
|
||||||
@@ -211,7 +214,7 @@ class Logger : public Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Format string to explicit buffer with varargs
|
// Format string to explicit buffer with varargs
|
||||||
inline void printf_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, ...) {
|
inline void printf_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format, ...) {
|
||||||
va_list arg;
|
va_list arg;
|
||||||
va_start(arg, format);
|
va_start(arg, format);
|
||||||
this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg);
|
this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg);
|
||||||
@@ -222,41 +225,50 @@ class Logger : public Component {
|
|||||||
const char *get_uart_selection_();
|
const char *get_uart_selection_();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Group 4-byte aligned members first
|
||||||
uint32_t baud_rate_;
|
uint32_t baud_rate_;
|
||||||
char *tx_buffer_{nullptr};
|
char *tx_buffer_{nullptr};
|
||||||
int tx_buffer_at_{0};
|
#ifdef USE_ARDUINO
|
||||||
int tx_buffer_size_{0};
|
Stream *hw_serial_{nullptr};
|
||||||
|
#endif
|
||||||
|
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||||
|
void *main_task_ = nullptr; // Only used for thread name identification
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
// Task-specific recursion guards:
|
||||||
|
// - Main task uses a dedicated member variable for efficiency
|
||||||
|
// - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
|
||||||
|
pthread_key_t log_recursion_key_; // 4 bytes
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ESP_IDF
|
||||||
|
uart_port_t uart_num_; // 4 bytes (enum defaults to int size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Large objects (internally aligned)
|
||||||
|
std::map<std::string, uint8_t> log_levels_{};
|
||||||
|
CallbackManager<void(uint8_t, const char *, const char *)> log_callback_{};
|
||||||
|
CallbackManager<void(uint8_t)> level_callback_{};
|
||||||
|
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
||||||
|
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Group smaller types together at the end
|
||||||
|
uint16_t tx_buffer_at_{0};
|
||||||
|
uint16_t tx_buffer_size_{0};
|
||||||
|
uint8_t current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
|
||||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
|
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
|
||||||
UARTSelection uart_{UART_SELECTION_UART0};
|
UARTSelection uart_{UART_SELECTION_UART0};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIBRETINY
|
#ifdef USE_LIBRETINY
|
||||||
UARTSelection uart_{UART_SELECTION_DEFAULT};
|
UARTSelection uart_{UART_SELECTION_DEFAULT};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ARDUINO
|
|
||||||
Stream *hw_serial_{nullptr};
|
|
||||||
#endif
|
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
uart_port_t uart_num_;
|
|
||||||
#endif
|
|
||||||
std::map<std::string, int> log_levels_{};
|
|
||||||
CallbackManager<void(int, const char *, const char *)> log_callback_{};
|
|
||||||
int current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
|
|
||||||
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
|
||||||
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
|
|
||||||
#endif
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
// Task-specific recursion guards:
|
|
||||||
// - Main task uses a dedicated member variable for efficiency
|
|
||||||
// - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
|
|
||||||
bool main_task_recursion_guard_{false};
|
bool main_task_recursion_guard_{false};
|
||||||
pthread_key_t log_recursion_key_;
|
|
||||||
#else
|
#else
|
||||||
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
|
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
|
||||||
#endif
|
#endif
|
||||||
CallbackManager<void(int)> level_callback_{};
|
|
||||||
|
|
||||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||||
void *main_task_ = nullptr; // Only used for thread name identification
|
|
||||||
const char *HOT get_thread_name_() {
|
const char *HOT get_thread_name_() {
|
||||||
TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
|
TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
|
||||||
if (current_task == main_task_) {
|
if (current_task == main_task_) {
|
||||||
@@ -297,11 +309,10 @@ class Logger : public Component {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
inline void HOT write_header_to_buffer_(int level, const char *tag, int line, const char *thread_name, char *buffer,
|
inline void HOT write_header_to_buffer_(uint8_t level, const char *tag, int line, const char *thread_name,
|
||||||
int *buffer_at, int buffer_size) {
|
char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
|
||||||
// Format header
|
// Format header
|
||||||
if (level < 0)
|
// uint8_t level is already bounded 0-255, just ensure it's <= 7
|
||||||
level = 0;
|
|
||||||
if (level > 7)
|
if (level > 7)
|
||||||
level = 7;
|
level = 7;
|
||||||
|
|
||||||
@@ -320,12 +331,12 @@ class Logger : public Component {
|
|||||||
this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line);
|
this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void HOT format_body_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format,
|
inline void HOT format_body_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format,
|
||||||
va_list args) {
|
va_list args) {
|
||||||
// Get remaining capacity in the buffer
|
// Get remaining capacity in the buffer
|
||||||
const int remaining = buffer_size - *buffer_at;
|
if (*buffer_at >= buffer_size)
|
||||||
if (remaining <= 0)
|
|
||||||
return;
|
return;
|
||||||
|
const uint16_t remaining = buffer_size - *buffer_at;
|
||||||
|
|
||||||
const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args);
|
const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args);
|
||||||
|
|
||||||
@@ -334,7 +345,7 @@ class Logger : public Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update buffer_at with the formatted length (handle truncation)
|
// Update buffer_at with the formatted length (handle truncation)
|
||||||
int formatted_len = (ret >= remaining) ? remaining : ret;
|
uint16_t formatted_len = (ret >= remaining) ? remaining : ret;
|
||||||
*buffer_at += formatted_len;
|
*buffer_at += formatted_len;
|
||||||
|
|
||||||
// Remove all trailing newlines right after formatting
|
// Remove all trailing newlines right after formatting
|
||||||
@@ -343,18 +354,18 @@ class Logger : public Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void HOT write_footer_to_buffer_(char *buffer, int *buffer_at, int buffer_size) {
|
inline void HOT write_footer_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
|
||||||
static const int RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR);
|
static const uint16_t RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR);
|
||||||
this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
|
this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
class LoggerMessageTrigger : public Trigger<int, const char *, const char *> {
|
class LoggerMessageTrigger : public Trigger<uint8_t, const char *, const char *> {
|
||||||
public:
|
public:
|
||||||
explicit LoggerMessageTrigger(Logger *parent, int level) {
|
explicit LoggerMessageTrigger(Logger *parent, uint8_t level) {
|
||||||
this->level_ = level;
|
this->level_ = level;
|
||||||
parent->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
parent->add_on_log_callback([this](uint8_t level, const char *tag, const char *message) {
|
||||||
if (level <= this->level_) {
|
if (level <= this->level_) {
|
||||||
this->trigger(level, tag, message);
|
this->trigger(level, tag, message);
|
||||||
}
|
}
|
||||||
@@ -362,7 +373,7 @@ class LoggerMessageTrigger : public Trigger<int, const char *, const char *> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int level_;
|
uint8_t level_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace logger
|
} // namespace logger
|
||||||
|
@@ -454,9 +454,13 @@ def container_validator(schema, widget_type: WidgetType):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def validator(value):
|
def validator(value):
|
||||||
result = schema
|
|
||||||
if w_sch := widget_type.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
|
ltype = df.TYPE_NONE
|
||||||
if value and (layout := value.get(df.CONF_LAYOUT)):
|
if value and (layout := value.get(df.CONF_LAYOUT)):
|
||||||
if not isinstance(layout, dict):
|
if not isinstance(layout, dict):
|
||||||
|
@@ -3,7 +3,6 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
from esphome.core import ID
|
from esphome.core import ID
|
||||||
from esphome.cpp_generator import MockObj
|
|
||||||
|
|
||||||
from .defines import (
|
from .defines import (
|
||||||
CONF_STYLE_DEFINITIONS,
|
CONF_STYLE_DEFINITIONS,
|
||||||
@@ -13,12 +12,13 @@ from .defines import (
|
|||||||
literal,
|
literal,
|
||||||
)
|
)
|
||||||
from .helpers import add_lv_use
|
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 .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 (
|
from .widgets import (
|
||||||
Widget,
|
Widget,
|
||||||
add_widgets,
|
add_widgets,
|
||||||
|
collect_parts,
|
||||||
set_obj_properties,
|
set_obj_properties,
|
||||||
theme_widget_map,
|
theme_widget_map,
|
||||||
wait_for_widgets,
|
wait_for_widgets,
|
||||||
@@ -37,12 +37,18 @@ async def style_set(svar, style):
|
|||||||
lv.call(f"style_set_{remapped_prop}", svar, literal(value))
|
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):
|
async def styles_to_code(config):
|
||||||
"""Convert styles to C__ code."""
|
"""Convert styles to C__ code."""
|
||||||
for style in config.get(CONF_STYLE_DEFINITIONS, ()):
|
for style in config.get(CONF_STYLE_DEFINITIONS, ()):
|
||||||
svar = cg.new_Pvariable(style[CONF_ID])
|
await create_style(style, style[CONF_ID].id)
|
||||||
lv.style_init(svar)
|
|
||||||
await style_set(svar, style)
|
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
@@ -68,16 +74,18 @@ async def theme_to_code(config):
|
|||||||
if theme := config.get(CONF_THEME):
|
if theme := config.get(CONF_THEME):
|
||||||
add_lv_use(CONF_THEME)
|
add_lv_use(CONF_THEME)
|
||||||
for w_name, style in theme.items():
|
for w_name, style in theme.items():
|
||||||
if not isinstance(style, dict):
|
# Work around Python 3.10 bug with nested async comprehensions
|
||||||
continue
|
# With Python 3.11 this could be simplified
|
||||||
|
styles = {}
|
||||||
lname = "lv_theme_apply_" + w_name
|
for part, states in collect_parts(style).items():
|
||||||
apply = lv_variable(lv_lambda_t, lname)
|
styles[part] = {
|
||||||
theme_widget_map[w_name] = apply
|
state: await create_style(
|
||||||
ow = Widget.create("obj", MockObj(ID("obj")), obj_spec)
|
props,
|
||||||
async with LambdaContext([(lv_obj_t_ptr, "obj")], where=w_name) as context:
|
"_lv_theme_style_" + w_name + "_" + part + "_" + state,
|
||||||
await set_obj_properties(ow, style)
|
)
|
||||||
lv_assign(apply, await context.get_lambda())
|
for state, props in states.items()
|
||||||
|
}
|
||||||
|
theme_widget_map[w_name] = styles
|
||||||
|
|
||||||
|
|
||||||
async def add_top_layer(lv_component, config):
|
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.const import CONF_DEFAULT, CONF_GROUP, CONF_ID, CONF_STATE, CONF_TYPE
|
||||||
from esphome.core import ID, TimePeriod
|
from esphome.core import ID, TimePeriod
|
||||||
from esphome.coroutine import FakeAwaitable
|
from esphome.coroutine import FakeAwaitable
|
||||||
from esphome.cpp_generator import CallExpression, MockObj
|
from esphome.cpp_generator import MockObj
|
||||||
|
|
||||||
from ..defines import (
|
from ..defines import (
|
||||||
CONF_FLEX_ALIGN_CROSS,
|
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)
|
w = Widget.create(wid, var, spec, w_cnfig)
|
||||||
if theme := theme_widget_map.get(w_type):
|
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 set_obj_properties(w, w_cnfig)
|
||||||
await add_widgets(w, w_cnfig)
|
await add_widgets(w, w_cnfig)
|
||||||
await spec.to_code(w, w_cnfig)
|
await spec.to_code(w, w_cnfig)
|
||||||
|
@@ -1,8 +1,15 @@
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, CONF_VALUE
|
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 ..defines import (
|
||||||
from ..lv_validation import animated, get_start_value, lv_float
|
BAR_MODES,
|
||||||
|
CONF_ANIMATED,
|
||||||
|
CONF_INDICATOR,
|
||||||
|
CONF_MAIN,
|
||||||
|
CONF_START_VALUE,
|
||||||
|
literal,
|
||||||
|
)
|
||||||
|
from ..lv_validation import animated, lv_int
|
||||||
from ..lvcode import lv
|
from ..lvcode import lv
|
||||||
from ..types import LvNumber, NumberType
|
from ..types import LvNumber, NumberType
|
||||||
from . import Widget
|
from . import Widget
|
||||||
@@ -10,22 +17,30 @@ from . import Widget
|
|||||||
# Note this file cannot be called "bar.py" because that name is disallowed.
|
# Note this file cannot be called "bar.py" because that name is disallowed.
|
||||||
|
|
||||||
CONF_BAR = "bar"
|
CONF_BAR = "bar"
|
||||||
BAR_MODIFY_SCHEMA = cv.Schema(
|
|
||||||
{
|
|
||||||
cv.Optional(CONF_VALUE): lv_float,
|
def validate_bar(config):
|
||||||
cv.Optional(CONF_ANIMATED, default=True): animated,
|
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(
|
BAR_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_VALUE): lv_float,
|
cv.Optional(CONF_VALUE): lv_int,
|
||||||
cv.Optional(CONF_MIN_VALUE, default=0): cv.int_,
|
cv.Optional(CONF_START_VALUE): lv_int,
|
||||||
cv.Optional(CONF_MAX_VALUE, default=100): cv.int_,
|
cv.Optional(CONF_MIN_VALUE): lv_int,
|
||||||
cv.Optional(CONF_MODE, default="NORMAL"): BAR_MODES.one_of,
|
cv.Optional(CONF_MAX_VALUE): lv_int,
|
||||||
|
cv.Optional(CONF_MODE): BAR_MODES.one_of,
|
||||||
cv.Optional(CONF_ANIMATED, default=True): animated,
|
cv.Optional(CONF_ANIMATED, default=True): animated,
|
||||||
}
|
}
|
||||||
)
|
).add_extra(validate_bar)
|
||||||
|
|
||||||
|
|
||||||
class BarType(NumberType):
|
class BarType(NumberType):
|
||||||
@@ -35,17 +50,23 @@ class BarType(NumberType):
|
|||||||
LvNumber("lv_bar_t"),
|
LvNumber("lv_bar_t"),
|
||||||
parts=(CONF_MAIN, CONF_INDICATOR),
|
parts=(CONF_MAIN, CONF_INDICATOR),
|
||||||
schema=BAR_SCHEMA,
|
schema=BAR_SCHEMA,
|
||||||
modify_schema=BAR_MODIFY_SCHEMA,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def to_code(self, w: Widget, config):
|
async def to_code(self, w: Widget, config):
|
||||||
var = w.obj
|
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:
|
if CONF_MIN_VALUE in config:
|
||||||
lv.bar_set_range(var, config[CONF_MIN_VALUE], config[CONF_MAX_VALUE])
|
lv.bar_set_range(
|
||||||
lv.bar_set_mode(var, literal(config[CONF_MODE]))
|
var,
|
||||||
value = await get_start_value(config)
|
await lv_int.process(config[CONF_MIN_VALUE]),
|
||||||
if value is not None:
|
await lv_int.process(config[CONF_MAX_VALUE]),
|
||||||
lv.bar_set_value(var, value, literal(config[CONF_ANIMATED]))
|
)
|
||||||
|
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
|
@property
|
||||||
def animated(self):
|
def animated(self):
|
||||||
|
@@ -963,13 +963,12 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool
|
|||||||
uint16_t ret = 0;
|
uint16_t ret = 0;
|
||||||
uint8_t c = 0;
|
uint8_t c = 0;
|
||||||
uint8_t nr_of_ff_bytes = 0;
|
uint8_t nr_of_ff_bytes = 0;
|
||||||
uint64_t start;
|
|
||||||
bool exit_flag = false;
|
bool exit_flag = false;
|
||||||
bool ff_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()) {
|
if (!this->available()) {
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
delay(1);
|
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 = new nextion::NextionComponentBase;
|
||||||
nextion_queue->component->set_variable_name(variable_name);
|
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);
|
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_PANID", config[CONF_PAN_ID])
|
||||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", config[CONF_CHANNEL])
|
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", config[CONF_CHANNEL])
|
||||||
add_idf_sdkconfig_option(
|
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):
|
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:
|
if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None:
|
||||||
add_idf_sdkconfig_option(
|
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:
|
if (mesh_local_prefix := config.get(CONF_MESH_LOCAL_PREFIX)) is not None:
|
||||||
add_idf_sdkconfig_option(
|
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:
|
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 CONF_FORCE_DATASET in config:
|
||||||
if config[CONF_FORCE_DATASET]:
|
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_EXT_PAN_ID): cv.hex_int,
|
||||||
cv.Optional(CONF_NETWORK_NAME): cv.string_strict,
|
cv.Optional(CONF_NETWORK_NAME): cv.string_strict,
|
||||||
cv.Optional(CONF_PSKC): cv.hex_int,
|
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
|
// Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this
|
||||||
// component
|
// component
|
||||||
this->mdns_services_ = this->mdns_->get_services();
|
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_) {
|
for (const auto &service : this->mdns_services_) {
|
||||||
otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
|
otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
@@ -185,11 +185,11 @@ void OpenThreadSrpComponent::setup() {
|
|||||||
if (error != OT_ERROR_NONE) {
|
if (error != OT_ERROR_NONE) {
|
||||||
ESP_LOGW(TAG, "Failed to add service: %s", otThreadErrorToString(error));
|
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);
|
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) {
|
void *OpenThreadSrpComponent::pool_alloc_(size_t size) {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
# Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9
|
# Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9
|
||||||
import binascii
|
import binascii
|
||||||
|
import ipaddress
|
||||||
|
|
||||||
from esphome.const import CONF_CHANNEL
|
from esphome.const import CONF_CHANNEL
|
||||||
|
|
||||||
@@ -37,6 +38,12 @@ def parse_tlv(tlv) -> dict:
|
|||||||
if tag in TLV_TYPES:
|
if tag in TLV_TYPES:
|
||||||
if tag == 3:
|
if tag == 3:
|
||||||
output[TLV_TYPES[tag]] = val.decode("utf-8")
|
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:
|
else:
|
||||||
output[TLV_TYPES[tag]] = int.from_bytes(val)
|
output[TLV_TYPES[tag]] = int.from_bytes(val)
|
||||||
return output
|
return output
|
||||||
|
@@ -6,7 +6,7 @@ from esphome.components.esp32 import (
|
|||||||
only_on_variant,
|
only_on_variant,
|
||||||
)
|
)
|
||||||
import esphome.config_validation as cv
|
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
|
from esphome.cpp_types import Component
|
||||||
|
|
||||||
AUTO_LOAD = ["bytebuffer"]
|
AUTO_LOAD = ["bytebuffer"]
|
||||||
@@ -16,7 +16,6 @@ usb_host_ns = cg.esphome_ns.namespace("usb_host")
|
|||||||
USBHost = usb_host_ns.class_("USBHost", Component)
|
USBHost = usb_host_ns.class_("USBHost", Component)
|
||||||
USBClient = usb_host_ns.class_("USBClient", Component)
|
USBClient = usb_host_ns.class_("USBClient", Component)
|
||||||
|
|
||||||
CONF_DEVICES = "devices"
|
|
||||||
CONF_VID = "vid"
|
CONF_VID = "vid"
|
||||||
CONF_PID = "pid"
|
CONF_PID = "pid"
|
||||||
CONF_ENABLE_HUBS = "enable_hubs"
|
CONF_ENABLE_HUBS = "enable_hubs"
|
||||||
|
@@ -3,7 +3,15 @@
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
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 logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -1176,6 +1184,14 @@ def ipv4address(value):
|
|||||||
return address
|
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):
|
def ipv4address_multi_broadcast(value):
|
||||||
address = ipv4address(value)
|
address = ipv4address(value)
|
||||||
if not (address.is_multicast or (address == IPv4Address("255.255.255.255"))):
|
if not (address.is_multicast or (address == IPv4Address("255.255.255.255"))):
|
||||||
@@ -1193,6 +1209,33 @@ def ipaddress(value):
|
|||||||
return address
|
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):
|
def _valid_topic(value):
|
||||||
"""Validate that this is a valid topic name/filter."""
|
"""Validate that this is a valid topic name/filter."""
|
||||||
if value is None: # Used to disable publishing and subscribing
|
if value is None: # Used to disable publishing and subscribing
|
||||||
|
@@ -217,6 +217,7 @@ CONF_DEST = "dest"
|
|||||||
CONF_DEVICE = "device"
|
CONF_DEVICE = "device"
|
||||||
CONF_DEVICE_CLASS = "device_class"
|
CONF_DEVICE_CLASS = "device_class"
|
||||||
CONF_DEVICE_FACTOR = "device_factor"
|
CONF_DEVICE_FACTOR = "device_factor"
|
||||||
|
CONF_DEVICES = "devices"
|
||||||
CONF_DIELECTRIC_CONSTANT = "dielectric_constant"
|
CONF_DIELECTRIC_CONSTANT = "dielectric_constant"
|
||||||
CONF_DIMENSIONS = "dimensions"
|
CONF_DIMENSIONS = "dimensions"
|
||||||
CONF_DIO_PIN = "dio_pin"
|
CONF_DIO_PIN = "dio_pin"
|
||||||
|
@@ -257,6 +257,17 @@ void Application::teardown_components(uint32_t timeout_ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Application::calculate_looping_components_() {
|
void Application::calculate_looping_components_() {
|
||||||
|
// Count total components that need looping
|
||||||
|
size_t total_looping = 0;
|
||||||
|
for (auto *obj : this->components_) {
|
||||||
|
if (obj->has_overridden_loop()) {
|
||||||
|
total_looping++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-reserve vector to avoid reallocations
|
||||||
|
this->looping_components_.reserve(total_looping);
|
||||||
|
|
||||||
// First add all active components
|
// First add all active components
|
||||||
for (auto *obj : this->components_) {
|
for (auto *obj : this->components_) {
|
||||||
if (obj->has_overridden_loop() &&
|
if (obj->has_overridden_loop() &&
|
||||||
|
@@ -29,7 +29,7 @@ void HOT esp_log_vprintf_(int level, const char *tag, int line, const char *form
|
|||||||
if (log == nullptr)
|
if (log == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
log->log_vprintf_(level, tag, line, format, args);
|
log->log_vprintf_(static_cast<uint8_t>(level), tag, line, format, args);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ void HOT esp_log_vprintf_(int level, const char *tag, int line, const __FlashStr
|
|||||||
if (log == nullptr)
|
if (log == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
log->log_vprintf_(level, tag, line, format, args);
|
log->log_vprintf_(static_cast<uint8_t>(level), tag, line, format, args);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -319,13 +319,17 @@ bool HOT Scheduler::cancel_item_(Component *component, const std::string &name,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
uint64_t Scheduler::millis_() {
|
uint64_t Scheduler::millis_() {
|
||||||
|
// Get the current 32-bit millis value
|
||||||
const uint32_t now = millis();
|
const uint32_t now = millis();
|
||||||
|
// Check for rollover by comparing with last value
|
||||||
if (now < this->last_millis_) {
|
if (now < this->last_millis_) {
|
||||||
|
// Detected rollover (happens every ~49.7 days)
|
||||||
this->millis_major_++;
|
this->millis_major_++;
|
||||||
ESP_LOGD(TAG, "Incrementing scheduler major at %" PRIu64 "ms",
|
ESP_LOGD(TAG, "Incrementing scheduler major at %" PRIu64 "ms",
|
||||||
now + (static_cast<uint64_t>(this->millis_major_) << 32));
|
now + (static_cast<uint64_t>(this->millis_major_) << 32));
|
||||||
}
|
}
|
||||||
this->last_millis_ = now;
|
this->last_millis_ = now;
|
||||||
|
// Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
|
||||||
return now + (static_cast<uint64_t>(this->millis_major_) << 32);
|
return now + (static_cast<uint64_t>(this->millis_major_) << 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,12 +29,16 @@ class Scheduler {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct SchedulerItem {
|
struct SchedulerItem {
|
||||||
|
// Ordered by size to minimize padding
|
||||||
Component *component;
|
Component *component;
|
||||||
std::string name;
|
|
||||||
enum Type { TIMEOUT, INTERVAL } type;
|
|
||||||
uint32_t interval;
|
uint32_t interval;
|
||||||
|
// 64-bit time to handle millis() rollover. The scheduler combines the 32-bit millis()
|
||||||
|
// with a 16-bit rollover counter to create a 64-bit time that won't roll over for
|
||||||
|
// billions of years. This ensures correct scheduling even when devices run for months.
|
||||||
uint64_t next_execution_;
|
uint64_t next_execution_;
|
||||||
|
std::string name;
|
||||||
std::function<void()> callback;
|
std::function<void()> callback;
|
||||||
|
enum Type : uint8_t { TIMEOUT, INTERVAL } type;
|
||||||
bool remove;
|
bool remove;
|
||||||
|
|
||||||
static bool cmp(const std::unique_ptr<SchedulerItem> &a, const std::unique_ptr<SchedulerItem> &b);
|
static bool cmp(const std::unique_ptr<SchedulerItem> &a, const std::unique_ptr<SchedulerItem> &b);
|
||||||
|
@@ -5,7 +5,7 @@ import fnmatch
|
|||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
from io import BytesIO, TextIOBase, TextIOWrapper
|
from io import BytesIO, TextIOBase, TextIOWrapper
|
||||||
from ipaddress import _BaseAddress
|
from ipaddress import _BaseAddress, _BaseNetwork
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
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(int, ESPHomeDumper.represent_int)
|
||||||
ESPHomeDumper.add_multi_representer(float, ESPHomeDumper.represent_float)
|
ESPHomeDumper.add_multi_representer(float, ESPHomeDumper.represent_float)
|
||||||
ESPHomeDumper.add_multi_representer(_BaseAddress, ESPHomeDumper.represent_stringify)
|
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(MACAddress, ESPHomeDumper.represent_stringify)
|
||||||
ESPHomeDumper.add_multi_representer(TimePeriod, ESPHomeDumper.represent_stringify)
|
ESPHomeDumper.add_multi_representer(TimePeriod, ESPHomeDumper.represent_stringify)
|
||||||
ESPHomeDumper.add_multi_representer(Lambda, ESPHomeDumper.represent_lambda)
|
ESPHomeDumper.add_multi_representer(Lambda, ESPHomeDumper.represent_lambda)
|
||||||
|
@@ -728,12 +728,15 @@ lvgl:
|
|||||||
value: 30
|
value: 30
|
||||||
max_value: 100
|
max_value: 100
|
||||||
min_value: 10
|
min_value: 10
|
||||||
|
start_value: 20
|
||||||
mode: range
|
mode: range
|
||||||
on_click:
|
on_click:
|
||||||
then:
|
then:
|
||||||
- lvgl.bar.update:
|
- lvgl.bar.update:
|
||||||
id: bar_id
|
id: bar_id
|
||||||
value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
||||||
|
start_value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
||||||
|
mode: symmetrical
|
||||||
- logger.log:
|
- logger.log:
|
||||||
format: "bar value %f"
|
format: "bar value %f"
|
||||||
args: [x]
|
args: [x]
|
||||||
|
@@ -8,4 +8,6 @@ openthread:
|
|||||||
pan_id: 0x8f28
|
pan_id: 0x8f28
|
||||||
ext_pan_id: 0xd63e8e3e495ebbc3
|
ext_pan_id: 0xd63e8e3e495ebbc3
|
||||||
pskc: 0xc23a76e98f1a6483639b1ac1271e2e27
|
pskc: 0xc23a76e98f1a6483639b1ac1271e2e27
|
||||||
|
mesh_local_prefix: fd53:145f:ed22:ad81::/64
|
||||||
force_dataset: true
|
force_dataset: true
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user