mirror of
https://github.com/esphome/esphome.git
synced 2026-10-03 01:35:59 +00:00
Merge remote-tracking branch 'origin/dev' into integration
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,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}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -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,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}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user