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

Compare commits

...

77 Commits

Author SHA1 Message Date
Keith Burzinski
ec3162282c Merge pull request #6132 from esphome/bump-2023.12.9
2023.12.9
2024-01-22 19:54:22 -06:00
Keith Burzinski
f3997d0f77 Bump version to 2023.12.9 2024-01-22 19:28:12 -06:00
aschmitz
4e5534850c fix: negative temperatures on PMS5003T sensors (#6100) 2024-01-22 19:28:12 -06:00
Samuel Sieb
354314dbf3 fix negative temperature for pmsx003 (#6083)
* fix negative temperature for pmsx003

* Update esphome/components/pmsx003/pmsx003.cpp
2024-01-22 19:28:11 -06:00
Samuel Sieb
2cda6462f3 negative values for all DHT22 variants (#6074)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2024-01-22 19:28:11 -06:00
Samuel Sieb
a6f864a4a3 fix sen5x negative temperature (#6082) 2024-01-22 19:28:11 -06:00
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
Jesse Hills
816dcdf24f Merge pull request #5975 from esphome/bump-2023.12.0b6
2023.12.0b6
2023-12-21 07:56:11 +09:00
Jesse Hills
bec1ad9396 Bump version to 2023.12.0b6 2023-12-20 20:59:46 +09:00
Jesse Hills
6a9e85438f Fix pin reuse error with pin expanders (#5973)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2023-12-20 20:59:46 +09:00
Kamil Trzciński
ab25e32509 image: allow the image to by auto-loaded by animation (#5139) 2023-12-20 20:59:46 +09:00
Jesse Hills
2ccf985de5 Merge pull request #5972 from esphome/bump-2023.12.0b5
2023.12.0b5
2023-12-20 14:17:31 +09:00
Jesse Hills
a64e96e7ad Bump version to 2023.12.0b5 2023-12-20 13:43:43 +09:00
NP v/d Spek
fbf3d03a33 rename set_raw_touch_position_ to add_raw_touch_position_ (#5962) 2023-12-20 13:43:43 +09:00
Jesse Hills
7b2b6aaeb1 Merge pull request #5971 from esphome/bump-2023.12.0b4
2023.12.0b4
2023-12-20 10:49:42 +09:00
Jesse Hills
e2a00f66b8 Bump version to 2023.12.0b4 2023-12-20 09:59:58 +09:00
J. Nick Koston
1b3068a409 Bump aioesphomeapi to 21.0.1 (#5969) 2023-12-20 09:59:52 +09:00
J. Nick Koston
e5414d70f5 Speed up writing protobuf strings/bytes (#5828) 2023-12-20 09:57:37 +09:00
mathieu-mp
977e0184a7 Add deep sleep between updates for waveshare epaper 1.54in and 1.54inv2 (#5961) 2023-12-20 09:57:37 +09:00
Jean Louis-Guerin
820f328248 Fix I2CBus::write() bug and add i2c documentation (#5947) 2023-12-20 09:57:37 +09:00
Keith Burzinski
7807f0d892 Fix build issue with UART component when building with Arduino and CDC (#5964) 2023-12-20 09:57:37 +09:00
Jesse Hills
b59666c512 Merge pull request #5958 from esphome/bump-2023.12.0b3
2023.12.0b3
2023-12-18 17:32:02 +09:00
Jesse Hills
dbfa77cb4b Bump version to 2023.12.0b3 2023-12-18 16:51:11 +09:00
Keith Burzinski
ab22a3da34 Use the correct UART/Serial when CDC is enabled (#5957) 2023-12-18 16:51:11 +09:00
Jean Louis-Guerin
eefa1cd3ab Revert pure virtual functions in UART component from #5920 (#5932)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-12-18 16:51:10 +09:00
dentra
d0df73769d web_server_idf: fix call with hardcoded http code (#5942) 2023-12-18 16:51:10 +09:00
Alex Hermann
e8ce780482 esp32_camera: Set framebuffer task prio to 1 (#5943) 2023-12-18 16:51:10 +09:00
Alex Hermann
168e704130 i2s_audio: Set player_task's prio to 1 (#5945) 2023-12-18 16:51:10 +09:00
Grant Le Roux
2309f15ce0 Fix - Tuya Fan - Allow integer speed datapoint (#5948)
Co-authored-by: Cram42 <5396871+cram42@users.noreply.github.com>
2023-12-18 16:51:10 +09:00
Edward Firmo
917e0f93ed UARTComponent inline doc (#5930) 2023-12-18 16:51:10 +09:00
dependabot[bot]
70dac54113 Bump zeroconf from 0.128.4 to 0.130.0 (#5950)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 16:51:10 +09:00
73 changed files with 1006 additions and 273 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

@@ -12,7 +12,7 @@ import argcomplete
from esphome import const, writer, yaml_util
import esphome.codegen as cg
from esphome.config import iter_components, read_config, strip_default_ids
from esphome.config import iter_component_configs, read_config, strip_default_ids
from esphome.const import (
ALLOWED_NAME_CHARS,
CONF_BAUD_RATE,
@@ -196,7 +196,7 @@ def write_cpp(config):
def generate_cpp_contents(config):
_LOGGER.info("Generating C++ source...")
for name, component, conf in iter_components(CORE.config):
for name, component, conf in iter_component_configs(CORE.config):
if component.to_code is not None:
coro = wrap_to_code(name, component)
CORE.add_job(coro, conf)

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

@@ -160,8 +160,7 @@ class ProtoWriteBuffer {
this->encode_field_raw(field_id, 2);
this->encode_varint_raw(len);
auto *data = reinterpret_cast<const uint8_t *>(string);
for (size_t i = 0; i < len; i++)
this->write(data[i]);
this->buffer_->insert(this->buffer_->end(), data, data + len);
}
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
this->encode_string(field_id, value.data(), value.size());

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

@@ -217,8 +217,12 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
uint16_t raw_humidity = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
uint16_t raw_temperature = (uint16_t(data[2] & 0xFF) << 8) | (data[3] & 0xFF);
if (this->model_ != DHT_MODEL_DHT22_TYPE2 && (raw_temperature & 0x8000) != 0)
raw_temperature = ~(raw_temperature & 0x7FFF);
if (raw_temperature & 0x8000) {
if (!(raw_temperature & 0x4000))
raw_temperature = ~(raw_temperature & 0x7FFF);
} else if (raw_temperature & 0x800) {
raw_temperature |= 0xf000;
}
if (raw_temperature == 1 && raw_humidity == 10) {
if (report_errors) {

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

@@ -74,7 +74,7 @@ void EKTF2232Touchscreen::update_touches() {
uint8_t *d = raw + 1 + (i * 3);
x_raw = (d[0] & 0xF0) << 4 | d[1];
y_raw = (d[0] & 0x0F) << 8 | d[2];
this->set_raw_touch_position_(i, x_raw, y_raw);
this->add_raw_touch_position_(i, x_raw, y_raw);
}
}

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

@@ -37,7 +37,7 @@ void ESP32Camera::setup() {
"framebuffer_task", // name
1024, // stack size
nullptr, // task pv params
0, // priority
1, // priority
nullptr, // handle
1 // core
);

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

@@ -94,7 +94,7 @@ class FT5x06Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
esph_log_d(TAG, "Read %X status, id: %d, pos %d/%d", status, id, x, y);
if (status == 0 || status == 2) {
this->set_raw_touch_position_(id, x, y);
this->add_raw_touch_position_(id, x, y);
}
}
}

View File

@@ -53,13 +53,13 @@ void FT63X6Touchscreen::update_touches() {
uint8_t touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH1_ID); // id1 = 0 or 1
int16_t x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_X);
int16_t y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_Y);
this->set_raw_touch_position_(touch_id, x, y);
this->add_raw_touch_position_(touch_id, x, y);
if (touch_count >= 2) {
touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH2_ID); // id2 = 0 or 1(~id1 & 0x01)
x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_X);
y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_Y);
this->set_raw_touch_position_(touch_id, x, y);
this->add_raw_touch_position_(touch_id, x, y);
}
}

View File

@@ -92,7 +92,7 @@ void GT911Touchscreen::update_touches() {
uint16_t id = data[i][0];
uint16_t x = encode_uint16(data[i][2], data[i][1]);
uint16_t y = encode_uint16(data[i][4], data[i][3]);
this->set_raw_touch_position_(id, x, y);
this->add_raw_touch_position_(id, x, y);
}
auto keys = data[num_of_touches][0];
for (size_t i = 0; i != 4; i++) {

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

@@ -11,43 +11,116 @@ namespace i2c {
#define LOG_I2C_DEVICE(this) ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
class I2CDevice;
class I2CDevice; // forward declaration
/// @brief This class is used to create I2CRegister objects that act as proxies to read/write internal registers on an
/// I2C device.
/// @details
/// @n typical usage:
/// @code
/// constexpr uint8_t ADDR_REGISTER_1 = 0x12;
/// i2c::I2CRegister reg_1 = this->reg(ADDR_REGISTER_1); // declare
/// reg_1 |= 0x01; // set bit
/// reg_1 &= ~0x01; // reset bit
/// reg_1 = 10; // Set value
/// uint val = reg_1.get(); // get value
/// @endcode
/// @details The I²C protocol specifies how to read/write in sets of 8-bits followed by an Acknowledgement (ACK/NACK)
/// from the device receiving the data. How the device interprets the bits read/written can vary greatly from
/// device to device. However most of the devices follow the same protocol for reading/writing 8 bit registers using as
/// implemented in the I2CRegister: after sending the device address, the controller sends one byte with the internal
/// register address and then read or write the specified register content.
class I2CRegister {
public:
/// @brief overloads the = operator. This allows to set the value of an i2c register
/// @param value value to be set in the register
/// @return pointer to current object
I2CRegister &operator=(uint8_t value);
/// @brief overloads the compound &= operator. This allows to reset specific bits of an I²C register
/// @param value used for the & operation
/// @return pointer to current object
I2CRegister &operator&=(uint8_t value);
/// @brief overloads the compound |= operator. This allows to set specific bits of an I²C register
/// @param value used for the & operation
/// @return pointer to current object
I2CRegister &operator|=(uint8_t value);
/// @brief overloads the uint8_t() cast operator to return the I²C register value
/// @return pointer to current object
explicit operator uint8_t() const { return get(); }
/// @brief returns the register value
/// @return the register value
uint8_t get() const;
protected:
friend class I2CDevice;
/// @brief protected constructor that stores the owning object and the register address. Note as only friends can
/// create an I2CRegister @see I2CDevice::reg()
/// @param parent our parent
/// @param a_register address of the i2c register
I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {}
I2CDevice *parent_;
uint8_t register_;
I2CDevice *parent_; ///< I2CDevice object pointer
uint8_t register_; ///< the internal address of the register
};
/// @brief This class is used to create I2CRegister16 objects that act as proxies to read/write internal registers
/// (specified with a 16 bit address) on an I2C device.
/// @details
/// @n typical usage:
/// @code
/// constexpr uint16_t X16_BIT_ADDR_REGISTER_1 = 0x1234;
/// i2c::I2CRegister16 reg_1 = this->reg16(X16_BIT_ADDR_REGISTER_1); // declare
/// reg_1 |= 0x01; // set bit
/// reg_1 &= ~0x01; // reset bit
/// reg_1 = 10; // Set value
/// uint val = reg_1.get(); // get value
/// @endcode
/// @details The I²C protocol specification, reads/writes in sets of 8-bits followed by an Acknowledgement (ACK/NACK)
/// from the device receiving the data. How the device interprets the bits read/written to it can vary greatly from
/// device to device. This class can be used to access in the device 8 bits registers that uses a 16 bits internal
/// address. After sending the device address, the controller sends the internal register address (using two consecutive
/// bytes following the big indian convention) and then read or write the register content.
class I2CRegister16 {
public:
/// @brief overloads the = operator. This allows to set the value of an I²C register
/// @param value value to be set in the register
/// @return pointer to current object
I2CRegister16 &operator=(uint8_t value);
/// @brief overloads the compound &= operator. This allows to reset specific bits of an I²C register
/// @param value used for the & operation
/// @return pointer to current object
I2CRegister16 &operator&=(uint8_t value);
/// @brief overloads the compound |= operator. This allows to set bits of an I²C register
/// @param value used for the & operation
/// @return pointer to current object
I2CRegister16 &operator|=(uint8_t value);
/// @brief overloads the uint8_t() cast operator to return the I²C register value
/// @return the register value
explicit operator uint8_t() const { return get(); }
/// @brief returns the register value
/// @return the register value
uint8_t get() const;
protected:
friend class I2CDevice;
/// @brief protected constructor that store the owning object and the register address. Only friends can create an
/// I2CRegister16 @see I2CDevice::reg16()
/// @param parent our parent
/// @param a_register 16 bits address of the i2c register
I2CRegister16(I2CDevice *parent, uint16_t a_register) : parent_(parent), register_(a_register) {}
I2CDevice *parent_;
uint16_t register_;
I2CDevice *parent_; ///< I2CDevice object pointer
uint16_t register_; ///< the internal 16 bits address of the register
};
// like ntohs/htons but without including networking headers.
@@ -55,29 +128,91 @@ class I2CRegister16 {
inline uint16_t i2ctohs(uint16_t i2cshort) { return convert_big_endian(i2cshort); }
inline uint16_t htoi2cs(uint16_t hostshort) { return convert_big_endian(hostshort); }
/// @brief This Class provides the methods to read/write bytes from/to an i2c device.
/// Objects keep a list of devices found on bus as well as a pointer to the I2CBus in use.
class I2CDevice {
public:
/// @brief we use the C++ default constructor
I2CDevice() = default;
/// @brief We store the address of the device on the bus
/// @param address of the device
void set_i2c_address(uint8_t address) { address_ = address; }
/// @brief we store the pointer to the I2CBus to use
/// @param bus pointer to the I2CBus object
void set_i2c_bus(I2CBus *bus) { bus_ = bus; }
/// @brief calls the I2CRegister constructor
/// @param a_register address of the I²C register
/// @return an I2CRegister proxy object
I2CRegister reg(uint8_t a_register) { return {this, a_register}; }
/// @brief calls the I2CRegister16 constructor
/// @param a_register 16 bits address of the I²C register
/// @return an I2CRegister16 proxy object
I2CRegister16 reg16(uint16_t a_register) { return {this, a_register}; }
/// @brief reads an array of bytes from the device using an I2CBus
/// @param data pointer to an array to store the bytes
/// @param len length of the buffer = number of bytes to read
/// @return an i2c::ErrorCode
ErrorCode read(uint8_t *data, size_t len) { return bus_->read(address_, data, len); }
/// @brief reads an array of bytes from a specific register in the I²C device
/// @param a_register an 8 bits internal address of the I²C register to read from
/// @param data pointer to an array to store the bytes
/// @param len length of the buffer = number of bytes to read
/// @param stop (true/false): True will send a stop message, releasing the bus after
/// transmission. False will send a restart, keeping the connection active.
/// @return an i2c::ErrorCode
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop = true);
/// @brief reads an array of bytes from a specific register in the I²C device
/// @param a_register the 16 bits internal address of the I²C register to read from
/// @param data pointer to an array of bytes to store the information
/// @param len length of the buffer = number of bytes to read
/// @param stop (true/false): True will send a stop message, releasing the bus after
/// transmission. False will send a restart, keeping the connection active.
/// @return an i2c::ErrorCode
ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop = true);
ErrorCode write(const uint8_t *data, uint8_t len, bool stop = true) { return bus_->write(address_, data, len, stop); }
/// @brief writes an array of bytes to a device using an I2CBus
/// @param data pointer to an array that contains the bytes to send
/// @param len length of the buffer = number of bytes to write
/// @param stop (true/false): True will send a stop message, releasing the bus after
/// transmission. False will send a restart, keeping the connection active.
/// @return an i2c::ErrorCode
ErrorCode write(const uint8_t *data, size_t len, bool stop = true) { return bus_->write(address_, data, len, stop); }
/// @brief writes an array of bytes to a specific register in the I²C device
/// @param a_register the internal address of the register to read from
/// @param data pointer to an array to store the bytes
/// @param len length of the buffer = number of bytes to read
/// @param stop (true/false): True will send a stop message, releasing the bus after
/// transmission. False will send a restart, keeping the connection active.
/// @return an i2c::ErrorCode
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop = true);
/// @brief write an array of bytes to a specific register in the I²C device
/// @param a_register the 16 bits internal address of the register to read from
/// @param data pointer to an array to store the bytes
/// @param len length of the buffer = number of bytes to read
/// @param stop (true/false): True will send a stop message, releasing the bus after
/// transmission. False will send a restart, keeping the connection active.
/// @return an i2c::ErrorCode
ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop = true);
// Compat APIs
///
/// Compat APIs
/// All methods below have been added for compatibility reasons. They do not bring any functionality and therefore on
/// new code it is not recommend to use them.
///
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len) {
return read_register(a_register, data, len) == ERROR_OK;
}
bool read_bytes_raw(uint8_t *data, uint8_t len) { return read(data, len) == ERROR_OK; }
template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) {
@@ -131,8 +266,8 @@ class I2CDevice {
bool write_byte_16(uint8_t a_register, uint16_t data) { return write_bytes_16(a_register, &data, 1); }
protected:
uint8_t address_{0x00};
I2CBus *bus_{nullptr};
uint8_t address_{0x00}; ///< store the address of the device on the bus
I2CBus *bus_{nullptr}; ///< pointer to I2CBus instance
};
} // namespace i2c

View File

@@ -7,50 +7,93 @@
namespace esphome {
namespace i2c {
/// @brief Error codes returned by I2CBus and I2CDevice methods
enum ErrorCode {
ERROR_OK = 0,
ERROR_INVALID_ARGUMENT = 1,
ERROR_NOT_ACKNOWLEDGED = 2,
ERROR_TIMEOUT = 3,
ERROR_NOT_INITIALIZED = 4,
ERROR_TOO_LARGE = 5,
ERROR_UNKNOWN = 6,
ERROR_CRC = 7,
NO_ERROR = 0, ///< No error found during execution of method
ERROR_OK = 0, ///< No error found during execution of method
ERROR_INVALID_ARGUMENT = 1, ///< method called invalid argument(s)
ERROR_NOT_ACKNOWLEDGED = 2, ///< I2C bus acknowledgment not received
ERROR_TIMEOUT = 3, ///< timeout while waiting to receive bytes
ERROR_NOT_INITIALIZED = 4, ///< call method to a not initialized bus
ERROR_TOO_LARGE = 5, ///< requested a transfer larger than buffers can hold
ERROR_UNKNOWN = 6, ///< miscellaneous I2C error during execution
ERROR_CRC = 7, ///< bytes received with a CRC error
};
/// @brief the ReadBuffer structure stores a pointer to a read buffer and its length
struct ReadBuffer {
uint8_t *data;
size_t len;
};
struct WriteBuffer {
const uint8_t *data;
size_t len;
uint8_t *data; ///< pointer to the read buffer
size_t len; ///< length of the buffer
};
/// @brief the WriteBuffer structure stores a pointer to a write buffer and its length
struct WriteBuffer {
const uint8_t *data; ///< pointer to the write buffer
size_t len; ///< length of the buffer
};
/// @brief This Class provides the methods to read and write bytes from an I2CBus.
/// @note The I2CBus virtual class follows a *Factory design pattern* that provides all the interfaces methods required
/// by clients while deferring the actual implementation of these methods to a subclasses. I2C-bus specification and
/// user manual can be found here https://www.nxp.com/docs/en/user-guide/UM10204.pdf and an interesting I²C Application
/// note https://www.nxp.com/docs/en/application-note/AN10216.pdf
class I2CBus {
public:
/// @brief Creates a ReadBuffer and calls the virtual readv() method to read bytes into this buffer
/// @param address address of the I²C component on the i2c bus
/// @param buffer pointer to an array of bytes that will be used to store the data received
/// @param len length of the buffer = number of bytes to read
/// @return an i2c::ErrorCode
virtual ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) {
ReadBuffer buf;
buf.data = buffer;
buf.len = len;
return readv(address, &buf, 1);
}
virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) = 0;
/// @brief This virtual method reads bytes from an I2CBus into an array of ReadBuffer.
/// @param address address of the I²C component on the i2c bus
/// @param buffers pointer to an array of ReadBuffer
/// @param count number of ReadBuffer to read
/// @return an i2c::ErrorCode
/// @details This is a pure virtual method that must be implemented in a subclass.
virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t count) = 0;
virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len) {
return write(address, buffer, len, true);
}
/// @brief Creates a WriteBuffer and calls the writev() method to send the bytes from this buffer
/// @param address address of the I²C component on the i2c bus
/// @param buffer pointer to an array of bytes that contains the data to be sent
/// @param len length of the buffer = number of bytes to write
/// @param stop true or false: True will send a stop message, releasing the bus after
/// transmission. False will send a restart, keeping the connection active.
/// @return an i2c::ErrorCode
virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop) {
WriteBuffer buf;
buf.data = buffer;
buf.len = len;
return writev(address, &buf, 1, stop);
}
virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) {
return writev(address, buffers, cnt, true);
}
virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) = 0;
/// @brief This virtual method writes bytes to an I2CBus from an array of WriteBuffer.
/// @param address address of the I²C component on the i2c bus
/// @param buffers pointer to an array of WriteBuffer
/// @param count number of WriteBuffer to write
/// @param stop true or false: True will send a stop message, releasing the bus after
/// transmission. False will send a restart, keeping the connection active.
/// @return an i2c::ErrorCode
/// @details This is a pure virtual method that must be implemented in the subclass.
virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t count, bool stop) = 0;
protected:
/// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair
/// that contains the address and the corresponding bool presence flag.
void i2c_scan_() {
for (uint8_t address = 8; address < 120; address++) {
auto err = writev(address, nullptr, 0);
@@ -61,8 +104,8 @@ class I2CBus {
}
}
}
std::vector<std::pair<uint8_t, bool>> scan_results_;
bool scan_{false};
std::vector<std::pair<uint8_t, bool>> scan_results_; ///< array containing scan results
bool scan_{false}; ///< Should we scan ? Can be set in the yaml
};
} // namespace i2c

View File

@@ -29,7 +29,7 @@ void I2SAudioSpeaker::start_() {
}
this->state_ = speaker::STATE_RUNNING;
xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 0, &this->player_task_handle_);
xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 1, &this->player_task_handle_);
}
void I2SAudioSpeaker::player_task(void *params) {

View File

@@ -36,6 +36,7 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = "image"
DEPENDENCIES = ["display"]
MULTI_CONF = True
MULTI_CONF_NO_DEFAULT = True
image_ns = cg.esphome_ns.namespace("image")

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 @@ void LilygoT547Touchscreen::update_touches() {
id = (buffer[i * 5] >> 4) & 0x0F;
y_raw = (uint16_t) ((buffer[i * 5 + 1] << 4) | ((buffer[i * 5 + 3] >> 4) & 0x0F));
x_raw = (uint16_t) ((buffer[i * 5 + 2] << 4) | (buffer[i * 5 + 3] & 0x0F));
this->set_raw_touch_position_(id, x_raw, y_raw);
this->add_raw_touch_position_(id, x_raw, y_raw);
}
this->status_clear_warning();

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

@@ -237,8 +237,8 @@ void Logger::pre_setup() {
Serial1.begin(this->baud_rate_);
#else
#if ARDUINO_USB_CDC_ON_BOOT
this->hw_serial_ = &Serial;
Serial.begin(this->baud_rate_);
this->hw_serial_ = &Serial0;
Serial0.begin(this->baud_rate_);
#else
this->hw_serial_ = &Serial;
Serial.begin(this->baud_rate_);
@@ -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

@@ -195,7 +195,7 @@ void PMSX003Component::send_command_(uint8_t cmd, uint16_t data) {
void PMSX003Component::parse_data_() {
switch (this->type_) {
case PMSX003_TYPE_5003ST: {
float temperature = this->get_16_bit_uint_(30) / 10.0f;
float temperature = (int16_t) this->get_16_bit_uint_(30) / 10.0f;
float humidity = this->get_16_bit_uint_(32) / 10.0f;
ESP_LOGD(TAG, "Got Temperature: %.1f°C, Humidity: %.1f%%", temperature, humidity);
@@ -279,7 +279,7 @@ void PMSX003Component::parse_data_() {
// Note the pm particles 50um & 100um are not returned,
// as PMS5003T uses those data values for temperature and humidity.
float temperature = this->get_16_bit_uint_(24) / 10.0f;
float temperature = (int16_t) this->get_16_bit_uint_(24) / 10.0f;
float humidity = this->get_16_bit_uint_(26) / 10.0f;
ESP_LOGD(TAG,

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

@@ -352,7 +352,7 @@ void SEN5XComponent::update() {
float humidity = measurements[4] / 100.0;
if (measurements[4] == 0xFFFF)
humidity = NAN;
float temperature = measurements[5] / 200.0;
float temperature = (int16_t) measurements[5] / 200.0;
if (measurements[5] == 0xFFFF)
temperature = NAN;
float voc = measurements[6] / 10.0;

View File

@@ -51,7 +51,7 @@ void Touchscreen::loop() {
}
}
void Touchscreen::set_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw) {
void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw) {
TouchPoint tp;
uint16_t x, y;
if (this->touches_.count(id) == 0) {

View File

@@ -87,7 +87,7 @@ class Touchscreen : public PollingComponent {
void attach_interrupt_(InternalGPIOPin *irq_pin, esphome::gpio::InterruptType type);
void set_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0);
void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0);
void send_touches_();

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() {
@@ -109,7 +112,7 @@ void TT21100Touchscreen::update_touches() {
i, touch->touch_type, touch->tip, touch->event_id, touch->touch_id, touch->x, touch->y,
touch->pressure, touch->major_axis_length, touch->orientation);
this->set_raw_touch_position_(touch->tip, touch->x, touch->y, touch->pressure);
this->add_raw_touch_position_(touch->tip, touch->x, touch->y, touch->pressure);
}
}
}

View File

@@ -9,13 +9,20 @@ static const char *const TAG = "tuya.fan";
void TuyaFan::setup() {
if (this->speed_id_.has_value()) {
this->parent_->register_listener(*this->speed_id_, [this](const TuyaDatapoint &datapoint) {
ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum);
if (datapoint.value_enum >= this->speed_count_) {
ESP_LOGE(TAG, "Speed has invalid value %d", datapoint.value_enum);
} else {
this->speed = datapoint.value_enum + 1;
if (datapoint.type == TuyaDatapointType::ENUM) {
ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum);
if (datapoint.value_enum >= this->speed_count_) {
ESP_LOGE(TAG, "Speed has invalid value %d", datapoint.value_enum);
} else {
this->speed = datapoint.value_enum + 1;
this->publish_state();
}
} else if (datapoint.type == TuyaDatapointType::INTEGER) {
ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_int);
this->speed = datapoint.value_int;
this->publish_state();
}
this->speed_type_ = datapoint.type;
});
}
if (this->switch_id_.has_value()) {
@@ -80,7 +87,11 @@ void TuyaFan::control(const fan::FanCall &call) {
this->parent_->set_enum_datapoint_value(*this->direction_id_, enable);
}
if (this->speed_id_.has_value() && call.get_speed().has_value()) {
this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1);
if (this->speed_type_ == TuyaDatapointType::ENUM) {
this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1);
} else if (this->speed_type_ == TuyaDatapointType::INTEGER) {
this->parent_->set_integer_datapoint_value(*this->speed_id_, *call.get_speed());
}
}
}

View File

@@ -28,6 +28,7 @@ class TuyaFan : public Component, public fan::Fan {
optional<uint8_t> oscillation_id_{};
optional<uint8_t> direction_id_{};
int speed_count_{};
TuyaDatapointType speed_type_{};
};
} // namespace tuya

View File

@@ -31,40 +31,122 @@ const LogString *parity_to_str(UARTParityOptions parity);
class UARTComponent {
public:
// Writes an array of bytes to the UART bus.
// @param data A vector of bytes to be written.
void write_array(const std::vector<uint8_t> &data) { this->write_array(&data[0], data.size()); }
// Writes a single byte to the UART bus.
// @param data The byte to be written.
void write_byte(uint8_t data) { this->write_array(&data, 1); };
// Writes a null-terminated string to the UART bus.
// @param str Pointer to the null-terminated string.
void write_str(const char *str) {
const auto *data = reinterpret_cast<const uint8_t *>(str);
this->write_array(data, strlen(str));
};
// Pure virtual method to write an array of bytes to the UART bus.
// @param data Pointer to the array of bytes.
// @param len Length of the array.
virtual void write_array(const uint8_t *data, size_t len) = 0;
// Reads a single byte from the UART bus.
// @param data Pointer to the byte where the read data will be stored.
// @return True if a byte was successfully read, false otherwise.
bool read_byte(uint8_t *data) { return this->read_array(data, 1); };
// Pure virtual method to peek the next byte in the UART buffer without removing it.
// @param data Pointer to the byte where the peeked data will be stored.
// @return True if a byte is available to peek, false otherwise.
virtual bool peek_byte(uint8_t *data) = 0;
// Pure virtual method to read an array of bytes from the UART bus.
// @param data Pointer to the array where the read data will be stored.
// @param len Number of bytes to read.
// @return True if the specified number of bytes were successfully read, false otherwise.
virtual bool read_array(uint8_t *data, size_t len) = 0;
/// Return available number of bytes.
// Pure virtual method to return the number of bytes available for reading.
// @return Number of available bytes.
virtual int available() = 0;
/// Block until all bytes have been written to the UART bus.
// Pure virtual method to block until all bytes have been written to the UART bus.
virtual void flush() = 0;
// Sets the TX (transmit) pin for the UART bus.
// @param tx_pin Pointer to the internal GPIO pin used for transmission.
void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; }
// Sets the RX (receive) pin for the UART bus.
// @param rx_pin Pointer to the internal GPIO pin used for reception.
void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; }
// Sets the size of the RX buffer.
// @param rx_buffer_size Size of the RX buffer in bytes.
void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; }
// Gets the size of the RX buffer.
// @return Size of the RX buffer in bytes.
size_t get_rx_buffer_size() { return this->rx_buffer_size_; }
// Sets the number of stop bits used in UART communication.
// @param stop_bits Number of stop bits.
void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; }
// Gets the number of stop bits used in UART communication.
// @return Number of stop bits.
uint8_t get_stop_bits() const { return this->stop_bits_; }
// Set the number of data bits used in UART communication.
// @param data_bits Number of data bits.
void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; }
// Get the number of data bits used in UART communication.
// @return Number of data bits.
uint8_t get_data_bits() const { return this->data_bits_; }
// Set the parity used in UART communication.
// @param parity Parity option.
void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
// Get the parity used in UART communication.
// @return Parity option.
UARTParityOptions get_parity() const { return this->parity_; }
// Set the baud rate for UART communication.
// @param baud_rate Baud rate in bits per second.
void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; }
// Get the baud rate for UART communication.
// @return Baud rate in bits per second.
uint32_t get_baud_rate() const { return baud_rate_; }
#ifdef USE_ESP32
virtual void load_settings() = 0;
virtual void load_settings(bool dump_config) = 0;
/**
* Load the UART settings.
* @param dump_config If true (default), output the new settings to logs; otherwise, change settings quietly.
*
* Example:
* ```cpp
* id(uart1).load_settings(false);
* ```
*
* This will load the current UART interface with the latest settings (baud_rate, parity, etc).
*/
virtual void load_settings(bool dump_config){};
/**
* Load the UART settings.
*
* Example:
* ```cpp
* id(uart1).load_settings();
* ```
*
* This will load the current UART interface with the latest settings (baud_rate, parity, etc).
*/
virtual void load_settings(){};
#endif // USE_ESP32
#ifdef USE_UART_DEBUGGER

