mirror of
https://github.com/esphome/esphome.git
synced 2025-08-03 00:47:47 +00:00
Merge branch 'api_errors' into integration
This commit is contained in:
commit
3dd457c471
@ -86,8 +86,8 @@ void APIConnection::start() {
|
||||
APIError err = this->helper_->init();
|
||||
if (err != APIError::OK) {
|
||||
on_fatal_error();
|
||||
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
api_error_to_str(err), errno);
|
||||
ESP_LOGW(TAG, "%s: Helper init failed %s errno=%d", this->get_client_combined_info().c_str(), api_error_to_str(err),
|
||||
errno);
|
||||
return;
|
||||
}
|
||||
this->client_info_ = helper_->getpeername();
|
||||
@ -119,7 +119,7 @@ void APIConnection::loop() {
|
||||
APIError err = this->helper_->loop();
|
||||
if (err != APIError::OK) {
|
||||
on_fatal_error();
|
||||
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
ESP_LOGW(TAG, "%s: Socket operation failed %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
api_error_to_str(err), errno);
|
||||
return;
|
||||
}
|
||||
@ -136,14 +136,8 @@ void APIConnection::loop() {
|
||||
break;
|
||||
} else if (err != APIError::OK) {
|
||||
on_fatal_error();
|
||||
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
|
||||
ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
|
||||
} else if (err == APIError::CONNECTION_CLOSED) {
|
||||
ESP_LOGW(TAG, "%s: Connection closed", this->get_client_combined_info().c_str());
|
||||
} else {
|
||||
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
api_error_to_str(err), errno);
|
||||
}
|
||||
ESP_LOGW(TAG, "%s: Reading failed %s errno=%d", this->get_client_combined_info().c_str(), api_error_to_str(err),
|
||||
errno);
|
||||
return;
|
||||
} else {
|
||||
this->last_traffic_ = now;
|
||||
@ -1612,7 +1606,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
||||
APIError err = this->helper_->loop();
|
||||
if (err != APIError::OK) {
|
||||
on_fatal_error();
|
||||
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
ESP_LOGW(TAG, "%s: Socket operation failed %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
api_error_to_str(err), errno);
|
||||
return false;
|
||||
}
|
||||
@ -1633,12 +1627,8 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
|
||||
return false;
|
||||
if (err != APIError::OK) {
|
||||
on_fatal_error();
|
||||
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
|
||||
ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
|
||||
} else {
|
||||
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
api_error_to_str(err), errno);
|
||||
}
|
||||
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
api_error_to_str(err), errno);
|
||||
return false;
|
||||
}
|
||||
// Do not set last_traffic_ on send
|
||||
@ -1646,11 +1636,11 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
|
||||
}
|
||||
void APIConnection::on_unauthenticated_access() {
|
||||
this->on_fatal_error();
|
||||
ESP_LOGD(TAG, "%s requested access without authentication", this->get_client_combined_info().c_str());
|
||||
ESP_LOGD(TAG, "%s access without authentication", this->get_client_combined_info().c_str());
|
||||
}
|
||||
void APIConnection::on_no_setup_connection() {
|
||||
this->on_fatal_error();
|
||||
ESP_LOGD(TAG, "%s requested access without full connection", this->get_client_combined_info().c_str());
|
||||
ESP_LOGD(TAG, "%s access without full connection", this->get_client_combined_info().c_str());
|
||||
}
|
||||
void APIConnection::on_fatal_error() {
|
||||
this->helper_->close();
|
||||
@ -1815,12 +1805,8 @@ void APIConnection::process_batch_() {
|
||||
this->helper_->write_protobuf_packets(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info);
|
||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||
on_fatal_error();
|
||||
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
|
||||
ESP_LOGW(TAG, "%s: Connection reset during batch write", this->get_client_combined_info().c_str());
|
||||
} else {
|
||||
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(),
|
||||
api_error_to_str(err), errno);
|
||||
}
|
||||
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(), api_error_to_str(err),
|
||||
errno);
|
||||
}
|
||||
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
|
@ -426,7 +426,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
|
||||
ESP_LOGD(TAG, "Noise PSK saved");
|
||||
if (make_active) {
|
||||
this->set_timeout(100, [this, psk]() {
|
||||
ESP_LOGW(TAG, "Disconnecting all clients to reset connections");
|
||||
ESP_LOGW(TAG, "Disconnecting all clients to reset PSK");
|
||||
this->set_noise_psk(psk);
|
||||
for (auto &c : this->clients_) {
|
||||
c->send_message(DisconnectRequest());
|
||||
|
@ -1,11 +1,16 @@
|
||||
import logging
|
||||
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_PIN
|
||||
from esphome.const import CONF_ID, CONF_NAME, CONF_NUMBER, CONF_PIN
|
||||
from esphome.core import CORE
|
||||
|
||||
from .. import gpio_ns
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
GPIOBinarySensor = gpio_ns.class_(
|
||||
"GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
@ -41,6 +46,22 @@ async def to_code(config):
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
|
||||
cg.add(var.set_use_interrupt(config[CONF_USE_INTERRUPT]))
|
||||
if config[CONF_USE_INTERRUPT]:
|
||||
# Check for ESP8266 GPIO16 interrupt limitation
|
||||
# GPIO16 on ESP8266 is a special pin that doesn't support interrupts through
|
||||
# the Arduino attachInterrupt() function. This is the only known GPIO pin
|
||||
# across all supported platforms that has this limitation, so we handle it
|
||||
# here instead of in the platform-specific code.
|
||||
use_interrupt = config[CONF_USE_INTERRUPT]
|
||||
if use_interrupt and CORE.is_esp8266 and config[CONF_PIN][CONF_NUMBER] == 16:
|
||||
_LOGGER.warning(
|
||||
"GPIO binary_sensor '%s': GPIO16 on ESP8266 doesn't support interrupts. "
|
||||
"Falling back to polling mode (same as in ESPHome <2025.7). "
|
||||
"The sensor will work exactly as before, but other pins have better "
|
||||
"performance with interrupts.",
|
||||
config.get(CONF_NAME, config[CONF_ID]),
|
||||
)
|
||||
use_interrupt = False
|
||||
|
||||
cg.add(var.set_use_interrupt(use_interrupt))
|
||||
if use_interrupt:
|
||||
cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE]))
|
||||
|
@ -29,9 +29,9 @@ from ..defines import (
|
||||
)
|
||||
from ..helpers import add_lv_use, lvgl_components_required
|
||||
from ..lv_validation import (
|
||||
angle,
|
||||
get_end_value,
|
||||
get_start_value,
|
||||
lv_angle,
|
||||
lv_bool,
|
||||
lv_color,
|
||||
lv_float,
|
||||
@ -162,7 +162,7 @@ SCALE_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_RANGE_FROM, default=0.0): cv.float_,
|
||||
cv.Optional(CONF_RANGE_TO, default=100.0): cv.float_,
|
||||
cv.Optional(CONF_ANGLE_RANGE, default=270): cv.int_range(0, 360),
|
||||
cv.Optional(CONF_ROTATION): angle,
|
||||
cv.Optional(CONF_ROTATION): lv_angle,
|
||||
cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA),
|
||||
}
|
||||
)
|
||||
@ -187,7 +187,7 @@ class MeterType(WidgetType):
|
||||
for scale_conf in config.get(CONF_SCALES, ()):
|
||||
rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2
|
||||
if CONF_ROTATION in scale_conf:
|
||||
rotation = scale_conf[CONF_ROTATION] // 10
|
||||
rotation = await lv_angle.process(scale_conf[CONF_ROTATION])
|
||||
with LocalVariable(
|
||||
"meter_var", "lv_meter_scale_t", lv_expr.meter_add_scale(var)
|
||||
) as meter_var:
|
||||
@ -205,21 +205,20 @@ class MeterType(WidgetType):
|
||||
var,
|
||||
meter_var,
|
||||
ticks[CONF_COUNT],
|
||||
ticks[CONF_WIDTH],
|
||||
ticks[CONF_LENGTH],
|
||||
await size.process(ticks[CONF_WIDTH]),
|
||||
await size.process(ticks[CONF_LENGTH]),
|
||||
color,
|
||||
)
|
||||
if CONF_MAJOR in ticks:
|
||||
major = ticks[CONF_MAJOR]
|
||||
color = await lv_color.process(major[CONF_COLOR])
|
||||
lv.meter_set_scale_major_ticks(
|
||||
var,
|
||||
meter_var,
|
||||
major[CONF_STRIDE],
|
||||
major[CONF_WIDTH],
|
||||
major[CONF_LENGTH],
|
||||
color,
|
||||
major[CONF_LABEL_GAP],
|
||||
await size.process(major[CONF_WIDTH]),
|
||||
await size.process(major[CONF_LENGTH]),
|
||||
await lv_color.process(major[CONF_COLOR]),
|
||||
await size.process(major[CONF_LABEL_GAP]),
|
||||
)
|
||||
for indicator in scale_conf.get(CONF_INDICATORS, ()):
|
||||
(t, v) = next(iter(indicator.items()))
|
||||
@ -233,7 +232,11 @@ class MeterType(WidgetType):
|
||||
lv_assign(
|
||||
ivar,
|
||||
lv_expr.meter_add_needle_line(
|
||||
var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD]
|
||||
var,
|
||||
meter_var,
|
||||
await size.process(v[CONF_WIDTH]),
|
||||
color,
|
||||
await size.process(v[CONF_R_MOD]),
|
||||
),
|
||||
)
|
||||
if t == CONF_ARC:
|
||||
@ -241,7 +244,11 @@ class MeterType(WidgetType):
|
||||
lv_assign(
|
||||
ivar,
|
||||
lv_expr.meter_add_arc(
|
||||
var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD]
|
||||
var,
|
||||
meter_var,
|
||||
await size.process(v[CONF_WIDTH]),
|
||||
color,
|
||||
await size.process(v[CONF_R_MOD]),
|
||||
),
|
||||
)
|
||||
if t == CONF_TICK_STYLE:
|
||||
@ -257,7 +264,7 @@ class MeterType(WidgetType):
|
||||
color_start,
|
||||
color_end,
|
||||
v[CONF_LOCAL],
|
||||
v[CONF_WIDTH],
|
||||
size.process(v[CONF_WIDTH]),
|
||||
),
|
||||
)
|
||||
if t == CONF_IMAGE:
|
||||
|
69
tests/component_tests/gpio/test_gpio_binary_sensor.py
Normal file
69
tests/component_tests/gpio/test_gpio_binary_sensor.py
Normal file
@ -0,0 +1,69 @@
|
||||
"""Tests for the GPIO binary sensor component."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_basic_setup(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""
|
||||
When the GPIO binary sensor is set in the yaml file, it should be registered in main
|
||||
"""
|
||||
main_cpp = generate_main("tests/component_tests/gpio/test_gpio_binary_sensor.yaml")
|
||||
|
||||
assert "new gpio::GPIOBinarySensor();" in main_cpp
|
||||
assert "App.register_binary_sensor" in main_cpp
|
||||
assert "bs_gpio->set_use_interrupt(true);" in main_cpp
|
||||
assert "bs_gpio->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_esp8266_gpio16_disables_interrupt(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""
|
||||
Test that ESP8266 GPIO16 automatically disables interrupt mode with a warning
|
||||
"""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml"
|
||||
)
|
||||
|
||||
# Check that interrupt is disabled for GPIO16
|
||||
assert "bs_gpio16->set_use_interrupt(false);" in main_cpp
|
||||
|
||||
# Check that the warning was logged
|
||||
assert "GPIO16 on ESP8266 doesn't support interrupts" in caplog.text
|
||||
assert "Falling back to polling mode" in caplog.text
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_esp8266_other_pins_use_interrupt(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""
|
||||
Test that ESP8266 pins other than GPIO16 still use interrupt mode
|
||||
"""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml"
|
||||
)
|
||||
|
||||
# GPIO5 should still use interrupts
|
||||
assert "bs_gpio5->set_use_interrupt(true);" in main_cpp
|
||||
assert "bs_gpio5->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp
|
||||
|
||||
|
||||
def test_gpio_binary_sensor_explicit_polling_mode(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""
|
||||
Test that explicitly setting use_interrupt: false works
|
||||
"""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml"
|
||||
)
|
||||
|
||||
assert "bs_polling->set_use_interrupt(false);" in main_cpp
|
11
tests/component_tests/gpio/test_gpio_binary_sensor.yaml
Normal file
11
tests/component_tests/gpio/test_gpio_binary_sensor.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: 5
|
||||
name: "Test GPIO Binary Sensor"
|
||||
id: bs_gpio
|
@ -0,0 +1,20 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp8266:
|
||||
board: d1_mini
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: 16
|
||||
mode: INPUT_PULLDOWN_16
|
||||
name: "GPIO16 Touch Sensor"
|
||||
id: bs_gpio16
|
||||
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: 5
|
||||
mode: INPUT_PULLUP
|
||||
name: "GPIO5 Button"
|
||||
id: bs_gpio5
|
@ -0,0 +1,12 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: 5
|
||||
name: "Polling Mode Sensor"
|
||||
id: bs_polling
|
||||
use_interrupt: false
|
@ -919,21 +919,21 @@ lvgl:
|
||||
text_color: 0xFFFFFF
|
||||
scales:
|
||||
- ticks:
|
||||
width: 1
|
||||
width: !lambda return 1;
|
||||
count: 61
|
||||
length: 20
|
||||
length: 20%
|
||||
color: 0xFFFFFF
|
||||
range_from: 0
|
||||
range_to: 60
|
||||
angle_range: 360
|
||||
rotation: 270
|
||||
rotation: !lambda return 2700;
|
||||
indicators:
|
||||
- line:
|
||||
opa: 50%
|
||||
id: minute_hand
|
||||
color: 0xFF0000
|
||||
r_mod: -1
|
||||
width: 3
|
||||
r_mod: !lambda return -1;
|
||||
width: !lambda return 3;
|
||||
-
|
||||
angle_range: 330
|
||||
rotation: 300
|
||||
|
Loading…
x
Reference in New Issue
Block a user