mirror of
https://github.com/esphome/esphome.git
synced 2026-09-30 08:20:21 +00:00
Merge remote-tracking branch 'upstream/dev' into enhancement/ethernet-spi-interface
# Conflicts: # esphome/components/ethernet/__init__.py # esphome/components/ethernet/ethernet_component.cpp # esphome/components/ethernet/ethernet_component.h
This commit is contained in:
@@ -7,10 +7,75 @@
|
||||
testing binaries that combine many components. By convention, this unique namespace is `esphome::component::testing`
|
||||
(where "component" is the component under test), for example: `esphome::uart::testing`.
|
||||
|
||||
### Platform components
|
||||
|
||||
For components that expose to a platform component, create a folder under your component test folder with the platform component name, e.g. `binary_sensor` and
|
||||
include the relevant `.cpp` and `.h` test files there.
|
||||
|
||||
### Override component code generation for testing
|
||||
|
||||
During C++ test builds, `to_code` is suppressed for every component by default — most components do not
|
||||
need to generate configuration code for a unit test binary.
|
||||
|
||||
#### Manifest overrides
|
||||
|
||||
If your component needs to customise code generation behavior for testing — for example to re-enable
|
||||
`to_code`, supply a lightweight stub, add a test-only dependency, or change any other manifest attribute —
|
||||
create an `__init__.py` in your component's test directory and define `override_manifest`:
|
||||
|
||||
**Top-level component** (`tests/components/<component>/__init__.py`):
|
||||
|
||||
```python
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
# Re-enable the component's own to_code (needed when the component must
|
||||
# emit C++ setup code that the test binary depends on at link time).
|
||||
manifest.enable_codegen()
|
||||
```
|
||||
|
||||
Or supply a lightweight stub instead of the real `to_code`:
|
||||
|
||||
```python
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
async def to_code_testing(config):
|
||||
# Only emit what the C++ tests actually need
|
||||
pass
|
||||
|
||||
manifest.to_code = to_code_testing
|
||||
manifest.dependencies = manifest.dependencies + ["some_test_only_dep"]
|
||||
```
|
||||
|
||||
**Platform component** (`tests/components/<component>/<domain>/__init__.py`,
|
||||
e.g. `tests/components/my_sensor/sensor/__init__.py`):
|
||||
|
||||
```python
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
manifest.enable_codegen()
|
||||
```
|
||||
|
||||
`override_manifest` receives a `ComponentManifestOverride` that wraps the real manifest.
|
||||
Attribute assignments store an override; reads fall back to the real manifest when no
|
||||
override is present.
|
||||
|
||||
Key methods:
|
||||
|
||||
| Method | Effect |
|
||||
|---|---|
|
||||
| `manifest.enable_codegen()` | Remove the `to_code` suppression, re-enabling code generation |
|
||||
| `manifest.restore()` | Clear **all** overrides, reverting every attribute to the original |
|
||||
|
||||
The function is called after `to_code` has already been set to `None`, so calling
|
||||
`enable_codegen()` is a deliberate opt-in.
|
||||
|
||||
## Running component unit tests
|
||||
|
||||
(from the repository root)
|
||||
|
||||
```bash
|
||||
./script/cpp_unit_test.py component1 component2 ...
|
||||
```
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
sensor:
|
||||
- id: my_sensor
|
||||
platform: adc
|
||||
pin: GPIO1
|
||||
name: ADC Test sensor
|
||||
update_interval: "1:01"
|
||||
attenuation: 2.5db
|
||||
unit_of_measurement: "°C"
|
||||
icon: "mdi:water-percent"
|
||||
accuracy_decimals: 5
|
||||
setup_priority: -100
|
||||
force_update: true
|
||||
@@ -0,0 +1,11 @@
|
||||
sensor:
|
||||
- id: my_sensor
|
||||
platform: adc
|
||||
pin: VCC
|
||||
name: ADC Test sensor
|
||||
update_interval: "1:01"
|
||||
unit_of_measurement: "°C"
|
||||
icon: "mdi:water-percent"
|
||||
accuracy_decimals: 5
|
||||
setup_priority: -100
|
||||
force_update: true
|
||||
@@ -0,0 +1,5 @@
|
||||
<<: !include common.yaml
|
||||
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
@@ -0,0 +1,9 @@
|
||||
audio_file:
|
||||
- id: test_audio
|
||||
file:
|
||||
type: local
|
||||
path: $component_dir/test.wav
|
||||
|
||||
media_source:
|
||||
- platform: audio_file
|
||||
id: audio_file_source
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
ble_nus:
|
||||
type: uart
|
||||
tx_buffer_size: 160
|
||||
rx_buffer_size: 160
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,9 @@
|
||||
sensor:
|
||||
- platform: bmp581_spi
|
||||
cs_pin: ${cs_pin}
|
||||
temperature:
|
||||
name: BMP581 Temperature
|
||||
iir_filter: 2x
|
||||
pressure:
|
||||
name: BMP581 Pressure
|
||||
oversampling: 128x
|
||||
@@ -0,0 +1,7 @@
|
||||
substitutions:
|
||||
cs_pin: GPIO5
|
||||
|
||||
packages:
|
||||
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,7 @@
|
||||
substitutions:
|
||||
cs_pin: GPIO15
|
||||
|
||||
packages:
|
||||
spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,7 @@
|
||||
substitutions:
|
||||
cs_pin: GPIO5
|
||||
|
||||
packages:
|
||||
spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !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"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
ch423:
|
||||
- id: ch423_hub
|
||||
i2c_id: i2c_bus
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
id: ch423_input
|
||||
name: CH423 Binary Sensor
|
||||
pin:
|
||||
ch423: ch423_hub
|
||||
number: 1
|
||||
mode: INPUT
|
||||
inverted: true
|
||||
- platform: gpio
|
||||
id: ch423_input_2
|
||||
name: CH423 Binary Sensor 2
|
||||
pin:
|
||||
ch423: ch423_hub
|
||||
number: 0
|
||||
mode: INPUT
|
||||
inverted: false
|
||||
output:
|
||||
- platform: gpio
|
||||
id: ch423_out_11
|
||||
pin:
|
||||
ch423: ch423_hub
|
||||
number: 11
|
||||
mode: OUTPUT_OPEN_DRAIN
|
||||
inverted: true
|
||||
- platform: gpio
|
||||
id: ch423_out_23
|
||||
pin:
|
||||
ch423: ch423_hub
|
||||
number: 23
|
||||
mode: OUTPUT_OPEN_DRAIN
|
||||
inverted: false
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
# core (esphome/core/config.py) must run its to_code during C++ test builds
|
||||
# because it bootstraps the fundamental application infrastructure that all
|
||||
# components depend on (component registration, event loop, etc.).
|
||||
manifest.enable_codegen()
|
||||
@@ -0,0 +1,19 @@
|
||||
sensor:
|
||||
- platform: dew_point
|
||||
name: Dew Point
|
||||
temperature: template_temperature
|
||||
humidity: template_humidity
|
||||
- platform: template
|
||||
id: template_humidity
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
return 0.6;
|
||||
}
|
||||
return 0.0;
|
||||
- platform: template
|
||||
id: template_temperature
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
return 42.0;
|
||||
}
|
||||
return 0.0;
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,11 @@
|
||||
dlms_meter:
|
||||
decryption_key: "36C66639E48A8CA4D6BC8B282A793BBB" # change this to your decryption key!
|
||||
|
||||
sensor:
|
||||
- platform: dlms_meter
|
||||
reactive_energy_plus:
|
||||
name: "Reactive energy taken from grid"
|
||||
reactive_energy_minus:
|
||||
name: "Reactive energy put into grid"
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,17 @@
|
||||
dlms_meter:
|
||||
decryption_key: "36C66639E48A8CA4D6BC8B282A793BBB" # change this to your decryption key!
|
||||
provider: netznoe # (optional) key - only set if using evn
|
||||
|
||||
sensor:
|
||||
- platform: dlms_meter
|
||||
# EVN
|
||||
power_factor:
|
||||
name: "Power Factor"
|
||||
|
||||
text_sensor:
|
||||
- platform: dlms_meter
|
||||
# EVN
|
||||
meternumber:
|
||||
name: "meterNumber"
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,27 @@
|
||||
sensor:
|
||||
- platform: dlms_meter
|
||||
voltage_l1:
|
||||
name: "Voltage L1"
|
||||
voltage_l2:
|
||||
name: "Voltage L2"
|
||||
voltage_l3:
|
||||
name: "Voltage L3"
|
||||
current_l1:
|
||||
name: "Current L1"
|
||||
current_l2:
|
||||
name: "Current L2"
|
||||
current_l3:
|
||||
name: "Current L3"
|
||||
active_power_plus:
|
||||
name: "Active power taken from grid"
|
||||
active_power_minus:
|
||||
name: "Active power put into grid"
|
||||
active_energy_plus:
|
||||
name: "Active energy taken from grid"
|
||||
active_energy_minus:
|
||||
name: "Active energy put into grid"
|
||||
|
||||
text_sensor:
|
||||
- platform: dlms_meter
|
||||
timestamp:
|
||||
name: "timestamp"
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart_2400: !include ../../test_build_components/common/uart_2400/esp32-ard.yaml
|
||||
|
||||
<<: !include common-generic.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart_2400: !include ../../test_build_components/common/uart_2400/esp32-idf.yaml
|
||||
|
||||
<<: !include common-netznoe.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart_2400: !include ../../test_build_components/common/uart_2400/esp8266-ard.yaml
|
||||
|
||||
<<: !include common-generic.yaml
|
||||
@@ -1,6 +1,16 @@
|
||||
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}
|
||||
request_interval: 20s
|
||||
receive_timeout: 100ms
|
||||
thermal_mbus_id: 3
|
||||
|
||||
@@ -25,6 +25,22 @@ display:
|
||||
lambda: |-
|
||||
it.circle(64, 64, 50, Color::BLACK);
|
||||
|
||||
- platform: epaper_spi
|
||||
spi_id: spi_bus
|
||||
model: waveshare-1.54in-G
|
||||
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
|
||||
|
||||
- platform: epaper_spi
|
||||
spi_id: spi_bus
|
||||
model: waveshare-2.13in-v3
|
||||
@@ -41,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
|
||||
@@ -48,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));
|
||||
|
||||
@@ -8,6 +8,18 @@ esp32:
|
||||
enable_lwip_bridge_interface: true
|
||||
disable_libc_locks_in_iram: false # Test explicit opt-out of RAM optimization
|
||||
use_full_certificate_bundle: false # Test CMN bundle (default)
|
||||
include_builtin_idf_components:
|
||||
- freertos # Test escape hatch (freertos is always included anyway)
|
||||
enable_full_printf: false
|
||||
disable_debug_stubs: true
|
||||
disable_ocd_aware: true
|
||||
disable_usb_serial_jtag_secondary: true
|
||||
disable_dev_null_vfs: true
|
||||
disable_mbedtls_peer_cert: true
|
||||
disable_mbedtls_pkcs7: true
|
||||
disable_regi2c_in_iram: true
|
||||
disable_fatfs: true
|
||||
sram1_as_iram: true
|
||||
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
esp32:
|
||||
variant: esp32p4
|
||||
engineering_sample: true
|
||||
flash_size: 32MB
|
||||
cpu_frequency: 400MHz
|
||||
framework:
|
||||
type: esp-idf
|
||||
components:
|
||||
- espressif/mdns^1.8.2
|
||||
- name: espressif/esp_hosted
|
||||
ref: 2.7.0
|
||||
- name: espressif/button
|
||||
ref: 4.1.5
|
||||
advanced:
|
||||
enable_idf_experimental_features: yes
|
||||
disable_debug_stubs: true
|
||||
disable_ocd_aware: true
|
||||
disable_usb_serial_jtag_secondary: true
|
||||
disable_dev_null_vfs: true
|
||||
disable_mbedtls_peer_cert: true
|
||||
disable_mbedtls_pkcs7: true
|
||||
disable_regi2c_in_iram: true
|
||||
disable_fatfs: true
|
||||
|
||||
ota:
|
||||
platform: esphome
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
esp32:
|
||||
variant: esp32s3
|
||||
partitions:
|
||||
- name: my_data
|
||||
type: data
|
||||
subtype: spiffs
|
||||
size: 0x1000
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
execute_from_psram: true
|
||||
disable_libc_locks_in_iram: true # Test default RAM optimization enabled
|
||||
disable_debug_stubs: true
|
||||
disable_ocd_aware: true
|
||||
disable_usb_serial_jtag_secondary: true
|
||||
disable_dev_null_vfs: true
|
||||
disable_mbedtls_peer_cert: true
|
||||
disable_mbedtls_pkcs7: true
|
||||
disable_regi2c_in_iram: true
|
||||
disable_fatfs: true
|
||||
|
||||
psram:
|
||||
mode: octal
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
esp32_ble:
|
||||
io_capability: keyboard_display
|
||||
# Explicitly not setting some parameters to test ifdef selection
|
||||
# max_key_size: 16
|
||||
# min_key_size: 7
|
||||
auth_req_mode: sc_mitm_bond
|
||||
@@ -0,0 +1,5 @@
|
||||
esp32_ble:
|
||||
io_capability: keyboard_display
|
||||
max_key_size: 16
|
||||
min_key_size: 7
|
||||
auth_req_mode: sc_mitm_bond
|
||||
@@ -0,0 +1,5 @@
|
||||
<<: !include common.yaml
|
||||
|
||||
esp32_ble:
|
||||
io_capability: keyboard_only
|
||||
disable_bt_logs: false
|
||||
@@ -0,0 +1,8 @@
|
||||
packages:
|
||||
ble: !include ../../test_build_components/common/ble/esp32-p4-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
|
||||
esp32_ble:
|
||||
io_capability: keyboard_only
|
||||
disable_bt_logs: false
|
||||
@@ -0,0 +1,7 @@
|
||||
packages:
|
||||
ble: !include ../../test_build_components/common/ble/esp32-p4-idf.yaml
|
||||
|
||||
# tx_power is not supported on ESP-Hosted platforms
|
||||
esp32_ble_beacon:
|
||||
type: iBeacon
|
||||
uuid: 'c29ce823-e67a-4e71-bff2-abaa32e77a98'
|
||||
@@ -0,0 +1,6 @@
|
||||
packages:
|
||||
ble: !include ../../test_build_components/common/ble/esp32-p4-idf.yaml
|
||||
|
||||
ble_client:
|
||||
- mac_address: 01:02:03:04:05:06
|
||||
id: blec
|
||||
@@ -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
|
||||
@@ -65,3 +69,11 @@ esp32_ble_server:
|
||||
- ble_server.descriptor.set_value:
|
||||
id: test_change_descriptor
|
||||
value: !lambda return bytebuffer::ByteBuffer::wrap({0x03, 0x04, 0x05}).get_data();
|
||||
- ble_server.characteristic.set_value:
|
||||
id: test_change_characteristic
|
||||
value:
|
||||
data: [0xfc, 0xef, 0xfe, 0x86]
|
||||
- ble_server.descriptor.set_value:
|
||||
id: test_change_descriptor
|
||||
value:
|
||||
data: [0x01, 0x02, 0x03]
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
ble: !include ../../test_build_components/common/ble/esp32-p4-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,7 @@
|
||||
packages:
|
||||
ble: !include ../../test_build_components/common/ble/esp32-p4-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
|
||||
esp32_ble_tracker:
|
||||
max_connections: 9
|
||||
@@ -0,0 +1,16 @@
|
||||
esp32_hosted:
|
||||
variant: ESP32C6
|
||||
slot: 1
|
||||
active_high: true
|
||||
reset_pin: GPIO15
|
||||
cmd_pin: GPIO13
|
||||
clk_pin: GPIO12
|
||||
d0_pin: GPIO11
|
||||
d1_pin: GPIO10
|
||||
d2_pin: GPIO9
|
||||
d3_pin: GPIO8
|
||||
sdio_frequency: 8MHz
|
||||
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
@@ -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
|
||||
@@ -1,3 +1,6 @@
|
||||
esp8266:
|
||||
enable_full_printf: false
|
||||
|
||||
logger:
|
||||
level: VERBOSE
|
||||
|
||||
@@ -11,3 +14,6 @@ esphome:
|
||||
assert(x == 95);
|
||||
x = clamp_at_most(x, 40);
|
||||
assert(x == 40);
|
||||
- lambda: |-
|
||||
float value = 0.0f;
|
||||
sscanf("3.14", "%f", &value);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
ethernet:
|
||||
type: ENC28J60
|
||||
clk_pin: 18
|
||||
mosi_pin: 19
|
||||
miso_pin: 16
|
||||
cs_pin: 17
|
||||
interrupt_pin: 21
|
||||
reset_pin: 20
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
@@ -0,0 +1,19 @@
|
||||
ethernet:
|
||||
type: ENC28J60
|
||||
clk_pin: 19
|
||||
mosi_pin: 21
|
||||
miso_pin: 23
|
||||
cs_pin: 18
|
||||
interrupt_pin: 36
|
||||
reset_pin: 22
|
||||
clock_speed: 10Mhz
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -12,3 +12,7 @@ ethernet:
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
ethernet:
|
||||
type: OPENETH
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
ethernet:
|
||||
type: W5100
|
||||
clk_pin: 18
|
||||
mosi_pin: 19
|
||||
miso_pin: 16
|
||||
cs_pin: 17
|
||||
interrupt_pin: 21
|
||||
reset_pin: 20
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
@@ -0,0 +1,18 @@
|
||||
ethernet:
|
||||
type: W5500
|
||||
clk_pin: 18
|
||||
mosi_pin: 19
|
||||
miso_pin: 16
|
||||
cs_pin: 17
|
||||
interrupt_pin: 21
|
||||
reset_pin: 20
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
@@ -2,10 +2,10 @@ ethernet:
|
||||
type: W5500
|
||||
clk_pin: 19
|
||||
mosi_pin: 21
|
||||
miso_pin: 23
|
||||
miso_pin: 17
|
||||
cs_pin: 18
|
||||
interrupt_pin: 36
|
||||
reset_pin: 22
|
||||
reset_pin: 12
|
||||
clock_speed: 10Mhz
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
@@ -13,3 +13,7 @@ ethernet:
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common-enc28j60.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common-enc28j60-rp2040.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common-w5100-rp2040.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common-w5500-rp2040.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common-ip101.yaml
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common-w5500.yaml
|
||||
@@ -1,6 +1,8 @@
|
||||
external_components:
|
||||
- source: github://esphome/esphome@dev
|
||||
- id: my_ext
|
||||
source: github://esphome/esphome@dev
|
||||
refresh: 1d
|
||||
components: [bh1750]
|
||||
- source: ../../../esphome/components
|
||||
- id: my_local
|
||||
source: ../../../esphome/components
|
||||
components: [sntp]
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
# WARNING: Using !extend or !remove prevents automatic component grouping in CI, making builds slower.
|
||||
<<: !include common.yaml
|
||||
|
||||
external_components:
|
||||
- id: !remove my_local
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -1 +1,2 @@
|
||||
*.pcf -text
|
||||
*.pcf -text
|
||||
*.ttf -text
|
||||
|
||||
@@ -27,3 +27,14 @@ globals:
|
||||
type: bool
|
||||
restore_value: false
|
||||
initial_value: "false"
|
||||
# Test restore_value with string "false" - should be converted to bool false
|
||||
- id: glob_no_restore_string_false
|
||||
type: int
|
||||
restore_value: "false"
|
||||
initial_value: "42"
|
||||
# Test restore_value with string "true" - should be converted to bool true
|
||||
- id: glob_restore_string_true
|
||||
type: int
|
||||
restore_value: "true"
|
||||
initial_value: "99"
|
||||
update_interval: 5s
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
gps:
|
||||
latitude:
|
||||
name: "Latitude"
|
||||
id: gps_lat
|
||||
on_value:
|
||||
then:
|
||||
- logger.log:
|
||||
format: "%.6f, %.6f"
|
||||
args: [id(gps_lat).state, id(gps_long).state]
|
||||
longitude:
|
||||
name: "Longitude"
|
||||
id: gps_long
|
||||
altitude:
|
||||
name: "Altitude"
|
||||
speed:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
sensor:
|
||||
- platform: hdc2080
|
||||
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
|
||||
@@ -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:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
# host must run its to_code during C++ test builds because it sets up the
|
||||
# host platform target, which is the execution environment for all unit tests.
|
||||
manifest.enable_codegen()
|
||||
@@ -1,4 +1,6 @@
|
||||
substitutions:
|
||||
verify_ssl: "false"
|
||||
|
||||
<<: !include common.yaml
|
||||
|
||||
http_request:
|
||||
verify_ssl: false
|
||||
tls_buffer_size_rx: 16384
|
||||
tls_buffer_size_tx: 512
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
sensor:
|
||||
- platform: htu21d
|
||||
id: htu21d_sensor
|
||||
i2c_id: i2c_bus
|
||||
model: htu21d
|
||||
temperature:
|
||||
@@ -9,3 +10,14 @@ sensor:
|
||||
heater:
|
||||
name: Heater
|
||||
update_interval: 15s
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: "Test HTU21D Actions"
|
||||
on_press:
|
||||
- htu21d.set_heater:
|
||||
id: htu21d_sensor
|
||||
status: true
|
||||
- htu21d.set_heater_level:
|
||||
id: htu21d_sensor
|
||||
level: 5
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
esphome:
|
||||
on_boot:
|
||||
then:
|
||||
- sensor.integration.reset:
|
||||
id: integration_sensor
|
||||
- sensor.integration.set_value:
|
||||
id: integration_sensor
|
||||
value: 100.0
|
||||
|
||||
sensor:
|
||||
- platform: adc
|
||||
id: my_sensor
|
||||
pin: ${pin}
|
||||
attenuation: 12db
|
||||
- platform: integration
|
||||
id: integration_sensor
|
||||
sensor: my_sensor
|
||||
name: Integration Sensor
|
||||
time_unit: s
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -8,6 +8,7 @@ infrared:
|
||||
- platform: ir_rf_proxy
|
||||
id: ir_rx
|
||||
name: "IR Receiver"
|
||||
receiver_frequency: 38kHz
|
||||
remote_receiver_id: ir_receiver
|
||||
|
||||
# RF 900MHz receiver
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -18,14 +18,23 @@ key_collector:
|
||||
- logger.log:
|
||||
format: "input progress: '%s', started by '%c'"
|
||||
args: ['x.c_str()', "(start == 0 ? '~' : start)"]
|
||||
- logger.log:
|
||||
format: "second listener - progress: '%s'"
|
||||
args: ['x.c_str()']
|
||||
on_result:
|
||||
- logger.log:
|
||||
format: "input result: '%s', started by '%c', ended by '%c'"
|
||||
args: ['x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)"]
|
||||
- logger.log:
|
||||
format: "second listener - result: '%s'"
|
||||
args: ['x.c_str()']
|
||||
on_timeout:
|
||||
- logger.log:
|
||||
format: "input timeout: '%s', started by '%c'"
|
||||
args: ['x.c_str()', "(start == 0 ? '~' : start)"]
|
||||
- logger.log:
|
||||
format: "second listener - timeout: '%s'"
|
||||
args: ['x.c_str()']
|
||||
enable_on_boot: false
|
||||
|
||||
button:
|
||||
@@ -34,3 +43,8 @@ button:
|
||||
on_press:
|
||||
- key_collector.enable:
|
||||
- key_collector.disable:
|
||||
|
||||
text_sensor:
|
||||
- platform: key_collector
|
||||
id: collected_keys
|
||||
source_id: reader
|
||||
|
||||
@@ -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(uart::UARTFlushResult, 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
|
||||
@@ -1,5 +1,8 @@
|
||||
ld2450:
|
||||
- id: ld2450_radar
|
||||
on_data:
|
||||
then:
|
||||
- logger.log: "LD2450 Radar Data Received"
|
||||
|
||||
button:
|
||||
- platform: ld2450
|
||||
|
||||
@@ -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
|
||||
@@ -60,6 +60,12 @@ esphome:
|
||||
}
|
||||
}
|
||||
|
||||
# Test set_effect with const char* doesn't cause ambiguous overload (issue #14728)
|
||||
- lambda: |-
|
||||
auto call = id(test_monochromatic_light).turn_on();
|
||||
call.set_effect("None");
|
||||
call.perform();
|
||||
|
||||
- light.toggle: test_binary_light
|
||||
- light.turn_off: test_rgb_light
|
||||
- light.turn_on:
|
||||
@@ -71,6 +77,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%
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
# logger must run its to_code during C++ test builds because it configures
|
||||
# the logging subsystem used by ESP_LOG* macros throughout component code.
|
||||
manifest.enable_codegen()
|
||||
@@ -5,3 +5,6 @@ esphome:
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
wait_for_cdc: true
|
||||
early_message: true
|
||||
task_log_buffer_size: 0
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user