mirror of
https://github.com/esphome/esphome.git
synced 2026-01-28 07:22:11 -07:00
Compare commits
39 Commits
copilot/ad
...
runtime_st
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d25e385d9 | ||
|
|
22e0a8ce2e | ||
|
|
b4f63fd992 | ||
|
|
ded835ab63 | ||
|
|
73a249c075 | ||
|
|
fe6f27c526 | ||
|
|
f73c539ea7 | ||
|
|
f87aa384d0 | ||
|
|
f9687a2a31 | ||
|
|
f084d320fc | ||
|
|
f93382445e | ||
|
|
463363a08d | ||
|
|
a0790f926e | ||
|
|
ca59ab8f37 | ||
|
|
b2474c6de9 | ||
|
|
3aaf10b6a8 | ||
|
|
33f545a8e3 | ||
|
|
d056e1040b | ||
|
|
75a78b2bf3 | ||
|
|
cd6314dc96 | ||
|
|
f91bffff9a | ||
|
|
5cbe9af485 | ||
|
|
a7fbecb25c | ||
|
|
bf92d94863 | ||
|
|
9c3817f544 | ||
|
|
ee9e3315b6 | ||
|
|
67dea1e538 | ||
|
|
003b9c6c3f | ||
|
|
2f1a345905 | ||
|
|
7ef933abec | ||
|
|
4ddd40bcfb | ||
|
|
8ae901b3f1 | ||
|
|
bc49174920 | ||
|
|
123ee02d39 | ||
|
|
0cc8055757 | ||
|
|
27a212c14d | ||
|
|
65dc182526 | ||
|
|
dd91039ff1 | ||
|
|
1c9a9c7536 |
@@ -1 +1 @@
|
||||
15dc295268b2dcf75942f42759b3ddec64eba89f75525698eb39c95a7f4b14ce
|
||||
cf3d341206b4184ec8b7fe85141aef4fe4696aa720c3f8a06d4e57930574bdab
|
||||
|
||||
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@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4.31.11
|
||||
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.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@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4.31.11
|
||||
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -38,8 +38,10 @@ async def to_code(config):
|
||||
# https://github.com/ESP32Async/ESPAsyncTCP
|
||||
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")
|
||||
elif CORE.is_rp2040:
|
||||
# https://github.com/khoih-prog/AsyncTCP_RP2040W
|
||||
cg.add_library("khoih-prog/AsyncTCP_RP2040W", "1.2.0")
|
||||
# https://github.com/ayushsharma82/RPAsyncTCP
|
||||
# RPAsyncTCP is a drop-in replacement for AsyncTCP_RP2040W with better
|
||||
# ESPAsyncWebServer compatibility
|
||||
cg.add_library("ayushsharma82/RPAsyncTCP", "1.3.2")
|
||||
# Other platforms (host, etc) use socket-based implementation
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
// Use ESPAsyncTCP library for ESP8266 (always Arduino)
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(USE_RP2040)
|
||||
// Use AsyncTCP_RP2040W library for RP2040
|
||||
#include <AsyncTCP_RP2040W.h>
|
||||
// Use RPAsyncTCP library for RP2040
|
||||
#include <RPAsyncTCP.h>
|
||||
#else
|
||||
// Use socket-based implementation for other platforms
|
||||
#include "async_tcp_socket.h"
|
||||
|
||||
@@ -14,10 +14,7 @@ void log_binary_sensor(const char *tag, const char *prefix, const char *type, Bi
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
||||
|
||||
if (!obj->get_device_class_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class_ref().c_str());
|
||||
}
|
||||
LOG_ENTITY_DEVICE_CLASS(tag, prefix, *obj);
|
||||
}
|
||||
|
||||
void BinarySensor::publish_state(bool new_state) {
|
||||
|
||||
@@ -9,7 +9,7 @@ static const char *const TAG = "bl0940.number";
|
||||
void CalibrationNumber::setup() {
|
||||
float value = 0.0f;
|
||||
if (this->restore_value_) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
if (!this->pref_.load(&value)) {
|
||||
value = 0.0f;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,7 @@ void log_button(const char *tag, const char *prefix, const char *type, Button *o
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
||||
|
||||
if (!obj->get_icon_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon_ref().c_str());
|
||||
}
|
||||
LOG_ENTITY_ICON(tag, prefix, *obj);
|
||||
}
|
||||
|
||||
void Button::press() {
|
||||
|
||||
@@ -96,10 +96,16 @@ void CaptivePortal::start() {
|
||||
}
|
||||
|
||||
void CaptivePortal::handleRequest(AsyncWebServerRequest *req) {
|
||||
if (req->url() == ESPHOME_F("/config.json")) {
|
||||
#ifdef USE_ESP32
|
||||
char url_buf[AsyncWebServerRequest::URL_BUF_SIZE];
|
||||
StringRef url = req->url_to(url_buf);
|
||||
#else
|
||||
const auto &url = req->url();
|
||||
#endif
|
||||
if (url == ESPHOME_F("/config.json")) {
|
||||
this->handle_config(req);
|
||||
return;
|
||||
} else if (req->url() == ESPHOME_F("/wifisave")) {
|
||||
} else if (url == ESPHOME_F("/wifisave")) {
|
||||
this->handle_wifisave(req);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -360,8 +360,7 @@ void Climate::add_on_control_callback(std::function<void(ClimateCall &)> &&callb
|
||||
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
|
||||
|
||||
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_preference_hash() ^
|
||||
RESTORE_STATE_VERSION);
|
||||
this->rtc_ = this->make_entity_preference<ClimateDeviceRestoreState>(RESTORE_STATE_VERSION);
|
||||
ClimateDeviceRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "cover.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include <strings.h>
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::cover {
|
||||
|
||||
static const char *const TAG = "cover";
|
||||
@@ -39,13 +39,13 @@ Cover::Cover() : position{COVER_OPEN} {}
|
||||
|
||||
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
|
||||
CoverCall &CoverCall::set_command(const char *command) {
|
||||
if (strcasecmp(command, "OPEN") == 0) {
|
||||
if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("OPEN")) == 0) {
|
||||
this->set_command_open();
|
||||
} else if (strcasecmp(command, "CLOSE") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("CLOSE")) == 0) {
|
||||
this->set_command_close();
|
||||
} else if (strcasecmp(command, "STOP") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("STOP")) == 0) {
|
||||
this->set_command_stop();
|
||||
} else if (strcasecmp(command, "TOGGLE") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TOGGLE")) == 0) {
|
||||
this->set_command_toggle();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
||||
@@ -187,7 +187,7 @@ void Cover::publish_state(bool save) {
|
||||
}
|
||||
}
|
||||
optional<CoverRestoreState> Cover::restore_state_() {
|
||||
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_preference_hash());
|
||||
this->rtc_ = this->make_entity_preference<CoverRestoreState>();
|
||||
CoverRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
|
||||
@@ -20,9 +20,7 @@ const extern float COVER_CLOSED;
|
||||
if (traits_.get_is_assumed_state()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \
|
||||
} \
|
||||
if (!(obj)->get_device_class_ref().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class_ref().c_str()); \
|
||||
} \
|
||||
LOG_ENTITY_DEVICE_CLASS(TAG, prefix, *(obj)); \
|
||||
}
|
||||
|
||||
class Cover;
|
||||
|
||||
@@ -15,9 +15,7 @@ namespace esphome::datetime {
|
||||
#define LOG_DATETIME_DATE(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon_ref().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon_ref().c_str()); \
|
||||
} \
|
||||
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
|
||||
}
|
||||
|
||||
class DateCall;
|
||||
|
||||
@@ -15,9 +15,7 @@ namespace esphome::datetime {
|
||||
#define LOG_DATETIME_DATETIME(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon_ref().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon_ref().c_str()); \
|
||||
} \
|
||||
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
|
||||
}
|
||||
|
||||
class DateTimeCall;
|
||||
|
||||
@@ -15,9 +15,7 @@ namespace esphome::datetime {
|
||||
#define LOG_DATETIME_TIME(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon_ref().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon_ref().c_str()); \
|
||||
} \
|
||||
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
|
||||
}
|
||||
|
||||
class TimeCall;
|
||||
|
||||
@@ -41,7 +41,7 @@ void DutyTimeSensor::setup() {
|
||||
uint32_t seconds = 0;
|
||||
|
||||
if (this->restore_) {
|
||||
this->pref_ = global_preferences->make_preference<uint32_t>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<uint32_t>();
|
||||
this->pref_.load(&seconds);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ from .const import ( # noqa
|
||||
KEY_ESP32,
|
||||
KEY_EXTRA_BUILD_FILES,
|
||||
KEY_FLASH_SIZE,
|
||||
KEY_FULL_CERT_BUNDLE,
|
||||
KEY_PATH,
|
||||
KEY_REF,
|
||||
KEY_REPO,
|
||||
@@ -670,6 +671,7 @@ CONF_FREERTOS_IN_IRAM = "freertos_in_iram"
|
||||
CONF_RINGBUF_IN_IRAM = "ringbuf_in_iram"
|
||||
CONF_HEAP_IN_IRAM = "heap_in_iram"
|
||||
CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size"
|
||||
CONF_USE_FULL_CERTIFICATE_BUNDLE = "use_full_certificate_bundle"
|
||||
|
||||
# VFS requirement tracking
|
||||
# Components that need VFS features can call require_vfs_select() or require_vfs_dir()
|
||||
@@ -695,6 +697,18 @@ def require_vfs_dir() -> None:
|
||||
CORE.data[KEY_VFS_DIR_REQUIRED] = True
|
||||
|
||||
|
||||
def require_full_certificate_bundle() -> None:
|
||||
"""Request the full certificate bundle instead of the common-CAs-only bundle.
|
||||
|
||||
By default, ESPHome uses CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN which
|
||||
includes only CAs with >1% market share (~51 KB smaller than full bundle).
|
||||
This covers ~99% of websites including Let's Encrypt, DigiCert, Google, Amazon.
|
||||
|
||||
Call this from components that need to connect to services using uncommon CAs.
|
||||
"""
|
||||
CORE.data[KEY_ESP32][KEY_FULL_CERT_BUNDLE] = True
|
||||
|
||||
|
||||
def _parse_idf_component(value: str) -> ConfigType:
|
||||
"""Parse IDF component shorthand syntax like 'owner/component^version'"""
|
||||
# Match operator followed by version-like string (digit or *)
|
||||
@@ -776,6 +790,9 @@ FRAMEWORK_SCHEMA = cv.Schema(
|
||||
min=8192, max=32768
|
||||
),
|
||||
cv.Optional(CONF_ENABLE_OTA_ROLLBACK, default=True): cv.boolean,
|
||||
cv.Optional(
|
||||
CONF_USE_FULL_CERTIFICATE_BUNDLE, default=False
|
||||
): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list(
|
||||
@@ -1048,6 +1065,19 @@ async def to_code(config):
|
||||
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ESP_IDF")
|
||||
if use_platformio:
|
||||
cg.add_platformio_option("framework", "espidf")
|
||||
|
||||
# Wrap std::__throw_* functions to abort immediately, eliminating ~3KB of
|
||||
# exception class overhead. See throw_stubs.cpp for implementation.
|
||||
# ESP-IDF already compiles with -fno-exceptions, so this code was dead anyway.
|
||||
for mangled in [
|
||||
"_ZSt20__throw_length_errorPKc",
|
||||
"_ZSt19__throw_logic_errorPKc",
|
||||
"_ZSt20__throw_out_of_rangePKc",
|
||||
"_ZSt24__throw_out_of_range_fmtPKcz",
|
||||
"_ZSt17__throw_bad_allocv",
|
||||
"_ZSt25__throw_bad_function_callv",
|
||||
]:
|
||||
cg.add_build_flag(f"-Wl,--wrap={mangled}")
|
||||
else:
|
||||
cg.add_build_flag("-DUSE_ARDUINO")
|
||||
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
|
||||
@@ -1080,6 +1110,18 @@ async def to_code(config):
|
||||
|
||||
cg.add_build_flag("-Wno-nonnull-compare")
|
||||
|
||||
# Use CMN (common CAs) bundle by default to save ~51KB flash
|
||||
# CMN covers CAs with >1% market share (~99% of websites)
|
||||
# Components needing uncommon CAs can call require_full_certificate_bundle()
|
||||
use_full_bundle = conf[CONF_ADVANCED].get(
|
||||
CONF_USE_FULL_CERTIFICATE_BUNDLE, False
|
||||
) or CORE.data[KEY_ESP32].get(KEY_FULL_CERT_BUNDLE, False)
|
||||
add_idf_sdkconfig_option(
|
||||
"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL", use_full_bundle
|
||||
)
|
||||
if not use_full_bundle:
|
||||
add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN", True)
|
||||
|
||||
add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True)
|
||||
add_idf_sdkconfig_option(
|
||||
f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True
|
||||
|
||||
@@ -12,6 +12,7 @@ KEY_REFRESH = "refresh"
|
||||
KEY_PATH = "path"
|
||||
KEY_SUBMODULES = "submodules"
|
||||
KEY_EXTRA_BUILD_FILES = "extra_build_files"
|
||||
KEY_FULL_CERT_BUNDLE = "full_cert_bundle"
|
||||
|
||||
VARIANT_ESP32 = "ESP32"
|
||||
VARIANT_ESP32C2 = "ESP32C2"
|
||||
|
||||
57
esphome/components/esp32/throw_stubs.cpp
Normal file
57
esphome/components/esp32/throw_stubs.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Linker wrap stubs for std::__throw_* functions.
|
||||
*
|
||||
* ESP-IDF compiles with -fno-exceptions, so C++ exceptions always abort.
|
||||
* However, ESP-IDF only wraps low-level functions (__cxa_throw, etc.),
|
||||
* not the std::__throw_* functions that construct exception objects first.
|
||||
* This pulls in ~3KB of dead exception class code that can never run.
|
||||
*
|
||||
* ESP8266 Arduino already solved this: their toolchain rebuilds libstdc++
|
||||
* with throw functions that just call abort(). We achieve the same result
|
||||
* using linker --wrap without requiring toolchain changes.
|
||||
*
|
||||
* These stubs abort immediately with a descriptive message, allowing
|
||||
* the linker to dead-code eliminate the exception class infrastructure.
|
||||
*
|
||||
* Wrapped functions and their callers:
|
||||
* - std::__throw_length_error: std::string::reserve, std::vector::reserve
|
||||
* - std::__throw_logic_error: std::promise, std::packaged_task
|
||||
* - std::__throw_out_of_range: std::string::at, std::vector::at
|
||||
* - std::__throw_out_of_range_fmt: std::bitset::to_ulong
|
||||
* - std::__throw_bad_alloc: operator new
|
||||
* - std::__throw_bad_function_call: std::function::operator()
|
||||
*/
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#include "esp_system.h"
|
||||
|
||||
namespace esphome::esp32 {}
|
||||
|
||||
// Linker wraps for std::__throw_* - must be extern "C" at global scope.
|
||||
// Names must be __wrap_ + mangled name for the linker's --wrap option.
|
||||
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
extern "C" {
|
||||
|
||||
// std::__throw_length_error(char const*) - called when container size exceeds max_size()
|
||||
void __wrap__ZSt20__throw_length_errorPKc(const char *) { esp_system_abort("std::length_error"); }
|
||||
|
||||
// std::__throw_logic_error(char const*) - called for logic errors (e.g., promise already satisfied)
|
||||
void __wrap__ZSt19__throw_logic_errorPKc(const char *) { esp_system_abort("std::logic_error"); }
|
||||
|
||||
// std::__throw_out_of_range(char const*) - called by at() when index is out of bounds
|
||||
void __wrap__ZSt20__throw_out_of_rangePKc(const char *) { esp_system_abort("std::out_of_range"); }
|
||||
|
||||
// std::__throw_out_of_range_fmt(char const*, ...) - called by bitset::to_ulong when value doesn't fit
|
||||
void __wrap__ZSt24__throw_out_of_range_fmtPKcz(const char *, ...) { esp_system_abort("std::out_of_range"); }
|
||||
|
||||
// std::__throw_bad_alloc() - called when operator new fails
|
||||
void __wrap__ZSt17__throw_bad_allocv() { esp_system_abort("std::bad_alloc"); }
|
||||
|
||||
// std::__throw_bad_function_call() - called when invoking empty std::function
|
||||
void __wrap__ZSt25__throw_bad_function_callv() { esp_system_abort("std::bad_function_call"); }
|
||||
|
||||
} // extern "C"
|
||||
// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
|
||||
#endif // USE_ESP_IDF
|
||||
@@ -16,12 +16,8 @@ namespace event {
|
||||
#define LOG_EVENT(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon_ref().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon_ref().c_str()); \
|
||||
} \
|
||||
if (!(obj)->get_device_class_ref().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class_ref().c_str()); \
|
||||
} \
|
||||
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
|
||||
LOG_ENTITY_DEVICE_CLASS(TAG, prefix, *(obj)); \
|
||||
}
|
||||
|
||||
class Event : public EntityBase, public EntityBase_DeviceClass {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
@@ -19,7 +20,8 @@ static bool was_power_cycled() {
|
||||
#endif
|
||||
#ifdef USE_ESP8266
|
||||
auto reset_reason = EspClass::getResetReason();
|
||||
return strcasecmp(reset_reason.c_str(), "power On") == 0 || strcasecmp(reset_reason.c_str(), "external system") == 0;
|
||||
return ESPHOME_strcasecmp_P(reset_reason.c_str(), ESPHOME_PSTR("power On")) == 0 ||
|
||||
ESPHOME_strcasecmp_P(reset_reason.c_str(), ESPHOME_PSTR("external system")) == 0;
|
||||
#endif
|
||||
#ifdef USE_LIBRETINY
|
||||
auto reason = lt_get_reboot_reason();
|
||||
|
||||
@@ -227,8 +227,7 @@ void Fan::publish_state() {
|
||||
constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA;
|
||||
optional<FanRestoreState> Fan::restore_state_() {
|
||||
FanRestoreState recovered{};
|
||||
this->rtc_ =
|
||||
global_preferences->make_preference<FanRestoreState>(this->get_preference_hash() ^ RESTORE_STATE_VERSION);
|
||||
this->rtc_ = this->make_entity_preference<FanRestoreState>(RESTORE_STATE_VERSION);
|
||||
bool restored = this->rtc_.load(&recovered);
|
||||
|
||||
switch (this->restore_mode_) {
|
||||
|
||||
@@ -350,8 +350,7 @@ ClimateTraits HaierClimateBase::traits() { return traits_; }
|
||||
|
||||
void HaierClimateBase::initialization() {
|
||||
constexpr uint32_t restore_settings_version = 0xA77D21EF;
|
||||
this->base_rtc_ =
|
||||
global_preferences->make_preference<HaierBaseSettings>(this->get_preference_hash() ^ restore_settings_version);
|
||||
this->base_rtc_ = this->make_entity_preference<HaierBaseSettings>(restore_settings_version);
|
||||
HaierBaseSettings recovered;
|
||||
if (!this->base_rtc_.load(&recovered)) {
|
||||
recovered = {false, true};
|
||||
|
||||
@@ -515,8 +515,7 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) {
|
||||
void HonClimate::initialization() {
|
||||
HaierClimateBase::initialization();
|
||||
constexpr uint32_t restore_settings_version = 0x57EB59DDUL;
|
||||
this->hon_rtc_ =
|
||||
global_preferences->make_preference<HonSettings>(this->get_preference_hash() ^ restore_settings_version);
|
||||
this->hon_rtc_ = this->make_entity_preference<HonSettings>(restore_settings_version);
|
||||
HonSettings recovered;
|
||||
if (this->hon_rtc_.load(&recovered)) {
|
||||
this->settings_ = recovered;
|
||||
|
||||
@@ -126,7 +126,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
),
|
||||
cv.Optional(CONF_CA_CERTIFICATE_PATH): cv.All(
|
||||
cv.file_,
|
||||
cv.only_on(PLATFORM_HOST),
|
||||
cv.Any(cv.only_on(PLATFORM_HOST), cv.only_on_esp32),
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
@@ -160,7 +160,24 @@ async def to_code(config):
|
||||
cg.add(var.set_verify_ssl(config[CONF_VERIFY_SSL]))
|
||||
|
||||
if config.get(CONF_VERIFY_SSL):
|
||||
esp32.add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True)
|
||||
if ca_cert_path := config.get(CONF_CA_CERTIFICATE_PATH):
|
||||
with open(ca_cert_path, encoding="utf-8") as f:
|
||||
ca_cert_content = f.read()
|
||||
cg.add(var.set_ca_certificate(ca_cert_content))
|
||||
else:
|
||||
# Uses the certificate bundle configured in esp32 component.
|
||||
# By default, ESPHome uses the CMN (common CAs) bundle which covers
|
||||
# ~99% of websites including GitHub, Let's Encrypt, DigiCert, etc.
|
||||
# If connecting to services with uncommon CAs, components can call:
|
||||
# esp32.require_full_certificate_bundle()
|
||||
# Or users can set in their config:
|
||||
# esp32:
|
||||
# framework:
|
||||
# advanced:
|
||||
# use_full_certificate_bundle: true
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True
|
||||
)
|
||||
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_TLS_INSECURE",
|
||||
|
||||
@@ -27,8 +27,9 @@ void HttpRequestIDF::dump_config() {
|
||||
HttpRequestComponent::dump_config();
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Buffer Size RX: %u\n"
|
||||
" Buffer Size TX: %u",
|
||||
this->buffer_size_rx_, this->buffer_size_tx_);
|
||||
" Buffer Size TX: %u\n"
|
||||
" Custom CA Certificate: %s",
|
||||
this->buffer_size_rx_, this->buffer_size_tx_, YESNO(this->ca_certificate_ != nullptr));
|
||||
}
|
||||
|
||||
esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) {
|
||||
@@ -88,11 +89,15 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::perform(const std::string &url, c
|
||||
config.disable_auto_redirect = !this->follow_redirects_;
|
||||
config.max_redirection_count = this->redirect_limit_;
|
||||
config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
if (secure && this->verify_ssl_) {
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
}
|
||||
if (this->ca_certificate_ != nullptr) {
|
||||
config.cert_pem = this->ca_certificate_;
|
||||
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
} else {
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (this->useragent_ != nullptr) {
|
||||
config.user_agent = this->useragent_;
|
||||
|
||||
@@ -35,6 +35,7 @@ class HttpRequestIDF : public HttpRequestComponent {
|
||||
void set_buffer_size_rx(uint16_t buffer_size_rx) { this->buffer_size_rx_ = buffer_size_rx; }
|
||||
void set_buffer_size_tx(uint16_t buffer_size_tx) { this->buffer_size_tx_ = buffer_size_tx; }
|
||||
void set_verify_ssl(bool verify_ssl) { this->verify_ssl_ = verify_ssl; }
|
||||
void set_ca_certificate(const char *ca_certificate) { this->ca_certificate_ = ca_certificate; }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method, const std::string &body,
|
||||
@@ -44,6 +45,7 @@ class HttpRequestIDF : public HttpRequestComponent {
|
||||
uint16_t buffer_size_rx_{};
|
||||
uint16_t buffer_size_tx_{};
|
||||
bool verify_ssl_{true};
|
||||
const char *ca_certificate_{nullptr};
|
||||
|
||||
/// @brief Monitors the http client events to gather response headers
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
|
||||
|
||||
@@ -82,7 +82,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
uint32_t last_progress = 0;
|
||||
uint32_t update_start_time = millis();
|
||||
md5::MD5Digest md5_receive;
|
||||
std::unique_ptr<char[]> md5_receive_str(new char[33]);
|
||||
char md5_receive_str[33];
|
||||
|
||||
if (this->md5_expected_.empty() && !this->http_get_md5_()) {
|
||||
return OTA_MD5_INVALID;
|
||||
@@ -176,14 +176,14 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
|
||||
// verify MD5 is as expected and act accordingly
|
||||
md5_receive.calculate();
|
||||
md5_receive.get_hex(md5_receive_str.get());
|
||||
this->md5_computed_ = md5_receive_str.get();
|
||||
md5_receive.get_hex(md5_receive_str);
|
||||
this->md5_computed_ = md5_receive_str;
|
||||
if (strncmp(this->md5_computed_.c_str(), this->md5_expected_.c_str(), MD5_SIZE) != 0) {
|
||||
ESP_LOGE(TAG, "MD5 computed: %s - Aborting due to MD5 mismatch", this->md5_computed_.c_str());
|
||||
this->cleanup_(std::move(backend), container);
|
||||
return ota::OTA_RESPONSE_ERROR_MD5_MISMATCH;
|
||||
} else {
|
||||
backend->set_update_md5(md5_receive_str.get());
|
||||
backend->set_update_md5(md5_receive_str);
|
||||
}
|
||||
|
||||
container->end();
|
||||
|
||||
@@ -2,6 +2,25 @@
|
||||
|
||||
from . import BoardConfig
|
||||
|
||||
# Huidu HD-WF1
|
||||
BoardConfig(
|
||||
"huidu-hd-wf1",
|
||||
r1_pin=2,
|
||||
g1_pin=6,
|
||||
b1_pin=3,
|
||||
r2_pin=4,
|
||||
g2_pin=8,
|
||||
b2_pin=5,
|
||||
a_pin=39,
|
||||
b_pin=38,
|
||||
c_pin=37,
|
||||
d_pin=36,
|
||||
e_pin=12,
|
||||
lat_pin=33,
|
||||
oe_pin=35,
|
||||
clk_pin=34,
|
||||
)
|
||||
|
||||
# Huidu HD-WF2
|
||||
BoardConfig(
|
||||
"huidu-hd-wf2",
|
||||
|
||||
@@ -587,7 +587,7 @@ def _build_config_struct(
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
add_idf_component(
|
||||
name="esphome/esp-hub75",
|
||||
ref="0.3.0",
|
||||
ref="0.3.2",
|
||||
)
|
||||
|
||||
# Set compile-time configuration via build flags (so external library sees them)
|
||||
|
||||
@@ -10,7 +10,7 @@ static const char *const TAG = "integration";
|
||||
|
||||
void IntegrationSensor::setup() {
|
||||
if (this->restore_) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
float preference_value = 0;
|
||||
this->pref_.load(&preference_value);
|
||||
this->result_ = preference_value;
|
||||
|
||||
@@ -184,7 +184,7 @@ static inline bool validate_header_footer(const uint8_t *header_footer, const ui
|
||||
void LD2450Component::setup() {
|
||||
#ifdef USE_NUMBER
|
||||
if (this->presence_timeout_number_ != nullptr) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_preference_hash());
|
||||
this->pref_ = this->presence_timeout_number_->make_entity_preference<float>();
|
||||
this->set_presence_timeout();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -191,10 +191,17 @@ def _notify_old_style(config):
|
||||
|
||||
|
||||
# The dev and latest branches will be at *least* this version, which is what matters.
|
||||
# Use GitHub releases directly to avoid PlatformIO moderation delays.
|
||||
ARDUINO_VERSIONS = {
|
||||
"dev": (cv.Version(1, 9, 2), "https://github.com/libretiny-eu/libretiny.git"),
|
||||
"latest": (cv.Version(1, 9, 2), "libretiny"),
|
||||
"recommended": (cv.Version(1, 9, 2), None),
|
||||
"dev": (cv.Version(1, 11, 0), "https://github.com/libretiny-eu/libretiny.git"),
|
||||
"latest": (
|
||||
cv.Version(1, 11, 0),
|
||||
"https://github.com/libretiny-eu/libretiny.git#v1.11.0",
|
||||
),
|
||||
"recommended": (
|
||||
cv.Version(1, 11, 0),
|
||||
"https://github.com/libretiny-eu/libretiny.git#v1.11.0",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,9 +7,7 @@ namespace esphome::light {
|
||||
|
||||
class AddressableLightWrapper : public light::AddressableLight {
|
||||
public:
|
||||
explicit AddressableLightWrapper(light::LightState *light_state) : light_state_(light_state) {
|
||||
this->wrapper_state_ = new uint8_t[5]; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
explicit AddressableLightWrapper(light::LightState *light_state) : light_state_(light_state) {}
|
||||
|
||||
int32_t size() const override { return 1; }
|
||||
|
||||
@@ -118,7 +116,7 @@ class AddressableLightWrapper : public light::AddressableLight {
|
||||
}
|
||||
|
||||
light::LightState *light_state_;
|
||||
uint8_t *wrapper_state_;
|
||||
mutable uint8_t wrapper_state_[5]{};
|
||||
ColorMode color_mode_{ColorMode::UNKNOWN};
|
||||
};
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ void LightState::setup() {
|
||||
case LIGHT_RESTORE_DEFAULT_ON:
|
||||
case LIGHT_RESTORE_INVERTED_DEFAULT_OFF:
|
||||
case LIGHT_RESTORE_INVERTED_DEFAULT_ON:
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_preference_hash());
|
||||
this->rtc_ = this->make_entity_preference<LightStateRTCState>();
|
||||
// Attempt to load from preferences, else fall back to default values
|
||||
if (!this->rtc_.load(&recovered)) {
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_DEFAULT_ON ||
|
||||
@@ -57,7 +57,7 @@ void LightState::setup() {
|
||||
break;
|
||||
case LIGHT_RESTORE_AND_OFF:
|
||||
case LIGHT_RESTORE_AND_ON:
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_preference_hash());
|
||||
this->rtc_ = this->make_entity_preference<LightStateRTCState>();
|
||||
this->rtc_.load(&recovered);
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
|
||||
break;
|
||||
|
||||
@@ -14,9 +14,7 @@ class Lock;
|
||||
#define LOG_LOCK(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon_ref().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon_ref().c_str()); \
|
||||
} \
|
||||
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
|
||||
if ((obj)->traits.get_assumed_state()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \
|
||||
} \
|
||||
|
||||
@@ -21,7 +21,7 @@ class LVGLNumber : public number::Number, public Component {
|
||||
void setup() override {
|
||||
float value = this->value_lambda_();
|
||||
if (this->restore_) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
if (this->pref_.load(&value)) {
|
||||
this->control_lambda_(value);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class LVGLSelect : public select::Select, public Component {
|
||||
this->set_options_();
|
||||
if (this->restore_) {
|
||||
size_t index;
|
||||
this->pref_ = global_preferences->make_preference<size_t>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<size_t>();
|
||||
if (this->pref_.load(&index))
|
||||
this->widget_->set_selected_index(index, LV_ANIM_OFF);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace mhz19 {
|
||||
namespace esphome::mhz19 {
|
||||
|
||||
static const char *const TAG = "mhz19";
|
||||
static const uint8_t MHZ19_REQUEST_LENGTH = 8;
|
||||
@@ -17,6 +16,19 @@ static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM[] = {0xFF, 0x01, 0x
|
||||
static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88};
|
||||
static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x27, 0x10};
|
||||
|
||||
static const LogString *detection_range_to_log_string(MHZ19DetectionRange range) {
|
||||
switch (range) {
|
||||
case MHZ19_DETECTION_RANGE_0_2000PPM:
|
||||
return LOG_STR("0-2000 ppm");
|
||||
case MHZ19_DETECTION_RANGE_0_5000PPM:
|
||||
return LOG_STR("0-5000 ppm");
|
||||
case MHZ19_DETECTION_RANGE_0_10000PPM:
|
||||
return LOG_STR("0-10000 ppm");
|
||||
default:
|
||||
return LOG_STR("default");
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t mhz19_checksum(const uint8_t *command) {
|
||||
uint8_t sum = 0;
|
||||
for (uint8_t i = 1; i < MHZ19_REQUEST_LENGTH; i++) {
|
||||
@@ -91,24 +103,24 @@ void MHZ19Component::abc_disable() {
|
||||
this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, nullptr);
|
||||
}
|
||||
|
||||
void MHZ19Component::range_set(MHZ19DetectionRange detection_ppm) {
|
||||
switch (detection_ppm) {
|
||||
case MHZ19_DETECTION_RANGE_DEFAULT:
|
||||
ESP_LOGV(TAG, "Using previously set detection range (no change)");
|
||||
break;
|
||||
void MHZ19Component::range_set(MHZ19DetectionRange detection_range) {
|
||||
const uint8_t *command;
|
||||
switch (detection_range) {
|
||||
case MHZ19_DETECTION_RANGE_0_2000PPM:
|
||||
ESP_LOGD(TAG, "Setting detection range to 0 to 2000ppm");
|
||||
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM, nullptr);
|
||||
command = MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM;
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_5000PPM:
|
||||
ESP_LOGD(TAG, "Setting detection range to 0 to 5000ppm");
|
||||
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM, nullptr);
|
||||
command = MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM;
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_10000PPM:
|
||||
ESP_LOGD(TAG, "Setting detection range to 0 to 10000ppm");
|
||||
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM, nullptr);
|
||||
command = MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGV(TAG, "Using previously set detection range (no change)");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Setting detection range to %s", LOG_STR_ARG(detection_range_to_log_string(detection_range)));
|
||||
this->mhz19_write_command_(command, nullptr);
|
||||
}
|
||||
|
||||
bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) {
|
||||
@@ -140,24 +152,7 @@ void MHZ19Component::dump_config() {
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(TAG, " Warmup time: %" PRIu32 " s", this->warmup_seconds_);
|
||||
|
||||
const char *range_str;
|
||||
switch (this->detection_range_) {
|
||||
case MHZ19_DETECTION_RANGE_DEFAULT:
|
||||
range_str = "default";
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_2000PPM:
|
||||
range_str = "0 to 2000ppm";
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_5000PPM:
|
||||
range_str = "0 to 5000ppm";
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_10000PPM:
|
||||
range_str = "0 to 10000ppm";
|
||||
break;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Detection range: %s", range_str);
|
||||
ESP_LOGCONFIG(TAG, " Detection range: %s", LOG_STR_ARG(detection_range_to_log_string(this->detection_range_)));
|
||||
}
|
||||
|
||||
} // namespace mhz19
|
||||
} // namespace esphome
|
||||
} // namespace esphome::mhz19
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mhz19 {
|
||||
namespace esphome::mhz19 {
|
||||
|
||||
enum MHZ19ABCLogic {
|
||||
MHZ19_ABC_NONE = 0,
|
||||
@@ -32,7 +31,7 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice {
|
||||
void calibrate_zero();
|
||||
void abc_enable();
|
||||
void abc_disable();
|
||||
void range_set(MHZ19DetectionRange detection_ppm);
|
||||
void range_set(MHZ19DetectionRange detection_range);
|
||||
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; }
|
||||
@@ -74,5 +73,4 @@ template<typename... Ts> class MHZ19DetectionRangeSetAction : public Action<Ts..
|
||||
void play(const Ts &...x) override { this->parent_->range_set(this->detection_range_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace mhz19
|
||||
} // namespace esphome
|
||||
} // namespace esphome::mhz19
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "mqtt_alarm_control_panel.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -18,21 +19,21 @@ void MQTTAlarmControlPanelComponent::setup() {
|
||||
this->alarm_control_panel_->add_on_state_callback([this]() { this->publish_state(); });
|
||||
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
|
||||
auto call = this->alarm_control_panel_->make_call();
|
||||
if (strcasecmp(payload.c_str(), "ARM_AWAY") == 0) {
|
||||
if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_AWAY")) == 0) {
|
||||
call.arm_away();
|
||||
} else if (strcasecmp(payload.c_str(), "ARM_HOME") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_HOME")) == 0) {
|
||||
call.arm_home();
|
||||
} else if (strcasecmp(payload.c_str(), "ARM_NIGHT") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_NIGHT")) == 0) {
|
||||
call.arm_night();
|
||||
} else if (strcasecmp(payload.c_str(), "ARM_VACATION") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_VACATION")) == 0) {
|
||||
call.arm_vacation();
|
||||
} else if (strcasecmp(payload.c_str(), "ARM_CUSTOM_BYPASS") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_CUSTOM_BYPASS")) == 0) {
|
||||
call.arm_custom_bypass();
|
||||
} else if (strcasecmp(payload.c_str(), "DISARM") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("DISARM")) == 0) {
|
||||
call.disarm();
|
||||
} else if (strcasecmp(payload.c_str(), "PENDING") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("PENDING")) == 0) {
|
||||
call.pending();
|
||||
} else if (strcasecmp(payload.c_str(), "TRIGGERED") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("TRIGGERED")) == 0) {
|
||||
call.triggered();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
@@ -119,7 +120,8 @@ bool MQTTAlarmControlPanelComponent::publish_state() {
|
||||
default:
|
||||
state_s = "unknown";
|
||||
}
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -52,8 +52,9 @@ bool MQTTBinarySensorComponent::publish_state(bool state) {
|
||||
if (this->binary_sensor_->is_status_binary_sensor())
|
||||
return true;
|
||||
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
const char *state_s = state ? "ON" : "OFF";
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -132,17 +132,29 @@ std::string MQTTComponent::get_command_topic_() const {
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const std::string &topic, const std::string &payload) {
|
||||
return this->publish(topic, payload.data(), payload.size());
|
||||
return this->publish(topic.c_str(), payload.data(), payload.size());
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const std::string &topic, const char *payload, size_t payload_length) {
|
||||
if (topic.empty())
|
||||
return this->publish(topic.c_str(), payload, payload_length);
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const char *topic, const char *payload, size_t payload_length) {
|
||||
if (topic[0] == '\0')
|
||||
return false;
|
||||
return global_mqtt_client->publish(topic, payload, payload_length, this->qos_, this->retain_);
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const char *topic, const char *payload) {
|
||||
return this->publish(topic, payload, strlen(payload));
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish_json(const std::string &topic, const json::json_build_t &f) {
|
||||
if (topic.empty())
|
||||
return this->publish_json(topic.c_str(), f);
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish_json(const char *topic, const json::json_build_t &f) {
|
||||
if (topic[0] == '\0')
|
||||
return false;
|
||||
return global_mqtt_client->publish_json(topic, f, this->qos_, this->retain_);
|
||||
}
|
||||
|
||||
@@ -157,6 +157,38 @@ class MQTTComponent : public Component {
|
||||
*/
|
||||
bool publish(const std::string &topic, const char *payload, size_t payload_length);
|
||||
|
||||
/** Send a MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as C string.
|
||||
* @param payload The payload buffer.
|
||||
* @param payload_length The length of the payload.
|
||||
*/
|
||||
bool publish(const char *topic, const char *payload, size_t payload_length);
|
||||
|
||||
/** Send a MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as StringRef (for use with get_state_topic_to_()).
|
||||
* @param payload The payload buffer.
|
||||
* @param payload_length The length of the payload.
|
||||
*/
|
||||
bool publish(StringRef topic, const char *payload, size_t payload_length) {
|
||||
return this->publish(topic.c_str(), payload, payload_length);
|
||||
}
|
||||
|
||||
/** Send a MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as C string.
|
||||
* @param payload The null-terminated payload.
|
||||
*/
|
||||
bool publish(const char *topic, const char *payload);
|
||||
|
||||
/** Send a MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as StringRef (for use with get_state_topic_to_()).
|
||||
* @param payload The null-terminated payload.
|
||||
*/
|
||||
bool publish(StringRef topic, const char *payload) { return this->publish(topic.c_str(), payload); }
|
||||
|
||||
/** Construct and send a JSON MQTT message.
|
||||
*
|
||||
* @param topic The topic.
|
||||
@@ -164,6 +196,20 @@ class MQTTComponent : public Component {
|
||||
*/
|
||||
bool publish_json(const std::string &topic, const json::json_build_t &f);
|
||||
|
||||
/** Construct and send a JSON MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as C string.
|
||||
* @param f The Json Message builder.
|
||||
*/
|
||||
bool publish_json(const char *topic, const json::json_build_t &f);
|
||||
|
||||
/** Construct and send a JSON MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as StringRef (for use with get_state_topic_to_()).
|
||||
* @param f The Json Message builder.
|
||||
*/
|
||||
bool publish_json(StringRef topic, const json::json_build_t &f) { return this->publish_json(topic.c_str(), f); }
|
||||
|
||||
/** Subscribe to a MQTT topic.
|
||||
*
|
||||
* @param topic The topic. Wildcards are currently not supported.
|
||||
|
||||
@@ -115,7 +115,8 @@ bool MQTTCoverComponent::publish_state() {
|
||||
: this->cover_->position == COVER_OPEN ? "open"
|
||||
: traits.get_supports_position() ? "open"
|
||||
: "unknown";
|
||||
if (!this->publish(this->get_state_topic_(), state_s))
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (!this->publish(this->get_state_topic_to_(topic_buf), state_s))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ bool MQTTDateComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) {
|
||||
return this->publish_json(this->get_state_topic_(), [year, month, day](JsonObject root) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [year, month, day](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("year")] = year;
|
||||
root[ESPHOME_F("month")] = month;
|
||||
|
||||
@@ -66,15 +66,17 @@ bool MQTTDateTimeComponent::send_initial_state() {
|
||||
}
|
||||
bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
|
||||
uint8_t second) {
|
||||
return this->publish_json(this->get_state_topic_(), [year, month, day, hour, minute, second](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("year")] = year;
|
||||
root[ESPHOME_F("month")] = month;
|
||||
root[ESPHOME_F("day")] = day;
|
||||
root[ESPHOME_F("hour")] = hour;
|
||||
root[ESPHOME_F("minute")] = minute;
|
||||
root[ESPHOME_F("second")] = second;
|
||||
});
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf),
|
||||
[year, month, day, hour, minute, second](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("year")] = year;
|
||||
root[ESPHOME_F("month")] = month;
|
||||
root[ESPHOME_F("day")] = day;
|
||||
root[ESPHOME_F("hour")] = hour;
|
||||
root[ESPHOME_F("minute")] = minute;
|
||||
root[ESPHOME_F("second")] = second;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -44,7 +44,8 @@ void MQTTEventComponent::dump_config() {
|
||||
}
|
||||
|
||||
bool MQTTEventComponent::publish_event_(const std::string &event_type) {
|
||||
return this->publish_json(this->get_state_topic_(), [event_type](JsonObject root) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [event_type](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[MQTT_EVENT_TYPE] = event_type;
|
||||
});
|
||||
|
||||
@@ -158,9 +158,10 @@ void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig
|
||||
}
|
||||
}
|
||||
bool MQTTFanComponent::publish_state() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
const char *state_s = this->state_->state ? "ON" : "OFF";
|
||||
ESP_LOGD(TAG, "'%s' Sending state %s.", this->state_->get_name().c_str(), state_s);
|
||||
this->publish(this->get_state_topic_(), state_s);
|
||||
this->publish(this->get_state_topic_to_(topic_buf), state_s);
|
||||
bool failed = false;
|
||||
if (this->state_->get_traits().supports_direction()) {
|
||||
bool success = this->publish(this->get_direction_state_topic(),
|
||||
|
||||
@@ -34,7 +34,8 @@ void MQTTJSONLightComponent::on_light_remote_values_update() {
|
||||
MQTTJSONLightComponent::MQTTJSONLightComponent(LightState *state) : state_(state) {}
|
||||
|
||||
bool MQTTJSONLightComponent::publish_state_() {
|
||||
return this->publish_json(this->get_state_topic_(), [this](JsonObject root) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [this](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
LightJSONSchema::dump_json(*this->state_, root);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "mqtt_lock.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -16,11 +17,11 @@ MQTTLockComponent::MQTTLockComponent(lock::Lock *a_lock) : lock_(a_lock) {}
|
||||
|
||||
void MQTTLockComponent::setup() {
|
||||
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
|
||||
if (strcasecmp(payload.c_str(), "LOCK") == 0) {
|
||||
if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("LOCK")) == 0) {
|
||||
this->lock_->lock();
|
||||
} else if (strcasecmp(payload.c_str(), "UNLOCK") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("UNLOCK")) == 0) {
|
||||
this->lock_->unlock();
|
||||
} else if (strcasecmp(payload.c_str(), "OPEN") == 0) {
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("OPEN")) == 0) {
|
||||
this->lock_->open();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
@@ -47,13 +48,14 @@ void MQTTLockComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfi
|
||||
bool MQTTLockComponent::send_initial_state() { return this->publish_state(); }
|
||||
|
||||
bool MQTTLockComponent::publish_state() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
char buf[LOCK_STATE_STR_SIZE];
|
||||
strncpy_P(buf, (PGM_P) lock_state_to_string(this->lock_->state), sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
return this->publish(this->get_state_topic_(), buf);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), buf);
|
||||
#else
|
||||
return this->publish(this->get_state_topic_(), LOG_STR_ARG(lock_state_to_string(this->lock_->state)));
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), LOG_STR_ARG(lock_state_to_string(this->lock_->state)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -74,9 +74,10 @@ bool MQTTNumberComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTNumberComponent::publish_state(float value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
char buffer[64];
|
||||
buf_append_printf(buffer, sizeof(buffer), 0, "%f", value);
|
||||
return this->publish(this->get_state_topic_(), buffer);
|
||||
size_t len = buf_append_printf(buffer, sizeof(buffer), 0, "%f", value);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), buffer, len);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -50,7 +50,8 @@ bool MQTTSelectComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTSelectComponent::publish_state(const std::string &value) {
|
||||
return this->publish(this->get_state_topic_(), value);
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), value.data(), value.size());
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -79,12 +79,13 @@ bool MQTTSensorComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTSensorComponent::publish_state(float value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (mqtt::global_mqtt_client->is_publish_nan_as_none() && std::isnan(value))
|
||||
return this->publish(this->get_state_topic_(), "None", 4);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), "None", 4);
|
||||
int8_t accuracy = this->sensor_->get_accuracy_decimals();
|
||||
char buf[VALUE_ACCURACY_MAX_LEN];
|
||||
size_t len = value_accuracy_to_buf(buf, value, accuracy);
|
||||
return this->publish(this->get_state_topic_(), buf, len);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), buf, len);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -52,8 +52,9 @@ void MQTTSwitchComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
|
||||
bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); }
|
||||
|
||||
bool MQTTSwitchComponent::publish_state(bool state) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
const char *state_s = state ? "ON" : "OFF";
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -53,7 +53,8 @@ bool MQTTTextComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTTextComponent::publish_state(const std::string &value) {
|
||||
return this->publish(this->get_state_topic_(), value);
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), value.data(), value.size());
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -31,7 +31,10 @@ void MQTTTextSensor::dump_config() {
|
||||
LOG_MQTT_COMPONENT(true, false);
|
||||
}
|
||||
|
||||
bool MQTTTextSensor::publish_state(const std::string &value) { return this->publish(this->get_state_topic_(), value); }
|
||||
bool MQTTTextSensor::publish_state(const std::string &value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), value.data(), value.size());
|
||||
}
|
||||
bool MQTTTextSensor::send_initial_state() {
|
||||
if (this->sensor_->has_state()) {
|
||||
return this->publish_state(this->sensor_->state);
|
||||
|
||||
@@ -53,7 +53,8 @@ bool MQTTTimeComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||
return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [hour, minute, second](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("hour")] = hour;
|
||||
root[ESPHOME_F("minute")] = minute;
|
||||
|
||||
@@ -28,7 +28,8 @@ void MQTTUpdateComponent::setup() {
|
||||
}
|
||||
|
||||
bool MQTTUpdateComponent::publish_state() {
|
||||
return this->publish_json(this->get_state_topic_(), [this](JsonObject root) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [this](JsonObject root) {
|
||||
root[ESPHOME_F("installed_version")] = this->update_->update_info.current_version;
|
||||
root[ESPHOME_F("latest_version")] = this->update_->update_info.latest_version;
|
||||
root[ESPHOME_F("title")] = this->update_->update_info.title;
|
||||
|
||||
@@ -84,7 +84,8 @@ bool MQTTValveComponent::publish_state() {
|
||||
: this->valve_->position == VALVE_OPEN ? "open"
|
||||
: traits.get_supports_position() ? "open"
|
||||
: "unknown";
|
||||
if (!this->publish(this->get_state_topic_(), state_s))
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (!this->publish(this->get_state_topic_to_(topic_buf), state_s))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -150,27 +150,24 @@ void Nextion::dump_config() {
|
||||
#ifdef USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE
|
||||
ESP_LOGCONFIG(TAG, " Skip handshake: YES");
|
||||
#else // USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE
|
||||
ESP_LOGCONFIG(TAG,
|
||||
#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Device Model: %s\n"
|
||||
" FW Version: %s\n"
|
||||
" Serial Number: %s\n"
|
||||
" Flash Size: %s\n"
|
||||
" Max queue age: %u ms\n"
|
||||
" Startup override: %u ms\n",
|
||||
this->device_model_.c_str(), this->firmware_version_.c_str(), this->serial_number_.c_str(),
|
||||
this->flash_size_.c_str(), this->max_q_age_ms_, this->startup_override_ms_);
|
||||
#endif // USE_NEXTION_CONFIG_DUMP_DEVICE_INFO
|
||||
#ifdef USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START
|
||||
" Exit reparse: YES\n"
|
||||
ESP_LOGCONFIG(TAG, " Exit reparse: YES\n");
|
||||
#endif // USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Wake On Touch: %s\n"
|
||||
" Touch Timeout: %" PRIu16,
|
||||
#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO
|
||||
this->device_model_.c_str(), this->firmware_version_.c_str(), this->serial_number_.c_str(),
|
||||
this->flash_size_.c_str(), this->max_q_age_ms_,
|
||||
this->startup_override_ms_
|
||||
#endif // USE_NEXTION_CONFIG_DUMP_DEVICE_INFO
|
||||
YESNO(this->connection_state_.auto_wake_on_touch_),
|
||||
this->touch_sleep_timeout_);
|
||||
YESNO(this->connection_state_.auto_wake_on_touch_), this->touch_sleep_timeout_);
|
||||
#endif // USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE
|
||||
|
||||
#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP
|
||||
|
||||
@@ -40,7 +40,7 @@ void NfcTagBinarySensor::set_tag_name(const std::string &str) {
|
||||
this->match_tag_name_ = true;
|
||||
}
|
||||
|
||||
void NfcTagBinarySensor::set_uid(const std::vector<uint8_t> &uid) { this->uid_ = uid; }
|
||||
void NfcTagBinarySensor::set_uid(const NfcTagUid &uid) { this->uid_ = uid; }
|
||||
|
||||
bool NfcTagBinarySensor::tag_match_ndef_string(const std::shared_ptr<NdefMessage> &msg) {
|
||||
for (const auto &record : msg->get_records()) {
|
||||
@@ -63,7 +63,7 @@ bool NfcTagBinarySensor::tag_match_tag_name(const std::shared_ptr<NdefMessage> &
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NfcTagBinarySensor::tag_match_uid(const std::vector<uint8_t> &data) {
|
||||
bool NfcTagBinarySensor::tag_match_uid(const NfcTagUid &data) {
|
||||
if (data.size() != this->uid_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ class NfcTagBinarySensor : public binary_sensor::BinarySensor,
|
||||
|
||||
void set_ndef_match_string(const std::string &str);
|
||||
void set_tag_name(const std::string &str);
|
||||
void set_uid(const std::vector<uint8_t> &uid);
|
||||
void set_uid(const NfcTagUid &uid);
|
||||
|
||||
bool tag_match_ndef_string(const std::shared_ptr<NdefMessage> &msg);
|
||||
bool tag_match_tag_name(const std::shared_ptr<NdefMessage> &msg);
|
||||
bool tag_match_uid(const std::vector<uint8_t> &data);
|
||||
bool tag_match_uid(const NfcTagUid &data);
|
||||
|
||||
void tag_off(NfcTag &tag) override;
|
||||
void tag_on(NfcTag &tag) override;
|
||||
@@ -31,7 +31,7 @@ class NfcTagBinarySensor : public binary_sensor::BinarySensor,
|
||||
protected:
|
||||
bool match_tag_name_{false};
|
||||
std::string match_string_;
|
||||
std::vector<uint8_t> uid_;
|
||||
NfcTagUid uid_;
|
||||
};
|
||||
|
||||
} // namespace nfc
|
||||
|
||||
@@ -8,19 +8,23 @@ namespace nfc {
|
||||
|
||||
static const char *const TAG = "nfc";
|
||||
|
||||
char *format_uid_to(char *buffer, const std::vector<uint8_t> &uid) {
|
||||
char *format_uid_to(char *buffer, std::span<const uint8_t> uid) {
|
||||
return format_hex_pretty_to(buffer, FORMAT_UID_BUFFER_SIZE, uid.data(), uid.size(), '-');
|
||||
}
|
||||
|
||||
char *format_bytes_to(char *buffer, const std::vector<uint8_t> &bytes) {
|
||||
char *format_bytes_to(char *buffer, std::span<const uint8_t> bytes) {
|
||||
return format_hex_pretty_to(buffer, FORMAT_BYTES_BUFFER_SIZE, bytes.data(), bytes.size(), ' ');
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
// Deprecated wrappers intentionally use heap-allocating version for backward compatibility
|
||||
std::string format_uid(const std::vector<uint8_t> &uid) { return format_hex_pretty(uid, '-', false); } // NOLINT
|
||||
std::string format_bytes(const std::vector<uint8_t> &bytes) { return format_hex_pretty(bytes, ' ', false); } // NOLINT
|
||||
std::string format_uid(std::span<const uint8_t> uid) {
|
||||
return format_hex_pretty(uid.data(), uid.size(), '-', false); // NOLINT
|
||||
}
|
||||
std::string format_bytes(std::span<const uint8_t> bytes) {
|
||||
return format_hex_pretty(bytes.data(), bytes.size(), ' ', false); // NOLINT
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
uint8_t guess_tag_type(uint8_t uid_length) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "ndef_record.h"
|
||||
#include "nfc_tag.h"
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
@@ -56,19 +57,19 @@ static const uint8_t MAD_KEY[6] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
|
||||
/// Max UID size is 10 bytes, formatted as "XX-XX-XX-XX-XX-XX-XX-XX-XX-XX\0" = 30 chars
|
||||
static constexpr size_t FORMAT_UID_BUFFER_SIZE = 30;
|
||||
/// Format UID to buffer with '-' separator (e.g., "04-11-22-33"). Returns buffer for inline use.
|
||||
char *format_uid_to(char *buffer, const std::vector<uint8_t> &uid);
|
||||
char *format_uid_to(char *buffer, std::span<const uint8_t> uid);
|
||||
|
||||
/// Buffer size for format_bytes_to (64 bytes max = 192 chars with space separator)
|
||||
static constexpr size_t FORMAT_BYTES_BUFFER_SIZE = 192;
|
||||
/// Format bytes to buffer with ' ' separator (e.g., "04 11 22 33"). Returns buffer for inline use.
|
||||
char *format_bytes_to(char *buffer, const std::vector<uint8_t> &bytes);
|
||||
char *format_bytes_to(char *buffer, std::span<const uint8_t> bytes);
|
||||
|
||||
// Remove before 2026.6.0
|
||||
ESPDEPRECATED("Use format_uid_to() with stack buffer instead. Removed in 2026.6.0", "2025.12.0")
|
||||
std::string format_uid(const std::vector<uint8_t> &uid);
|
||||
std::string format_uid(std::span<const uint8_t> uid);
|
||||
// Remove before 2026.6.0
|
||||
ESPDEPRECATED("Use format_bytes_to() with stack buffer instead. Removed in 2026.6.0", "2025.12.0")
|
||||
std::string format_bytes(const std::vector<uint8_t> &bytes);
|
||||
std::string format_bytes(std::span<const uint8_t> bytes);
|
||||
|
||||
uint8_t guess_tag_type(uint8_t uid_length);
|
||||
uint8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data);
|
||||
|
||||
@@ -10,26 +10,27 @@
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
||||
// NFC UIDs are 4, 7, or 10 bytes depending on tag type
|
||||
static constexpr size_t NFC_UID_MAX_LENGTH = 10;
|
||||
using NfcTagUid = StaticVector<uint8_t, NFC_UID_MAX_LENGTH>;
|
||||
|
||||
class NfcTag {
|
||||
public:
|
||||
NfcTag() {
|
||||
this->uid_ = {};
|
||||
this->tag_type_ = "Unknown";
|
||||
};
|
||||
NfcTag(std::vector<uint8_t> &uid) {
|
||||
NfcTag() { this->tag_type_ = "Unknown"; };
|
||||
NfcTag(const NfcTagUid &uid) {
|
||||
this->uid_ = uid;
|
||||
this->tag_type_ = "Unknown";
|
||||
};
|
||||
NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type) {
|
||||
NfcTag(const NfcTagUid &uid, const std::string &tag_type) {
|
||||
this->uid_ = uid;
|
||||
this->tag_type_ = tag_type;
|
||||
};
|
||||
NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type, std::unique_ptr<nfc::NdefMessage> ndef_message) {
|
||||
NfcTag(const NfcTagUid &uid, const std::string &tag_type, std::unique_ptr<nfc::NdefMessage> ndef_message) {
|
||||
this->uid_ = uid;
|
||||
this->tag_type_ = tag_type;
|
||||
this->ndef_message_ = std::move(ndef_message);
|
||||
};
|
||||
NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type, std::vector<uint8_t> &ndef_data) {
|
||||
NfcTag(const NfcTagUid &uid, const std::string &tag_type, std::vector<uint8_t> &ndef_data) {
|
||||
this->uid_ = uid;
|
||||
this->tag_type_ = tag_type;
|
||||
this->ndef_message_ = make_unique<NdefMessage>(ndef_data);
|
||||
@@ -41,14 +42,14 @@ class NfcTag {
|
||||
ndef_message_ = make_unique<NdefMessage>(*rhs.ndef_message_);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> &get_uid() { return this->uid_; };
|
||||
NfcTagUid &get_uid() { return this->uid_; };
|
||||
const std::string &get_tag_type() { return this->tag_type_; };
|
||||
bool has_ndef_message() { return this->ndef_message_ != nullptr; };
|
||||
const std::shared_ptr<NdefMessage> &get_ndef_message() { return this->ndef_message_; };
|
||||
void set_ndef_message(std::unique_ptr<NdefMessage> ndef_message) { this->ndef_message_ = std::move(ndef_message); };
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> uid_;
|
||||
NfcTagUid uid_;
|
||||
std::string tag_type_;
|
||||
std::shared_ptr<NdefMessage> ndef_message_;
|
||||
};
|
||||
|
||||
@@ -69,9 +69,21 @@ def set_core_data(config: ConfigType) -> ConfigType:
|
||||
|
||||
|
||||
def set_framework(config: ConfigType) -> ConfigType:
|
||||
version = cv.Version.parse(cv.version_number(config[CONF_FRAMEWORK][CONF_VERSION]))
|
||||
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = version
|
||||
return config
|
||||
framework_ver = cv.Version.parse(
|
||||
cv.version_number(config[CONF_FRAMEWORK][CONF_VERSION])
|
||||
)
|
||||
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = framework_ver
|
||||
if framework_ver < cv.Version(2, 9, 2):
|
||||
return cv.require_framework_version(
|
||||
nrf52_zephyr=cv.Version(2, 6, 1, "a"),
|
||||
)(config)
|
||||
if framework_ver < cv.Version(3, 2, 0):
|
||||
return cv.require_framework_version(
|
||||
nrf52_zephyr=cv.Version(2, 9, 2, "2"),
|
||||
)(config)
|
||||
return cv.require_framework_version(
|
||||
nrf52_zephyr=cv.Version(3, 2, 0, "1"),
|
||||
)(config)
|
||||
|
||||
|
||||
BOOTLOADERS = [
|
||||
@@ -140,7 +152,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_UICR_ERASE, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_FRAMEWORK, default={CONF_VERSION: "2.6.1-7"}): cv.Schema(
|
||||
cv.Optional(CONF_FRAMEWORK, default={CONF_VERSION: "2.6.1-a"}): cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_VERSION): cv.string_strict,
|
||||
}
|
||||
@@ -181,13 +193,12 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add_platformio_option(CONF_FRAMEWORK, CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK])
|
||||
cg.add_platformio_option(
|
||||
"platform",
|
||||
"https://github.com/tomaszduda23/platform-nordicnrf52/archive/refs/tags/v10.3.0-1.zip",
|
||||
"https://github.com/tomaszduda23/platform-nordicnrf52/archive/refs/tags/v10.3.0-5.zip",
|
||||
)
|
||||
cg.add_platformio_option(
|
||||
"platform_packages",
|
||||
[
|
||||
f"platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v{CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}.zip",
|
||||
"platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.17.4-0.zip",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@ void ValueRangeTrigger::setup() {
|
||||
float local_min = this->min_.value(0.0);
|
||||
float local_max = this->max_.value(0.0);
|
||||
convert hash = {.from = (local_max - local_min)};
|
||||
uint32_t myhash = hash.to ^ this->parent_->get_preference_hash();
|
||||
this->rtc_ = global_preferences->make_preference<bool>(myhash);
|
||||
this->rtc_ = this->parent_->make_entity_preference<bool>(hash.to);
|
||||
bool initial_state;
|
||||
if (this->rtc_.load(&initial_state)) {
|
||||
this->previous_in_range_ = initial_state;
|
||||
|
||||
@@ -14,18 +14,9 @@ void log_number(const char *tag, const char *prefix, const char *type, Number *o
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
||||
|
||||
if (!obj->get_icon_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon_ref().c_str());
|
||||
}
|
||||
|
||||
if (!obj->traits.get_unit_of_measurement_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Unit of Measurement: '%s'", prefix, obj->traits.get_unit_of_measurement_ref().c_str());
|
||||
}
|
||||
|
||||
if (!obj->traits.get_device_class_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->traits.get_device_class_ref().c_str());
|
||||
}
|
||||
LOG_ENTITY_ICON(tag, prefix, *obj);
|
||||
LOG_ENTITY_UNIT_OF_MEASUREMENT(tag, prefix, obj->traits);
|
||||
LOG_ENTITY_DEVICE_CLASS(tag, prefix, obj->traits);
|
||||
}
|
||||
|
||||
void Number::publish_state(float state) {
|
||||
|
||||
@@ -17,7 +17,7 @@ void OpenthermNumber::setup() {
|
||||
if (!this->restore_value_) {
|
||||
value = this->initial_value_;
|
||||
} else {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
if (!this->pref_.load(&value)) {
|
||||
if (!std::isnan(this->initial_value_)) {
|
||||
value = this->initial_value_;
|
||||
|
||||
@@ -168,11 +168,11 @@ void PN532::loop() {
|
||||
}
|
||||
|
||||
uint8_t nfcid_length = read[5];
|
||||
std::vector<uint8_t> nfcid(read.begin() + 6, read.begin() + 6 + nfcid_length);
|
||||
if (read.size() < 6U + nfcid_length) {
|
||||
if (nfcid_length > nfc::NFC_UID_MAX_LENGTH || read.size() < 6U + nfcid_length) {
|
||||
// oops, pn532 returned invalid data
|
||||
return;
|
||||
}
|
||||
nfc::NfcTagUid nfcid(read.begin() + 6, read.begin() + 6 + nfcid_length);
|
||||
|
||||
bool report = true;
|
||||
for (auto *bin_sens : this->binary_sensors_) {
|
||||
@@ -358,7 +358,7 @@ void PN532::turn_off_rf_() {
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_tag_(std::vector<uint8_t> &uid) {
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_tag_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
|
||||
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
|
||||
@@ -393,7 +393,7 @@ void PN532::write_mode(nfc::NdefMessage *message) {
|
||||
ESP_LOGD(TAG, "Waiting to write next tag");
|
||||
}
|
||||
|
||||
bool PN532::clean_tag_(std::vector<uint8_t> &uid) {
|
||||
bool PN532::clean_tag_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
|
||||
return this->format_mifare_classic_mifare_(uid);
|
||||
@@ -404,7 +404,7 @@ bool PN532::clean_tag_(std::vector<uint8_t> &uid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PN532::format_tag_(std::vector<uint8_t> &uid) {
|
||||
bool PN532::format_tag_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
|
||||
return this->format_mifare_classic_ndef_(uid);
|
||||
@@ -415,7 +415,7 @@ bool PN532::format_tag_(std::vector<uint8_t> &uid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PN532::write_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
|
||||
bool PN532::write_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
|
||||
return this->write_mifare_classic_tag_(uid, message);
|
||||
@@ -448,7 +448,7 @@ void PN532::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
bool PN532BinarySensor::process(std::vector<uint8_t> &data) {
|
||||
bool PN532BinarySensor::process(const nfc::NfcTagUid &data) {
|
||||
if (data.size() != this->uid_.size())
|
||||
return false;
|
||||
|
||||
|
||||
@@ -69,28 +69,28 @@ class PN532 : public PollingComponent {
|
||||
virtual bool read_data(std::vector<uint8_t> &data, uint8_t len) = 0;
|
||||
virtual bool read_response(uint8_t command, std::vector<uint8_t> &data) = 0;
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> read_tag_(std::vector<uint8_t> &uid);
|
||||
std::unique_ptr<nfc::NfcTag> read_tag_(nfc::NfcTagUid &uid);
|
||||
|
||||
bool format_tag_(std::vector<uint8_t> &uid);
|
||||
bool clean_tag_(std::vector<uint8_t> &uid);
|
||||
bool write_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
|
||||
bool format_tag_(nfc::NfcTagUid &uid);
|
||||
bool clean_tag_(nfc::NfcTagUid &uid);
|
||||
bool write_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_classic_tag_(std::vector<uint8_t> &uid);
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_classic_tag_(nfc::NfcTagUid &uid);
|
||||
bool read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
|
||||
bool write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
|
||||
bool auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key);
|
||||
bool format_mifare_classic_mifare_(std::vector<uint8_t> &uid);
|
||||
bool format_mifare_classic_ndef_(std::vector<uint8_t> &uid);
|
||||
bool write_mifare_classic_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
|
||||
bool auth_mifare_classic_block_(nfc::NfcTagUid &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key);
|
||||
bool format_mifare_classic_mifare_(nfc::NfcTagUid &uid);
|
||||
bool format_mifare_classic_ndef_(nfc::NfcTagUid &uid);
|
||||
bool write_mifare_classic_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_ultralight_tag_(std::vector<uint8_t> &uid);
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_ultralight_tag_(nfc::NfcTagUid &uid);
|
||||
bool read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector<uint8_t> &data);
|
||||
bool is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_to_6);
|
||||
uint16_t read_mifare_ultralight_capacity_();
|
||||
bool find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
|
||||
uint8_t &message_start_index);
|
||||
bool write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data);
|
||||
bool write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
|
||||
bool write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message);
|
||||
bool clean_mifare_ultralight_();
|
||||
|
||||
bool updates_enabled_{true};
|
||||
@@ -98,7 +98,7 @@ class PN532 : public PollingComponent {
|
||||
std::vector<PN532BinarySensor *> binary_sensors_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontag_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontagremoved_;
|
||||
std::vector<uint8_t> current_uid_;
|
||||
nfc::NfcTagUid current_uid_;
|
||||
nfc::NdefMessage *next_task_message_to_write_;
|
||||
uint32_t rd_start_time_{0};
|
||||
enum PN532ReadReady rd_ready_ { WOULDBLOCK };
|
||||
@@ -118,9 +118,9 @@ class PN532 : public PollingComponent {
|
||||
|
||||
class PN532BinarySensor : public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void set_uid(const std::vector<uint8_t> &uid) { uid_ = uid; }
|
||||
void set_uid(const nfc::NfcTagUid &uid) { uid_ = uid; }
|
||||
|
||||
bool process(std::vector<uint8_t> &data);
|
||||
bool process(const nfc::NfcTagUid &data);
|
||||
|
||||
void on_scan_end() {
|
||||
if (!this->found_) {
|
||||
@@ -130,7 +130,7 @@ class PN532BinarySensor : public binary_sensor::BinarySensor {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> uid_;
|
||||
nfc::NfcTagUid uid_;
|
||||
bool found_{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace pn532 {
|
||||
|
||||
static const char *const TAG = "pn532.mifare_classic";
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(std::vector<uint8_t> &uid) {
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(nfc::NfcTagUid &uid) {
|
||||
uint8_t current_block = 4;
|
||||
uint8_t message_start_index = 0;
|
||||
uint32_t message_length = 0;
|
||||
@@ -82,8 +82,7 @@ bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PN532::auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_num, uint8_t key_num,
|
||||
const uint8_t *key) {
|
||||
bool PN532::auth_mifare_classic_block_(nfc::NfcTagUid &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key) {
|
||||
std::vector<uint8_t> data({
|
||||
PN532_COMMAND_INDATAEXCHANGE,
|
||||
0x01, // One card
|
||||
@@ -106,7 +105,7 @@ bool PN532::auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PN532::format_mifare_classic_mifare_(std::vector<uint8_t> &uid) {
|
||||
bool PN532::format_mifare_classic_mifare_(nfc::NfcTagUid &uid) {
|
||||
std::vector<uint8_t> blank_buffer(
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
std::vector<uint8_t> trailer_buffer(
|
||||
@@ -141,7 +140,7 @@ bool PN532::format_mifare_classic_mifare_(std::vector<uint8_t> &uid) {
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool PN532::format_mifare_classic_ndef_(std::vector<uint8_t> &uid) {
|
||||
bool PN532::format_mifare_classic_ndef_(nfc::NfcTagUid &uid) {
|
||||
std::vector<uint8_t> empty_ndef_message(
|
||||
{0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
std::vector<uint8_t> blank_block(
|
||||
@@ -216,7 +215,7 @@ bool PN532::write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t>
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PN532::write_mifare_classic_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
|
||||
bool PN532::write_mifare_classic_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message) {
|
||||
auto encoded = message->encode();
|
||||
|
||||
uint32_t message_length = encoded.size();
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace pn532 {
|
||||
|
||||
static const char *const TAG = "pn532.mifare_ultralight";
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_mifare_ultralight_tag_(std::vector<uint8_t> &uid) {
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_mifare_ultralight_tag_(nfc::NfcTagUid &uid) {
|
||||
std::vector<uint8_t> data;
|
||||
// pages 3 to 6 contain various info we are interested in -- do one read to grab it all
|
||||
if (!this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE * nfc::MIFARE_ULTRALIGHT_READ_SIZE,
|
||||
@@ -114,7 +114,7 @@ bool PN532::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PN532::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
|
||||
bool PN532::write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message) {
|
||||
uint32_t capacity = this->read_mifare_ultralight_capacity_();
|
||||
|
||||
auto encoded = message->encode();
|
||||
|
||||
@@ -478,7 +478,7 @@ uint8_t PN7150::read_endpoint_data_(nfc::NfcTag &tag) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7150::clean_endpoint_(std::vector<uint8_t> &uid) {
|
||||
uint8_t PN7150::clean_endpoint_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -494,7 +494,7 @@ uint8_t PN7150::clean_endpoint_(std::vector<uint8_t> &uid) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7150::format_endpoint_(std::vector<uint8_t> &uid) {
|
||||
uint8_t PN7150::format_endpoint_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -510,7 +510,7 @@ uint8_t PN7150::format_endpoint_(std::vector<uint8_t> &uid) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7150::write_endpoint_(std::vector<uint8_t> &uid, std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t PN7150::write_endpoint_(nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -534,7 +534,7 @@ std::unique_ptr<nfc::NfcTag> PN7150::build_tag_(const uint8_t mode_tech, const s
|
||||
ESP_LOGE(TAG, "UID length cannot be zero");
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<uint8_t> uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
nfc::NfcTagUid uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
const auto *tag_type_str =
|
||||
nfc::guess_tag_type(uid_length) == nfc::TAG_TYPE_MIFARE_CLASSIC ? nfc::MIFARE_CLASSIC : nfc::NFC_FORUM_TYPE_2;
|
||||
return make_unique<nfc::NfcTag>(uid, tag_type_str);
|
||||
@@ -543,7 +543,7 @@ std::unique_ptr<nfc::NfcTag> PN7150::build_tag_(const uint8_t mode_tech, const s
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
optional<size_t> PN7150::find_tag_uid_(const std::vector<uint8_t> &uid) {
|
||||
optional<size_t> PN7150::find_tag_uid_(const nfc::NfcTagUid &uid) {
|
||||
if (!this->discovered_endpoint_.empty()) {
|
||||
for (size_t i = 0; i < this->discovered_endpoint_.size(); i++) {
|
||||
auto existing_tag_uid = this->discovered_endpoint_[i].tag->get_uid();
|
||||
|
||||
@@ -203,12 +203,12 @@ class PN7150 : public nfc::Nfcc, public Component {
|
||||
void select_endpoint_();
|
||||
|
||||
uint8_t read_endpoint_data_(nfc::NfcTag &tag);
|
||||
uint8_t clean_endpoint_(std::vector<uint8_t> &uid);
|
||||
uint8_t format_endpoint_(std::vector<uint8_t> &uid);
|
||||
uint8_t write_endpoint_(std::vector<uint8_t> &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t clean_endpoint_(nfc::NfcTagUid &uid);
|
||||
uint8_t format_endpoint_(nfc::NfcTagUid &uid);
|
||||
uint8_t write_endpoint_(nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> build_tag_(uint8_t mode_tech, const std::vector<uint8_t> &data);
|
||||
optional<size_t> find_tag_uid_(const std::vector<uint8_t> &uid);
|
||||
optional<size_t> find_tag_uid_(const nfc::NfcTagUid &uid);
|
||||
void purge_old_tags_();
|
||||
void erase_tag_(uint8_t tag_index);
|
||||
|
||||
@@ -251,7 +251,7 @@ class PN7150 : public nfc::Nfcc, public Component {
|
||||
uint8_t find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
|
||||
uint8_t &message_start_index);
|
||||
uint8_t write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data);
|
||||
uint8_t write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t clean_mifare_ultralight_();
|
||||
|
||||
enum NfcTask : uint8_t {
|
||||
|
||||
@@ -115,8 +115,7 @@ uint8_t PN7150::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7150::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid,
|
||||
const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t PN7150::write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint32_t capacity = this->read_mifare_ultralight_capacity_();
|
||||
|
||||
auto encoded = message->encode();
|
||||
|
||||
@@ -506,7 +506,7 @@ uint8_t PN7160::read_endpoint_data_(nfc::NfcTag &tag) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7160::clean_endpoint_(std::vector<uint8_t> &uid) {
|
||||
uint8_t PN7160::clean_endpoint_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -522,7 +522,7 @@ uint8_t PN7160::clean_endpoint_(std::vector<uint8_t> &uid) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7160::format_endpoint_(std::vector<uint8_t> &uid) {
|
||||
uint8_t PN7160::format_endpoint_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -538,7 +538,7 @@ uint8_t PN7160::format_endpoint_(std::vector<uint8_t> &uid) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7160::write_endpoint_(std::vector<uint8_t> &uid, std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t PN7160::write_endpoint_(nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -562,7 +562,7 @@ std::unique_ptr<nfc::NfcTag> PN7160::build_tag_(const uint8_t mode_tech, const s
|
||||
ESP_LOGE(TAG, "UID length cannot be zero");
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<uint8_t> uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
nfc::NfcTagUid uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
const auto *tag_type_str =
|
||||
nfc::guess_tag_type(uid_length) == nfc::TAG_TYPE_MIFARE_CLASSIC ? nfc::MIFARE_CLASSIC : nfc::NFC_FORUM_TYPE_2;
|
||||
return make_unique<nfc::NfcTag>(uid, tag_type_str);
|
||||
@@ -571,7 +571,7 @@ std::unique_ptr<nfc::NfcTag> PN7160::build_tag_(const uint8_t mode_tech, const s
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
optional<size_t> PN7160::find_tag_uid_(const std::vector<uint8_t> &uid) {
|
||||
optional<size_t> PN7160::find_tag_uid_(const nfc::NfcTagUid &uid) {
|
||||
if (!this->discovered_endpoint_.empty()) {
|
||||
for (size_t i = 0; i < this->discovered_endpoint_.size(); i++) {
|
||||
auto existing_tag_uid = this->discovered_endpoint_[i].tag->get_uid();
|
||||
|
||||
@@ -220,12 +220,12 @@ class PN7160 : public nfc::Nfcc, public Component {
|
||||
void select_endpoint_();
|
||||
|
||||
uint8_t read_endpoint_data_(nfc::NfcTag &tag);
|
||||
uint8_t clean_endpoint_(std::vector<uint8_t> &uid);
|
||||
uint8_t format_endpoint_(std::vector<uint8_t> &uid);
|
||||
uint8_t write_endpoint_(std::vector<uint8_t> &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t clean_endpoint_(nfc::NfcTagUid &uid);
|
||||
uint8_t format_endpoint_(nfc::NfcTagUid &uid);
|
||||
uint8_t write_endpoint_(nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> build_tag_(uint8_t mode_tech, const std::vector<uint8_t> &data);
|
||||
optional<size_t> find_tag_uid_(const std::vector<uint8_t> &uid);
|
||||
optional<size_t> find_tag_uid_(const nfc::NfcTagUid &uid);
|
||||
void purge_old_tags_();
|
||||
void erase_tag_(uint8_t tag_index);
|
||||
|
||||
@@ -268,7 +268,7 @@ class PN7160 : public nfc::Nfcc, public Component {
|
||||
uint8_t find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
|
||||
uint8_t &message_start_index);
|
||||
uint8_t write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data);
|
||||
uint8_t write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t clean_mifare_ultralight_();
|
||||
|
||||
enum NfcTask : uint8_t {
|
||||
|
||||
@@ -115,8 +115,7 @@ uint8_t PN7160::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7160::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid,
|
||||
const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t PN7160::write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint32_t capacity = this->read_mifare_ultralight_capacity_();
|
||||
|
||||
auto encoded = message->encode();
|
||||
|
||||
@@ -41,12 +41,14 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
|
||||
void add_label_name(EntityBase *obj, const std::string &value) { relabel_map_name_.insert({obj, value}); }
|
||||
|
||||
bool canHandle(AsyncWebServerRequest *request) const override {
|
||||
if (request->method() == HTTP_GET) {
|
||||
if (request->url() == "/metrics")
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (request->method() != HTTP_GET)
|
||||
return false;
|
||||
#ifdef USE_ESP32
|
||||
char url_buf[AsyncWebServerRequest::URL_BUF_SIZE];
|
||||
return request->url_to(url_buf) == "/metrics";
|
||||
#else
|
||||
return request->url() == ESPHOME_F("/metrics");
|
||||
#endif
|
||||
}
|
||||
|
||||
void handleRequest(AsyncWebServerRequest *req) override;
|
||||
|
||||
@@ -132,7 +132,7 @@ void RotaryEncoderSensor::setup() {
|
||||
int32_t initial_value = 0;
|
||||
switch (this->restore_mode_) {
|
||||
case ROTARY_ENCODER_RESTORE_DEFAULT_ZERO:
|
||||
this->rtc_ = global_preferences->make_preference<int32_t>(this->get_preference_hash());
|
||||
this->rtc_ = this->make_entity_preference<int32_t>();
|
||||
if (!this->rtc_.load(&initial_value)) {
|
||||
initial_value = 0;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "preferences.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -25,6 +24,9 @@ static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-no
|
||||
|
||||
static const uint32_t RP2040_FLASH_STORAGE_SIZE = 512;
|
||||
|
||||
// Stack buffer size for preferences - covers virtually all real-world preferences without heap allocation
|
||||
static constexpr size_t PREF_BUFFER_SIZE = 64;
|
||||
|
||||
extern "C" uint8_t _EEPROM_start;
|
||||
|
||||
template<class It> uint8_t calculate_crc(It first, It last, uint32_t type) {
|
||||
@@ -42,12 +44,14 @@ class RP2040PreferenceBackend : public ESPPreferenceBackend {
|
||||
uint32_t type = 0;
|
||||
|
||||
bool save(const uint8_t *data, size_t len) override {
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.resize(len + 1);
|
||||
memcpy(buffer.data(), data, len);
|
||||
buffer[buffer.size() - 1] = calculate_crc(buffer.begin(), buffer.end() - 1, type);
|
||||
const size_t buffer_size = len + 1;
|
||||
SmallBufferWithHeapFallback<PREF_BUFFER_SIZE> buffer_alloc(buffer_size);
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
|
||||
for (uint32_t i = 0; i < len + 1; i++) {
|
||||
memcpy(buffer, data, len);
|
||||
buffer[len] = calculate_crc(buffer, buffer + len, type);
|
||||
|
||||
for (size_t i = 0; i < buffer_size; i++) {
|
||||
uint32_t j = offset + i;
|
||||
if (j >= RP2040_FLASH_STORAGE_SIZE)
|
||||
return false;
|
||||
@@ -60,22 +64,23 @@ class RP2040PreferenceBackend : public ESPPreferenceBackend {
|
||||
return true;
|
||||
}
|
||||
bool load(uint8_t *data, size_t len) override {
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.resize(len + 1);
|
||||
const size_t buffer_size = len + 1;
|
||||
SmallBufferWithHeapFallback<PREF_BUFFER_SIZE> buffer_alloc(buffer_size);
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
|
||||
for (size_t i = 0; i < len + 1; i++) {
|
||||
for (size_t i = 0; i < buffer_size; i++) {
|
||||
uint32_t j = offset + i;
|
||||
if (j >= RP2040_FLASH_STORAGE_SIZE)
|
||||
return false;
|
||||
buffer[i] = s_flash_storage[j];
|
||||
}
|
||||
|
||||
uint8_t crc = calculate_crc(buffer.begin(), buffer.end() - 1, type);
|
||||
if (buffer[buffer.size() - 1] != crc) {
|
||||
uint8_t crc = calculate_crc(buffer, buffer + len, type);
|
||||
if (buffer[len] != crc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, buffer.data(), len);
|
||||
memcpy(data, buffer, len);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,6 +23,10 @@ RTL87XX_BOARDS = {
|
||||
"name": "WR2 Wi-Fi Module",
|
||||
"family": FAMILY_RTL8710B,
|
||||
},
|
||||
"wbr3": {
|
||||
"name": "WBR3 Wi-Fi Module",
|
||||
"family": FAMILY_RTL8720C,
|
||||
},
|
||||
"generic-rtl8710bn-2mb-468k": {
|
||||
"name": "Generic - RTL8710BN (2M/468k)",
|
||||
"family": FAMILY_RTL8710B,
|
||||
@@ -79,6 +83,10 @@ RTL87XX_BOARDS = {
|
||||
"name": "T103_V1.0",
|
||||
"family": FAMILY_RTL8710B,
|
||||
},
|
||||
"generic-rtl8720cf-2mb-896k": {
|
||||
"name": "Generic - RTL8720CF (2M/896k)",
|
||||
"family": FAMILY_RTL8720C,
|
||||
},
|
||||
"generic-rtl8720cf-2mb-992k": {
|
||||
"name": "Generic - RTL8720CF (2M/992k)",
|
||||
"family": FAMILY_RTL8720C,
|
||||
@@ -221,6 +229,71 @@ RTL87XX_BOARD_PINS = {
|
||||
"D9": 29,
|
||||
"A1": 41,
|
||||
},
|
||||
"wbr3": {
|
||||
"WIRE0_SCL_0": 11,
|
||||
"WIRE0_SCL_1": 2,
|
||||
"WIRE0_SCL_2": 19,
|
||||
"WIRE0_SCL_3": 15,
|
||||
"WIRE0_SDA_0": 3,
|
||||
"WIRE0_SDA_1": 12,
|
||||
"WIRE0_SDA_2": 16,
|
||||
"SERIAL0_RX_0": 12,
|
||||
"SERIAL0_RX_1": 13,
|
||||
"SERIAL0_TX_0": 11,
|
||||
"SERIAL0_TX_1": 14,
|
||||
"SERIAL1_CTS": 4,
|
||||
"SERIAL1_RX_0": 2,
|
||||
"SERIAL1_RX_1": 0,
|
||||
"SERIAL1_TX_0": 3,
|
||||
"SERIAL1_TX_1": 1,
|
||||
"SERIAL2_CTS": 19,
|
||||
"SERIAL2_RX": 15,
|
||||
"SERIAL2_TX": 16,
|
||||
"CS0": 15,
|
||||
"CTS1": 4,
|
||||
"CTS2": 19,
|
||||
"PA00": 0,
|
||||
"PA0": 0,
|
||||
"PA01": 1,
|
||||
"PA1": 1,
|
||||
"PA02": 2,
|
||||
"PA2": 2,
|
||||
"PA03": 3,
|
||||
"PA3": 3,
|
||||
"PA04": 4,
|
||||
"PA4": 4,
|
||||
"PA07": 7,
|
||||
"PA7": 7,
|
||||
"PA11": 11,
|
||||
"PA12": 12,
|
||||
"PA13": 13,
|
||||
"PA14": 14,
|
||||
"PA15": 15,
|
||||
"PA16": 16,
|
||||
"PA17": 17,
|
||||
"PA18": 18,
|
||||
"PA19": 19,
|
||||
"PWM5": 17,
|
||||
"PWM6": 18,
|
||||
"RX2": 15,
|
||||
"SDA0": 16,
|
||||
"TX2": 16,
|
||||
"D0": 7,
|
||||
"D1": 11,
|
||||
"D2": 2,
|
||||
"D3": 3,
|
||||
"D4": 4,
|
||||
"D5": 12,
|
||||
"D6": 16,
|
||||
"D7": 17,
|
||||
"D8": 18,
|
||||
"D9": 19,
|
||||
"D10": 13,
|
||||
"D11": 14,
|
||||
"D12": 15,
|
||||
"D13": 0,
|
||||
"D14": 1,
|
||||
},
|
||||
"generic-rtl8710bn-2mb-468k": {
|
||||
"SPI0_CS": 19,
|
||||
"SPI0_MISO": 22,
|
||||
@@ -1178,6 +1251,104 @@ RTL87XX_BOARD_PINS = {
|
||||
"A0": 19,
|
||||
"A1": 41,
|
||||
},
|
||||
"generic-rtl8720cf-2mb-896k": {
|
||||
"SPI0_CS_0": 2,
|
||||
"SPI0_CS_1": 7,
|
||||
"SPI0_CS_2": 15,
|
||||
"SPI0_MISO_0": 10,
|
||||
"SPI0_MISO_1": 20,
|
||||
"SPI0_MOSI_0": 4,
|
||||
"SPI0_MOSI_1": 9,
|
||||
"SPI0_MOSI_2": 19,
|
||||
"SPI0_SCK_0": 3,
|
||||
"SPI0_SCK_1": 8,
|
||||
"SPI0_SCK_2": 16,
|
||||
"WIRE0_SCL_0": 2,
|
||||
"WIRE0_SCL_1": 11,
|
||||
"WIRE0_SCL_2": 15,
|
||||
"WIRE0_SCL_3": 19,
|
||||
"WIRE0_SDA_0": 3,
|
||||
"WIRE0_SDA_1": 12,
|
||||
"WIRE0_SDA_2": 16,
|
||||
"WIRE0_SDA_3": 20,
|
||||
"SERIAL0_CTS": 10,
|
||||
"SERIAL0_RTS": 9,
|
||||
"SERIAL0_RX_0": 12,
|
||||
"SERIAL0_RX_1": 13,
|
||||
"SERIAL0_TX_0": 11,
|
||||
"SERIAL0_TX_1": 14,
|
||||
"SERIAL1_CTS": 4,
|
||||
"SERIAL1_RX_0": 0,
|
||||
"SERIAL1_RX_1": 2,
|
||||
"SERIAL1_TX_0": 1,
|
||||
"SERIAL1_TX_1": 3,
|
||||
"SERIAL2_CTS": 19,
|
||||
"SERIAL2_RTS": 20,
|
||||
"SERIAL2_RX": 15,
|
||||
"SERIAL2_TX": 16,
|
||||
"CS0": 15,
|
||||
"CTS0": 10,
|
||||
"CTS1": 4,
|
||||
"CTS2": 19,
|
||||
"MOSI0": 19,
|
||||
"PA00": 0,
|
||||
"PA0": 0,
|
||||
"PA01": 1,
|
||||
"PA1": 1,
|
||||
"PA02": 2,
|
||||
"PA2": 2,
|
||||
"PA03": 3,
|
||||
"PA3": 3,
|
||||
"PA04": 4,
|
||||
"PA4": 4,
|
||||
"PA07": 7,
|
||||
"PA7": 7,
|
||||
"PA08": 8,
|
||||
"PA8": 8,
|
||||
"PA09": 9,
|
||||
"PA9": 9,
|
||||
"PA10": 10,
|
||||
"PA11": 11,
|
||||
"PA12": 12,
|
||||
"PA13": 13,
|
||||
"PA14": 14,
|
||||
"PA15": 15,
|
||||
"PA16": 16,
|
||||
"PA17": 17,
|
||||
"PA18": 18,
|
||||
"PA19": 19,
|
||||
"PA20": 20,
|
||||
"PA23": 23,
|
||||
"PWM0": 20,
|
||||
"PWM5": 17,
|
||||
"PWM6": 18,
|
||||
"PWM7": 23,
|
||||
"RTS0": 9,
|
||||
"RTS2": 20,
|
||||
"RX2": 15,
|
||||
"SCK0": 16,
|
||||
"TX2": 16,
|
||||
"D0": 0,
|
||||
"D1": 1,
|
||||
"D2": 2,
|
||||
"D3": 3,
|
||||
"D4": 4,
|
||||
"D5": 7,
|
||||
"D6": 8,
|
||||
"D7": 9,
|
||||
"D8": 10,
|
||||
"D9": 11,
|
||||
"D10": 12,
|
||||
"D11": 13,
|
||||
"D12": 14,
|
||||
"D13": 15,
|
||||
"D14": 16,
|
||||
"D15": 17,
|
||||
"D16": 18,
|
||||
"D17": 19,
|
||||
"D18": 20,
|
||||
"D19": 23,
|
||||
},
|
||||
"generic-rtl8720cf-2mb-992k": {
|
||||
"SPI0_CS_0": 2,
|
||||
"SPI0_CS_1": 7,
|
||||
|
||||
@@ -27,46 +27,61 @@ void RuntimeStatsCollector::record_component_time(Component *component, uint32_t
|
||||
}
|
||||
|
||||
void RuntimeStatsCollector::log_stats_() {
|
||||
// First pass: count active components
|
||||
size_t count = 0;
|
||||
for (const auto &it : this->component_stats_) {
|
||||
if (it.second.get_period_count() > 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Component Runtime Statistics\n"
|
||||
" Period stats (last %" PRIu32 "ms):",
|
||||
this->log_interval_);
|
||||
" Period stats (last %" PRIu32 "ms): %zu active components",
|
||||
this->log_interval_, count);
|
||||
|
||||
// First collect stats we want to display
|
||||
std::vector<ComponentStatPair> stats_to_display;
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stack buffer sized to actual active count (up to 256 = 1KB), heap fallback for larger
|
||||
SmallBufferWithHeapFallback<256, Component *> buffer(count);
|
||||
Component **sorted = buffer.get();
|
||||
|
||||
// Second pass: fill buffer with active components
|
||||
size_t idx = 0;
|
||||
for (const auto &it : this->component_stats_) {
|
||||
Component *component = it.first;
|
||||
const ComponentRuntimeStats &stats = it.second;
|
||||
if (stats.get_period_count() > 0) {
|
||||
ComponentStatPair pair = {component, &stats};
|
||||
stats_to_display.push_back(pair);
|
||||
if (it.second.get_period_count() > 0) {
|
||||
sorted[idx++] = it.first;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by period runtime (descending)
|
||||
std::sort(stats_to_display.begin(), stats_to_display.end(), std::greater<ComponentStatPair>());
|
||||
std::sort(sorted, sorted + count, [this](Component *a, Component *b) {
|
||||
return this->component_stats_[a].get_period_time_ms() > this->component_stats_[b].get_period_time_ms();
|
||||
});
|
||||
|
||||
// Log top components by period runtime
|
||||
for (const auto &it : stats_to_display) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const auto &stats = this->component_stats_[sorted[i]];
|
||||
ESP_LOGI(TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms",
|
||||
LOG_STR_ARG(it.component->get_component_log_str()), it.stats->get_period_count(),
|
||||
it.stats->get_period_avg_time_ms(), it.stats->get_period_max_time_ms(), it.stats->get_period_time_ms());
|
||||
LOG_STR_ARG(sorted[i]->get_component_log_str()), stats.get_period_count(), stats.get_period_avg_time_ms(),
|
||||
stats.get_period_max_time_ms(), stats.get_period_time_ms());
|
||||
}
|
||||
|
||||
// Log total stats since boot
|
||||
ESP_LOGI(TAG, " Total stats (since boot):");
|
||||
// Log total stats since boot (only for active components - idle ones haven't changed)
|
||||
ESP_LOGI(TAG, " Total stats (since boot): %zu active components", count);
|
||||
|
||||
// Re-sort by total runtime for all-time stats
|
||||
std::sort(stats_to_display.begin(), stats_to_display.end(),
|
||||
[](const ComponentStatPair &a, const ComponentStatPair &b) {
|
||||
return a.stats->get_total_time_ms() > b.stats->get_total_time_ms();
|
||||
});
|
||||
std::sort(sorted, sorted + count, [this](Component *a, Component *b) {
|
||||
return this->component_stats_[a].get_total_time_ms() > this->component_stats_[b].get_total_time_ms();
|
||||
});
|
||||
|
||||
for (const auto &it : stats_to_display) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const auto &stats = this->component_stats_[sorted[i]];
|
||||
ESP_LOGI(TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms",
|
||||
LOG_STR_ARG(it.component->get_component_log_str()), it.stats->get_total_count(),
|
||||
it.stats->get_total_avg_time_ms(), it.stats->get_total_max_time_ms(), it.stats->get_total_time_ms());
|
||||
LOG_STR_ARG(sorted[i]->get_component_log_str()), stats.get_total_count(), stats.get_total_avg_time_ms(),
|
||||
stats.get_total_max_time_ms(), stats.get_total_time_ms());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,17 +77,6 @@ class ComponentRuntimeStats {
|
||||
uint32_t total_max_time_ms_;
|
||||
};
|
||||
|
||||
// For sorting components by run time
|
||||
struct ComponentStatPair {
|
||||
Component *component;
|
||||
const ComponentRuntimeStats *stats;
|
||||
|
||||
bool operator>(const ComponentStatPair &other) const {
|
||||
// Sort by period time as that's what we're displaying in the logs
|
||||
return stats->get_period_time_ms() > other.stats->get_period_time_ms();
|
||||
}
|
||||
};
|
||||
|
||||
class RuntimeStatsCollector {
|
||||
public:
|
||||
RuntimeStatsCollector();
|
||||
|
||||
@@ -12,9 +12,7 @@ namespace esphome::select {
|
||||
#define LOG_SELECT(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon_ref().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon_ref().c_str()); \
|
||||
} \
|
||||
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
|
||||
}
|
||||
|
||||
#define SUB_SELECT(name) \
|
||||
|
||||
@@ -39,7 +39,7 @@ class ValueRangeTrigger : public Trigger<float>, public Component {
|
||||
template<typename V> void set_max(V max) { this->max_ = max; }
|
||||
|
||||
void setup() override {
|
||||
this->rtc_ = global_preferences->make_preference<bool>(this->parent_->get_preference_hash());
|
||||
this->rtc_ = this->parent_->make_entity_preference<bool>();
|
||||
bool initial_state;
|
||||
if (this->rtc_.load(&initial_state)) {
|
||||
this->previous_in_range_ = initial_state;
|
||||
|
||||
@@ -22,13 +22,8 @@ void log_sensor(const char *tag, const char *prefix, const char *type, Sensor *o
|
||||
LOG_STR_ARG(state_class_to_string(obj->get_state_class())), prefix,
|
||||
obj->get_unit_of_measurement_ref().c_str(), prefix, obj->get_accuracy_decimals());
|
||||
|
||||
if (!obj->get_device_class_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class_ref().c_str());
|
||||
}
|
||||
|
||||
if (!obj->get_icon_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon_ref().c_str());
|
||||
}
|
||||
LOG_ENTITY_DEVICE_CLASS(tag, prefix, *obj);
|
||||
LOG_ENTITY_ICON(tag, prefix, *obj);
|
||||
|
||||
if (obj->get_force_update()) {
|
||||
ESP_LOGV(tag, "%s Force Update: YES", prefix);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace esphome {
|
||||
@@ -21,7 +22,7 @@ enum SmlMessageType : uint16_t { SML_PUBLIC_OPEN_RES = 0x0101, SML_GET_LIST_RES
|
||||
const uint16_t START_MASK = 0x55aa; // 0x1b 1b 1b 1b 01 01 01 01
|
||||
const uint16_t END_MASK = 0x0157; // 0x1b 1b 1b 1b 1a
|
||||
|
||||
const std::vector<uint8_t> START_SEQ = {0x1b, 0x1b, 0x1b, 0x1b, 0x01, 0x01, 0x01, 0x01};
|
||||
constexpr std::array<uint8_t, 8> START_SEQ = {0x1b, 0x1b, 0x1b, 0x1b, 0x01, 0x01, 0x01, 0x01};
|
||||
|
||||
} // namespace sml
|
||||
} // namespace esphome
|
||||
|
||||
@@ -29,6 +29,14 @@ void socket_delay(uint32_t ms) {
|
||||
// Use esp_delay with a callback that checks if socket data arrived.
|
||||
// This allows the delay to exit early when socket_wake() is called by
|
||||
// lwip recv_fn/accept_fn callbacks, reducing socket latency.
|
||||
//
|
||||
// When ms is 0, we must use delay(0) because esp_delay(0, callback)
|
||||
// exits immediately without yielding, which can cause watchdog timeouts
|
||||
// when the main loop runs in high-frequency mode (e.g., during light effects).
|
||||
if (ms == 0) {
|
||||
delay(0);
|
||||
return;
|
||||
}
|
||||
s_socket_woke = false;
|
||||
esp_delay(ms, []() { return !s_socket_woke; });
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ void SpeakerMediaPlayer::setup() {
|
||||
|
||||
this->media_control_command_queue_ = xQueueCreate(MEDIA_CONTROLS_QUEUE_LENGTH, sizeof(MediaCallCommand));
|
||||
|
||||
this->pref_ = global_preferences->make_preference<VolumeRestoreState>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<VolumeRestoreState>();
|
||||
|
||||
VolumeRestoreState volume_restore_state;
|
||||
if (this->pref_.load(&volume_restore_state)) {
|
||||
|
||||
@@ -16,7 +16,7 @@ void SprinklerControllerNumber::setup() {
|
||||
if (!this->restore_value_) {
|
||||
value = this->initial_value_;
|
||||
} else {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
if (!this->pref_.load(&value)) {
|
||||
if (!std::isnan(this->initial_value_)) {
|
||||
value = this->initial_value_;
|
||||
|
||||
@@ -34,7 +34,7 @@ optional<bool> Switch::get_initial_state() {
|
||||
if (!(restore_mode & RESTORE_MODE_PERSISTENT_MASK))
|
||||
return {};
|
||||
|
||||
this->rtc_ = global_preferences->make_preference<bool>(this->get_preference_hash());
|
||||
this->rtc_ = this->make_entity_preference<bool>();
|
||||
bool initial_state;
|
||||
if (!this->rtc_.load(&initial_state))
|
||||
return {};
|
||||
@@ -96,18 +96,14 @@ void log_switch(const char *tag, const char *prefix, const char *type, Switch *o
|
||||
LOG_STR_ARG(onoff));
|
||||
|
||||
// Add optional fields separately
|
||||
if (!obj->get_icon_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon_ref().c_str());
|
||||
}
|
||||
LOG_ENTITY_ICON(tag, prefix, *obj);
|
||||
if (obj->assumed_state()) {
|
||||
ESP_LOGCONFIG(tag, "%s Assumed State: YES", prefix);
|
||||
}
|
||||
if (obj->is_inverted()) {
|
||||
ESP_LOGCONFIG(tag, "%s Inverted: YES", prefix);
|
||||
}
|
||||
if (!obj->get_device_class_ref().empty()) {
|
||||
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class_ref().c_str());
|
||||
}
|
||||
LOG_ENTITY_DEVICE_CLASS(tag, prefix, *obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ void TemplateAlarmControlPanel::setup() {
|
||||
this->current_state_ = ACP_STATE_DISARMED;
|
||||
if (this->restore_mode_ == ALARM_CONTROL_PANEL_RESTORE_DEFAULT_DISARMED) {
|
||||
uint8_t value;
|
||||
this->pref_ = global_preferences->make_preference<uint8_t>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<uint8_t>();
|
||||
if (this->pref_.load(&value)) {
|
||||
this->current_state_ = static_cast<alarm_control_panel::AlarmControlPanelState>(value);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@ void TemplateDate::setup() {
|
||||
state = this->initial_value_;
|
||||
} else {
|
||||
datetime::DateEntityRestoreState temp;
|
||||
this->pref_ =
|
||||
global_preferences->make_preference<datetime::DateEntityRestoreState>(194434030U ^ this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<datetime::DateEntityRestoreState>(194434030U);
|
||||
if (this->pref_.load(&temp)) {
|
||||
temp.apply(this);
|
||||
return;
|
||||
|
||||
@@ -18,8 +18,7 @@ void TemplateDateTime::setup() {
|
||||
state = this->initial_value_;
|
||||
} else {
|
||||
datetime::DateTimeEntityRestoreState temp;
|
||||
this->pref_ = global_preferences->make_preference<datetime::DateTimeEntityRestoreState>(
|
||||
194434090U ^ this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<datetime::DateTimeEntityRestoreState>(194434090U);
|
||||
if (this->pref_.load(&temp)) {
|
||||
temp.apply(this);
|
||||
return;
|
||||
|
||||
@@ -18,8 +18,7 @@ void TemplateTime::setup() {
|
||||
state = this->initial_value_;
|
||||
} else {
|
||||
datetime::TimeEntityRestoreState temp;
|
||||
this->pref_ =
|
||||
global_preferences->make_preference<datetime::TimeEntityRestoreState>(194434060U ^ this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<datetime::TimeEntityRestoreState>(194434060U);
|
||||
if (this->pref_.load(&temp)) {
|
||||
temp.apply(this);
|
||||
return;
|
||||
|
||||
@@ -13,7 +13,7 @@ void TemplateNumber::setup() {
|
||||
if (!this->restore_value_) {
|
||||
value = this->initial_value_;
|
||||
} else {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
if (!this->pref_.load(&value)) {
|
||||
if (!std::isnan(this->initial_value_)) {
|
||||
value = this->initial_value_;
|
||||
|
||||
@@ -11,7 +11,7 @@ void TemplateSelect::setup() {
|
||||
|
||||
size_t index = this->initial_option_index_;
|
||||
if (this->restore_value_) {
|
||||
this->pref_ = global_preferences->make_preference<size_t>(this->get_preference_hash());
|
||||
this->pref_ = this->make_entity_preference<size_t>();
|
||||
size_t restored_index;
|
||||
if (this->pref_.load(&restored_index) && this->has_index(restored_index)) {
|
||||
index = restored_index;
|
||||
|
||||
@@ -20,7 +20,14 @@ void TemplateText::setup() {
|
||||
|
||||
// Need std::string for pref_->setup() to fill from flash
|
||||
std::string value{this->initial_value_ != nullptr ? this->initial_value_ : ""};
|
||||
// For future hash migration: use migrate_entity_preference_() with:
|
||||
// old_key = get_preference_hash() + extra
|
||||
// new_key = get_preference_hash_v2() + extra
|
||||
// See: https://github.com/esphome/backlog/issues/85
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
uint32_t key = this->get_preference_hash();
|
||||
#pragma GCC diagnostic pop
|
||||
key += this->traits.get_min_length() << 2;
|
||||
key += this->traits.get_max_length() << 4;
|
||||
key += fnv1_hash(this->traits.get_pattern_c_str()) << 6;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user