View File

@@ -88,7 +88,11 @@ void ESP32ArduinoUARTComponent::setup() {
#endif
static uint8_t next_uart_num = 0;
if (is_default_tx && is_default_rx && next_uart_num == 0) {
#if ARDUINO_USB_CDC_ON_BOOT
this->hw_serial_ = &Serial0;
#else
this->hw_serial_ = &Serial;
#endif
next_uart_num++;
} else {
#ifdef USE_LOGGER

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

@@ -167,6 +167,25 @@ void WaveshareEPaper::on_safe_shutdown() { this->deep_sleep(); }
// ========================================================
void WaveshareEPaperTypeA::initialize() {
// Achieve display intialization
this->init_display_();
// If a reset pin is configured, eligible displays can be set to deep sleep
// between updates, as recommended by the hardware provider
if (this->reset_pin_ != nullptr) {
switch (this->model_) {
// More models can be added here to enable deep sleep if eligible
case WAVESHARE_EPAPER_1_54_IN:
case WAVESHARE_EPAPER_1_54_IN_V2:
this->deep_sleep_between_updates_ = true;
ESP_LOGI(TAG, "Set the display to deep sleep");
this->deep_sleep();
break;
default:
break;
}
}
}
void WaveshareEPaperTypeA::init_display_() {
if (this->model_ == TTGO_EPAPER_2_13_IN_B74) {
this->reset_pin_->digital_write(false);
delay(10);
@@ -261,6 +280,13 @@ void HOT WaveshareEPaperTypeA::display() {
bool full_update = this->at_update_ == 0;
bool prev_full_update = this->at_update_ == 1;
if (this->deep_sleep_between_updates_) {
ESP_LOGI(TAG, "Wake up the display");
this->reset_();
this->wait_until_idle_();
this->init_display_();
}
if (!this->wait_until_idle_()) {
this->status_set_warning();
return;
@@ -384,6 +410,11 @@ void HOT WaveshareEPaperTypeA::display() {
this->command(0xFF);
this->status_clear_warning();
if (this->deep_sleep_between_updates_) {
ESP_LOGI(TAG, "Set the display back to deep sleep");
this->deep_sleep();
}
}
int WaveshareEPaperTypeA::get_width_internal() {
switch (this->model_) {
@@ -445,6 +476,8 @@ void WaveshareEPaperTypeA::set_full_update_every(uint32_t full_update_every) {
uint32_t WaveshareEPaperTypeA::idle_timeout_() {
switch (this->model_) {
case WAVESHARE_EPAPER_1_54_IN:
case WAVESHARE_EPAPER_1_54_IN_V2:
case TTGO_EPAPER_2_13_IN_B1:
return 2500;
default:

View File

@@ -92,13 +92,20 @@ class WaveshareEPaperTypeA : public WaveshareEPaper {
void display() override;
void deep_sleep() override {
if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2 || this->model_ == WAVESHARE_EPAPER_1_54_IN_V2) {
// COMMAND DEEP SLEEP MODE
this->command(0x10);
this->data(0x01);
} else {
// COMMAND DEEP SLEEP MODE
this->command(0x10);
switch (this->model_) {
// Models with specific deep sleep command and data
case WAVESHARE_EPAPER_1_54_IN:
case WAVESHARE_EPAPER_1_54_IN_V2:
case WAVESHARE_EPAPER_2_9_IN_V2:
// COMMAND DEEP SLEEP MODE
this->command(0x10);
this->data(0x01);
break;
// Other models default to simple deep sleep command
default:
// COMMAND DEEP SLEEP
this->command(0x10);
break;
}
this->wait_until_idle_();
}
@@ -108,6 +115,8 @@ class WaveshareEPaperTypeA : public WaveshareEPaper {
protected:
void write_lut_(const uint8_t *lut, uint8_t size);
void init_display_();
int get_width_internal() override;
int get_height_internal() override;
@@ -118,6 +127,8 @@ class WaveshareEPaperTypeA : public WaveshareEPaper {
uint32_t at_update_{0};
WaveshareEPaperTypeAModel model_;
uint32_t idle_timeout_() override;
bool deep_sleep_between_updates_{false};
};
enum WaveshareEPaperTypeBModel {

View File

@@ -117,7 +117,7 @@ class AsyncWebServerRequest {
// NOLINTNEXTLINE(readability-identifier-naming)
AsyncWebServerResponse *beginResponse(int code, const char *content_type) {
auto *res = new AsyncWebServerResponseEmpty(this); // NOLINT(cppcoreguidelines-owning-memory)
this->init_response_(res, 200, content_type);
this->init_response_(res, code, content_type);
return res;
}
// NOLINTNEXTLINE(readability-identifier-naming)

View File

@@ -55,7 +55,7 @@ void XPT2046Component::update_touches() {
ESP_LOGV(TAG, "Touchscreen Update [%d, %d], z = %d", x_raw, y_raw, z_raw);
this->set_raw_touch_position_(0, x_raw, y_raw, z_raw);
this->add_raw_touch_position_(0, x_raw, y_raw, z_raw);
}
}

View File

@@ -39,6 +39,17 @@ _LOGGER = logging.getLogger(__name__)
def iter_components(config):
for domain, conf in config.items():
component = get_component(domain)
yield domain, component
if component.is_platform_component:
for p_config in conf:
p_name = f"{domain}.{p_config[CONF_PLATFORM]}"
platform = get_platform(domain, p_config[CONF_PLATFORM])
yield p_name, platform
def iter_component_configs(config):
for domain, conf in config.items():
component = get_component(domain)
if component.multi_conf:
@@ -303,8 +314,14 @@ class LoadValidationStep(ConfigValidationStep):
# Ignore top-level keys starting with a dot
return
result.add_output_path([self.domain], self.domain)
result[self.domain] = self.conf
component = get_component(self.domain)
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]
if component is None:
result.add_str_error(f"Component not found: {self.domain}", path)
@@ -424,7 +441,10 @@ class MetadataValidationStep(ConfigValidationStep):
def run(self, result: Config) -> None:
if self.conf is None:
result[self.domain] = self.conf = {}
if self.comp.multi_conf and self.comp.multi_conf_no_default:
result[self.domain] = self.conf = []
else:
result[self.domain] = self.conf = {}
success = True
for dependency in self.comp.dependencies:

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.0b2"
__version__ = "2023.12.9"
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

@@ -57,6 +57,10 @@ class ComponentManifest:
def multi_conf(self) -> bool:
return getattr(self.module, "MULTI_CONF", False)
@property
def multi_conf_no_default(self) -> bool:
return getattr(self.module, "MULTI_CONF_NO_DEFAULT", False)
@property
def to_code(self) -> Optional[Callable[[Any], None]]:
return getattr(self.module, "to_code", None)

View File

@@ -1,7 +1,7 @@
import operator
from functools import reduce
import esphome.config_validation as cv
from esphome.core import CORE, ID
from esphome.core import CORE
from esphome.const import (
CONF_INPUT,
@@ -25,15 +25,16 @@ class PinRegistry(dict):
def reset(self):
self.pins_used = {}
def get_count(self, key, number):
def get_count(self, key, id, number):
"""
Get the number of places a given pin is used.
:param key: The ID of the defining component
:param key: The key of the registered pin schema.
:param id: The ID of the defining component
:param number: The pin number
:return: The number of places the pin is used.
"""
pin_key = (key, number)
return self.pins_used[pin_key] if pin_key in self.pins_used else 0
pin_key = (key, id, number)
return len(self.pins_used[pin_key]) if pin_key in self.pins_used else 0
def register(self, name, schema, final_validate=None):
"""
@@ -65,9 +66,10 @@ class PinRegistry(dict):
result = self[key][1](conf)
if CONF_NUMBER in result:
# key maps to the pin schema
if isinstance(key, ID):
key = key.id
pin_key = (key, result[CONF_NUMBER])
if key != CORE.target_platform:
pin_key = (key, conf[key], result[CONF_NUMBER])
else:
pin_key = (key, key, result[CONF_NUMBER])
if pin_key not in self.pins_used:
self.pins_used[pin_key] = []
# client_id identifies the instance of the providing component
@@ -101,7 +103,7 @@ class PinRegistry(dict):
Run the final validation for all pins, and check for reuse
:param fconf: The full config
"""
for (key, _), pin_list in self.pins_used.items():
for (key, _, _), pin_list in self.pins_used.items():
count = len(pin_list) # number of places same pin used.
final_val_fun = self[key][2] # final validation function
for pin_path, client_id, pin_config in pin_list:

View File

@@ -4,7 +4,7 @@ import re
from pathlib import Path
from typing import Union
from esphome.config import iter_components
from esphome.config import iter_components, iter_component_configs
from esphome.const import (
HEADER_FILE_EXTENSIONS,
SOURCE_FILE_EXTENSIONS,
@@ -70,14 +70,14 @@ UPLOAD_SPEED_OVERRIDE = {
def get_flags(key):
flags = set()
for _, component, conf in iter_components(CORE.config):
for _, component, conf in iter_component_configs(CORE.config):
flags |= getattr(component, key)(conf)
return flags
def get_include_text():
include_text = '#include "esphome.h"\nusing namespace esphome;\n'
for _, component, conf in iter_components(CORE.config):
for _, component, conf in iter_component_configs(CORE.config):
if not hasattr(component, "includes"):
continue
includes = component.includes
@@ -232,7 +232,7 @@ the custom_components folder or the external_components feature.
def copy_src_tree():
source_files: list[loader.FileResource] = []
for _, component, _ in iter_components(CORE.config):
for _, component in iter_components(CORE.config):
source_files += component.resources
source_files_map = {
Path(x.package.replace(".", "/") + "/" + x.resource): x for x in source_files

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

@@ -10,8 +10,8 @@ platformio==6.1.11 # When updating platformio, also update Dockerfile
esptool==4.6.2
click==8.1.7
esphome-dashboard==20231107.0
aioesphomeapi==21.0.0
zeroconf==0.128.4
aioesphomeapi==21.0.1
zeroconf==0.130.0
python-magic==0.4.27
# esp-idf requires this, but doesn't bundle it by default

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:

View File

@@ -1667,7 +1667,6 @@ binary_sensor:
mcp23xxx: mcp23s08_hub
# Use pin number 1
number: 1
allow_other_uses: true
# One of INPUT or INPUT_PULLUP
mode: INPUT_PULLUP
inverted: false
@@ -2149,7 +2148,6 @@ output:
pin:
mcp23xxx: mcp23017_hub
number: 0
allow_other_uses: true
mode: OUTPUT
inverted: false
- platform: gpio
@@ -2157,7 +2155,6 @@ output:
pin:
mcp23xxx: mcp23008_hub
number: 0
allow_other_uses: true
mode: OUTPUT
inverted: false
- platform: gpio
@@ -2597,7 +2594,6 @@ switch:
mcp23xxx: mcp23s08_hub
# Use pin number 0
number: 0
allow_other_uses: true
mode: OUTPUT
inverted: false
- platform: gpio

View File

@@ -401,7 +401,6 @@ switch:
pin:
mcp23xxx: mcp23017_hub
number: 0
allow_other_uses: true
mode: OUTPUT
interlock: &interlock [gpio_switch1, gpio_switch2, gpio_switch3]
- platform: gpio
@@ -409,7 +408,6 @@ switch:
pin:
mcp23xxx: mcp23008_hub
number: 0
allow_other_uses: true
mode: OUTPUT
interlock: *interlock
- platform: gpio

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

@@ -92,3 +92,13 @@ sensor:
name: "Loop Time"
psram:
name: "PSRAM Free"
# Purposely test that `animation:` does auto-load `image:`
# Keep the `image:` undefined.
# image:
animation:
- id: rgb565_animation
file: pnglogo.png
type: RGB565
use_transparency: no

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)