1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-01 15:41:52 +00:00

Compare commits

...

47 Commits

Author SHA1 Message Date
Jesse Hills
1821ddd996 Merge pull request #6122 from esphome/bump-2023.12.8
2023.12.8
2024-01-20 04:22:36 +13:00
Jesse Hills
aee702f84f Bump version to 2023.12.8 2024-01-19 23:40:26 +09:00
Jesse Hills
d5fe5b0899 Fix some Voice Assistant bugs (#6121) 2024-01-19 23:40:25 +09:00
guillempages
bd7fe1227c Let show_*_page actions depend on "Display" (#6092)
Instead of forcing a DisplayBuffer, let the display page actions use Displays without buffer.
2024-01-19 23:40:25 +09:00
Jesse Hills
7dced7f55d Merge pull request #6104 from esphome/bump-2023.12.7
2023.12.7
2024-01-17 13:24:00 +13:00
Jesse Hills
36de644065 Bump version to 2023.12.7 2024-01-17 08:27:03 +09:00
Clyde Stubbs
95292dbba1 Inkplate6: Fix crash with initial set of greyscale (#6038) 2024-01-17 08:27:03 +09:00
Jesse Hills
86c9546362 Create RingBuffer for VoiceAssistant (#6102) 2024-01-17 08:27:03 +09:00
Piotr Majkrzak
f37a812e59 Fix RMT timing clock base (#6101) 2024-01-17 08:27:03 +09:00
Jesse Hills
78a6509fb1 Merge pull request #6097 from esphome/bump-2023.12.6
2023.12.6
2024-01-15 13:06:58 +13:00
Jesse Hills
534c14e313 Bump version to 2023.12.6 2024-01-15 08:25:48 +09:00
J. Nick Koston
3fec8f9b53 Fallback to pure-python loader for better error when YAML loading fails (#6081) 2024-01-15 08:25:41 +09:00
tomaszduda23
b8b6462844 add STATE_CLASS_TOTAL_INCREASING to bl0940 and bl0942 (#6090) 2024-01-15 08:23:56 +09:00
Keith Burzinski
59e7c52341 Improv Serial -- don't wait for incoming bytes (#6089) 2024-01-15 08:23:55 +09:00
Keith Burzinski
ff7de4c971 ESP32-C3 USB_CDC fixes (#6069) 2024-01-15 08:23:55 +09:00
Robert Paskowitz
978a676c7c Support full (>460 char) dumps of Pronto IR commands (#6040)
Co-authored-by: Rob Paskowitz <rob@paskowitz.ca>
2024-01-15 08:23:55 +09:00
functionpointer
33051906bd pylontech: Fix parsing error with US2000 (#6061) 2024-01-15 08:23:55 +09:00
tomaszduda23
da56d333dc fix compilation error for libretiny (#6064) 2024-01-15 08:23:55 +09:00
J. Nick Koston
48a4e6bae9 Fix device not requesting Home Assistant time at the update interval (#6022) 2024-01-15 08:23:55 +09:00
Jesse Hills
41dc73d228 Merge pull request #6017 from esphome/bump-2023.12.5
2023.12.5
2023-12-26 02:44:59 +13:00
Jesse Hills
6ceefe08ab Bump version to 2023.12.5 2023-12-25 22:24:13 +09:00
J. Nick Koston
21e5806a73 Fix docker builds (#6012) 2023-12-25 22:24:13 +09:00
Jesse Hills
4fd79fee2c Merge pull request #6016 from esphome/bump-2023.12.4
2023.12.4
2023-12-26 01:45:41 +13:00
Jesse Hills
4c8c4a2579 Bump version to 2023.12.4 2023-12-25 21:14:55 +09:00
NP v/d Spek
b68420b2cc Display: fix class inherence in Python script (#6009) 2023-12-25 21:14:55 +09:00
J. Nick Koston
7bce999bba dashboard: Fix file writes on Windows (#6013) 2023-12-25 21:14:55 +09:00
NP v/d Spek
dc0cc0b431 tt21100: restore init read (#6008) 2023-12-25 21:14:55 +09:00
J. Nick Koston
0990d0812e dashboard: Only ping when polling is active (#6001)
fixes https://github.com/esphome/issues/issues/5257
2023-12-25 21:14:55 +09:00
Jesse Hills
35388cf2a2 Merge pull request #5994 from esphome/bump-2023.12.3
2023.12.3
2023-12-22 21:42:41 +13:00
Jesse Hills
417e37d291 Bump version to 2023.12.3 2023-12-22 17:12:42 +09:00
Jesse Hills
7dc35a1029 Fix broken configs with non-existent components (#5993) 2023-12-22 17:12:42 +09:00
J. Nick Koston
9202a30dc7 Fix dashboard logs when api is disabled and using MQTT (#5992) 2023-12-22 17:12:42 +09:00
matzman666
45f9f3d972 Improved sensor readings in htu21d component. (#5839) 2023-12-22 17:12:42 +09:00
Jesse Hills
46310ff223 Regenerate api_pb2 after manual changes were added incorrectly in #5732 (#5990) 2023-12-22 17:12:42 +09:00
J. Nick Koston
f5c99d1647 Fix unexpected disconnects when outgoing buffer is full during keepalive (#5988) 2023-12-22 17:12:42 +09:00
Jesse Hills
9b72a3a584 Merge pull request #5989 from esphome/bump-2023.12.2
2023.12.2
2023-12-22 15:57:09 +13:00
Jesse Hills
19e5a4a81a Bump version to 2023.12.2 2023-12-22 11:04:00 +09:00
Jessica Hamilton
8e13c3e1b0 web_server.py: return empty content when file doesn't exist (#5980)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-12-22 11:04:00 +09:00
Keith Burzinski
4f8e3211bf Add workaround for crash in Arduino 2.0.9 when CDC is configured (#5987) 2023-12-22 11:04:00 +09:00
davidmonro
872519f7f6 Override GPIOs 12 and 13 on the airm2m (LuatOS) board (#5982)
Co-authored-by: David Monro <david.monro@anu.edu.au>
2023-12-22 11:04:00 +09:00
CVan
2a69a49061 Update libtiff6 (#5985) 2023-12-22 11:04:00 +09:00
Clyde Stubbs
1a8e7854c7 ESP32-S3 and ESP-IDF don't play well with USB_CDC and need USB_SERIAL_JTAG (#5929)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2023-12-22 11:04:00 +09:00
Jesse Hills
6d3c7f035d Merge pull request #5984 from esphome/bump-2023.12.1
2023.12.1
2023-12-22 04:02:39 +13:00
Jesse Hills
00ab17cb8e Bump version to 2023.12.1 2023-12-21 23:33:35 +09:00
Jesse Hills
2ee01e22cd Fix replaced - in allowed characters during object_id sanitizing (#5983) 2023-12-21 23:33:35 +09:00
Jesse Hills
cfa5e5c5a9 Merge pull request #5977 from esphome/bump-2023.12.0
2023.12.0
2023-12-21 08:47:53 +09:00
Jesse Hills
8675955614 Bump version to 2023.12.0 2023-12-21 08:09:10 +09:00
41 changed files with 566 additions and 183 deletions

View File

@@ -34,7 +34,7 @@ RUN \
python3-wheel=0.38.4-2 \
iputils-ping=3:20221126-1 \
git=1:2.39.2-1.1 \
curl=7.88.1-10+deb12u4 \
curl=7.88.1-10+deb12u5 \
openssh-client=1:9.2p1-2+deb12u1 \
python3-cffi=1.15.1-5 \
libcairo2=1.16.0-7 \
@@ -50,7 +50,7 @@ RUN \
libssl-dev=3.0.11-1~deb12u2 \
libffi-dev=3.4.4-1 \
libopenjp2-7=2.5.0-2 \
libtiff6=4.5.0-6 \
libtiff6=4.5.0-6+deb12u1 \
cargo=0.66.0+ds1-1 \
pkg-config=1.8.1-1 \
gcc-arm-linux-gnueabihf=4:12.2.0-3; \

View File

@@ -118,7 +118,9 @@ void APIConnection::loop() {
this->list_entities_iterator_.advance();
this->initial_state_iterator_.advance();
const uint32_t keepalive = 60000;
static uint32_t keepalive = 60000;
static uint8_t max_ping_retries = 60;
static uint16_t ping_retry_interval = 1000;
const uint32_t now = millis();
if (this->sent_ping_) {
// Disconnect if not responded within 2.5*keepalive
@@ -126,10 +128,24 @@ void APIConnection::loop() {
on_fatal_error();
ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_combined_info_.c_str());
}
} else if (now - this->last_traffic_ > keepalive) {
} else if (now - this->last_traffic_ > keepalive && now > this->next_ping_retry_) {
ESP_LOGVV(TAG, "Sending keepalive PING...");
this->sent_ping_ = true;
this->send_ping_request(PingRequest());
this->sent_ping_ = this->send_ping_request(PingRequest());
if (!this->sent_ping_) {
this->next_ping_retry_ = now + ping_retry_interval;
this->ping_retries_++;
if (this->ping_retries_ >= max_ping_retries) {
on_fatal_error();
ESP_LOGE(TAG, "%s: Sending keepalive failed %d time(s). Disconnecting...", this->client_combined_info_.c_str(),
this->ping_retries_);
} else if (this->ping_retries_ >= 10) {
ESP_LOGW(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms",
this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval);
} else {
ESP_LOGD(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms",
this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval);
}
}
}
#ifdef USE_ESP32_CAMERA

View File

@@ -140,6 +140,7 @@ class APIConnection : public APIServerConnection {
void on_disconnect_response(const DisconnectResponse &value) override;
void on_ping_response(const PingResponse &value) override {
// we initiated ping
this->ping_retries_ = 0;
this->sent_ping_ = false;
}
void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override;
@@ -217,6 +218,8 @@ class APIConnection : public APIServerConnection {
bool state_subscription_{false};
int log_subscription_{ESPHOME_LOG_LEVEL_NONE};
uint32_t last_traffic_;
uint32_t next_ping_retry_{0};
uint8_t ping_retries_{0};
bool sent_ping_{false};
bool service_call_subscription_{false};
bool next_close_ = false;

View File

@@ -3848,6 +3848,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
sprintf(buffer, "%g", this->visual_max_humidity);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
@@ -4015,6 +4016,7 @@ void ClimateStateResponse::dump_to(std::string &out) const {
sprintf(buffer, "%g", this->target_humidity);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {

View File

@@ -319,7 +319,7 @@ void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeo
#ifdef USE_HOMEASSISTANT_TIME
void APIServer::request_time() {
for (auto &client : this->clients_) {
if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED)
if (!client->remove_ && client->is_authenticated())
client->send_time_request();
}
}

View File

@@ -18,6 +18,7 @@ from esphome.const import (
UNIT_KILOWATT_HOURS,
UNIT_VOLT,
UNIT_WATT,
STATE_CLASS_TOTAL_INCREASING,
)
DEPENDENCIES = ["uart"]
@@ -54,6 +55,7 @@ CONFIG_SCHEMA = (
unit_of_measurement=UNIT_KILOWATT_HOURS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,

View File

@@ -19,6 +19,7 @@ from esphome.const import (
UNIT_VOLT,
UNIT_WATT,
UNIT_HERTZ,
STATE_CLASS_TOTAL_INCREASING,
)
DEPENDENCIES = ["uart"]
@@ -52,6 +53,7 @@ CONFIG_SCHEMA = (
unit_of_measurement=UNIT_KILOWATT_HOURS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
unit_of_measurement=UNIT_HERTZ,

View File

@@ -18,8 +18,8 @@ from esphome.core import coroutine_with_priority
IS_PLATFORM_COMPONENT = True
display_ns = cg.esphome_ns.namespace("display")
Display = display_ns.class_("Display")
DisplayBuffer = display_ns.class_("DisplayBuffer")
Display = display_ns.class_("Display", cg.PollingComponent)
DisplayBuffer = display_ns.class_("DisplayBuffer", Display)
DisplayPage = display_ns.class_("DisplayPage")
DisplayPagePtr = DisplayPage.operator("ptr")
DisplayRef = Display.operator("ref")
@@ -145,7 +145,7 @@ async def display_page_show_to_code(config, action_id, template_arg, args):
DisplayPageShowNextAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)),
cv.Required(CONF_ID): cv.templatable(cv.use_id(Display)),
}
),
)
@@ -159,7 +159,7 @@ async def display_page_show_next_to_code(config, action_id, template_arg, args):
DisplayPageShowPrevAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)),
cv.Required(CONF_ID): cv.templatable(cv.use_id(Display)),
}
),
)
@@ -173,7 +173,7 @@ async def display_page_show_previous_to_code(config, action_id, template_arg, ar
DisplayIsDisplayingPageCondition,
cv.maybe_simple_value(
{
cv.GenerateID(CONF_ID): cv.use_id(DisplayBuffer),
cv.GenerateID(CONF_ID): cv.use_id(Display),
cv.Required(CONF_PAGE_ID): cv.use_id(DisplayPage),
},
key=CONF_PAGE_ID,

View File

@@ -133,6 +133,10 @@ ESP32_BOARD_PINS = {
"BUTTON": 0,
"SWITCH": 0,
},
"airm2m_core_esp32c3": {
"LED1_BUILTIN": 12,
"LED2_BUILTIN": 13,
},
"alksesp32": {
"A0": 32,
"A1": 33,

View File

@@ -13,6 +13,8 @@ namespace esp32_rmt_led_strip {
static const char *const TAG = "esp32_rmt_led_strip";
static const uint32_t RMT_CLK_FREQ = 80000000;
static const uint8_t RMT_CLK_DIV = 2;
void ESP32RMTLEDStripLightOutput::setup() {
@@ -65,7 +67,7 @@ void ESP32RMTLEDStripLightOutput::setup() {
void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
uint32_t bit1_low) {
float ratio = (float) APB_CLK_FREQ / RMT_CLK_DIV / 1e09f;
float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f;
// 0-bit
this->bit0_.duration0 = (uint32_t) (ratio * bit0_high);

View File

@@ -39,45 +39,54 @@ void HTU21DComponent::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_);
}
void HTU21DComponent::update() {
uint16_t raw_temperature;
if (this->write(&HTU21D_REGISTER_TEMPERATURE, 1) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
delay(50); // NOLINT
if (this->read(reinterpret_cast<uint8_t *>(&raw_temperature), 2) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
raw_temperature = i2c::i2ctohs(raw_temperature);
float temperature = (float(raw_temperature & 0xFFFC)) * 175.72f / 65536.0f - 46.85f;
// According to the datasheet sht21 temperature readings can take up to 85ms
this->set_timeout(85, [this]() {
uint16_t raw_temperature;
if (this->read(reinterpret_cast<uint8_t *>(&raw_temperature), 2) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
raw_temperature = i2c::i2ctohs(raw_temperature);
uint16_t raw_humidity;
if (this->write(&HTU21D_REGISTER_HUMIDITY, 1) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
delay(50); // NOLINT
if (this->read(reinterpret_cast<uint8_t *>(&raw_humidity), 2) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
raw_humidity = i2c::i2ctohs(raw_humidity);
float temperature = (float(raw_temperature & 0xFFFC)) * 175.72f / 65536.0f - 46.85f;
float humidity = (float(raw_humidity & 0xFFFC)) * 125.0f / 65536.0f - 6.0f;
ESP_LOGD(TAG, "Got Temperature=%.1f°C", temperature);
int8_t heater_level = this->get_heater_level();
if (this->temperature_ != nullptr)
this->temperature_->publish_state(temperature);
this->status_clear_warning();
ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%% Heater Level=%d", temperature, humidity, heater_level);
if (this->write(&HTU21D_REGISTER_HUMIDITY, 1) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
if (this->temperature_ != nullptr)
this->temperature_->publish_state(temperature);
if (this->humidity_ != nullptr)
this->humidity_->publish_state(humidity);
if (this->heater_ != nullptr)
this->heater_->publish_state(heater_level);
this->status_clear_warning();
this->set_timeout(50, [this]() {
uint16_t raw_humidity;
if (this->read(reinterpret_cast<uint8_t *>(&raw_humidity), 2) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
raw_humidity = i2c::i2ctohs(raw_humidity);
float humidity = (float(raw_humidity & 0xFFFC)) * 125.0f / 65536.0f - 6.0f;
int8_t heater_level = this->get_heater_level();
ESP_LOGD(TAG, "Got Humidity=%.1f%% Heater Level=%d", humidity, heater_level);
if (this->humidity_ != nullptr)
this->humidity_->publish_state(humidity);
if (this->heater_ != nullptr)
this->heater_->publish_state(heater_level);
this->status_clear_warning();
});
});
}
bool HTU21DComponent::is_heater_enabled() {

View File

@@ -52,7 +52,7 @@ optional<uint8_t> ImprovSerialComponent::read_byte_() {
size_t available;
uart_get_buffered_data_len(this->uart_num_, &available);
if (available) {
uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_PERIOD_MS);
uart_read_bytes(this->uart_num_, &data, 1, 0);
byte = data;
}
}
@@ -71,7 +71,7 @@ optional<uint8_t> ImprovSerialComponent::read_byte_() {
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_USB_SERIAL_JTAG: {
if (usb_serial_jtag_read_bytes((char *) &data, 1, 20 / portTICK_PERIOD_MS)) {
if (usb_serial_jtag_read_bytes((char *) &data, 1, 0)) {
byte = data;
}
break;

View File

@@ -55,6 +55,9 @@ void Inkplate6::setup() {
this->wakeup_pin_->digital_write(false);
}
/**
* Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed.
*/
void Inkplate6::initialize_() {
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
ExternalRAMAllocator<uint32_t> allocator32(ExternalRAMAllocator<uint32_t>::ALLOW_FAILURE);

View File

@@ -68,8 +68,9 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
void set_greyscale(bool greyscale) {
this->greyscale_ = greyscale;
this->initialize_();
this->block_partial_ = true;
if (this->is_ready())
this->initialize_();
}
void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; }
void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; }

View File

@@ -309,7 +309,7 @@ async def component_to_code(config):
lt_options["LT_UART_SILENT_ENABLED"] = 0
lt_options["LT_UART_SILENT_ALL"] = 0
# set default UART port
if uart_port := framework.get(CONF_UART_PORT, None) is not None:
if (uart_port := framework.get(CONF_UART_PORT, None)) is not None:
lt_options["LT_UART_DEFAULT_PORT"] = uart_port
# add custom options
lt_options.update(framework[CONF_OPTIONS])

View File

@@ -84,7 +84,7 @@ UART_SELECTION_ESP32 = {
VARIANT_ESP32: [UART0, UART1, UART2],
VARIANT_ESP32S2: [UART0, UART1, USB_CDC],
VARIANT_ESP32S3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
VARIANT_ESP32C3: [UART0, UART1, USB_SERIAL_JTAG],
VARIANT_ESP32C3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
VARIANT_ESP32C2: [UART0, UART1],
VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
VARIANT_ESP32H2: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
@@ -172,7 +172,10 @@ CONFIG_SCHEMA = cv.All(
esp8266=UART0,
esp32=UART0,
esp32_s2=USB_CDC,
esp32_s3=USB_CDC,
esp32_s3_arduino=USB_CDC,
esp32_s3_idf=USB_SERIAL_JTAG,
esp32_c3_arduino=USB_CDC,
esp32_c3_idf=USB_SERIAL_JTAG,
rp2040=USB_CDC,
bk72xx=DEFAULT,
rtl87xx=DEFAULT,
@@ -263,6 +266,8 @@ async def to_code(config):
if CORE.using_arduino:
if config[CONF_HARDWARE_UART] == USB_CDC:
cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=1")
if CORE.is_esp32 and get_esp32_variant() == VARIANT_ESP32C3:
cg.add_build_flag("-DARDUINO_USB_MODE=1")
if CORE.using_esp_idf:
if config[CONF_HARDWARE_UART] == USB_CDC:

View File

@@ -272,25 +272,22 @@ void Logger::pre_setup() {
#endif
#if defined(USE_ESP32) && \
(defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3))
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3)
case UART_SELECTION_USB_CDC:
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
case UART_SELECTION_USB_SERIAL_JTAG:
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3
#ifdef USE_ESP32_VARIANT_ESP32C3
this->hw_serial_ = &Serial;
Serial.begin(this->baud_rate_);
#endif // USE_ESP32_VARIANT_ESP32C3
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3)
#if ARDUINO_USB_CDC_ON_BOOT
this->hw_serial_ = &Serial;
Serial.setTxTimeoutMs(0); // workaround for 2.0.9 crash when there's no data connection
Serial.begin(this->baud_rate_);
#else
this->hw_serial_ = &Serial;
Serial.begin(this->baud_rate_);
#endif // ARDUINO_USB_CDC_ON_BOOT
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3
break;
#endif // USE_ESP32 && (USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3)
#ifdef USE_RP2040

View File

@@ -45,9 +45,10 @@ enum UARTSelection {
UART_SELECTION_UART2,
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 &&
// !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \
(defined(USE_ESP32_VARIANT_ESP32C3) && defined(USE_ARDUINO))
UART_SELECTION_USB_CDC,
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
UART_SELECTION_USB_SERIAL_JTAG,

View File

@@ -19,7 +19,7 @@ PylontechComponent = pylontech_ns.class_(
)
PylontechBattery = pylontech_ns.class_("PylontechBattery")
CV_NUM_BATTERIES = cv.int_range(1, 6)
CV_NUM_BATTERIES = cv.int_range(1, 16)
PYLONTECH_COMPONENT_SCHEMA = cv.Schema(
{

View File

@@ -1,5 +1,6 @@
#include "pylontech.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace pylontech {
@@ -34,26 +35,30 @@ void PylontechComponent::setup() {
void PylontechComponent::update() { this->write_str("pwr\n"); }
void PylontechComponent::loop() {
uint8_t data;
// pylontech sends a lot of data very suddenly
// we need to quickly put it all into our own buffer, otherwise the uart's buffer will overflow
while (this->available() > 0) {
if (this->read_byte(&data)) {
buffer_[buffer_index_write_] += (char) data;
if (buffer_[buffer_index_write_].back() == static_cast<char>(ASCII_LF) ||
buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) {
// complete line received
buffer_index_write_ = (buffer_index_write_ + 1) % NUM_BUFFERS;
if (this->available() > 0) {
// pylontech sends a lot of data very suddenly
// we need to quickly put it all into our own buffer, otherwise the uart's buffer will overflow
uint8_t data;
int recv = 0;
while (this->available() > 0) {
if (this->read_byte(&data)) {
buffer_[buffer_index_write_] += (char) data;
recv++;
if (buffer_[buffer_index_write_].back() == static_cast<char>(ASCII_LF) ||
buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) {
// complete line received
buffer_index_write_ = (buffer_index_write_ + 1) % NUM_BUFFERS;
}
}
}
}
// only process one line per call of loop() to not block esphome for too long
if (buffer_index_read_ != buffer_index_write_) {
this->process_line_(buffer_[buffer_index_read_]);
buffer_[buffer_index_read_].clear();
buffer_index_read_ = (buffer_index_read_ + 1) % NUM_BUFFERS;
ESP_LOGV(TAG, "received %d bytes", recv);
} else {
// only process one line per call of loop() to not block esphome for too long
if (buffer_index_read_ != buffer_index_write_) {
this->process_line_(buffer_[buffer_index_read_]);
buffer_[buffer_index_read_].clear();
buffer_index_read_ = (buffer_index_read_ + 1) % NUM_BUFFERS;
}
}
}
@@ -66,10 +71,11 @@ void PylontechComponent::process_line_(std::string &buffer) {
// clang-format on
PylontechListener::LineContents l{};
const int parsed = sscanf( // NOLINT
buffer.c_str(), "%d %d %d %d %d %d %d %d %7s %7s %7s %7s %d%% %*d-%*d-%*d %*d:%*d:%*d %*s %*s %d %*s", // NOLINT
&l.bat_num, &l.volt, &l.curr, &l.tempr, &l.tlow, &l.thigh, &l.vlow, &l.vhigh, l.base_st, l.volt_st, // NOLINT
l.curr_st, l.temp_st, &l.coulomb, &l.mostempr); // NOLINT
char mostempr_s[6];
const int parsed = sscanf( // NOLINT
buffer.c_str(), "%d %d %d %d %d %d %d %d %7s %7s %7s %7s %d%% %*d-%*d-%*d %*d:%*d:%*d %*s %*s %5s %*s", // NOLINT
&l.bat_num, &l.volt, &l.curr, &l.tempr, &l.tlow, &l.thigh, &l.vlow, &l.vhigh, l.base_st, l.volt_st, // NOLINT
l.curr_st, l.temp_st, &l.coulomb, mostempr_s); // NOLINT
if (l.bat_num <= 0) {
ESP_LOGD(TAG, "invalid bat_num in line %s", buffer.substr(0, buffer.size() - 2).c_str());
@@ -79,6 +85,13 @@ void PylontechComponent::process_line_(std::string &buffer) {
ESP_LOGW(TAG, "invalid line: found only %d items in %s", parsed, buffer.substr(0, buffer.size() - 2).c_str());
return;
}
auto mostempr_parsed = parse_number<int>(mostempr_s);
if (mostempr_parsed.has_value()) {
l.mostempr = mostempr_parsed.value();
} else {
l.mostempr = -300;
ESP_LOGW(TAG, "bat_num %d: received no mostempr", l.bat_num);
}
for (PylontechListener *listener : this->listeners_) {
listener->on_line_read(&l);

View File

@@ -227,16 +227,17 @@ optional<ProntoData> ProntoProtocol::decode(RemoteReceiveData src) {
}
void ProntoProtocol::dump(const ProntoData &data) {
std::string first, rest;
if (data.data.size() < 230) {
first = data.data;
} else {
first = data.data.substr(0, 229);
rest = data.data.substr(230);
}
ESP_LOGI(TAG, "Received Pronto: data=%s", first.c_str());
if (!rest.empty()) {
ESP_LOGI(TAG, "%s", rest.c_str());
std::string rest;
rest = data.data;
ESP_LOGI(TAG, "Received Pronto: data=");
while (true) {
ESP_LOGI(TAG, "%s", rest.substr(0, 230).c_str());
if (rest.size() > 230) {
rest = rest.substr(230);
} else {
break;
}
}
}

View File

@@ -64,6 +64,9 @@ void TT21100Touchscreen::setup() {
// Update display dimensions if they were updated during display setup
this->x_raw_max_ = this->get_width_();
this->y_raw_max_ = this->get_height_();
// Trigger initial read to activate the interrupt
this->store_.touched = true;
}
void TT21100Touchscreen::update_touches() {

View File

@@ -17,7 +17,7 @@ static const char *const TAG = "voice_assistant";
static const size_t SAMPLE_RATE_HZ = 16000;
static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms
static const size_t BUFFER_SIZE = 1000 * SAMPLE_RATE_HZ / 1000; // 1s
static const size_t BUFFER_SIZE = 1024 * SAMPLE_RATE_HZ / 1000;
static const size_t SEND_BUFFER_SIZE = INPUT_BUFFER_SIZE * sizeof(int16_t);
static const size_t RECEIVE_SIZE = 1024;
static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE;
@@ -86,14 +86,14 @@ void VoiceAssistant::setup() {
#ifdef USE_ESP_ADF
this->vad_instance_ = vad_create(VAD_MODE_4);
#endif
this->ring_buffer_ = rb_create(BUFFER_SIZE, sizeof(int16_t));
this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t));
if (this->ring_buffer_ == nullptr) {
ESP_LOGW(TAG, "Could not allocate ring buffer");
this->mark_failed();
return;
}
#endif
ExternalRAMAllocator<uint8_t> send_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->send_buffer_ = send_allocator.allocate(SEND_BUFFER_SIZE);
@@ -112,14 +112,8 @@ int VoiceAssistant::read_microphone_() {
memset(this->input_buffer_, 0, INPUT_BUFFER_SIZE * sizeof(int16_t));
return 0;
}
#ifdef USE_ESP_ADF
// Write audio into ring buffer
int available = rb_bytes_available(this->ring_buffer_);
if (available < bytes_read) {
rb_read(this->ring_buffer_, nullptr, bytes_read - available, 0);
}
rb_write(this->ring_buffer_, (char *) this->input_buffer_, bytes_read, 0);
#endif
this->ring_buffer_->write((void *) this->input_buffer_, bytes_read);
} else {
ESP_LOGD(TAG, "microphone not running");
}
@@ -141,9 +135,9 @@ void VoiceAssistant::loop() {
switch (this->state_) {
case State::IDLE: {
if (this->continuous_ && this->desired_state_ == State::IDLE) {
this->ring_buffer_->reset();
#ifdef USE_ESP_ADF
if (this->use_wake_word_) {
rb_reset(this->ring_buffer_);
this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD);
} else
#endif
@@ -236,19 +230,15 @@ void VoiceAssistant::loop() {
break; // State changed when udp server port received
}
case State::STREAMING_MICROPHONE: {
size_t bytes_read = this->read_microphone_();
#ifdef USE_ESP_ADF
if (rb_bytes_filled(this->ring_buffer_) >= SEND_BUFFER_SIZE) {
rb_read(this->ring_buffer_, (char *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
this->socket_->sendto(this->send_buffer_, SEND_BUFFER_SIZE, 0, (struct sockaddr *) &this->dest_addr_,
this->read_microphone_();
size_t available = this->ring_buffer_->available();
while (available >= SEND_BUFFER_SIZE) {
size_t read_bytes = this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
this->socket_->sendto(this->send_buffer_, read_bytes, 0, (struct sockaddr *) &this->dest_addr_,
sizeof(this->dest_addr_));
available = this->ring_buffer_->available();
}
#else
if (bytes_read > 0) {
this->socket_->sendto(this->input_buffer_, bytes_read, 0, (struct sockaddr *) &this->dest_addr_,
sizeof(this->dest_addr_));
}
#endif
break;
}
case State::STOP_MICROPHONE: {
@@ -473,9 +463,9 @@ void VoiceAssistant::request_start(bool continuous, bool silence_detection) {
if (this->state_ == State::IDLE) {
this->continuous_ = continuous;
this->silence_detection_ = silence_detection;
this->ring_buffer_->reset();
#ifdef USE_ESP_ADF
if (this->use_wake_word_) {
rb_reset(this->ring_buffer_);
this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD);
} else
#endif
@@ -618,9 +608,9 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
case api::enums::VOICE_ASSISTANT_RUN_END: {
ESP_LOGD(TAG, "Assist Pipeline ended");
if (this->state_ == State::STREAMING_MICROPHONE) {
this->ring_buffer_->reset();
#ifdef USE_ESP_ADF
if (this->use_wake_word_) {
rb_reset(this->ring_buffer_);
// No need to stop the microphone since we didn't use the speaker
this->set_state_(State::WAIT_FOR_VAD, State::WAITING_FOR_VAD);
} else

View File

@@ -7,6 +7,7 @@
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/ring_buffer.h"
#include "esphome/components/api/api_connection.h"
#include "esphome/components/api/api_pb2.h"
@@ -21,7 +22,6 @@
#ifdef USE_ESP_ADF
#include <esp_vad.h>
#include <ringbuf.h>
#endif
namespace esphome {
@@ -177,10 +177,10 @@ class VoiceAssistant : public Component {
#ifdef USE_ESP_ADF
vad_handle_t vad_instance_;
ringbuf_handle_t ring_buffer_;
uint8_t vad_threshold_{5};
uint8_t vad_counter_{0};
#endif
std::unique_ptr<RingBuffer> ring_buffer_;
bool use_wake_word_;
uint8_t noise_suppression_level_;

View File

@@ -315,7 +315,11 @@ class LoadValidationStep(ConfigValidationStep):
return
result.add_output_path([self.domain], self.domain)
component = get_component(self.domain)
if component.multi_conf_no_default and isinstance(self.conf, core.AutoLoad):
if (
component is not None
and component.multi_conf_no_default
and isinstance(self.conf, core.AutoLoad)
):
self.conf = []
result[self.domain] = self.conf
path = [self.domain]

View File

@@ -1541,6 +1541,9 @@ class SplitDefault(Optional):
esp32_s3=vol.UNDEFINED,
esp32_s3_arduino=vol.UNDEFINED,
esp32_s3_idf=vol.UNDEFINED,
esp32_c3=vol.UNDEFINED,
esp32_c3_arduino=vol.UNDEFINED,
esp32_c3_idf=vol.UNDEFINED,
rp2040=vol.UNDEFINED,
bk72xx=vol.UNDEFINED,
rtl87xx=vol.UNDEFINED,
@@ -1549,22 +1552,28 @@ class SplitDefault(Optional):
super().__init__(key)
self._esp8266_default = vol.default_factory(esp8266)
self._esp32_arduino_default = vol.default_factory(
_get_priority_default(esp32, esp32_arduino)
_get_priority_default(esp32_arduino, esp32)
)
self._esp32_idf_default = vol.default_factory(
_get_priority_default(esp32, esp32_idf)
_get_priority_default(esp32_idf, esp32)
)
self._esp32_s2_arduino_default = vol.default_factory(
_get_priority_default(esp32_s2, esp32, esp32_s2_arduino, esp32_arduino)
_get_priority_default(esp32_s2_arduino, esp32_s2, esp32_arduino, esp32)
)
self._esp32_s2_idf_default = vol.default_factory(
_get_priority_default(esp32_s2, esp32, esp32_s2_idf, esp32_idf)
_get_priority_default(esp32_s2_idf, esp32_s2, esp32_idf, esp32)
)
self._esp32_s3_arduino_default = vol.default_factory(
_get_priority_default(esp32_s3, esp32, esp32_s3_arduino, esp32_arduino)
_get_priority_default(esp32_s3_arduino, esp32_s3, esp32_arduino, esp32)
)
self._esp32_s3_idf_default = vol.default_factory(
_get_priority_default(esp32_s3, esp32, esp32_s3_idf, esp32_idf)
_get_priority_default(esp32_s3_idf, esp32_s3, esp32_idf, esp32)
)
self._esp32_c3_arduino_default = vol.default_factory(
_get_priority_default(esp32_c3_arduino, esp32_c3, esp32_arduino, esp32)
)
self._esp32_c3_idf_default = vol.default_factory(
_get_priority_default(esp32_c3_idf, esp32_c3, esp32_idf, esp32)
)
self._rp2040_default = vol.default_factory(rp2040)
self._bk72xx_default = vol.default_factory(bk72xx)
@@ -1580,6 +1589,7 @@ class SplitDefault(Optional):
from esphome.components.esp32.const import (
VARIANT_ESP32S2,
VARIANT_ESP32S3,
VARIANT_ESP32C3,
)
variant = get_esp32_variant()
@@ -1593,6 +1603,11 @@ class SplitDefault(Optional):
return self._esp32_s3_arduino_default
if CORE.using_esp_idf:
return self._esp32_s3_idf_default
elif variant == VARIANT_ESP32C3:
if CORE.using_arduino:
return self._esp32_c3_arduino_default
if CORE.using_esp_idf:
return self._esp32_c3_idf_default
else:
if CORE.using_arduino:
return self._esp32_arduino_default

View File

@@ -1,6 +1,6 @@
"""Constants used by esphome."""
__version__ = "2023.12.0b6"
__version__ = "2023.12.8"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = (

View File

@@ -0,0 +1,50 @@
#include "ring_buffer.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
#include "helpers.h"
namespace esphome {
static const char *const TAG = "ring_buffer";
std::unique_ptr<RingBuffer> RingBuffer::create(size_t len) {
std::unique_ptr<RingBuffer> rb = make_unique<RingBuffer>();
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
rb->storage_ = allocator.allocate(len + 1);
if (rb->storage_ == nullptr) {
return nullptr;
}
rb->handle_ = xStreamBufferCreateStatic(len + 1, 0, rb->storage_, &rb->structure_);
ESP_LOGD(TAG, "Created ring buffer with size %u", len);
return rb;
}
size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) {
return xStreamBufferReceive(this->handle_, data, len, ticks_to_wait);
}
size_t RingBuffer::write(void *data, size_t len) {
size_t free = this->free();
if (free < len) {
size_t needed = len - free;
uint8_t discard[needed];
xStreamBufferReceive(this->handle_, discard, needed, 0);
}
return xStreamBufferSend(this->handle_, data, len, 0);
}
size_t RingBuffer::available() const { return xStreamBufferBytesAvailable(this->handle_); }
size_t RingBuffer::free() const { return xStreamBufferSpacesAvailable(this->handle_); }
BaseType_t RingBuffer::reset() { return xStreamBufferReset(this->handle_); }
} // namespace esphome
#endif

View File

@@ -0,0 +1,34 @@
#pragma once
#ifdef USE_ESP32
#include <freertos/FreeRTOS.h>
#include <freertos/stream_buffer.h>
#include <cinttypes>
#include <memory>
namespace esphome {
class RingBuffer {
public:
size_t read(void *data, size_t len, TickType_t ticks_to_wait = 0);
size_t write(void *data, size_t len);
size_t available() const;
size_t free() const;
BaseType_t reset();
static std::unique_ptr<RingBuffer> create(size_t len);
protected:
StreamBufferHandle_t handle_;
StaticStreamBuffer_t structure_;
uint8_t *storage_;
};
} // namespace esphome
#endif

View File

@@ -31,6 +31,7 @@ class PingStatus:
while not dashboard.stop_event.is_set():
# Only ping if the dashboard is open
await dashboard.ping_request.wait()
dashboard.ping_request.clear()
current_entries = dashboard.entries.async_all()
to_ping: list[DashboardEntry] = [
entry for entry in current_entries if entry.address is not None

View File

@@ -30,6 +30,7 @@ def write_file(
"""
tmp_filename = ""
missing_fchmod = False
try:
# Modern versions of Python tempfile create this file with mode 0o600
with tempfile.NamedTemporaryFile(
@@ -38,8 +39,15 @@ def write_file(
fdesc.write(utf8_data)
tmp_filename = fdesc.name
if not private:
os.fchmod(fdesc.fileno(), 0o644)
try:
os.fchmod(fdesc.fileno(), 0o644)
except AttributeError:
# os.fchmod is not available on Windows
missing_fchmod = True
os.replace(tmp_filename, filename)
if missing_fchmod:
os.chmod(filename, 0o644)
finally:
if os.path.exists(tmp_filename):
try:

View File

@@ -301,11 +301,16 @@ class EsphomePortCommandWebSocket(EsphomeCommandWebSocket):
config_file = settings.rel_path(configuration)
port = json_message["port"]
if (
port == "OTA"
port == "OTA" # pylint: disable=too-many-boolean-expressions
and (mdns := dashboard.mdns_status)
and (entry := entries.get(config_file))
and entry.loaded_integrations
and "api" in entry.loaded_integrations
and (address := await mdns.async_resolve_host(entry.name))
):
# Use the IP address if available but only
# if the API is loaded and the device is online
# since MQTT logging will not work otherwise
port = address
return [
@@ -792,13 +797,22 @@ class EditRequestHandler(BaseHandler):
"""Get the content of a file."""
loop = asyncio.get_running_loop()
filename = settings.rel_path(configuration)
content = await loop.run_in_executor(None, self._read_file, filename)
self.write(content)
content = await loop.run_in_executor(
None, self._read_file, filename, configuration
)
if content is not None:
self.write(content)
def _read_file(self, filename: str) -> bytes:
def _read_file(self, filename: str, configuration: str) -> bytes | None:
"""Read a file and return the content as bytes."""
with open(file=filename, encoding="utf-8") as f:
return f.read()
try:
with open(file=filename, encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
if configuration in const.SECRETS_FILES:
return ""
self.set_status(404)
return None
def _write_file(self, filename: str, content: bytes) -> None:
"""Write a file with the given content."""

View File

@@ -357,7 +357,7 @@ def snake_case(value):
return value.replace(" ", "_").lower()
_DISALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9_]")
_DISALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9-_]")
def sanitize(value):

View File

@@ -1,36 +1,37 @@
from __future__ import annotations
import fnmatch
import functools
import inspect
import logging
import math
import os
import uuid
from typing import Any
import yaml
import yaml.constructor
from yaml import SafeLoader as PurePythonLoader
try:
from yaml import CSafeLoader as FastestAvailableSafeLoader
except ImportError:
FastestAvailableSafeLoader = PurePythonLoader
from esphome import core
from esphome.config_helpers import read_config_file, Extend, Remove
from esphome.config_helpers import Extend, Remove, read_config_file
from esphome.core import (
CORE,
DocumentRange,
EsphomeError,
IPAddress,
Lambda,
MACAddress,
TimePeriod,
DocumentRange,
CORE,
)
from esphome.helpers import add_class_to_obj
from esphome.util import OrderedDict, filter_yaml_files
try:
from yaml import CSafeLoader as FastestAvailableSafeLoader
except ImportError:
from yaml import ( # type: ignore[assignment]
SafeLoader as FastestAvailableSafeLoader,
)
_LOGGER = logging.getLogger(__name__)
# Mostly copied from Home Assistant because that code works fine and
@@ -97,7 +98,7 @@ def _add_data_ref(fn):
return wrapped
class ESPHomeLoader(FastestAvailableSafeLoader):
class ESPHomeLoaderMixin:
"""Loader class that keeps track of line numbers."""
@_add_data_ref
@@ -282,8 +283,8 @@ class ESPHomeLoader(FastestAvailableSafeLoader):
return file, vars
def substitute_vars(config, vars):
from esphome.const import CONF_SUBSTITUTIONS
from esphome.components import substitutions
from esphome.const import CONF_SUBSTITUTIONS
org_subs = None
result = config
@@ -367,50 +368,64 @@ class ESPHomeLoader(FastestAvailableSafeLoader):
return Remove(str(node.value))
ESPHomeLoader.add_constructor("tag:yaml.org,2002:int", ESPHomeLoader.construct_yaml_int)
ESPHomeLoader.add_constructor(
"tag:yaml.org,2002:float", ESPHomeLoader.construct_yaml_float
)
ESPHomeLoader.add_constructor(
"tag:yaml.org,2002:binary", ESPHomeLoader.construct_yaml_binary
)
ESPHomeLoader.add_constructor(
"tag:yaml.org,2002:omap", ESPHomeLoader.construct_yaml_omap
)
ESPHomeLoader.add_constructor("tag:yaml.org,2002:str", ESPHomeLoader.construct_yaml_str)
ESPHomeLoader.add_constructor("tag:yaml.org,2002:seq", ESPHomeLoader.construct_yaml_seq)
ESPHomeLoader.add_constructor("tag:yaml.org,2002:map", ESPHomeLoader.construct_yaml_map)
ESPHomeLoader.add_constructor("!env_var", ESPHomeLoader.construct_env_var)
ESPHomeLoader.add_constructor("!secret", ESPHomeLoader.construct_secret)
ESPHomeLoader.add_constructor("!include", ESPHomeLoader.construct_include)
ESPHomeLoader.add_constructor(
"!include_dir_list", ESPHomeLoader.construct_include_dir_list
)
ESPHomeLoader.add_constructor(
"!include_dir_merge_list", ESPHomeLoader.construct_include_dir_merge_list
)
ESPHomeLoader.add_constructor(
"!include_dir_named", ESPHomeLoader.construct_include_dir_named
)
ESPHomeLoader.add_constructor(
"!include_dir_merge_named", ESPHomeLoader.construct_include_dir_merge_named
)
ESPHomeLoader.add_constructor("!lambda", ESPHomeLoader.construct_lambda)
ESPHomeLoader.add_constructor("!force", ESPHomeLoader.construct_force)
ESPHomeLoader.add_constructor("!extend", ESPHomeLoader.construct_extend)
ESPHomeLoader.add_constructor("!remove", ESPHomeLoader.construct_remove)
class ESPHomeLoader(ESPHomeLoaderMixin, FastestAvailableSafeLoader):
"""Loader class that keeps track of line numbers."""
def load_yaml(fname, clear_secrets=True):
class ESPHomePurePythonLoader(ESPHomeLoaderMixin, PurePythonLoader):
"""Loader class that keeps track of line numbers."""
for _loader in (ESPHomeLoader, ESPHomePurePythonLoader):
_loader.add_constructor("tag:yaml.org,2002:int", _loader.construct_yaml_int)
_loader.add_constructor("tag:yaml.org,2002:float", _loader.construct_yaml_float)
_loader.add_constructor("tag:yaml.org,2002:binary", _loader.construct_yaml_binary)
_loader.add_constructor("tag:yaml.org,2002:omap", _loader.construct_yaml_omap)
_loader.add_constructor("tag:yaml.org,2002:str", _loader.construct_yaml_str)
_loader.add_constructor("tag:yaml.org,2002:seq", _loader.construct_yaml_seq)
_loader.add_constructor("tag:yaml.org,2002:map", _loader.construct_yaml_map)
_loader.add_constructor("!env_var", _loader.construct_env_var)
_loader.add_constructor("!secret", _loader.construct_secret)
_loader.add_constructor("!include", _loader.construct_include)
_loader.add_constructor("!include_dir_list", _loader.construct_include_dir_list)
_loader.add_constructor(
"!include_dir_merge_list", _loader.construct_include_dir_merge_list
)
_loader.add_constructor("!include_dir_named", _loader.construct_include_dir_named)
_loader.add_constructor(
"!include_dir_merge_named", _loader.construct_include_dir_merge_named
)
_loader.add_constructor("!lambda", _loader.construct_lambda)
_loader.add_constructor("!force", _loader.construct_force)
_loader.add_constructor("!extend", _loader.construct_extend)
_loader.add_constructor("!remove", _loader.construct_remove)
def load_yaml(fname: str, clear_secrets: bool = True) -> Any:
if clear_secrets:
_SECRET_VALUES.clear()
_SECRET_CACHE.clear()
return _load_yaml_internal(fname)
def _load_yaml_internal(fname):
def _load_yaml_internal(fname: str) -> Any:
"""Load a YAML file."""
content = read_config_file(fname)
loader = ESPHomeLoader(content)
try:
return _load_yaml_internal_with_type(ESPHomeLoader, fname, content)
except EsphomeError:
# Loading failed, so we now load with the Python loader which has more
# readable exceptions
return _load_yaml_internal_with_type(ESPHomePurePythonLoader, fname, content)
def _load_yaml_internal_with_type(
loader_type: type[ESPHomeLoader] | type[ESPHomePurePythonLoader],
fname: str,
content: str,
) -> Any:
"""Load a YAML file."""
loader = loader_type(content)
loader.name = fname
try:
return loader.get_single_data() or OrderedDict()

View File

@@ -13,7 +13,7 @@ def test_write_utf8_file(tmp_path: Path) -> None:
assert tmp_path.joinpath("foo.txt").read_text() == "foo"
with pytest.raises(OSError):
write_utf8_file(Path("/not-writable"), "bar")
write_utf8_file(Path("/dev/not-writable"), "bar")
def test_write_file(tmp_path: Path) -> None:

78
tests/test8.1.yaml Normal file
View File

@@ -0,0 +1,78 @@
# Tests for ESP32-S3 boards - IDf
---
wifi:
ssid: "ssid"
network:
enable_ipv6: true
esp32:
board: esp32s3box
variant: ESP32S3
framework:
type: esp-idf
esphome:
name: esp32-s3-test
logger:
debug:
psram:
spi:
- id: spi_id_1
clk_pin:
number: GPIO7
allow_other_uses: false
mosi_pin: GPIO6
interface: any
spi_device:
id: spidev
data_rate: 2MHz
spi_id: spi_id_1
mode: 3
bit_order: lsb_first
display:
- platform: ili9xxx
id: displ8
model: ili9342
cs_pin: GPIO5
dc_pin: GPIO4
reset_pin:
number: GPIO48
allow_other_uses: true
i2c:
scl: GPIO18
sda: GPIO8
touchscreen:
- platform: tt21100
display: displ8
interrupt_pin:
number: GPIO3
ignore_strapping_warning: true
allow_other_uses: false
reset_pin:
number: GPIO48
allow_other_uses: true
binary_sensor:
- platform: tt21100
name: Home Button
index: 1
sensor:
- platform: debug
free:
name: "Heap Free"
block:
name: "Max Block Free"
loop_time:
name: "Loop Time"
psram:
name: "PSRAM Free"

75
tests/test8.2.yaml Normal file
View File

@@ -0,0 +1,75 @@
# Tests for ESP32-C3 boards - IDf
---
wifi:
ssid: "ssid"
network:
enable_ipv6: true
esp32:
board: lolin_c3_mini
variant: ESP32C3
framework:
type: esp-idf
esphome:
name: esp32-c3-test
logger:
debug:
psram:
spi:
- id: spi_id_1
clk_pin:
number: GPIO7
allow_other_uses: false
mosi_pin: GPIO6
interface: any
spi_device:
id: spidev
data_rate: 2MHz
spi_id: spi_id_1
mode: 3
bit_order: lsb_first
display:
- platform: ili9xxx
id: displ8
model: ili9342
cs_pin: GPIO5
dc_pin: GPIO4
reset_pin:
number: GPIO21
i2c:
scl: GPIO18
sda: GPIO8
touchscreen:
- platform: tt21100
display: displ8
interrupt_pin:
number: GPIO3
allow_other_uses: false
reset_pin:
number: GPIO20
binary_sensor:
- platform: tt21100
name: Home Button
index: 1
sensor:
- platform: debug
free:
name: "Heap Free"
block:
name: "Max Block Free"
loop_time:
name: "Loop Time"
psram:
name: "PSRAM Free"

View File

@@ -0,0 +1,18 @@
---
substitutions:
name: original
wifi: !include
file: includes/broken_included.yaml.txt
vars:
name: my_custom_ssid
esphome:
# should be substituted as 'original',
# not overwritten by vars in the !include above
name: ${name}
name_add_mac_suffix: true
platform: esp8266
board: !include {file: includes/scalar.yaml, vars: {var1: nodemcu}}
libraries: !include {file: includes/list.yaml, vars: {var1: Wire}}

View File

@@ -0,0 +1,5 @@
---
# yamllint disable-line
ssid: ${name}
# yamllint disable-line
fdf: error

View File

@@ -261,6 +261,7 @@ def test_snake_case(text, expected):
('!"§$%&/()=?foo_bar', "___________foo_bar"),
('foo_!"§$%&/()=?bar', "foo____________bar"),
('foo_bar!"§$%&/()=?', "foo_bar___________"),
('foo-bar!"§$%&/()=?', "foo-bar___________"),
),
)
def test_sanitize(text, expected):

View File

@@ -1,5 +1,6 @@
from esphome import yaml_util
from esphome.components import substitutions
from esphome.core import EsphomeError
def test_include_with_vars(fixture_path):
@@ -11,3 +12,13 @@ def test_include_with_vars(fixture_path):
assert actual["esphome"]["libraries"][0] == "Wire"
assert actual["esphome"]["board"] == "nodemcu"
assert actual["wifi"]["ssid"] == "my_custom_ssid"
def test_loading_a_broken_yaml_file(fixture_path):
"""Ensure we fallback to pure python to give good errors."""
yaml_file = fixture_path / "yaml_util" / "broken_includetest.yaml"
try:
yaml_util.load_yaml(yaml_file)
except EsphomeError as err:
assert "broken_included.yaml" in str(err)