mirror of
https://github.com/esphome/esphome.git
synced 2026-09-24 05:24:14 +00:00
Merge branch 'dev' into ble_set_security_params
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -35,3 +35,99 @@ button:
|
||||
data: [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]
|
||||
- cc1101.send_packet: !lambda |-
|
||||
return {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
||||
|
||||
- cc1101.set_frequency: !lambda |-
|
||||
return 433.91e6;
|
||||
- cc1101.set_frequency:
|
||||
value: "433.91MHz"
|
||||
- cc1101.set_frequency:
|
||||
value: 433911000
|
||||
- cc1101.set_frequency: 433912000
|
||||
|
||||
- cc1101.set_output_power: !lambda |-
|
||||
return -29.9;
|
||||
- cc1101.set_output_power:
|
||||
value: "-28"
|
||||
- cc1101.set_output_power:
|
||||
value: 10
|
||||
- cc1101.set_output_power: 11
|
||||
|
||||
- cc1101.set_modulation_type: !lambda |-
|
||||
return cc1101::Modulation::MODULATION_2_FSK;
|
||||
- cc1101.set_modulation_type:
|
||||
value: "4-FSK"
|
||||
- cc1101.set_modulation_type: "GFSK"
|
||||
|
||||
- cc1101.set_symbol_rate: !lambda |-
|
||||
return 6000.0;
|
||||
- cc1101.set_symbol_rate:
|
||||
value: "7000.0"
|
||||
- cc1101.set_symbol_rate:
|
||||
value: 8000.0
|
||||
- cc1101.set_symbol_rate: 9000
|
||||
|
||||
- cc1101.set_rx_attenuation: !lambda |-
|
||||
return cc1101::RxAttenuation::RX_ATTENUATION_0DB;
|
||||
- cc1101.set_rx_attenuation:
|
||||
value: "6dB"
|
||||
- cc1101.set_rx_attenuation: "12dB"
|
||||
|
||||
- cc1101.set_dc_blocking_filter: !lambda |-
|
||||
return false;
|
||||
- cc1101.set_dc_blocking_filter:
|
||||
value: true
|
||||
- cc1101.set_dc_blocking_filter: false
|
||||
|
||||
- cc1101.set_manchester: !lambda |-
|
||||
return false;
|
||||
- cc1101.set_manchester:
|
||||
value: true
|
||||
- cc1101.set_manchester: false
|
||||
|
||||
- cc1101.set_filter_bandwidth: !lambda |-
|
||||
return 58e3;
|
||||
- cc1101.set_filter_bandwidth:
|
||||
value: "59kHz"
|
||||
- cc1101.set_filter_bandwidth:
|
||||
value: 60000
|
||||
- cc1101.set_filter_bandwidth: "61kHz"
|
||||
|
||||
- cc1101.set_fsk_deviation: !lambda |-
|
||||
return 1.5e3;
|
||||
- cc1101.set_fsk_deviation:
|
||||
value: "1.6kHz"
|
||||
- cc1101.set_fsk_deviation:
|
||||
value: 1700
|
||||
- cc1101.set_fsk_deviation: "1.8kHz"
|
||||
|
||||
- cc1101.set_msk_deviation: !lambda |-
|
||||
return 1;
|
||||
- cc1101.set_msk_deviation:
|
||||
value: "2"
|
||||
- cc1101.set_msk_deviation:
|
||||
value: 3
|
||||
- cc1101.set_msk_deviation: "4"
|
||||
|
||||
- cc1101.set_channel: !lambda |-
|
||||
return 0;
|
||||
- cc1101.set_channel:
|
||||
value: "1"
|
||||
- cc1101.set_channel:
|
||||
value: 3
|
||||
- cc1101.set_channel: 3
|
||||
|
||||
- cc1101.set_channel_spacing: !lambda |-
|
||||
return 25e3;
|
||||
- cc1101.set_channel_spacing:
|
||||
value: "26kHz"
|
||||
- cc1101.set_channel_spacing:
|
||||
value: 27000
|
||||
- cc1101.set_channel_spacing: "28kHz"
|
||||
|
||||
- cc1101.set_if_frequency: !lambda |-
|
||||
return 25e3;
|
||||
- cc1101.set_if_frequency:
|
||||
value: "26kHz"
|
||||
- cc1101.set_if_frequency:
|
||||
value: 27000
|
||||
- cc1101.set_if_frequency: "28kHz"
|
||||
|
||||
@@ -27,9 +27,9 @@ sensor:
|
||||
name: Linearly combined temperatures
|
||||
sources:
|
||||
- source: template_temperature1
|
||||
coeffecient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;"
|
||||
coefficient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;"
|
||||
- source: template_temperature2
|
||||
coeffecient: 1.5
|
||||
coefficient: 1.5
|
||||
- platform: combination
|
||||
type: max
|
||||
name: Max of combined temperatures
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
esphome:
|
||||
on_boot:
|
||||
then:
|
||||
- lambda: |-
|
||||
// Test deprecated std::string overload still compiles
|
||||
std::string key = "00112233445566778899aabbccddeeff";
|
||||
id(dsmr_instance).set_decryption_key(key);
|
||||
|
||||
dsmr:
|
||||
id: dsmr_instance
|
||||
decryption_key: 00112233445566778899aabbccddeeff
|
||||
max_telegram_length: 1000
|
||||
request_pin: ${request_pin}
|
||||
|
||||
@@ -57,6 +57,23 @@ display:
|
||||
allow_other_uses: true
|
||||
number: GPIO4
|
||||
|
||||
- platform: epaper_spi
|
||||
spi_id: spi_bus
|
||||
model: waveshare-7.5in-H
|
||||
cs_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO5
|
||||
dc_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO17
|
||||
reset_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO16
|
||||
busy_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO4
|
||||
inverted: true
|
||||
|
||||
- platform: epaper_spi
|
||||
model: seeed-reterminal-e1002
|
||||
- platform: epaper_spi
|
||||
@@ -64,3 +81,66 @@ display:
|
||||
# Override pins to avoid conflict with other display configs
|
||||
busy_pin: 43
|
||||
dc_pin: 42
|
||||
|
||||
# WeAct 2.13" 3-color e-paper (122x250, SSD1680)
|
||||
- platform: epaper_spi
|
||||
spi_id: spi_bus
|
||||
model: weact-2.13in-3c
|
||||
cs_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO5
|
||||
dc_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO17
|
||||
reset_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO16
|
||||
busy_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO4
|
||||
lambda: |-
|
||||
it.filled_rectangle(0, 0, it.get_width(), it.get_height(), Color::WHITE);
|
||||
it.circle(it.get_width() / 2, it.get_height() / 2, 20, Color::BLACK);
|
||||
it.circle(it.get_width() / 2, it.get_height() / 2, 15, Color(255, 0, 0));
|
||||
|
||||
# WeAct 2.9" 3-color e-paper (128x296, SSD1683)
|
||||
- platform: epaper_spi
|
||||
spi_id: spi_bus
|
||||
model: weact-2.9in-3c
|
||||
cs_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO5
|
||||
dc_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO17
|
||||
reset_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO16
|
||||
busy_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO4
|
||||
lambda: |-
|
||||
it.filled_rectangle(0, 0, it.get_width(), it.get_height(), Color::WHITE);
|
||||
it.circle(it.get_width() / 2, it.get_height() / 2, 20, Color::BLACK);
|
||||
it.circle(it.get_width() / 2, it.get_height() / 2, 15, Color(255, 0, 0));
|
||||
|
||||
# WeAct 4.2" 3-color e-paper (400x300, SSD1683)
|
||||
- platform: epaper_spi
|
||||
spi_id: spi_bus
|
||||
model: weact-4.2in-3c
|
||||
cs_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO5
|
||||
dc_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO17
|
||||
reset_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO16
|
||||
busy_pin:
|
||||
allow_other_uses: true
|
||||
number: GPIO4
|
||||
lambda: |-
|
||||
it.filled_rectangle(0, 0, it.get_width(), it.get_height(), Color::WHITE);
|
||||
it.circle(it.get_width() / 2, it.get_height() / 2, 30, Color::BLACK);
|
||||
it.circle(it.get_width() / 2, it.get_height() / 2, 20, Color(255, 0, 0));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
esp32:
|
||||
variant: esp32p4
|
||||
engineering_sample: true
|
||||
flash_size: 32MB
|
||||
cpu_frequency: 400MHz
|
||||
framework:
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<<: !include common.yaml
|
||||
|
||||
esp32_ble:
|
||||
io_capability: keyboard_only
|
||||
disable_bt_logs: false
|
||||
@@ -33,6 +33,10 @@ esp32_ble_server:
|
||||
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc42d
|
||||
advertise: false
|
||||
characteristics:
|
||||
- id: test_lambda_characteristic
|
||||
uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc12c
|
||||
read: true
|
||||
value: !lambda return { 1, 2 };
|
||||
- id: test_change_characteristic
|
||||
uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc11c
|
||||
read: true
|
||||
|
||||
@@ -4,7 +4,6 @@ esp32_touch:
|
||||
measurement_duration: 8ms
|
||||
low_voltage_reference: 0.5V
|
||||
high_voltage_reference: 2.7V
|
||||
voltage_attenuation: 1.5V
|
||||
|
||||
binary_sensor:
|
||||
- platform: esp32_touch
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
pin: GPIO5
|
||||
|
||||
<<: !include common-variants.yaml
|
||||
<<: !include common-get-value.yaml
|
||||
@@ -3,10 +3,13 @@ esp_ldo:
|
||||
channel: 3
|
||||
voltage: 2.5V
|
||||
adjustable: true
|
||||
- id: ldo_4
|
||||
- id: ldo_4_passthrough
|
||||
channel: 4
|
||||
voltage: 2.0V
|
||||
setup_priority: 900
|
||||
voltage: passthrough
|
||||
- id: ldo_1_internal
|
||||
channel: 1
|
||||
voltage: 1.8V
|
||||
allow_internal_channel: true
|
||||
|
||||
esphome:
|
||||
on_boot:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
esphome:
|
||||
on_boot:
|
||||
then:
|
||||
- hdc302x.heater_on:
|
||||
id: hdc302x_sensor
|
||||
power: QUARTER
|
||||
duration: 5s
|
||||
- hdc302x.heater_off:
|
||||
id: hdc302x_sensor
|
||||
|
||||
sensor:
|
||||
- platform: hdc302x
|
||||
id: hdc302x_sensor
|
||||
i2c_id: i2c_bus
|
||||
temperature:
|
||||
name: Temperature
|
||||
humidity:
|
||||
name: Humidity
|
||||
update_interval: 15s
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -90,6 +90,19 @@ text_sensor:
|
||||
id: ha_hello_world_text2
|
||||
attribute: some_attribute
|
||||
|
||||
event:
|
||||
- platform: template
|
||||
name: Test Event
|
||||
id: test_event
|
||||
event_types:
|
||||
- test_event_type
|
||||
on_event:
|
||||
- homeassistant.event:
|
||||
event: esphome.test_event
|
||||
data:
|
||||
event_name: !lambda |-
|
||||
return event_type;
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
on_time:
|
||||
|
||||
@@ -4,15 +4,16 @@ interval:
|
||||
- interval: 60s
|
||||
then:
|
||||
- lambda: |-
|
||||
// Test build_json
|
||||
std::string json_str = esphome::json::build_json([](JsonObject root) {
|
||||
// Test build_json - returns SerializationBuffer, use auto to avoid heap allocation
|
||||
auto json_buf = esphome::json::build_json([](JsonObject root) {
|
||||
root["sensor"] = "temperature";
|
||||
root["value"] = 23.5;
|
||||
root["unit"] = "°C";
|
||||
});
|
||||
ESP_LOGD("test", "Built JSON: %s", json_str.c_str());
|
||||
ESP_LOGD("test", "Built JSON: %s", json_buf.c_str());
|
||||
|
||||
// Test parse_json
|
||||
// Test parse_json - implicit conversion to std::string for backward compatibility
|
||||
std::string json_str = json_buf;
|
||||
bool parse_ok = esphome::json::parse_json(json_str, [](JsonObject root) {
|
||||
if (root["sensor"].is<const char*>() && root["value"].is<float>()) {
|
||||
const char* sensor = root["sensor"];
|
||||
@@ -26,10 +27,10 @@ interval:
|
||||
});
|
||||
ESP_LOGD("test", "Parse result (JSON syntax only): %s", parse_ok ? "success" : "failed");
|
||||
|
||||
// Test JsonBuilder class
|
||||
// Test JsonBuilder class - returns SerializationBuffer
|
||||
esphome::json::JsonBuilder builder;
|
||||
JsonObject obj = builder.root();
|
||||
obj["test"] = "direct_builder";
|
||||
obj["count"] = 42;
|
||||
std::string result = builder.serialize();
|
||||
auto result = builder.serialize();
|
||||
ESP_LOGD("test", "JsonBuilder result: %s", result.c_str());
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "esphome/components/ld2450/ld2450.h"
|
||||
#include "esphome/components/uart/uart_component.h"
|
||||
|
||||
namespace esphome::ld2450::testing {
|
||||
|
||||
// Mock UART component to satisfy UARTDevice parent requirement.
|
||||
class MockUARTComponent : public uart::UARTComponent {
|
||||
public:
|
||||
void write_array(const uint8_t *data, size_t len) override {}
|
||||
MOCK_METHOD(bool, read_array, (uint8_t * data, size_t len), (override));
|
||||
MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override));
|
||||
MOCK_METHOD(size_t, available, (), (override));
|
||||
MOCK_METHOD(void, flush, (), (override));
|
||||
MOCK_METHOD(void, check_logger_conflict, (), (override));
|
||||
};
|
||||
|
||||
// Expose protected members for testing.
|
||||
class TestableLD2450 : public LD2450Component {
|
||||
public:
|
||||
using LD2450Component::buffer_data_;
|
||||
using LD2450Component::buffer_pos_;
|
||||
using LD2450Component::readline_;
|
||||
|
||||
void feed(const std::vector<uint8_t> &data) {
|
||||
for (uint8_t byte : data) {
|
||||
this->readline_(byte);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// LD2450 periodic data frame: header (4) + 3 targets * 8 bytes + footer (2) = 30 bytes
|
||||
// All-zero targets means no presence detected.
|
||||
inline std::vector<uint8_t> make_periodic_frame(uint8_t fill = 0x00) {
|
||||
std::vector<uint8_t> frame = {0xAA, 0xFF, 0x03, 0x00}; // DATA_FRAME_HEADER
|
||||
for (int i = 0; i < 24; i++) {
|
||||
frame.push_back(fill); // 3 targets * 8 bytes
|
||||
}
|
||||
frame.push_back(0x55); // DATA_FRAME_FOOTER
|
||||
frame.push_back(0xCC);
|
||||
return frame;
|
||||
}
|
||||
|
||||
// LD2450 command ACK frame for CMD_ENABLE_CONF (0xFF), successful.
|
||||
// header (4) + length (2) + command (2) + result (2) + footer (4) = 14 bytes
|
||||
inline std::vector<uint8_t> make_ack_frame() {
|
||||
return {
|
||||
0xFD, 0xFC, 0xFB, 0xFA, // CMD_FRAME_HEADER
|
||||
0x04, 0x00, // length = 4
|
||||
0xFF, 0x01, // command = enable_conf, status = success
|
||||
0x00, 0x00, // result = ok
|
||||
0x04, 0x03, 0x02, 0x01 // CMD_FRAME_FOOTER
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2450::testing
|
||||
@@ -0,0 +1,145 @@
|
||||
#include "common.h"
|
||||
|
||||
namespace esphome::ld2450::testing {
|
||||
|
||||
class LD2450ReadlineTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
this->ld2450_.set_uart_parent(&this->mock_uart_);
|
||||
// Ensure clean state
|
||||
ASSERT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
MockUARTComponent mock_uart_;
|
||||
TestableLD2450 ld2450_;
|
||||
};
|
||||
|
||||
// --- Good data tests ---
|
||||
|
||||
TEST_F(LD2450ReadlineTest, ValidPeriodicFrame) {
|
||||
auto frame = make_periodic_frame();
|
||||
this->ld2450_.feed(frame);
|
||||
// After a complete valid frame, buffer should be reset
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
TEST_F(LD2450ReadlineTest, ValidCommandAckFrame) {
|
||||
auto frame = make_ack_frame();
|
||||
this->ld2450_.feed(frame);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
TEST_F(LD2450ReadlineTest, BackToBackPeriodicFrames) {
|
||||
auto frame = make_periodic_frame();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
this->ld2450_.feed(frame);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0) << "Frame " << i << " not processed";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LD2450ReadlineTest, BackToBackMixedFrames) {
|
||||
auto periodic = make_periodic_frame();
|
||||
auto ack = make_ack_frame();
|
||||
this->ld2450_.feed(periodic);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
this->ld2450_.feed(ack);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
this->ld2450_.feed(periodic);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
// --- Garbage then valid frame tests ---
|
||||
|
||||
TEST_F(LD2450ReadlineTest, GarbageThenValidFrame) {
|
||||
// Garbage bytes accumulate in the buffer but don't match any footer.
|
||||
// A valid frame follows; its footer resets the buffer and resyncs.
|
||||
std::vector<uint8_t> garbage = {0x01, 0x02, 0x03, 0x42, 0x99};
|
||||
this->ld2450_.feed(garbage);
|
||||
EXPECT_GT(this->ld2450_.buffer_pos_, 0); // Garbage accumulated
|
||||
|
||||
auto frame = make_periodic_frame();
|
||||
this->ld2450_.feed(frame);
|
||||
// Footer from the valid frame resyncs the parser
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
// --- Footer-based resynchronization tests ---
|
||||
|
||||
TEST_F(LD2450ReadlineTest, FooterInGarbageResyncs) {
|
||||
// Garbage containing a periodic frame footer (0x55 0xCC) triggers
|
||||
// a buffer reset, allowing the next frame to be parsed cleanly.
|
||||
std::vector<uint8_t> garbage_with_footer = {0x01, 0x02, 0x03, 0x04, 0x55, 0xCC};
|
||||
this->ld2450_.feed(garbage_with_footer);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0); // Footer reset the buffer
|
||||
|
||||
auto frame = make_periodic_frame();
|
||||
this->ld2450_.feed(frame);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
TEST_F(LD2450ReadlineTest, CmdFooterInGarbageResyncs) {
|
||||
// Garbage containing a command frame footer (04 03 02 01) also resyncs.
|
||||
std::vector<uint8_t> garbage_with_footer = {0x10, 0x20, 0x30, 0x40, 0x04, 0x03, 0x02, 0x01};
|
||||
this->ld2450_.feed(garbage_with_footer);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
|
||||
auto frame = make_periodic_frame();
|
||||
this->ld2450_.feed(frame);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
// --- Overflow recovery tests ---
|
||||
|
||||
TEST_F(LD2450ReadlineTest, OverflowResetsBuffer) {
|
||||
// Fill the buffer to capacity with filler that won't match any footer.
|
||||
// MAX_LINE_LENGTH is 45, usable is 44. The 45th byte triggers overflow.
|
||||
std::vector<uint8_t> overflow_data(MAX_LINE_LENGTH, 0x11);
|
||||
this->ld2450_.feed(overflow_data);
|
||||
// After overflow, buffer_pos_ resets to 0 (via the < 4 early return path)
|
||||
EXPECT_LT(this->ld2450_.buffer_pos_, 4);
|
||||
}
|
||||
|
||||
TEST_F(LD2450ReadlineTest, OverflowThenValidFrame) {
|
||||
// Overflow, then a valid frame should be processed.
|
||||
std::vector<uint8_t> overflow_data(MAX_LINE_LENGTH, 0x11);
|
||||
this->ld2450_.feed(overflow_data);
|
||||
|
||||
auto frame = make_periodic_frame();
|
||||
this->ld2450_.feed(frame);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
TEST_F(LD2450ReadlineTest, BufferLargeEnoughForDesyncedFooter) {
|
||||
// The key fix: the buffer (45) is large enough that a desynced periodic frame's
|
||||
// footer (at most 30 bytes into the stream) will land inside the buffer before overflow.
|
||||
// Simulate starting 10 bytes into a periodic frame, then a full frame follows.
|
||||
std::vector<uint8_t> mid_frame = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};
|
||||
// Then a complete periodic frame whose footer will land at position 40 (10 + 30),
|
||||
// well within the buffer size of 45.
|
||||
auto frame = make_periodic_frame();
|
||||
mid_frame.insert(mid_frame.end(), frame.begin(), frame.end());
|
||||
|
||||
this->ld2450_.feed(mid_frame);
|
||||
// The footer from the frame should have triggered a reset
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
TEST_F(LD2450ReadlineTest, SimulatedRestartThenFrames) {
|
||||
// Simulate LD2450 restart: burst of garbage followed by valid periodic frames.
|
||||
// The garbage + first frame should fit in the buffer so the footer resyncs.
|
||||
std::vector<uint8_t> restart_noise = {
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // 8 bytes of mid-frame data
|
||||
};
|
||||
auto frame = make_periodic_frame();
|
||||
// 8 garbage + 30 frame = 38 bytes, well within buffer of 45
|
||||
restart_noise.insert(restart_noise.end(), frame.begin(), frame.end());
|
||||
|
||||
this->ld2450_.feed(restart_noise);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
|
||||
// Subsequent frames should work normally
|
||||
this->ld2450_.feed(frame);
|
||||
EXPECT_EQ(this->ld2450_.buffer_pos_, 0);
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2450::testing
|
||||
@@ -71,6 +71,32 @@ esphome:
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
state: on
|
||||
# Test static effect name resolution at codegen time
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: Strobe
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: none
|
||||
# Test resolving a different effect on the same light
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
effect: My Flicker
|
||||
# Test effect: None (capitalized)
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
effect: None
|
||||
# Test effect lambda with no args (on_boot has empty Ts...)
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: !lambda 'return "Strobe";'
|
||||
# Test effect lambda with non-empty args (repeat passes uint32_t iteration)
|
||||
- repeat:
|
||||
count: 3
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: !lambda 'return iteration > 1 ? "Strobe" : "none";'
|
||||
- light.dim_relative:
|
||||
id: test_monochromatic_light
|
||||
relative_brightness: 5%
|
||||
|
||||
@@ -5,3 +5,6 @@ esphome:
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
wait_for_cdc: true
|
||||
early_message: true
|
||||
task_log_buffer_size: 0
|
||||
|
||||
@@ -13,10 +13,10 @@ esphome:
|
||||
on_boot:
|
||||
- priority: 100
|
||||
then:
|
||||
- max7129digit.invert_off:
|
||||
- max7129digit.invert_on:
|
||||
- max7129digit.turn_on:
|
||||
- max7129digit.turn_off:
|
||||
- max7129digit.reverse_on:
|
||||
- max7129digit.reverse_off:
|
||||
- max7129digit.intensity: 10
|
||||
- max7219digit.invert_off:
|
||||
- max7219digit.invert_on:
|
||||
- max7219digit.turn_on:
|
||||
- max7219digit.turn_off:
|
||||
- max7219digit.reverse_on:
|
||||
- max7219digit.reverse_off:
|
||||
- max7219digit.intensity: 10
|
||||
|
||||
@@ -23,8 +23,27 @@ media_player:
|
||||
- media_player.stop:
|
||||
- media_player.stop:
|
||||
announcement: true
|
||||
on_announcement:
|
||||
- media_player.play:
|
||||
on_turn_on:
|
||||
- media_player.play:
|
||||
on_turn_off:
|
||||
- media_player.stop:
|
||||
on_pause:
|
||||
- media_player.toggle:
|
||||
- media_player.turn_on:
|
||||
- media_player.turn_off:
|
||||
- media_player.next:
|
||||
- media_player.previous:
|
||||
- media_player.mute:
|
||||
- media_player.unmute:
|
||||
- media_player.repeat_off:
|
||||
- media_player.repeat_one:
|
||||
- media_player.repeat_all:
|
||||
- media_player.shuffle:
|
||||
- media_player.unshuffle:
|
||||
- media_player.group_join:
|
||||
- media_player.clear_playlist:
|
||||
- wait_until:
|
||||
media_player.is_idle:
|
||||
- wait_until:
|
||||
@@ -33,6 +52,12 @@ media_player:
|
||||
media_player.is_announcing:
|
||||
- wait_until:
|
||||
media_player.is_paused:
|
||||
- wait_until:
|
||||
media_player.is_on:
|
||||
- wait_until:
|
||||
media_player.is_off:
|
||||
- wait_until:
|
||||
media_player.is_muted:
|
||||
- media_player.volume_up:
|
||||
- media_player.volume_down:
|
||||
- media_player.volume_set: 50%
|
||||
|
||||
@@ -3,9 +3,15 @@ display:
|
||||
spi_16: true
|
||||
pixel_mode: 18bit
|
||||
model: ili9488
|
||||
dc_pin: ${dc_pin}
|
||||
cs_pin: ${cs_pin}
|
||||
reset_pin: ${reset_pin}
|
||||
dc_pin:
|
||||
allow_other_uses: true
|
||||
number: ${dc_pin}
|
||||
cs_pin:
|
||||
allow_other_uses: true
|
||||
number: ${cs_pin}
|
||||
reset_pin:
|
||||
allow_other_uses: true
|
||||
number: ${reset_pin}
|
||||
data_rate: 20MHz
|
||||
invert_colors: true
|
||||
show_test_card: true
|
||||
@@ -24,3 +30,15 @@ display:
|
||||
height: 200
|
||||
enable_pin: ${enable_pin}
|
||||
bus_mode: single
|
||||
|
||||
- platform: mipi_spi
|
||||
model: WAVESHARE-1.83-V2
|
||||
dc_pin:
|
||||
allow_other_uses: true
|
||||
number: ${dc_pin}
|
||||
cs_pin:
|
||||
allow_other_uses: true
|
||||
number: ${cs_pin}
|
||||
reset_pin:
|
||||
allow_other_uses: true
|
||||
number: ${reset_pin}
|
||||
|
||||
@@ -219,6 +219,7 @@ cover:
|
||||
name: Template Cover
|
||||
state_topic: some/topic/cover
|
||||
qos: 2
|
||||
mqtt_json_state_payload: true
|
||||
lambda: |-
|
||||
if (id(some_binary_sensor).state) {
|
||||
return COVER_OPEN;
|
||||
@@ -231,6 +232,53 @@ cover:
|
||||
stop_action:
|
||||
- logger.log: stop_action
|
||||
optimistic: true
|
||||
- platform: template
|
||||
name: Template Cover with Position and Tilt
|
||||
state_topic: some/topic/cover_pt
|
||||
position_state_topic: some/topic/cover_pt/position
|
||||
position_command_topic: some/topic/cover_pt/position/set
|
||||
tilt_state_topic: some/topic/cover_pt/tilt
|
||||
tilt_command_topic: some/topic/cover_pt/tilt/set
|
||||
qos: 2
|
||||
has_position: true
|
||||
lambda: |-
|
||||
if (id(some_binary_sensor).state) {
|
||||
return COVER_OPEN;
|
||||
}
|
||||
return COVER_CLOSED;
|
||||
position_action:
|
||||
- logger.log: position_action
|
||||
tilt_action:
|
||||
- logger.log: tilt_action
|
||||
open_action:
|
||||
- logger.log: open_action
|
||||
close_action:
|
||||
- logger.log: close_action
|
||||
stop_action:
|
||||
- logger.log: stop_action
|
||||
optimistic: true
|
||||
- platform: template
|
||||
name: Template Cover with Position and Tilt JSON
|
||||
state_topic: some/topic/cover_pt_json
|
||||
qos: 2
|
||||
mqtt_json_state_payload: true
|
||||
has_position: true
|
||||
lambda: |-
|
||||
if (id(some_binary_sensor).state) {
|
||||
return COVER_OPEN;
|
||||
}
|
||||
return COVER_CLOSED;
|
||||
position_action:
|
||||
- logger.log: position_action
|
||||
tilt_action:
|
||||
- logger.log: tilt_action
|
||||
open_action:
|
||||
- logger.log: open_action
|
||||
close_action:
|
||||
- logger.log: close_action
|
||||
stop_action:
|
||||
- logger.log: stop_action
|
||||
optimistic: true
|
||||
|
||||
datetime:
|
||||
- platform: template
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
sensor:
|
||||
- platform: pulse_counter
|
||||
name: Pulse Counter
|
||||
pin: 4
|
||||
use_pcnt: false
|
||||
count_mode:
|
||||
rising_edge: INCREMENT
|
||||
falling_edge: DECREMENT
|
||||
internal_filter: 13us
|
||||
update_interval: 15s
|
||||
@@ -0,0 +1,12 @@
|
||||
remote_receiver:
|
||||
id: rcvr
|
||||
pin: GPIO2
|
||||
dump: all
|
||||
<<: !include common-actions.yaml
|
||||
|
||||
binary_sensor:
|
||||
- platform: remote_receiver
|
||||
name: Panasonic Remote Input
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100BCBD
|
||||
@@ -0,0 +1,7 @@
|
||||
remote_transmitter:
|
||||
id: xmitr
|
||||
pin: GPIO2
|
||||
carrier_duty_percent: 50%
|
||||
|
||||
packages:
|
||||
buttons: !include common-buttons.yaml
|
||||
@@ -0,0 +1,11 @@
|
||||
substitutions:
|
||||
network_enable_ipv6: "false"
|
||||
|
||||
socket:
|
||||
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
network:
|
||||
enable_ipv6: ${network_enable_ipv6}
|
||||
@@ -0,0 +1,4 @@
|
||||
substitutions:
|
||||
network_enable_ipv6: "true"
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
socket:
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,3 @@
|
||||
socket:
|
||||
|
||||
network:
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -1,9 +1,21 @@
|
||||
from esphome.components import socket
|
||||
from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_TARGET_PLATFORM,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
def _setup_platform(platform=PLATFORM_ESP8266) -> None:
|
||||
"""Set up CORE.data with a platform for testing."""
|
||||
CORE.data[KEY_CORE] = {KEY_TARGET_PLATFORM: platform}
|
||||
|
||||
|
||||
def test_require_wake_loop_threadsafe__first_call() -> None:
|
||||
"""Test that first call sets up define and consumes socket."""
|
||||
_setup_platform()
|
||||
CORE.config = {"wifi": True}
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
@@ -32,6 +44,7 @@ def test_require_wake_loop_threadsafe__idempotent() -> None:
|
||||
|
||||
def test_require_wake_loop_threadsafe__multiple_calls() -> None:
|
||||
"""Test that multiple calls only set up once."""
|
||||
_setup_platform()
|
||||
# Call three times
|
||||
CORE.config = {"openthread": True}
|
||||
socket.require_wake_loop_threadsafe()
|
||||
@@ -66,12 +79,38 @@ def test_require_wake_loop_threadsafe__no_networking_does_not_consume_socket() -
|
||||
CORE.config = {"logger": {}}
|
||||
|
||||
# Track initial socket consumer state
|
||||
initial_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS, {})
|
||||
initial_udp = CORE.data.get(socket.KEY_SOCKET_CONSUMERS_UDP, {})
|
||||
|
||||
# Call require_wake_loop_threadsafe
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
# Verify no socket was consumed
|
||||
consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS, {})
|
||||
assert "socket.wake_loop_threadsafe" not in consumers
|
||||
assert consumers == initial_consumers
|
||||
udp_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS_UDP, {})
|
||||
assert "socket.wake_loop_threadsafe" not in udp_consumers
|
||||
assert udp_consumers == initial_udp
|
||||
|
||||
|
||||
def test_require_wake_loop_threadsafe__esp32_no_udp_socket() -> None:
|
||||
"""Test that ESP32 uses task notifications instead of UDP socket."""
|
||||
_setup_platform(PLATFORM_ESP32)
|
||||
CORE.config = {"wifi": True}
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
# Verify the define was added
|
||||
assert CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] is True
|
||||
assert any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines)
|
||||
|
||||
# Verify no UDP socket was consumed (ESP32 uses FreeRTOS task notifications)
|
||||
udp_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS_UDP, {})
|
||||
assert "socket.wake_loop_threadsafe" not in udp_consumers
|
||||
|
||||
|
||||
def test_require_wake_loop_threadsafe__non_esp32_consumes_udp_socket() -> None:
|
||||
"""Test that non-ESP32 platforms consume a UDP socket for wake notifications."""
|
||||
_setup_platform(PLATFORM_ESP8266)
|
||||
CORE.config = {"wifi": True}
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
# Verify UDP socket was consumed
|
||||
udp_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS_UDP, {})
|
||||
assert udp_consumers.get("socket.wake_loop_threadsafe") == 1
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
substitutions:
|
||||
i2s_bclk_pin: GPIO27
|
||||
i2s_lrclk_pin: GPIO26
|
||||
i2s_mclk_pin: GPIO25
|
||||
i2s_dout_pin: GPIO23
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
|
||||
|
||||
<<: !include common-audio_dac.yaml
|
||||
@@ -1,5 +1,11 @@
|
||||
<<: !include common.yaml
|
||||
|
||||
wifi:
|
||||
ap:
|
||||
|
||||
psram:
|
||||
mode: quad
|
||||
|
||||
media_player:
|
||||
- platform: speaker
|
||||
id: speaker_media_player_id
|
||||
@@ -10,3 +16,4 @@ media_player:
|
||||
volume_max: 0.95
|
||||
volume_min: 0.0
|
||||
task_stack_in_psram: true
|
||||
codec_support_enabled: all
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO2
|
||||
sda_pin: GPIO3
|
||||
i2s_bclk_pin: GPIO4
|
||||
i2s_lrclk_pin: GPIO5
|
||||
i2s_mclk_pin: GPIO6
|
||||
i2s_dout_pin: GPIO7
|
||||
|
||||
<<: !include common-media_player.yaml
|
||||
@@ -1,10 +0,0 @@
|
||||
substitutions:
|
||||
i2s_bclk_pin: GPIO27
|
||||
i2s_lrclk_pin: GPIO26
|
||||
i2s_mclk_pin: GPIO25
|
||||
i2s_dout_pin: GPIO4
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,16 @@
|
||||
text_sensor:
|
||||
- platform: version
|
||||
name: "ESPHome Version"
|
||||
name: "ESPHome Version Full"
|
||||
|
||||
- platform: version
|
||||
name: "ESPHome Version No Timestamp"
|
||||
hide_timestamp: true
|
||||
|
||||
- platform: version
|
||||
name: "ESPHome Version No Hash"
|
||||
hide_hash: true
|
||||
|
||||
- platform: version
|
||||
name: "ESPHome Version Shortest"
|
||||
hide_timestamp: true
|
||||
hide_hash: true
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
wifi:
|
||||
band_mode: 5GHZ
|
||||
|
||||
packages:
|
||||
- !include common.yaml
|
||||
Reference in New Issue
Block a user