1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-30 22:53:59 +00:00

Merge branch 'dev' into jesserockz-2025-457

This commit is contained in:
Jesse Hills
2025-10-07 07:35:49 +13:00
180 changed files with 2516 additions and 1640 deletions

View File

@@ -0,0 +1,194 @@
"""Tests for PSRAM component."""
from typing import Any
import pytest
from esphome.components.esp32.const import (
KEY_VARIANT,
VARIANT_ESP32,
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C5,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32P4,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
import esphome.config_validation as cv
from esphome.const import CONF_ESPHOME, PlatformFramework
from tests.component_tests.types import SetCoreConfigCallable
UNSUPPORTED_PSRAM_VARIANTS = [
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C5,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
]
SUPPORTED_PSRAM_VARIANTS = [
VARIANT_ESP32,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
VARIANT_ESP32P4,
]
@pytest.mark.parametrize(
("config", "error_match"),
[
pytest.param(
{},
r"PSRAM is not supported on this chip",
id="psram_not_supported",
),
],
)
@pytest.mark.parametrize("variant", UNSUPPORTED_PSRAM_VARIANTS)
def test_psram_configuration_errors_unsupported_variants(
config: Any,
error_match: str,
variant: str,
set_core_config: SetCoreConfigCallable,
) -> None:
set_core_config(
PlatformFramework.ESP32_IDF,
platform_data={KEY_VARIANT: variant},
full_config={CONF_ESPHOME: {}},
)
"""Test detection of invalid PSRAM configuration on unsupported variants."""
from esphome.components.psram import CONFIG_SCHEMA
with pytest.raises(cv.Invalid, match=error_match):
CONFIG_SCHEMA(config)
@pytest.mark.parametrize("variant", SUPPORTED_PSRAM_VARIANTS)
def test_psram_configuration_valid_supported_variants(
variant: str,
set_core_config: SetCoreConfigCallable,
) -> None:
set_core_config(
PlatformFramework.ESP32_IDF,
platform_data={KEY_VARIANT: variant},
full_config={
CONF_ESPHOME: {},
"esp32": {
"variant": variant,
"cpu_frequency": "160MHz",
"framework": {"type": "esp-idf"},
},
},
)
"""Test that PSRAM configuration is valid on supported variants."""
from esphome.components.psram import CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA
# This should not raise an exception
config = CONFIG_SCHEMA({})
FINAL_VALIDATE_SCHEMA(config)
def _setup_psram_final_validation_test(
esp32_config: dict,
set_core_config: SetCoreConfigCallable,
set_component_config: Any,
) -> str:
"""Helper function to set up ESP32 configuration for PSRAM final validation tests."""
# Use ESP32S3 for schema validation to allow all options, then override for final validation
schema_variant = "ESP32S3"
final_variant = esp32_config.get("variant", "ESP32S3")
full_esp32_config = {
"variant": final_variant,
"cpu_frequency": esp32_config.get("cpu_frequency", "240MHz"),
"framework": {"type": "esp-idf"},
}
set_core_config(
PlatformFramework.ESP32_IDF,
platform_data={KEY_VARIANT: schema_variant},
full_config={
CONF_ESPHOME: {},
"esp32": full_esp32_config,
},
)
set_component_config("esp32", full_esp32_config)
return final_variant
@pytest.mark.parametrize(
("config", "esp32_config", "expect_error", "error_match"),
[
pytest.param(
{"speed": "120MHz"},
{"cpu_frequency": "160MHz"},
True,
r"PSRAM 120MHz requires 240MHz CPU frequency",
id="120mhz_requires_240mhz_cpu",
),
pytest.param(
{"mode": "octal"},
{"variant": "ESP32"},
True,
r"Octal PSRAM is only supported on ESP32-S3",
id="octal_mode_only_esp32s3",
),
pytest.param(
{"mode": "quad", "enable_ecc": True},
{},
True,
r"ECC is only available in octal mode",
id="ecc_only_in_octal_mode",
),
pytest.param(
{"speed": "120MHZ"},
{"cpu_frequency": "240MHZ"},
False,
None,
id="120mhz_with_240mhz_cpu",
),
pytest.param(
{"mode": "octal"},
{"variant": "ESP32S3"},
False,
None,
id="octal_mode_on_esp32s3",
),
pytest.param(
{"mode": "octal", "enable_ecc": True},
{"variant": "ESP32S3"},
False,
None,
id="ecc_in_octal_mode",
),
],
)
def test_psram_final_validation(
config: Any,
esp32_config: dict,
expect_error: bool,
error_match: str | None,
set_core_config: SetCoreConfigCallable,
set_component_config: Any,
) -> None:
"""Test PSRAM final validation for both error and valid cases."""
from esphome.components.psram import CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA
from esphome.core import CORE
final_variant = _setup_psram_final_validation_test(
esp32_config, set_core_config, set_component_config
)
validated_config = CONFIG_SCHEMA(config)
# Update CORE variant for final validation
CORE.data["esp32"][KEY_VARIANT] = final_variant
if expect_error:
with pytest.raises(cv.Invalid, match=error_match):
FINAL_VALIDATE_SCHEMA(validated_config)
else:
# This should not raise an exception
FINAL_VALIDATE_SCHEMA(validated_config)

View File

@@ -0,0 +1,89 @@
esphome:
on_boot:
then:
- canbus.send:
# Extended ID explicit
canbus_id: esp32_internal_can
use_extended_id: true
can_id: 0x100
data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
- canbus.send:
# Standard ID by default
canbus_id: esp32_internal_can
can_id: 0x100
data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
- canbus.send:
# Extended ID explicit
canbus_id: esp32_internal_can_2
use_extended_id: true
can_id: 0x100
data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
- canbus.send:
# Standard ID by default
canbus_id: esp32_internal_can_2
can_id: 0x100
data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
canbus:
- platform: esp32_can
id: esp32_internal_can
rx_pin: GPIO8
tx_pin: GPIO7
can_id: 4
bit_rate: 50kbps
on_frame:
- can_id: 500
then:
- lambda: |-
std::string b(x.begin(), x.end());
ESP_LOGD("canbus1", "canid 500 %s", b.c_str() );
- can_id: 0b00000000000000000000001000000
can_id_mask: 0b11111000000000011111111000000
use_extended_id: true
then:
- lambda: |-
auto pdo_id = can_id >> 14;
switch (pdo_id)
{
case 117:
ESP_LOGD("canbus1", "exhaust_fan_duty");
break;
case 118:
ESP_LOGD("canbus1", "supply_fan_duty");
break;
case 119:
ESP_LOGD("canbus1", "supply_fan_flow");
break;
// to be continued...
}
- platform: esp32_can
id: esp32_internal_can_2
rx_pin: GPIO10
tx_pin: GPIO9
can_id: 4
bit_rate: 50kbps
on_frame:
- can_id: 500
then:
- lambda: |-
std::string b(x.begin(), x.end());
ESP_LOGD("canbus2", "canid 500 %s", b.c_str() );
- can_id: 0b00000000000000000000001000000
can_id_mask: 0b11111000000000011111111000000
use_extended_id: true
then:
- lambda: |-
auto pdo_id = can_id >> 14;
switch (pdo_id)
{
case 117:
ESP_LOGD("canbus2", "exhaust_fan_duty");
break;
case 118:
ESP_LOGD("canbus2", "supply_fan_duty");
break;
case 119:
ESP_LOGD("canbus2", "supply_fan_flow");
break;
// to be continued...
}

View File

@@ -0,0 +1,9 @@
i2c:
- id: i2c_lm75b
scl: ${scl_pin}
sda: ${sda_pin}
sensor:
- platform: lm75b
name: LM75B Temperature
update_interval: 30s

View File

@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO15
sda_pin: GPIO13
<<: !include common.yaml

View File

@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
<<: !include common.yaml

View File

@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
<<: !include common.yaml

View File

@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO15
sda_pin: GPIO13
<<: !include common.yaml

View File

@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
<<: !include common.yaml

View File

@@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
<<: !include common.yaml

View File

@@ -6,11 +6,16 @@ esphome:
format: "Warning: Logger level is %d"
args: [id(logger_id).get_log_level()]
- logger.set_level: WARN
- logger.set_level:
level: ERROR
tag: mqtt.client
logger:
id: logger_id
level: DEBUG
initial_level: INFO
logs:
mqtt.component: WARN
select:
- platform: logger

View File

@@ -17,5 +17,7 @@ sensor:
temperature:
name: QMC5883L Temperature
range: 800uT
data_rate: 200Hz
oversampling: 256x
update_interval: 15s
drdy_pin: ${drdy_pin}

View File

@@ -1,5 +1,6 @@
substitutions:
scl_pin: GPIO16
sda_pin: GPIO17
drdy_pin: GPIO18
<<: !include common.yaml

View File

@@ -1,5 +1,6 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
drdy_pin: GPIO6
<<: !include common.yaml

View File

@@ -1,5 +1,6 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
drdy_pin: GPIO6
<<: !include common.yaml

View File

@@ -1,5 +1,6 @@
substitutions:
scl_pin: GPIO16
sda_pin: GPIO17
drdy_pin: GPIO18
<<: !include common.yaml

View File

@@ -1,5 +1,6 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
drdy_pin: GPIO2
<<: !include common.yaml

View File

@@ -1,5 +1,6 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
drdy_pin: GPIO2
<<: !include common.yaml

View File

@@ -1,6 +1,8 @@
substitutions:
pin: GPIO2
clock_resolution: "2000000"
carrier_duty_percent: "25"
carrier_frequency: "30000"
filter_symbols: "2"
receive_symbols: "4"
rmt_symbols: "64"

View File

@@ -5,6 +5,9 @@ substitutions:
var21: '79'
value: 33
values: 44
position:
x: 79
y: 82
esphome:
name: test
@@ -26,3 +29,7 @@ test_list:
- Literal $values ${are not substituted}
- ["list $value", "${is not}", "${substituted}"]
- {"$dictionary": "$value", "${is not}": "${substituted}"}
- |-
{{{ "x", "79"}, { "y", "82"}}}
- '{{{"AA"}}}'
- '"HELLO"'

View File

@@ -8,6 +8,9 @@ substitutions:
var21: "79"
value: 33
values: 44
position:
x: 79
y: 82
test_list:
- "$var1"
@@ -27,3 +30,7 @@ test_list:
- !literal Literal $values ${are not substituted}
- !literal ["list $value", "${is not}", "${substituted}"]
- !literal {"$dictionary": "$value", "${is not}": "${substituted}"}
- |- # Test parsing things that look like a python set of sets when rendered:
{{{ "x", "${ position.x }"}, { "y", "${ position.y }"}}}
- ${ '{{{"AA"}}}' }
- ${ '"HELLO"' }