mirror of
https://github.com/esphome/esphome.git
synced 2025-11-02 23:48:40 +00:00
Compare commits
11 Commits
web_server
...
determine_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9913d7d9e9 | ||
|
|
119dc8b530 | ||
|
|
3c8f4c1d5d | ||
|
|
4892638768 | ||
|
|
306448c02c | ||
|
|
eb73a23ab1 | ||
|
|
22cb0fbb70 | ||
|
|
04d22becd0 | ||
|
|
36a7951f7a | ||
|
|
81382db3d0 | ||
|
|
e0b02ccfc5 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -460,7 +460,7 @@ jobs:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
max-parallel: 1
|
||||
matrix:
|
||||
include:
|
||||
- id: clang-tidy
|
||||
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
|
||||
uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
@@ -86,6 +86,6 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
|
||||
uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -11,7 +11,7 @@ ci:
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.14.3
|
||||
rev: v0.14.2
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -207,14 +207,14 @@ def choose_upload_log_host(
|
||||
if has_mqtt_logging():
|
||||
resolved.append("MQTT")
|
||||
|
||||
if has_api() and has_non_ip_address() and has_resolvable_address():
|
||||
if has_api() and has_non_ip_address():
|
||||
resolved.extend(_resolve_with_cache(CORE.address, purpose))
|
||||
|
||||
elif purpose == Purpose.UPLOADING:
|
||||
if has_ota() and has_mqtt_ip_lookup():
|
||||
resolved.append("MQTTIP")
|
||||
|
||||
if has_ota() and has_non_ip_address() and has_resolvable_address():
|
||||
if has_ota() and has_non_ip_address():
|
||||
resolved.extend(_resolve_with_cache(CORE.address, purpose))
|
||||
else:
|
||||
resolved.append(device)
|
||||
@@ -318,17 +318,7 @@ def has_resolvable_address() -> bool:
|
||||
"""Check if CORE.address is resolvable (via mDNS, DNS, or is an IP address)."""
|
||||
# Any address (IP, mDNS hostname, or regular DNS hostname) is resolvable
|
||||
# The resolve_ip_address() function in helpers.py handles all types via AsyncResolver
|
||||
if CORE.address is None:
|
||||
return False
|
||||
|
||||
if has_ip_address():
|
||||
return True
|
||||
|
||||
if has_mdns():
|
||||
return True
|
||||
|
||||
# .local mDNS hostnames are only resolvable if mDNS is enabled
|
||||
return not CORE.address.endswith(".local")
|
||||
return CORE.address is not None
|
||||
|
||||
|
||||
def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str):
|
||||
|
||||
@@ -182,7 +182,7 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||
value = cv.Schema([extra_validators])(value)
|
||||
if single:
|
||||
if len(value) != 1:
|
||||
raise cv.Invalid("This trigger allows only a single automation")
|
||||
raise cv.Invalid("Cannot have more than 1 automation for templates")
|
||||
return value[0]
|
||||
return value
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// random comment
|
||||
|
||||
#include "api_connection.h"
|
||||
#ifdef USE_API
|
||||
#ifdef USE_API_NOISE
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
#include "e131_addressable_light_effect.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace esphome {
|
||||
namespace e131 {
|
||||
|
||||
@@ -78,14 +76,14 @@ void E131Component::loop() {
|
||||
}
|
||||
|
||||
void E131Component::add_effect(E131AddressableLightEffect *light_effect) {
|
||||
if (std::find(light_effects_.begin(), light_effects_.end(), light_effect) != light_effects_.end()) {
|
||||
if (light_effects_.count(light_effect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Registering '%s' for universes %d-%d.", light_effect->get_name(), light_effect->get_first_universe(),
|
||||
light_effect->get_last_universe());
|
||||
|
||||
light_effects_.push_back(light_effect);
|
||||
light_effects_.insert(light_effect);
|
||||
|
||||
for (auto universe = light_effect->get_first_universe(); universe <= light_effect->get_last_universe(); ++universe) {
|
||||
join_(universe);
|
||||
@@ -93,17 +91,14 @@ void E131Component::add_effect(E131AddressableLightEffect *light_effect) {
|
||||
}
|
||||
|
||||
void E131Component::remove_effect(E131AddressableLightEffect *light_effect) {
|
||||
auto it = std::find(light_effects_.begin(), light_effects_.end(), light_effect);
|
||||
if (it == light_effects_.end()) {
|
||||
if (!light_effects_.count(light_effect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Unregistering '%s' for universes %d-%d.", light_effect->get_name(), light_effect->get_first_universe(),
|
||||
light_effect->get_last_universe());
|
||||
|
||||
// Swap with last element and pop for O(1) removal (order doesn't matter)
|
||||
*it = light_effects_.back();
|
||||
light_effects_.pop_back();
|
||||
light_effects_.erase(light_effect);
|
||||
|
||||
for (auto universe = light_effect->get_first_universe(); universe <= light_effect->get_last_universe(); ++universe) {
|
||||
leave_(universe);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
@@ -46,8 +47,9 @@ class E131Component : public esphome::Component {
|
||||
|
||||
E131ListenMethod listen_method_{E131_MULTICAST};
|
||||
std::unique_ptr<socket::Socket> socket_;
|
||||
std::vector<E131AddressableLightEffect *> light_effects_;
|
||||
std::set<E131AddressableLightEffect *> light_effects_;
|
||||
std::map<int, int> universe_consumers_;
|
||||
std::map<int, E131Packet> universe_packets_;
|
||||
};
|
||||
|
||||
} // namespace e131
|
||||
|
||||
@@ -94,8 +94,6 @@ async def to_code(config):
|
||||
)
|
||||
use_interrupt = False
|
||||
|
||||
cg.add(var.set_use_interrupt(use_interrupt))
|
||||
if use_interrupt:
|
||||
cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE]))
|
||||
else:
|
||||
# Only generate call when disabling interrupts (default is true)
|
||||
cg.add(var.set_use_interrupt(use_interrupt))
|
||||
|
||||
@@ -671,33 +671,18 @@ async def write_image(config, all_frames=False):
|
||||
resize = config.get(CONF_RESIZE)
|
||||
if is_svg_file(path):
|
||||
# Local import so use of non-SVG files needn't require cairosvg installed
|
||||
from pyexpat import ExpatError
|
||||
from xml.etree.ElementTree import ParseError
|
||||
|
||||
from cairosvg import svg2png
|
||||
from cairosvg.helpers import PointError
|
||||
|
||||
if not resize:
|
||||
resize = (None, None)
|
||||
try:
|
||||
with open(path, "rb") as file:
|
||||
image = svg2png(
|
||||
file_obj=file,
|
||||
output_width=resize[0],
|
||||
output_height=resize[1],
|
||||
)
|
||||
image = Image.open(io.BytesIO(image))
|
||||
width, height = image.size
|
||||
except (
|
||||
ValueError,
|
||||
ParseError,
|
||||
IndexError,
|
||||
ExpatError,
|
||||
AttributeError,
|
||||
TypeError,
|
||||
PointError,
|
||||
) as e:
|
||||
raise core.EsphomeError(f"Could not load SVG image {path}: {e}") from e
|
||||
with open(path, "rb") as file:
|
||||
image = svg2png(
|
||||
file_obj=file,
|
||||
output_width=resize[0],
|
||||
output_height=resize[1],
|
||||
)
|
||||
image = Image.open(io.BytesIO(image))
|
||||
width, height = image.size
|
||||
else:
|
||||
image = Image.open(path)
|
||||
width, height = image.size
|
||||
|
||||
@@ -138,7 +138,6 @@ def _concat_nodes_override(values: Iterator[Any]) -> Any:
|
||||
values = chain(head, values)
|
||||
raw = "".join([str(v) for v in values])
|
||||
|
||||
result = None
|
||||
try:
|
||||
# Attempt to parse the concatenated string into a Python literal.
|
||||
# This allows expressions like "1 + 2" to be evaluated to the integer 3.
|
||||
@@ -146,16 +145,11 @@ def _concat_nodes_override(values: Iterator[Any]) -> Any:
|
||||
# fall back to returning the raw string. This is consistent with
|
||||
# Home Assistant's behavior when evaluating templates
|
||||
result = literal_eval(raw)
|
||||
except (ValueError, SyntaxError, MemoryError, TypeError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(result, set):
|
||||
# Sets are not supported, return raw string
|
||||
return raw
|
||||
|
||||
if not isinstance(result, str):
|
||||
return result
|
||||
|
||||
except (ValueError, SyntaxError, MemoryError, TypeError):
|
||||
pass
|
||||
return raw
|
||||
|
||||
|
||||
|
||||
@@ -6,21 +6,17 @@ namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.binary_sensor";
|
||||
|
||||
void TemplateBinarySensor::setup() {
|
||||
if (!this->f_.has_value()) {
|
||||
this->disable_loop();
|
||||
} else {
|
||||
this->loop();
|
||||
}
|
||||
}
|
||||
void TemplateBinarySensor::setup() { this->loop(); }
|
||||
|
||||
void TemplateBinarySensor::loop() {
|
||||
auto s = this->f_();
|
||||
if (!this->f_.has_value())
|
||||
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_
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -9,7 +8,7 @@ namespace template_ {
|
||||
|
||||
class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<bool> (*f)()) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
@@ -18,7 +17,7 @@ class TemplateBinarySensor : public Component, public binary_sensor::BinarySenso
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
protected:
|
||||
TemplateLambda<bool> f_;
|
||||
optional<optional<bool> (*)()> f_;
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
|
||||
@@ -33,27 +33,28 @@ void TemplateCover::setup() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!this->state_f_.has_value() && !this->tilt_f_.has_value())
|
||||
this->disable_loop();
|
||||
}
|
||||
void TemplateCover::loop() {
|
||||
bool changed = false;
|
||||
|
||||
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->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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto tilt = this->tilt_f_();
|
||||
if (tilt.has_value()) {
|
||||
auto tilt_val = clamp(*tilt, 0.0f, 1.0f);
|
||||
if (tilt_val != this->tilt) {
|
||||
this->tilt = tilt_val;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +63,7 @@ void TemplateCover::loop() {
|
||||
}
|
||||
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(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_; }
|
||||
@@ -122,6 +124,7 @@ CoverTraits TemplateCover::get_traits() {
|
||||
}
|
||||
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(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; }
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
#include "esphome/components/cover/cover.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -18,8 +17,7 @@ class TemplateCover : public cover::Cover, public Component {
|
||||
public:
|
||||
TemplateCover();
|
||||
|
||||
template<typename F> void set_state_lambda(F &&f) { this->state_f_.set(std::forward<F>(f)); }
|
||||
template<typename F> void set_tilt_lambda(F &&f) { this->tilt_f_.set(std::forward<F>(f)); }
|
||||
void set_state_lambda(optional<float> (*f)());
|
||||
Trigger<> *get_open_trigger() const;
|
||||
Trigger<> *get_close_trigger() const;
|
||||
Trigger<> *get_stop_trigger() const;
|
||||
@@ -28,6 +26,7 @@ class TemplateCover : public cover::Cover, public Component {
|
||||
Trigger<float> *get_tilt_trigger() const;
|
||||
void set_optimistic(bool optimistic);
|
||||
void set_assumed_state(bool assumed_state);
|
||||
void set_tilt_lambda(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);
|
||||
@@ -46,8 +45,8 @@ class TemplateCover : public cover::Cover, public Component {
|
||||
void stop_prev_trigger_();
|
||||
|
||||
TemplateCoverRestoreMode restore_mode_{COVER_RESTORE};
|
||||
TemplateLambda<float> state_f_;
|
||||
TemplateLambda<float> tilt_f_;
|
||||
optional<optional<float> (*)()> state_f_;
|
||||
optional<optional<float> (*)()> tilt_f_;
|
||||
bool assumed_state_{false};
|
||||
bool optimistic_{false};
|
||||
Trigger<> *open_trigger_;
|
||||
|
||||
@@ -40,13 +40,14 @@ void TemplateDate::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = this->f_();
|
||||
if (val.has_value()) {
|
||||
this->year_ = val->year;
|
||||
this->month_ = val->month;
|
||||
this->day_ = val->day_of_month;
|
||||
this->publish_state();
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -9,14 +9,13 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/time.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateDate : public datetime::DateEntity, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<ESPTime> (*f)()) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
@@ -36,7 +35,7 @@ class TemplateDate : public datetime::DateEntity, public PollingComponent {
|
||||
ESPTime initial_value_{};
|
||||
bool restore_value_{false};
|
||||
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
|
||||
TemplateLambda<ESPTime> f_;
|
||||
optional<optional<ESPTime> (*)()> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
@@ -43,16 +43,17 @@ void TemplateDateTime::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = this->f_();
|
||||
if (val.has_value()) {
|
||||
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();
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -9,14 +9,13 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/time.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<ESPTime> (*f)()) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
@@ -36,7 +35,7 @@ class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponen
|
||||
ESPTime initial_value_{};
|
||||
bool restore_value_{false};
|
||||
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
|
||||
TemplateLambda<ESPTime> f_;
|
||||
optional<optional<ESPTime> (*)()> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
@@ -40,13 +40,14 @@ void TemplateTime::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = this->f_();
|
||||
if (val.has_value()) {
|
||||
this->hour_ = val->hour;
|
||||
this->minute_ = val->minute;
|
||||
this->second_ = val->second;
|
||||
this->publish_state();
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -9,14 +9,13 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/time.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateTime : public datetime::TimeEntity, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<ESPTime> (*f)()) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
@@ -36,7 +35,7 @@ class TemplateTime : public datetime::TimeEntity, public PollingComponent {
|
||||
ESPTime initial_value_{};
|
||||
bool restore_value_{false};
|
||||
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
|
||||
TemplateLambda<ESPTime> f_;
|
||||
optional<optional<ESPTime> (*)()> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
@@ -11,16 +11,14 @@ static const char *const TAG = "template.lock";
|
||||
TemplateLock::TemplateLock()
|
||||
: lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {}
|
||||
|
||||
void TemplateLock::setup() {
|
||||
if (!this->f_.has_value())
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
void TemplateLock::loop() {
|
||||
auto val = this->f_();
|
||||
if (val.has_value()) {
|
||||
this->publish_state(*val);
|
||||
}
|
||||
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) {
|
||||
@@ -47,6 +45,7 @@ void TemplateLock::open_latch() {
|
||||
this->open_trigger_->trigger();
|
||||
}
|
||||
void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void TemplateLock::set_state_lambda(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_; }
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
#include "esphome/components/lock/lock.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -12,10 +11,9 @@ class TemplateLock : public lock::Lock, public Component {
|
||||
public:
|
||||
TemplateLock();
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
template<typename F> void set_state_lambda(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_state_lambda(optional<lock::LockState> (*f)());
|
||||
Trigger<> *get_lock_trigger() const;
|
||||
Trigger<> *get_unlock_trigger() const;
|
||||
Trigger<> *get_open_trigger() const;
|
||||
@@ -28,7 +26,7 @@ class TemplateLock : public lock::Lock, public Component {
|
||||
void control(const lock::LockCall &call) override;
|
||||
void open_latch() override;
|
||||
|
||||
TemplateLambda<lock::LockState> f_;
|
||||
optional<optional<lock::LockState> (*)()> f_;
|
||||
bool optimistic_{false};
|
||||
Trigger<> *lock_trigger_;
|
||||
Trigger<> *unlock_trigger_;
|
||||
|
||||
@@ -30,10 +30,11 @@ void TemplateNumber::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = this->f_();
|
||||
if (val.has_value()) {
|
||||
this->publish_state(*val);
|
||||
}
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->publish_state(*val);
|
||||
}
|
||||
|
||||
void TemplateNumber::control(float value) {
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateNumber : public number::Number, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<float> (*f)()) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
@@ -29,7 +28,7 @@ class TemplateNumber : public number::Number, public PollingComponent {
|
||||
float initial_value_{NAN};
|
||||
bool restore_value_{false};
|
||||
Trigger<float> *set_trigger_ = new Trigger<float>();
|
||||
TemplateLambda<float> f_;
|
||||
optional<optional<float> (*)()> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
@@ -31,14 +31,16 @@ void TemplateSelect::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = this->f_();
|
||||
if (val.has_value()) {
|
||||
if (!this->has_option(*val)) {
|
||||
ESP_LOGE(TAG, "Lambda returned an invalid option: %s", (*val).c_str());
|
||||
return;
|
||||
}
|
||||
this->publish_state(*val);
|
||||
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) {
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateSelect : public select::Select, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<std::string> (*f)()) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
@@ -29,7 +28,7 @@ class TemplateSelect : public select::Select, public PollingComponent {
|
||||
size_t initial_option_index_{0};
|
||||
bool restore_value_ = false;
|
||||
Trigger<std::string> *set_trigger_ = new Trigger<std::string>();
|
||||
TemplateLambda<std::string> f_;
|
||||
optional<optional<std::string> (*)()> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
@@ -11,14 +11,13 @@ void TemplateSensor::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = this->f_();
|
||||
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(optional<float> (*f)()) { this->f_ = f; }
|
||||
void TemplateSensor::dump_config() {
|
||||
LOG_SENSOR("", "Template Sensor", this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -9,7 +8,7 @@ namespace template_ {
|
||||
|
||||
class TemplateSensor : public sensor::Sensor, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<float> (*f)());
|
||||
|
||||
void update() override;
|
||||
|
||||
@@ -18,7 +17,7 @@ class TemplateSensor : public sensor::Sensor, public PollingComponent {
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
TemplateLambda<float> f_;
|
||||
optional<optional<float> (*)()> f_;
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
|
||||
@@ -9,10 +9,13 @@ static const char *const TAG = "template.switch";
|
||||
TemplateSwitch::TemplateSwitch() : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {}
|
||||
|
||||
void TemplateSwitch::loop() {
|
||||
auto s = this->f_();
|
||||
if (s.has_value()) {
|
||||
this->publish_state(*s);
|
||||
}
|
||||
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) {
|
||||
@@ -32,13 +35,11 @@ void TemplateSwitch::write_state(bool state) {
|
||||
}
|
||||
void TemplateSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
bool TemplateSwitch::assumed_state() { return this->assumed_state_; }
|
||||
void TemplateSwitch::set_state_lambda(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() {
|
||||
if (!this->f_.has_value())
|
||||
this->disable_loop();
|
||||
|
||||
optional<bool> initial_state = this->get_initial_state_with_restore_mode();
|
||||
|
||||
if (initial_state.has_value()) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -15,7 +14,7 @@ class TemplateSwitch : public switch_::Switch, public Component {
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
template<typename F> void set_state_lambda(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_state_lambda(optional<bool> (*f)());
|
||||
Trigger<> *get_turn_on_trigger() const;
|
||||
Trigger<> *get_turn_off_trigger() const;
|
||||
void set_optimistic(bool optimistic);
|
||||
@@ -29,7 +28,7 @@ class TemplateSwitch : public switch_::Switch, public Component {
|
||||
|
||||
void write_state(bool state) override;
|
||||
|
||||
TemplateLambda<bool> f_;
|
||||
optional<optional<bool> (*)()> f_;
|
||||
bool optimistic_{false};
|
||||
bool assumed_state_{false};
|
||||
Trigger<> *turn_on_trigger_;
|
||||
|
||||
@@ -7,8 +7,10 @@ namespace template_ {
|
||||
static const char *const TAG = "template.text";
|
||||
|
||||
void TemplateText::setup() {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
if (!(this->f_ == nullptr)) {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
}
|
||||
std::string value = this->initial_value_;
|
||||
if (!this->pref_) {
|
||||
ESP_LOGD(TAG, "State from initial: %s", value.c_str());
|
||||
@@ -24,13 +26,17 @@ void TemplateText::setup() {
|
||||
}
|
||||
|
||||
void TemplateText::update() {
|
||||
if (this->f_ == nullptr)
|
||||
return;
|
||||
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = this->f_();
|
||||
if (val.has_value()) {
|
||||
this->publish_state(*val);
|
||||
}
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->publish_state(*val);
|
||||
}
|
||||
|
||||
void TemplateText::control(const std::string &value) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
@@ -62,7 +61,7 @@ template<uint8_t SZ> class TextSaver : public TemplateTextSaverBase {
|
||||
|
||||
class TemplateText : public text::Text, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<std::string> (*f)()) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
@@ -79,7 +78,7 @@ class TemplateText : public text::Text, public PollingComponent {
|
||||
bool optimistic_ = false;
|
||||
std::string initial_value_;
|
||||
Trigger<std::string> *set_trigger_ = new Trigger<std::string>();
|
||||
TemplateLambda<std::string> f_{};
|
||||
optional<optional<std::string> (*)()> f_{nullptr};
|
||||
|
||||
TemplateTextSaverBase *pref_ = nullptr;
|
||||
};
|
||||
|
||||
@@ -10,14 +10,13 @@ void TemplateTextSensor::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = this->f_();
|
||||
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(optional<std::string> (*f)()) { this->f_ = f; }
|
||||
void TemplateTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); }
|
||||
|
||||
} // namespace template_
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -10,7 +9,7 @@ namespace template_ {
|
||||
|
||||
class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
void set_template(optional<std::string> (*f)());
|
||||
|
||||
void update() override;
|
||||
|
||||
@@ -19,7 +18,7 @@ class TemplateTextSensor : public text_sensor::TextSensor, public PollingCompone
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
TemplateLambda<std::string> f_{};
|
||||
optional<optional<std::string> (*)()> f_{};
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
|
||||
@@ -33,19 +33,19 @@ void TemplateValve::setup() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!this->state_f_.has_value())
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
void TemplateValve::loop() {
|
||||
bool changed = false;
|
||||
|
||||
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->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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ void TemplateValve::loop() {
|
||||
|
||||
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(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_; }
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
#include "esphome/components/valve/valve.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -18,7 +17,7 @@ class TemplateValve : public valve::Valve, public Component {
|
||||
public:
|
||||
TemplateValve();
|
||||
|
||||
template<typename F> void set_state_lambda(F &&f) { this->state_f_.set(std::forward<F>(f)); }
|
||||
void set_state_lambda(optional<float> (*f)());
|
||||
Trigger<> *get_open_trigger() const;
|
||||
Trigger<> *get_close_trigger() const;
|
||||
Trigger<> *get_stop_trigger() const;
|
||||
@@ -43,7 +42,7 @@ class TemplateValve : public valve::Valve, public Component {
|
||||
void stop_prev_trigger_();
|
||||
|
||||
TemplateValveRestoreMode restore_mode_{VALVE_NO_RESTORE};
|
||||
TemplateLambda<float> state_f_;
|
||||
optional<optional<float> (*)()> state_f_;
|
||||
bool assumed_state_{false};
|
||||
bool optimistic_{false};
|
||||
Trigger<> *open_trigger_;
|
||||
|
||||
@@ -435,10 +435,9 @@ void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
||||
}
|
||||
void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (sensor::Sensor *obj : App.get_sensors()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
// Note: request->method() is always HTTP_GET here (canHandle ensures this)
|
||||
if (match.method_empty()) {
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
auto detail = get_request_detail(request);
|
||||
std::string data = this->sensor_json(obj, obj->state, detail);
|
||||
request->send(200, "application/json", data.c_str());
|
||||
@@ -478,10 +477,9 @@ void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s
|
||||
}
|
||||
void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (text_sensor::TextSensor *obj : App.get_text_sensors()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
// Note: request->method() is always HTTP_GET here (canHandle ensures this)
|
||||
if (match.method_empty()) {
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
auto detail = get_request_detail(request);
|
||||
std::string data = this->text_sensor_json(obj, obj->state, detail);
|
||||
request->send(200, "application/json", data.c_str());
|
||||
@@ -518,7 +516,7 @@ void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||
}
|
||||
void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (switch_::Switch *obj : App.get_switches()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -587,7 +585,7 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail
|
||||
#ifdef USE_BUTTON
|
||||
void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (button::Button *obj : App.get_buttons()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
auto detail = get_request_detail(request);
|
||||
@@ -629,10 +627,9 @@ void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) {
|
||||
}
|
||||
void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
// Note: request->method() is always HTTP_GET here (canHandle ensures this)
|
||||
if (match.method_empty()) {
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
auto detail = get_request_detail(request);
|
||||
std::string data = this->binary_sensor_json(obj, obj->state, detail);
|
||||
request->send(200, "application/json", data.c_str());
|
||||
@@ -668,7 +665,7 @@ void WebServer::on_fan_update(fan::Fan *obj) {
|
||||
}
|
||||
void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (fan::Fan *obj : App.get_fans()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -742,7 +739,7 @@ void WebServer::on_light_update(light::LightState *obj) {
|
||||
}
|
||||
void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (light::LightState *obj : App.get_lights()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -815,7 +812,7 @@ void WebServer::on_cover_update(cover::Cover *obj) {
|
||||
}
|
||||
void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (cover::Cover *obj : App.get_covers()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -900,7 +897,7 @@ void WebServer::on_number_update(number::Number *obj, float state) {
|
||||
}
|
||||
void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (auto *obj : App.get_numbers()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -965,7 +962,7 @@ void WebServer::on_date_update(datetime::DateEntity *obj) {
|
||||
}
|
||||
void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (auto *obj : App.get_dates()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
auto detail = get_request_detail(request);
|
||||
@@ -1020,7 +1017,7 @@ void WebServer::on_time_update(datetime::TimeEntity *obj) {
|
||||
}
|
||||
void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (auto *obj : App.get_times()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
auto detail = get_request_detail(request);
|
||||
@@ -1074,7 +1071,7 @@ void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) {
|
||||
}
|
||||
void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (auto *obj : App.get_datetimes()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
auto detail = get_request_detail(request);
|
||||
@@ -1129,7 +1126,7 @@ void WebServer::on_text_update(text::Text *obj, const std::string &state) {
|
||||
}
|
||||
void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (auto *obj : App.get_texts()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -1183,7 +1180,7 @@ void WebServer::on_select_update(select::Select *obj, const std::string &state,
|
||||
}
|
||||
void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (auto *obj : App.get_selects()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -1239,7 +1236,7 @@ void WebServer::on_climate_update(climate::Climate *obj) {
|
||||
}
|
||||
void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (auto *obj : App.get_climates()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -1380,7 +1377,7 @@ void WebServer::on_lock_update(lock::Lock *obj) {
|
||||
}
|
||||
void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (lock::Lock *obj : App.get_locks()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -1451,7 +1448,7 @@ void WebServer::on_valve_update(valve::Valve *obj) {
|
||||
}
|
||||
void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (valve::Valve *obj : App.get_valves()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -1532,7 +1529,7 @@ void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP
|
||||
}
|
||||
void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (alarm_control_panel::AlarmControlPanel *obj : App.get_alarm_control_panels()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
@@ -1611,11 +1608,10 @@ void WebServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||
|
||||
void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (event::Event *obj : App.get_events()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
// Note: request->method() is always HTTP_GET here (canHandle ensures this)
|
||||
if (match.method_empty()) {
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
auto detail = get_request_detail(request);
|
||||
std::string data = this->event_json(obj, "", detail);
|
||||
request->send(200, "application/json", data.c_str());
|
||||
@@ -1677,7 +1673,7 @@ void WebServer::on_update(update::UpdateEntity *obj) {
|
||||
}
|
||||
void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (update::UpdateEntity *obj : App.get_updates()) {
|
||||
if (!match.id_equals_entity(obj))
|
||||
if (!match.id_equals(obj->get_object_id()))
|
||||
continue;
|
||||
|
||||
if (request->method() == HTTP_GET && match.method_empty()) {
|
||||
|
||||
@@ -48,15 +48,8 @@ struct UrlMatch {
|
||||
return domain && domain_len == strlen(str) && memcmp(domain, str, domain_len) == 0;
|
||||
}
|
||||
|
||||
bool id_equals_entity(EntityBase *entity) const {
|
||||
// Zero-copy comparison using StringRef
|
||||
StringRef static_ref = entity->get_object_id_ref_for_api_();
|
||||
if (!static_ref.empty()) {
|
||||
return id && id_len == static_ref.size() && memcmp(id, static_ref.c_str(), id_len) == 0;
|
||||
}
|
||||
// Fallback to allocation (rare)
|
||||
const auto &obj_id = entity->get_object_id();
|
||||
return id && id_len == obj_id.length() && memcmp(id, obj_id.c_str(), id_len) == 0;
|
||||
bool id_equals(const std::string &str) const {
|
||||
return id && id_len == str.length() && memcmp(id, str.c_str(), id_len) == 0;
|
||||
}
|
||||
|
||||
bool method_equals(const char *str) const {
|
||||
|
||||
@@ -17,10 +17,6 @@ namespace api {
|
||||
class APIConnection;
|
||||
} // namespace api
|
||||
|
||||
namespace web_server {
|
||||
struct UrlMatch;
|
||||
} // namespace web_server
|
||||
|
||||
enum EntityCategory : uint8_t {
|
||||
ENTITY_CATEGORY_NONE = 0,
|
||||
ENTITY_CATEGORY_CONFIG = 1,
|
||||
@@ -120,7 +116,6 @@ class EntityBase {
|
||||
|
||||
protected:
|
||||
friend class api::APIConnection;
|
||||
friend struct web_server::UrlMatch;
|
||||
|
||||
// Get object_id as StringRef when it's static (for API usage)
|
||||
// Returns empty StringRef if object_id is dynamic (needs allocation)
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/optional.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/** Lightweight wrapper for template platform lambdas (stateless function pointers only).
|
||||
*
|
||||
* This optimizes template platforms by storing only a function pointer (4 bytes on ESP32)
|
||||
* instead of std::function (16-32 bytes).
|
||||
*
|
||||
* IMPORTANT: This only supports stateless lambdas (no captures). The set_template() method
|
||||
* is an internal API used by YAML codegen, not intended for external use.
|
||||
*
|
||||
* Lambdas must return optional<T> to support the pattern:
|
||||
* return {}; // Don't publish a value
|
||||
* return 42.0; // Publish this value
|
||||
*
|
||||
* operator() returns optional<T>, returning nullopt when no lambda is set (nullptr check).
|
||||
*
|
||||
* @tparam T The return type (e.g., float for sensor values)
|
||||
* @tparam Args Optional arguments for the lambda
|
||||
*/
|
||||
template<typename T, typename... Args> class TemplateLambda {
|
||||
public:
|
||||
TemplateLambda() : f_(nullptr) {}
|
||||
|
||||
/** Set the lambda function pointer.
|
||||
* INTERNAL API: Only for use by YAML codegen.
|
||||
* Only stateless lambdas (no captures) are supported.
|
||||
*/
|
||||
void set(optional<T> (*f)(Args...)) { this->f_ = f; }
|
||||
|
||||
/** Check if a lambda is set */
|
||||
bool has_value() const { return this->f_ != nullptr; }
|
||||
|
||||
/** Call the lambda, returning nullopt if no lambda is set */
|
||||
optional<T> operator()(Args &&...args) {
|
||||
if (this->f_ == nullptr)
|
||||
return nullopt;
|
||||
return this->f_(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/** Alias for operator() for compatibility */
|
||||
optional<T> call(Args &&...args) { return (*this)(std::forward<Args>(args)...); }
|
||||
|
||||
protected:
|
||||
optional<T> (*f_)(Args...); // Function pointer (4 bytes on ESP32)
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
@@ -1,6 +1,6 @@
|
||||
pylint==4.0.2
|
||||
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
|
||||
ruff==0.14.3 # also change in .pre-commit-config.yaml when updating
|
||||
ruff==0.14.2 # also change in .pre-commit-config.yaml when updating
|
||||
pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating
|
||||
pre-commit
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ def test_gpio_binary_sensor_basic_setup(
|
||||
|
||||
assert "new gpio::GPIOBinarySensor();" in main_cpp
|
||||
assert "App.register_binary_sensor" in main_cpp
|
||||
# set_use_interrupt(true) should NOT be generated (uses C++ default)
|
||||
assert "bs_gpio->set_use_interrupt(true);" not in main_cpp
|
||||
assert "bs_gpio->set_use_interrupt(true);" in main_cpp
|
||||
assert "bs_gpio->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp
|
||||
|
||||
|
||||
@@ -52,8 +51,8 @@ def test_gpio_binary_sensor_esp8266_other_pins_use_interrupt(
|
||||
"tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml"
|
||||
)
|
||||
|
||||
# GPIO5 should still use interrupts (default, so no setter call)
|
||||
assert "bs_gpio5->set_use_interrupt(true);" not in main_cpp
|
||||
# GPIO5 should still use interrupts
|
||||
assert "bs_gpio5->set_use_interrupt(true);" in main_cpp
|
||||
assert "bs_gpio5->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp
|
||||
|
||||
|
||||
|
||||
@@ -9,13 +9,6 @@ esphome:
|
||||
id: template_sens
|
||||
state: !lambda "return 42.0;"
|
||||
|
||||
# Test C++ API: set_template() with stateless lambda (no captures)
|
||||
# NOTE: set_template() is not intended to be a public API, but we test it to ensure it doesn't break.
|
||||
- lambda: |-
|
||||
id(template_sens).set_template([]() -> esphome::optional<float> {
|
||||
return 123.0f;
|
||||
});
|
||||
|
||||
- datetime.date.set:
|
||||
id: test_date
|
||||
date:
|
||||
@@ -222,7 +215,6 @@ cover:
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
id: template_number
|
||||
name: "Template number"
|
||||
optimistic: true
|
||||
min_value: 0
|
||||
|
||||
@@ -32,7 +32,6 @@ switch:
|
||||
name: "Test Switch"
|
||||
id: test_switch
|
||||
optimistic: true
|
||||
lambda: return false;
|
||||
|
||||
interval:
|
||||
- interval: 0.5s
|
||||
|
||||
@@ -33,4 +33,3 @@ test_list:
|
||||
{{{ "x", "79"}, { "y", "82"}}}
|
||||
- '{{{"AA"}}}'
|
||||
- '"HELLO"'
|
||||
- '{ 79, 82 }'
|
||||
|
||||
@@ -34,4 +34,3 @@ test_list:
|
||||
{{{ "x", "${ position.x }"}, { "y", "${ position.y }"}}}
|
||||
- ${ '{{{"AA"}}}' }
|
||||
- ${ '"HELLO"' }
|
||||
- '{ ${position.x}, ${position.y} }'
|
||||
|
||||
@@ -744,7 +744,7 @@ def test_choose_upload_log_host_ota_local_all_options() -> None:
|
||||
check_default=None,
|
||||
purpose=Purpose.UPLOADING,
|
||||
)
|
||||
assert result == ["MQTTIP"]
|
||||
assert result == ["MQTTIP", "test.local"]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_serial_ports")
|
||||
@@ -794,7 +794,7 @@ def test_choose_upload_log_host_ota_local_all_options_logging() -> None:
|
||||
check_default=None,
|
||||
purpose=Purpose.LOGGING,
|
||||
)
|
||||
assert result == ["MQTTIP", "MQTT"]
|
||||
assert result == ["MQTTIP", "MQTT", "test.local"]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_no_mqtt_logging")
|
||||
@@ -1564,7 +1564,7 @@ def test_has_resolvable_address() -> None:
|
||||
setup_core(
|
||||
config={CONF_MDNS: {CONF_DISABLED: True}}, address="esphome-device.local"
|
||||
)
|
||||
assert has_resolvable_address() is False
|
||||
assert has_resolvable_address() is True
|
||||
|
||||
# Test with mDNS disabled and regular DNS hostname (resolvable)
|
||||
setup_core(config={CONF_MDNS: {CONF_DISABLED: True}}, address="device.example.com")
|
||||
|
||||
Reference in New Issue
Block a user