[uart] Validate parity and stop bits correctly (#20161)

This commit is contained in:
Max Slotov
2026-10-05 13:21:48 +11:00
committed by GitHub
parent 88378ab354
commit 645144ad4d
3 changed files with 64 additions and 2 deletions
+2 -1
View File
@@ -489,7 +489,8 @@ def final_validate_device_schema(
return value
def validate_stop_bits(value):
if value != stop_bits:
# usb_uart channels store stop bits as strings ("1", "1.5", "2").
if float(value) != stop_bits:
raise cv.Invalid(
f"Component {name} requires {stop_bits} stop bits for the uart referenced by {uart_bus}"
)
+16 -1
View File
@@ -155,7 +155,22 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
// Re-apply the current line settings (baud, parity, etc) to this already-open channel.
void load_settings(bool dump_config) override;
using UARTComponent::load_settings; // also bring in the no-arg overload for convenience
void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
void set_parity(UARTParityOptions parity) {
this->parity_ = parity;
// Keep the base-class parity in sync so uart::UARTComponent::get_parity() reports the configured value.
// MARK/SPACE have no uart:: equivalent and report as NONE.
switch (parity) {
case UART_CONFIG_PARITY_EVEN:
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_EVEN);
break;
case UART_CONFIG_PARITY_ODD:
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_ODD);
break;
default:
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_NONE);
break;
}
}
void set_debug(bool debug) { this->debug_ = debug; }
void set_dummy_receiver(bool dummy_receiver) { this->dummy_receiver_ = dummy_receiver; }
void set_debug_prefix(const char *prefix) { this->debug_prefix_ = StringRef(prefix); }
@@ -0,0 +1,46 @@
"""Tests for final validation of UART device requirements against usb_uart channels."""
from pathlib import Path
import pytest
from esphome import config, yaml_util
from esphome.core import CORE
USB_UART_CONFIG = """
esphome:
name: usb-uart-final-validate
esp32:
variant: esp32s3
framework:
type: esp-idf
usb_uart:
- type: ch340
channels:
- id: meter_uart
baud_rate: 1200
stop_bits: {stop_bits}
sensor:
- platform: kamstrup_kmp
uart_id: meter_uart
heat_energy:
name: Heat energy
"""
@pytest.mark.parametrize(
("stop_bits", "valid"), [("2", True), ("1", False), ("1.5", False)]
)
def test_stop_bits_requirement_on_usb_uart_channel(
stop_bits: str, valid: bool, tmp_path: Path
) -> None:
"""usb_uart stores stop bits as strings; they must still satisfy a numeric requirement."""
path = tmp_path / "usb_uart.yaml"
path.write_text(USB_UART_CONFIG.format(stop_bits=stop_bits), encoding="utf-8")
CORE.config_path = path
result = config.validate_config(yaml_util.load_yaml(path), {})
errors = [str(err) for err in result.errors]
if valid:
assert errors == []
else:
assert any("requires 2 stop bits" in err for err in errors), errors