mirror of
https://github.com/esphome/esphome.git
synced 2026-10-11 05:44:15 +00:00
[gpio][esp32][uart] Add option to hold gpio during (deep)sleep and reboot (3/3) (#18964)
Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
co-authored by
J. Nick Koston
J. Nick Koston
parent
dc553aae55
commit
ba7d3bd770
@@ -10,6 +10,7 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.const import CONF_HOLD_STATE
|
||||
from esphome.components.esp32 import (
|
||||
_ESP_TLS_LINKING_COMPONENTS,
|
||||
DEFAULT_EXCLUDED_IDF_COMPONENTS,
|
||||
@@ -38,13 +39,30 @@ from esphome.components.esp32.const import (
|
||||
KEY_NETWORK_SDKCONFIG,
|
||||
KEY_SDKCONFIG_OPTIONS,
|
||||
KEY_VARIANT,
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32C5,
|
||||
VARIANT_ESP32C6,
|
||||
VARIANT_ESP32C61,
|
||||
VARIANT_ESP32H2,
|
||||
VARIANT_ESP32H4,
|
||||
VARIANT_ESP32H21,
|
||||
VARIANT_ESP32P4,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANT_ESP32S31,
|
||||
)
|
||||
from esphome.components.esp32.gpio import validate_gpio_pin
|
||||
from esphome.components.esp32.gpio import validate_gpio_pin, validate_supports
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ESPHOME,
|
||||
CONF_IGNORE_PIN_VALIDATION_ERROR,
|
||||
CONF_INPUT,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_OPEN_DRAIN,
|
||||
CONF_OUTPUT,
|
||||
CONF_PULLDOWN,
|
||||
CONF_PULLUP,
|
||||
PlatformFramework,
|
||||
Toolchain,
|
||||
)
|
||||
@@ -57,7 +75,7 @@ def test_esp32_config(
|
||||
) -> None:
|
||||
set_core_config(PlatformFramework.ESP32_IDF)
|
||||
|
||||
from esphome.components.esp32 import CONFIG_SCHEMA, VARIANT_ESP32, VARIANT_FRIENDLY
|
||||
from esphome.components.esp32 import CONFIG_SCHEMA, VARIANT_FRIENDLY
|
||||
|
||||
# Example ESP32 configuration
|
||||
config = {
|
||||
@@ -1722,9 +1740,8 @@ def test_esp32_s31_gpio_validation(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""S31: GPIO26-28/30-32 are reserved for the SPI flash interface, GPIO29
|
||||
and GPIO41 do not exist, GPIO33 is a normal pin, and GPIO36 is a
|
||||
and GPIO41 do not exist, GPIO35 is a normal pin, and GPIO36 is a
|
||||
strapping pin."""
|
||||
from esphome.components.esp32.const import VARIANT_ESP32S31
|
||||
from esphome.components.esp32.gpio import validate_supports
|
||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_OPEN_DRAIN, CONF_OUTPUT
|
||||
|
||||
@@ -1735,8 +1752,8 @@ def test_esp32_s31_gpio_validation(
|
||||
input_mode = {CONF_INPUT: True, CONF_OUTPUT: False, CONF_OPEN_DRAIN: False}
|
||||
|
||||
# Not reserved; a normal GPIO
|
||||
pin = {CONF_NUMBER: 33, CONF_IGNORE_PIN_VALIDATION_ERROR: False}
|
||||
assert validate_gpio_pin(pin)[CONF_NUMBER] == 33
|
||||
pin = {CONF_NUMBER: 35, CONF_IGNORE_PIN_VALIDATION_ERROR: False}
|
||||
assert validate_gpio_pin(pin)[CONF_NUMBER] == 35
|
||||
|
||||
# Reserved for the SPI flash interface, but can be bypassed with
|
||||
# ignore_pin_validation_error
|
||||
@@ -1764,6 +1781,113 @@ def test_esp32_s31_gpio_validation(
|
||||
assert "GPIO36 is a strapping pin" in caplog.text
|
||||
|
||||
|
||||
_INPUT_ONLY_SETTINGS = (
|
||||
(CONF_OUTPUT, "does not support output pin mode"),
|
||||
(CONF_PULLUP, "does not support pullups"),
|
||||
(CONF_PULLDOWN, "does not support pulldowns"),
|
||||
(CONF_HOLD_STATE, "is input-only and cannot be held"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("variant", "number", "setting", "error"),
|
||||
[
|
||||
pytest.param(
|
||||
variant, number, setting, error, id=f"{name}-gpio{number}-{setting}"
|
||||
)
|
||||
for variant, name, numbers in (
|
||||
(VARIANT_ESP32, "esp32", range(34, 40)),
|
||||
(VARIANT_ESP32S2, "s2", (46,)),
|
||||
)
|
||||
for number in numbers
|
||||
for setting, error in _INPUT_ONLY_SETTINGS
|
||||
]
|
||||
+ [
|
||||
pytest.param(
|
||||
VARIANT_ESP32,
|
||||
20,
|
||||
CONF_HOLD_STATE,
|
||||
"GPIO20 has no hold function",
|
||||
id="esp32-gpio20-hold_state",
|
||||
)
|
||||
],
|
||||
)
|
||||
def test_input_only_gpio_rejects_unsupported_modes(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
variant: str,
|
||||
number: int,
|
||||
setting: str,
|
||||
error: str,
|
||||
) -> None:
|
||||
set_core_config(PlatformFramework.ESP32_IDF, platform_data={KEY_VARIANT: variant})
|
||||
mode = {
|
||||
CONF_INPUT: True,
|
||||
CONF_OUTPUT: False,
|
||||
CONF_OPEN_DRAIN: False,
|
||||
CONF_PULLUP: False,
|
||||
CONF_PULLDOWN: False,
|
||||
}
|
||||
pin = {CONF_NUMBER: number, CONF_MODE: mode}
|
||||
if setting == CONF_HOLD_STATE:
|
||||
pin[setting] = True
|
||||
else:
|
||||
mode[setting] = True
|
||||
|
||||
with pytest.raises(cv.Invalid, match=error):
|
||||
validate_supports(pin)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("variant", "number"),
|
||||
[
|
||||
pytest.param(VARIANT_ESP32C3, 18, id="c3-18"),
|
||||
pytest.param(VARIANT_ESP32C3, 19, id="c3-19"),
|
||||
pytest.param(VARIANT_ESP32C5, 13, id="c5-13"),
|
||||
pytest.param(VARIANT_ESP32C5, 14, id="c5-14"),
|
||||
pytest.param(VARIANT_ESP32C6, 12, id="c6-12"),
|
||||
pytest.param(VARIANT_ESP32C6, 13, id="c6-13"),
|
||||
pytest.param(VARIANT_ESP32C61, 12, id="c61-12"),
|
||||
pytest.param(VARIANT_ESP32C61, 13, id="c61-13"),
|
||||
pytest.param(VARIANT_ESP32H2, 26, id="h2-26"),
|
||||
pytest.param(VARIANT_ESP32H2, 27, id="h2-27"),
|
||||
pytest.param(VARIANT_ESP32H4, 13, id="h4-13"),
|
||||
pytest.param(VARIANT_ESP32H4, 14, id="h4-14"),
|
||||
pytest.param(VARIANT_ESP32H21, 17, id="h21-17"),
|
||||
pytest.param(VARIANT_ESP32H21, 18, id="h21-18"),
|
||||
pytest.param(VARIANT_ESP32P4, 24, id="p4-24"),
|
||||
pytest.param(VARIANT_ESP32P4, 25, id="p4-25"),
|
||||
pytest.param(VARIANT_ESP32S3, 19, id="s3-19"),
|
||||
pytest.param(VARIANT_ESP32S3, 20, id="s3-20"),
|
||||
pytest.param(VARIANT_ESP32S31, 33, id="s31-33"),
|
||||
pytest.param(VARIANT_ESP32S31, 34, id="s31-34"),
|
||||
],
|
||||
)
|
||||
def test_usb_jtag_gpio_hold_state_warns(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
variant: str,
|
||||
number: int,
|
||||
) -> None:
|
||||
set_core_config(PlatformFramework.ESP32_IDF, platform_data={KEY_VARIANT: variant})
|
||||
pin = {
|
||||
CONF_NUMBER: number,
|
||||
CONF_MODE: {
|
||||
CONF_INPUT: True,
|
||||
CONF_OUTPUT: False,
|
||||
CONF_OPEN_DRAIN: False,
|
||||
},
|
||||
CONF_HOLD_STATE: True,
|
||||
}
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
validate_supports(pin)
|
||||
|
||||
assert (
|
||||
f"GPIO{number} cannot hold at low level during wakeup from deep sleep."
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
|
||||
_TLS_SERVER_OPTIONS = (
|
||||
"CONFIG_MBEDTLS_TLS_CLIENT_ONLY",
|
||||
"CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Tests for GPIO output final validation."""
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import config_validation as cv
|
||||
from esphome.components.const import CONF_HOLD_STATE
|
||||
from esphome.components.gpio.output import FINAL_VALIDATE_SCHEMA
|
||||
from esphome.config import Config
|
||||
from esphome.const import CONF_ID, CONF_PIN, CONF_POWER_SUPPLY, PlatformFramework
|
||||
from esphome.core import ID
|
||||
from tests.component_tests.types import SetCoreConfigCallable
|
||||
|
||||
|
||||
@pytest.mark.parametrize("power_supply_holds", [False, True])
|
||||
def test_output_hold_state_requires_power_supply_hold_state(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
power_supply_holds: bool,
|
||||
) -> None:
|
||||
supply_id = ID("supply", is_declaration=True, type="power_supply")
|
||||
full_config = Config()
|
||||
full_config["power_supply"] = [
|
||||
{CONF_ID: supply_id, CONF_PIN: {CONF_HOLD_STATE: power_supply_holds}}
|
||||
]
|
||||
full_config.declare_ids.append((supply_id, ["power_supply", 0, CONF_ID]))
|
||||
set_core_config(PlatformFramework.ESP32_IDF, full_config=full_config)
|
||||
|
||||
config = {
|
||||
CONF_PIN: {CONF_HOLD_STATE: True},
|
||||
CONF_POWER_SUPPLY: ID("supply", is_declaration=False, type="power_supply"),
|
||||
}
|
||||
if power_supply_holds:
|
||||
FINAL_VALIDATE_SCHEMA(config)
|
||||
else:
|
||||
with pytest.raises(
|
||||
cv.Invalid,
|
||||
match="The power supply pin must also set hold_state when an output it powers sets it",
|
||||
):
|
||||
FINAL_VALIDATE_SCHEMA(config)
|
||||
Reference in New Issue
Block a user