mirror of
https://github.com/esphome/esphome.git
synced 2026-01-26 22:42:06 -07:00
Compare commits
7 Commits
dev
...
libretiny_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37e2f7ccf5 | ||
|
|
2f3b2f027d | ||
|
|
a3ba3a33d9 | ||
|
|
b82149c291 | ||
|
|
a7223a2cd7 | ||
|
|
3bc1ea45ce | ||
|
|
3853e3a4dc |
@@ -1 +1 @@
|
||||
a172e2f65981e98354cc6b5ecf69bdb055dd13602226042ab2c7acd037a2bf41
|
||||
d565b0589e35e692b5f2fc0c14723a99595b4828a3a3ef96c442e86a23176c00
|
||||
|
||||
@@ -9,7 +9,7 @@ static const char *const TAG = "bl0940.number";
|
||||
void CalibrationNumber::setup() {
|
||||
float value = 0.0f;
|
||||
if (this->restore_value_) {
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
if (!this->pref_.load(&value)) {
|
||||
value = 0.0f;
|
||||
}
|
||||
|
||||
@@ -360,7 +360,8 @@ 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_ = this->make_entity_preference<ClimateDeviceRestoreState>(RESTORE_STATE_VERSION);
|
||||
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_preference_hash() ^
|
||||
RESTORE_STATE_VERSION);
|
||||
ClimateDeviceRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
|
||||
@@ -187,7 +187,7 @@ void Cover::publish_state(bool save) {
|
||||
}
|
||||
}
|
||||
optional<CoverRestoreState> Cover::restore_state_() {
|
||||
this->rtc_ = this->make_entity_preference<CoverRestoreState>();
|
||||
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_preference_hash());
|
||||
CoverRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
|
||||
@@ -41,7 +41,7 @@ void DutyTimeSensor::setup() {
|
||||
uint32_t seconds = 0;
|
||||
|
||||
if (this->restore_) {
|
||||
this->pref_ = this->make_entity_preference<uint32_t>();
|
||||
this->pref_ = global_preferences->make_preference<uint32_t>(this->get_preference_hash());
|
||||
this->pref_.load(&seconds);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
@@ -20,8 +19,7 @@ static bool was_power_cycled() {
|
||||
#endif
|
||||
#ifdef USE_ESP8266
|
||||
auto reset_reason = EspClass::getResetReason();
|
||||
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;
|
||||
return strcasecmp(reset_reason.c_str(), "power On") == 0 || strcasecmp(reset_reason.c_str(), "external system") == 0;
|
||||
#endif
|
||||
#ifdef USE_LIBRETINY
|
||||
auto reason = lt_get_reboot_reason();
|
||||
|
||||
@@ -227,7 +227,8 @@ void Fan::publish_state() {
|
||||
constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA;
|
||||
optional<FanRestoreState> Fan::restore_state_() {
|
||||
FanRestoreState recovered{};
|
||||
this->rtc_ = this->make_entity_preference<FanRestoreState>(RESTORE_STATE_VERSION);
|
||||
this->rtc_ =
|
||||
global_preferences->make_preference<FanRestoreState>(this->get_preference_hash() ^ RESTORE_STATE_VERSION);
|
||||
bool restored = this->rtc_.load(&recovered);
|
||||
|
||||
switch (this->restore_mode_) {
|
||||
|
||||
@@ -350,7 +350,8 @@ ClimateTraits HaierClimateBase::traits() { return traits_; }
|
||||
|
||||
void HaierClimateBase::initialization() {
|
||||
constexpr uint32_t restore_settings_version = 0xA77D21EF;
|
||||
this->base_rtc_ = this->make_entity_preference<HaierBaseSettings>(restore_settings_version);
|
||||
this->base_rtc_ =
|
||||
global_preferences->make_preference<HaierBaseSettings>(this->get_preference_hash() ^ restore_settings_version);
|
||||
HaierBaseSettings recovered;
|
||||
if (!this->base_rtc_.load(&recovered)) {
|
||||
recovered = {false, true};
|
||||
|
||||
@@ -515,7 +515,8 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) {
|
||||
void HonClimate::initialization() {
|
||||
HaierClimateBase::initialization();
|
||||
constexpr uint32_t restore_settings_version = 0x57EB59DDUL;
|
||||
this->hon_rtc_ = this->make_entity_preference<HonSettings>(restore_settings_version);
|
||||
this->hon_rtc_ =
|
||||
global_preferences->make_preference<HonSettings>(this->get_preference_hash() ^ restore_settings_version);
|
||||
HonSettings recovered;
|
||||
if (this->hon_rtc_.load(&recovered)) {
|
||||
this->settings_ = recovered;
|
||||
|
||||
@@ -10,7 +10,7 @@ static const char *const TAG = "integration";
|
||||
|
||||
void IntegrationSensor::setup() {
|
||||
if (this->restore_) {
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
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_ = this->presence_timeout_number_->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_preference_hash());
|
||||
this->set_presence_timeout();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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_ = this->make_entity_preference<LightStateRTCState>();
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_preference_hash());
|
||||
// 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_ = this->make_entity_preference<LightStateRTCState>();
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_preference_hash());
|
||||
this->rtc_.load(&recovered);
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
|
||||
break;
|
||||
|
||||
@@ -21,7 +21,7 @@ class LVGLNumber : public number::Number, public Component {
|
||||
void setup() override {
|
||||
float value = this->value_lambda_();
|
||||
if (this->restore_) {
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
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_ = this->make_entity_preference<size_t>();
|
||||
this->pref_ = global_preferences->make_preference<size_t>(this->get_preference_hash());
|
||||
if (this->pref_.load(&index))
|
||||
this->widget_->set_selected_index(index, LV_ANIM_OFF);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "mqtt_alarm_control_panel.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -19,21 +18,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 (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_AWAY")) == 0) {
|
||||
if (strcasecmp(payload.c_str(), "ARM_AWAY") == 0) {
|
||||
call.arm_away();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_HOME")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "ARM_HOME") == 0) {
|
||||
call.arm_home();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_NIGHT")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "ARM_NIGHT") == 0) {
|
||||
call.arm_night();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_VACATION")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "ARM_VACATION") == 0) {
|
||||
call.arm_vacation();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("ARM_CUSTOM_BYPASS")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "ARM_CUSTOM_BYPASS") == 0) {
|
||||
call.arm_custom_bypass();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("DISARM")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "DISARM") == 0) {
|
||||
call.disarm();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("PENDING")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "PENDING") == 0) {
|
||||
call.pending();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("TRIGGERED")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "TRIGGERED") == 0) {
|
||||
call.triggered();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
@@ -120,8 +119,7 @@ bool MQTTAlarmControlPanelComponent::publish_state() {
|
||||
default:
|
||||
state_s = "unknown";
|
||||
}
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), state_s);
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -52,9 +52,8 @@ 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_to_(topic_buf), state_s);
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -132,29 +132,17 @@ std::string MQTTComponent::get_command_topic_() const {
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const std::string &topic, const std::string &payload) {
|
||||
return this->publish(topic.c_str(), payload.data(), payload.size());
|
||||
return this->publish(topic, payload.data(), payload.size());
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const std::string &topic, const char *payload, size_t payload_length) {
|
||||
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')
|
||||
if (topic.empty())
|
||||
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) {
|
||||
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')
|
||||
if (topic.empty())
|
||||
return false;
|
||||
return global_mqtt_client->publish_json(topic, f, this->qos_, this->retain_);
|
||||
}
|
||||
|
||||
@@ -157,38 +157,6 @@ 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.
|
||||
@@ -196,20 +164,6 @@ 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,8 +115,7 @@ bool MQTTCoverComponent::publish_state() {
|
||||
: this->cover_->position == COVER_OPEN ? "open"
|
||||
: traits.get_supports_position() ? "open"
|
||||
: "unknown";
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (!this->publish(this->get_state_topic_to_(topic_buf), state_s))
|
||||
if (!this->publish(this->get_state_topic_(), state_s))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,7 @@ bool MQTTDateComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [year, month, day](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [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,17 +66,15 @@ 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) {
|
||||
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;
|
||||
});
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -44,8 +44,7 @@ void MQTTEventComponent::dump_config() {
|
||||
}
|
||||
|
||||
bool MQTTEventComponent::publish_event_(const std::string &event_type) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [event_type](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [event_type](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[MQTT_EVENT_TYPE] = event_type;
|
||||
});
|
||||
|
||||
@@ -158,10 +158,9 @@ 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_to_(topic_buf), state_s);
|
||||
this->publish(this->get_state_topic_(), state_s);
|
||||
bool failed = false;
|
||||
if (this->state_->get_traits().supports_direction()) {
|
||||
bool success = this->publish(this->get_direction_state_topic(),
|
||||
|
||||
@@ -34,8 +34,7 @@ void MQTTJSONLightComponent::on_light_remote_values_update() {
|
||||
MQTTJSONLightComponent::MQTTJSONLightComponent(LightState *state) : state_(state) {}
|
||||
|
||||
bool MQTTJSONLightComponent::publish_state_() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [this](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [this](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
LightJSONSchema::dump_json(*this->state_, root);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "mqtt_lock.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -17,11 +16,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 (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("LOCK")) == 0) {
|
||||
if (strcasecmp(payload.c_str(), "LOCK") == 0) {
|
||||
this->lock_->lock();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("UNLOCK")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "UNLOCK") == 0) {
|
||||
this->lock_->unlock();
|
||||
} else if (ESPHOME_strcasecmp_P(payload.c_str(), ESPHOME_PSTR("OPEN")) == 0) {
|
||||
} else if (strcasecmp(payload.c_str(), "OPEN") == 0) {
|
||||
this->lock_->open();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
@@ -48,14 +47,13 @@ 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_to_(topic_buf), buf);
|
||||
return this->publish(this->get_state_topic_(), buf);
|
||||
#else
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), LOG_STR_ARG(lock_state_to_string(this->lock_->state)));
|
||||
return this->publish(this->get_state_topic_(), LOG_STR_ARG(lock_state_to_string(this->lock_->state)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -74,10 +74,9 @@ bool MQTTNumberComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTNumberComponent::publish_state(float value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
char buffer[64];
|
||||
size_t len = buf_append_printf(buffer, sizeof(buffer), 0, "%f", value);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), buffer, len);
|
||||
buf_append_printf(buffer, sizeof(buffer), 0, "%f", value);
|
||||
return this->publish(this->get_state_topic_(), buffer);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -50,8 +50,7 @@ bool MQTTSelectComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTSelectComponent::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());
|
||||
return this->publish(this->get_state_topic_(), value);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -79,13 +79,12 @@ 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_to_(topic_buf), "None", 4);
|
||||
return this->publish(this->get_state_topic_(), "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_to_(topic_buf), buf, len);
|
||||
return this->publish(this->get_state_topic_(), buf, len);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -52,9 +52,8 @@ 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_to_(topic_buf), state_s);
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -53,8 +53,7 @@ bool MQTTTextComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTTextComponent::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());
|
||||
return this->publish(this->get_state_topic_(), value);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -31,10 +31,7 @@ void MQTTTextSensor::dump_config() {
|
||||
LOG_MQTT_COMPONENT(true, false);
|
||||
}
|
||||
|
||||
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::publish_state(const std::string &value) { return this->publish(this->get_state_topic_(), value); }
|
||||
bool MQTTTextSensor::send_initial_state() {
|
||||
if (this->sensor_->has_state()) {
|
||||
return this->publish_state(this->sensor_->state);
|
||||
|
||||
@@ -53,8 +53,7 @@ bool MQTTTimeComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [hour, minute, second](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [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,8 +28,7 @@ void MQTTUpdateComponent::setup() {
|
||||
}
|
||||
|
||||
bool MQTTUpdateComponent::publish_state() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [this](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [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,8 +84,7 @@ bool MQTTValveComponent::publish_state() {
|
||||
: this->valve_->position == VALVE_OPEN ? "open"
|
||||
: traits.get_supports_position() ? "open"
|
||||
: "unknown";
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (!this->publish(this->get_state_topic_to_(topic_buf), state_s))
|
||||
if (!this->publish(this->get_state_topic_(), state_s))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ 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)};
|
||||
this->rtc_ = this->parent_->make_entity_preference<bool>(hash.to);
|
||||
uint32_t myhash = hash.to ^ this->parent_->get_preference_hash();
|
||||
this->rtc_ = global_preferences->make_preference<bool>(myhash);
|
||||
bool initial_state;
|
||||
if (this->rtc_.load(&initial_state)) {
|
||||
this->previous_in_range_ = initial_state;
|
||||
|
||||
@@ -17,7 +17,7 @@ void OpenthermNumber::setup() {
|
||||
if (!this->restore_value_) {
|
||||
value = this->initial_value_;
|
||||
} else {
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
if (!this->pref_.load(&value)) {
|
||||
if (!std::isnan(this->initial_value_)) {
|
||||
value = this->initial_value_;
|
||||
|
||||
@@ -132,7 +132,7 @@ void RotaryEncoderSensor::setup() {
|
||||
int32_t initial_value = 0;
|
||||
switch (this->restore_mode_) {
|
||||
case ROTARY_ENCODER_RESTORE_DEFAULT_ZERO:
|
||||
this->rtc_ = this->make_entity_preference<int32_t>();
|
||||
this->rtc_ = global_preferences->make_preference<int32_t>(this->get_preference_hash());
|
||||
if (!this->rtc_.load(&initial_value)) {
|
||||
initial_value = 0;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "preferences.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -24,9 +25,6 @@ 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) {
|
||||
@@ -44,14 +42,12 @@ class RP2040PreferenceBackend : public ESPPreferenceBackend {
|
||||
uint32_t type = 0;
|
||||
|
||||
bool save(const uint8_t *data, size_t len) override {
|
||||
const size_t buffer_size = len + 1;
|
||||
SmallBufferWithHeapFallback<PREF_BUFFER_SIZE> buffer_alloc(buffer_size);
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
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);
|
||||
|
||||
memcpy(buffer, data, len);
|
||||
buffer[len] = calculate_crc(buffer, buffer + len, type);
|
||||
|
||||
for (size_t i = 0; i < buffer_size; i++) {
|
||||
for (uint32_t i = 0; i < len + 1; i++) {
|
||||
uint32_t j = offset + i;
|
||||
if (j >= RP2040_FLASH_STORAGE_SIZE)
|
||||
return false;
|
||||
@@ -64,23 +60,22 @@ class RP2040PreferenceBackend : public ESPPreferenceBackend {
|
||||
return true;
|
||||
}
|
||||
bool load(uint8_t *data, size_t len) override {
|
||||
const size_t buffer_size = len + 1;
|
||||
SmallBufferWithHeapFallback<PREF_BUFFER_SIZE> buffer_alloc(buffer_size);
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.resize(len + 1);
|
||||
|
||||
for (size_t i = 0; i < buffer_size; i++) {
|
||||
for (size_t i = 0; i < len + 1; 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, buffer + len, type);
|
||||
if (buffer[len] != crc) {
|
||||
uint8_t crc = calculate_crc(buffer.begin(), buffer.end() - 1, type);
|
||||
if (buffer[buffer.size() - 1] != crc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, buffer, len);
|
||||
memcpy(data, buffer.data(), 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,
|
||||
|
||||
@@ -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_ = this->parent_->make_entity_preference<bool>();
|
||||
this->rtc_ = global_preferences->make_preference<bool>(this->parent_->get_preference_hash());
|
||||
bool initial_state;
|
||||
if (this->rtc_.load(&initial_state)) {
|
||||
this->previous_in_range_ = initial_state;
|
||||
|
||||
@@ -29,14 +29,6 @@ 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_ = this->make_entity_preference<VolumeRestoreState>();
|
||||
this->pref_ = global_preferences->make_preference<VolumeRestoreState>(this->get_preference_hash());
|
||||
|
||||
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_ = this->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
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_ = this->make_entity_preference<bool>();
|
||||
this->rtc_ = global_preferences->make_preference<bool>(this->get_preference_hash());
|
||||
bool initial_state;
|
||||
if (!this->rtc_.load(&initial_state))
|
||||
return {};
|
||||
|
||||
@@ -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_ = this->make_entity_preference<uint8_t>();
|
||||
this->pref_ = global_preferences->make_preference<uint8_t>(this->get_preference_hash());
|
||||
if (this->pref_.load(&value)) {
|
||||
this->current_state_ = static_cast<alarm_control_panel::AlarmControlPanelState>(value);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ void TemplateDate::setup() {
|
||||
state = this->initial_value_;
|
||||
} else {
|
||||
datetime::DateEntityRestoreState temp;
|
||||
this->pref_ = this->make_entity_preference<datetime::DateEntityRestoreState>(194434030U);
|
||||
this->pref_ =
|
||||
global_preferences->make_preference<datetime::DateEntityRestoreState>(194434030U ^ this->get_preference_hash());
|
||||
if (this->pref_.load(&temp)) {
|
||||
temp.apply(this);
|
||||
return;
|
||||
|
||||
@@ -18,7 +18,8 @@ void TemplateDateTime::setup() {
|
||||
state = this->initial_value_;
|
||||
} else {
|
||||
datetime::DateTimeEntityRestoreState temp;
|
||||
this->pref_ = this->make_entity_preference<datetime::DateTimeEntityRestoreState>(194434090U);
|
||||
this->pref_ = global_preferences->make_preference<datetime::DateTimeEntityRestoreState>(
|
||||
194434090U ^ this->get_preference_hash());
|
||||
if (this->pref_.load(&temp)) {
|
||||
temp.apply(this);
|
||||
return;
|
||||
|
||||
@@ -18,7 +18,8 @@ void TemplateTime::setup() {
|
||||
state = this->initial_value_;
|
||||
} else {
|
||||
datetime::TimeEntityRestoreState temp;
|
||||
this->pref_ = this->make_entity_preference<datetime::TimeEntityRestoreState>(194434060U);
|
||||
this->pref_ =
|
||||
global_preferences->make_preference<datetime::TimeEntityRestoreState>(194434060U ^ this->get_preference_hash());
|
||||
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_ = this->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
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_ = this->make_entity_preference<size_t>();
|
||||
this->pref_ = global_preferences->make_preference<size_t>(this->get_preference_hash());
|
||||
size_t restored_index;
|
||||
if (this->pref_.load(&restored_index) && this->has_index(restored_index)) {
|
||||
index = restored_index;
|
||||
|
||||
@@ -20,14 +20,7 @@ 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;
|
||||
|
||||
@@ -10,7 +10,7 @@ void TotalDailyEnergy::setup() {
|
||||
float initial_value = 0;
|
||||
|
||||
if (this->restore_) {
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
this->pref_.load(&initial_value);
|
||||
}
|
||||
this->publish_state_and_save(initial_value);
|
||||
|
||||
@@ -8,7 +8,7 @@ static const char *const TAG = "tuya.number";
|
||||
|
||||
void TuyaNumber::setup() {
|
||||
if (this->restore_value_) {
|
||||
this->pref_ = this->make_entity_preference<float>();
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
}
|
||||
|
||||
this->parent_->register_listener(this->number_id_, [this](const TuyaDatapoint &datapoint) {
|
||||
|
||||
@@ -163,7 +163,7 @@ void Valve::publish_state(bool save) {
|
||||
}
|
||||
}
|
||||
optional<ValveRestoreState> Valve::restore_state_() {
|
||||
this->rtc_ = this->make_entity_preference<ValveRestoreState>();
|
||||
this->rtc_ = global_preferences->make_preference<ValveRestoreState>(this->get_preference_hash());
|
||||
ValveRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
|
||||
@@ -185,7 +185,7 @@ void WaterHeater::publish_state() {
|
||||
}
|
||||
|
||||
optional<WaterHeaterCall> WaterHeater::restore_state_() {
|
||||
this->pref_ = this->make_entity_preference<SavedWaterHeaterState>();
|
||||
this->pref_ = global_preferences->make_preference<SavedWaterHeaterState>(this->get_preference_hash());
|
||||
SavedWaterHeaterState recovered{};
|
||||
if (!this->pref_.load(&recovered))
|
||||
return {};
|
||||
|
||||
@@ -48,4 +48,4 @@ async def to_code(config):
|
||||
if CORE.is_libretiny:
|
||||
CORE.add_platformio_option("lib_ignore", ["ESPAsyncTCP", "RPAsyncTCP"])
|
||||
# https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json
|
||||
cg.add_library("ESP32Async/ESPAsyncWebServer", "3.9.5")
|
||||
cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.10")
|
||||
|
||||
@@ -746,32 +746,16 @@ void WiFiComponent::setup_ap_config_() {
|
||||
return;
|
||||
|
||||
if (this->ap_.get_ssid().empty()) {
|
||||
// Build AP SSID from app name without heap allocation
|
||||
// WiFi SSID max is 32 bytes, with MAC suffix we keep first 25 + last 7
|
||||
static constexpr size_t AP_SSID_MAX_LEN = 32;
|
||||
static constexpr size_t AP_SSID_PREFIX_LEN = 25;
|
||||
static constexpr size_t AP_SSID_SUFFIX_LEN = 7;
|
||||
|
||||
const std::string &app_name = App.get_name();
|
||||
const char *name_ptr = app_name.c_str();
|
||||
size_t name_len = app_name.length();
|
||||
|
||||
if (name_len <= AP_SSID_MAX_LEN) {
|
||||
// Name fits, use directly
|
||||
this->ap_.set_ssid(name_ptr);
|
||||
} else {
|
||||
// Name too long, need to truncate into stack buffer
|
||||
char ssid_buf[AP_SSID_MAX_LEN + 1];
|
||||
std::string name = App.get_name();
|
||||
if (name.length() > 32) {
|
||||
if (App.is_name_add_mac_suffix_enabled()) {
|
||||
// Keep first 25 chars and last 7 chars (MAC suffix), remove middle
|
||||
memcpy(ssid_buf, name_ptr, AP_SSID_PREFIX_LEN);
|
||||
memcpy(ssid_buf + AP_SSID_PREFIX_LEN, name_ptr + name_len - AP_SSID_SUFFIX_LEN, AP_SSID_SUFFIX_LEN);
|
||||
name.erase(25, name.length() - 32);
|
||||
} else {
|
||||
memcpy(ssid_buf, name_ptr, AP_SSID_MAX_LEN);
|
||||
name.resize(32);
|
||||
}
|
||||
ssid_buf[AP_SSID_MAX_LEN] = '\0';
|
||||
this->ap_.set_ssid(ssid_buf);
|
||||
}
|
||||
this->ap_.set_ssid(name);
|
||||
}
|
||||
this->ap_setup_ = this->wifi_start_ap_(this->ap_);
|
||||
|
||||
|
||||
@@ -92,48 +92,6 @@ StringRef EntityBase::get_object_id_to(std::span<char, OBJECT_ID_MAX_LEN> buf) c
|
||||
|
||||
uint32_t EntityBase::get_object_id_hash() { return this->object_id_hash_; }
|
||||
|
||||
// Migrate preference data from old_key to new_key if they differ.
|
||||
// This helper is exposed so callers with custom key computation (like TextPrefs)
|
||||
// can use it for manual migration. See: https://github.com/esphome/backlog/issues/85
|
||||
//
|
||||
// FUTURE IMPLEMENTATION:
|
||||
// This will require raw load/save methods on ESPPreferenceObject that take uint8_t* and size.
|
||||
// void EntityBase::migrate_entity_preference_(size_t size, uint32_t old_key, uint32_t new_key) {
|
||||
// if (old_key == new_key)
|
||||
// return;
|
||||
// auto old_pref = global_preferences->make_preference(size, old_key);
|
||||
// auto new_pref = global_preferences->make_preference(size, new_key);
|
||||
// SmallBufferWithHeapFallback<64> buffer(size);
|
||||
// if (old_pref.load(buffer.data(), size)) {
|
||||
// new_pref.save(buffer.data(), size);
|
||||
// }
|
||||
// }
|
||||
|
||||
ESPPreferenceObject EntityBase::make_entity_preference_(size_t size, uint32_t version) {
|
||||
// This helper centralizes preference creation to enable fixing hash collisions.
|
||||
// See: https://github.com/esphome/backlog/issues/85
|
||||
//
|
||||
// COLLISION PROBLEM: get_preference_hash() uses fnv1_hash on sanitized object_id.
|
||||
// Multiple entity names can sanitize to the same object_id:
|
||||
// - "Living Room" and "living_room" both become "living_room"
|
||||
// - UTF-8 names like "温度" and "湿度" both become "__" (underscores)
|
||||
// This causes entities to overwrite each other's stored preferences.
|
||||
//
|
||||
// FUTURE MIGRATION: When implementing get_preference_hash_v2() that hashes
|
||||
// the original entity name (not sanitized object_id):
|
||||
//
|
||||
// uint32_t old_key = this->get_preference_hash() ^ version;
|
||||
// uint32_t new_key = this->get_preference_hash_v2() ^ version;
|
||||
// this->migrate_entity_preference_(size, old_key, new_key);
|
||||
// return global_preferences->make_preference(size, new_key);
|
||||
//
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
uint32_t key = this->get_preference_hash() ^ version;
|
||||
#pragma GCC diagnostic pop
|
||||
return global_preferences->make_preference(size, key);
|
||||
}
|
||||
|
||||
std::string EntityBase_DeviceClass::get_device_class() {
|
||||
if (this->device_class_ == nullptr) {
|
||||
return "";
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "string_ref.h"
|
||||
#include "helpers.h"
|
||||
#include "log.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#ifdef USE_DEVICES
|
||||
#include "device.h"
|
||||
@@ -139,12 +138,7 @@ class EntityBase {
|
||||
* from previous versions, so existing single-device configurations will continue to work.
|
||||
*
|
||||
* @return uint32_t The unique hash for preferences, including device_id if available.
|
||||
* @deprecated Use make_entity_preference<T>() instead, or preferences won't be migrated.
|
||||
* See https://github.com/esphome/backlog/issues/85
|
||||
*/
|
||||
ESPDEPRECATED("Use make_entity_preference<T>() instead, or preferences won't be migrated. "
|
||||
"See https://github.com/esphome/backlog/issues/85. Will be removed in 2027.1.0.",
|
||||
"2026.7.0")
|
||||
uint32_t get_preference_hash() {
|
||||
#ifdef USE_DEVICES
|
||||
// Combine object_id_hash with device_id to ensure uniqueness across devices
|
||||
@@ -157,19 +151,7 @@ class EntityBase {
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Create a preference object for storing this entity's state/settings.
|
||||
/// @tparam T The type of data to store (must be trivially copyable)
|
||||
/// @param version Optional version hash XORed with preference key (change when struct layout changes)
|
||||
template<typename T> ESPPreferenceObject make_entity_preference(uint32_t version = 0) {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
|
||||
return this->make_entity_preference_(sizeof(T), version);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Non-template helper for make_entity_preference() to avoid code bloat.
|
||||
/// When preference hash algorithm changes, migration logic goes here.
|
||||
ESPPreferenceObject make_entity_preference_(size_t size, uint32_t version);
|
||||
|
||||
void calc_object_id_();
|
||||
|
||||
StringRef name_;
|
||||
|
||||
@@ -655,11 +655,9 @@ inline uint32_t fnv1_hash_object_id(const char *str, size_t len) {
|
||||
}
|
||||
|
||||
/// snprintf-like function returning std::string of maximum length \p len (excluding null terminator).
|
||||
/// @warning Allocates heap memory. Use snprintf() with a stack buffer instead.
|
||||
std::string __attribute__((format(printf, 1, 3))) str_snprintf(const char *fmt, size_t len, ...);
|
||||
|
||||
/// sprintf-like function returning std::string.
|
||||
/// @warning Allocates heap memory. Use snprintf() with a stack buffer instead.
|
||||
std::string __attribute__((format(printf, 1, 2))) str_sprintf(const char *fmt, ...);
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
|
||||
@@ -114,7 +114,7 @@ lib_deps =
|
||||
ESP8266WiFi ; wifi (Arduino built-in)
|
||||
Update ; ota (Arduino built-in)
|
||||
ESP32Async/ESPAsyncTCP@2.0.0 ; async_tcp
|
||||
ESP32Async/ESPAsyncWebServer@3.9.5 ; web_server_base
|
||||
ESP32Async/ESPAsyncWebServer@3.7.8 ; web_server_base
|
||||
makuna/NeoPixelBus@2.7.3 ; neopixelbus
|
||||
ESP8266HTTPClient ; http_request (Arduino built-in)
|
||||
ESP8266mDNS ; mdns (Arduino built-in)
|
||||
@@ -201,7 +201,7 @@ framework = arduino
|
||||
lib_deps =
|
||||
${common:arduino.lib_deps}
|
||||
bblanchon/ArduinoJson@7.4.2 ; json
|
||||
ESP32Async/ESPAsyncWebServer@3.9.5 ; web_server_base
|
||||
ESP32Async/ESPAsyncWebServer@3.7.8 ; web_server_base
|
||||
build_flags =
|
||||
${common:arduino.build_flags}
|
||||
-DUSE_RP2040
|
||||
@@ -217,7 +217,7 @@ framework = arduino
|
||||
lib_compat_mode = soft
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson@7.4.2 ; json
|
||||
ESP32Async/ESPAsyncWebServer@3.9.5 ; web_server_base
|
||||
ESP32Async/ESPAsyncWebServer@3.7.8 ; web_server_base
|
||||
droscy/esp_wireguard@0.4.2 ; wireguard
|
||||
build_flags =
|
||||
${common:arduino.build_flags}
|
||||
|
||||
@@ -692,8 +692,6 @@ HEAP_ALLOCATING_HELPERS = {
|
||||
"str_truncate": "removal (function is unused)",
|
||||
"str_upper_case": "removal (function is unused)",
|
||||
"str_snake_case": "removal (function is unused)",
|
||||
"str_sprintf": "snprintf() with a stack buffer",
|
||||
"str_snprintf": "snprintf() with a stack buffer",
|
||||
}
|
||||
|
||||
|
||||
@@ -712,9 +710,7 @@ HEAP_ALLOCATING_HELPERS = {
|
||||
r"str_sanitize(?!_)|"
|
||||
r"str_truncate|"
|
||||
r"str_upper_case|"
|
||||
r"str_snake_case|"
|
||||
r"str_sprintf|"
|
||||
r"str_snprintf"
|
||||
r"str_snake_case"
|
||||
r")\s*\(" + CPP_RE_EOL,
|
||||
include=cpp_include,
|
||||
exclude=[
|
||||
|
||||
Reference in New Issue
Block a user