Merge branch 'request_log_listener' into integration

This commit is contained in:
J. Nick Koston
2026-01-13 14:00:40 -10:00
11 changed files with 54 additions and 7 deletions

View File

@@ -4,6 +4,7 @@ import logging
from esphome import automation
from esphome.automation import Condition
import esphome.codegen as cg
from esphome.components.logger import request_log_listener
from esphome.config_helpers import get_logger_level
import esphome.config_validation as cv
from esphome.const import (
@@ -326,6 +327,9 @@ async def to_code(config: ConfigType) -> None:
# Track controller registration for StaticVector sizing
CORE.register_controller()
# Request a log listener slot for API log streaming
request_log_listener()
cg.add(var.set_port(config[CONF_PORT]))
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))

View File

@@ -1,4 +1,5 @@
import esphome.codegen as cg
from esphome.components.logger import request_log_listener
from esphome.components.zephyr import zephyr_add_prj_conf
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_LOGS, CONF_TYPE
@@ -25,5 +26,8 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
zephyr_add_prj_conf("BT_NUS", True)
cg.add(var.set_expose_log(config[CONF_TYPE] == CONF_LOGS))
expose_log = config[CONF_TYPE] == CONF_LOGS
cg.add(var.set_expose_log(expose_log))
if expose_log:
request_log_listener() # Request a log listener slot for BLE NUS log streaming
await cg.register_component(var, config)

View File

@@ -421,6 +421,7 @@ async def to_code(config):
await cg.register_component(log, config)
for conf in config.get(CONF_ON_MESSAGE, []):
request_log_listener() # Each on_message trigger needs a listener slot
trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], log, LOG_LEVEL_SEVERITY.index(conf[CONF_LEVEL])
)
@@ -546,6 +547,7 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
# Keys for CORE.data storage
DOMAIN = "logger"
KEY_LEVEL_LISTENERS = "level_listeners"
KEY_LOG_LISTENERS = "log_listeners"
def request_logger_level_listeners() -> None:
@@ -558,8 +560,24 @@ def request_logger_level_listeners() -> None:
CORE.data.setdefault(DOMAIN, {})[KEY_LEVEL_LISTENERS] = True
def request_log_listener() -> None:
"""Request a log listener slot.
Components that need to receive log messages should call this function
during their code generation. This increments the listener count used
to size the StaticVector.
"""
data = CORE.data.setdefault(DOMAIN, {})
data[KEY_LOG_LISTENERS] = data.get(KEY_LOG_LISTENERS, 0) + 1
@coroutine_with_priority(CoroPriority.FINAL)
async def final_step():
"""Final code generation step to configure optional logger features."""
if CORE.data.get(DOMAIN, {}).get(KEY_LEVEL_LISTENERS, False):
domain_data = CORE.data.get(DOMAIN, {})
if domain_data.get(KEY_LEVEL_LISTENERS, False):
cg.add_define("USE_LOGGER_LEVEL_LISTENERS")
# Set exact count of log listeners - runtime will error if exceeded
log_listener_count = domain_data.get(KEY_LOG_LISTENERS, 0)
cg.add_define("ESPHOME_LOG_MAX_LISTENERS", log_listener_count)

View File

