mirror of
https://github.com/esphome/esphome.git
synced 2025-11-04 17:11:51 +00:00
Compare commits
1 Commits
template_l
...
qualify_mi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9b08491cc |
@@ -16,12 +16,7 @@ from esphome.const import (
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import (
|
||||
LambdaExpression,
|
||||
MockObj,
|
||||
MockObjClass,
|
||||
TemplateArgsType,
|
||||
)
|
||||
from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.types import ConfigType
|
||||
from esphome.util import Registry
|
||||
@@ -105,36 +100,6 @@ LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
|
||||
ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
|
||||
|
||||
|
||||
def new_lambda_pvariable(
|
||||
id_obj: ID,
|
||||
lambda_expr: LambdaExpression,
|
||||
stateless_class: MockObjClass,
|
||||
template_arg: cg.TemplateArguments | None = None,
|
||||
) -> MockObj:
|
||||
"""Create Pvariable for lambda, using stateless class if applicable.
|
||||
|
||||
Combines ID selection and Pvariable creation in one call. For stateless
|
||||
lambdas (empty capture), uses function pointer instead of std::function.
|
||||
|
||||
Args:
|
||||
id_obj: The ID object (action_id, condition_id, or filter_id)
|
||||
lambda_expr: The lambda expression object
|
||||
stateless_class: The stateless class to use for stateless lambdas
|
||||
template_arg: Optional template arguments (for actions/conditions)
|
||||
|
||||
Returns:
|
||||
The created Pvariable
|
||||
"""
|
||||
# For stateless lambdas, use function pointer instead of std::function
|
||||
if lambda_expr.capture == "":
|
||||
id_obj = id_obj.copy()
|
||||
id_obj.type = stateless_class
|
||||
|
||||
if template_arg is not None:
|
||||
return cg.new_Pvariable(id_obj, template_arg, lambda_expr)
|
||||
return cg.new_Pvariable(id_obj, lambda_expr)
|
||||
|
||||
|
||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||
if extra_schema is None:
|
||||
extra_schema = {}
|
||||
|
||||
@@ -304,13 +304,9 @@ def _format_framework_arduino_version(ver: cv.Version) -> str:
|
||||
def _format_framework_espidf_version(ver: cv.Version, release: str) -> str:
|
||||
# format the given espidf (https://github.com/pioarduino/esp-idf/releases) version to
|
||||
# a PIO platformio/framework-espidf value
|
||||
if ver == cv.Version(5, 4, 3) or ver >= cv.Version(5, 5, 1):
|
||||
ext = "tar.xz"
|
||||
else:
|
||||
ext = "zip"
|
||||
if release:
|
||||
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.{ext}"
|
||||
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.{ext}"
|
||||
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.zip"
|
||||
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.zip"
|
||||
|
||||
|
||||
def _is_framework_url(source: str) -> str:
|
||||
@@ -359,7 +355,6 @@ ESP_IDF_FRAMEWORK_VERSION_LOOKUP = {
|
||||
ESP_IDF_PLATFORM_VERSION_LOOKUP = {
|
||||
cv.Version(5, 5, 1): cv.Version(55, 3, 31, "1"),
|
||||
cv.Version(5, 5, 0): cv.Version(55, 3, 31, "1"),
|
||||
cv.Version(5, 4, 3): cv.Version(55, 3, 32),
|
||||
cv.Version(5, 4, 2): cv.Version(54, 3, 21, "2"),
|
||||
cv.Version(5, 4, 1): cv.Version(54, 3, 21, "2"),
|
||||
cv.Version(5, 4, 0): cv.Version(54, 3, 21, "2"),
|
||||
|
||||
@@ -41,12 +41,12 @@ class ESP32InternalGPIOPin : public InternalGPIOPin {
|
||||
// - 1 byte padding for alignment
|
||||
// - 4 bytes for vtable pointer
|
||||
uint8_t pin_; // GPIO pin number (0-255, actual max ~54 on ESP32)
|
||||
gpio::Flags flags_{}; // GPIO flags (1 byte)
|
||||
gpio::Flags flags_; // GPIO flags (1 byte)
|
||||
struct PinFlags {
|
||||
uint8_t inverted : 1; // Invert pin logic (1 bit)
|
||||
uint8_t drive_strength : 2; // Drive strength 0-3 (2 bits)
|
||||
uint8_t reserved : 5; // Reserved for future use (5 bits)
|
||||
} pin_flags_{}; // Total: 1 byte
|
||||
} pin_flags_; // Total: 1 byte
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool isr_service_installed;
|
||||
};
|
||||
|
||||
@@ -223,10 +223,7 @@ async def esp32_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}")))
|
||||
# Only set if true to avoid bloating setup() function
|
||||
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||
if config[CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
if CONF_DRIVE_STRENGTH in config:
|
||||
cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
|
||||
@@ -29,8 +29,8 @@ class ESP8266GPIOPin : public InternalGPIOPin {
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
gpio::Flags flags_{};
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
};
|
||||
|
||||
} // namespace esp8266
|
||||
|
||||
@@ -165,10 +165,7 @@ async def esp8266_pin_to_code(config):
|
||||
num = config[CONF_NUMBER]
|
||||
mode = config[CONF_MODE]
|
||||
cg.add(var.set_pin(num))
|
||||
# Only set if true to avoid bloating setup() function
|
||||
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||
if config[CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(mode)))
|
||||
if num < 16:
|
||||
initial_state: PinInitialState = CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES][
|
||||
|
||||
@@ -28,8 +28,8 @@ class HostGPIOPin : public InternalGPIOPin {
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
gpio::Flags flags_{};
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
};
|
||||
|
||||
} // namespace host
|
||||
|
||||
@@ -57,9 +57,6 @@ async def host_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
# Only set if true to avoid bloating setup() function
|
||||
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||
if config[CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
||||
|
||||
@@ -169,7 +169,7 @@ class HttpRequestComponent : public Component {
|
||||
protected:
|
||||
virtual std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method,
|
||||
const std::string &body, const std::list<Header> &request_headers,
|
||||
const std::set<std::string> &collect_headers) = 0;
|
||||
std::set<std::string> collect_headers) = 0;
|
||||
const char *useragent_{nullptr};
|
||||
bool follow_redirects_{};
|
||||
uint16_t redirect_limit_{};
|
||||
|
||||
@@ -17,7 +17,7 @@ static const char *const TAG = "http_request.arduino";
|
||||
std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &url, const std::string &method,
|
||||
const std::string &body,
|
||||
const std::list<Header> &request_headers,
|
||||
const std::set<std::string> &collect_headers) {
|
||||
std::set<std::string> collect_headers) {
|
||||
if (!network::is_connected()) {
|
||||
this->status_momentary_error("failed", 1000);
|
||||
ESP_LOGW(TAG, "HTTP Request failed; Not connected to network");
|
||||
|
||||
@@ -33,7 +33,7 @@ class HttpRequestArduino : public HttpRequestComponent {
|
||||
protected:
|
||||
std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method, const std::string &body,
|
||||
const std::list<Header> &request_headers,
|
||||
const std::set<std::string> &collect_headers) override;
|
||||
std::set<std::string> collect_headers) override;
|
||||
};
|
||||
|
||||
} // namespace http_request
|
||||
|
||||
@@ -20,7 +20,7 @@ static const char *const TAG = "http_request.host";
|
||||
std::shared_ptr<HttpContainer> HttpRequestHost::perform(const std::string &url, const std::string &method,
|
||||
const std::string &body,
|
||||
const std::list<Header> &request_headers,
|
||||
const std::set<std::string> &response_headers) {
|
||||
std::set<std::string> response_headers) {
|
||||
if (!network::is_connected()) {
|
||||
this->status_momentary_error("failed", 1000);
|
||||
ESP_LOGW(TAG, "HTTP Request failed; Not connected to network");
|
||||
|
||||
@@ -20,7 +20,7 @@ class HttpRequestHost : public HttpRequestComponent {
|
||||
public:
|
||||
std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method, const std::string &body,
|
||||
const std::list<Header> &request_headers,
|
||||
const std::set<std::string> &response_headers) override;
|
||||
std::set<std::string> response_headers) override;
|
||||
void set_ca_path(const char *ca_path) { this->ca_path_ = ca_path; }
|
||||
|
||||
protected:
|
||||
|
||||
@@ -55,7 +55,7 @@ esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) {
|
||||
std::shared_ptr<HttpContainer> HttpRequestIDF::perform(const std::string &url, const std::string &method,
|
||||
const std::string &body,
|
||||
const std::list<Header> &request_headers,
|
||||
const std::set<std::string> &collect_headers) {
|
||||
std::set<std::string> collect_headers) {
|
||||
if (!network::is_connected()) {
|
||||
this->status_momentary_error("failed", 1000);
|
||||
ESP_LOGE(TAG, "HTTP Request failed; Not connected to network");
|
||||
|
||||
@@ -39,7 +39,7 @@ class HttpRequestIDF : public HttpRequestComponent {
|
||||
protected:
|
||||
std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method, const std::string &body,
|
||||
const std::list<Header> &request_headers,
|
||||
const std::set<std::string> &collect_headers) override;
|
||||
std::set<std::string> collect_headers) override;
|
||||
// if zero ESP-IDF will use DEFAULT_HTTP_BUF_SIZE
|
||||
uint16_t buffer_size_rx_{};
|
||||
uint16_t buffer_size_tx_{};
|
||||
|
||||
@@ -199,9 +199,6 @@ async def component_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
# Only set if true to avoid bloating setup() function
|
||||
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||
if config[CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
||||
|
||||
@@ -27,8 +27,8 @@ class ArduinoInternalGPIOPin : public InternalGPIOPin {
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
gpio::Flags flags_{};
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
};
|
||||
|
||||
} // namespace libretiny
|
||||
|
||||
@@ -74,9 +74,6 @@ async def nrf52_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
# Only set if true to avoid bloating setup() function
|
||||
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||
if config[CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
||||
|
||||
@@ -38,6 +38,7 @@ void Pipsolar::loop() {
|
||||
}
|
||||
if (this->state_ == STATE_COMMAND_COMPLETE) {
|
||||
if (this->check_incoming_length_(4)) {
|
||||
ESP_LOGD(TAG, "response length for command OK");
|
||||
if (this->check_incoming_crc_()) {
|
||||
// crc ok
|
||||
if (this->read_buffer_[1] == 'A' && this->read_buffer_[2] == 'C' && this->read_buffer_[3] == 'K') {
|
||||
@@ -48,15 +49,15 @@ void Pipsolar::loop() {
|
||||
this->command_queue_[this->command_queue_position_] = std::string("");
|
||||
this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH;
|
||||
this->state_ = STATE_IDLE;
|
||||
|
||||
} else {
|
||||
// crc failed
|
||||
// no log message necessary, check_incoming_crc_() logs
|
||||
this->command_queue_[this->command_queue_position_] = std::string("");
|
||||
this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH;
|
||||
this->state_ = STATE_IDLE;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(TAG, "command %s response length not OK: with length %zu",
|
||||
ESP_LOGD(TAG, "response length for command %s not OK: with length %zu",
|
||||
this->command_queue_[this->command_queue_position_].c_str(), this->read_pos_);
|
||||
this->command_queue_[this->command_queue_position_] = std::string("");
|
||||
this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH;
|
||||
@@ -65,10 +66,46 @@ void Pipsolar::loop() {
|
||||
}
|
||||
|
||||
if (this->state_ == STATE_POLL_CHECKED) {
|
||||
ESP_LOGD(TAG, "poll %s decode", this->enabled_polling_commands_[this->last_polling_command_].command);
|
||||
this->handle_poll_response_(this->enabled_polling_commands_[this->last_polling_command_].identifier,
|
||||
(const char *) this->read_buffer_);
|
||||
switch (this->enabled_polling_commands_[this->last_polling_command_].identifier) {
|
||||
case POLLING_QPIRI:
|
||||
ESP_LOGD(TAG, "Decode QPIRI");
|
||||
handle_qpiri_((const char *) this->read_buffer_);
|
||||
this->state_ = STATE_IDLE;
|
||||
break;
|
||||
case POLLING_QPIGS:
|
||||
ESP_LOGD(TAG, "Decode QPIGS");
|
||||
handle_qpigs_((const char *) this->read_buffer_);
|
||||
this->state_ = STATE_IDLE;
|
||||
break;
|
||||
case POLLING_QMOD:
|
||||
ESP_LOGD(TAG, "Decode QMOD");
|
||||
handle_qmod_((const char *) this->read_buffer_);
|
||||
this->state_ = STATE_IDLE;
|
||||
break;
|
||||
case POLLING_QFLAG:
|
||||
ESP_LOGD(TAG, "Decode QFLAG");
|
||||
handle_qflag_((const char *) this->read_buffer_);
|
||||
this->state_ = STATE_IDLE;
|
||||
break;
|
||||
case POLLING_QPIWS:
|
||||
ESP_LOGD(TAG, "Decode QPIWS");
|
||||
handle_qpiws_((const char *) this->read_buffer_);
|
||||
this->state_ = STATE_IDLE;
|
||||
break;
|
||||
case POLLING_QT:
|
||||
ESP_LOGD(TAG, "Decode QT");
|
||||
handle_qt_((const char *) this->read_buffer_);
|
||||
this->state_ = STATE_IDLE;
|
||||
break;
|
||||
case POLLING_QMN:
|
||||
ESP_LOGD(TAG, "Decode QMN");
|
||||
handle_qmn_((const char *) this->read_buffer_);
|
||||
this->state_ = STATE_IDLE;
|
||||
break;
|
||||
default:
|
||||
this->state_ = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,8 +113,6 @@ void Pipsolar::loop() {
|
||||
if (this->check_incoming_crc_()) {
|
||||
if (this->read_buffer_[0] == '(' && this->read_buffer_[1] == 'N' && this->read_buffer_[2] == 'A' &&
|
||||
this->read_buffer_[3] == 'K') {
|
||||
ESP_LOGD(TAG, "poll %s NACK", this->enabled_polling_commands_[this->last_polling_command_].command);
|
||||
this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier);
|
||||
this->state_ = STATE_IDLE;
|
||||
return;
|
||||
}
|
||||
@@ -86,9 +121,6 @@ void Pipsolar::loop() {
|
||||
this->state_ = STATE_POLL_CHECKED;
|
||||
return;
|
||||
} else {
|
||||
// crc failed
|
||||
// no log message necessary, check_incoming_crc_() logs
|
||||
this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier);
|
||||
this->state_ = STATE_IDLE;
|
||||
}
|
||||
}
|
||||
@@ -126,19 +158,21 @@ void Pipsolar::loop() {
|
||||
// command timeout
|
||||
const char *command = this->command_queue_[this->command_queue_position_].c_str();
|
||||
this->command_start_millis_ = millis();
|
||||
ESP_LOGD(TAG, "command %s timeout", command);
|
||||
ESP_LOGD(TAG, "timeout command from queue: %s", command);
|
||||
this->command_queue_[this->command_queue_position_] = std::string("");
|
||||
this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH;
|
||||
this->state_ = STATE_IDLE;
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
if (this->state_ == STATE_POLL) {
|
||||
if (millis() - this->command_start_millis_ > esphome::pipsolar::Pipsolar::COMMAND_TIMEOUT) {
|
||||
// command timeout
|
||||
ESP_LOGD(TAG, "poll %s timeout", this->enabled_polling_commands_[this->last_polling_command_].command);
|
||||
this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier);
|
||||
ESP_LOGD(TAG, "timeout command to poll: %s",
|
||||
this->enabled_polling_commands_[this->last_polling_command_].command);
|
||||
this->state_ = STATE_IDLE;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,6 +187,7 @@ uint8_t Pipsolar::check_incoming_length_(uint8_t length) {
|
||||
uint8_t Pipsolar::check_incoming_crc_() {
|
||||
uint16_t crc16;
|
||||
crc16 = this->pipsolar_crc_(read_buffer_, read_pos_ - 3);
|
||||
ESP_LOGD(TAG, "checking crc on incoming message");
|
||||
if (((uint8_t) ((crc16) >> 8)) == read_buffer_[read_pos_ - 3] &&
|
||||
((uint8_t) ((crc16) &0xff)) == read_buffer_[read_pos_ - 2]) {
|
||||
ESP_LOGD(TAG, "CRC OK");
|
||||
@@ -239,38 +274,6 @@ void Pipsolar::queue_command(const std::string &command) {
|
||||
ESP_LOGD(TAG, "Command queue full dropping command: %s", command.c_str());
|
||||
}
|
||||
|
||||
void Pipsolar::handle_poll_response_(ENUMPollingCommand polling_command, const char *message) {
|
||||
switch (polling_command) {
|
||||
case POLLING_QPIRI:
|
||||
handle_qpiri_(message);
|
||||
break;
|
||||
case POLLING_QPIGS:
|
||||
handle_qpigs_(message);
|
||||
break;
|
||||
case POLLING_QMOD:
|
||||
handle_qmod_(message);
|
||||
break;
|
||||
case POLLING_QFLAG:
|
||||
handle_qflag_(message);
|
||||
break;
|
||||
case POLLING_QPIWS:
|
||||
handle_qpiws_(message);
|
||||
break;
|
||||
case POLLING_QT:
|
||||
handle_qt_(message);
|
||||
break;
|
||||
case POLLING_QMN:
|
||||
handle_qmn_(message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Pipsolar::handle_poll_error_(ENUMPollingCommand polling_command) {
|
||||
// handlers are designed in a way that an empty message sets all sensors to unknown
|
||||
this->handle_poll_response_(polling_command, "");
|
||||
}
|
||||
|
||||
void Pipsolar::handle_qpiri_(const char *message) {
|
||||
if (this->last_qpiri_) {
|
||||
this->last_qpiri_->publish_state(message);
|
||||
|
||||
@@ -204,9 +204,6 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent {
|
||||
bool send_next_command_();
|
||||
bool send_next_poll_();
|
||||
|
||||
void handle_poll_response_(ENUMPollingCommand polling_command, const char *message);
|
||||
void handle_poll_error_(ENUMPollingCommand polling_command);
|
||||
// these handlers are designed in a way that an empty message sets all sensors to unknown
|
||||
void handle_qpiri_(const char *message);
|
||||
void handle_qpigs_(const char *message);
|
||||
void handle_qmod_(const char *message);
|
||||
|
||||
@@ -4,18 +4,11 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BATTERY_VOLTAGE,
|
||||
CONF_BUS_VOLTAGE,
|
||||
DEVICE_CLASS_APPARENT_POWER,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_FREQUENCY,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_BATTERY,
|
||||
ICON_CURRENT_AC,
|
||||
ICON_FLASH,
|
||||
ICON_GAUGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_HERTZ,
|
||||
@@ -29,10 +22,6 @@ from .. import CONF_PIPSOLAR_ID, PIPSOLAR_COMPONENT_SCHEMA
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
||||
ICON_SOLAR_POWER = "mdi:solar-power"
|
||||
ICON_SOLAR_PANEL = "mdi:solar-panel"
|
||||
ICON_CURRENT_DC = "mdi:current-dc"
|
||||
|
||||
# QPIRI sensors
|
||||
CONF_GRID_RATING_VOLTAGE = "grid_rating_voltage"
|
||||
CONF_GRID_RATING_CURRENT = "grid_rating_current"
|
||||
@@ -86,19 +75,16 @@ TYPES = {
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_GRID_RATING_CURRENT: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_AC_OUTPUT_RATING_VOLTAGE: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_AC_OUTPUT_RATING_FREQUENCY: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HERTZ,
|
||||
@@ -112,12 +98,11 @@ TYPES = {
|
||||
),
|
||||
CONF_AC_OUTPUT_RATING_APPARENT_POWER: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT_AMPS,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_APPARENT_POWER,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_AC_OUTPUT_RATING_ACTIVE_POWER: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
),
|
||||
CONF_BATTERY_RATING_VOLTAGE: sensor.sensor_schema(
|
||||
@@ -146,151 +131,124 @@ TYPES = {
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
),
|
||||
CONF_BATTERY_TYPE: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_CURRENT_MAX_AC_CHARGING_CURRENT: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
),
|
||||
CONF_CURRENT_MAX_CHARGING_CURRENT: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
),
|
||||
CONF_INPUT_VOLTAGE_RANGE: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_OUTPUT_SOURCE_PRIORITY: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_CHARGER_SOURCE_PRIORITY: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_PARALLEL_MAX_NUM: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_MACHINE_TYPE: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_TOPOLOGY: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_OUTPUT_MODE: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_BATTERY_REDISCHARGE_VOLTAGE: sensor.sensor_schema(
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_PV_OK_CONDITION_FOR_PARALLEL: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_PV_POWER_BALANCE: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_GRID_VOLTAGE: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_GRID_FREQUENCY: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HERTZ,
|
||||
icon=ICON_CURRENT_AC,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_FREQUENCY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_AC_OUTPUT_VOLTAGE: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_AC_OUTPUT_FREQUENCY: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HERTZ,
|
||||
icon=ICON_CURRENT_AC,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_FREQUENCY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_AC_OUTPUT_APPARENT_POWER: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT_AMPS,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_APPARENT_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_AC_OUTPUT_ACTIVE_POWER: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_OUTPUT_LOAD_PERCENT: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_GAUGE,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_BUS_VOLTAGE: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
icon=ICON_FLASH,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_BATTERY_VOLTAGE: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
icon=ICON_BATTERY,
|
||||
accuracy_decimals=2,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_BATTERY_CHARGING_CURRENT: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
icon=ICON_CURRENT_DC,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_BATTERY_CAPACITY_PERCENT: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_INVERTER_HEAT_SINK_TEMPERATURE: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_PV_INPUT_CURRENT_FOR_BATTERY: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
icon=ICON_SOLAR_PANEL,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_PV_INPUT_VOLTAGE: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
icon=ICON_SOLAR_PANEL,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_BATTERY_VOLTAGE_SCC: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=2,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_BATTERY_DISCHARGE_CURRENT: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
icon=ICON_CURRENT_DC,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
CONF_BATTERY_VOLTAGE_OFFSET_FOR_FANS_ON: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
@@ -298,14 +256,12 @@ TYPES = {
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
),
|
||||
CONF_EEPROM_VERSION: sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
),
|
||||
CONF_PV_CHARGING_POWER: sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
icon=ICON_SOLAR_POWER,
|
||||
accuracy_decimals=0,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -12,25 +12,6 @@
|
||||
namespace esphome {
|
||||
namespace remote_transmitter {
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
// IDF version 5.5.1 and above is required because of a bug in
|
||||
// the RMT encoder: https://github.com/espressif/esp-idf/issues/17244
|
||||
typedef union { // NOLINT(modernize-use-using)
|
||||
struct {
|
||||
uint16_t duration : 15;
|
||||
uint16_t level : 1;
|
||||
};
|
||||
uint16_t val;
|
||||
} rmt_symbol_half_t;
|
||||
|
||||
struct RemoteTransmitterComponentStore {
|
||||
uint32_t times{0};
|
||||
uint32_t index{0};
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||
public Component
|
||||
#ifdef USE_ESP32
|
||||
@@ -75,14 +56,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||
#ifdef USE_ESP32
|
||||
void configure_rmt_();
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
RemoteTransmitterComponentStore store_{};
|
||||
std::vector<rmt_symbol_half_t> rmt_temp_;
|
||||
#else
|
||||
std::vector<rmt_symbol_word_t> rmt_temp_;
|
||||
#endif
|
||||
uint32_t current_carrier_frequency_{38000};
|
||||
bool initialized_{false};
|
||||
std::vector<rmt_symbol_word_t> rmt_temp_;
|
||||
bool with_dma_{false};
|
||||
bool eot_level_{false};
|
||||
rmt_channel_handle_t channel_{NULL};
|
||||
|
||||
@@ -10,46 +10,6 @@ namespace remote_transmitter {
|
||||
|
||||
static const char *const TAG = "remote_transmitter";
|
||||
|
||||
// Maximum RMT symbol duration (15-bit field)
|
||||
static constexpr uint32_t RMT_SYMBOL_DURATION_MAX = 0x7FFF;
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
static size_t IRAM_ATTR HOT encoder_callback(const void *data, size_t size, size_t written, size_t free,
|
||||
rmt_symbol_word_t *symbols, bool *done, void *arg) {
|
||||
auto *store = static_cast<RemoteTransmitterComponentStore *>(arg);
|
||||
const auto *encoded = static_cast<const rmt_symbol_half_t *>(data);
|
||||
size_t length = size / sizeof(rmt_symbol_half_t);
|
||||
size_t count = 0;
|
||||
|
||||
// copy symbols
|
||||
for (size_t i = 0; i < free; i++) {
|
||||
uint16_t sym_0 = encoded[store->index++].val;
|
||||
if (store->index >= length) {
|
||||
store->index = 0;
|
||||
store->times--;
|
||||
if (store->times == 0) {
|
||||
*done = true;
|
||||
symbols[count++].val = sym_0;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
uint16_t sym_1 = encoded[store->index++].val;
|
||||
if (store->index >= length) {
|
||||
store->index = 0;
|
||||
store->times--;
|
||||
if (store->times == 0) {
|
||||
*done = true;
|
||||
symbols[count++].val = sym_0 | (sym_1 << 16);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
symbols[count++].val = sym_0 | (sym_1 << 16);
|
||||
}
|
||||
*done = false;
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RemoteTransmitterComponent::setup() {
|
||||
this->inverted_ = this->pin_->is_inverted();
|
||||
this->configure_rmt_();
|
||||
@@ -74,17 +34,6 @@ void RemoteTransmitterComponent::dump_config() {
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::digital_write(bool value) {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
rmt_symbol_half_t symbol = {
|
||||
.duration = 1,
|
||||
.level = value,
|
||||
};
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags.eot_level = value;
|
||||
this->store_.times = 1;
|
||||
this->store_.index = 0;
|
||||
#else
|
||||
rmt_symbol_word_t symbol = {
|
||||
.duration0 = 1,
|
||||
.level0 = value,
|
||||
@@ -93,8 +42,8 @@ void RemoteTransmitterComponent::digital_write(bool value) {
|
||||
};
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.loop_count = 0;
|
||||
config.flags.eot_level = value;
|
||||
#endif
|
||||
esp_err_t error = rmt_transmit(this->channel_, this->encoder_, &symbol, sizeof(symbol), &config);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error));
|
||||
@@ -141,20 +90,6 @@ void RemoteTransmitterComponent::configure_rmt_() {
|
||||
gpio_pullup_dis(gpio_num_t(this->pin_->get_pin()));
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
rmt_simple_encoder_config_t encoder;
|
||||
memset(&encoder, 0, sizeof(encoder));
|
||||
encoder.callback = encoder_callback;
|
||||
encoder.arg = &this->store_;
|
||||
encoder.min_chunk_size = 1;
|
||||
error = rmt_new_simple_encoder(&encoder, &this->encoder_);
|
||||
if (error != ESP_OK) {
|
||||
this->error_code_ = error;
|
||||
this->error_string_ = "in rmt_new_simple_encoder";
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
rmt_copy_encoder_config_t encoder;
|
||||
memset(&encoder, 0, sizeof(encoder));
|
||||
error = rmt_new_copy_encoder(&encoder, &this->encoder_);
|
||||
@@ -164,7 +99,6 @@ void RemoteTransmitterComponent::configure_rmt_() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
error = rmt_enable(this->channel_);
|
||||
if (error != ESP_OK) {
|
||||
@@ -196,79 +130,6 @@ void RemoteTransmitterComponent::configure_rmt_() {
|
||||
}
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
|
||||
if (this->is_failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->current_carrier_frequency_ != this->temp_.get_carrier_frequency()) {
|
||||
this->current_carrier_frequency_ = this->temp_.get_carrier_frequency();
|
||||
this->configure_rmt_();
|
||||
}
|
||||
|
||||
this->rmt_temp_.clear();
|
||||
this->rmt_temp_.reserve(this->temp_.get_data().size() + 1);
|
||||
|
||||
// encode any delay at the start of the buffer to simplify the encoder callback
|
||||
// this will be skipped the first time around
|
||||
send_wait = this->from_microseconds_(static_cast<uint32_t>(send_wait));
|
||||
while (send_wait > 0) {
|
||||
int32_t duration = std::min(send_wait, uint32_t(RMT_SYMBOL_DURATION_MAX));
|
||||
this->rmt_temp_.push_back({
|
||||
.duration = static_cast<uint16_t>(duration),
|
||||
.level = static_cast<uint16_t>(this->eot_level_),
|
||||
});
|
||||
send_wait -= duration;
|
||||
}
|
||||
|
||||
// encode data
|
||||
size_t offset = this->rmt_temp_.size();
|
||||
for (int32_t value : this->temp_.get_data()) {
|
||||
bool level = value >= 0;
|
||||
if (!level) {
|
||||
value = -value;
|
||||
}
|
||||
value = this->from_microseconds_(static_cast<uint32_t>(value));
|
||||
while (value > 0) {
|
||||
int32_t duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX));
|
||||
this->rmt_temp_.push_back({
|
||||
.duration = static_cast<uint16_t>(duration),
|
||||
.level = static_cast<uint16_t>(level ^ this->inverted_),
|
||||
});
|
||||
value -= duration;
|
||||
}
|
||||
}
|
||||
|
||||
if ((this->rmt_temp_.data() == nullptr) || this->rmt_temp_.size() <= offset) {
|
||||
ESP_LOGE(TAG, "Empty data");
|
||||
return;
|
||||
}
|
||||
|
||||
this->transmit_trigger_->trigger();
|
||||
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags.eot_level = this->eot_level_;
|
||||
this->store_.times = send_times;
|
||||
this->store_.index = offset;
|
||||
esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(),
|
||||
this->rmt_temp_.size() * sizeof(rmt_symbol_half_t), &config);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error));
|
||||
this->status_set_warning();
|
||||
} else {
|
||||
this->status_clear_warning();
|
||||
}
|
||||
error = rmt_tx_wait_all_done(this->channel_, -1);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
|
||||
this->status_set_warning();
|
||||
}
|
||||
|
||||
this->complete_trigger_->trigger();
|
||||
}
|
||||
#else
|
||||
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
@@ -290,7 +151,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||
val = this->from_microseconds_(static_cast<uint32_t>(val));
|
||||
|
||||
do {
|
||||
int32_t item = std::min(val, int32_t(RMT_SYMBOL_DURATION_MAX));
|
||||
int32_t item = std::min(val, int32_t(32767));
|
||||
val -= item;
|
||||
|
||||
if (rmt_i % 2 == 0) {
|
||||
@@ -319,6 +180,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||
for (uint32_t i = 0; i < send_times; i++) {
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.loop_count = 0;
|
||||
config.flags.eot_level = this->eot_level_;
|
||||
esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(),
|
||||
this->rmt_temp_.size() * sizeof(rmt_symbol_word_t), &config);
|
||||
@@ -338,7 +200,6 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||
}
|
||||
this->complete_trigger_->trigger();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace remote_transmitter
|
||||
} // namespace esphome
|
||||
|
||||
@@ -29,8 +29,8 @@ class RP2040GPIOPin : public InternalGPIOPin {
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
gpio::Flags flags_{};
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
};
|
||||
|
||||
} // namespace rp2040
|
||||
|
||||
@@ -94,9 +94,6 @@ async def rp2040_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
# Only set if true to avoid bloating setup() function
|
||||
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||
if config[CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
||||
|
||||
@@ -878,9 +878,7 @@ async def setup_sensor_core_(var, config):
|
||||
cg.add(var.set_unit_of_measurement(unit_of_measurement))
|
||||
if (accuracy_decimals := config.get(CONF_ACCURACY_DECIMALS)) is not None:
|
||||
cg.add(var.set_accuracy_decimals(accuracy_decimals))
|
||||
# Only set force_update if True (default is False)
|
||||
if config[CONF_FORCE_UPDATE]:
|
||||
cg.add(var.set_force_update(True))
|
||||
cg.add(var.set_force_update(config[CONF_FORCE_UPDATE]))
|
||||
if config.get(CONF_FILTERS): # must exist and not be empty
|
||||
filters = await build_filters(config[CONF_FILTERS])
|
||||
cg.add(var.set_filters(filters))
|
||||
|
||||
@@ -27,7 +27,7 @@ void SNTPComponent::setup() {
|
||||
esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
|
||||
size_t i = 0;
|
||||
for (auto &server : this->servers_) {
|
||||
esp_sntp_setservername(i++, server);
|
||||
esp_sntp_setservername(i++, server.c_str());
|
||||
}
|
||||
esp_sntp_set_sync_interval(this->get_update_interval());
|
||||
esp_sntp_set_time_sync_notification_cb([](struct timeval *tv) {
|
||||
@@ -42,7 +42,7 @@ void SNTPComponent::setup() {
|
||||
|
||||
size_t i = 0;
|
||||
for (auto &server : this->servers_) {
|
||||
sntp_setservername(i++, server);
|
||||
sntp_setservername(i++, server.c_str());
|
||||
}
|
||||
|
||||
#if defined(USE_ESP8266)
|
||||
@@ -59,7 +59,7 @@ void SNTPComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "SNTP Time:");
|
||||
size_t i = 0;
|
||||
for (auto &server : this->servers_) {
|
||||
ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server);
|
||||
ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server.c_str());
|
||||
}
|
||||
}
|
||||
void SNTPComponent::update() {
|
||||
|
||||
@@ -2,14 +2,10 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
#include <array>
|
||||
|
||||
namespace esphome {
|
||||
namespace sntp {
|
||||
|
||||
// Server count is calculated at compile time by Python codegen
|
||||
// SNTP_SERVER_COUNT will always be defined
|
||||
|
||||
/// The SNTP component allows you to configure local timekeeping via Simple Network Time Protocol.
|
||||
///
|
||||
/// \note
|
||||
@@ -18,7 +14,10 @@ namespace sntp {
|
||||
/// \see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
|
||||
class SNTPComponent : public time::RealTimeClock {
|
||||
public:
|
||||
SNTPComponent(const std::array<const char *, SNTP_SERVER_COUNT> &servers) : servers_(servers) {}
|
||||
SNTPComponent(const std::vector<std::string> &servers) : servers_(servers) {}
|
||||
|
||||
// Note: set_servers() has been removed and replaced by a constructor - calling set_servers after setup would
|
||||
// have had no effect anyway, and making the strings immutable avoids the need to strdup their contents.
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
@@ -30,10 +29,7 @@ class SNTPComponent : public time::RealTimeClock {
|
||||
void time_synced();
|
||||
|
||||
protected:
|
||||
// Store const char pointers to string literals
|
||||
// ESP8266: strings in rodata (RAM), but avoids std::string overhead (~24 bytes each)
|
||||
// Other platforms: strings in flash
|
||||
std::array<const char *, SNTP_SERVER_COUNT> servers_;
|
||||
std::vector<std::string> servers_;
|
||||
bool has_time_{false};
|
||||
|
||||
#if defined(USE_ESP32)
|
||||
|
||||
@@ -43,11 +43,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
async def to_code(config):
|
||||
servers = config[CONF_SERVERS]
|
||||
|
||||
# Define server count at compile time
|
||||
cg.add_define("SNTP_SERVER_COUNT", len(servers))
|
||||
|
||||
# Pass string literals to constructor - stored in flash/rodata by compiler
|
||||
var = cg.new_Pvariable(config[CONF_ID], servers)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -10,9 +10,6 @@ from .. import template_ns
|
||||
TemplateBinarySensor = template_ns.class_(
|
||||
"TemplateBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
StatelessTemplateBinarySensor = template_ns.class_(
|
||||
"StatelessTemplateBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
binary_sensor.binary_sensor_schema(TemplateBinarySensor)
|
||||
@@ -29,22 +26,15 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
# Check if we have a lambda first - determines which class to instantiate
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if lamb := config.get(CONF_LAMBDA):
|
||||
# Use new_lambda_pvariable to create either TemplateBinarySensor or StatelessTemplateBinarySensor
|
||||
template_ = await cg.process_lambda(
|
||||
lamb, [], return_type=cg.optional.template(bool)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateBinarySensor
|
||||
)
|
||||
# Manually register as binary sensor since we didn't use new_binary_sensor
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
elif condition := config.get(CONF_CONDITION):
|
||||
# For conditions, create stateful version and set template
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_template(template_))
|
||||
if condition := config.get(CONF_CONDITION):
|
||||
condition = await automation.build_condition(
|
||||
condition, cg.TemplateArguments(), []
|
||||
)
|
||||
@@ -52,10 +42,6 @@ async def to_code(config):
|
||||
f"return {condition.check()};", [], return_type=cg.optional.template(bool)
|
||||
)
|
||||
cg.add(var.set_template(template_))
|
||||
else:
|
||||
# No lambda or condition - just create the base template sensor
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
|
||||
@@ -6,13 +6,18 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.binary_sensor";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateBinarySensorBase<F>::dump_config() {
|
||||
LOG_BINARY_SENSOR("", "Template Binary Sensor", this);
|
||||
}
|
||||
void TemplateBinarySensor::setup() { this->loop(); }
|
||||
|
||||
template class TemplateBinarySensorBase<std::function<optional<bool>()>>;
|
||||
template class TemplateBinarySensorBase<optional<bool> (*)()>;
|
||||
void TemplateBinarySensor::loop() {
|
||||
if (this->f_ == nullptr)
|
||||
return;
|
||||
|
||||
auto s = this->f_();
|
||||
if (s.has_value()) {
|
||||
this->publish_state(*s);
|
||||
}
|
||||
}
|
||||
void TemplateBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Template Binary Sensor", this); }
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -6,41 +6,18 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateBinarySensorBase : public Component, public binary_sensor::BinarySensor {
|
||||
class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void setup() override { this->loop(); }
|
||||
|
||||
void loop() override {
|
||||
if (this->f_ == nullptr)
|
||||
return;
|
||||
auto s = this->f_();
|
||||
if (s.has_value()) {
|
||||
this->publish_state(*s);
|
||||
}
|
||||
}
|
||||
void set_template(std::function<optional<bool>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
protected:
|
||||
F f_;
|
||||
};
|
||||
|
||||
class TemplateBinarySensor : public TemplateBinarySensorBase<std::function<optional<bool>()>> {
|
||||
public:
|
||||
TemplateBinarySensor() { this->f_ = nullptr; }
|
||||
void set_template(std::function<optional<bool>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template binary sensor for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
*/
|
||||
class StatelessTemplateBinarySensor : public TemplateBinarySensorBase<optional<bool> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateBinarySensor(optional<bool> (*f)()) { this->f_ = f; }
|
||||
std::function<optional<bool>()> f_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
|
||||
@@ -23,9 +23,6 @@ from esphome.const import (
|
||||
from .. import template_ns
|
||||
|
||||
TemplateCover = template_ns.class_("TemplateCover", cover.Cover, cg.Component)
|
||||
StatelessTemplateCover = template_ns.class_(
|
||||
"StatelessTemplateCover", cover.Cover, cg.Component
|
||||
)
|
||||
|
||||
TemplateCoverRestoreMode = template_ns.enum("TemplateCoverRestoreMode")
|
||||
RESTORE_MODES = {
|
||||
@@ -66,22 +63,13 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await cover.new_cover(config)
|
||||
await cg.register_component(var, config)
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either TemplateCover or StatelessTemplateCover
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(float)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateCover
|
||||
)
|
||||
# Manually register as cover since we didn't use new_cover
|
||||
await cover.register_cover(var, config)
|
||||
await cg.register_component(var, config)
|
||||
else:
|
||||
# No state lambda - just create the base template cover
|
||||
var = await cover.new_cover(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_state_lambda(template_))
|
||||
if CONF_OPEN_ACTION in config:
|
||||
await automation.build_automation(
|
||||
var.get_open_trigger(), [], config[CONF_OPEN_ACTION]
|
||||
|
||||
@@ -8,8 +8,14 @@ using namespace esphome::cover;
|
||||
|
||||
static const char *const TAG = "template.cover";
|
||||
|
||||
// Template instantiations
|
||||
template<typename StateF, typename TiltF> void TemplateCoverBase<StateF, TiltF>::setup() {
|
||||
TemplateCover::TemplateCover()
|
||||
: open_trigger_(new Trigger<>()),
|
||||
close_trigger_(new Trigger<>),
|
||||
stop_trigger_(new Trigger<>()),
|
||||
toggle_trigger_(new Trigger<>()),
|
||||
position_trigger_(new Trigger<float>()),
|
||||
tilt_trigger_(new Trigger<float>()) {}
|
||||
void TemplateCover::setup() {
|
||||
switch (this->restore_mode_) {
|
||||
case COVER_NO_RESTORE:
|
||||
break;
|
||||
@@ -28,12 +34,43 @@ template<typename StateF, typename TiltF> void TemplateCoverBase<StateF, TiltF>:
|
||||
}
|
||||
}
|
||||
}
|
||||
void TemplateCover::loop() {
|
||||
bool changed = false;
|
||||
|
||||
template<typename StateF, typename TiltF> void TemplateCoverBase<StateF, TiltF>::dump_config() {
|
||||
LOG_COVER("", "Template Cover", this);
|
||||
if (this->state_f_.has_value()) {
|
||||
auto s = (*this->state_f_)();
|
||||
if (s.has_value()) {
|
||||
auto pos = clamp(*s, 0.0f, 1.0f);
|
||||
if (pos != this->position) {
|
||||
this->position = pos;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this->tilt_f_.has_value()) {
|
||||
auto s = (*this->tilt_f_)();
|
||||
if (s.has_value()) {
|
||||
auto tilt = clamp(*s, 0.0f, 1.0f);
|
||||
if (tilt != this->tilt) {
|
||||
this->tilt = tilt;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename StateF, typename TiltF> void TemplateCoverBase<StateF, TiltF>::control(const CoverCall &call) {
|
||||
if (changed)
|
||||
this->publish_state();
|
||||
}
|
||||
void TemplateCover::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void TemplateCover::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
|
||||
void TemplateCover::set_state_lambda(std::function<optional<float>()> &&f) { this->state_f_ = f; }
|
||||
float TemplateCover::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
Trigger<> *TemplateCover::get_open_trigger() const { return this->open_trigger_; }
|
||||
Trigger<> *TemplateCover::get_close_trigger() const { return this->close_trigger_; }
|
||||
Trigger<> *TemplateCover::get_stop_trigger() const { return this->stop_trigger_; }
|
||||
Trigger<> *TemplateCover::get_toggle_trigger() const { return this->toggle_trigger_; }
|
||||
void TemplateCover::dump_config() { LOG_COVER("", "Template Cover", this); }
|
||||
void TemplateCover::control(const CoverCall &call) {
|
||||
if (call.get_stop()) {
|
||||
this->stop_prev_trigger_();
|
||||
this->stop_trigger_->trigger();
|
||||
@@ -76,8 +113,7 @@ template<typename StateF, typename TiltF> void TemplateCoverBase<StateF, TiltF>:
|
||||
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
template<typename StateF, typename TiltF> CoverTraits TemplateCoverBase<StateF, TiltF>::get_traits() {
|
||||
CoverTraits TemplateCover::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_is_assumed_state(this->assumed_state_);
|
||||
traits.set_supports_stop(this->has_stop_);
|
||||
@@ -86,16 +122,19 @@ template<typename StateF, typename TiltF> CoverTraits TemplateCoverBase<StateF,
|
||||
traits.set_supports_tilt(this->has_tilt_);
|
||||
return traits;
|
||||
}
|
||||
|
||||
template<typename StateF, typename TiltF> void TemplateCoverBase<StateF, TiltF>::stop_prev_trigger_() {
|
||||
Trigger<float> *TemplateCover::get_position_trigger() const { return this->position_trigger_; }
|
||||
Trigger<float> *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; }
|
||||
void TemplateCover::set_tilt_lambda(std::function<optional<float>()> &&tilt_f) { this->tilt_f_ = tilt_f; }
|
||||
void TemplateCover::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; }
|
||||
void TemplateCover::set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; }
|
||||
void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; }
|
||||
void TemplateCover::set_has_tilt(bool has_tilt) { this->has_tilt_ = has_tilt; }
|
||||
void TemplateCover::stop_prev_trigger_() {
|
||||
if (this->prev_command_trigger_ != nullptr) {
|
||||
this->prev_command_trigger_->stop_action();
|
||||
this->prev_command_trigger_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template class TemplateCoverBase<std::function<optional<float>()>, std::function<optional<float>()>>;
|
||||
template class TemplateCoverBase<optional<float> (*)(), optional<float> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -13,59 +13,31 @@ enum TemplateCoverRestoreMode {
|
||||
COVER_RESTORE_AND_CALL,
|
||||
};
|
||||
|
||||
template<typename StateF, typename TiltF> class TemplateCoverBase : public cover::Cover, public Component {
|
||||
class TemplateCover : public cover::Cover, public Component {
|
||||
public:
|
||||
TemplateCoverBase()
|
||||
: open_trigger_(new Trigger<>()),
|
||||
close_trigger_(new Trigger<>()),
|
||||
stop_trigger_(new Trigger<>()),
|
||||
toggle_trigger_(new Trigger<>()),
|
||||
position_trigger_(new Trigger<float>()),
|
||||
tilt_trigger_(new Trigger<float>()) {}
|
||||
TemplateCover();
|
||||
|
||||
void loop() override {
|
||||
bool changed = false;
|
||||
if (this->state_f_.has_value()) {
|
||||
auto s = (*this->state_f_)();
|
||||
if (s.has_value()) {
|
||||
auto pos = clamp(*s, 0.0f, 1.0f);
|
||||
if (pos != this->position) {
|
||||
this->position = pos;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this->tilt_f_.has_value()) {
|
||||
auto s = (*this->tilt_f_)();
|
||||
if (s.has_value()) {
|
||||
auto tilt = clamp(*s, 0.0f, 1.0f);
|
||||
if (tilt != this->tilt) {
|
||||
this->tilt = tilt;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
this->publish_state();
|
||||
}
|
||||
void set_state_lambda(std::function<optional<float>()> &&f);
|
||||
Trigger<> *get_open_trigger() const;
|
||||
Trigger<> *get_close_trigger() const;
|
||||
Trigger<> *get_stop_trigger() const;
|
||||
Trigger<> *get_toggle_trigger() const;
|
||||
Trigger<float> *get_position_trigger() const;
|
||||
Trigger<float> *get_tilt_trigger() const;
|
||||
void set_optimistic(bool optimistic);
|
||||
void set_assumed_state(bool assumed_state);
|
||||
void set_tilt_lambda(std::function<optional<float>()> &&tilt_f);
|
||||
void set_has_stop(bool has_stop);
|
||||
void set_has_position(bool has_position);
|
||||
void set_has_tilt(bool has_tilt);
|
||||
void set_has_toggle(bool has_toggle);
|
||||
void set_restore_mode(TemplateCoverRestoreMode restore_mode) { restore_mode_ = restore_mode; }
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
Trigger<> *get_open_trigger() const { return this->open_trigger_; }
|
||||
Trigger<> *get_close_trigger() const { return this->close_trigger_; }
|
||||
Trigger<> *get_stop_trigger() const { return this->stop_trigger_; }
|
||||
Trigger<> *get_toggle_trigger() const { return this->toggle_trigger_; }
|
||||
Trigger<float> *get_position_trigger() const { return this->position_trigger_; }
|
||||
Trigger<float> *get_tilt_trigger() const { return this->tilt_trigger_; }
|
||||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
|
||||
void set_has_stop(bool has_stop) { this->has_stop_ = has_stop; }
|
||||
void set_has_position(bool has_position) { this->has_position_ = has_position; }
|
||||
void set_has_tilt(bool has_tilt) { this->has_tilt_ = has_tilt; }
|
||||
void set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; }
|
||||
void set_restore_mode(TemplateCoverRestoreMode restore_mode) { restore_mode_ = restore_mode; }
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
void control(const cover::CoverCall &call) override;
|
||||
@@ -73,8 +45,8 @@ template<typename StateF, typename TiltF> class TemplateCoverBase : public cover
|
||||
void stop_prev_trigger_();
|
||||
|
||||
TemplateCoverRestoreMode restore_mode_{COVER_RESTORE};
|
||||
optional<StateF> state_f_;
|
||||
optional<TiltF> tilt_f_;
|
||||
optional<std::function<optional<float>()>> state_f_;
|
||||
optional<std::function<optional<float>()>> tilt_f_;
|
||||
bool assumed_state_{false};
|
||||
bool optimistic_{false};
|
||||
Trigger<> *open_trigger_;
|
||||
@@ -90,22 +62,5 @@ template<typename StateF, typename TiltF> class TemplateCoverBase : public cover
|
||||
bool has_tilt_{false};
|
||||
};
|
||||
|
||||
class TemplateCover : public TemplateCoverBase<std::function<optional<float>()>, std::function<optional<float>()>> {
|
||||
public:
|
||||
void set_state_lambda(std::function<optional<float>()> &&f) { this->state_f_ = f; }
|
||||
void set_tilt_lambda(std::function<optional<float>()> &&tilt_f) { this->tilt_f_ = tilt_f; }
|
||||
};
|
||||
|
||||
/** Optimized template cover for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointers instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function) per lambda.
|
||||
*/
|
||||
class StatelessTemplateCover : public TemplateCoverBase<optional<float> (*)(), optional<float> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateCover(optional<float> (*state_f)()) { this->state_f_ = state_f; }
|
||||
void set_tilt_lambda(optional<float> (*tilt_f)()) { this->tilt_f_ = tilt_f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -5,7 +5,6 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_DAY,
|
||||
CONF_HOUR,
|
||||
CONF_ID,
|
||||
CONF_INITIAL_VALUE,
|
||||
CONF_LAMBDA,
|
||||
CONF_MINUTE,
|
||||
@@ -26,23 +25,14 @@ CODEOWNERS = ["@rfdarter"]
|
||||
TemplateDate = template_ns.class_(
|
||||
"TemplateDate", datetime.DateEntity, cg.PollingComponent
|
||||
)
|
||||
StatelessTemplateDate = template_ns.class_(
|
||||
"StatelessTemplateDate", datetime.DateEntity, cg.PollingComponent
|
||||
)
|
||||
|
||||
TemplateTime = template_ns.class_(
|
||||
"TemplateTime", datetime.TimeEntity, cg.PollingComponent
|
||||
)
|
||||
StatelessTemplateTime = template_ns.class_(
|
||||
"StatelessTemplateTime", datetime.TimeEntity, cg.PollingComponent
|
||||
)
|
||||
|
||||
TemplateDateTime = template_ns.class_(
|
||||
"TemplateDateTime", datetime.DateTimeEntity, cg.PollingComponent
|
||||
)
|
||||
StatelessTemplateDateTime = template_ns.class_(
|
||||
"StatelessTemplateDateTime", datetime.DateTimeEntity, cg.PollingComponent
|
||||
)
|
||||
|
||||
|
||||
def validate(config):
|
||||
@@ -109,30 +99,15 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await datetime.new_datetime(config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either Template* or StatelessTemplate*
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(cg.ESPTime)
|
||||
)
|
||||
# Determine the appropriate stateless class based on type
|
||||
if config[CONF_TYPE] == "DATE":
|
||||
stateless_class = StatelessTemplateDate
|
||||
elif config[CONF_TYPE] == "TIME":
|
||||
stateless_class = StatelessTemplateTime
|
||||
else: # DATETIME
|
||||
stateless_class = StatelessTemplateDateTime
|
||||
cg.add(var.set_template(template_))
|
||||
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, stateless_class
|
||||
)
|
||||
# Manually register as datetime since we didn't use new_datetime
|
||||
await datetime.register_datetime(var, config)
|
||||
await cg.register_component(var, config)
|
||||
else:
|
||||
# No lambda - just create the base template datetime
|
||||
var = await datetime.new_datetime(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
|
||||
|
||||
@@ -171,3 +146,5 @@ async def to_code(config):
|
||||
[(cg.ESPTime, "x")],
|
||||
config[CONF_SET_ACTION],
|
||||
)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -9,8 +9,7 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.date";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateDateBase<F>::setup() {
|
||||
void TemplateDate::setup() {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
|
||||
@@ -37,7 +36,21 @@ template<typename F> void TemplateDateBase<F>::setup() {
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
template<typename F> void TemplateDateBase<F>::control(const datetime::DateCall &call) {
|
||||
void TemplateDate::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->year_ = val->year;
|
||||
this->month_ = val->month;
|
||||
this->day_ = val->day_of_month;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void TemplateDate::control(const datetime::DateCall &call) {
|
||||
bool has_year = call.get_year().has_value();
|
||||
bool has_month = call.get_month().has_value();
|
||||
bool has_day = call.get_day().has_value();
|
||||
@@ -86,15 +99,12 @@ template<typename F> void TemplateDateBase<F>::control(const datetime::DateCall
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F> void TemplateDateBase<F>::dump_config() {
|
||||
void TemplateDate::dump_config() {
|
||||
LOG_DATETIME_DATE("", "Template Date", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
template class TemplateDateBase<std::function<optional<ESPTime>()>>;
|
||||
template class TemplateDateBase<optional<ESPTime> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -13,23 +13,12 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateDateBase : public datetime::DateEntity, public PollingComponent {
|
||||
class TemplateDate : public datetime::DateEntity, public PollingComponent {
|
||||
public:
|
||||
void update() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->year_ = val->year;
|
||||
this->month_ = val->month;
|
||||
this->day_ = val->day_of_month;
|
||||
this->publish_state();
|
||||
}
|
||||
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
@@ -46,26 +35,11 @@ template<typename F> class TemplateDateBase : public datetime::DateEntity, publi
|
||||
ESPTime initial_value_{};
|
||||
bool restore_value_{false};
|
||||
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
|
||||
optional<F> f_;
|
||||
optional<std::function<optional<ESPTime>()>> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
class TemplateDate : public TemplateDateBase<std::function<optional<ESPTime>()>> {
|
||||
public:
|
||||
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template date for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointers instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function) per lambda.
|
||||
*/
|
||||
class StatelessTemplateDate : public TemplateDateBase<optional<ESPTime> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateDate(optional<ESPTime> (*f)()) { this->f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.datetime";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateDateTimeBase<F>::setup() {
|
||||
void TemplateDateTime::setup() {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
|
||||
@@ -40,7 +39,24 @@ template<typename F> void TemplateDateTimeBase<F>::setup() {
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
template<typename F> void TemplateDateTimeBase<F>::control(const datetime::DateTimeCall &call) {
|
||||
void TemplateDateTime::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->year_ = val->year;
|
||||
this->month_ = val->month;
|
||||
this->day_ = val->day_of_month;
|
||||
this->hour_ = val->hour;
|
||||
this->minute_ = val->minute;
|
||||
this->second_ = val->second;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void TemplateDateTime::control(const datetime::DateTimeCall &call) {
|
||||
bool has_year = call.get_year().has_value();
|
||||
bool has_month = call.get_month().has_value();
|
||||
bool has_day = call.get_day().has_value();
|
||||
@@ -122,15 +138,12 @@ template<typename F> void TemplateDateTimeBase<F>::control(const datetime::DateT
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F> void TemplateDateTimeBase<F>::dump_config() {
|
||||
void TemplateDateTime::dump_config() {
|
||||
LOG_DATETIME_DATETIME("", "Template DateTime", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
template class TemplateDateTimeBase<std::function<optional<ESPTime>()>>;
|
||||
template class TemplateDateTimeBase<optional<ESPTime> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -13,26 +13,12 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateDateTimeBase : public datetime::DateTimeEntity, public PollingComponent {
|
||||
class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent {
|
||||
public:
|
||||
void update() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->year_ = val->year;
|
||||
this->month_ = val->month;
|
||||
this->day_ = val->day_of_month;
|
||||
this->hour_ = val->hour;
|
||||
this->minute_ = val->minute;
|
||||
this->second_ = val->second;
|
||||
this->publish_state();
|
||||
}
|
||||
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
@@ -49,26 +35,11 @@ template<typename F> class TemplateDateTimeBase : public datetime::DateTimeEntit
|
||||
ESPTime initial_value_{};
|
||||
bool restore_value_{false};
|
||||
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
|
||||
optional<F> f_;
|
||||
optional<std::function<optional<ESPTime>()>> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
class TemplateDateTime : public TemplateDateTimeBase<std::function<optional<ESPTime>()>> {
|
||||
public:
|
||||
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template datetime for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointers instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function) per lambda.
|
||||
*/
|
||||
class StatelessTemplateDateTime : public TemplateDateTimeBase<optional<ESPTime> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateDateTime(optional<ESPTime> (*f)()) { this->f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.time";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateTimeBase<F>::setup() {
|
||||
void TemplateTime::setup() {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
|
||||
@@ -37,7 +36,21 @@ template<typename F> void TemplateTimeBase<F>::setup() {
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
template<typename F> void TemplateTimeBase<F>::control(const datetime::TimeCall &call) {
|
||||
void TemplateTime::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->hour_ = val->hour;
|
||||
this->minute_ = val->minute;
|
||||
this->second_ = val->second;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void TemplateTime::control(const datetime::TimeCall &call) {
|
||||
bool has_hour = call.get_hour().has_value();
|
||||
bool has_minute = call.get_minute().has_value();
|
||||
bool has_second = call.get_second().has_value();
|
||||
@@ -86,15 +99,12 @@ template<typename F> void TemplateTimeBase<F>::control(const datetime::TimeCall
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F> void TemplateTimeBase<F>::dump_config() {
|
||||
void TemplateTime::dump_config() {
|
||||
LOG_DATETIME_TIME("", "Template Time", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
template class TemplateTimeBase<std::function<optional<ESPTime>()>>;
|
||||
template class TemplateTimeBase<optional<ESPTime> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -13,23 +13,12 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateTimeBase : public datetime::TimeEntity, public PollingComponent {
|
||||
class TemplateTime : public datetime::TimeEntity, public PollingComponent {
|
||||
public:
|
||||
void update() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->hour_ = val->hour;
|
||||
this->minute_ = val->minute;
|
||||
this->second_ = val->second;
|
||||
this->publish_state();
|
||||
}
|
||||
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
@@ -46,26 +35,11 @@ template<typename F> class TemplateTimeBase : public datetime::TimeEntity, publi
|
||||
ESPTime initial_value_{};
|
||||
bool restore_value_{false};
|
||||
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
|
||||
optional<F> f_;
|
||||
optional<std::function<optional<ESPTime>()>> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
class TemplateTime : public TemplateTimeBase<std::function<optional<ESPTime>()>> {
|
||||
public:
|
||||
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template time for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointers instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function) per lambda.
|
||||
*/
|
||||
class StatelessTemplateTime : public TemplateTimeBase<optional<ESPTime> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateTime(optional<ESPTime> (*f)()) { this->f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/fan/fan.h"
|
||||
@@ -16,7 +16,7 @@ class TemplateFan : public Component, public fan::Fan {
|
||||
void set_has_direction(bool has_direction) { this->has_direction_ = has_direction; }
|
||||
void set_has_oscillating(bool has_oscillating) { this->has_oscillating_ = has_oscillating; }
|
||||
void set_speed_count(int count) { this->speed_count_ = count; }
|
||||
void set_preset_modes(const std::initializer_list<std::string> &presets) { this->preset_modes_ = presets; }
|
||||
void set_preset_modes(const std::set<std::string> &presets) { this->preset_modes_ = presets; }
|
||||
fan::FanTraits get_traits() override { return this->traits_; }
|
||||
|
||||
protected:
|
||||
@@ -26,7 +26,7 @@ class TemplateFan : public Component, public fan::Fan {
|
||||
bool has_direction_{false};
|
||||
int speed_count_{0};
|
||||
fan::FanTraits traits_;
|
||||
std::vector<std::string> preset_modes_{};
|
||||
std::set<std::string> preset_modes_{};
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
|
||||
@@ -16,9 +16,6 @@ from esphome.const import (
|
||||
from .. import template_ns
|
||||
|
||||
TemplateLock = template_ns.class_("TemplateLock", lock.Lock, cg.Component)
|
||||
StatelessTemplateLock = template_ns.class_(
|
||||
"StatelessTemplateLock", lock.Lock, cg.Component
|
||||
)
|
||||
|
||||
TemplateLockPublishAction = template_ns.class_(
|
||||
"TemplateLockPublishAction",
|
||||
@@ -58,22 +55,14 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either TemplateLock or StatelessTemplateLock
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(lock.LockState)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateLock
|
||||
)
|
||||
# Manually register as lock since we didn't use new_lock
|
||||
await lock.register_lock(var, config)
|
||||
await cg.register_component(var, config)
|
||||
else:
|
||||
# No lambda - just create the base template lock
|
||||
var = await lock.new_lock(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(lock.LockState)
|
||||
)
|
||||
cg.add(var.set_state_lambda(template_))
|
||||
if CONF_UNLOCK_ACTION in config:
|
||||
await automation.build_automation(
|
||||
var.get_unlock_trigger(), [], config[CONF_UNLOCK_ACTION]
|
||||
|
||||
@@ -8,8 +8,19 @@ using namespace esphome::lock;
|
||||
|
||||
static const char *const TAG = "template.lock";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateLockBase<F>::control(const lock::LockCall &call) {
|
||||
TemplateLock::TemplateLock()
|
||||
: lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {}
|
||||
|
||||
void TemplateLock::loop() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->publish_state(*val);
|
||||
}
|
||||
void TemplateLock::control(const lock::LockCall &call) {
|
||||
if (this->prev_trigger_ != nullptr) {
|
||||
this->prev_trigger_->stop_action();
|
||||
}
|
||||
@@ -26,22 +37,23 @@ template<typename F> void TemplateLockBase<F>::control(const lock::LockCall &cal
|
||||
if (this->optimistic_)
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
||||
template<typename F> void TemplateLockBase<F>::open_latch() {
|
||||
void TemplateLock::open_latch() {
|
||||
if (this->prev_trigger_ != nullptr) {
|
||||
this->prev_trigger_->stop_action();
|
||||
}
|
||||
this->prev_trigger_ = this->open_trigger_;
|
||||
this->open_trigger_->trigger();
|
||||
}
|
||||
|
||||
template<typename F> void TemplateLockBase<F>::dump_config() {
|
||||
void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void TemplateLock::set_state_lambda(std::function<optional<lock::LockState>()> &&f) { this->f_ = f; }
|
||||
float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; }
|
||||
Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; }
|
||||
Trigger<> *TemplateLock::get_open_trigger() const { return this->open_trigger_; }
|
||||
void TemplateLock::dump_config() {
|
||||
LOG_LOCK("", "Template Lock", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
}
|
||||
|
||||
template class TemplateLockBase<std::function<optional<lock::LockState>()>>;
|
||||
template class TemplateLockBase<optional<lock::LockState> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -7,35 +7,26 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateLockBase : public lock::Lock, public Component {
|
||||
class TemplateLock : public lock::Lock, public Component {
|
||||
public:
|
||||
TemplateLockBase()
|
||||
: lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {}
|
||||
|
||||
void loop() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->publish_state(*val);
|
||||
}
|
||||
TemplateLock();
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
Trigger<> *get_lock_trigger() const { return this->lock_trigger_; }
|
||||
Trigger<> *get_unlock_trigger() const { return this->unlock_trigger_; }
|
||||
Trigger<> *get_open_trigger() const { return this->open_trigger_; }
|
||||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void set_state_lambda(std::function<optional<lock::LockState>()> &&f);
|
||||
Trigger<> *get_lock_trigger() const;
|
||||
Trigger<> *get_unlock_trigger() const;
|
||||
Trigger<> *get_open_trigger() const;
|
||||
void set_optimistic(bool optimistic);
|
||||
void loop() override;
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
void control(const lock::LockCall &call) override;
|
||||
void open_latch() override;
|
||||
|
||||
optional<F> f_;
|
||||
optional<std::function<optional<lock::LockState>()>> f_;
|
||||
bool optimistic_{false};
|
||||
Trigger<> *lock_trigger_;
|
||||
Trigger<> *unlock_trigger_;
|
||||
@@ -43,20 +34,5 @@ template<typename F> class TemplateLockBase : public lock::Lock, public Componen
|
||||
Trigger<> *prev_trigger_{nullptr};
|
||||
};
|
||||
|
||||
class TemplateLock : public TemplateLockBase<std::function<optional<lock::LockState>()>> {
|
||||
public:
|
||||
void set_state_lambda(std::function<optional<lock::LockState>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template lock for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointers instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function) per lambda.
|
||||
*/
|
||||
class StatelessTemplateLock : public TemplateLockBase<optional<lock::LockState> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateLock(optional<lock::LockState> (*f)()) { this->f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -19,9 +19,6 @@ from .. import template_ns
|
||||
TemplateNumber = template_ns.class_(
|
||||
"TemplateNumber", number.Number, cg.PollingComponent
|
||||
)
|
||||
StatelessTemplateNumber = template_ns.class_(
|
||||
"StatelessTemplateNumber", number.Number, cg.PollingComponent
|
||||
)
|
||||
|
||||
|
||||
def validate_min_max(config):
|
||||
@@ -69,24 +66,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either TemplateNumber or StatelessTemplateNumber
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(float)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateNumber
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
await number.register_number(
|
||||
var,
|
||||
config,
|
||||
min_value=config[CONF_MIN_VALUE],
|
||||
max_value=config[CONF_MAX_VALUE],
|
||||
step=config[CONF_STEP],
|
||||
)
|
||||
else:
|
||||
# No lambda - just create the base template number
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await number.register_number(
|
||||
@@ -96,6 +75,14 @@ async def to_code(config):
|
||||
max_value=config[CONF_MAX_VALUE],
|
||||
step=config[CONF_STEP],
|
||||
)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(float)
|
||||
)
|
||||
cg.add(var.set_template(template_))
|
||||
|
||||
else:
|
||||
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE]))
|
||||
if CONF_RESTORE_VALUE in config:
|
||||
|
||||
@@ -6,8 +6,7 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.number";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateNumberBase<F>::setup() {
|
||||
void TemplateNumber::setup() {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
|
||||
@@ -27,7 +26,18 @@ template<typename F> void TemplateNumberBase<F>::setup() {
|
||||
this->publish_state(value);
|
||||
}
|
||||
|
||||
template<typename F> void TemplateNumberBase<F>::control(float value) {
|
||||
void TemplateNumber::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->publish_state(*val);
|
||||
}
|
||||
|
||||
void TemplateNumber::control(float value) {
|
||||
this->set_trigger_->trigger(value);
|
||||
|
||||
if (this->optimistic_)
|
||||
@@ -36,15 +46,11 @@ template<typename F> void TemplateNumberBase<F>::control(float value) {
|
||||
if (this->restore_value_)
|
||||
this->pref_.save(&value);
|
||||
}
|
||||
|
||||
template<typename F> void TemplateNumberBase<F>::dump_config() {
|
||||
void TemplateNumber::dump_config() {
|
||||
LOG_NUMBER("", "Template Number", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
template class TemplateNumberBase<std::function<optional<float>()>>;
|
||||
template class TemplateNumberBase<optional<float> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -8,22 +8,13 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateNumberBase : public number::Number, public PollingComponent {
|
||||
class TemplateNumber : public number::Number, public PollingComponent {
|
||||
public:
|
||||
TemplateNumberBase() : set_trigger_(new Trigger<float>()) {}
|
||||
void set_template(std::function<optional<float>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
void update() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
this->publish_state(*val);
|
||||
}
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
Trigger<float> *get_set_trigger() const { return set_trigger_; }
|
||||
@@ -36,26 +27,11 @@ template<typename F> class TemplateNumberBase : public number::Number, public Po
|
||||
bool optimistic_{false};
|
||||
float initial_value_{NAN};
|
||||
bool restore_value_{false};
|
||||
Trigger<float> *set_trigger_;
|
||||
optional<F> f_;
|
||||
Trigger<float> *set_trigger_ = new Trigger<float>();
|
||||
optional<std::function<optional<float>()>> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
class TemplateNumber : public TemplateNumberBase<std::function<optional<float>()>> {
|
||||
public:
|
||||
void set_template(std::function<optional<float>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template number for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
*/
|
||||
class StatelessTemplateNumber : public TemplateNumberBase<optional<float> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateNumber(optional<float> (*f)()) { this->f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -17,9 +17,6 @@ from .. import template_ns
|
||||
TemplateSelect = template_ns.class_(
|
||||
"TemplateSelect", select.Select, cg.PollingComponent
|
||||
)
|
||||
StatelessTemplateSelect = template_ns.class_(
|
||||
"StatelessTemplateSelect", select.Select, cg.PollingComponent
|
||||
)
|
||||
|
||||
|
||||
def validate(config):
|
||||
@@ -65,34 +62,22 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either TemplateSelect or StatelessTemplateSelect
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(cg.std_string)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateSelect
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
await select.register_select(var, config, options=config[CONF_OPTIONS])
|
||||
else:
|
||||
# No lambda - just create the base template select
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await select.register_select(var, config, options=config[CONF_OPTIONS])
|
||||
|
||||
# Only set if non-default to avoid bloating setup() function
|
||||
if config[CONF_OPTIMISTIC]:
|
||||
cg.add(var.set_optimistic(True))
|
||||
initial_option_index = config[CONF_OPTIONS].index(config[CONF_INITIAL_OPTION])
|
||||
# Only set if non-zero to avoid bloating setup() function
|
||||
# (initial_option_index_ is zero-initialized in the header)
|
||||
if initial_option_index != 0:
|
||||
cg.add(var.set_initial_option_index(initial_option_index))
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(cg.std_string)
|
||||
)
|
||||
cg.add(var.set_template(template_))
|
||||
|
||||
# Only set if True (default is False)
|
||||
if config.get(CONF_RESTORE_VALUE):
|
||||
cg.add(var.set_restore_value(True))
|
||||
else:
|
||||
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
cg.add(var.set_initial_option(config[CONF_INITIAL_OPTION]))
|
||||
|
||||
if CONF_RESTORE_VALUE in config:
|
||||
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
|
||||
|
||||
if CONF_SET_ACTION in config:
|
||||
await automation.build_automation(
|
||||
|
||||
@@ -6,29 +6,49 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.select";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateSelectBase<F>::setup() {
|
||||
void TemplateSelect::setup() {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
|
||||
size_t index = this->initial_option_index_;
|
||||
if (this->restore_value_) {
|
||||
std::string value;
|
||||
if (!this->restore_value_) {
|
||||
value = this->initial_option_;
|
||||
ESP_LOGD(TAG, "State from initial: %s", value.c_str());
|
||||
} else {
|
||||
size_t index;
|
||||
this->pref_ = global_preferences->make_preference<size_t>(this->get_preference_hash());
|
||||
size_t restored_index;
|
||||
if (this->pref_.load(&restored_index) && this->has_index(restored_index)) {
|
||||
index = restored_index;
|
||||
ESP_LOGD(TAG, "State from restore: %s", this->at(index).value().c_str());
|
||||
if (!this->pref_.load(&index)) {
|
||||
value = this->initial_option_;
|
||||
ESP_LOGD(TAG, "State from initial (could not load stored index): %s", value.c_str());
|
||||
} else if (!this->has_index(index)) {
|
||||
value = this->initial_option_;
|
||||
ESP_LOGD(TAG, "State from initial (restored index %d out of bounds): %s", index, value.c_str());
|
||||
} else {
|
||||
ESP_LOGD(TAG, "State from initial (could not load or invalid stored index): %s", this->at(index).value().c_str());
|
||||
value = this->at(index).value();
|
||||
ESP_LOGD(TAG, "State from restore: %s", value.c_str());
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(TAG, "State from initial: %s", this->at(index).value().c_str());
|
||||
}
|
||||
|
||||
this->publish_state(this->at(index).value());
|
||||
this->publish_state(value);
|
||||
}
|
||||
|
||||
template<typename F> void TemplateSelectBase<F>::control(const std::string &value) {
|
||||
void TemplateSelect::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
if (!this->has_option(*val)) {
|
||||
ESP_LOGE(TAG, "Lambda returned an invalid option: %s", (*val).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
this->publish_state(*val);
|
||||
}
|
||||
|
||||
void TemplateSelect::control(const std::string &value) {
|
||||
this->set_trigger_->trigger(value);
|
||||
|
||||
if (this->optimistic_)
|
||||
@@ -40,7 +60,7 @@ template<typename F> void TemplateSelectBase<F>::control(const std::string &valu
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F> void TemplateSelectBase<F>::dump_config() {
|
||||
void TemplateSelect::dump_config() {
|
||||
LOG_SELECT("", "Template Select", this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
if (this->f_.has_value())
|
||||
@@ -49,12 +69,8 @@ template<typename F> void TemplateSelectBase<F>::dump_config() {
|
||||
" Optimistic: %s\n"
|
||||
" Initial Option: %s\n"
|
||||
" Restore Value: %s",
|
||||
YESNO(this->optimistic_), this->at(this->initial_option_index_).value().c_str(),
|
||||
YESNO(this->restore_value_));
|
||||
YESNO(this->optimistic_), this->initial_option_.c_str(), YESNO(this->restore_value_));
|
||||
}
|
||||
|
||||
template class TemplateSelectBase<std::function<optional<std::string>()>>;
|
||||
template class TemplateSelectBase<optional<std::string> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -8,58 +8,30 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateSelectBase : public select::Select, public PollingComponent {
|
||||
class TemplateSelect : public select::Select, public PollingComponent {
|
||||
public:
|
||||
TemplateSelectBase() : set_trigger_(new Trigger<std::string>()) {}
|
||||
void set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
void update() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
if (!this->has_option(*val)) {
|
||||
ESP_LOGE("template.select", "Lambda returned an invalid option: %s", (*val).c_str());
|
||||
return;
|
||||
}
|
||||
this->publish_state(*val);
|
||||
}
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
Trigger<std::string> *get_set_trigger() const { return this->set_trigger_; }
|
||||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void set_initial_option_index(size_t initial_option_index) { this->initial_option_index_ = initial_option_index; }
|
||||
void set_initial_option(const std::string &initial_option) { this->initial_option_ = initial_option; }
|
||||
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
bool optimistic_ = false;
|
||||
size_t initial_option_index_{0};
|
||||
std::string initial_option_;
|
||||
bool restore_value_ = false;
|
||||
Trigger<std::string> *set_trigger_;
|
||||
optional<F> f_;
|
||||
Trigger<std::string> *set_trigger_ = new Trigger<std::string>();
|
||||
optional<std::function<optional<std::string>()>> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
class TemplateSelect : public TemplateSelectBase<std::function<optional<std::string>()>> {
|
||||
public:
|
||||
void set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template select for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
*/
|
||||
class StatelessTemplateSelect : public TemplateSelectBase<optional<std::string> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateSelect(optional<std::string> (*f)()) { this->f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -9,9 +9,6 @@ from .. import template_ns
|
||||
TemplateSensor = template_ns.class_(
|
||||
"TemplateSensor", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
StatelessTemplateSensor = template_ns.class_(
|
||||
"StatelessTemplateSensor", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
@@ -28,21 +25,14 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either TemplateSensor or StatelessTemplateSensor
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(float)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateSensor
|
||||
)
|
||||
# Manually register as sensor since we didn't use new_sensor
|
||||
await sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
else:
|
||||
# No lambda - just create the base template sensor
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_template(template_))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
|
||||
@@ -7,14 +7,21 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.sensor";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateSensorBase<F>::dump_config() {
|
||||
void TemplateSensor::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (val.has_value()) {
|
||||
this->publish_state(*val);
|
||||
}
|
||||
}
|
||||
float TemplateSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
void TemplateSensor::set_template(std::function<optional<float>()> &&f) { this->f_ = f; }
|
||||
void TemplateSensor::dump_config() {
|
||||
LOG_SENSOR("", "Template Sensor", this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
template class TemplateSensorBase<std::function<optional<float>()>>;
|
||||
template class TemplateSensorBase<optional<float> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -6,38 +6,18 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateSensorBase : public sensor::Sensor, public PollingComponent {
|
||||
class TemplateSensor : public sensor::Sensor, public PollingComponent {
|
||||
public:
|
||||
void update() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto val = (*this->f_)();
|
||||
if (val.has_value()) {
|
||||
this->publish_state(*val);
|
||||
}
|
||||
}
|
||||
void set_template(std::function<optional<float>()> &&f);
|
||||
|
||||
void update() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
optional<F> f_;
|
||||
};
|
||||
|
||||
class TemplateSensor : public TemplateSensorBase<std::function<optional<float>()>> {
|
||||
public:
|
||||
void set_template(std::function<optional<float>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template sensor for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
*/
|
||||
class StatelessTemplateSensor : public TemplateSensorBase<optional<float> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateSensor(optional<float> (*f)()) { this->f_ = f; }
|
||||
optional<std::function<optional<float>()>> f_;
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
|
||||
@@ -16,9 +16,6 @@ from esphome.const import (
|
||||
from .. import template_ns
|
||||
|
||||
TemplateSwitch = template_ns.class_("TemplateSwitch", switch.Switch, cg.Component)
|
||||
StatelessTemplateSwitch = template_ns.class_(
|
||||
"StatelessTemplateSwitch", switch.Switch, cg.Component
|
||||
)
|
||||
|
||||
|
||||
def validate(config):
|
||||
@@ -58,22 +55,14 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either TemplateSwitch or StatelessTemplateSwitch
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(bool)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateSwitch
|
||||
)
|
||||
# Manually register as switch since we didn't use new_switch
|
||||
await switch.register_switch(var, config)
|
||||
await cg.register_component(var, config)
|
||||
else:
|
||||
# No lambda - just create the base template switch
|
||||
var = await switch.new_switch(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(bool)
|
||||
)
|
||||
cg.add(var.set_state_lambda(template_))
|
||||
if CONF_TURN_OFF_ACTION in config:
|
||||
await automation.build_automation(
|
||||
var.get_turn_off_trigger(), [], config[CONF_TURN_OFF_ACTION]
|
||||
|
||||
@@ -6,8 +6,18 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.switch";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateSwitchBase<F>::write_state(bool state) {
|
||||
TemplateSwitch::TemplateSwitch() : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {}
|
||||
|
||||
void TemplateSwitch::loop() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto s = (*this->f_)();
|
||||
if (!s.has_value())
|
||||
return;
|
||||
|
||||
this->publish_state(*s);
|
||||
}
|
||||
void TemplateSwitch::write_state(bool state) {
|
||||
if (this->prev_trigger_ != nullptr) {
|
||||
this->prev_trigger_->stop_action();
|
||||
}
|
||||
@@ -23,8 +33,13 @@ template<typename F> void TemplateSwitchBase<F>::write_state(bool state) {
|
||||
if (this->optimistic_)
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
||||
template<typename F> void TemplateSwitchBase<F>::setup() {
|
||||
void TemplateSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
bool TemplateSwitch::assumed_state() { return this->assumed_state_; }
|
||||
void TemplateSwitch::set_state_lambda(std::function<optional<bool>()> &&f) { this->f_ = f; }
|
||||
float TemplateSwitch::get_setup_priority() const { return setup_priority::HARDWARE - 2.0f; }
|
||||
Trigger<> *TemplateSwitch::get_turn_on_trigger() const { return this->turn_on_trigger_; }
|
||||
Trigger<> *TemplateSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; }
|
||||
void TemplateSwitch::setup() {
|
||||
optional<bool> initial_state = this->get_initial_state_with_restore_mode();
|
||||
|
||||
if (initial_state.has_value()) {
|
||||
@@ -37,14 +52,11 @@ template<typename F> void TemplateSwitchBase<F>::setup() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F> void TemplateSwitchBase<F>::dump_config() {
|
||||
void TemplateSwitch::dump_config() {
|
||||
LOG_SWITCH("", "Template Switch", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
}
|
||||
|
||||
template class TemplateSwitchBase<std::function<optional<bool>()>>;
|
||||
template class TemplateSwitchBase<optional<bool> (*)()>;
|
||||
void TemplateSwitch::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -7,35 +7,28 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateSwitchBase : public switch_::Switch, public Component {
|
||||
class TemplateSwitch : public switch_::Switch, public Component {
|
||||
public:
|
||||
TemplateSwitchBase() : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {}
|
||||
TemplateSwitch();
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
void loop() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto s = (*this->f_)();
|
||||
if (!s.has_value())
|
||||
return;
|
||||
this->publish_state(*s);
|
||||
}
|
||||
void set_state_lambda(std::function<optional<bool>()> &&f);
|
||||
Trigger<> *get_turn_on_trigger() const;
|
||||
Trigger<> *get_turn_off_trigger() const;
|
||||
void set_optimistic(bool optimistic);
|
||||
void set_assumed_state(bool assumed_state);
|
||||
void loop() override;
|
||||
|
||||
Trigger<> *get_turn_on_trigger() const { return this->turn_on_trigger_; }
|
||||
Trigger<> *get_turn_off_trigger() const { return this->turn_off_trigger_; }
|
||||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE - 2.0f; }
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
bool assumed_state() override { return this->assumed_state_; }
|
||||
bool assumed_state() override;
|
||||
|
||||
void write_state(bool state) override;
|
||||
|
||||
optional<F> f_;
|
||||
optional<std::function<optional<bool>()>> f_;
|
||||
bool optimistic_{false};
|
||||
bool assumed_state_{false};
|
||||
Trigger<> *turn_on_trigger_;
|
||||
@@ -43,20 +36,5 @@ template<typename F> class TemplateSwitchBase : public switch_::Switch, public C
|
||||
Trigger<> *prev_trigger_{nullptr};
|
||||
};
|
||||
|
||||
class TemplateSwitch : public TemplateSwitchBase<std::function<optional<bool>()>> {
|
||||
public:
|
||||
void set_state_lambda(std::function<optional<bool>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template switch for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
*/
|
||||
class StatelessTemplateSwitch : public TemplateSwitchBase<optional<bool> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateSwitch(optional<bool> (*f)()) { this->f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -3,7 +3,6 @@ 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,
|
||||
@@ -17,9 +16,6 @@ from esphome.const import (
|
||||
from .. import template_ns
|
||||
|
||||
TemplateText = template_ns.class_("TemplateText", text.Text, cg.PollingComponent)
|
||||
StatelessTemplateText = template_ns.class_(
|
||||
"StatelessTemplateText", text.Text, cg.PollingComponent
|
||||
)
|
||||
|
||||
TextSaverBase = template_ns.class_("TemplateTextSaverBase")
|
||||
TextSaverTemplate = template_ns.class_("TextSaver", TextSaverBase)
|
||||
@@ -69,24 +65,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either TemplateText or StatelessTemplateText
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(cg.std_string)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateText
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
await text.register_text(
|
||||
var,
|
||||
config,
|
||||
min_length=config[CONF_MIN_LENGTH],
|
||||
max_length=config[CONF_MAX_LENGTH],
|
||||
pattern=config.get(CONF_PATTERN),
|
||||
)
|
||||
else:
|
||||
# No lambda - just create the base template text
|
||||
var = await text.new_text(
|
||||
config,
|
||||
min_length=config[CONF_MIN_LENGTH],
|
||||
@@ -94,6 +72,14 @@ async def to_code(config):
|
||||
pattern=config.get(CONF_PATTERN),
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(cg.std_string)
|
||||
)
|
||||
cg.add(var.set_template(template_))
|
||||
|
||||
else:
|
||||
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
if initial_value_config := config.get(CONF_INITIAL_VALUE):
|
||||
cg.add(var.set_initial_value(initial_value_config))
|
||||
|
||||
@@ -6,8 +6,7 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.text";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateTextBase<F>::setup() {
|
||||
void TemplateText::setup() {
|
||||
if (!(this->f_ == nullptr)) {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
@@ -26,7 +25,21 @@ template<typename F> void TemplateTextBase<F>::setup() {
|
||||
this->publish_state(value);
|
||||
}
|
||||
|
||||
template<typename F> void TemplateTextBase<F>::control(const std::string &value) {
|
||||
void TemplateText::update() {
|
||||
if (this->f_ == nullptr)
|
||||
return;
|
||||
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->publish_state(*val);
|
||||
}
|
||||
|
||||
void TemplateText::control(const std::string &value) {
|
||||
this->set_trigger_->trigger(value);
|
||||
|
||||
if (this->optimistic_)
|
||||
@@ -38,15 +51,11 @@ template<typename F> void TemplateTextBase<F>::control(const std::string &value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F> void TemplateTextBase<F>::dump_config() {
|
||||
void TemplateText::dump_config() {
|
||||
LOG_TEXT("", "Template Text Input", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
template class TemplateTextBase<std::function<optional<std::string>()>>;
|
||||
template class TemplateTextBase<optional<std::string> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -59,24 +59,13 @@ template<uint8_t SZ> class TextSaver : public TemplateTextSaverBase {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F> class TemplateTextBase : public text::Text, public PollingComponent {
|
||||
class TemplateText : public text::Text, public PollingComponent {
|
||||
public:
|
||||
TemplateTextBase() : set_trigger_(new Trigger<std::string>()) {}
|
||||
void set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
void update() override {
|
||||
if (this->f_ == nullptr)
|
||||
return;
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
this->publish_state(*val);
|
||||
}
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
Trigger<std::string> *get_set_trigger() const { return this->set_trigger_; }
|
||||
@@ -88,26 +77,11 @@ template<typename F> class TemplateTextBase : public text::Text, public PollingC
|
||||
void control(const std::string &value) override;
|
||||
bool optimistic_ = false;
|
||||
std::string initial_value_;
|
||||
Trigger<std::string> *set_trigger_;
|
||||
optional<F> f_{nullptr};
|
||||
Trigger<std::string> *set_trigger_ = new Trigger<std::string>();
|
||||
optional<std::function<optional<std::string>()>> f_{nullptr};
|
||||
|
||||
TemplateTextSaverBase *pref_ = nullptr;
|
||||
};
|
||||
|
||||
class TemplateText : public TemplateTextBase<std::function<optional<std::string>()>> {
|
||||
public:
|
||||
void set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template text for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
*/
|
||||
class StatelessTemplateText : public TemplateTextBase<optional<std::string> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateText(optional<std::string> (*f)()) { this->f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -10,9 +10,6 @@ from .. import template_ns
|
||||
TemplateTextSensor = template_ns.class_(
|
||||
"TemplateTextSensor", text_sensor.TextSensor, cg.PollingComponent
|
||||
)
|
||||
StatelessTemplateTextSensor = template_ns.class_(
|
||||
"StatelessTemplateTextSensor", text_sensor.TextSensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
text_sensor.text_sensor_schema()
|
||||
@@ -27,21 +24,14 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await text_sensor.new_text_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
# Use new_lambda_pvariable to create either TemplateTextSensor or StatelessTemplateTextSensor
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(cg.std_string)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateTextSensor
|
||||
)
|
||||
# Manually register as text sensor since we didn't use new_text_sensor
|
||||
await text_sensor.register_text_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
else:
|
||||
# No lambda - just create the base template text sensor
|
||||
var = await text_sensor.new_text_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_template(template_))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
|
||||
@@ -6,11 +6,18 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.text_sensor";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateTextSensorBase<F>::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); }
|
||||
void TemplateTextSensor::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
template class TemplateTextSensorBase<std::function<optional<std::string>()>>;
|
||||
template class TemplateTextSensorBase<optional<std::string> (*)()>;
|
||||
auto val = (*this->f_)();
|
||||
if (val.has_value()) {
|
||||
this->publish_state(*val);
|
||||
}
|
||||
}
|
||||
float TemplateTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
void TemplateTextSensor::set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; }
|
||||
void TemplateTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); }
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -7,38 +7,18 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
template<typename F> class TemplateTextSensorBase : public text_sensor::TextSensor, public PollingComponent {
|
||||
class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent {
|
||||
public:
|
||||
void update() override {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
auto val = (*this->f_)();
|
||||
if (val.has_value()) {
|
||||
this->publish_state(*val);
|
||||
}
|
||||
}
|
||||
void set_template(std::function<optional<std::string>()> &&f);
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void update() override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
optional<F> f_;
|
||||
};
|
||||
|
||||
class TemplateTextSensor : public TemplateTextSensorBase<std::function<optional<std::string>()>> {
|
||||
public:
|
||||
void set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template text sensor for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
*/
|
||||
class StatelessTemplateTextSensor : public TemplateTextSensorBase<optional<std::string> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateTextSensor(optional<std::string> (*f)()) { this->f_ = f; }
|
||||
optional<std::function<optional<std::string>()>> f_{};
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
|
||||
@@ -20,9 +20,6 @@ from esphome.const import (
|
||||
from .. import template_ns
|
||||
|
||||
TemplateValve = template_ns.class_("TemplateValve", valve.Valve, cg.Component)
|
||||
StatelessTemplateValve = template_ns.class_(
|
||||
"StatelessTemplateValve", valve.Valve, cg.Component
|
||||
)
|
||||
|
||||
TemplateValvePublishAction = template_ns.class_(
|
||||
"TemplateValvePublishAction", automation.Action, cg.Parented.template(TemplateValve)
|
||||
@@ -65,22 +62,13 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await valve.new_valve(config)
|
||||
await cg.register_component(var, config)
|
||||
if lambda_config := config.get(CONF_LAMBDA):
|
||||
# Use new_lambda_pvariable to create either TemplateValve or StatelessTemplateValve
|
||||
template_ = await cg.process_lambda(
|
||||
lambda_config, [], return_type=cg.optional.template(float)
|
||||
)
|
||||
var = automation.new_lambda_pvariable(
|
||||
config[CONF_ID], template_, StatelessTemplateValve
|
||||
)
|
||||
# Manually register as valve since we didn't use new_valve
|
||||
await valve.register_valve(var, config)
|
||||
await cg.register_component(var, config)
|
||||
else:
|
||||
# No lambda - just create the base template valve
|
||||
var = await valve.new_valve(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_state_lambda(template_))
|
||||
if open_action_config := config.get(CONF_OPEN_ACTION):
|
||||
await automation.build_automation(
|
||||
var.get_open_trigger(), [], open_action_config
|
||||
|
||||
@@ -8,8 +8,14 @@ using namespace esphome::valve;
|
||||
|
||||
static const char *const TAG = "template.valve";
|
||||
|
||||
// Template instantiations
|
||||
template<typename F> void TemplateValveBase<F>::setup() {
|
||||
TemplateValve::TemplateValve()
|
||||
: open_trigger_(new Trigger<>()),
|
||||
close_trigger_(new Trigger<>),
|
||||
stop_trigger_(new Trigger<>()),
|
||||
toggle_trigger_(new Trigger<>()),
|
||||
position_trigger_(new Trigger<float>()) {}
|
||||
|
||||
void TemplateValve::setup() {
|
||||
switch (this->restore_mode_) {
|
||||
case VALVE_NO_RESTORE:
|
||||
break;
|
||||
@@ -29,7 +35,35 @@ template<typename F> void TemplateValveBase<F>::setup() {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F> void TemplateValveBase<F>::dump_config() {
|
||||
void TemplateValve::loop() {
|
||||
bool changed = false;
|
||||
|
||||
if (this->state_f_.has_value()) {
|
||||
auto s = (*this->state_f_)();
|
||||
if (s.has_value()) {
|
||||
auto pos = clamp(*s, 0.0f, 1.0f);
|
||||
if (pos != this->position) {
|
||||
this->position = pos;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void TemplateValve::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void TemplateValve::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
|
||||
void TemplateValve::set_state_lambda(std::function<optional<float>()> &&f) { this->state_f_ = f; }
|
||||
float TemplateValve::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
Trigger<> *TemplateValve::get_open_trigger() const { return this->open_trigger_; }
|
||||
Trigger<> *TemplateValve::get_close_trigger() const { return this->close_trigger_; }
|
||||
Trigger<> *TemplateValve::get_stop_trigger() const { return this->stop_trigger_; }
|
||||
Trigger<> *TemplateValve::get_toggle_trigger() const { return this->toggle_trigger_; }
|
||||
|
||||
void TemplateValve::dump_config() {
|
||||
LOG_VALVE("", "Template Valve", this);
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Has position: %s\n"
|
||||
@@ -37,7 +71,7 @@ template<typename F> void TemplateValveBase<F>::dump_config() {
|
||||
YESNO(this->has_position_), YESNO(this->optimistic_));
|
||||
}
|
||||
|
||||
template<typename F> void TemplateValveBase<F>::control(const ValveCall &call) {
|
||||
void TemplateValve::control(const ValveCall &call) {
|
||||
if (call.get_stop()) {
|
||||
this->stop_prev_trigger_();
|
||||
this->stop_trigger_->trigger();
|
||||
@@ -72,7 +106,7 @@ template<typename F> void TemplateValveBase<F>::control(const ValveCall &call) {
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
template<typename F> valve::ValveTraits TemplateValveBase<F>::get_traits() {
|
||||
ValveTraits TemplateValve::get_traits() {
|
||||
auto traits = ValveTraits();
|
||||
traits.set_is_assumed_state(this->assumed_state_);
|
||||
traits.set_supports_stop(this->has_stop_);
|
||||
@@ -81,15 +115,18 @@ template<typename F> valve::ValveTraits TemplateValveBase<F>::get_traits() {
|
||||
return traits;
|
||||
}
|
||||
|
||||
template<typename F> void TemplateValveBase<F>::stop_prev_trigger_() {
|
||||
Trigger<float> *TemplateValve::get_position_trigger() const { return this->position_trigger_; }
|
||||
|
||||
void TemplateValve::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; }
|
||||
void TemplateValve::set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; }
|
||||
void TemplateValve::set_has_position(bool has_position) { this->has_position_ = has_position; }
|
||||
|
||||
void TemplateValve::stop_prev_trigger_() {
|
||||
if (this->prev_command_trigger_ != nullptr) {
|
||||
this->prev_command_trigger_->stop_action();
|
||||
this->prev_command_trigger_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template class TemplateValveBase<std::function<optional<float>()>>;
|
||||
template class TemplateValveBase<optional<float> (*)()>;
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -13,48 +13,28 @@ enum TemplateValveRestoreMode {
|
||||
VALVE_RESTORE_AND_CALL,
|
||||
};
|
||||
|
||||
template<typename F> class TemplateValveBase : public valve::Valve, public Component {
|
||||
class TemplateValve : public valve::Valve, public Component {
|
||||
public:
|
||||
TemplateValveBase()
|
||||
: open_trigger_(new Trigger<>()),
|
||||
close_trigger_(new Trigger<>()),
|
||||
stop_trigger_(new Trigger<>()),
|
||||
toggle_trigger_(new Trigger<>()),
|
||||
position_trigger_(new Trigger<float>()) {}
|
||||
TemplateValve();
|
||||
|
||||
void loop() override {
|
||||
bool changed = false;
|
||||
|
||||
if (this->state_f_.has_value()) {
|
||||
auto s = (*this->state_f_)();
|
||||
if (s.has_value()) {
|
||||
auto pos = clamp(*s, 0.0f, 1.0f);
|
||||
if (pos != this->position) {
|
||||
this->position = pos;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
this->publish_state();
|
||||
}
|
||||
void set_state_lambda(std::function<optional<float>()> &&f);
|
||||
Trigger<> *get_open_trigger() const;
|
||||
Trigger<> *get_close_trigger() const;
|
||||
Trigger<> *get_stop_trigger() const;
|
||||
Trigger<> *get_toggle_trigger() const;
|
||||
Trigger<float> *get_position_trigger() const;
|
||||
void set_optimistic(bool optimistic);
|
||||
void set_assumed_state(bool assumed_state);
|
||||
void set_has_stop(bool has_stop);
|
||||
void set_has_position(bool has_position);
|
||||
void set_has_toggle(bool has_toggle);
|
||||
void set_restore_mode(TemplateValveRestoreMode restore_mode) { restore_mode_ = restore_mode; }
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
Trigger<> *get_open_trigger() const { return this->open_trigger_; }
|
||||
Trigger<> *get_close_trigger() const { return this->close_trigger_; }
|
||||
Trigger<> *get_stop_trigger() const { return this->stop_trigger_; }
|
||||
Trigger<> *get_toggle_trigger() const { return this->toggle_trigger_; }
|
||||
Trigger<float> *get_position_trigger() const { return this->position_trigger_; }
|
||||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
|
||||
void set_has_stop(bool has_stop) { this->has_stop_ = has_stop; }
|
||||
void set_has_position(bool has_position) { this->has_position_ = has_position; }
|
||||
void set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; }
|
||||
void set_restore_mode(TemplateValveRestoreMode restore_mode) { restore_mode_ = restore_mode; }
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
void control(const valve::ValveCall &call) override;
|
||||
@@ -62,7 +42,7 @@ template<typename F> class TemplateValveBase : public valve::Valve, public Compo
|
||||
void stop_prev_trigger_();
|
||||
|
||||
TemplateValveRestoreMode restore_mode_{VALVE_NO_RESTORE};
|
||||
optional<F> state_f_;
|
||||
optional<std::function<optional<float>()>> state_f_;
|
||||
bool assumed_state_{false};
|
||||
bool optimistic_{false};
|
||||
Trigger<> *open_trigger_;
|
||||
@@ -76,20 +56,5 @@ template<typename F> class TemplateValveBase : public valve::Valve, public Compo
|
||||
bool has_position_{false};
|
||||
};
|
||||
|
||||
class TemplateValve : public TemplateValveBase<std::function<optional<float>()>> {
|
||||
public:
|
||||
void set_state_lambda(std::function<optional<float>()> &&f) { this->state_f_ = f; }
|
||||
};
|
||||
|
||||
/** Optimized template valve for stateless lambdas (no capture).
|
||||
*
|
||||
* Uses function pointers instead of std::function to reduce memory overhead.
|
||||
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function) per lambda.
|
||||
*/
|
||||
class StatelessTemplateValve : public TemplateValveBase<optional<float> (*)()> {
|
||||
public:
|
||||
explicit StatelessTemplateValve(optional<float> (*f)()) { this->state_f_ = f; }
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -26,10 +26,10 @@ class ZephyrGPIOPin : public InternalGPIOPin {
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
gpio::Flags flags_{};
|
||||
const device *gpio_{nullptr};
|
||||
bool value_{false};
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
const device *gpio_ = nullptr;
|
||||
bool value_ = false;
|
||||
};
|
||||
|
||||
} // namespace zephyr
|
||||
|
||||
@@ -79,18 +79,6 @@ template<typename... Ts> class LambdaCondition : public Condition<Ts...> {
|
||||
std::function<bool(Ts...)> f_;
|
||||
};
|
||||
|
||||
/// Optimized lambda condition for stateless lambdas (no capture).
|
||||
/// Uses function pointer instead of std::function to reduce memory overhead.
|
||||
/// Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
template<typename... Ts> class StatelessLambdaCondition : public Condition<Ts...> {
|
||||
public:
|
||||
explicit StatelessLambdaCondition(bool (*f)(Ts...)) : f_(f) {}
|
||||
bool check(Ts... x) override { return this->f_(x...); }
|
||||
|
||||
protected:
|
||||
bool (*f_)(Ts...);
|
||||
};
|
||||
|
||||
template<typename... Ts> class ForCondition : public Condition<Ts...>, public Component {
|
||||
public:
|
||||
explicit ForCondition(Condition<> *condition) : condition_(condition) {}
|
||||
@@ -202,19 +190,6 @@ template<typename... Ts> class LambdaAction : public Action<Ts...> {
|
||||
std::function<void(Ts...)> f_;
|
||||
};
|
||||
|
||||
/// Optimized lambda action for stateless lambdas (no capture).
|
||||
/// Uses function pointer instead of std::function to reduce memory overhead.
|
||||
/// Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||
template<typename... Ts> class StatelessLambdaAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit StatelessLambdaAction(void (*f)(Ts...)) : f_(f) {}
|
||||
|
||||
void play(Ts... x) override { this->f_(x...); }
|
||||
|
||||
protected:
|
||||
void (*f_)(Ts...);
|
||||
};
|
||||
|
||||
template<typename... Ts> class IfAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit IfAction(Condition<Ts...> *condition) : condition_(condition) {}
|
||||
|
||||
@@ -87,7 +87,6 @@
|
||||
#define USE_MDNS_STORE_SERVICES
|
||||
#define MDNS_SERVICE_COUNT 3
|
||||
#define MDNS_DYNAMIC_TXT_COUNT 3
|
||||
#define SNTP_SERVER_COUNT 3
|
||||
#define USE_MEDIA_PLAYER
|
||||
#define USE_NEXTION_TFT_UPLOAD
|
||||
#define USE_NUMBER
|
||||
|
||||
@@ -105,9 +105,7 @@ async def setup_entity(var: MockObj, config: ConfigType, platform: str) -> None:
|
||||
config[CONF_NAME],
|
||||
platform,
|
||||
)
|
||||
# Only set disabled_by_default if True (default is False)
|
||||
if config[CONF_DISABLED_BY_DEFAULT]:
|
||||
add(var.set_disabled_by_default(True))
|
||||
add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
|
||||
if CONF_INTERNAL in config:
|
||||
add(var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_ICON in config:
|
||||
|
||||
@@ -198,8 +198,6 @@ class LambdaExpression(Expression):
|
||||
self.return_type = safe_exp(return_type) if return_type is not None else None
|
||||
|
||||
def __str__(self):
|
||||
# Stateless lambdas (empty capture) implicitly convert to function pointers
|
||||
# when assigned to function pointer types - no unary + needed
|
||||
cpp = f"[{self.capture}]({self.parameters})"
|
||||
if self.return_type is not None:
|
||||
cpp += f" -> {self.return_type}"
|
||||
@@ -702,12 +700,6 @@ async def process_lambda(
|
||||
parts[i * 3 + 1] = var
|
||||
parts[i * 3 + 2] = ""
|
||||
|
||||
# All id() references are global variables in generated C++ code.
|
||||
# Global variables should not be captured - they're accessible everywhere.
|
||||
# Use empty capture instead of capture-by-value.
|
||||
if capture == "=":
|
||||
capture = ""
|
||||
|
||||
if isinstance(value, ESPHomeDataBase) and value.esp_range is not None:
|
||||
location = value.esp_range.start_mark
|
||||
location.line += value.content_offset
|
||||
|
||||
@@ -224,37 +224,36 @@ def resolve_ip_address(
|
||||
return res
|
||||
|
||||
# Process hosts
|
||||
|
||||
cached_addresses: list[str] = []
|
||||
uncached_hosts: list[str] = []
|
||||
has_cache = address_cache is not None
|
||||
|
||||
for h in hosts:
|
||||
if is_ip_address(h):
|
||||
_add_ip_addresses_to_addrinfo([h], port, res)
|
||||
if has_cache:
|
||||
# If we have a cache, treat IPs as cached
|
||||
cached_addresses.append(h)
|
||||
else:
|
||||
# If no cache, pass IPs through to resolver with hostnames
|
||||
uncached_hosts.append(h)
|
||||
elif address_cache and (cached := address_cache.get_addresses(h)):
|
||||
_add_ip_addresses_to_addrinfo(cached, port, res)
|
||||
# Found in cache
|
||||
cached_addresses.extend(cached)
|
||||
else:
|
||||
# Not cached, need to resolve
|
||||
if address_cache and address_cache.has_cache():
|
||||
_LOGGER.info("Host %s not in cache, will need to resolve", h)
|
||||
uncached_hosts.append(h)
|
||||
|
||||
# Process cached addresses (includes direct IPs and cached lookups)
|
||||
_add_ip_addresses_to_addrinfo(cached_addresses, port, res)
|
||||
|
||||
# If we have uncached hosts (only non-IP hostnames), resolve them
|
||||
if uncached_hosts:
|
||||
from aioesphomeapi.host_resolver import AddrInfo as AioAddrInfo
|
||||
|
||||
from esphome.core import EsphomeError
|
||||
from esphome.resolver import AsyncResolver
|
||||
|
||||
resolver = AsyncResolver(uncached_hosts, port)
|
||||
addr_infos: list[AioAddrInfo] = []
|
||||
try:
|
||||
addr_infos = resolver.resolve()
|
||||
except EsphomeError as err:
|
||||
if not res:
|
||||
# No pre-resolved addresses available, DNS resolution is fatal
|
||||
raise
|
||||
_LOGGER.info("%s (using %d already resolved IP addresses)", err, len(res))
|
||||
|
||||
# Convert aioesphomeapi AddrInfo to our format
|
||||
for addr_info in addr_infos:
|
||||
sockaddr = addr_info.sockaddr
|
||||
|
||||
@@ -19,9 +19,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Home Automation",
|
||||
]
|
||||
|
||||
# Python 3.14 is currently not supported by IDF <= 5.5.1, see https://github.com/esphome/esphome/issues/11502
|
||||
requires-python = ">=3.11.0,<3.14"
|
||||
requires-python = ">=3.11.0"
|
||||
|
||||
dynamic = ["dependencies", "optional-dependencies", "version"]
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ esphome-dashboard==20251013.0
|
||||
aioesphomeapi==42.3.0
|
||||
zeroconf==0.148.0
|
||||
puremagic==1.30
|
||||
ruamel.yaml==0.18.16 # dashboard_import
|
||||
ruamel.yaml==0.18.15 # dashboard_import
|
||||
ruamel.yaml.clib==0.2.14 # dashboard_import
|
||||
esphome-glyphsets==0.2.0
|
||||
pillow==11.3.0
|
||||
|
||||
@@ -77,7 +77,6 @@ ISOLATED_COMPONENTS = {
|
||||
"esphome": "Defines devices/areas in esphome: section that are referenced in other sections - breaks when merged",
|
||||
"ethernet": "Defines ethernet: which conflicts with wifi: used by most components",
|
||||
"ethernet_info": "Related to ethernet component which conflicts with wifi",
|
||||
"gps": "TinyGPSPlus library declares millis() function that creates ambiguity with ESPHome millis() macro when merged with components using millis() in lambdas",
|
||||
"lvgl": "Defines multiple SDL displays on host platform that conflict when merged with other display configs",
|
||||
"mapping": "Uses dict format for image/display sections incompatible with standard list format - ESPHome merge_config cannot handle",
|
||||
"openthread": "Conflicts with wifi: used by most components",
|
||||
|
||||
@@ -58,7 +58,7 @@ def test_text_config_value_mode_set(generate_main):
|
||||
|
||||
def test_text_config_lamda_is_set(generate_main):
|
||||
"""
|
||||
Test if lambda is set for lambda mode (optimized with stateless lambda)
|
||||
Test if lambda is set for lambda mode
|
||||
"""
|
||||
# Given
|
||||
|
||||
@@ -66,5 +66,5 @@ def test_text_config_lamda_is_set(generate_main):
|
||||
main_cpp = generate_main("tests/component_tests/text/test_text.yaml")
|
||||
|
||||
# Then
|
||||
assert "it_4->set_template([]() -> esphome::optional<std::string> {" in main_cpp
|
||||
assert "it_4->set_template([=]() -> esphome::optional<std::string> {" in main_cpp
|
||||
assert 'return std::string{"Hello"};' in main_cpp
|
||||
|
||||
@@ -6,14 +6,14 @@ sensor:
|
||||
- platform: template
|
||||
id: template_humidity
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return 0.6;
|
||||
}
|
||||
return 0.0;
|
||||
- platform: template
|
||||
id: template_temperature
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return 42.0;
|
||||
}
|
||||
return 0.0;
|
||||
|
||||
@@ -3,7 +3,7 @@ sensor:
|
||||
id: template_sensor
|
||||
name: Template Sensor
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return 42.0;
|
||||
}
|
||||
return 0.0;
|
||||
|
||||
@@ -10,7 +10,7 @@ sensor:
|
||||
- platform: template
|
||||
id: template_sensor1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return 42.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
|
||||
@@ -2,21 +2,21 @@ binary_sensor:
|
||||
- platform: template
|
||||
id: bin1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
- platform: template
|
||||
id: bin2
|
||||
lambda: |-
|
||||
if (millis() > 20000) {
|
||||
if (esphome::millis() > 20000) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
- platform: template
|
||||
id: bin3
|
||||
lambda: |-
|
||||
if (millis() > 30000) {
|
||||
if (esphome::millis() > 30000) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -2,14 +2,14 @@ sensor:
|
||||
- platform: template
|
||||
id: template_temperature1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return 0.6;
|
||||
}
|
||||
return 0.0;
|
||||
- platform: template
|
||||
id: template_temperature2
|
||||
lambda: |-
|
||||
if (millis() > 20000) {
|
||||
if (esphome::millis() > 20000) {
|
||||
return 0.8;
|
||||
}
|
||||
return 0.0;
|
||||
|
||||
@@ -2,7 +2,7 @@ binary_sensor:
|
||||
- platform: template
|
||||
id: bin1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -2,7 +2,7 @@ binary_sensor:
|
||||
- platform: template
|
||||
id: bin1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -15,7 +15,7 @@ lock:
|
||||
id: test_lock1
|
||||
name: Template Lock
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return LOCK_STATE_LOCKED;
|
||||
}
|
||||
return LOCK_STATE_UNLOCKED;
|
||||
|
||||
@@ -25,7 +25,7 @@ sensor:
|
||||
- platform: template
|
||||
id: template_sensor1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return 42.0;
|
||||
}
|
||||
return 0.0;
|
||||
|
||||
@@ -33,7 +33,7 @@ sensor:
|
||||
- platform: template
|
||||
id: template_sensor1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return 42.0;
|
||||
}
|
||||
return 0.0;
|
||||
@@ -46,7 +46,7 @@ text_sensor:
|
||||
- platform: template
|
||||
id: template_text_sensor1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return {"Hello World"};
|
||||
}
|
||||
return {"Goodbye (cruel) World"};
|
||||
@@ -56,7 +56,7 @@ binary_sensor:
|
||||
- platform: template
|
||||
id: template_binary_sensor1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -65,7 +65,7 @@ switch:
|
||||
- platform: template
|
||||
id: template_switch1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -79,7 +79,7 @@ cover:
|
||||
- platform: template
|
||||
id: template_cover1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return COVER_OPEN;
|
||||
}
|
||||
return COVER_CLOSED;
|
||||
@@ -88,7 +88,7 @@ lock:
|
||||
- platform: template
|
||||
id: template_lock1
|
||||
lambda: |-
|
||||
if (millis() > 10000) {
|
||||
if (esphome::millis() > 10000) {
|
||||
return LOCK_STATE_LOCKED;
|
||||
}
|
||||
return LOCK_STATE_UNLOCKED;
|
||||
|
||||
@@ -173,61 +173,6 @@ class TestLambdaExpression:
|
||||
"}"
|
||||
)
|
||||
|
||||
def test_str__stateless_no_return(self):
|
||||
"""Test stateless lambda (empty capture) generates correctly"""
|
||||
target = cg.LambdaExpression(
|
||||
('ESP_LOGD("main", "Test message");',),
|
||||
(), # No parameters
|
||||
"", # Empty capture (stateless)
|
||||
)
|
||||
|
||||
actual = str(target)
|
||||
|
||||
assert actual == ('[]() {\n ESP_LOGD("main", "Test message");\n}')
|
||||
|
||||
def test_str__stateless_with_return(self):
|
||||
"""Test stateless lambda with return type generates correctly"""
|
||||
target = cg.LambdaExpression(
|
||||
("return global_value > 0;",),
|
||||
(), # No parameters
|
||||
"", # Empty capture (stateless)
|
||||
bool, # Return type
|
||||
)
|
||||
|
||||
actual = str(target)
|
||||
|
||||
assert actual == ("[]() -> bool {\n return global_value > 0;\n}")
|
||||
|
||||
def test_str__stateless_with_params(self):
|
||||
"""Test stateless lambda with parameters generates correctly"""
|
||||
target = cg.LambdaExpression(
|
||||
("return foo + bar;",),
|
||||
((int, "foo"), (float, "bar")),
|
||||
"", # Empty capture (stateless)
|
||||
float,
|
||||
)
|
||||
|
||||
actual = str(target)
|
||||
|
||||
assert actual == (
|
||||
"[](int32_t foo, float bar) -> float {\n return foo + bar;\n}"
|
||||
)
|
||||
|
||||
def test_str__with_capture(self):
|
||||
"""Test lambda with capture generates correctly"""
|
||||
target = cg.LambdaExpression(
|
||||
("return captured_var + x;",),
|
||||
((int, "x"),),
|
||||
"captured_var", # Has capture (not stateless)
|
||||
int,
|
||||
)
|
||||
|
||||
actual = str(target)
|
||||
|
||||
assert actual == (
|
||||
"[captured_var](int32_t x) -> int32_t {\n return captured_var + x;\n}"
|
||||
)
|
||||
|
||||
|
||||
class TestLiterals:
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -454,27 +454,9 @@ def test_resolve_ip_address_mixed_list() -> None:
|
||||
# Mix of IP and hostname - should use async resolver
|
||||
result = helpers.resolve_ip_address(["192.168.1.100", "test.local"], 6053)
|
||||
|
||||
assert len(result) == 2
|
||||
assert result[0][4][0] == "192.168.1.100"
|
||||
assert result[1][4][0] == "192.168.1.200"
|
||||
MockResolver.assert_called_once_with(["test.local"], 6053)
|
||||
mock_resolver.resolve.assert_called_once()
|
||||
|
||||
|
||||
def test_resolve_ip_address_mixed_list_fail() -> None:
|
||||
"""Test resolving a mix of IPs and hostnames with resolve failed."""
|
||||
with patch("esphome.resolver.AsyncResolver") as MockResolver:
|
||||
mock_resolver = MockResolver.return_value
|
||||
mock_resolver.resolve.side_effect = EsphomeError(
|
||||
"Error resolving IP address: [test.local]"
|
||||
)
|
||||
|
||||
# Mix of IP and hostname - should use async resolver
|
||||
result = helpers.resolve_ip_address(["192.168.1.100", "test.local"], 6053)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0][4][0] == "192.168.1.100"
|
||||
MockResolver.assert_called_once_with(["test.local"], 6053)
|
||||
assert result[0][4][0] == "192.168.1.200"
|
||||
MockResolver.assert_called_once_with(["192.168.1.100", "test.local"], 6053)
|
||||
mock_resolver.resolve.assert_called_once()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user