Merge remote-tracking branch 'origin/dev' into integration

This commit is contained in:
J. Nick Koston
2026-07-18 10:21:31 -10:00
466 changed files with 14801 additions and 3978 deletions
+11 -8
View File
@@ -1,23 +1,26 @@
animation:
- id: rgb565_animation
image:
- platform: animation
id: rgb565_animation
file: $component_dir/anim.gif
type: RGB565
transparency: opaque
resize: 50x50
- id: rgb_animation
- platform: animation
id: rgb_animation
file: $component_dir/anim.apng
type: RGB
transparency: chroma_key
resize: 50x50
- id: grayscale_animation
- platform: animation
id: grayscale_animation
file: $component_dir/anim.apng
type: grayscale
display:
lambda: |-
id(rgb565_animation).next_frame();
id(rgb_animation1).next_frame();
id(grayscale_animation2).next_frame();
id(rgb_animation).next_frame();
id(grayscale_animation).next_frame();
it.image(0, 0, rgb565_animation);
it.image(120, 0, rgb_animation1);
it.image(240, 0, grayscale_animation2);
it.image(120, 0, rgb_animation);
it.image(240, 0, grayscale_animation);
@@ -0,0 +1,16 @@
# Legacy top-level `animation:` form (deprecated; migrates to
# `platform: animation`). Config-only test exercising the deprecation path.
display:
- platform: sdl
id: animation_display
auto_clear_enabled: false
dimensions:
width: 480
height: 480
animation:
- id: legacy_animation
file: $component_dir/anim.gif
type: RGB565
transparency: opaque
resize: 50x50
@@ -1,5 +1,11 @@
<<: !include common-base.yaml
packages:
common: !include common-base.yaml
wifi:
ssid: MySSID
password: password1
# Encryption enabled without a key: compiles both frame helpers so the key
# can be provisioned at runtime (zero-PSK noise or deprecated plaintext)
api:
encryption:
+16
View File
@@ -0,0 +1,16 @@
# Declares the component graph the C++ unit test build needs so that the aqi
# component's sources (which include sensor.h) compile. to_code is suppressed by
# the test harness; this only pulls the sensor + aqi source/include paths in.
# Loaded with plain yaml.safe_load, so avoid lambdas / ESPHome-tagged values here.
sensor:
- platform: template
id: pm25_sensor
name: "PM2.5"
- platform: template
id: pm10_sensor
name: "PM10"
- platform: aqi
name: "AQI"
pm_2_5: pm25_sensor
pm_10_0: pm10_sensor
calculation_type: AQI
+7
View File
@@ -20,3 +20,10 @@ sensor:
pm_2_5: pm25_sensor
pm_10_0: pm10_sensor
calculation_type: CAQI
- platform: aqi
name: "Air Quality Index (AQI, extended)"
pm_2_5: pm25_sensor
pm_10_0: pm10_sensor
calculation_type: AQI
extended_range: true
@@ -0,0 +1,85 @@
#include <gtest/gtest.h>
#include "esphome/components/aqi/aqi_calculator.h"
#include "esphome/components/aqi/caqi_calculator.h"
namespace esphome::aqi::testing {
// US AQI (EPA 2024): PM2.5 225.5-500.4 -> 301-500, PM10 425-604 -> 301-500.
TEST(USAQI, LowRangeUnaffectedByExtendedFlag) {
AQICalculator calc;
// PM2.5 25 drives over PM10 50; well below the top band, so the flag changes nothing.
EXPECT_EQ(calc.get_aqi(25.0f, 50.0f, false), 81);
EXPECT_EQ(calc.get_aqi(25.0f, 50.0f, true), 81);
}
TEST(USAQI, HazardousInterpolatesNotPinnedAt301) {
AQICalculator calc;
// Regression guard: the old FLT_MAX top bucket collapsed every hazardous reading to 301.
EXPECT_EQ(calc.get_aqi(225.5f, 0.0f, false), 301); // band start
EXPECT_EQ(calc.get_aqi(250.0f, 0.0f, false), 319); // interpolated, not 301
EXPECT_EQ(calc.get_aqi(500.4f, 0.0f, false), 500); // band top
}
TEST(USAQI, DefaultClampsAtStandardMaximum) {
AQICalculator calc;
EXPECT_EQ(calc.get_aqi(600.0f, 0.0f, false), 500);
EXPECT_EQ(calc.get_aqi(1000.0f, 0.0f, false), 500);
EXPECT_EQ(calc.get_aqi(0.0f, 604.0f, false), 500); // PM10 top breakpoint
}
TEST(USAQI, ExtendedRangeExtrapolatesBeyond500) {
AQICalculator calc;
EXPECT_EQ(calc.get_aqi(600.0f, 0.0f, true), 572);
EXPECT_EQ(calc.get_aqi(1000.0f, 0.0f, true), 862);
EXPECT_EQ(calc.get_aqi(0.0f, 700.0f, true), 607); // PM10 extrapolated past 500
}
TEST(USAQI, ExtendedRangeSaturatesUint16NoWraparound) {
AQICalculator calc;
// An absurd concentration would overflow uint16_t; it must saturate, not wrap to a small value.
EXPECT_EQ(calc.get_aqi(100000.0f, 0.0f, true), 65535);
}
TEST(USAQI, WorseOfTwoPollutantsWins) {
AQICalculator calc;
// PM10 604 -> 500 dominates PM2.5 25 -> 81.
EXPECT_EQ(calc.get_aqi(25.0f, 604.0f, false), 500);
}
// CAQI (CITEAIR): no maximum by spec -- the top ">100" class is open, so it is always unbounded
// and the extended_range flag does not apply.
TEST(CAQI, LowRange) {
CAQICalculator calc;
EXPECT_EQ(calc.get_aqi(25.0f, 50.0f, false), 50);
}
TEST(CAQI, ContinuousAt100NoPinAt101) {
CAQICalculator calc;
// Old code pinned everything above the top breakpoint to 101; now it reaches exactly 100.
EXPECT_EQ(calc.get_aqi(110.1f, 0.0f, false), 100);
}
TEST(CAQI, UnboundedAboveTopBand) {
CAQICalculator calc;
EXPECT_EQ(calc.get_aqi(200.0f, 0.0f, false), 139);
EXPECT_EQ(calc.get_aqi(2000.0f, 0.0f, false), 925);
}
TEST(CAQI, ExtendedRangeFlagIsIgnored) {
CAQICalculator calc;
// CAQI is always unbounded, so the flag must make no difference either way.
EXPECT_EQ(calc.get_aqi(200.0f, 0.0f, true), calc.get_aqi(200.0f, 0.0f, false));
EXPECT_EQ(calc.get_aqi(2000.0f, 0.0f, true), calc.get_aqi(2000.0f, 0.0f, false));
}
TEST(CAQI, SaturatesUint16NoWraparound) {
CAQICalculator calc;
// CAQI is unbounded, so an extreme reading can extrapolate past uint16_t; it must saturate,
// not wrap around to a small (falsely "good") value.
EXPECT_EQ(calc.get_aqi(200000.0f, 0.0f, false), 65535);
}
} // namespace esphome::aqi::testing
+9
View File
@@ -17,6 +17,15 @@ cc1101:
sync0: 0x91
sync1: 0xD3
num_preamble: 2
foc_bs_cs_gate: true
foc_pre_k: "2K"
foc_post_k: "K/2"
foc_limit: "BW/4"
bs_pre_ki: "3KI"
bs_pre_kp: "4KP"
bs_post_ki: "KI/2"
bs_post_kp: "KP"
bs_limit: "12.5%"
on_packet:
then:
- lambda: |-
@@ -6,9 +6,15 @@ deep_sleep:
sleep_duration: 50s
wakeup_pin: ${wakeup_pin}
wakeup_pin_mode: INVERT_WAKEUP
on_wake:
- logger.log:
format: "Woke up, cause %d"
args: ["static_cast<int>(cause)"]
esp32_ext1_wakeup:
pins:
- number: GPIO2
- pin: GPIO2
on_wake:
- logger.log: Woken by ext1 pin GPIO2
- number: GPIO13
mode: ANY_HIGH
touch_wakeup: true
@@ -5,8 +5,15 @@ deep_sleep:
sleep_duration: 50s
wakeup_pin: ${wakeup_pin}
wakeup_pin_mode: INVERT_WAKEUP
on_wake:
- logger.log:
format: "Woke up, cause %d"
args: ["static_cast<int>(cause)"]
esp32_ext1_wakeup:
pins:
- number: GPIO2
- pin:
number: GPIO2
on_wake:
- logger.log: Woken by ext1 pin GPIO2
- number: GPIO5
mode: ANY_HIGH
@@ -5,3 +5,7 @@ deep_sleep:
sleep_duration: 50s
wakeup_pin: ${wakeup_pin}
wakeup_pin_mode: INVERT_WAKEUP
on_wake:
- logger.log:
format: "Woke up, cause %d"
args: ["static_cast<int>(cause)"]
@@ -1,6 +1,10 @@
deep_sleep:
run_duration: 30s
sleep_duration: 12h
on_wake:
- logger.log:
format: "Woke up, cause %d"
args: ["static_cast<int>(cause)"]
wakeup_pin:
- pin:
number: P6
@@ -1,5 +1,9 @@
deep_sleep:
run_duration: 10s
sleep_duration: 50s
on_wake:
- logger.log:
format: "Woke up, cause %d"
args: ["static_cast<int>(cause)"]
<<: !include common.yaml
+115
View File
@@ -0,0 +1,115 @@
# Combined DS248x test covering all chip variants and options:
# - DS2482-100: active pullup, multiple sensors + index access
# - DS2482-101: sleep pin, bus_sleep / hub_sleep
# - DS2482-800: all 8 channels
# - DS2484: adjustable 1-Wire timing + RWPU pullup resistor selection
ds248x:
- id: ds2482_100
address: 0x18
type: ds2482-100
active_pullup: true
- id: ds2482_101
address: 0x19
type: ds2482-101
active_pullup: true
sleep_pin:
number: GPIO12
inverted: false
bus_sleep: true
hub_sleep: true
- id: ds2482_800
address: 0x1a
type: ds2482-800
active_pullup: true
- id: ds2484_hub
address: 0x1b
type: ds2484
active_pullup: true
# DS2484-specific 1-Wire timing parameters (optional fine-tuning)
reset_low_time: 8 # tRSTL: Reset low time
master_sample_time: 8 # tMSP: Master sample point
write_0_low_time: 8 # tW0L: Write-0 low time
recovery_time: 8 # tREC0: Recovery time
active_pullup_resistance: 1000ohm # RWPU: weak pullup resistor selection
one_wire:
- platform: ds248x
ds248x_id: ds2482_100
channel: 0
id: ow_100
- platform: ds248x
ds248x_id: ds2482_101
channel: 0
id: ow_101
- platform: ds248x
ds248x_id: ds2482_800
channel: 0
id: ow_800_0
- platform: ds248x
ds248x_id: ds2482_800
channel: 1
id: ow_800_1
- platform: ds248x
ds248x_id: ds2482_800
channel: 2
id: ow_800_2
- platform: ds248x
ds248x_id: ds2482_800
channel: 3
id: ow_800_3
- platform: ds248x
ds248x_id: ds2482_800
channel: 4
id: ow_800_4
- platform: ds248x
ds248x_id: ds2482_800
channel: 5
id: ow_800_5
- platform: ds248x
ds248x_id: ds2482_800
channel: 6
id: ow_800_6
- platform: ds248x
ds248x_id: ds2482_800
channel: 7
id: ow_800_7
- platform: ds248x
ds248x_id: ds2484_hub
channel: 0
id: ow_2484
sensor:
# DS2482-100: explicit address + index-based access on the same bus
- platform: dallas_temp
one_wire_id: ow_100
address: 0x1c0000031edd2a28
name: Temp 100 by address
resolution: 12
- platform: dallas_temp
one_wire_id: ow_100
index: 0
name: Temp 100 by index
# DS2482-101 (sleep variant)
- platform: dallas_temp
one_wire_id: ow_101
address: 0x578295491f64ff28
name: Temp 101
# DS2482-800: sensors on a few of the eight channels
- platform: dallas_temp
one_wire_id: ow_800_0
address: 0x1c0000031edd2a28
name: Temp 800 CH0
- platform: dallas_temp
one_wire_id: ow_800_3
index: 0
name: Temp 800 CH3 by index
- platform: dallas_temp
one_wire_id: ow_800_7
address: 0x2800000123456789
name: Temp 800 CH7
# DS2484 (adjustable timing)
- platform: dallas_temp
one_wire_id: ow_2484
address: 0x1c0000031edd2a28
name: Temp 2484
resolution: 12
@@ -0,0 +1,4 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
<<: !include common.yaml
@@ -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,9 @@
esp32:
variant: esp32s3
framework:
type: esp-idf
advanced:
nvs_encryption:
key_id: 0
<<: !include common.yaml
@@ -0,0 +1,11 @@
# Secure Boot V2 schemes carry the public key inside each image's signature
# block, so verifying externally-signed binaries needs no key in the config:
# a bare block enables verification with the default rsa3072 scheme.
esp32:
variant: esp32s3
framework:
type: esp-idf
advanced:
signed_ota_verification:
<<: !include common.yaml
+17
View File
@@ -0,0 +1,17 @@
image:
- platform: file
id: file_binary_image
file: ../../pnglogo.png
type: BINARY
dither: FloydSteinberg
- platform: file
id: file_rgb565_image
file: ../../pnglogo.png
type: RGB565
transparency: alpha_channel
resize: 50x50
- platform: file
id: file_mdi_image
file: mdi:alert-circle-outline
type: BINARY
resize: 24x24
+14
View File
@@ -0,0 +1,14 @@
packages:
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
display:
- platform: ili9xxx
id: file_main_lcd
spi_id: spi_bus
model: ili9342
cs_pin: 15
dc_pin: 13
reset_pin: 21
invert_colors: true
<<: !include common.yaml
+9
View File
@@ -0,0 +1,9 @@
display:
- platform: sdl
id: file_display
auto_clear_enabled: false
dimensions:
width: 480
height: 480
<<: !include common.yaml
@@ -0,0 +1,22 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
xl9535:
id: expander
touchscreen:
# Firmware downloaded from the model's default release URL and cached.
- platform: gsl3670
model: seeed-reterminal-d1001
display: test_display_screen
interrupt_pin: 18
# Explicit firmware URL + SHA-256 override.
- platform: gsl3670
model: seeed-reterminal-d1001
display: test_display_screen
reset_pin: 10
interrupt_pin: 11
firmware:
url: https://github.com/esphome-libs/gsl3670-firmware/releases/download/v1.0.0/seeed-d1001-fw.bin
sha256: 2e50501ad83656fb6fa3d92591f9f31add4d442c8e8a79f29f5c4d335bd127a4
+1 -12
View File
@@ -1,19 +1,8 @@
display:
- platform: ssd1306_i2c
i2c_id: i2c_bus
id: gt911_ssd1306_i2c_display
model: SSD1306_128X64
reset_pin: ${display_reset_pin}
pages:
- id: gt911_page1
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
touchscreen:
- platform: gt911
i2c_id: i2c_bus
id: gt911_touchscreen
display: gt911_ssd1306_i2c_display
display: test_display_screen
interrupt_pin: ${interrupt_pin}
reset_pin: ${reset_pin}
+2 -3
View File
@@ -1,9 +1,8 @@
substitutions:
display_reset_pin: "10"
interrupt_pin: "20"
reset_pin: "21"
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
<<: !include common.yaml
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
gt911: !include common.yaml
+2 -3
View File
@@ -1,9 +1,8 @@
substitutions:
display_reset_pin: "10"
interrupt_pin: "12"
reset_pin: "13"
packages:
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
<<: !include common.yaml
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
gt911: !include common.yaml
+2 -3
View File
@@ -1,9 +1,8 @@
substitutions:
display_reset_pin: "10"
interrupt_pin: "20"
reset_pin: "21"
packages:
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
<<: !include common.yaml
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
gt911: !include common.yaml
+38 -19
View File
@@ -1,85 +1,104 @@
image:
- id: binary_image
- platform: file
id: binary_image
file: ../../pnglogo.png
type: BINARY
dither: FloydSteinberg
- id: transparent_transparent_image
- platform: file
id: transparent_transparent_image
file: ../../pnglogo.png
type: BINARY
transparency: chroma_key
- id: rgba_image
- platform: file
id: rgba_image
file: ../../pnglogo.png
type: RGB
transparency: alpha_channel
resize: 50x50
- id: rgb24_image
- platform: file
id: rgb24_image
file: ../../pnglogo.png
type: RGB
transparency: chroma_key
- id: rgb_image
- platform: file
id: rgb_image
file: ../../pnglogo.png
type: RGB
transparency: opaque
- id: rgb565_image
- platform: file
id: rgb565_image
file: ../../pnglogo.png
type: RGB565
transparency: opaque
- id: rgb565_ck_image
- platform: file
id: rgb565_ck_image
file: ../../pnglogo.png
type: RGB565
transparency: chroma_key
- id: rgb565_alpha_image
- platform: file
id: rgb565_alpha_image
file: ../../pnglogo.png
type: RGB565
transparency: alpha_channel
- id: grayscale_alpha_image
- platform: file
id: grayscale_alpha_image
file: ../../pnglogo.png
type: grayscale
transparency: alpha_channel
resize: 50x50
- id: grayscale_ck_image
- platform: file
id: grayscale_ck_image
file: ../../pnglogo.png
type: grayscale
transparency: chroma_key
- id: grayscale_image
- platform: file
id: grayscale_image
file: ../../pnglogo.png
type: grayscale
transparency: opaque
- id: web_svg_image
- platform: file
id: web_svg_image
file: https://media.esphome.io/logo/logo.svg
resize: 256x48
type: BINARY
transparency: chroma_key
- id: web_tiff_image
- platform: file
id: web_tiff_image
file: https://media.esphome.io/tests/images/SIPI_Jelly_Beans_4.1.07.tiff
type: RGB
resize: 48x48
- id: web_redirect_image
- platform: file
id: web_redirect_image
file: https://media.esphome.io/logo/logo.png
type: RGB
resize: 48x48
- id: mdi_alert
- platform: file
id: mdi_alert
type: BINARY
file: mdi:alert-circle-outline
resize: 50x50
- id: another_alert_icon
- platform: file
id: another_alert_icon
file: mdi:alert-outline
type: BINARY
- file: mdil:arrange-bring-to-front
- platform: file
file: mdil:arrange-bring-to-front
id: mdil_id
resize: 50x50
type: binary
transparency: chroma_key
- file: mdi:beer
- platform: file
file: mdi:beer
id: mdi_id
resize: 50x50
type: binary
transparency: chroma_key
- file: memory:alert-octagon
- platform: file
file: memory:alert-octagon
id: memory_id
resize: 50x50
type: binary
+3 -4
View File
@@ -12,12 +12,11 @@ display:
invert_colors: true
image:
defaults:
- platform: file
id: test_image
file: ../../pnglogo.png
type: rgb565
transparency: opaque
byte_order: little_endian
resize: 50x50
dither: FloydSteinberg
images:
- id: test_image
file: ../../pnglogo.png
+57 -40
View File
@@ -7,43 +7,60 @@ display:
height: 480
image:
binary:
- id: binary_image
file: ../../pnglogo.png
dither: FloydSteinberg
- id: transparent_transparent_image
file: ../../pnglogo.png
transparency: chroma_key
rgb:
alpha_channel:
- id: rgba_image
file: ../../pnglogo.png
resize: 50x50
chroma_key:
- id: rgb24_image
file: ../../pnglogo.png
type: RGB
opaque:
- id: rgb_image
file: ../../pnglogo.png
rgb565:
- id: rgb565_image
file: ../../pnglogo.png
transparency: opaque
- id: rgb565_ck_image
file: ../../pnglogo.png
transparency: chroma_key
- id: rgb565_alpha_image
file: ../../pnglogo.png
transparency: alpha_channel
grayscale:
- id: grayscale_alpha_image
file: ../../pnglogo.png
transparency: alpha_channel
resize: 50x50
- id: grayscale_ck_image
file: ../../pnglogo.png
transparency: chroma_key
- id: grayscale_image
file: ../../pnglogo.png
transparency: opaque
- platform: file
id: binary_image
file: ../../pnglogo.png
type: binary
dither: FloydSteinberg
- platform: file
id: transparent_transparent_image
file: ../../pnglogo.png
type: binary
transparency: chroma_key
- platform: file
id: rgba_image
file: ../../pnglogo.png
type: rgb
transparency: alpha_channel
resize: 50x50
- platform: file
id: rgb24_image
file: ../../pnglogo.png
type: RGB
transparency: chroma_key
- platform: file
id: rgb_image
file: ../../pnglogo.png
type: rgb
transparency: opaque
- platform: file
id: rgb565_image
file: ../../pnglogo.png
type: rgb565
transparency: opaque
- platform: file
id: rgb565_ck_image
file: ../../pnglogo.png
type: rgb565
transparency: chroma_key
- platform: file
id: rgb565_alpha_image
file: ../../pnglogo.png
type: rgb565
transparency: alpha_channel
- platform: file
id: grayscale_alpha_image
file: ../../pnglogo.png
type: grayscale
transparency: alpha_channel
resize: 50x50
- platform: file
id: grayscale_ck_image
file: ../../pnglogo.png
type: grayscale
transparency: chroma_key
- platform: file
id: grayscale_image
file: ../../pnglogo.png
type: grayscale
transparency: opaque
@@ -0,0 +1,25 @@
# Legacy top-level `image:` defaults/images form (deprecated; migrates to
# `platform: file`). Config-only test exercising the deprecation/migration path,
# including the per-type byte_order drop when an entry overrides to a non-endian
# type (binary).
display:
- platform: sdl
id: image_display
auto_clear_enabled: false
dimensions:
width: 480
height: 480
image:
defaults:
type: rgb565
transparency: opaque
byte_order: little_endian
resize: 50x50
dither: FloydSteinberg
images:
- id: legacy_defaults_image
file: ../../pnglogo.png
- id: legacy_defaults_binary
file: ../../pnglogo.png
type: binary
@@ -0,0 +1,24 @@
# Legacy top-level `image:` structured form using single-dict (non-list) values
# for `images:`, a type group, and a transparency group -- the old `ensure_list`
# accepted a bare dict in each of these places. Deprecated; migrates to
# `platform: file`. Config-only test exercising the deprecation/migration path.
display:
- platform: sdl
id: image_display
auto_clear_enabled: false
dimensions:
width: 480
height: 480
image:
images:
id: legacy_images_single_dict
file: ../../pnglogo.png
type: rgb565
rgb565:
id: legacy_grouped_type_single_dict
file: ../../pnglogo.png
rgb:
alpha_channel:
id: legacy_grouped_transparency_single_dict
file: ../../pnglogo.png
@@ -0,0 +1,25 @@
# Legacy top-level `image:` type-grouped form (deprecated; migrates to
# `platform: file`). Config-only test exercising the deprecation/migration path.
display:
- platform: sdl
id: image_display
auto_clear_enabled: false
dimensions:
width: 480
height: 480
image:
binary:
- id: legacy_grouped_binary
file: ../../pnglogo.png
rgb:
alpha_channel:
- id: legacy_grouped_rgba
file: ../../pnglogo.png
opaque:
- id: legacy_grouped_rgb
file: ../../pnglogo.png
rgb565:
- id: legacy_grouped_rgb565
file: ../../pnglogo.png
transparency: chroma_key
@@ -0,0 +1,16 @@
# Legacy top-level `image:` single-dict form (a bare image dict instead of a
# list; deprecated, migrates to `platform: file`). Config-only test exercising
# the deprecation/migration path.
display:
- platform: sdl
id: image_display
auto_clear_enabled: false
dimensions:
width: 480
height: 480
image:
id: legacy_single_image
file: ../../pnglogo.png
type: RGB565
transparency: opaque
+18
View File
@@ -0,0 +1,18 @@
# Legacy top-level `image:` list form (deprecated; migrates to `platform: file`).
# Config-only test exercising the deprecation/migration path.
display:
- platform: sdl
id: image_display
auto_clear_enabled: false
dimensions:
width: 480
height: 480
image:
- id: legacy_list_binary
file: ../../pnglogo.png
type: BINARY
- id: legacy_list_rgb565
file: ../../pnglogo.png
type: RGB565
transparency: alpha_channel
+2 -2
View File
@@ -91,8 +91,8 @@ binary_sensor:
animation: move_right
time: 600ms
- platform: lvgl
id: button_checker
name: LVGL button
id: common_button_checker
name: Common button
widget: spin_up
on_state:
then:
+122 -17
View File
@@ -24,20 +24,6 @@ binary_sensor:
name: Button A checked
widget: button_a
state: checked
- platform: lvgl
id: button_checker
name: LVGL button
widget: button_button
state: checked
on_state:
then:
- lvgl.checkbox.update:
id: checkbox_id
state:
checked: !lambda |-
auto y = x; // block inlining of one line return
return y;
- platform: lvgl
id: button_presser
name: Button pressed
@@ -49,6 +35,9 @@ lvgl:
rotation: 90
log_level: debug
resume_on_input: true
paused: true
update_when_display_idle: true
refresh_interval: 30ms
on_pause:
- logger.log: LVGL is Paused
- lvgl.display.set_rotation: 90
@@ -57,6 +46,40 @@ lvgl:
- lvgl.display.set_rotation:
rotation: 0
lvgl_id: lvgl_id
- lvgl.display.set_rotation:
rotation: !lambda "return 180;"
lvgl_id: lvgl_id
on_landscape:
- logger.log: LVGL display is now landscape
# Re-layout a container in response to orientation changes. The layout type
# and grid structure are fixed at creation; only the style options change.
- lvgl.widget.update:
id: grid_rows_only_shorthand
layout:
grid_column_align: center
grid_row_align: space_between
pad_row: 4px
- lvgl.update:
top_layer:
layout:
flex_flow: row
on_portrait:
- logger.log: LVGL display is now portrait
- lvgl.widget.update:
id: grid_rows_only_shorthand
layout:
grid_column_align: start
pad_row: 2px
# Top-level layout applies to the active screen
- lvgl.update:
layout:
flex_flow: column
pad_row: 8px
- lvgl.update:
top_layer:
layout:
flex_flow: column
flex_align_main: center
on_boot:
- logger.log: LVGL has started
@@ -64,6 +87,12 @@ lvgl:
id: meter_arc_indicator
start_value: 0
end_value: 180
- lvgl.animation.start:
id:
- anim_slide
- anim_color
duration: 3s
loop: true
on_invalidate_area:
logger.log: Invalidate area
on_resolution_change:
@@ -108,6 +137,52 @@ lvgl:
- obj:
bg_color: 0x000000
bg_opa: cover
top_layer:
widgets:
- obj:
id: anim_box
x: 0
y: 0
width: 50
height: 50
bg_color: 0xFF0000
- label:
id: anim_label
text: anim
animations:
- id: anim_slide
duration: 1s
start_delay: 100ms
auto_start: true
loop: true
timing: ease_in_out
on_start:
- logger.log: anim started
on_stop:
- logger.log: anim stopped
widgets:
- id: anim_box
x:
from: 0
to: 100
y:
from: 0
to: !lambda "return 80;"
opa:
from: 50%
to: 100%
- id: anim_color
duration: 2s
timing:
- round_trip
- type: gravity
bounce: 0.3
acceleration: 0.8
widgets:
- id: anim_label
text_color:
from: 0xFF0000
to: color_id
theme:
dark_mode: true
obj:
@@ -210,6 +285,11 @@ lvgl:
on_click:
then:
- lvgl.display.set_rotation: 0
- lvgl.animation.stop: anim_slide
- lvgl.animation.stop:
id:
- anim_slide
- anim_color
- lvgl.widget.hide: message_box
- lvgl.style.update:
id: style_test
@@ -217,6 +297,9 @@ lvgl:
bg_opa: !lambda return 0.5;
- lvgl.image.update:
id: lv_image
src:
mapping: image_map
value: !lambda return round(1.0);
scale: !lambda return 512;
rotation: !lambda return 100;
pivot_x: !lambda return 20;
@@ -342,9 +425,16 @@ lvgl:
text_font: montserrat_40
border_post: true
on_press:
lvgl.label.update:
id: hello_label
text: Goodbye
- lvgl.label.update:
id: hello_label
text:
mapping: lvgl_string_map
value: !lambda return 2;
- lvgl.label.update:
id: hello_label
text:
mapping: lvgl_string_map
value: 2
on_click:
then:
- lvgl.animimg.stop: anim_img
@@ -1450,6 +1540,21 @@ image:
invert_alpha: true
transparency: alpha_channel
mapping:
- id: image_map
from: int
to: image
entries:
0: cat_image
1: dog_image
- id: lvgl_string_map
from: int
to: string
entries:
0: "First"
1: "Second"
2: "Third"
color:
- id: light_blue
hex: "3340FF"
+2 -3
View File
@@ -1,7 +1,8 @@
packages:
lvgl: !include lvgl-package.yaml
lvgl_package: !include lvgl-package.yaml
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
lvgl: !include common.yaml
sensor:
- platform: rotary_encoder
@@ -77,5 +78,3 @@ lvgl:
- component.update: tft_display
- delay: 60s
- lvgl.resume:
<<: !include common.yaml
+38 -1
View File
@@ -22,6 +22,36 @@ lvgl:
displays: sdl0
rotation: 180
top_layer:
widgets:
- obj:
id: anim_box
x: 0
y: 0
width: 40
height: 40
bg_color: 0xFF0000
animations:
- id: anim_slide
duration: 1s
start_delay: 100ms
auto_start: true
loop: true
timing:
- round_trip
- type: ease_in_out
weight: 3
on_start:
- logger.log: anim started
on_stop:
- logger.log: anim stopped
widgets:
- id: anim_box
x:
from: 0
to: !lambda "return 100;"
opa:
from: 50%
to: 100%
- id: lvgl_1
displays: sdl1
@@ -42,7 +72,14 @@ lvgl:
- label:
text: Click ME
on_click:
logger.log: Clicked
then:
- logger.log: Clicked
- lvgl.animation.stop:
id: anim_slide
lvgl_id: lvgl_0
- lvgl.animation.start:
id: anim_slide
lvgl_id: lvgl_0
font:
- file: "gfonts://Roboto"
@@ -0,0 +1,7 @@
# No api, web_server or extra services so the fallback _http service
# (with version, mac and config_hash TXT records) is compiled.
wifi:
ssid: MySSID
password: password1
mdns:
@@ -0,0 +1,9 @@
# web_server without the native api so the version, mac and config_hash
# TXT records are attached to the web_server _http service.
wifi:
ssid: MySSID
password: password1
web_server:
mdns:
@@ -1,5 +1,5 @@
packages:
- !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml
i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml
psram:
mode: octal
@@ -37,6 +37,9 @@ class MockUARTComponent : public uart::UARTComponent {
MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override));
MOCK_METHOD(uart::UARTFlushResult, flush, (), (override));
MOCK_METHOD(void, check_logger_conflict, (), (override));
#if defined(USE_ESP8266) || defined(USE_ESP32)
void load_settings(bool dump_config) override {}
#endif // defined(USE_ESP8266) || defined(USE_ESP32)
};
class TestableMitsubishiCN105 : public MitsubishiCN105 {
@@ -0,0 +1,178 @@
#include <gtest/gtest.h>
#include <cstdint>
#include <span>
#include "esphome/components/modbus/modbus.h"
namespace esphome::modbus::testing {
namespace {
// Exposes the protected tx queue and waiting-for-response slot so tests can drive the
// no-response path without a UART: force_send_front() mimics send_next_frame_() moving the
// front frame in flight, timeout_waiting() mimics the loop() no-response timeout handling.
class NoResponseProbeHub : public ModbusClientHub {
public:
size_t queued_frames() const { return this->tx_buffer_.size(); }
const ModbusDeviceCommand &front() const { return this->tx_buffer_.front(); }
bool waiting() const { return this->waiting_for_response_.has_value(); }
const ModbusDeviceCommand &waiting_command() const {
EXPECT_TRUE(this->waiting_for_response_.has_value());
return *this->waiting_for_response_; // NOLINT(bugprone-unchecked-optional-access)
}
void force_send_front() {
this->waiting_for_response_ = std::move(this->tx_buffer_.front());
this->tx_buffer_.pop_front();
}
// Drives the real unexpected-frame branch in process_modbus_server_frame().
void receive_frame_for_test(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len) {
this->process_modbus_server_frame(address, function_code, data, len);
}
void timeout_waiting() {
if (this->waiting_for_response_.has_value())
this->notify_no_response_(*this->waiting_for_response_);
this->waiting_for_response_.reset();
}
};
// A device with a scripted answer to on_modbus_no_response().
class RetryingDevice : public ModbusClientDevice {
public:
RetryingDevice(ModbusClientHub *hub, uint8_t address, bool retry) : ModbusClientDevice(hub, address), retry_(retry) {}
bool on_modbus_no_response() override {
this->no_response_count_++;
return this->retry_;
}
int no_response_count_{0};
protected:
bool retry_{false};
};
// A device that clears its own queued traffic from inside the no-response callback, then asks for a retry.
class ClearingRetryDevice : public ModbusClientDevice {
public:
ClearingRetryDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {}
bool on_modbus_no_response() override {
this->no_response_count_++;
this->clear_tx_queue_for_device(); // detaches this device from the waiting slot mid-callback
return true; // and still requests a retry
}
int no_response_count_{0};
};
constexpr uint8_t READ_PDU[] = {0x03, 0x01, 0x00, 0x00, 0x02}; // read 2 holding registers at 0x100
StaticVector<uint8_t, MAX_PDU_SIZE> read_pdu() {
StaticVector<uint8_t, MAX_PDU_SIZE> pdu;
pdu.assign(READ_PDU, READ_PDU + sizeof(READ_PDU));
return pdu;
}
} // namespace
// A device that requests a retry gets the frame the hub was holding re-queued on its behalf,
// byte-identical and still routed to the same device.
TEST(ModbusClientHubNoResponse, RetryRequeuesWaitingFrame) {
NoResponseProbeHub hub;
RetryingDevice device(&hub, 0x02, /*retry=*/true);
device.send_pdu(read_pdu());
ASSERT_EQ(hub.queued_frames(), 1u);
hub.force_send_front();
ASSERT_EQ(hub.queued_frames(), 0u);
ASSERT_TRUE(hub.waiting());
hub.timeout_waiting();
EXPECT_EQ(device.no_response_count_, 1);
EXPECT_FALSE(hub.waiting());
ASSERT_EQ(hub.queued_frames(), 1u);
const ModbusDeviceCommand &requeued = hub.front();
EXPECT_EQ(requeued.device, &device);
// address + PDU + CRC
ASSERT_EQ(requeued.frame.size(), sizeof(READ_PDU) + 3);
EXPECT_EQ(requeued.frame.data.data()[0], 0x02);
EXPECT_EQ(0, memcmp(requeued.frame.data.data() + 1, READ_PDU, sizeof(READ_PDU)));
}
// A device that declines the retry has the frame dropped.
TEST(ModbusClientHubNoResponse, NoRetryDropsWaitingFrame) {
NoResponseProbeHub hub;
RetryingDevice device(&hub, 0x02, /*retry=*/false);
device.send_pdu(read_pdu());
hub.force_send_front();
hub.timeout_waiting();
EXPECT_EQ(device.no_response_count_, 1);
EXPECT_FALSE(hub.waiting());
EXPECT_EQ(hub.queued_frames(), 0u);
}
// After the device is detached from the waiting frame (e.g. clear_tx_queue_for_device on
// destruction), a timeout must not deliver a callback or re-queue anything.
TEST(ModbusClientHubNoResponse, DetachedDeviceIsNotNotified) {
NoResponseProbeHub hub;
{
RetryingDevice device(&hub, 0x02, /*retry=*/true);
device.send_pdu(read_pdu());
hub.force_send_front();
// device destructor clears its queue entries, including the waiting frame's device pointer
}
ASSERT_TRUE(hub.waiting());
EXPECT_EQ(hub.waiting_command().device, nullptr);
hub.timeout_waiting();
EXPECT_FALSE(hub.waiting());
EXPECT_EQ(hub.queued_frames(), 0u);
}
// An unexpected frame interrupts the transaction: the retry is re-queued immediately, but the
// waiting entry survives as an interrupted shell (device detached) that keeps tx blocked until the
// send-wait timeout clears it - without a second no-response callback or a duplicate requeue.
TEST(ModbusClientHubNoResponse, RetryBehindInterruptedShell) {
NoResponseProbeHub hub;
RetryingDevice device(&hub, 0x02, /*retry=*/true);
device.send_pdu(read_pdu());
hub.force_send_front();
// A frame from the wrong address (0x07, expected 0x02) hits the unexpected-frame branch.
const uint8_t stray_payload[] = {0x04, 0x00, 0x2A, 0x01, 0x00};
hub.receive_frame_for_test(0x07, 0x03, stray_payload, sizeof(stray_payload));
EXPECT_EQ(device.no_response_count_, 1);
ASSERT_EQ(hub.queued_frames(), 1u); // exactly one requeue...
EXPECT_EQ(hub.front().device, &device);
ASSERT_TRUE(hub.waiting()); // ...while the shell stays in the waiting slot
EXPECT_TRUE(hub.waiting_command().interrupted);
EXPECT_EQ(hub.waiting_command().device, nullptr);
// The send-wait timeout clears the shell without a second callback or another requeue.
hub.timeout_waiting();
EXPECT_FALSE(hub.waiting());
EXPECT_EQ(device.no_response_count_, 1);
EXPECT_EQ(hub.queued_frames(), 1u);
}
// A callback that detaches the device (clear_tx_queue_for_device()) wins over its own retry request:
// no orphaned frame with a null device is re-queued.
TEST(ModbusClientHubNoResponse, MidCallbackClearCancelsRetry) {
NoResponseProbeHub hub;
ClearingRetryDevice device(&hub, 0x02);
device.send_pdu(read_pdu());
hub.force_send_front();
hub.timeout_waiting();
EXPECT_EQ(device.no_response_count_, 1);
EXPECT_EQ(hub.queued_frames(), 0u); // the retry was not re-queued for a detached device
EXPECT_FALSE(hub.waiting());
}
} // namespace esphome::modbus::testing
@@ -181,17 +181,17 @@ TEST(ModbusCreateClientPdu, WriteMultipleOverEntityLimitReturnsEmpty) {
TEST(ModbusHelpersTest, PayloadToNumberRejectsOffsetAtEndOfBuffer) {
const std::vector<uint8_t> data{0x12, 0x34};
EXPECT_EQ(payload_to_number(data, SensorValueType::U_WORD, 2, 0xFFFFFFFF), 0);
EXPECT_FALSE(payload_to_number(std::span<const uint8_t>(data), SensorValueType::U_WORD, 2, 0xFFFFFFFF).has_value());
}
TEST(ModbusHelpersTest, PayloadToNumberRejectsTruncatedMultiRegisterValue) {
const std::vector<uint8_t> data{0x12, 0x34, 0x56};
EXPECT_EQ(payload_to_number(data, SensorValueType::U_DWORD, 0, 0xFFFFFFFF), 0);
EXPECT_FALSE(payload_to_number(std::span<const uint8_t>(data), SensorValueType::U_DWORD, 0, 0xFFFFFFFF).has_value());
}
TEST(ModbusHelpersTest, PayloadToNumberDecodesValidWord) {
const std::vector<uint8_t> data{0x12, 0x34};
EXPECT_EQ(payload_to_number(data, SensorValueType::U_WORD, 0, 0xFFFFFFFF), 0x1234);
EXPECT_EQ(payload_to_number(std::span<const uint8_t>(data), SensorValueType::U_WORD, 0, 0xFFFFFFFF), 0x1234);
}
// --- registers_to_number ---------------------------------------------------
@@ -218,16 +218,15 @@ TEST(ModbusHelpersTest, RegistersToNumberMatchesPayloadToNumber) {
const uint16_t registers[] = {0x8001, 0x0002};
const std::vector<uint8_t> bytes{0x80, 0x01, 0x00, 0x02};
for (auto value_type : {SensorValueType::S_DWORD, SensorValueType::U_DWORD, SensorValueType::S_DWORD_R}) {
EXPECT_EQ(registers_to_number(registers, 2, value_type), payload_to_number(bytes, value_type, 0, 0xFFFFFFFF))
EXPECT_EQ(registers_to_number(registers, 2, value_type),
payload_to_number(std::span<const uint8_t>(bytes), value_type, 0, 0xFFFFFFFF))
<< "value_type=" << static_cast<int>(value_type);
}
}
TEST(ModbusHelpersTest, RegistersToNumberRejectsTruncatedMultiRegisterValue) {
const uint16_t registers[] = {0x1234};
bool error = false;
EXPECT_EQ(registers_to_number(registers, 1, SensorValueType::U_DWORD, &error), 0);
EXPECT_TRUE(error);
EXPECT_FALSE(registers_to_number(registers, 1, SensorValueType::U_DWORD).has_value());
}
} // namespace esphome::modbus::helpers
+17 -12
View File
@@ -2,11 +2,9 @@ wifi:
ssid: MySSID
password: password1
# Purposely test that `online_image:` does auto-load `image:`
# Keep the `image:` undefined.
# image:
online_image:
- id: online_binary_image
image:
- platform: online_image
id: online_binary_image
url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png
format: PNG
type: BINARY
@@ -21,34 +19,41 @@ online_image:
} else {
ESP_LOGD("online_image", "Cache miss: fresh download");
}
- id: online_binary_transparent_image
- platform: online_image
id: online_binary_transparent_image
url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png
type: BINARY
transparency: chroma_key
format: png
- id: online_rgba_image
- platform: online_image
id: online_rgba_image
url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png
format: PNG
type: RGB
transparency: alpha_channel
- id: online_rgb24_image
- platform: online_image
id: online_rgb24_image
url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png
format: PNG
type: RGB
transparency: chroma_key
- id: online_binary_bmp
- platform: online_image
id: online_binary_bmp
url: https://samples-files.com/samples/images/bmp/480-360-sample.bmp
format: BMP
type: BINARY
- id: online_rgb_bmp_8bit
- platform: online_image
id: online_rgb_bmp_8bit
url: https://samples-files.com/samples/images/bmp/480-360-sample.bmp
format: BMP
type: RGB
- id: online_jpeg_image
- platform: online_image
id: online_jpeg_image
url: http://www.faqs.org/images/library.jpg
format: JPEG
type: RGB
- id: online_jpg_image
- platform: online_image
id: online_jpg_image
url: http://www.faqs.org/images/library.jpg
format: JPG
type: RGB565
@@ -0,0 +1,22 @@
# Legacy top-level `online_image:` form (deprecated; migrates to
# `platform: online_image`). Config-only test exercising the deprecation path.
wifi:
ssid: MySSID
password: password1
http_request:
display:
- platform: sdl
id: online_image_display
auto_clear_enabled: false
dimensions:
width: 480
height: 480
online_image:
- id: legacy_online_image
url: http://www.example.org/example.png
format: PNG
type: RGB565
resize: 50x50
+11
View File
@@ -4,6 +4,11 @@ audio_dac:
i2c_id: i2c_bus
address: 0x4D
bits_per_sample: 32bit
analog_gain: -6db
channel_mix: swapped
volume_min_db: -60dB
volume_max_db: -3dB
enable_pin: GPIO12
output:
- platform: gpio
@@ -22,3 +27,9 @@ binary_sensor:
number: 4
mode:
input: true
switch:
- platform: pcm5122
pcm5122: pcm5122_dac
name: PCM5122 Power Down
power_mode: powerdown
@@ -0,0 +1,25 @@
# Exercises the provisioning window: api registers as a provisioning source
# (encryption enabled, no key), the on_timeout automation, and the wifi +
# esp32_improv cross-component guards. improv_serial is intentionally NOT gated.
provisioning:
timeout: 1min
on_timeout:
then:
- logger.log: "Provisioning window expired"
api:
encryption:
wifi:
ssid: MySSID
password: password1
improv_serial:
binary_sensor:
- platform: gpio
pin: 0
id: io0_button
esp32_improv:
authorizer: io0_button
@@ -0,0 +1,16 @@
# Provisioning window on ESP8266 (no BLE Improv): api as a provisioning source
# and the wifi reboot guard. improv_serial is present and intentionally NOT gated.
provisioning:
timeout: 1min
on_timeout:
then:
- logger.log: "Provisioning window expired"
api:
encryption:
wifi:
ssid: MySSID
password: password1
improv_serial:
@@ -0,0 +1,15 @@
# A device provisioned over the network (wifi / Improv) with no api: network
# connectivity alone satisfies provisioning, so `provisioning:` is valid without an
# api encryption source. Config-only -- exercises the network provisioning-source
# validation path (the Improv-only case from the review).
provisioning:
timeout: 1min
on_timeout:
then:
- logger.log: "Provisioning window expired"
wifi:
ssid: MySSID
password: password1
improv_serial:
+13
View File
@@ -0,0 +1,13 @@
# The test_display platform (and its external_components entry) is provided by
# the shared package included from the test.*.yaml files. These extra instances
# exercise the remaining `dimensions` code paths: the width/height map form and
# the default when omitted. The package's own `test_display_screen` covers the
# "WIDTHxHEIGHT" string form.
display:
- platform: test_display
id: test_display_wh_dimensions
dimensions:
width: 320
height: 240
- platform: test_display
id: test_display_default_dimensions
@@ -0,0 +1,36 @@
import esphome.codegen as cg
from esphome.components import display
import esphome.config_validation as cv
from esphome.const import CONF_DIMENSIONS, CONF_HEIGHT, CONF_ID, CONF_WIDTH
from esphome.core import CoroPriority, coroutine_with_priority
test_display_ns = cg.esphome_ns.namespace("test_display")
TestDisplay = test_display_ns.class_("TestDisplay", display.Display)
CONFIG_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TestDisplay),
cv.Optional(CONF_DIMENSIONS, default="100x100"): cv.Any(
cv.dimensions,
cv.Schema(
{
cv.Required(CONF_WIDTH): cv.int_,
cv.Required(CONF_HEIGHT): cv.int_,
}
),
),
}
)
@coroutine_with_priority(CoroPriority.CORE)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await display.register_display(var, config)
dimensions = config[CONF_DIMENSIONS]
if isinstance(dimensions, dict):
width, height = dimensions[CONF_WIDTH], dimensions[CONF_HEIGHT]
else:
width, height = dimensions
cg.add(var.set_dimensions(width, height))
@@ -0,0 +1,36 @@
#pragma once
#include "esphome/components/display/display.h"
#include "esphome/core/color.h"
namespace esphome::test_display {
/** A no-op display that draws nothing and uses no pins.
*
* It exists purely to satisfy components that require a display (for example
* touchscreens, which read the display dimensions) in configurations - most
* notably YAML build tests - where a real display driver would only get in the
* way by occupying GPIO pins and pulling in bus dependencies.
*/
class TestDisplay : public display::Display {
public:
void update() override { this->do_update_(); }
void set_dimensions(int width, int height) {
this->width_ = width;
this->height_ = height;
}
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
void draw_pixel_at(int x, int y, Color color) override {}
protected:
int get_width_internal() override { return this->width_; }
int get_height_internal() override { return this->height_; }
int width_{0};
int height_{0};
};
} // namespace esphome::test_display
@@ -0,0 +1,3 @@
packages:
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
test_display_extra: !include common.yaml
@@ -0,0 +1,3 @@
packages:
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
test_display_extra: !include common.yaml
@@ -0,0 +1,3 @@
packages:
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
test_display_extra: !include common.yaml
+1 -12
View File
@@ -1,19 +1,8 @@
display:
- platform: ssd1306_i2c
i2c_id: i2c_bus
id: tt21100_ssd1306_i2c_display
model: SSD1306_128X64
reset_pin: ${disp_reset_pin}
pages:
- id: tt21100_page1
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
touchscreen:
- platform: tt21100
i2c_id: i2c_bus
id: tt21100_touchscreen
display: tt21100_ssd1306_i2c_display
display: test_display_screen
interrupt_pin: ${interrupt_pin}
reset_pin: ${reset_pin}
+2 -3
View File
@@ -1,9 +1,8 @@
substitutions:
disp_reset_pin: GPIO12
interrupt_pin: GPIO15
reset_pin: GPIO4
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
<<: !include common.yaml
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
tt21100: !include common.yaml
@@ -1,9 +1,8 @@
substitutions:
disp_reset_pin: GPIO0
interrupt_pin: GPIO15
reset_pin: GPIO16
packages:
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
<<: !include common.yaml
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
tt21100: !include common.yaml
@@ -1,9 +1,8 @@
substitutions:
disp_reset_pin: GPIO10
interrupt_pin: GPIO2
reset_pin: GPIO3
packages:
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
<<: !include common.yaml
test_display: !include ../../test_build_components/common/test_display/test_display.yaml
tt21100: !include common.yaml
+3
View File
@@ -32,6 +32,9 @@ class MockUARTComponent : public UARTComponent {
MOCK_METHOD(size_t, available, (), (override));
MOCK_METHOD(UARTFlushResult, flush, (), (override));
MOCK_METHOD(void, check_logger_conflict, (), (override));
#if defined(USE_ESP8266) || defined(USE_ESP32)
MOCK_METHOD(void, load_settings, (bool dump_config), (override));
#endif
};
} // namespace esphome::uart::testing
@@ -0,0 +1 @@
<<: !include common.yaml
@@ -5,3 +5,6 @@ web_server:
port: 8080
version: 2
compression: br
enable_private_network_access: true
allowed_origins:
- https://app.esphome.io
@@ -4,6 +4,9 @@ packages:
web_server:
port: 8080
version: 3
# allowed_origins can be set independently of Private Network Access
allowed_origins:
- https://app.esphome.io
sorting_groups:
- id: sorting_group_1
name: "Group 1 Diplayed Last"
@@ -1 +1,8 @@
<<: !include common_v2.yaml
packages:
web_server: !include common_v2.yaml
web_server:
auth:
username: admin
password: password
type: digest
@@ -1,6 +1,8 @@
<<: !include common_v2.yaml
packages:
web_server: !include common_v2.yaml
web_server:
auth:
username: admin
password: password
type: digest
@@ -1 +1,8 @@
<<: !include common_v2.yaml
packages:
web_server: !include common_v2.yaml
web_server:
auth:
username: admin
password: password
type: digest
@@ -1 +1,8 @@
<<: !include common_v2.yaml
packages:
web_server: !include common_v2.yaml
web_server:
auth:
username: admin
password: password
type: basic
@@ -1 +1,2 @@
<<: !include common_v1.yaml
packages:
web_server: !include common_v1.yaml
@@ -1 +1,2 @@
<<: !include common_v1.yaml
packages:
web_server: !include common_v1.yaml
@@ -1 +1,2 @@
<<: !include common_v3.yaml
packages:
web_server: !include common_v3.yaml
@@ -0,0 +1,8 @@
packages:
web_server: !include common_v2.yaml
web_server:
auth:
username: admin
password: password
type: basic
+12 -1
View File
@@ -5,13 +5,24 @@ binary_sensor:
- platform: template
name: "Garage Door Open 10"
report: "default"
use_device_type: false
- platform: template
name: "Garage Door Open 12"
report: "force"
endpoint: 1
sensor:
- platform: template
name: "Temperature Sensor"
lambda: return 10.0;
device_class: temperature
unit_of_measurement: "°C"
endpoint: 1
use_device_type: true
zigbee:
model: zigbee_test
router: true
router: false
power_source: MAINS_SINGLE_PHASE
on_join:
then:
@@ -0,0 +1,7 @@
zigbee:
model: zigbee_test
router: true
power_source: MAINS_SINGLE_PHASE
on_join:
then:
- logger.log: "Joined network"