@@ -394,7 +394,8 @@ class Logger : public Component {
#ifdef USE_LOGGER_RUNTIME_TAG_LEVELS
std::map<const char *, uint8_t, CStrCompare> log_levels_{};
#endif
std::vector<LogListener *> log_listeners_; // Log message listeners (API, MQTT, syslog, etc.)
StaticVector<LogListener *, ESPHOME_LOG_MAX_LISTENERS>
log_listeners_; // Log message listeners (API, MQTT, syslog, etc.)
#ifdef USE_LOGGER_LEVEL_LISTENERS
std::vector<LoggerLevelListener *> level_listeners_; // Log level change listeners
#endif

View File

@@ -350,6 +350,10 @@ def exp_mqtt_message(config):
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
# Request a log listener slot for MQTT log streaming
logger.request_log_listener()
# Add required libraries for ESP8266 and LibreTiny
if CORE.is_esp8266 or CORE.is_libretiny:
# https://github.com/heman/async-mqtt-client/blob/master/library.json

View File

@@ -32,6 +32,8 @@ static const uint8_t SSD1306_COMMAND_PAGE_ADDRESS = 0x22;
static const uint8_t SSD1306_COMMAND_NORMAL_DISPLAY = 0xA6;
static const uint8_t SSD1306_COMMAND_INVERSE_DISPLAY = 0xA7;
static const uint8_t SSD1306B_COMMAND_SELECT_IREF = 0xAD;
static const uint8_t SSD1305_COMMAND_SET_BRIGHTNESS = 0x82;
static const uint8_t SSD1305_COMMAND_SET_AREA_COLOR = 0xD8;
@@ -95,6 +97,12 @@ void SSD1306::setup() {
this->command(0x8B);
}
} else {
if (this->is_ssd1306b_()) {
// Select external or internal Iref (0xAD)
this->command(SSD1306B_COMMAND_SELECT_IREF);
// Enable internal Iref and change from 19ua (POR) to 30uA
this->command(0x20 | 0x10);
}
// Enable charge pump (0x8D)
this->command(SSD1306_COMMAND_CHARGE_PUMP);
if (this->external_vcc_) {
@@ -226,6 +234,8 @@ bool SSD1306::is_sh1107_() const { return this->model_ == SH1107_MODEL_128_64 ||
bool SSD1306::is_ssd1305_() const {
return this->model_ == SSD1305_MODEL_128_64 || this->model_ == SSD1305_MODEL_128_32;
}
bool SSD1306::is_ssd1306b_() const { return this->model_ == SSD1306_MODEL_72_40; }
void SSD1306::update() {
this->do_update_();
this->display();

View File

@@ -63,6 +63,7 @@ class SSD1306 : public display::DisplayBuffer {
bool is_sh1106_() const;
bool is_sh1107_() const;
bool is_ssd1305_() const;
bool is_ssd1306b_() const;
void draw_absolute_pixel_internal(int x, int y, Color color) override;

View File

@@ -62,9 +62,9 @@ void HOT I2CSSD1306::write_display_data() {
}
} else {
size_t block_size = 16;
if ((this->get_buffer_length_() & 8) == 8) {
// use smaller block size for e.g. 72x40 displays where buffer size is multiple of 8, not 16
block_size = 8;
if ((this->get_buffer_length_() % 24) == 0) {
// use 24 byte block size for e.g. 72x40 displays where buffer size is multiple of 24, not 16
block_size = 24;
}
for (uint32_t i = 0; i < this->get_buffer_length_();) {

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg
from esphome.components import udp
from esphome.components.logger import LOG_LEVELS, is_log_level
from esphome.components.logger import LOG_LEVELS, is_log_level, request_log_listener
from esphome.components.time import RealTimeClock
from esphome.components.udp import CONF_UDP_ID
import esphome.config_validation as cv
@@ -36,6 +36,7 @@ async def to_code(config):
level = LOG_LEVELS[config[CONF_LEVEL]]
var = cg.new_Pvariable(config[CONF_ID], level, time)
await cg.register_component(var, config)
request_log_listener() # Request a log listener slot for syslog
await cg.register_parented(var, parent)
cg.add(var.set_strip(config[CONF_STRIP]))
cg.add(var.set_facility(config[CONF_FACILITY]))

View File

@@ -4,6 +4,7 @@ import gzip
import esphome.codegen as cg
from esphome.components import web_server_base
from esphome.components.logger import request_log_listener
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
import esphome.config_validation as cv
from esphome.const import (
@@ -313,6 +314,8 @@ async def to_code(config):
if config.get(CONF_OTA) is False:
cg.add_define("USE_WEBSERVER_OTA_DISABLED")
cg.add(var.set_expose_log(config[CONF_LOG]))
if config[CONF_LOG]:
request_log_listener() # Request a log listener slot for web server log streaming
if config[CONF_ENABLE_PRIVATE_NETWORK_ACCESS]:
cg.add_define("USE_WEBSERVER_PRIVATE_NETWORK_ACCESS")
if CONF_AUTH in config:

View File

@@ -20,6 +20,7 @@
// logger
#define ESPHOME_LOG_LEVEL ESPHOME_LOG_LEVEL_VERY_VERBOSE
#define ESPHOME_LOG_MAX_LISTENERS 8
// Feature flags
#define USE_ALARM_CONTROL_PANEL