mirror of
https://github.com/esphome/esphome.git
synced 2026-09-03 19:46:02 +00:00
Merge remote-tracking branch 'origin/dev' into integration
This commit is contained in:
@@ -766,32 +766,38 @@ void LD2412Component::get_distance_resolution_() { this->send_command_(CMD_QUERY
|
||||
void LD2412Component::query_light_control_() { this->send_command_(CMD_QUERY_LIGHT_CONTROL, nullptr, 0); }
|
||||
|
||||
void LD2412Component::set_basic_config() {
|
||||
uint8_t min_gate = 1;
|
||||
uint8_t max_gate = TOTAL_GATES;
|
||||
uint16_t timeout = DEFAULT_PRESENCE_TIMEOUT;
|
||||
uint8_t out_pin_level = 0x01;
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
if (!this->min_distance_gate_number_->has_state() || !this->max_distance_gate_number_->has_state() ||
|
||||
!this->timeout_number_->has_state()) {
|
||||
return;
|
||||
if (this->min_distance_gate_number_ != nullptr) {
|
||||
if (!this->min_distance_gate_number_->has_state())
|
||||
return;
|
||||
min_gate = static_cast<int>(this->min_distance_gate_number_->state);
|
||||
}
|
||||
if (this->max_distance_gate_number_ != nullptr) {
|
||||
if (!this->max_distance_gate_number_->has_state())
|
||||
return;
|
||||
max_gate = static_cast<int>(this->max_distance_gate_number_->state) + 1;
|
||||
}
|
||||
if (this->timeout_number_ != nullptr) {
|
||||
if (!this->timeout_number_->has_state())
|
||||
return;
|
||||
timeout = static_cast<int>(this->timeout_number_->state);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
if (!this->out_pin_level_select_->has_state()) {
|
||||
return;
|
||||
if (this->out_pin_level_select_ != nullptr) {
|
||||
if (!this->out_pin_level_select_->has_state())
|
||||
return;
|
||||
out_pin_level = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option().c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t value[5] = {
|
||||
#ifdef USE_NUMBER
|
||||
lowbyte(static_cast<int>(this->min_distance_gate_number_->state)),
|
||||
lowbyte(static_cast<int>(this->max_distance_gate_number_->state) + 1),
|
||||
lowbyte(static_cast<int>(this->timeout_number_->state)),
|
||||
highbyte(static_cast<int>(this->timeout_number_->state)),
|
||||
#else
|
||||
1, TOTAL_GATES, DEFAULT_PRESENCE_TIMEOUT, 0,
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option().c_str()),
|
||||
#else
|
||||
0x01, // Default value if not using select
|
||||
#endif
|
||||
lowbyte(min_gate), lowbyte(max_gate), lowbyte(timeout), highbyte(timeout), out_pin_level,
|
||||
};
|
||||
this->set_config_mode_(true);
|
||||
this->send_command_(CMD_BASIC_CONF, value, sizeof(value));
|
||||
|
||||
@@ -35,9 +35,11 @@ LockStateForwarder = lock_ns.class_("LockStateForwarder")
|
||||
LockState = lock_ns.enum("LockState")
|
||||
|
||||
LOCK_STATES = {
|
||||
"OPEN": LockState.LOCK_STATE_OPEN,
|
||||
"LOCKED": LockState.LOCK_STATE_LOCKED,
|
||||
"UNLOCKED": LockState.LOCK_STATE_UNLOCKED,
|
||||
"JAMMED": LockState.LOCK_STATE_JAMMED,
|
||||
"OPENING": LockState.LOCK_STATE_OPENING,
|
||||
"LOCKING": LockState.LOCK_STATE_LOCKING,
|
||||
"UNLOCKING": LockState.LOCK_STATE_UNLOCKING,
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ namespace esphome::lock {
|
||||
|
||||
static const char *const TAG = "lock";
|
||||
|
||||
// Lock state strings indexed by LockState enum (0-5): NONE(UNKNOWN), LOCKED, UNLOCKED, JAMMED, LOCKING, UNLOCKING
|
||||
// Lock state strings indexed by LockState enum.
|
||||
// Index 0 is UNKNOWN (for LOCK_STATE_NONE), also used as fallback for out-of-range
|
||||
PROGMEM_STRING_TABLE(LockStateStrings, "UNKNOWN", "LOCKED", "UNLOCKED", "JAMMED", "LOCKING", "UNLOCKING");
|
||||
PROGMEM_STRING_TABLE(LockStateStrings, "UNKNOWN", "LOCKED", "UNLOCKED", "JAMMED", "LOCKING", "UNLOCKING", "OPENING",
|
||||
"OPEN");
|
||||
|
||||
const LogString *lock_state_to_string(LockState state) {
|
||||
return LockStateStrings::get_log_str(static_cast<uint8_t>(state), 0);
|
||||
@@ -74,12 +75,16 @@ LockCall &LockCall::set_state(optional<LockState> state) {
|
||||
return *this;
|
||||
}
|
||||
LockCall &LockCall::set_state(const char *state) {
|
||||
if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("LOCKED")) == 0) {
|
||||
if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("OPEN")) == 0) {
|
||||
this->set_state(LOCK_STATE_OPEN);
|
||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("LOCKED")) == 0) {
|
||||
this->set_state(LOCK_STATE_LOCKED);
|
||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("UNLOCKED")) == 0) {
|
||||
this->set_state(LOCK_STATE_UNLOCKED);
|
||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("JAMMED")) == 0) {
|
||||
this->set_state(LOCK_STATE_JAMMED);
|
||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("OPENING")) == 0) {
|
||||
this->set_state(LOCK_STATE_OPENING);
|
||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("LOCKING")) == 0) {
|
||||
this->set_state(LOCK_STATE_LOCKING);
|
||||
} else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("UNLOCKING")) == 0) {
|
||||
|
||||
@@ -26,7 +26,9 @@ enum LockState : uint8_t {
|
||||
LOCK_STATE_UNLOCKED = 2,
|
||||
LOCK_STATE_JAMMED = 3,
|
||||
LOCK_STATE_LOCKING = 4,
|
||||
LOCK_STATE_UNLOCKING = 5
|
||||
LOCK_STATE_UNLOCKING = 5,
|
||||
LOCK_STATE_OPENING = 6,
|
||||
LOCK_STATE_OPEN = 7,
|
||||
};
|
||||
const LogString *lock_state_to_string(LockState state);
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ static const uint8_t QMC5883L_REGISTER_CONTROL_1 = 0x09;
|
||||
static const uint8_t QMC5883L_REGISTER_CONTROL_2 = 0x0A;
|
||||
static const uint8_t QMC5883L_REGISTER_PERIOD = 0x0B;
|
||||
|
||||
void IRAM_ATTR QMC5883LComponent::gpio_intr(QMC5883LComponent *arg) { arg->enable_loop_soon_any_context(); }
|
||||
|
||||
void QMC5883LComponent::setup() {
|
||||
// Soft Reset
|
||||
if (!this->write_byte(QMC5883L_REGISTER_CONTROL_2, 1 << 7)) {
|
||||
@@ -35,6 +37,12 @@ void QMC5883LComponent::setup() {
|
||||
|
||||
if (this->drdy_pin_) {
|
||||
this->drdy_pin_->setup();
|
||||
if (this->drdy_pin_->is_internal()) {
|
||||
static_cast<InternalGPIOPin *>(this->drdy_pin_)
|
||||
->attach_interrupt(&QMC5883LComponent::gpio_intr, this, gpio::INTERRUPT_RISING_EDGE);
|
||||
this->drdy_use_isr_ = true;
|
||||
this->stop_poller();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t control_1 = 0;
|
||||
@@ -65,8 +73,8 @@ void QMC5883LComponent::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->get_update_interval() < App.get_loop_interval()) {
|
||||
high_freq_.start();
|
||||
if (!this->drdy_use_isr_ && this->get_update_interval() < App.get_loop_interval()) {
|
||||
this->high_freq_.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,16 +92,32 @@ void QMC5883LComponent::dump_config() {
|
||||
LOG_SENSOR(" ", "Heading", this->heading_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
LOG_PIN(" DRDY Pin: ", this->drdy_pin_);
|
||||
if (this->drdy_pin_ != nullptr) {
|
||||
ESP_LOGCONFIG(TAG, " DRDY mode: %s",
|
||||
this->drdy_use_isr_ ? LOG_STR_LITERAL("interrupt") : LOG_STR_LITERAL("polling"));
|
||||
}
|
||||
}
|
||||
|
||||
void QMC5883LComponent::update() {
|
||||
i2c::ErrorCode err;
|
||||
uint8_t status = false;
|
||||
|
||||
// If DRDY pin is configured and the data is not ready return.
|
||||
// If DRDY is on an external expander we keep the polling path and early-return
|
||||
// if data is not ready yet. Internal DRDY pins take the ISR path via loop().
|
||||
if (this->drdy_pin_ && !this->drdy_pin_->digital_read()) {
|
||||
return;
|
||||
}
|
||||
this->read_sensor_();
|
||||
}
|
||||
|
||||
void QMC5883LComponent::loop() {
|
||||
this->disable_loop();
|
||||
if (!this->drdy_use_isr_ || !this->drdy_pin_->digital_read()) {
|
||||
return;
|
||||
}
|
||||
this->read_sensor_();
|
||||
}
|
||||
|
||||
void QMC5883LComponent::read_sensor_() {
|
||||
i2c::ErrorCode err;
|
||||
uint8_t status = false;
|
||||
|
||||
// Status byte gets cleared when data is read, so we have to read this first.
|
||||
// If status and two axes are desired, it's possible to save one byte of traffic by enabling
|
||||
|
||||
@@ -32,6 +32,7 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
void loop() override;
|
||||
|
||||
void set_drdy_pin(GPIOPin *pin) { drdy_pin_ = pin; }
|
||||
void set_datarate(QMC5883LDatarate datarate) { datarate_ = datarate; }
|
||||
@@ -44,6 +45,9 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
|
||||
protected:
|
||||
static void IRAM_ATTR gpio_intr(QMC5883LComponent *arg);
|
||||
void read_sensor_();
|
||||
|
||||
QMC5883LDatarate datarate_{QMC5883L_DATARATE_10_HZ};
|
||||
QMC5883LRange range_{QMC5883L_RANGE_200_UT};
|
||||
QMC5883LOversampling oversampling_{QMC5883L_SAMPLING_512};
|
||||
@@ -53,6 +57,7 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||
sensor::Sensor *heading_sensor_{nullptr};
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
GPIOPin *drdy_pin_{nullptr};
|
||||
bool drdy_use_isr_{false};
|
||||
enum ErrorCode {
|
||||
NONE = 0,
|
||||
COMMUNICATION_FAILED,
|
||||
|
||||
@@ -3,6 +3,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import text
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INITIAL_VALUE,
|
||||
CONF_LAMBDA,
|
||||
CONF_MAX_LENGTH,
|
||||
@@ -12,6 +13,7 @@ from esphome.const import (
|
||||
CONF_RESTORE_VALUE,
|
||||
CONF_SET_ACTION,
|
||||
)
|
||||
from esphome.core import ID
|
||||
|
||||
from .. import template_ns
|
||||
|
||||
@@ -84,8 +86,15 @@ async def to_code(config):
|
||||
if initial_value_config := config.get(CONF_INITIAL_VALUE):
|
||||
cg.add(var.set_initial_value(initial_value_config))
|
||||
if config[CONF_RESTORE_VALUE]:
|
||||
args = cg.TemplateArguments(config[CONF_MAX_LENGTH])
|
||||
saver = TextSaverTemplate.template(args).new()
|
||||
saver_id = ID(
|
||||
f"{config[CONF_ID].id}_value_saver",
|
||||
is_declaration=True,
|
||||
type=TextSaverBase,
|
||||
)
|
||||
saver_type = TextSaverTemplate.template(
|
||||
cg.TemplateArguments(config[CONF_MAX_LENGTH])
|
||||
)
|
||||
saver = cg.Pvariable(saver_id, saver_type.new())
|
||||
cg.add(var.set_value_saver(saver))
|
||||
|
||||
if CONF_SET_ACTION in config:
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
host:
|
||||
|
||||
text:
|
||||
- platform: template
|
||||
name: "Test Text Restore"
|
||||
id: test_text_restore
|
||||
optimistic: true
|
||||
max_length: 10
|
||||
mode: text
|
||||
initial_value: "hello"
|
||||
restore_value: true
|
||||
@@ -0,0 +1,44 @@
|
||||
"""Tests for the template text component."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_template_text_saver_uses_placement_new_with_templated_subclass(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
"""Regression test for template text restore saver using placement new.
|
||||
|
||||
When ``restore_value: true``, the saver is its own Pvariable with
|
||||
placement new: storage is sized for ``TextSaver<MAX_LENGTH>``, the
|
||||
declared pointer stays at ``TemplateTextSaverBase *`` for polymorphism,
|
||||
and the templated subclass constructor runs. A regression would either
|
||||
reintroduce the heap ``new TextSaver<...>()`` expression or size the
|
||||
storage for the base class and silently skip the subclass ctor.
|
||||
"""
|
||||
main_cpp = generate_main(component_config_path("template_text_restore.yaml"))
|
||||
|
||||
# Storage is sized and aligned for the templated subclass.
|
||||
assert "sizeof(template_::TextSaver<10>)" in main_cpp
|
||||
assert "alignas(template_::TextSaver<10>)" in main_cpp
|
||||
# Pointer declared as base type for polymorphism.
|
||||
assert (
|
||||
"static template_::TemplateTextSaverBase *const test_text_restore_value_saver"
|
||||
in main_cpp
|
||||
)
|
||||
# Placement new runs the templated subclass constructor.
|
||||
assert "new(test_text_restore_value_saver) template_::TextSaver<10>()" in main_cpp
|
||||
# Base-class default ctor must NOT be used.
|
||||
assert (
|
||||
"new(test_text_restore_value_saver) template_::TemplateTextSaverBase()"
|
||||
not in main_cpp
|
||||
)
|
||||
# No heap `new TextSaver<...>()` left over — the pre-fix pattern.
|
||||
assert "new template_::TextSaver<" not in main_cpp
|
||||
# Saver is wired into the text component.
|
||||
assert (
|
||||
"test_text_restore->set_value_saver(test_text_restore_value_saver)" in main_cpp
|
||||
)
|
||||
Reference in New Issue
Block a user