mirror of
https://github.com/esphome/esphome.git
synced 2025-09-21 04:32:23 +01:00
Merge remote-tracking branch 'upstream/logger_disable_loop' into integration
This commit is contained in:
@@ -522,6 +522,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
|
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:
|
cg.StructInitializer(
|
||||||
glyph_initializer.append(
|
GlyphData,
|
||||||
cg.StructInitializer(
|
(
|
||||||
GlyphData,
|
"a_char",
|
||||||
(
|
cg.RawExpression(f"(const uint8_t *){cpp_string_escape(x.glyph)}"),
|
||||||
"a_char",
|
),
|
||||||
cg.RawExpression(
|
(
|
||||||
f"(const uint8_t *){cpp_string_escape(codepoint)}"
|
"data",
|
||||||
),
|
cg.RawExpression(f"{str(prog_arr)} + {str(y - len(x.bitmap_data))}"),
|
||||||
),
|
),
|
||||||
(
|
("advance", x.advance),
|
||||||
"data",
|
("offset_x", x.offset_x),
|
||||||
cg.RawExpression(
|
("offset_y", x.offset_y),
|
||||||
f"{str(prog_arr)} + {str(glyph_args[codepoint].data_len)}"
|
("width", x.width),
|
||||||
),
|
("height", x.height),
|
||||||
),
|
|
||||||
("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),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -46,8 +46,8 @@ void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const ch
|
|||||||
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);
|
||||||
if (message_sent) {
|
if (message_sent) {
|
||||||
// Enable logger loop to process the buffered message
|
// Enable logger loop to process the buffered message
|
||||||
// This is safe to call from any context including ISRs
|
// 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
|
#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() {
|
void Logger::loop() {
|
||||||
#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
|
#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
|
||||||
if (this->uart_ == UART_SELECTION_USB_CDC) {
|
if (this->uart_ == UART_SELECTION_USB_CDC) {
|
||||||
|
@@ -107,7 +107,7 @@ class Logger : public Component {
|
|||||||
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
||||||
void init_log_buffer(size_t total_buffer_size);
|
void init_log_buffer(size_t total_buffer_size);
|
||||||
#endif
|
#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;
|
void loop() override;
|
||||||
#endif
|
#endif
|
||||||
/// Manually set the baud rate for serial, set to 0 to disable.
|
/// 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);
|
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)
|
// Disable loop when task buffer is empty (with USB CDC check)
|
||||||
inline void disable_loop_when_buffer_empty_() {
|
inline void disable_loop_when_buffer_empty_() {
|
||||||
// Thread safety note: This is safe even if another task calls enable_loop_soon_any_context()
|
// Thread safety note: This is safe even if another task calls enable_loop_soon_any_context()
|
||||||
|
@@ -454,9 +454,13 @@ def container_validator(schema, widget_type: WidgetType):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def validator(value):
|
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"
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
// logger
|
// logger
|
||||||
#define ESPHOME_LOG_LEVEL ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
#define ESPHOME_LOG_LEVEL ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||||
#define USE_ESPHOME_TASK_LOG_BUFFER
|
|
||||||
|
|
||||||
// Feature flags
|
// Feature flags
|
||||||
#define USE_ALARM_CONTROL_PANEL
|
#define USE_ALARM_CONTROL_PANEL
|
||||||
@@ -131,6 +130,8 @@
|
|||||||
|
|
||||||
// ESP32-specific feature flags
|
// ESP32-specific feature flags
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
#define USE_ESPHOME_TASK_LOG_BUFFER
|
||||||
|
|
||||||
#define USE_BLUETOOTH_PROXY
|
#define USE_BLUETOOTH_PROXY
|
||||||
#define USE_CAPTIVE_PORTAL
|
#define USE_CAPTIVE_PORTAL
|
||||||
#define USE_ESP32_BLE
|
#define USE_ESP32_BLE
|
||||||
|
@@ -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