Merge remote-tracking branch 'upstream/get_peername_stack_save_ram' into get_peername_stack_save_ram

This commit is contained in:
J. Nick Koston
2026-01-03 22:01:59 -10:00
73 changed files with 1017 additions and 281 deletions
@@ -90,13 +90,16 @@ void AbsoluteHumidityComponent::loop() {
this->status_set_error(LOG_STR("Invalid saturation vapor pressure equation selection!"));
return;
}
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
// Calculate absolute humidity
const float absolute_humidity = vapor_density(es, hr, temperature_k);
ESP_LOGD(TAG,
"Saturation vapor pressure %f kPa\n"
"Publishing absolute humidity %f g/m³",
es, absolute_humidity);
// Publish absolute humidity
ESP_LOGD(TAG, "Publishing absolute humidity %f g/m³", absolute_humidity);
this->status_clear_warning();
this->publish_state(absolute_humidity);
}
+3 -3
View File
@@ -211,13 +211,13 @@ void AcDimmer::write_state(float state) {
this->store_.value = new_value;
}
void AcDimmer::dump_config() {
ESP_LOGCONFIG(TAG, "AcDimmer:");
LOG_PIN(" Output Pin: ", this->gate_pin_);
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
ESP_LOGCONFIG(TAG,
"AcDimmer:\n"
" Min Power: %.1f%%\n"
" Init with half cycle: %s",
this->store_.min_power / 10.0f, YESNO(this->init_with_half_cycle_));
LOG_PIN(" Output Pin: ", this->gate_pin_);
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
if (method_ == DIM_METHOD_LEADING_PULSE) {
ESP_LOGCONFIG(TAG, " Method: leading pulse");
} else if (method_ == DIM_METHOD_LEADING) {
+7
View File
@@ -3,6 +3,7 @@ import esphome.codegen as cg
from esphome.components import output
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_METHOD, CONF_MIN_POWER
from esphome.core import CORE
CODEOWNERS = ["@glmnet"]
@@ -36,6 +37,12 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
if CORE.is_esp8266:
# ac_dimmer uses setTimer1Callback which requires the waveform generator
from esphome.components.esp8266.const import require_waveform
require_waveform()
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
+8 -10
View File
@@ -121,23 +121,21 @@ void ADCSensor::setup() {
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG,
" Channel: %d\n"
" Unit: %s\n"
" Attenuation: %s\n"
" Samples: %i\n"
" Sampling mode: %s",
this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)),
this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_,
LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(
TAG,
" Channel: %d\n"
" Unit: %s\n"
" Attenuation: %s\n"
" Samples: %i\n"
" Sampling mode: %s\n"
" Setup Status:\n"
" Handle Init: %s\n"
" Config: %s\n"
" Calibration: %s\n"
" Overall Init: %s",
this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)),
this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_,
LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)),
this->setup_flags_.handle_init_complete ? "OK" : "FAILED", this->setup_flags_.config_complete ? "OK" : "FAILED",
this->setup_flags_.calibration_complete ? "OK" : "FAILED", this->setup_flags_.init_complete ? "OK" : "FAILED");
+4 -2
View File
@@ -162,11 +162,13 @@ void ADE7880::update() {
}
void ADE7880::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7880:");
ESP_LOGCONFIG(TAG,
"ADE7880:\n"
" Frequency: %.0f Hz",
this->frequency_);
LOG_PIN(" IRQ0 Pin: ", this->irq0_pin_);
LOG_PIN(" IRQ1 Pin: ", this->irq1_pin_);
LOG_PIN(" RESET Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
if (this->channel_a_ != nullptr) {
ESP_LOGCONFIG(TAG, " Phase A:");
@@ -21,10 +21,12 @@ void ADS1115Sensor::update() {
void ADS1115Sensor::dump_config() {
LOG_SENSOR(" ", "ADS1115 Sensor", this);
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
ESP_LOGCONFIG(TAG, " Sample rate: %u", this->samplerate_);
ESP_LOGCONFIG(TAG,
" Multiplexer: %u\n"
" Gain: %u\n"
" Resolution: %u\n"
" Sample rate: %u",
this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
}
} // namespace ads1115
@@ -9,8 +9,10 @@ static const char *const TAG = "ads1118.sensor";
void ADS1118Sensor::dump_config() {
LOG_SENSOR(" ", "ADS1118 Sensor", this);
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
ESP_LOGCONFIG(TAG,
" Multiplexer: %u\n"
" Gain: %u",
this->multiplexer_, this->gain_);
}
float ADS1118Sensor::sample() {
+4 -2
View File
@@ -67,8 +67,10 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
ESP_LOGW(TAG,
"[%s] No control service found at device, not an Anova..?\n"
"[%s] Note, this component does not currently support Anova Nano.",
this->get_name().c_str(), this->get_name().c_str());
break;
}
this->char_handle_ = chr->handle;
+2 -3
View File
@@ -16,7 +16,7 @@ with warnings.catch_warnings():
import contextlib
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
from esphome.const import CONF_KEY, CONF_PORT, __version__
from esphome.core import CORE
from . import CONF_ENCRYPTION
@@ -35,7 +35,6 @@ async def async_run_logs(config: dict[str, Any], addresses: list[str]) -> None:
conf = config["api"]
name = config["esphome"]["name"]
port: int = int(conf[CONF_PORT])
password: str = conf[CONF_PASSWORD]
noise_psk: str | None = None
if (encryption := conf.get(CONF_ENCRYPTION)) and (key := encryption.get(CONF_KEY)):
noise_psk = key
@@ -50,7 +49,7 @@ async def async_run_logs(config: dict[str, Any], addresses: list[str]) -> None:
cli = APIClient(
addresses[0], # Primary address for compatibility
port,
password,
"", # Password auth removed in 2026.1.0
client_info=f"ESPHome Logs {__version__}",
noise_psk=noise_psk,
addresses=addresses, # Pass all addresses for automatic retry
+6 -4
View File
@@ -305,12 +305,14 @@ bool AS3935Component::calibrate_oscillator() {
}
void AS3935Component::tune_antenna() {
ESP_LOGI(TAG, "Starting antenna tuning");
uint8_t div_ratio = this->read_div_ratio();
uint8_t tune_val = this->read_capacitance();
ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio);
ESP_LOGI(TAG, "Internal Capacitor is set to: %d", tune_val);
ESP_LOGI(TAG, "Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio");
ESP_LOGI(TAG,
"Starting antenna tuning\n"
"Division Ratio is set to: %d\n"
"Internal Capacitor is set to: %d\n"
"Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio",
div_ratio, tune_val);
this->display_oscillator(true, ANTFREQ);
}
+8 -5
View File
@@ -216,11 +216,14 @@ bool BedJetHub::discover_characteristics_() {
}
}
ESP_LOGI(TAG, "[%s] Discovered service characteristics: ", this->get_name().c_str());
ESP_LOGI(TAG, " - Command char: 0x%x", this->char_handle_cmd_);
ESP_LOGI(TAG, " - Status char: 0x%x", this->char_handle_status_);
ESP_LOGI(TAG, " - config descriptor: 0x%x", this->config_descr_status_);
ESP_LOGI(TAG, " - Name char: 0x%x", this->char_handle_name_);
ESP_LOGI(TAG,
"[%s] Discovered service characteristics:\n"
" - Command char: 0x%x\n"
" - Status char: 0x%x\n"
" - config descriptor: 0x%x\n"
" - Name char: 0x%x",
this->get_name().c_str(), this->char_handle_cmd_, this->char_handle_status_, this->config_descr_status_,
this->char_handle_name_);
return result;
}
@@ -21,8 +21,10 @@ void MultiClickTrigger::on_state_(bool state) {
// Start matching
MultiClickTriggerEvent evt = this->timing_[0];
if (evt.state == state) {
ESP_LOGV(TAG, "START min=%" PRIu32 " max=%" PRIu32, evt.min_length, evt.max_length);
ESP_LOGV(TAG, "Multi Click: Starting multi click action!");
ESP_LOGV(TAG,
"START min=%" PRIu32 " max=%" PRIu32 "\n"
"Multi Click: Starting multi click action!",
evt.min_length, evt.max_length);
this->at_index_ = 1;
if (this->timing_.size() == 1 && evt.max_length == 4294967294UL) {
this->set_timeout("trigger", evt.min_length, [this]() { this->trigger_(); });
+4 -2
View File
@@ -103,8 +103,10 @@ void BLENUS::on_log(uint8_t level, const char *tag, const char *message, size_t
#endif
void BLENUS::dump_config() {
ESP_LOGCONFIG(TAG, "ble nus:");
ESP_LOGCONFIG(TAG, " log: %s", YESNO(this->expose_log_));
ESP_LOGCONFIG(TAG,
"ble nus:\n"
" log: %s",
YESNO(this->expose_log_));
uint32_t mtu = 0;
bt_conn *conn = this->conn_.load();
if (conn) {
+3 -3
View File
@@ -22,13 +22,13 @@ void BP1658CJ::setup() {
this->pwm_amounts_.resize(5, 0);
}
void BP1658CJ::dump_config() {
ESP_LOGCONFIG(TAG, "BP1658CJ:");
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
ESP_LOGCONFIG(TAG,
"BP1658CJ:\n"
" Color Channels Max Power: %u\n"
" White Channels Max Power: %u",
this->max_power_color_channels_, this->max_power_white_channels_);
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
}
void BP1658CJ::loop() {
+3 -3
View File
@@ -63,14 +63,14 @@ void CAP1188Component::finish_setup_() {
}
void CAP1188Component::dump_config() {
ESP_LOGCONFIG(TAG, "CAP1188:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG,
"CAP1188:\n"
" Product ID: 0x%x\n"
" Manufacture ID: 0x%x\n"
" Revision ID: 0x%x",
this->cap1188_product_id_, this->cap1188_manufacture_id_, this->cap1188_revision_);
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
switch (this->error_code_) {
case COMMUNICATION_FAILED:
@@ -49,9 +49,11 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
std::string ssid = request->arg("ssid").c_str(); // NOLINT(readability-redundant-string-cstr)
std::string psk = request->arg("psk").c_str(); // NOLINT(readability-redundant-string-cstr)
ESP_LOGI(TAG, "Requested WiFi Settings Change:");
ESP_LOGI(TAG, " SSID='%s'", ssid.c_str());
ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str());
ESP_LOGI(TAG,
"Requested WiFi Settings Change:\n"
" SSID='%s'\n"
" Password=" LOG_SECRET("'%s'"),
ssid.c_str(), psk.c_str());
// Defer save to main loop thread to avoid NVS operations from HTTP thread
this->defer([ssid, psk]() { wifi::global_wifi_component->save_wifi_sta(ssid, psk); });
request->redirect(ESPHOME_F("/?save"));
@@ -32,14 +32,14 @@ void CHSC6XTouchscreen::update_touches() {
}
void CHSC6XTouchscreen::dump_config() {
ESP_LOGCONFIG(TAG, "CHSC6X Touchscreen:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
ESP_LOGCONFIG(TAG,
"CHSC6X Touchscreen:\n"
" Touch timeout: %d\n"
" x_raw_max_: %d\n"
" y_raw_max_: %d",
this->touch_timeout_, this->x_raw_max_, this->y_raw_max_);
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
}
} // namespace chsc6x
@@ -83,14 +83,14 @@ void CST816Touchscreen::update_touches() {
}
void CST816Touchscreen::dump_config() {
ESP_LOGCONFIG(TAG, "CST816 Touchscreen:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG,
"CST816 Touchscreen:\n"
" X Raw Min: %d, X Raw Max: %d\n"
" Y Raw Min: %d, Y Raw Max: %d",
this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_);
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
const char *name;
switch (this->chip_id_) {
case CST716_CHIP_ID:
+11 -8
View File
@@ -47,14 +47,17 @@ void DebugComponent::get_device_info_(std::string &device_info) {
#if !defined(CLANG_TIDY)
auto reset_reason = get_reset_reason_();
ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
ESP_LOGD(TAG,
"Chip ID: 0x%08X\n"
"SDK Version: %s\n"
"Core Version: %s\n"
"Boot Version=%u Mode=%u\n"
"CPU Frequency: %u\n"
"Flash Chip ID=0x%08X\n"
"Reset Reason: %s\n"
"Reset Info: %s",
ESP.getChipId(), ESP.getSdkVersion(), ESP.getCoreVersion().c_str(), ESP.getBootVersion(), ESP.getBootMode(),
ESP.getCpuFreqMHz(), ESP.getFlashChipId(), reset_reason.c_str(), ESP.getResetInfo().c_str());
device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
device_info += "|SDK: ";
+9 -6
View File
@@ -13,12 +13,15 @@ uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); }
void DebugComponent::get_device_info_(std::string &device_info) {
std::string reset_reason = get_reset_reason_();
ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
ESP_LOGD(TAG, "Board: %s", lt_get_board_code());
ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024);
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
ESP_LOGD(TAG,
"LibreTiny Version: %s\n"
"Chip: %s (%04x) @ %u MHz\n"
"Chip ID: 0x%06X\n"
"Board: %s\n"
"Flash: %u KiB / RAM: %u KiB\n"
"Reset Reason: %s",
lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), lt_cpu_get_mac_id(),
lt_get_board_code(), lt_flash_get_size() / 1024, lt_ram_get_size() / 1024, reset_reason.c_str());
device_info += "|Version: ";
device_info += LT_BANNER_STR + 10;
+23 -20
View File
@@ -106,13 +106,13 @@ static void fa_cb(const struct flash_area *fa, void *user_data) {
void DebugComponent::log_partition_info_() {
#if CONFIG_FLASH_MAP_LABELS
ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
"| Label | Offset | Size");
ESP_LOGCONFIG(TAG, "--------------------------------------------"
"| Label | Offset | Size\n"
"--------------------------------------------"
"-----------------------------------------------");
#else
ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
"| Offset | Size");
ESP_LOGCONFIG(TAG, "-----------------------------------------"
"| Offset | Size\n"
"-----------------------------------------"
"------------------------------");
#endif
flash_area_foreach(fa_cb, nullptr);
@@ -300,18 +300,18 @@ void DebugComponent::get_device_info_(std::string &device_info) {
return "Unspecified";
};
ESP_LOGD(TAG, "Code page size: %u, code size: %u, device id: 0x%08x%08x", NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE,
NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0]);
ESP_LOGD(TAG, "Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x", NRF_FICR->ER[0],
ESP_LOGD(TAG,
"Code page size: %u, code size: %u, device id: 0x%08x%08x\n"
"Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x\n"
"Device address type: %s, address: %s\n"
"Part code: nRF%x, version: %c%c%c%c, package: %s\n"
"RAM: %ukB, Flash: %ukB, production test: %sdone",
NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE, NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0], NRF_FICR->ER[0],
NRF_FICR->ER[1], NRF_FICR->ER[2], NRF_FICR->ER[3], NRF_FICR->IR[0], NRF_FICR->IR[1], NRF_FICR->IR[2],
NRF_FICR->IR[3]);
ESP_LOGD(TAG, "Device address type: %s, address: %s", (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"),
get_mac_address_pretty().c_str());
ESP_LOGD(TAG, "Part code: nRF%x, version: %c%c%c%c, package: %s", NRF_FICR->INFO.PART,
NRF_FICR->INFO.VARIANT >> 24 & 0xFF, NRF_FICR->INFO.VARIANT >> 16 & 0xFF, NRF_FICR->INFO.VARIANT >> 8 & 0xFF,
NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE));
ESP_LOGD(TAG, "RAM: %ukB, Flash: %ukB, production test: %sdone", NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH,
(NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not "));
NRF_FICR->IR[3], (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"), get_mac_address_pretty().c_str(),
NRF_FICR->INFO.PART, NRF_FICR->INFO.VARIANT >> 24 & 0xFF, NRF_FICR->INFO.VARIANT >> 16 & 0xFF,
NRF_FICR->INFO.VARIANT >> 8 & 0xFF, NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE),
NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH, (NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not "));
bool n_reset_enabled = NRF_UICR->PSELRESET[0] == NRF_UICR->PSELRESET[1] &&
(NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) == UICR_PSELRESET_CONNECT_Connected
<< UICR_PSELRESET_CONNECT_Pos;
@@ -329,9 +329,10 @@ void DebugComponent::get_device_info_(std::string &device_info) {
#else
ESP_LOGD(TAG, "bootloader: Adafruit, version %u.%u.%u", (BOOTLOADER_VERSION_REGISTER >> 16) & 0xFF,
(BOOTLOADER_VERSION_REGISTER >> 8) & 0xFF, BOOTLOADER_VERSION_REGISTER & 0xFF);
ESP_LOGD(TAG, "MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x", read_mem_u32(MBR_BOOTLOADER_ADDR),
NRF_UICR->NRFFW[0]);
ESP_LOGD(TAG, "MBR param page addr 0x%08x, UICR param page addr 0x%08x", read_mem_u32(MBR_PARAM_PAGE_ADDR),
ESP_LOGD(TAG,
"MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x\n"
"MBR param page addr 0x%08x, UICR param page addr 0x%08x",
read_mem_u32(MBR_BOOTLOADER_ADDR), NRF_UICR->NRFFW[0], read_mem_u32(MBR_PARAM_PAGE_ADDR),
NRF_UICR->NRFFW[1]);
if (is_sd_present()) {
uint32_t const sd_id = sd_id_get();
@@ -368,8 +369,10 @@ void DebugComponent::get_device_info_(std::string &device_info) {
}
return res;
};
ESP_LOGD(TAG, "NRFFW %s", uicr(NRF_UICR->NRFFW, 13).c_str());
ESP_LOGD(TAG, "NRFHW %s", uicr(NRF_UICR->NRFHW, 12).c_str());
ESP_LOGD(TAG,
"NRFFW %s\n"
"NRFHW %s",
uicr(NRF_UICR->NRFFW, 13).c_str(), uicr(NRF_UICR->NRFHW, 12).c_str());
}
void DebugComponent::update_platform_() {}
+7 -4
View File
@@ -17,11 +17,14 @@ void DHT::setup() {
}
void DHT::dump_config() {
ESP_LOGCONFIG(TAG, "DHT:");
ESP_LOGCONFIG(TAG,
"DHT:\n"
" %sModel: %s\n"
" Internal pull-up: %s",
this->is_auto_detect_ ? "Auto-detected " : "",
this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22 or equivalent",
ONOFF(this->t_pin_->get_flags() & gpio::FLAG_PULLUP));
LOG_PIN(" Pin: ", this->t_pin_);
ESP_LOGCONFIG(TAG, " %sModel: %s", this->is_auto_detect_ ? "Auto-detected " : "",
this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22 or equivalent");
ESP_LOGCONFIG(TAG, " Internal pull-up: %s", ONOFF(this->t_pin_->get_flags() & gpio::FLAG_PULLUP));
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
+4 -2
View File
@@ -104,10 +104,12 @@ void EndstopCover::loop() {
}
void EndstopCover::dump_config() {
LOG_COVER("", "Endstop Cover", this);
ESP_LOGCONFIG(TAG,
" Open Duration: %.1fs\n"
" Close Duration: %.1fs",
this->open_duration_ / 1e3f, this->close_duration_ / 1e3f);
LOG_BINARY_SENSOR(" ", "Open Endstop", this->open_endstop_);
ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
LOG_BINARY_SENSOR(" ", "Close Endstop", this->close_endstop_);
ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
}
float EndstopCover::get_setup_priority() const { return setup_priority::DATA; }
void EndstopCover::stop_prev_trigger_() {
+9 -8
View File
@@ -331,20 +331,21 @@ void HOT EPaperBase::draw_pixel_at(int x, int y, Color color) {
void EPaperBase::dump_config() {
LOG_DISPLAY("", "E-Paper SPI", this);
ESP_LOGCONFIG(TAG, " Model: %s", this->name_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Busy Pin: ", this->busy_pin_);
LOG_PIN(" CS Pin: ", this->cs_);
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" SPI Data Rate: %uMHz\n"
" Full update every: %d\n"
" Swap X/Y: %s\n"
" Mirror X: %s\n"
" Mirror Y: %s",
(unsigned) (this->data_rate_ / 1000000), this->full_update_every_, YESNO(this->transform_ & SWAP_XY),
YESNO(this->transform_ & MIRROR_X), YESNO(this->transform_ & MIRROR_Y));
this->name_, (unsigned) (this->data_rate_ / 1000000), this->full_update_every_,
YESNO(this->transform_ & SWAP_XY), YESNO(this->transform_ & MIRROR_X),
YESNO(this->transform_ & MIRROR_Y));
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Busy Pin: ", this->busy_pin_);
LOG_PIN(" CS Pin: ", this->cs_);
LOG_UPDATE_INTERVAL(this);
}
} // namespace esphome::epaper_spi
+8
View File
@@ -644,6 +644,7 @@ CONF_DISABLE_VFS_SUPPORT_SELECT = "disable_vfs_support_select"
CONF_DISABLE_VFS_SUPPORT_DIR = "disable_vfs_support_dir"
CONF_FREERTOS_IN_IRAM = "freertos_in_iram"
CONF_RINGBUF_IN_IRAM = "ringbuf_in_iram"
CONF_HEAP_IN_IRAM = "heap_in_iram"
CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size"
# VFS requirement tracking
@@ -745,6 +746,7 @@ FRAMEWORK_SCHEMA = cv.Schema(
cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean,
cv.Optional(CONF_FREERTOS_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_RINGBUF_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_HEAP_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_EXECUTE_FROM_PSRAM, default=False): cv.boolean,
cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range(
min=8192, max=32768
@@ -1090,6 +1092,12 @@ async def to_code(config):
# Place in flash to save IRAM (default)
add_idf_sdkconfig_option("CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH", True)
# Place heap functions into flash to save IRAM (~4-6KB savings)
# Safe as long as heap functions are not called from ISRs (which they shouldn't be)
# Users can set heap_in_iram: true as an escape hatch if needed
if not conf[CONF_ADVANCED][CONF_HEAP_IN_IRAM]:
add_idf_sdkconfig_option("CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH", True)
# Setup watchdog
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT", True)
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_PANIC", True)
@@ -70,9 +70,9 @@ float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_B
void BLEClientBase::dump_config() {
ESP_LOGCONFIG(TAG,
" Address: %s\n"
" Auto-Connect: %s",
this->address_str(), TRUEFALSE(this->auto_connect_));
ESP_LOGCONFIG(TAG, " State: %s", espbt::client_state_to_string(this->state()));
" Auto-Connect: %s\n"
" State: %s",
this->address_str(), TRUEFALSE(this->auto_connect_), espbt::client_state_to_string(this->state()));
if (this->status_ == ESP_GATT_NO_RESOURCES) {
ESP_LOGE(TAG, " Failed due to no resources. Try to reduce number of BLE clients in config.");
} else if (this->status_ != ESP_GATT_OK) {
@@ -415,8 +415,10 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
for (auto &svc : this->services_) {
char uuid_buf[espbt::UUID_STR_LEN];
svc->uuid.to_str(uuid_buf);
ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_, uuid_buf);
ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_, this->address_str_,
ESP_LOGV(TAG,
"[%d] [%s] Service UUID: %s\n"
"[%d] [%s] start_handle: 0x%x end_handle: 0x%x",
this->connection_index_, this->address_str_, uuid_buf, this->connection_index_, this->address_str_,
svc->start_handle, svc->end_handle);
}
#endif
@@ -657,8 +657,10 @@ void ESP32BLETracker::dump_config() {
" Continuous Scanning: %s",
this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f,
this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_));
ESP_LOGCONFIG(TAG, " Scanner State: %s", this->scanner_state_to_string_(this->scanner_state_));
ESP_LOGCONFIG(TAG, " Connecting: %d, discovered: %d, disconnecting: %d", this->client_state_counts_.connecting,
ESP_LOGCONFIG(TAG,
" Scanner State: %s\n"
" Connecting: %d, discovered: %d, disconnecting: %d",
this->scanner_state_to_string_(this->scanner_state_), this->client_state_counts_.connecting,
this->client_state_counts_.discovered, this->client_state_counts_.disconnecting);
if (this->scan_start_fail_count_) {
ESP_LOGCONFIG(TAG, " Scan Start Fail Count: %d", this->scan_start_fail_count_);
@@ -98,7 +98,7 @@ void ESP32RMTLEDStripLightOutput::setup() {
channel.trans_queue_depth = 1;
channel.flags.io_loop_back = 0;
channel.flags.io_od_mode = 0;
channel.flags.invert_out = 0;
channel.flags.invert_out = this->invert_out_;
channel.flags.with_dma = this->use_dma_;
channel.intr_priority = 0;
if (rmt_new_tx_channel(&channel, &this->channel_) != ESP_OK) {
@@ -49,6 +49,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
}
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_inverted(bool inverted) { this->invert_out_ = inverted; }
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
@@ -93,6 +94,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
bool is_wrgb_{false};
bool use_dma_{false};
bool use_psram_{false};
bool invert_out_{false};
RGBOrder rgb_order_{ORDER_RGB};
@@ -8,9 +8,11 @@ from esphome.components.const import CONF_USE_PSRAM
import esphome.config_validation as cv
from esphome.const import (
CONF_CHIPSET,
CONF_INVERTED,
CONF_IS_RGBW,
CONF_MAX_REFRESH_RATE,
CONF_NUM_LEDS,
CONF_NUMBER,
CONF_OUTPUT_ID,
CONF_PIN,
CONF_RGB_ORDER,
@@ -71,7 +73,7 @@ CONFIG_SCHEMA = cv.All(
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ESP32RMTLEDStripLightOutput),
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
cv.SplitDefault(
@@ -132,7 +134,9 @@ async def to_code(config):
await cg.register_component(var, config)
cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
cg.add(var.set_pin(config[CONF_PIN]))
cg.add(var.set_pin(config[CONF_PIN][CONF_NUMBER]))
if config[CONF_PIN][CONF_INVERTED]:
cg.add(var.set_inverted(True))
if CONF_MAX_REFRESH_RATE in config:
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
@@ -18,9 +18,11 @@ void ESP8266PWM::setup() {
this->turn_off();
}
void ESP8266PWM::dump_config() {
ESP_LOGCONFIG(TAG, "ESP8266 PWM:");
ESP_LOGCONFIG(TAG,
"ESP8266 PWM:\n"
" Frequency: %.1f Hz",
this->frequency_);
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_);
LOG_FLOAT_OUTPUT(this);
}
void HOT ESP8266PWM::write_state(float state) {
+5 -3
View File
@@ -21,9 +21,11 @@ void EspLdo::setup() {
}
}
void EspLdo::dump_config() {
ESP_LOGCONFIG(TAG, "ESP LDO Channel %d:", this->channel_);
ESP_LOGCONFIG(TAG, " Voltage: %fV", this->voltage_);
ESP_LOGCONFIG(TAG, " Adjustable: %s", YESNO(this->adjustable_));
ESP_LOGCONFIG(TAG,
"ESP LDO Channel %d:\n"
" Voltage: %fV\n"
" Adjustable: %s",
this->channel_, this->voltage_, YESNO(this->adjustable_));
}
void EspLdo::adjust_voltage(float voltage) {
+20 -23
View File
@@ -64,18 +64,6 @@ static const LogString *espnow_error_to_str(esp_err_t error) {
}
}
std::string peer_str(uint8_t *peer) {
if (peer == nullptr || peer[0] == 0) {
return "[Not Set]";
} else if (memcmp(peer, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
return "[Broadcast]";
} else if (memcmp(peer, ESPNOW_MULTICAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
return "[Multicast]";
} else {
return format_mac_address_pretty(peer);
}
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
void on_send_report(const esp_now_send_info_t *info, esp_now_send_status_t status)
#else
@@ -140,11 +128,13 @@ void ESPNowComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Disabled");
return;
}
char own_addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(this->own_address_, own_addr_buf);
ESP_LOGCONFIG(TAG,
" Own address: %s\n"
" Version: v%" PRIu32 "\n"
" Wi-Fi channel: %d",
format_mac_address_pretty(this->own_address_).c_str(), version, this->wifi_channel_);
own_addr_buf, version, this->wifi_channel_);
#ifdef USE_WIFI
ESP_LOGCONFIG(TAG, " Wi-Fi enabled: %s", YESNO(this->is_wifi_enabled()));
#endif
@@ -300,9 +290,12 @@ void ESPNowComponent::loop() {
// Intentionally left as if instead of else in case the peer is added above
if (esp_now_is_peer_exist(info.src_addr)) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char src_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
char dst_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
char hex_buf[format_hex_pretty_size(ESP_NOW_MAX_DATA_LEN)];
ESP_LOGV(TAG, "<<< [%s -> %s] %s", format_mac_address_pretty(info.src_addr).c_str(),
format_mac_address_pretty(info.des_addr).c_str(),
format_mac_addr_upper(info.src_addr, src_buf);
format_mac_addr_upper(info.des_addr, dst_buf);
ESP_LOGV(TAG, "<<< [%s -> %s] %s", src_buf, dst_buf,
format_hex_pretty_to(hex_buf, packet->packet_.receive.data, packet->packet_.receive.size));
#endif
if (memcmp(info.des_addr, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
@@ -321,8 +314,9 @@ void ESPNowComponent::loop() {
}
case ESPNowPacket::SENT: {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
ESP_LOGV(TAG, ">>> [%s] %s", format_mac_address_pretty(packet->packet_.sent.address).c_str(),
LOG_STR_ARG(espnow_error_to_str(packet->packet_.sent.status)));
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(packet->packet_.sent.address, addr_buf);
ESP_LOGV(TAG, ">>> [%s] %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(packet->packet_.sent.status)));
#endif
if (this->current_send_packet_ != nullptr) {
this->current_send_packet_->callback_(packet->packet_.sent.status);
@@ -409,8 +403,9 @@ void ESPNowComponent::send_() {
this->current_send_packet_ = packet;
esp_err_t err = esp_now_send(packet->address_, packet->data_, packet->size_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send packet to %s - %s", format_mac_address_pretty(packet->address_).c_str(),
LOG_STR_ARG(espnow_error_to_str(err)));
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(packet->address_, addr_buf);
ESP_LOGE(TAG, "Failed to send packet to %s - %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(err)));
if (packet->callback_ != nullptr) {
packet->callback_(err);
}
@@ -439,8 +434,9 @@ esp_err_t ESPNowComponent::add_peer(const uint8_t *peer) {
esp_err_t err = esp_now_add_peer(&peer_info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to add peer %s - %s", format_mac_address_pretty(peer).c_str(),
LOG_STR_ARG(espnow_error_to_str(err)));
char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(peer, peer_buf);
ESP_LOGE(TAG, "Failed to add peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
this->status_momentary_warning("peer-add-failed");
return err;
}
@@ -468,8 +464,9 @@ esp_err_t ESPNowComponent::del_peer(const uint8_t *peer) {
if (esp_now_is_peer_exist(peer)) {
esp_err_t err = esp_now_del_peer(peer);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to delete peer %s - %s", format_mac_address_pretty(peer).c_str(),
LOG_STR_ARG(espnow_error_to_str(err)));
char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(peer, peer_buf);
ESP_LOGE(TAG, "Failed to delete peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
this->status_momentary_warning("peer-del-failed");
return err;
}
@@ -21,9 +21,11 @@ void ESPNowTransport::setup() {
return;
}
ESP_LOGI(TAG, "Registering ESP-NOW handlers");
ESP_LOGI(TAG, "Peer address: %02X:%02X:%02X:%02X:%02X:%02X", this->peer_address_[0], this->peer_address_[1],
this->peer_address_[2], this->peer_address_[3], this->peer_address_[4], this->peer_address_[5]);
ESP_LOGI(TAG,
"Registering ESP-NOW handlers\n"
"Peer address: %02X:%02X:%02X:%02X:%02X:%02X",
this->peer_address_[0], this->peer_address_[1], this->peer_address_[2], this->peer_address_[3],
this->peer_address_[4], this->peer_address_[5]);
// Register received handler
this->parent_->register_received_handler(this);
@@ -813,8 +813,10 @@ void EthernetComponent::write_phy_register_(esp_eth_mac_t *mac, PHYRegister regi
ESPHL_ERROR_CHECK(err, "Select PHY Register page failed");
}
ESP_LOGD(TAG, "Writing to PHY Register Address: 0x%02" PRIX32, register_data.address);
ESP_LOGD(TAG, "Writing to PHY Register Value: 0x%04" PRIX32, register_data.value);
ESP_LOGD(TAG,
"Writing to PHY Register Address: 0x%02" PRIX32 "\n"
"Writing to PHY Register Value: 0x%04" PRIX32,
register_data.address, register_data.value);
err = mac->write_phy_reg(mac, this->phy_addr_, register_data.address, register_data.value);
ESPHL_ERROR_CHECK(err, "Writing PHY Register failed");
+7 -4
View File
@@ -148,10 +148,13 @@ void EzoPMP::read_command_result_() {
char current_char = response_buffer[i];
if (current_char == '\0') {
ESP_LOGV(TAG, "Read Response from device: %s", (char *) response_buffer);
ESP_LOGV(TAG, "First Component: %s", (char *) first_parameter_buffer);
ESP_LOGV(TAG, "Second Component: %s", (char *) second_parameter_buffer);
ESP_LOGV(TAG, "Third Component: %s", (char *) third_parameter_buffer);
ESP_LOGV(TAG,
"Read Response from device: %s\n"
"First Component: %s\n"
"Second Component: %s\n"
"Third Component: %s",
(char *) response_buffer, (char *) first_parameter_buffer, (char *) second_parameter_buffer,
(char *) third_parameter_buffer);
break;
}
+4 -2
View File
@@ -179,8 +179,10 @@ void Fan::add_on_state_callback(std::function<void()> &&callback) { this->state_
void Fan::publish_state() {
auto traits = this->get_traits();
ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
ESP_LOGD(TAG, " State: %s", ONOFF(this->state));
ESP_LOGD(TAG,
"'%s' - Sending state:\n"
" State: %s",
this->name_.c_str(), ONOFF(this->state));
if (traits.supports_speed()) {
ESP_LOGD(TAG, " Speed: %d", this->speed);
}
@@ -105,9 +105,8 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
// we will compute MD5 on the fly for verification -- Arduino OTA seems to ignore it
md5_receive.init();
ESP_LOGV(TAG, "MD5Digest initialized");
ESP_LOGV(TAG, "OTA backend begin");
ESP_LOGV(TAG, "MD5Digest initialized\n"
"OTA backend begin");
auto backend = ota::make_ota_backend();
auto error_code = backend->begin(container->content_length);
if (error_code != ota::OTA_RESPONSE_OK) {
+1 -1
View File
@@ -250,7 +250,7 @@ async def register_i2c_device(var, config):
Sets the i2c bus to use and the i2c address.
This is a coroutine, you need to await it with a 'yield' expression!
This is a coroutine, you need to await it with an 'await' expression!
"""
parent = await cg.get_variable(config[CONF_I2C_ID])
cg.add(var.set_i2c_bus(parent))
+1 -1
View File
@@ -386,7 +386,7 @@ async def to_code(config):
except cv.Invalid:
pass
if CORE.using_zephyr:
if CORE.is_nrf52:
if config[CONF_HARDWARE_UART] == UART0:
zephyr_add_overlay("""&uart0 { status = "okay";};""")
if config[CONF_HARDWARE_UART] == UART1:
@@ -66,6 +66,8 @@ void Logger::pre_setup() {
void HOT Logger::write_msg_(const char *msg, size_t len) {
// Single write with newline already in buffer (added by caller)
#ifdef CONFIG_PRINTK
// Requires the debug component and an active SWD connection.
// It is used for pyocd rtt -t nrf52840
k_str_out(const_cast<char *>(msg), len);
#endif
if (this->uart_dev_ == nullptr) {
+44
View File
@@ -13,6 +13,9 @@ static const uint8_t MHZ19_COMMAND_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x
static const uint8_t MHZ19_COMMAND_ABC_ENABLE[] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00};
static const uint8_t MHZ19_COMMAND_ABC_DISABLE[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00};
static const uint8_t MHZ19_COMMAND_CALIBRATE_ZERO[] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00};
static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0};
static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88};
static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x27, 0x10};
uint8_t mhz19_checksum(const uint8_t *command) {
uint8_t sum = 0;
@@ -28,6 +31,8 @@ void MHZ19Component::setup() {
} else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) {
this->abc_disable();
}
this->range_set(this->detection_range_);
}
void MHZ19Component::update() {
@@ -86,6 +91,26 @@ void MHZ19Component::abc_disable() {
this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, nullptr);
}
void MHZ19Component::range_set(MHZ19DetectionRange detection_ppm) {
switch (detection_ppm) {
case MHZ19_DETECTION_RANGE_DEFAULT:
ESP_LOGV(TAG, "Using previously set detection range (no change)");
break;
case MHZ19_DETECTION_RANGE_0_2000PPM:
ESP_LOGD(TAG, "Setting detection range to 0 to 2000ppm");
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM, nullptr);
break;
case MHZ19_DETECTION_RANGE_0_5000PPM:
ESP_LOGD(TAG, "Setting detection range to 0 to 5000ppm");
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM, nullptr);
break;
case MHZ19_DETECTION_RANGE_0_10000PPM:
ESP_LOGD(TAG, "Setting detection range to 0 to 10000ppm");
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM, nullptr);
break;
}
}
bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) {
// Empty RX Buffer
while (this->available())
@@ -99,7 +124,9 @@ bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *respo
return this->read_array(response, MHZ19_RESPONSE_LENGTH);
}
float MHZ19Component::get_setup_priority() const { return setup_priority::DATA; }
void MHZ19Component::dump_config() {
ESP_LOGCONFIG(TAG, "MH-Z19:");
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
@@ -113,6 +140,23 @@ void MHZ19Component::dump_config() {
}
ESP_LOGCONFIG(TAG, " Warmup time: %" PRIu32 " s", this->warmup_seconds_);
const char *range_str;
switch (this->detection_range_) {
case MHZ19_DETECTION_RANGE_DEFAULT:
range_str = "default";
break;
case MHZ19_DETECTION_RANGE_0_2000PPM:
range_str = "0 to 2000ppm";
break;
case MHZ19_DETECTION_RANGE_0_5000PPM:
range_str = "0 to 5000ppm";
break;
case MHZ19_DETECTION_RANGE_0_10000PPM:
range_str = "0 to 10000ppm";
break;
}
ESP_LOGCONFIG(TAG, " Detection range: %s", range_str);
}
} // namespace mhz19
+28 -20
View File
@@ -8,7 +8,18 @@
namespace esphome {
namespace mhz19 {
enum MHZ19ABCLogic { MHZ19_ABC_NONE = 0, MHZ19_ABC_ENABLED, MHZ19_ABC_DISABLED };
enum MHZ19ABCLogic {
MHZ19_ABC_NONE = 0,
MHZ19_ABC_ENABLED,
MHZ19_ABC_DISABLED,
};
enum MHZ19DetectionRange {
MHZ19_DETECTION_RANGE_DEFAULT = 0,
MHZ19_DETECTION_RANGE_0_2000PPM,
MHZ19_DETECTION_RANGE_0_5000PPM,
MHZ19_DETECTION_RANGE_0_10000PPM,
};
class MHZ19Component : public PollingComponent, public uart::UARTDevice {
public:
@@ -21,11 +32,13 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice {
void calibrate_zero();
void abc_enable();
void abc_disable();
void range_set(MHZ19DetectionRange detection_ppm);
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; }
void set_abc_enabled(bool abc_enabled) { abc_boot_logic_ = abc_enabled ? MHZ19_ABC_ENABLED : MHZ19_ABC_DISABLED; }
void set_warmup_seconds(uint32_t seconds) { warmup_seconds_ = seconds; }
void set_detection_range(MHZ19DetectionRange detection_range) { detection_range_ = detection_range; }
protected:
bool mhz19_write_command_(const uint8_t *command, uint8_t *response);
@@ -33,37 +46,32 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice {
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *co2_sensor_{nullptr};
MHZ19ABCLogic abc_boot_logic_{MHZ19_ABC_NONE};
uint32_t warmup_seconds_;
MHZ19DetectionRange detection_range_{MHZ19_DETECTION_RANGE_DEFAULT};
};
template<typename... Ts> class MHZ19CalibrateZeroAction : public Action<Ts...> {
template<typename... Ts> class MHZ19CalibrateZeroAction : public Action<Ts...>, public Parented<MHZ19Component> {
public:
MHZ19CalibrateZeroAction(MHZ19Component *mhz19) : mhz19_(mhz19) {}
void play(const Ts &...x) override { this->mhz19_->calibrate_zero(); }
protected:
MHZ19Component *mhz19_;
void play(const Ts &...x) override { this->parent_->calibrate_zero(); }
};
template<typename... Ts> class MHZ19ABCEnableAction : public Action<Ts...> {
template<typename... Ts> class MHZ19ABCEnableAction : public Action<Ts...>, public Parented<MHZ19Component> {
public:
MHZ19ABCEnableAction(MHZ19Component *mhz19) : mhz19_(mhz19) {}
void play(const Ts &...x) override { this->mhz19_->abc_enable(); }
protected:
MHZ19Component *mhz19_;
void play(const Ts &...x) override { this->parent_->abc_enable(); }
};
template<typename... Ts> class MHZ19ABCDisableAction : public Action<Ts...> {
template<typename... Ts> class MHZ19ABCDisableAction : public Action<Ts...>, public Parented<MHZ19Component> {
public:
MHZ19ABCDisableAction(MHZ19Component *mhz19) : mhz19_(mhz19) {}
void play(const Ts &...x) override { this->parent_->abc_disable(); }
};
void play(const Ts &...x) override { this->mhz19_->abc_disable(); }
template<typename... Ts> class MHZ19DetectionRangeSetAction : public Action<Ts...>, public Parented<MHZ19Component> {
public:
TEMPLATABLE_VALUE(MHZ19DetectionRange, detection_range)
protected:
MHZ19Component *mhz19_;
void play(const Ts &...x) override { this->parent_->range_set(this->detection_range_.value(x...)); }
};
} // namespace mhz19
+58 -10
View File
@@ -19,14 +19,33 @@ DEPENDENCIES = ["uart"]
CONF_AUTOMATIC_BASELINE_CALIBRATION = "automatic_baseline_calibration"
CONF_WARMUP_TIME = "warmup_time"
CONF_DETECTION_RANGE = "detection_range"
mhz19_ns = cg.esphome_ns.namespace("mhz19")
MHZ19Component = mhz19_ns.class_("MHZ19Component", cg.PollingComponent, uart.UARTDevice)
MHZ19CalibrateZeroAction = mhz19_ns.class_(
"MHZ19CalibrateZeroAction", automation.Action
"MHZ19CalibrateZeroAction", automation.Action, cg.Parented.template(MHZ19Component)
)
MHZ19ABCEnableAction = mhz19_ns.class_("MHZ19ABCEnableAction", automation.Action)
MHZ19ABCDisableAction = mhz19_ns.class_("MHZ19ABCDisableAction", automation.Action)
MHZ19ABCEnableAction = mhz19_ns.class_(
"MHZ19ABCEnableAction", automation.Action, cg.Parented.template(MHZ19Component)
)
MHZ19ABCDisableAction = mhz19_ns.class_(
"MHZ19ABCDisableAction", automation.Action, cg.Parented.template(MHZ19Component)
)
MHZ19DetectionRangeSetAction = mhz19_ns.class_(
"MHZ19DetectionRangeSetAction",
automation.Action,
cg.Parented.template(MHZ19Component),
)
mhz19_detection_range = mhz19_ns.enum("MHZ19DetectionRange")
MHZ19_DETECTION_RANGE_ENUM = {
2000: mhz19_detection_range.MHZ19_DETECTION_RANGE_0_2000PPM,
5000: mhz19_detection_range.MHZ19_DETECTION_RANGE_0_5000PPM,
10000: mhz19_detection_range.MHZ19_DETECTION_RANGE_0_10000PPM,
}
_validate_ppm = cv.float_with_unit("parts per million", "ppm")
CONFIG_SCHEMA = (
cv.Schema(
@@ -49,6 +68,9 @@ CONFIG_SCHEMA = (
cv.Optional(
CONF_WARMUP_TIME, default="75s"
): cv.positive_time_period_seconds,
cv.Optional(CONF_DETECTION_RANGE): cv.All(
_validate_ppm, cv.enum(MHZ19_DETECTION_RANGE_ENUM)
),
}
)
.extend(cv.polling_component_schema("60s"))
@@ -78,8 +100,11 @@ async def to_code(config):
cg.add(var.set_warmup_seconds(config[CONF_WARMUP_TIME]))
if CONF_DETECTION_RANGE in config:
cg.add(var.set_detection_range(config[CONF_DETECTION_RANGE]))
CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
NO_ARGS_ACTION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(MHZ19Component),
}
@@ -87,14 +112,37 @@ CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
@automation.register_action(
"mhz19.calibrate_zero", MHZ19CalibrateZeroAction, CALIBRATION_ACTION_SCHEMA
"mhz19.calibrate_zero", MHZ19CalibrateZeroAction, NO_ARGS_ACTION_SCHEMA
)
@automation.register_action(
"mhz19.abc_enable", MHZ19ABCEnableAction, CALIBRATION_ACTION_SCHEMA
"mhz19.abc_enable", MHZ19ABCEnableAction, NO_ARGS_ACTION_SCHEMA
)
@automation.register_action(
"mhz19.abc_disable", MHZ19ABCDisableAction, CALIBRATION_ACTION_SCHEMA
"mhz19.abc_disable", MHZ19ABCDisableAction, NO_ARGS_ACTION_SCHEMA
)
async def mhz19_calibration_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
async def mhz19_no_args_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
RANGE_ACTION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(MHZ19Component),
cv.Required(CONF_DETECTION_RANGE): cv.All(
_validate_ppm, cv.enum(MHZ19_DETECTION_RANGE_ENUM)
),
}
)
@automation.register_action(
"mhz19.detection_range_set", MHZ19DetectionRangeSetAction, RANGE_ACTION_SCHEMA
)
async def mhz19_detection_range_set_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
detection_range = config.get(CONF_DETECTION_RANGE)
template_ = await cg.templatable(detection_range, args, mhz19_detection_range)
cg.add(var.set_detection_range(template_))
return var
+15 -1
View File
@@ -12,6 +12,7 @@ from esphome.components.zephyr import (
zephyr_add_prj_conf,
zephyr_data,
zephyr_set_core_data,
zephyr_setup_preferences,
zephyr_to_code,
)
from esphome.components.zephyr.const import (
@@ -49,7 +50,7 @@ from .const import (
from .gpio import nrf52_pin_to_code # noqa
CODEOWNERS = ["@tomaszduda23"]
AUTO_LOAD = ["zephyr"]
AUTO_LOAD = ["zephyr", "preferences"]
IS_TARGET_PLATFORM = True
_LOGGER = logging.getLogger(__name__)
@@ -194,6 +195,7 @@ async def to_code(config: ConfigType) -> None:
cg.add_platformio_option("board_upload.require_upload_port", "true")
cg.add_platformio_option("board_upload.wait_for_upload_port", "true")
zephyr_setup_preferences()
zephyr_to_code(config)
if dfu_config := config.get(CONF_DFU):
@@ -206,6 +208,18 @@ async def to_code(config: ConfigType) -> None:
if reg0_config[CONF_UICR_ERASE]:
cg.add_define("USE_NRF52_UICR_ERASE")
# c++ support
zephyr_add_prj_conf("CPLUSPLUS", True)
zephyr_add_prj_conf("LIB_CPLUSPLUS", True)
# watchdog
zephyr_add_prj_conf("WATCHDOG", True)
zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False)
# disable console
zephyr_add_prj_conf("UART_CONSOLE", False)
zephyr_add_prj_conf("CONSOLE", False)
# use NFC pins as GPIO
zephyr_add_prj_conf("NFCT_PINS_AS_GPIOS", True)
@coroutine_with_priority(CoroPriority.DIAGNOSTICS)
async def _dfu_to_code(dfu_config):
+8 -1
View File
@@ -71,8 +71,15 @@ NRF52_PIN_SCHEMA = cv.All(
@pins.PIN_SCHEMA_REGISTRY.register(PLATFORM_NRF52, NRF52_PIN_SCHEMA)
async def nrf52_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
num = config[CONF_NUMBER]
port = num // 32
pin_name_prefix = f"P{port}."
var = cg.new_Pvariable(
config[CONF_ID],
cg.RawExpression(f"DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpio{port}))"),
32,
pin_name_prefix,
)
cg.add(var.set_pin(num))
# Only set if true to avoid bloating setup() function
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
+1 -1
View File
@@ -32,7 +32,7 @@ async def register_one_wire_device(var, config):
Sets the 1-wire bus to use and the 1-wire address.
This is a coroutine, you need to await it with a 'yield' expression!
This is a coroutine, you need to await it with an 'await' expression!
"""
parent = await cg.get_variable(config[CONF_ONE_WIRE_ID])
cg.add(var.set_one_wire_bus(parent))
@@ -0,0 +1,123 @@
from esphome import automation
import esphome.codegen as cg
from esphome.components import water_heater
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_MODE,
CONF_OPTIMISTIC,
CONF_RESTORE_MODE,
CONF_SET_ACTION,
CONF_SUPPORTED_MODES,
CONF_TARGET_TEMPERATURE,
)
from esphome.core import ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
from .. import template_ns
CONF_CURRENT_TEMPERATURE = "current_temperature"
TemplateWaterHeater = template_ns.class_(
"TemplateWaterHeater", water_heater.WaterHeater
)
TemplateWaterHeaterPublishAction = template_ns.class_(
"TemplateWaterHeaterPublishAction",
automation.Action,
cg.Parented.template(TemplateWaterHeater),
)
TemplateWaterHeaterRestoreMode = template_ns.enum("TemplateWaterHeaterRestoreMode")
RESTORE_MODES = {
"NO_RESTORE": TemplateWaterHeaterRestoreMode.WATER_HEATER_NO_RESTORE,
"RESTORE": TemplateWaterHeaterRestoreMode.WATER_HEATER_RESTORE,
"RESTORE_AND_CALL": TemplateWaterHeaterRestoreMode.WATER_HEATER_RESTORE_AND_CALL,
}
CONFIG_SCHEMA = water_heater.water_heater_schema(TemplateWaterHeater).extend(
{
cv.Optional(CONF_OPTIMISTIC, default=True): cv.boolean,
cv.Optional(CONF_SET_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_RESTORE_MODE, default="NO_RESTORE"): cv.enum(
RESTORE_MODES, upper=True
),
cv.Optional(CONF_CURRENT_TEMPERATURE): cv.returning_lambda,
cv.Optional(CONF_MODE): cv.returning_lambda,
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(
water_heater.validate_water_heater_mode
),
}
)
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await water_heater.register_water_heater(var, config)
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
if CONF_SET_ACTION in config:
await automation.build_automation(
var.get_set_trigger(), [], config[CONF_SET_ACTION]
)
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
if CONF_CURRENT_TEMPERATURE in config:
template_ = await cg.process_lambda(
config[CONF_CURRENT_TEMPERATURE],
[],
return_type=cg.optional.template(cg.float_),
)
cg.add(var.set_current_temperature_lambda(template_))
if CONF_MODE in config:
template_ = await cg.process_lambda(
config[CONF_MODE],
[],
return_type=cg.optional.template(water_heater.WaterHeaterMode),
)
cg.add(var.set_mode_lambda(template_))
if CONF_SUPPORTED_MODES in config:
cg.add(var.set_supported_modes(config[CONF_SUPPORTED_MODES]))
@automation.register_action(
"water_heater.template.publish",
TemplateWaterHeaterPublishAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(TemplateWaterHeater),
cv.Optional(CONF_CURRENT_TEMPERATURE): cv.templatable(cv.temperature),
cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
cv.Optional(CONF_MODE): cv.templatable(
water_heater.validate_water_heater_mode
),
}
),
)
async def water_heater_template_publish_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
if current_temp := config.get(CONF_CURRENT_TEMPERATURE):
template_ = await cg.templatable(current_temp, args, float)
cg.add(var.set_current_temperature(template_))
if target_temp := config.get(CONF_TARGET_TEMPERATURE):
template_ = await cg.templatable(target_temp, args, float)
cg.add(var.set_target_temperature(template_))
if mode := config.get(CONF_MODE):
template_ = await cg.templatable(mode, args, water_heater.WaterHeaterMode)
cg.add(var.set_mode(template_))
return var
@@ -0,0 +1,35 @@
#pragma once
#include "template_water_heater.h"
#include "esphome/core/automation.h"
namespace esphome::template_ {
template<typename... Ts>
class TemplateWaterHeaterPublishAction : public Action<Ts...>, public Parented<TemplateWaterHeater> {
public:
TEMPLATABLE_VALUE(float, current_temperature)
TEMPLATABLE_VALUE(float, target_temperature)
TEMPLATABLE_VALUE(water_heater::WaterHeaterMode, mode)
void play(const Ts &...x) override {
if (this->current_temperature_.has_value()) {
this->parent_->set_current_temperature(this->current_temperature_.value(x...));
}
bool needs_call = this->target_temperature_.has_value() || this->mode_.has_value();
if (needs_call) {
auto call = this->parent_->make_call();
if (this->target_temperature_.has_value()) {
call.set_target_temperature(this->target_temperature_.value(x...));
}
if (this->mode_.has_value()) {
call.set_mode(this->mode_.value(x...));
}
call.perform();
} else {
this->parent_->publish_state();
}
}
};
} // namespace esphome::template_
@@ -0,0 +1,88 @@
#include "template_water_heater.h"
#include "esphome/core/log.h"
namespace esphome::template_ {
static const char *const TAG = "template.water_heater";
TemplateWaterHeater::TemplateWaterHeater() : set_trigger_(new Trigger<>()) {}
void TemplateWaterHeater::setup() {
if (this->restore_mode_ == TemplateWaterHeaterRestoreMode::WATER_HEATER_RESTORE ||
this->restore_mode_ == TemplateWaterHeaterRestoreMode::WATER_HEATER_RESTORE_AND_CALL) {
auto restore = this->restore_state();
if (restore.has_value()) {
restore->perform();
}
}
if (!this->current_temperature_f_.has_value() && !this->mode_f_.has_value())
this->disable_loop();
}
water_heater::WaterHeaterTraits TemplateWaterHeater::traits() {
water_heater::WaterHeaterTraits traits;
if (!this->supported_modes_.empty()) {
traits.set_supported_modes(this->supported_modes_);
}
traits.set_supports_current_temperature(true);
return traits;
}
void TemplateWaterHeater::loop() {
bool changed = false;
auto curr_temp = this->current_temperature_f_.call();
if (curr_temp.has_value()) {
if (*curr_temp != this->current_temperature_) {
this->current_temperature_ = *curr_temp;
changed = true;
}
}
auto new_mode = this->mode_f_.call();
if (new_mode.has_value()) {
if (*new_mode != this->mode_) {
this->mode_ = *new_mode;
changed = true;
}
}
if (changed) {
this->publish_state();
}
}
void TemplateWaterHeater::dump_config() {
LOG_WATER_HEATER("", "Template Water Heater", this);
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
}
float TemplateWaterHeater::get_setup_priority() const { return setup_priority::HARDWARE; }
water_heater::WaterHeaterCallInternal TemplateWaterHeater::make_call() {
return water_heater::WaterHeaterCallInternal(this);
}
void TemplateWaterHeater::control(const water_heater::WaterHeaterCall &call) {
if (call.get_mode().has_value()) {
if (this->optimistic_) {
this->mode_ = *call.get_mode();
}
}
if (!std::isnan(call.get_target_temperature())) {
if (this->optimistic_) {
this->target_temperature_ = call.get_target_temperature();
}
}
this->set_trigger_->trigger();
if (this->optimistic_) {
this->publish_state();
}
}
} // namespace esphome::template_
@@ -0,0 +1,53 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/core/template_lambda.h"
#include "esphome/components/water_heater/water_heater.h"
namespace esphome::template_ {
enum TemplateWaterHeaterRestoreMode {
WATER_HEATER_NO_RESTORE,
WATER_HEATER_RESTORE,
WATER_HEATER_RESTORE_AND_CALL,
};
class TemplateWaterHeater : public water_heater::WaterHeater {
public:
TemplateWaterHeater();
template<typename F> void set_current_temperature_lambda(F &&f) {
this->current_temperature_f_.set(std::forward<F>(f));
}
template<typename F> void set_mode_lambda(F &&f) { this->mode_f_.set(std::forward<F>(f)); }
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
void set_restore_mode(TemplateWaterHeaterRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
void set_supported_modes(const std::initializer_list<water_heater::WaterHeaterMode> &modes) {
this->supported_modes_ = modes;
}
Trigger<> *get_set_trigger() const { return this->set_trigger_; }
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override;
water_heater::WaterHeaterCallInternal make_call() override;
protected:
void control(const water_heater::WaterHeaterCall &call) override;
water_heater::WaterHeaterTraits traits() override;
// Ordered to minimize padding on 32-bit: 4-byte members first, then smaller
Trigger<> *set_trigger_;
TemplateLambda<float> current_temperature_f_;
TemplateLambda<water_heater::WaterHeaterMode> mode_f_;
TemplateWaterHeaterRestoreMode restore_mode_{WATER_HEATER_NO_RESTORE};
water_heater::WaterHeaterModeMask supported_modes_;
bool optimistic_{true};
};
} // namespace esphome::template_
+1 -1
View File
@@ -482,7 +482,7 @@ def final_validate_device_schema(
async def register_uart_device(var, config):
"""Register a UART device, setting up all the internal values.
This is a coroutine, you need to await it with a 'yield' expression!
This is a coroutine, you need to await it with an 'await' expression!
"""
parent = await cg.get_variable(config[CONF_UART_ID])
cg.add(var.set_uart_parent(parent))
@@ -120,8 +120,10 @@ void LibreTinyUARTComponent::setup() {
void LibreTinyUARTComponent::dump_config() {
bool is_software = this->hardware_idx_ == -1;
ESP_LOGCONFIG(TAG, "UART Bus:");
ESP_LOGCONFIG(TAG, " Type: %s", UART_TYPE[is_software]);
ESP_LOGCONFIG(TAG,
"UART Bus:\n"
" Type: %s",
UART_TYPE[is_software]);
if (!is_software) {
ESP_LOGCONFIG(TAG, " Port number: %d", this->hardware_idx_);
}
+4 -2
View File
@@ -9,8 +9,10 @@ namespace update {
static const char *const TAG = "update";
void UpdateEntity::publish_state() {
ESP_LOGD(TAG, "'%s' - Publishing:", this->name_.c_str());
ESP_LOGD(TAG, " Current Version: %s", this->update_info_.current_version.c_str());
ESP_LOGD(TAG,
"'%s' - Publishing:\n"
" Current Version: %s",
this->name_.c_str(), this->update_info_.current_version.c_str());
if (!this->update_info_.md5.empty()) {
ESP_LOGD(TAG, " Latest Version: %s", this->update_info_.latest_version.c_str());
@@ -152,8 +152,10 @@ void WaterHeater::setup() {
void WaterHeater::publish_state() {
auto traits = this->get_traits();
ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(water_heater_mode_to_string(this->mode_)));
ESP_LOGD(TAG,
"'%s' - Sending state:\n"
" Mode: %s",
this->name_.c_str(), LOG_STR_ARG(water_heater_mode_to_string(this->mode_)));
if (!std::isnan(this->current_temperature_)) {
ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature_);
}
+32 -10
View File
@@ -781,8 +781,10 @@ void WiFiComponent::start_connecting(const WiFiAP &ap) {
get_max_retries_for_phase(this->retry_phase_), LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_)));
#ifdef ESPHOME_LOG_HAS_VERBOSE
ESP_LOGV(TAG, "Connection Params:");
ESP_LOGV(TAG, " SSID: '%s'", ap.get_ssid().c_str());
ESP_LOGV(TAG,
"Connection Params:\n"
" SSID: '%s'",
ap.get_ssid().c_str());
if (ap.has_bssid()) {
ESP_LOGV(TAG, " BSSID: %s", bssid_s);
} else {
@@ -791,20 +793,28 @@ void WiFiComponent::start_connecting(const WiFiAP &ap) {
#ifdef USE_WIFI_WPA2_EAP
if (ap.get_eap().has_value()) {
ESP_LOGV(TAG, " WPA2 Enterprise authentication configured:");
EAPAuth eap_config = ap.get_eap().value();
ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str());
ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str());
ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str());
// clang-format off
ESP_LOGV(
TAG,
" WPA2 Enterprise authentication configured:\n"
" Identity: " LOG_SECRET("'%s'") "\n"
" Username: " LOG_SECRET("'%s'") "\n"
" Password: " LOG_SECRET("'%s'"),
eap_config.identity.c_str(), eap_config.username.c_str(), eap_config.password.c_str());
// clang-format on
#if defined(USE_ESP32) && defined(USE_WIFI_WPA2_EAP) && ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
ESP_LOGV(TAG, " TTLS Phase 2: " LOG_SECRET("'%s'"), eap_phase2_to_str(eap_config.ttls_phase_2));
#endif
bool ca_cert_present = eap_config.ca_cert != nullptr && strlen(eap_config.ca_cert);
bool client_cert_present = eap_config.client_cert != nullptr && strlen(eap_config.client_cert);
bool client_key_present = eap_config.client_key != nullptr && strlen(eap_config.client_key);
ESP_LOGV(TAG, " CA Cert: %s", ca_cert_present ? "present" : "not present");
ESP_LOGV(TAG, " Client Cert: %s", client_cert_present ? "present" : "not present");
ESP_LOGV(TAG, " Client Key: %s", client_key_present ? "present" : "not present");
ESP_LOGV(TAG,
" CA Cert: %s\n"
" Client Cert: %s\n"
" Client Key: %s",
ca_cert_present ? "present" : "not present", client_cert_present ? "present" : "not present",
client_key_present ? "present" : "not present");
} else {
#endif
ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), ap.get_password().c_str());
@@ -1049,15 +1059,27 @@ template<typename VectorType> static void insertion_sort_scan_results(VectorType
}
// Helper function to log matching scan results - marked noinline to prevent re-inlining into loop
//
// IMPORTANT: This function deliberately uses a SINGLE log call to minimize blocking.
// In environments with many matching networks (e.g., 18+ mesh APs), multiple log calls
// per network would block the main loop for an unacceptable duration. Each log call
// has overhead from UART transmission, so combining INFO+DEBUG into one line halves
// the blocking time. Do NOT split this into separate ESP_LOGI/ESP_LOGD calls.
__attribute__((noinline)) static void log_scan_result(const WiFiScanResult &res) {
char bssid_s[18];
auto bssid = res.get_bssid();
format_mac_addr_upper(bssid.data(), bssid_s);
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
// Single combined log line with all details when DEBUG enabled
ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s Ch:%2u %3ddB P:%d", res.get_ssid().c_str(),
res.get_is_hidden() ? LOG_STR_LITERAL("(HIDDEN) ") : LOG_STR_LITERAL(""), bssid_s,
LOG_STR_ARG(get_signal_bars(res.get_rssi())), res.get_channel(), res.get_rssi(), res.get_priority());
#else
ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(),
res.get_is_hidden() ? LOG_STR_LITERAL("(HIDDEN) ") : LOG_STR_LITERAL(""), bssid_s,
LOG_STR_ARG(get_signal_bars(res.get_rssi())));
ESP_LOGD(TAG, " Channel: %2u, RSSI: %3d dB, Priority: %4d", res.get_channel(), res.get_rssi(), res.get_priority());
#endif
}
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
@@ -518,8 +518,12 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
switch (event->event) {
case EVENT_STAMODE_CONNECTED: {
auto it = event->event_info.connected;
ESP_LOGV(TAG, "Connected ssid='%.*s' bssid=%s channel=%u", it.ssid_len, (const char *) it.ssid,
format_mac_address_pretty(it.bssid).c_str(), it.channel);
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char bssid_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.bssid, bssid_buf);
ESP_LOGV(TAG, "Connected ssid='%.*s' bssid=%s channel=%u", it.ssid_len, (const char *) it.ssid, bssid_buf,
it.channel);
#endif
s_sta_connected = true;
#ifdef USE_WIFI_LISTENERS
for (auto *listener : global_wifi_component->connect_state_listeners_) {
@@ -594,18 +598,30 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
break;
}
case EVENT_SOFTAPMODE_STACONNECTED: {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
auto it = event->event_info.sta_connected;
ESP_LOGV(TAG, "AP client connected MAC=%s aid=%u", format_mac_address_pretty(it.mac).c_str(), it.aid);
char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.mac, mac_buf);
ESP_LOGV(TAG, "AP client connected MAC=%s aid=%u", mac_buf, it.aid);
#endif
break;
}
case EVENT_SOFTAPMODE_STADISCONNECTED: {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
auto it = event->event_info.sta_disconnected;
ESP_LOGV(TAG, "AP client disconnected MAC=%s aid=%u", format_mac_address_pretty(it.mac).c_str(), it.aid);
char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.mac, mac_buf);
ESP_LOGV(TAG, "AP client disconnected MAC=%s aid=%u", mac_buf, it.aid);
#endif
break;
}
case EVENT_SOFTAPMODE_PROBEREQRECVED: {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
auto it = event->event_info.ap_probereqrecved;
ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_address_pretty(it.mac).c_str(), it.rssi);
char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.mac, mac_buf);
ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", mac_buf, it.rssi);
#endif
break;
}
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0)
@@ -616,9 +632,12 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
break;
}
case EVENT_SOFTAPMODE_DISTRIBUTE_STA_IP: {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
auto it = event->event_info.distribute_sta_ip;
ESP_LOGV(TAG, "AP Distribute Station IP MAC=%s IP=%s aid=%u", format_mac_address_pretty(it.mac).c_str(),
format_ip_addr(it.ip).c_str(), it.aid);
char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.mac, mac_buf);
ESP_LOGV(TAG, "AP Distribute Station IP MAC=%s IP=%s aid=%u", mac_buf, format_ip_addr(it.ip).c_str(), it.aid);
#endif
break;
}
#endif
@@ -734,9 +734,12 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_CONNECTED) {
const auto &it = data->data.sta_connected;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char bssid_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.bssid, bssid_buf);
ESP_LOGV(TAG, "Connected ssid='%.*s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", it.ssid_len,
(const char *) it.ssid, format_mac_address_pretty(it.bssid).c_str(), it.channel,
get_auth_mode_str(it.authmode));
(const char *) it.ssid, bssid_buf, it.channel, get_auth_mode_str(it.authmode));
#endif
s_sta_connected = true;
#ifdef USE_WIFI_LISTENERS
for (auto *listener : this->connect_state_listeners_) {
@@ -855,16 +858,28 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
this->ap_started_ = false;
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
const auto &it = data->data.ap_probe_req_rx;
ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_address_pretty(it.mac).c_str(), it.rssi);
char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.mac, mac_buf);
ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", mac_buf, it.rssi);
#endif
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STACONNECTED) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
const auto &it = data->data.ap_staconnected;
ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_address_pretty(it.mac).c_str());
char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.mac, mac_buf);
ESP_LOGV(TAG, "AP client connected MAC=%s", mac_buf);
#endif
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STADISCONNECTED) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
const auto &it = data->data.ap_stadisconnected;
ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_address_pretty(it.mac).c_str());
char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(it.mac, mac_buf);
ESP_LOGV(TAG, "AP client disconnected MAC=%s", mac_buf);
#endif
} else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_AP_STAIPASSIGNED) {
const auto &it = data->data.ip_ap_staipassigned;
+11 -8
View File
@@ -71,17 +71,20 @@ void WTS01Sensor::process_packet_() {
}
// Extract temperature value
int8_t temp = this->buffer_[6];
int32_t sign = 1;
const uint8_t raw = this->buffer_[6];
// Handle negative temperatures
if (temp < 0) {
sign = -1;
// WTS01 encodes sign in bit 7, magnitude in bits 0-6
const bool negative = (raw & 0x80) != 0;
const uint8_t magnitude = raw & 0x7F;
const float decimal = static_cast<float>(this->buffer_[7]) / 100.0f;
float temperature = static_cast<float>(magnitude) + decimal;
if (negative) {
temperature = -temperature;
}
// Calculate temperature (temp + decimal/100)
float temperature = static_cast<float>(temp) + (sign * static_cast<float>(this->buffer_[7]) / 100.0f);
ESP_LOGV(TAG, "Received new temperature: %.2f°C", temperature);
this->publish_state(temperature);
+10 -20
View File
@@ -21,7 +21,6 @@ from .const import (
)
CODEOWNERS = ["@tomaszduda23"]
AUTO_LOAD = ["preferences"]
PrjConfValueType = bool | str | int
@@ -111,32 +110,15 @@ def add_extra_script(stage: str, filename: str, path: Path) -> None:
def zephyr_to_code(config):
cg.add(zephyr_ns.setup_preferences())
cg.add_build_flag("-DUSE_ZEPHYR")
cg.set_cpp_standard("gnu++20")
# build is done by west so bypass board checking in platformio
cg.add_platformio_option("boards_dir", CORE.relative_build_path("boards"))
# c++ support
zephyr_add_prj_conf("NEWLIB_LIBC", True)
zephyr_add_prj_conf("CONFIG_FPU", True)
zephyr_add_prj_conf("FPU", True)
zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True)
zephyr_add_prj_conf("CPLUSPLUS", True)
zephyr_add_prj_conf("CONFIG_STD_CPP20", True)
zephyr_add_prj_conf("LIB_CPLUSPLUS", True)
# preferences
zephyr_add_prj_conf("SETTINGS", True)
zephyr_add_prj_conf("NVS", True)
zephyr_add_prj_conf("FLASH_MAP", True)
zephyr_add_prj_conf("CONFIG_FLASH", True)
# watchdog
zephyr_add_prj_conf("WATCHDOG", True)
zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False)
# disable console
zephyr_add_prj_conf("UART_CONSOLE", False)
zephyr_add_prj_conf("CONSOLE", False, False)
# use NFC pins as GPIO
zephyr_add_prj_conf("NFCT_PINS_AS_GPIOS", True)
zephyr_add_prj_conf("STD_CPP20", True)
# <err> os: ***** USAGE FAULT *****
# <err> os: Illegal load of EXC_RETURN into PC
@@ -149,6 +131,14 @@ def zephyr_to_code(config):
)
def zephyr_setup_preferences():
cg.add(zephyr_ns.setup_preferences())
zephyr_add_prj_conf("SETTINGS", True)
zephyr_add_prj_conf("NVS", True)
zephyr_add_prj_conf("FLASH_MAP", True)
zephyr_add_prj_conf("FLASH", True)
def _format_prj_conf_val(value: PrjConfValueType) -> str:
if isinstance(value, bool):
return "y" if value else "n"
+8 -1
View File
@@ -10,8 +10,10 @@
namespace esphome {
#ifdef CONFIG_WATCHDOG
static int wdt_channel_id = -1; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
static const device *const WDT = DEVICE_DT_GET(DT_ALIAS(watchdog0));
#endif
void yield() { ::k_yield(); }
uint32_t millis() { return k_ticks_to_ms_floor32(k_uptime_ticks()); }
@@ -20,6 +22,7 @@ void delayMicroseconds(uint32_t us) { ::k_usleep(us); }
void delay(uint32_t ms) { ::k_msleep(ms); }
void arch_init() {
#ifdef CONFIG_WATCHDOG
if (device_is_ready(WDT)) {
static wdt_timeout_cfg wdt_config{};
wdt_config.flags = WDT_FLAG_RESET_SOC;
@@ -36,12 +39,15 @@ void arch_init() {
wdt_setup(WDT, options);
}
}
#endif
}
void arch_feed_wdt() {
#ifdef CONFIG_WATCHDOG
if (wdt_channel_id >= 0) {
wdt_feed(WDT, wdt_channel_id);
}
#endif
}
void arch_restart() { sys_reboot(SYS_REBOOT_COLD); }
@@ -72,6 +78,7 @@ bool random_bytes(uint8_t *data, size_t len) {
return true;
}
#ifdef USE_NRF52
void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter)
mac[0] = ((NRF_FICR->DEVICEADDR[1] & 0xFFFF) >> 8) | 0xC0;
mac[1] = NRF_FICR->DEVICEADDR[1] & 0xFFFF;
@@ -80,7 +87,7 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame
mac[4] = NRF_FICR->DEVICEADDR[0] >> 8;
mac[5] = NRF_FICR->DEVICEADDR[0];
}
#endif
} // namespace esphome
void setup();
+6 -23
View File
@@ -50,25 +50,7 @@ void ZephyrGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::Inte
}
void ZephyrGPIOPin::setup() {
const struct device *gpio = nullptr;
if (this->pin_ < 32) {
#define GPIO0 DT_NODELABEL(gpio0)
#if DT_NODE_HAS_STATUS(GPIO0, okay)
gpio = DEVICE_DT_GET(GPIO0);
#else
#error "gpio0 is disabled"
#endif
} else {
#define GPIO1 DT_NODELABEL(gpio1)
#if DT_NODE_HAS_STATUS(GPIO1, okay)
gpio = DEVICE_DT_GET(GPIO1);
#else
#error "gpio1 is disabled"
#endif
}
if (device_is_ready(gpio)) {
this->gpio_ = gpio;
} else {
if (!device_is_ready(this->gpio_)) {
ESP_LOGE(TAG, "gpio %u is not ready.", this->pin_);
return;
}
@@ -79,21 +61,22 @@ void ZephyrGPIOPin::pin_mode(gpio::Flags flags) {
if (nullptr == this->gpio_) {
return;
}
auto ret = gpio_pin_configure(this->gpio_, this->pin_ % 32, flags_to_mode(flags, this->inverted_, this->value_));
auto ret = gpio_pin_configure(this->gpio_, this->pin_ % this->gpio_size_,
flags_to_mode(flags, this->inverted_, this->value_));
if (ret != 0) {
ESP_LOGE(TAG, "gpio %u cannot be configured %d.", this->pin_, ret);
}
}
size_t ZephyrGPIOPin::dump_summary(char *buffer, size_t len) const {
return snprintf(buffer, len, "GPIO%u, P%u.%u", this->pin_, this->pin_ / 32, this->pin_ % 32);
return snprintf(buffer, len, "GPIO%u, %s%u", this->pin_, this->pin_name_prefix_, this->pin_ % this->gpio_size_);
}
bool ZephyrGPIOPin::digital_read() {
if (nullptr == this->gpio_) {
return false;
}
return bool(gpio_pin_get(this->gpio_, this->pin_ % 32) != this->inverted_);
return bool(gpio_pin_get(this->gpio_, this->pin_ % this->gpio_size_) != this->inverted_);
}
void ZephyrGPIOPin::digital_write(bool value) {
@@ -103,7 +86,7 @@ void ZephyrGPIOPin::digital_write(bool value) {
if (nullptr == this->gpio_) {
return;
}
gpio_pin_set(this->gpio_, this->pin_ % 32, value != this->inverted_ ? 1 : 0);
gpio_pin_set(this->gpio_, this->pin_ % this->gpio_size_, value != this->inverted_ ? 1 : 0);
}
void ZephyrGPIOPin::detach_interrupt() const {
// TODO
+10 -3
View File
@@ -8,6 +8,11 @@ namespace zephyr {
class ZephyrGPIOPin : public InternalGPIOPin {
public:
ZephyrGPIOPin(const device *gpio, int gpio_size, const char *pin_name_prefix) {
this->gpio_ = gpio;
this->gpio_size_ = gpio_size;
this->pin_name_prefix_ = pin_name_prefix;
}
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_inverted(bool inverted) { this->inverted_ = inverted; }
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
@@ -25,10 +30,12 @@ class ZephyrGPIOPin : public InternalGPIOPin {
protected:
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
uint8_t pin_;
bool inverted_{};
gpio::Flags flags_{};
const device *gpio_{nullptr};
const char *pin_name_prefix_{nullptr};
gpio::Flags flags_{};
uint8_t pin_;
uint8_t gpio_size_{};
bool inverted_{};
bool value_{false};
};
@@ -1,4 +1,5 @@
#ifdef USE_ZEPHYR
#ifdef CONFIG_SETTINGS
#include <zephyr/kernel.h>
#include "esphome/core/preferences.h"
@@ -154,3 +155,4 @@ ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const
} // namespace esphome
#endif
#endif
+7 -1
View File
@@ -205,7 +205,13 @@ void Component::call() {
this->call_setup();
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
uint32_t setup_time = millis() - start_time;
ESP_LOGCONFIG(TAG, "Setup %s took %ums", LOG_STR_ARG(this->get_component_log_str()), (unsigned) setup_time);
// Only log at CONFIG level if setup took longer than the blocking threshold
// to avoid spamming the log and blocking the event loop
if (setup_time >= WARN_IF_BLOCKING_OVER_MS) {
ESP_LOGCONFIG(TAG, "Setup %s took %ums", LOG_STR_ARG(this->get_component_log_str()), (unsigned) setup_time);
} else {
ESP_LOGV(TAG, "Setup %s took %ums", LOG_STR_ARG(this->get_component_log_str()), (unsigned) setup_time);
}
#endif
break;
}
+2 -2
View File
@@ -643,7 +643,7 @@ async def get_variable(id_: ID) -> "MockObj":
Wait for the given ID to be defined in the code generation and
return it as a MockObj.
This is a coroutine, you need to await it with a 'await' expression!
This is a coroutine, you need to await it with an 'await' expression!
:param id_: The ID to retrieve
:return: The variable as a MockObj.
@@ -656,7 +656,7 @@ async def get_variable_with_full_id(id_: ID) -> tuple[ID, "MockObj"]:
Wait for the given ID to be defined in the code generation and
return it as a MockObj.
This is a coroutine, you need to await it with a 'await' expression!
This is a coroutine, you need to await it with an 'await' expression!
:param id_: The ID to retrieve
:return: The variable as a MockObj.
+8 -2
View File
@@ -12,14 +12,20 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
esptool==5.1.0
click==8.1.7
esphome-dashboard==20251013.0
aioesphomeapi==43.10.0
aioesphomeapi==43.10.1
zeroconf==0.148.0
puremagic==1.30
ruamel.yaml==0.19.1 # dashboard_import
ruamel.yaml.clib==0.2.15 # dashboard_import
esphome-glyphsets==0.2.0
pillow==11.3.0
cairosvg==2.8.2
# pycairo fork for Windows
cairosvg @ git+https://github.com/clydebarrow/cairosvg.git@release ; sys_platform == 'win32'
# Original for everything else
cairosvg==2.8.2 ; sys_platform != 'win32'
freetype-py==2.5.1
jinja2==3.1.6
bleak==2.1.1
+1
View File
@@ -6,3 +6,4 @@ sensor:
name: MH-Z19 Temperature
automatic_baseline_calibration: false
update_interval: 15s
detection_range: 5000ppm
@@ -9,6 +9,18 @@ esphome:
id: template_sens
state: !lambda "return 42.0;"
- water_heater.template.publish:
id: template_water_heater
target_temperature: 50.0
mode: ECO
# Templated
- water_heater.template.publish:
id: template_water_heater
current_temperature: !lambda "return 45.0;"
target_temperature: !lambda "return 55.0;"
mode: !lambda "return water_heater::WATER_HEATER_MODE_GAS;"
# Test C++ API: set_template() with stateless lambda (no captures)
# NOTE: set_template() is not intended to be a public API, but we test it to ensure it doesn't break.
- lambda: |-
@@ -299,6 +311,24 @@ alarm_control_panel:
codes:
- "1234"
water_heater:
- platform: template
id: template_water_heater
name: "Template Water Heater"
optimistic: true
current_temperature: !lambda "return 42.0f;"
mode: !lambda "return water_heater::WATER_HEATER_MODE_ECO;"
supported_modes:
- "OFF"
- ECO
- GAS
- ELECTRIC
- HEAT_PUMP
- HIGH_DEMAND
- PERFORMANCE
set_action:
- logger.log: "set_action"
datetime:
- platform: template
name: Date
+16
View File
@@ -0,0 +1,16 @@
water_heater:
- platform: template
id: my_boiler
name: "Test Boiler"
min_temperature: 10
max_temperature: 85
target_temperature_step: 0.5
current_temperature_step: 0.1
optimistic: true
current_temperature: 45.0
mode: !lambda "return water_heater::WATER_HEATER_MODE_ECO;"
visual:
min_temperature: 10
max_temperature: 85
target_temperature_step: 0.5
current_temperature_step: 0.1
+1
View File
@@ -36,3 +36,4 @@ datetime:
optimistic: yes
event:
update:
water_heater:
@@ -0,0 +1,23 @@
esphome:
name: water-heater-template-test
host:
api:
logger:
water_heater:
- platform: template
id: test_boiler
name: Test Boiler
optimistic: true
current_temperature: !lambda "return 45.0f;"
# Note: No mode lambda - we want optimistic mode changes to stick
# A mode lambda would override mode changes in loop()
supported_modes:
- "off"
- eco
- gas
- performance
visual:
min_temperature: 30.0
max_temperature: 85.0
target_temperature_step: 0.5
@@ -0,0 +1,109 @@
"""Integration test for template water heater component."""
from __future__ import annotations
import asyncio
import aioesphomeapi
from aioesphomeapi import WaterHeaterInfo, WaterHeaterMode, WaterHeaterState
import pytest
from .state_utils import InitialStateHelper
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_water_heater_template(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Test template water heater basic state and mode changes."""
loop = asyncio.get_running_loop()
async with run_compiled(yaml_config), api_client_connected() as client:
states: dict[int, aioesphomeapi.EntityState] = {}
gas_mode_future: asyncio.Future[WaterHeaterState] = loop.create_future()
eco_mode_future: asyncio.Future[WaterHeaterState] = loop.create_future()
def on_state(state: aioesphomeapi.EntityState) -> None:
states[state.key] = state
if isinstance(state, WaterHeaterState):
# Wait for GAS mode
if state.mode == WaterHeaterMode.GAS and not gas_mode_future.done():
gas_mode_future.set_result(state)
# Wait for ECO mode (we start at OFF, so test transitioning to ECO)
elif state.mode == WaterHeaterMode.ECO and not eco_mode_future.done():
eco_mode_future.set_result(state)
# Get entities and set up state synchronization
entities, services = await client.list_entities_services()
initial_state_helper = InitialStateHelper(entities)
water_heater_infos = [e for e in entities if isinstance(e, WaterHeaterInfo)]
assert len(water_heater_infos) == 1, (
f"Expected exactly 1 water heater entity, got {len(water_heater_infos)}. Entity types: {[type(e).__name__ for e in entities]}"
)
test_water_heater = water_heater_infos[0]
# Verify water heater entity info
assert test_water_heater.object_id == "test_boiler"
assert test_water_heater.name == "Test Boiler"
assert test_water_heater.min_temperature == 30.0
assert test_water_heater.max_temperature == 85.0
assert test_water_heater.target_temperature_step == 0.5
# Verify supported modes
supported_modes = test_water_heater.supported_modes
assert WaterHeaterMode.OFF in supported_modes, "Expected OFF in supported modes"
assert WaterHeaterMode.ECO in supported_modes, "Expected ECO in supported modes"
assert WaterHeaterMode.GAS in supported_modes, "Expected GAS in supported modes"
assert WaterHeaterMode.PERFORMANCE in supported_modes, (
"Expected PERFORMANCE in supported modes"
)
assert len(supported_modes) == 4, (
f"Expected 4 supported modes, got {len(supported_modes)}: {supported_modes}"
)
# Subscribe with the wrapper that filters initial states
client.subscribe_states(initial_state_helper.on_state_wrapper(on_state))
# Wait for all initial states to be broadcast
try:
await initial_state_helper.wait_for_initial_states()
except TimeoutError:
pytest.fail("Timeout waiting for initial states")
# Get initial state and verify
initial_state = initial_state_helper.initial_states.get(test_water_heater.key)
assert initial_state is not None, "Water heater initial state not found"
assert isinstance(initial_state, WaterHeaterState)
# Initial mode is OFF (default) since we don't have a mode lambda
# A mode lambda would override optimistic mode changes
assert initial_state.mode == WaterHeaterMode.OFF, (
f"Expected initial mode OFF, got {initial_state.mode}"
)
assert initial_state.current_temperature == 45.0, (
f"Expected current temp 45.0, got {initial_state.current_temperature}"
)
# Test changing to GAS mode
client.water_heater_command(test_water_heater.key, mode=WaterHeaterMode.GAS)
try:
gas_state = await asyncio.wait_for(gas_mode_future, timeout=5.0)
except TimeoutError:
pytest.fail("GAS mode change not received within 5 seconds")
assert isinstance(gas_state, WaterHeaterState)
assert gas_state.mode == WaterHeaterMode.GAS
# Test changing to ECO mode (from GAS)
client.water_heater_command(test_water_heater.key, mode=WaterHeaterMode.ECO)
try:
eco_state = await asyncio.wait_for(eco_mode_future, timeout=5.0)
except TimeoutError:
pytest.fail("ECO mode change not received within 5 seconds")
assert isinstance(eco_state, WaterHeaterState)
assert eco_state.mode == WaterHeaterMode.ECO