mirror of
https://github.com/esphome/esphome.git
synced 2026-09-30 16:30:22 +00:00
Gate use_interrupt_ storage, add codegen define tests, drop compile fixture
This commit is contained in:
@@ -15,12 +15,12 @@ PROGMEM_STRING_TABLE(InterruptTypeStrings, "UNKNOWN", "RISING_EDGE", "FALLING_ED
|
||||
static const LogString *interrupt_type_to_string(gpio::InterruptType type) {
|
||||
return InterruptTypeStrings::get_log_str(static_cast<uint8_t>(type), 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const LogString *gpio_mode_to_string(bool use_interrupt) {
|
||||
return use_interrupt ? LOG_STR("interrupt") : LOG_STR("polling");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_GPIO_BINARY_SENSOR_INTERRUPT
|
||||
void IRAM_ATTR GPIOBinarySensorStore::gpio_intr(GPIOBinarySensorStore *arg) {
|
||||
@@ -64,11 +64,13 @@ void GPIOBinarySensor::setup() {
|
||||
void GPIOBinarySensor::dump_config() {
|
||||
LOG_BINARY_SENSOR("", "GPIO Binary Sensor", this);
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
ESP_LOGCONFIG(TAG, " Mode: %s", LOG_STR_ARG(gpio_mode_to_string(this->store_.use_interrupt_)));
|
||||
#ifdef USE_GPIO_BINARY_SENSOR_INTERRUPT
|
||||
ESP_LOGCONFIG(TAG, " Mode: %s", LOG_STR_ARG(gpio_mode_to_string(this->store_.use_interrupt_)));
|
||||
if (this->store_.use_interrupt_) {
|
||||
ESP_LOGCONFIG(TAG, " Interrupt Type: %s", LOG_STR_ARG(interrupt_type_to_string(this->store_.interrupt_type_)));
|
||||
}
|
||||
#else
|
||||
ESP_LOGCONFIG(TAG, " Mode: polling");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ class GPIOBinarySensorStore {
|
||||
volatile bool state_{false};
|
||||
volatile bool changed_{false};
|
||||
gpio::InterruptType interrupt_type_{gpio::INTERRUPT_ANY_EDGE};
|
||||
#endif
|
||||
bool use_interrupt_{true};
|
||||
#endif
|
||||
};
|
||||
|
||||
class GPIOBinarySensor final : public binary_sensor::BinarySensor, public Component {
|
||||
@@ -51,9 +51,13 @@ class GPIOBinarySensor final : public binary_sensor::BinarySensor, public Compon
|
||||
// Interrupts are only detached on reboot when memory is cleared anyway.
|
||||
|
||||
void set_pin(GPIOPin *pin) { this->pin_ = pin; }
|
||||
void set_use_interrupt(bool use_interrupt) { this->store_.use_interrupt_ = use_interrupt; }
|
||||
#ifdef USE_GPIO_BINARY_SENSOR_INTERRUPT
|
||||
void set_use_interrupt(bool use_interrupt) { this->store_.use_interrupt_ = use_interrupt; }
|
||||
void set_interrupt_type(gpio::InterruptType type) { this->store_.interrupt_type_ = type; }
|
||||
#else
|
||||
// Polling-only build: codegen still emits set_use_interrupt(false) calls,
|
||||
// so keep the setter as an inlined no-op instead of storing the flag.
|
||||
void set_use_interrupt(bool /*use_interrupt*/) {}
|
||||
#endif
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
|
||||
@@ -3,10 +3,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.core import CORE
|
||||
|
||||
INTERRUPT_DEFINE = "USE_GPIO_BINARY_SENSOR_INTERRUPT"
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_basic_setup(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
@@ -69,3 +74,62 @@ def test_gpio_binary_sensor_explicit_polling_mode(
|
||||
)
|
||||
|
||||
assert "bs_polling->set_use_interrupt(false);" in main_cpp
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_interrupt_emits_define(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""
|
||||
An interrupt-mode sensor must emit the define that compiles the ISR code,
|
||||
since the platform ISR pin implementation is only built when needed
|
||||
"""
|
||||
generate_main("tests/component_tests/gpio/test_gpio_binary_sensor.yaml")
|
||||
|
||||
assert INTERRUPT_DEFINE in {d.name for d in CORE.defines}
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_polling_omits_define(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""
|
||||
A polling-only config must not emit the interrupt define, so the ISR code
|
||||
(and its reference to ISRInternalGPIOPin) is compiled out
|
||||
"""
|
||||
generate_main("tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml")
|
||||
|
||||
assert INTERRUPT_DEFINE not in {d.name for d in CORE.defines}
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_mixed_modes_emit_define(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""
|
||||
With one interrupt and one polling sensor, the define is emitted and the
|
||||
polling instance still opts out via its setter
|
||||
"""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/gpio/test_gpio_binary_sensor_mixed.yaml"
|
||||
)
|
||||
|
||||
assert INTERRUPT_DEFINE in {d.name for d in CORE.defines}
|
||||
assert "bs_polling->set_use_interrupt(false);" in main_cpp
|
||||
assert "bs_interrupt->set_use_interrupt" not in main_cpp
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_expander_pin_omits_define(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""
|
||||
An expander pin can't use interrupts: final validation falls back to
|
||||
polling and the interrupt define must not be emitted. This is the config
|
||||
that fails to link if the ISR code is compiled without an internal pin
|
||||
"""
|
||||
with caplog.at_level(logging.INFO):
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/gpio/test_gpio_binary_sensor_expander.yaml"
|
||||
)
|
||||
|
||||
assert "bs_expander->set_use_interrupt(false);" in main_cpp
|
||||
assert INTERRUPT_DEFINE not in {d.name for d in CORE.defines}
|
||||
assert "falling back to polling mode" in caplog.text
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
i2c:
|
||||
scl: 16
|
||||
sda: 17
|
||||
|
||||
ch422g:
|
||||
- id: ch422g_hub
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
name: "Expander Sensor"
|
||||
id: bs_expander
|
||||
pin:
|
||||
ch422g: ch422g_hub
|
||||
number: 1
|
||||
mode: INPUT
|
||||
@@ -0,0 +1,17 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: 5
|
||||
name: "Interrupt Sensor"
|
||||
id: bs_interrupt
|
||||
|
||||
- platform: gpio
|
||||
pin: 4
|
||||
name: "Polling Sensor"
|
||||
id: bs_polling
|
||||
use_interrupt: false
|
||||
@@ -3,13 +3,6 @@ binary_sensor:
|
||||
pin: ${binary_sensor_pin}
|
||||
id: gpio_binary_sensor
|
||||
|
||||
# Polling sensor alongside an interrupt sensor: proves the mixed build where
|
||||
# both code paths are compiled and selected per instance at runtime.
|
||||
- platform: gpio
|
||||
pin: ${binary_sensor_pin_2}
|
||||
id: gpio_binary_sensor_polling
|
||||
use_interrupt: false
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin: ${output_pin}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
binary_sensor_pin: GPIO2
|
||||
binary_sensor_pin_2: GPIO10
|
||||
output_pin: GPIO3
|
||||
switch_pin: GPIO4
|
||||
switch_pin_2: GPIO5
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
binary_sensor_pin: GPIO12
|
||||
binary_sensor_pin_2: GPIO18
|
||||
output_pin: GPIO13
|
||||
switch_pin: GPIO14
|
||||
switch_pin_2: GPIO15
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
binary_sensor_pin: GPIO0
|
||||
binary_sensor_pin_2: GPIO4
|
||||
output_pin: GPIO2
|
||||
switch_pin: GPIO15
|
||||
switch_pin_2: GPIO12
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
binary_sensor_pin: GPIO2
|
||||
binary_sensor_pin_2: GPIO8
|
||||
output_pin: GPIO3
|
||||
switch_pin: GPIO4
|
||||
switch_pin_2: GPIO5
|
||||
|
||||
Reference in New Issue
Block a user