mirror of
https://github.com/esphome/esphome.git
synced 2026-01-13 13:37:39 -07:00
Compare commits
81 Commits
memory_api
...
parition_c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df2936f686 | ||
|
|
2b337aa306 | ||
|
|
4ddaff4027 | ||
|
|
91c504061b | ||
|
|
dc8f7abce2 | ||
|
|
3d673ac55e | ||
|
|
b02696edc0 | ||
|
|
f9720026d0 | ||
|
|
d7b04a3d18 | ||
|
|
0e71fa97a7 | ||
|
|
42e061c9ae | ||
|
|
94763ebdab | ||
|
|
f32bb618ac | ||
|
|
0707f383a6 | ||
|
|
e91c6a79ea | ||
|
|
63fc8b4e5a | ||
|
|
ab73ed76b8 | ||
|
|
bf6a03d1cf | ||
|
|
9928ab09cf | ||
|
|
56c1691d72 | ||
|
|
a065990ab9 | ||
|
|
084f517a20 | ||
|
|
1122ec354f | ||
|
|
431183eebc | ||
|
|
08beaf8750 | ||
|
|
18814f12dc | ||
|
|
9cd888cef6 | ||
|
|
9727c7135c | ||
|
|
93621d85b0 | ||
|
|
046ea922e8 | ||
|
|
fab4efb469 | ||
|
|
efc5672567 | ||
|
|
0ea5f2fd81 | ||
|
|
fa3d998c3d | ||
|
|
5e630e9255 | ||
|
|
864aaeec01 | ||
|
|
9c88e44300 | ||
|
|
4d6a93f92d | ||
|
|
7216120bfd | ||
|
|
1897551b28 | ||
|
|
ead60bc5c4 | ||
|
|
7fe8e53f82 | ||
|
|
8cf0ee38a3 | ||
|
|
4c926cca60 | ||
|
|
57634b612a | ||
|
|
8dff7ee746 | ||
|
|
803bb742c9 | ||
|
|
839139df36 | ||
|
|
24d7e9dd23 | ||
|
|
1214bb6bad | ||
|
|
ab0ca3006a | ||
|
|
260ffba2a5 | ||
|
|
2e899dd010 | ||
|
|
61cbd07e1d | ||
|
|
450962850a | ||
|
|
6b088caf5d | ||
|
|
3e6a65e7dc | ||
|
|
3a101d8886 | ||
|
|
fa0f07bfe9 | ||
|
|
fffa16e4d8 | ||
|
|
734710d22a | ||
|
|
3a1be6822e | ||
|
|
c85b1b8609 | ||
|
|
2e9ddd967c | ||
|
|
078afe9656 | ||
|
|
46574fcbec | ||
|
|
359f45400f | ||
|
|
4da95ccd7e | ||
|
|
c69d58273a | ||
|
|
ffce80f96c | ||
|
|
fa5b14fad4 | ||
|
|
cee532a1e3 | ||
|
|
8524b894d6 | ||
|
|
3a5e708c13 | ||
|
|
96e418a8ca | ||
|
|
780a407b10 | ||
|
|
cfc0d8bdfc | ||
|
|
786d7266f5 | ||
|
|
ede64a9f47 | ||
|
|
e0ce66e011 | ||
|
|
6fce0a6104 |
@@ -1 +1 @@
|
||||
766420905c06eeb6c5f360f68fd965e5ddd9c4a5db6b823263d3ad3accb64a07
|
||||
6857423aecf90accd0a8bf584d36ee094a4938f872447a4efc05a2efc6dc6481
|
||||
|
||||
@@ -215,6 +215,7 @@ esphome/components/hlk_fm22x/* @OnFreund
|
||||
esphome/components/hlw8032/* @rici4kubicek
|
||||
esphome/components/hm3301/* @freekode
|
||||
esphome/components/hmac_md5/* @dwmw2
|
||||
esphome/components/hmac_sha256/* @dwmw2
|
||||
esphome/components/homeassistant/* @esphome/core @OttoWinter
|
||||
esphome/components/homeassistant/number/* @landonr
|
||||
esphome/components/homeassistant/switch/* @Links2004
|
||||
|
||||
@@ -741,13 +741,6 @@ def command_vscode(args: ArgsProtocol) -> int | None:
|
||||
|
||||
|
||||
def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
# Set memory analysis options in config
|
||||
if args.analyze_memory:
|
||||
config.setdefault(CONF_ESPHOME, {})["analyze_memory"] = True
|
||||
|
||||
if args.memory_report:
|
||||
config.setdefault(CONF_ESPHOME, {})["memory_report_file"] = args.memory_report
|
||||
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
@@ -1227,17 +1220,6 @@ def parse_args(argv):
|
||||
help="Only generate source code, do not compile.",
|
||||
action="store_true",
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--analyze-memory",
|
||||
help="Analyze and display memory usage by component after compilation.",
|
||||
action="store_true",
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--memory-report",
|
||||
help="Save memory analysis report to a file (supports .json or .txt).",
|
||||
type=str,
|
||||
metavar="FILE",
|
||||
)
|
||||
|
||||
parser_upload = subparsers.add_parser(
|
||||
"upload",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""CLI interface for memory analysis with report generation."""
|
||||
|
||||
from collections import defaultdict
|
||||
import json
|
||||
import sys
|
||||
|
||||
from . import (
|
||||
@@ -298,28 +297,6 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Export analysis results as JSON."""
|
||||
data = {
|
||||
"components": {
|
||||
name: {
|
||||
"text": mem.text_size,
|
||||
"rodata": mem.rodata_size,
|
||||
"data": mem.data_size,
|
||||
"bss": mem.bss_size,
|
||||
"flash_total": mem.flash_total,
|
||||
"ram_total": mem.ram_total,
|
||||
"symbol_count": mem.symbol_count,
|
||||
}
|
||||
for name, mem in self.components.items()
|
||||
},
|
||||
"totals": {
|
||||
"flash": sum(c.flash_total for c in self.components.values()),
|
||||
"ram": sum(c.ram_total for c in self.components.values()),
|
||||
},
|
||||
}
|
||||
return json.dumps(data, indent=2)
|
||||
|
||||
def dump_uncategorized_symbols(self, output_file: str | None = None) -> None:
|
||||
"""Dump uncategorized symbols for analysis."""
|
||||
# Sort by size descending
|
||||
|
||||
@@ -227,7 +227,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ADE7880),
|
||||
cv.Optional(CONF_FREQUENCY, default="50Hz"): cv.All(
|
||||
cv.frequency, cv.Range(min=45.0, max=66.0)
|
||||
cv.frequency, cv.float_range(min=45.0, max=66.0)
|
||||
),
|
||||
cv.Optional(CONF_IRQ0_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Required(CONF_IRQ1_PIN): pins.internal_gpio_input_pin_schema,
|
||||
|
||||
@@ -35,12 +35,26 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
|
||||
ESP_LOGD(TAG, "Set state to: %s, previous: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
|
||||
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
|
||||
this->current_state_ = state;
|
||||
// Single state callback - triggers check get_state() for specific states
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_ALARM_CONTROL_PANEL) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_alarm_control_panel_update(this);
|
||||
#endif
|
||||
// Cleared fires when leaving TRIGGERED state
|
||||
if (state == ACP_STATE_TRIGGERED) {
|
||||
this->triggered_callback_.call();
|
||||
} else if (state == ACP_STATE_ARMING) {
|
||||
this->arming_callback_.call();
|
||||
} else if (state == ACP_STATE_PENDING) {
|
||||
this->pending_callback_.call();
|
||||
} else if (state == ACP_STATE_ARMED_HOME) {
|
||||
this->armed_home_callback_.call();
|
||||
} else if (state == ACP_STATE_ARMED_NIGHT) {
|
||||
this->armed_night_callback_.call();
|
||||
} else if (state == ACP_STATE_ARMED_AWAY) {
|
||||
this->armed_away_callback_.call();
|
||||
} else if (state == ACP_STATE_DISARMED) {
|
||||
this->disarmed_callback_.call();
|
||||
}
|
||||
|
||||
if (prev_state == ACP_STATE_TRIGGERED) {
|
||||
this->cleared_callback_.call();
|
||||
}
|
||||
@@ -55,6 +69,34 @@ void AlarmControlPanel::add_on_state_callback(std::function<void()> &&callback)
|
||||
this->state_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void AlarmControlPanel::add_on_triggered_callback(std::function<void()> &&callback) {
|
||||
this->triggered_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void AlarmControlPanel::add_on_arming_callback(std::function<void()> &&callback) {
|
||||
this->arming_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void AlarmControlPanel::add_on_armed_home_callback(std::function<void()> &&callback) {
|
||||
this->armed_home_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void AlarmControlPanel::add_on_armed_night_callback(std::function<void()> &&callback) {
|
||||
this->armed_night_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void AlarmControlPanel::add_on_armed_away_callback(std::function<void()> &&callback) {
|
||||
this->armed_away_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void AlarmControlPanel::add_on_pending_callback(std::function<void()> &&callback) {
|
||||
this->pending_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void AlarmControlPanel::add_on_disarmed_callback(std::function<void()> &&callback) {
|
||||
this->disarmed_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void AlarmControlPanel::add_on_cleared_callback(std::function<void()> &&callback) {
|
||||
this->cleared_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
@@ -35,13 +35,54 @@ class AlarmControlPanel : public EntityBase {
|
||||
*/
|
||||
void publish_state(AlarmControlPanelState state);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel changes.
|
||||
* Triggers can check get_state() to determine the new state.
|
||||
/** Add a callback for when the state of the alarm_control_panel changes
|
||||
*
|
||||
* @param callback The callback function
|
||||
*/
|
||||
void add_on_state_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel chanes to triggered
|
||||
*
|
||||
* @param callback The callback function
|
||||
*/
|
||||
void add_on_triggered_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel chanes to arming
|
||||
*
|
||||
* @param callback The callback function
|
||||
*/
|
||||
void add_on_arming_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel changes to pending
|
||||
*
|
||||
* @param callback The callback function
|
||||
*/
|
||||
void add_on_pending_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel changes to armed_home
|
||||
*
|
||||
* @param callback The callback function
|
||||
*/
|
||||
void add_on_armed_home_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel changes to armed_night
|
||||
*
|
||||
* @param callback The callback function
|
||||
*/
|
||||
void add_on_armed_night_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel changes to armed_away
|
||||
*
|
||||
* @param callback The callback function
|
||||
*/
|
||||
void add_on_armed_away_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel changes to disarmed
|
||||
*
|
||||
* @param callback The callback function
|
||||
*/
|
||||
void add_on_disarmed_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback for when the state of the alarm_control_panel clears from triggered
|
||||
*
|
||||
* @param callback The callback function
|
||||
@@ -131,9 +172,23 @@ class AlarmControlPanel : public EntityBase {
|
||||
uint32_t last_update_;
|
||||
// the call control function
|
||||
virtual void control(const AlarmControlPanelCall &call) = 0;
|
||||
// state callback - triggers check get_state() for specific state
|
||||
// state callback
|
||||
CallbackManager<void()> state_callback_{};
|
||||
// clear callback - fires when leaving TRIGGERED state
|
||||
// trigger callback
|
||||
CallbackManager<void()> triggered_callback_{};
|
||||
// arming callback
|
||||
CallbackManager<void()> arming_callback_{};
|
||||
// pending callback
|
||||
CallbackManager<void()> pending_callback_{};
|
||||
// armed_home callback
|
||||
CallbackManager<void()> armed_home_callback_{};
|
||||
// armed_night callback
|
||||
CallbackManager<void()> armed_night_callback_{};
|
||||
// armed_away callback
|
||||
CallbackManager<void()> armed_away_callback_{};
|
||||
// disarmed callback
|
||||
CallbackManager<void()> disarmed_callback_{};
|
||||
// clear callback
|
||||
CallbackManager<void()> cleared_callback_{};
|
||||
// chime callback
|
||||
CallbackManager<void()> chime_callback_{};
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
namespace esphome {
|
||||
namespace alarm_control_panel {
|
||||
|
||||
/// Trigger on any state change
|
||||
class StateTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit StateTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
@@ -14,30 +13,55 @@ class StateTrigger : public Trigger<> {
|
||||
}
|
||||
};
|
||||
|
||||
/// Template trigger that fires when entering a specific state
|
||||
template<AlarmControlPanelState State> class StateEnterTrigger : public Trigger<> {
|
||||
class TriggeredTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit StateEnterTrigger(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {
|
||||
alarm_control_panel->add_on_state_callback([this]() {
|
||||
if (this->alarm_control_panel_->get_state() == State)
|
||||
this->trigger();
|
||||
});
|
||||
explicit TriggeredTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_triggered_callback([this]() { this->trigger(); });
|
||||
}
|
||||
|
||||
protected:
|
||||
AlarmControlPanel *alarm_control_panel_;
|
||||
};
|
||||
|
||||
// Type aliases for state-specific triggers
|
||||
using TriggeredTrigger = StateEnterTrigger<ACP_STATE_TRIGGERED>;
|
||||
using ArmingTrigger = StateEnterTrigger<ACP_STATE_ARMING>;
|
||||
using PendingTrigger = StateEnterTrigger<ACP_STATE_PENDING>;
|
||||
using ArmedHomeTrigger = StateEnterTrigger<ACP_STATE_ARMED_HOME>;
|
||||
using ArmedNightTrigger = StateEnterTrigger<ACP_STATE_ARMED_NIGHT>;
|
||||
using ArmedAwayTrigger = StateEnterTrigger<ACP_STATE_ARMED_AWAY>;
|
||||
using DisarmedTrigger = StateEnterTrigger<ACP_STATE_DISARMED>;
|
||||
class ArmingTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ArmingTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_arming_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class PendingTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit PendingTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_pending_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class ArmedHomeTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ArmedHomeTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_armed_home_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class ArmedNightTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ArmedNightTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_armed_night_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class ArmedAwayTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ArmedAwayTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_armed_away_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class DisarmedTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit DisarmedTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_disarmed_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
/// Trigger when leaving TRIGGERED state (alarm cleared)
|
||||
class ClearedTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
@@ -45,7 +69,6 @@ class ClearedTrigger : public Trigger<> {
|
||||
}
|
||||
};
|
||||
|
||||
/// Trigger on chime event (zone opened while disarmed)
|
||||
class ChimeTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ChimeTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
@@ -53,7 +76,6 @@ class ChimeTrigger : public Trigger<> {
|
||||
}
|
||||
};
|
||||
|
||||
/// Trigger on ready state change
|
||||
class ReadyTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ReadyTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "bh1750.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome::bh1750 {
|
||||
namespace esphome {
|
||||
namespace bh1750 {
|
||||
|
||||
static const char *const TAG = "bh1750.sensor";
|
||||
|
||||
@@ -13,31 +13,6 @@ static const uint8_t BH1750_COMMAND_ONE_TIME_L = 0b00100011;
|
||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
|
||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
|
||||
|
||||
static constexpr uint32_t MEASUREMENT_TIMEOUT_MS = 2000;
|
||||
static constexpr float HIGH_LIGHT_THRESHOLD_LX = 7000.0f;
|
||||
|
||||
// Measurement time constants (datasheet values)
|
||||
static constexpr uint16_t MTREG_DEFAULT = 69;
|
||||
static constexpr uint16_t MTREG_MIN = 31;
|
||||
static constexpr uint16_t MTREG_MAX = 254;
|
||||
static constexpr uint16_t MEAS_TIME_L_MS = 24; // L-resolution max measurement time @ mtreg=69
|
||||
static constexpr uint16_t MEAS_TIME_H_MS = 180; // H/H2-resolution max measurement time @ mtreg=69
|
||||
|
||||
// Conversion constants (datasheet formulas)
|
||||
static constexpr float RESOLUTION_DIVISOR = 1.2f; // counts to lux conversion divisor
|
||||
static constexpr float MODE_H2_DIVISOR = 2.0f; // H2 mode has 2x higher resolution
|
||||
|
||||
// MTreg calculation constants
|
||||
static constexpr int COUNTS_TARGET = 50000; // Target counts for optimal range (avoid saturation)
|
||||
static constexpr int COUNTS_NUMERATOR = 10;
|
||||
static constexpr int COUNTS_DENOMINATOR = 12;
|
||||
|
||||
// MTreg register bit manipulation constants
|
||||
static constexpr uint8_t MTREG_HI_SHIFT = 5; // High 3 bits start at bit 5
|
||||
static constexpr uint8_t MTREG_HI_MASK = 0b111; // 3-bit mask for high bits
|
||||
static constexpr uint8_t MTREG_LO_SHIFT = 0; // Low 5 bits start at bit 0
|
||||
static constexpr uint8_t MTREG_LO_MASK = 0b11111; // 5-bit mask for low bits
|
||||
|
||||
/*
|
||||
bh1750 properties:
|
||||
|
||||
@@ -68,7 +43,74 @@ void BH1750Sensor::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->state_ = IDLE;
|
||||
}
|
||||
|
||||
void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f) {
|
||||
// turn on (after one-shot sensor automatically powers down)
|
||||
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
||||
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Power on failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (active_mtreg_ != mtreg) {
|
||||
// set mtreg
|
||||
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
|
||||
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
|
||||
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Set measurement time failed");
|
||||
active_mtreg_ = 0;
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
active_mtreg_ = mtreg;
|
||||
}
|
||||
|
||||
uint8_t cmd;
|
||||
uint16_t meas_time;
|
||||
switch (mode) {
|
||||
case BH1750_MODE_L:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_L;
|
||||
meas_time = 24 * mtreg / 69;
|
||||
break;
|
||||
case BH1750_MODE_H:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_H;
|
||||
meas_time = 180 * mtreg / 69;
|
||||
break;
|
||||
case BH1750_MODE_H2:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_H2;
|
||||
meas_time = 180 * mtreg / 69;
|
||||
break;
|
||||
default:
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Start measurement failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
// probably not needed, but adjust for rounding
|
||||
meas_time++;
|
||||
|
||||
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
|
||||
uint16_t raw_value;
|
||||
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Read data failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
raw_value = i2c::i2ctohs(raw_value);
|
||||
|
||||
float lx = float(raw_value) / 1.2f;
|
||||
lx *= 69.0f / mtreg;
|
||||
if (mode == BH1750_MODE_H2)
|
||||
lx /= 2.0f;
|
||||
|
||||
f(lx);
|
||||
});
|
||||
}
|
||||
|
||||
void BH1750Sensor::dump_config() {
|
||||
@@ -82,189 +124,45 @@ void BH1750Sensor::dump_config() {
|
||||
}
|
||||
|
||||
void BH1750Sensor::update() {
|
||||
const uint32_t now = millis();
|
||||
|
||||
// Start coarse measurement to determine optimal mode/mtreg
|
||||
if (this->state_ != IDLE) {
|
||||
// Safety timeout: reset if stuck
|
||||
if (now - this->measurement_start_time_ > MEASUREMENT_TIMEOUT_MS) {
|
||||
ESP_LOGW(TAG, "Measurement timeout, resetting state");
|
||||
this->state_ = IDLE;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Previous measurement not complete, skipping update");
|
||||
// first do a quick measurement in L-mode with full range
|
||||
// to find right range
|
||||
this->read_lx_(BH1750_MODE_L, 31, [this](float val) {
|
||||
if (std::isnan(val)) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->start_measurement_(BH1750_MODE_L, MTREG_MIN, now)) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
this->state_ = WAITING_COARSE_MEASUREMENT;
|
||||
this->enable_loop(); // Enable loop while measurement in progress
|
||||
}
|
||||
|
||||
void BH1750Sensor::loop() {
|
||||
const uint32_t now = App.get_loop_component_start_time();
|
||||
|
||||
switch (this->state_) {
|
||||
case IDLE:
|
||||
// Disable loop when idle to save cycles
|
||||
this->disable_loop();
|
||||
break;
|
||||
|
||||
case WAITING_COARSE_MEASUREMENT:
|
||||
if (now - this->measurement_start_time_ >= this->measurement_duration_) {
|
||||
this->state_ = READING_COARSE_RESULT;
|
||||
}
|
||||
break;
|
||||
|
||||
case READING_COARSE_RESULT: {
|
||||
float lx;
|
||||
if (!this->read_measurement_(lx)) {
|
||||
this->fail_and_reset_();
|
||||
break;
|
||||
}
|
||||
|
||||
this->process_coarse_result_(lx);
|
||||
|
||||
// Start fine measurement with optimal settings
|
||||
// fetch millis() again since the read can take a bit
|
||||
if (!this->start_measurement_(this->fine_mode_, this->fine_mtreg_, millis())) {
|
||||
this->fail_and_reset_();
|
||||
break;
|
||||
}
|
||||
|
||||
this->state_ = WAITING_FINE_MEASUREMENT;
|
||||
break;
|
||||
BH1750Mode use_mode;
|
||||
uint8_t use_mtreg;
|
||||
if (val <= 7000) {
|
||||
use_mode = BH1750_MODE_H2;
|
||||
use_mtreg = 254;
|
||||
} else {
|
||||
use_mode = BH1750_MODE_H;
|
||||
// lx = counts / 1.2 * (69 / mtreg)
|
||||
// -> mtreg = counts / 1.2 * (69 / lx)
|
||||
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
|
||||
// -> mtreg = 50000*(10/12)*(69/lx)
|
||||
int ideal_mtreg = 50000 * 10 * 69 / (12 * (int) val);
|
||||
use_mtreg = std::min(254, std::max(31, ideal_mtreg));
|
||||
}
|
||||
ESP_LOGV(TAG, "L result: %f -> Calculated mode=%d, mtreg=%d", val, (int) use_mode, use_mtreg);
|
||||
|
||||
case WAITING_FINE_MEASUREMENT:
|
||||
if (now - this->measurement_start_time_ >= this->measurement_duration_) {
|
||||
this->state_ = READING_FINE_RESULT;
|
||||
this->read_lx_(use_mode, use_mtreg, [this](float val) {
|
||||
if (std::isnan(val)) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case READING_FINE_RESULT: {
|
||||
float lx;
|
||||
if (!this->read_measurement_(lx)) {
|
||||
this->fail_and_reset_();
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), lx);
|
||||
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), val);
|
||||
this->status_clear_warning();
|
||||
this->publish_state(lx);
|
||||
this->state_ = IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BH1750Sensor::start_measurement_(BH1750Mode mode, uint8_t mtreg, uint32_t now) {
|
||||
// Power on
|
||||
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
||||
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Power on failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set MTreg if changed
|
||||
if (this->active_mtreg_ != mtreg) {
|
||||
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> MTREG_HI_SHIFT) & MTREG_HI_MASK);
|
||||
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> MTREG_LO_SHIFT) & MTREG_LO_MASK);
|
||||
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Set measurement time failed");
|
||||
this->active_mtreg_ = 0;
|
||||
return false;
|
||||
}
|
||||
this->active_mtreg_ = mtreg;
|
||||
}
|
||||
|
||||
// Start measurement
|
||||
uint8_t cmd;
|
||||
uint16_t meas_time;
|
||||
switch (mode) {
|
||||
case BH1750_MODE_L:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_L;
|
||||
meas_time = MEAS_TIME_L_MS * mtreg / MTREG_DEFAULT;
|
||||
break;
|
||||
case BH1750_MODE_H:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_H;
|
||||
meas_time = MEAS_TIME_H_MS * mtreg / MTREG_DEFAULT;
|
||||
break;
|
||||
case BH1750_MODE_H2:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_H2;
|
||||
meas_time = MEAS_TIME_H_MS * mtreg / MTREG_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Start measurement failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store current measurement parameters
|
||||
this->current_mode_ = mode;
|
||||
this->current_mtreg_ = mtreg;
|
||||
this->measurement_start_time_ = now;
|
||||
this->measurement_duration_ = meas_time + 1; // Add 1ms for safety
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BH1750Sensor::read_measurement_(float &lx_out) {
|
||||
uint16_t raw_value;
|
||||
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Read data failed");
|
||||
return false;
|
||||
}
|
||||
raw_value = i2c::i2ctohs(raw_value);
|
||||
|
||||
float lx = float(raw_value) / RESOLUTION_DIVISOR;
|
||||
lx *= float(MTREG_DEFAULT) / this->current_mtreg_;
|
||||
if (this->current_mode_ == BH1750_MODE_H2) {
|
||||
lx /= MODE_H2_DIVISOR;
|
||||
}
|
||||
|
||||
lx_out = lx;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BH1750Sensor::process_coarse_result_(float lx) {
|
||||
if (std::isnan(lx)) {
|
||||
// Use defaults if coarse measurement failed
|
||||
this->fine_mode_ = BH1750_MODE_H2;
|
||||
this->fine_mtreg_ = MTREG_MAX;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lx <= HIGH_LIGHT_THRESHOLD_LX) {
|
||||
this->fine_mode_ = BH1750_MODE_H2;
|
||||
this->fine_mtreg_ = MTREG_MAX;
|
||||
} else {
|
||||
this->fine_mode_ = BH1750_MODE_H;
|
||||
// lx = counts / 1.2 * (69 / mtreg)
|
||||
// -> mtreg = counts / 1.2 * (69 / lx)
|
||||
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
|
||||
// -> mtreg = 50000*(10/12)*(69/lx)
|
||||
int ideal_mtreg = COUNTS_TARGET * COUNTS_NUMERATOR * MTREG_DEFAULT / (COUNTS_DENOMINATOR * (int) lx);
|
||||
this->fine_mtreg_ = std::min((int) MTREG_MAX, std::max((int) MTREG_MIN, ideal_mtreg));
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "L result: %.1f -> Calculated mode=%d, mtreg=%d", lx, (int) this->fine_mode_, this->fine_mtreg_);
|
||||
}
|
||||
|
||||
void BH1750Sensor::fail_and_reset_() {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
this->state_ = IDLE;
|
||||
this->publish_state(val);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
} // namespace esphome::bh1750
|
||||
} // namespace bh1750
|
||||
} // namespace esphome
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::bh1750 {
|
||||
namespace esphome {
|
||||
namespace bh1750 {
|
||||
|
||||
enum BH1750Mode : uint8_t {
|
||||
enum BH1750Mode {
|
||||
BH1750_MODE_L,
|
||||
BH1750_MODE_H,
|
||||
BH1750_MODE_H2,
|
||||
@@ -20,36 +21,13 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
void loop() override;
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
// State machine states
|
||||
enum State : uint8_t {
|
||||
IDLE,
|
||||
WAITING_COARSE_MEASUREMENT,
|
||||
READING_COARSE_RESULT,
|
||||
WAITING_FINE_MEASUREMENT,
|
||||
READING_FINE_RESULT,
|
||||
};
|
||||
void read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f);
|
||||
|
||||
// 4-byte aligned members
|
||||
uint32_t measurement_start_time_{0};
|
||||
uint32_t measurement_duration_{0};
|
||||
|
||||
// 1-byte members grouped together to minimize padding
|
||||
State state_{IDLE};
|
||||
BH1750Mode current_mode_{BH1750_MODE_L};
|
||||
uint8_t current_mtreg_{31};
|
||||
BH1750Mode fine_mode_{BH1750_MODE_H2};
|
||||
uint8_t fine_mtreg_{254};
|
||||
uint8_t active_mtreg_{0};
|
||||
|
||||
// Helper methods
|
||||
bool start_measurement_(BH1750Mode mode, uint8_t mtreg, uint32_t now);
|
||||
bool read_measurement_(float &lx_out);
|
||||
void process_coarse_result_(float lx);
|
||||
void fail_and_reset_();
|
||||
};
|
||||
|
||||
} // namespace esphome::bh1750
|
||||
} // namespace bh1750
|
||||
} // namespace esphome
|
||||
|
||||
@@ -11,6 +11,7 @@ CODEOWNERS = ["@neffs", "@kbx81"]
|
||||
|
||||
AUTO_LOAD = ["bme68x_bsec2"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
bme68x_bsec2_i2c_ns = cg.esphome_ns.namespace("bme68x_bsec2_i2c")
|
||||
BME68xBSEC2I2CComponent = bme68x_bsec2_i2c_ns.class_(
|
||||
|
||||
@@ -65,12 +65,6 @@ void CaptivePortal::start() {
|
||||
this->base_->init();
|
||||
if (!this->initialized_) {
|
||||
this->base_->add_handler(this);
|
||||
#ifdef USE_ESP32
|
||||
// Enable LRU socket purging to handle captive portal detection probe bursts
|
||||
// OS captive portal detection makes many simultaneous HTTP requests which can
|
||||
// exhaust sockets. LRU purging automatically closes oldest idle connections.
|
||||
this->base_->get_server()->set_lru_purge_enable(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
|
||||
|
||||
@@ -40,10 +40,6 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
||||
void end() {
|
||||
this->active_ = false;
|
||||
this->disable_loop(); // Stop processing DNS requests
|
||||
#ifdef USE_ESP32
|
||||
// Disable LRU socket purging now that captive portal is done
|
||||
this->base_->get_server()->set_lru_purge_enable(false);
|
||||
#endif
|
||||
this->base_->deinit();
|
||||
if (this->dns_server_ != nullptr) {
|
||||
this->dns_server_->stop();
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
from esphome import automation
|
||||
from esphome import automation, pins
|
||||
from esphome.automation import maybe_simple_id
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import spi
|
||||
from esphome.components.const import CONF_CRC_ENABLE, CONF_ON_PACKET
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_FREQUENCY, CONF_ID, CONF_WAIT_TIME
|
||||
from esphome.const import (
|
||||
CONF_CHANNEL,
|
||||
CONF_DATA,
|
||||
CONF_FREQUENCY,
|
||||
CONF_ID,
|
||||
CONF_WAIT_TIME,
|
||||
)
|
||||
from esphome.core import ID
|
||||
|
||||
CODEOWNERS = ["@lygris", "@gabest11"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
@@ -29,7 +37,6 @@ CONF_MANCHESTER = "manchester"
|
||||
CONF_NUM_PREAMBLE = "num_preamble"
|
||||
CONF_SYNC1 = "sync1"
|
||||
CONF_SYNC0 = "sync0"
|
||||
CONF_PKTLEN = "pktlen"
|
||||
CONF_MAGN_TARGET = "magn_target"
|
||||
CONF_MAX_LNA_GAIN = "max_lna_gain"
|
||||
CONF_MAX_DVGA_GAIN = "max_dvga_gain"
|
||||
@@ -41,6 +48,12 @@ CONF_FILTER_LENGTH_ASK_OOK = "filter_length_ask_ook"
|
||||
CONF_FREEZE = "freeze"
|
||||
CONF_HYST_LEVEL = "hyst_level"
|
||||
|
||||
# Packet mode config keys
|
||||
CONF_PACKET_MODE = "packet_mode"
|
||||
CONF_PACKET_LENGTH = "packet_length"
|
||||
CONF_WHITENING = "whitening"
|
||||
CONF_GDO0_PIN = "gdo0_pin"
|
||||
|
||||
# Enums
|
||||
SyncMode = ns.enum("SyncMode", True)
|
||||
SYNC_MODE = {
|
||||
@@ -152,7 +165,7 @@ CONFIG_MAP = {
|
||||
CONF_OUTPUT_POWER: cv.float_range(min=-30.0, max=11.0),
|
||||
CONF_RX_ATTENUATION: cv.enum(RX_ATTENUATION, upper=False),
|
||||
CONF_DC_BLOCKING_FILTER: cv.boolean,
|
||||
CONF_FREQUENCY: cv.All(cv.frequency, cv.float_range(min=300000000, max=928000000)),
|
||||
CONF_FREQUENCY: cv.All(cv.frequency, cv.float_range(min=300.0e6, max=928.0e6)),
|
||||
CONF_IF_FREQUENCY: cv.All(cv.frequency, cv.float_range(min=25000, max=788000)),
|
||||
CONF_FILTER_BANDWIDTH: cv.All(cv.frequency, cv.float_range(min=58000, max=812000)),
|
||||
CONF_CHANNEL: cv.uint8_t,
|
||||
@@ -167,7 +180,6 @@ CONFIG_MAP = {
|
||||
CONF_NUM_PREAMBLE: cv.int_range(min=0, max=7),
|
||||
CONF_SYNC1: cv.hex_uint8_t,
|
||||
CONF_SYNC0: cv.hex_uint8_t,
|
||||
CONF_PKTLEN: cv.uint8_t,
|
||||
CONF_MAGN_TARGET: cv.enum(MAGN_TARGET, upper=False),
|
||||
CONF_MAX_LNA_GAIN: cv.enum(MAX_LNA_GAIN, upper=False),
|
||||
CONF_MAX_DVGA_GAIN: cv.enum(MAX_DVGA_GAIN, upper=False),
|
||||
@@ -179,13 +191,36 @@ CONFIG_MAP = {
|
||||
CONF_FREEZE: cv.enum(FREEZE, upper=False),
|
||||
CONF_WAIT_TIME: cv.enum(WAIT_TIME, upper=False),
|
||||
CONF_HYST_LEVEL: cv.enum(HYST_LEVEL, upper=False),
|
||||
CONF_PACKET_MODE: cv.boolean,
|
||||
CONF_PACKET_LENGTH: cv.uint8_t,
|
||||
CONF_CRC_ENABLE: cv.boolean,
|
||||
CONF_WHITENING: cv.boolean,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema({cv.GenerateID(): cv.declare_id(CC1101Component)})
|
||||
|
||||
def _validate_packet_mode(config):
|
||||
if config.get(CONF_PACKET_MODE, False):
|
||||
if CONF_GDO0_PIN not in config:
|
||||
raise cv.Invalid("gdo0_pin is required when packet_mode is enabled")
|
||||
if CONF_PACKET_LENGTH not in config:
|
||||
raise cv.Invalid("packet_length is required when packet_mode is enabled")
|
||||
if config[CONF_PACKET_LENGTH] > 64:
|
||||
raise cv.Invalid("packet_length must be <= 64 (FIFO size)")
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CC1101Component),
|
||||
cv.Optional(CONF_GDO0_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_ON_PACKET): automation.validate_automation(single=True),
|
||||
}
|
||||
)
|
||||
.extend({cv.Optional(key): validator for key, validator in CONFIG_MAP.items()})
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(spi.spi_device_schema(cs_pin_required=True))
|
||||
.extend(spi.spi_device_schema(cs_pin_required=True)),
|
||||
_validate_packet_mode,
|
||||
)
|
||||
|
||||
|
||||
@@ -198,12 +233,29 @@ async def to_code(config):
|
||||
if key in config:
|
||||
cg.add(getattr(var, f"set_{key}")(config[key]))
|
||||
|
||||
if CONF_GDO0_PIN in config:
|
||||
gdo0_pin = await cg.gpio_pin_expression(config[CONF_GDO0_PIN])
|
||||
cg.add(var.set_gdo0_pin(gdo0_pin))
|
||||
if CONF_ON_PACKET in config:
|
||||
await automation.build_automation(
|
||||
var.get_packet_trigger(),
|
||||
[
|
||||
(cg.std_vector.template(cg.uint8), "x"),
|
||||
(cg.float_, "rssi"),
|
||||
(cg.uint8, "lqi"),
|
||||
],
|
||||
config[CONF_ON_PACKET],
|
||||
)
|
||||
|
||||
|
||||
# Actions
|
||||
BeginTxAction = ns.class_("BeginTxAction", automation.Action)
|
||||
BeginRxAction = ns.class_("BeginRxAction", automation.Action)
|
||||
ResetAction = ns.class_("ResetAction", automation.Action)
|
||||
SetIdleAction = ns.class_("SetIdleAction", automation.Action)
|
||||
SendPacketAction = ns.class_(
|
||||
"SendPacketAction", automation.Action, cg.Parented.template(CC1101Component)
|
||||
)
|
||||
|
||||
CC1101_ACTION_SCHEMA = cv.Schema(
|
||||
maybe_simple_id({cv.GenerateID(CONF_ID): cv.use_id(CC1101Component)})
|
||||
@@ -218,3 +270,42 @@ async def cc1101_action_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
def validate_raw_data(value):
|
||||
if isinstance(value, str):
|
||||
return value.encode("utf-8")
|
||||
if isinstance(value, list):
|
||||
return cv.Schema([cv.hex_uint8_t])(value)
|
||||
raise cv.Invalid(
|
||||
"data must either be a string wrapped in quotes or a list of bytes"
|
||||
)
|
||||
|
||||
|
||||
SEND_PACKET_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(CC1101Component),
|
||||
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
|
||||
},
|
||||
key=CONF_DATA,
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"cc1101.send_packet", SendPacketAction, SEND_PACKET_ACTION_SCHEMA
|
||||
)
|
||||
async def send_packet_action_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
data = config[CONF_DATA]
|
||||
if isinstance(data, bytes):
|
||||
data = list(data)
|
||||
if cg.is_template(data):
|
||||
templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
|
||||
cg.add(var.set_data_template(templ))
|
||||
else:
|
||||
# Generate static array in flash to avoid RAM copy
|
||||
arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8)
|
||||
arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data))
|
||||
cg.add(var.set_data_static(arr, len(data)))
|
||||
return var
|
||||
|
||||
@@ -99,11 +99,11 @@ CC1101Component::CC1101Component() {
|
||||
this->state_.FS_AUTOCAL = 1;
|
||||
|
||||
// Default Settings
|
||||
this->set_frequency(433920);
|
||||
this->set_if_frequency(153);
|
||||
this->set_filter_bandwidth(203);
|
||||
this->set_frequency(433920000);
|
||||
this->set_if_frequency(153000);
|
||||
this->set_filter_bandwidth(203000);
|
||||
this->set_channel(0);
|
||||
this->set_channel_spacing(200);
|
||||
this->set_channel_spacing(200000);
|
||||
this->set_symbol_rate(5000);
|
||||
this->set_sync_mode(SyncMode::SYNC_MODE_NONE);
|
||||
this->set_carrier_sense_above_threshold(true);
|
||||
@@ -143,6 +143,11 @@ void CC1101Component::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup GDO0 pin if configured
|
||||
if (this->gdo0_pin_ != nullptr) {
|
||||
this->gdo0_pin_->setup();
|
||||
}
|
||||
|
||||
this->initialized_ = true;
|
||||
|
||||
for (uint8_t i = 0; i <= static_cast<uint8_t>(Register::TEST0); i++) {
|
||||
@@ -151,8 +156,69 @@ void CC1101Component::setup() {
|
||||
}
|
||||
this->write_(static_cast<Register>(i));
|
||||
}
|
||||
this->write_(Register::PATABLE, this->pa_table_, sizeof(this->pa_table_));
|
||||
this->set_output_power(this->output_power_requested_);
|
||||
this->strobe_(Command::RX);
|
||||
|
||||
// Defer pin mode setup until after all components have completed setup()
|
||||
// This handles the case where remote_transmitter runs after CC1101 and changes pin mode
|
||||
if (this->gdo0_pin_ != nullptr) {
|
||||
this->defer([this]() { this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT); });
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::loop() {
|
||||
if (this->state_.PKT_FORMAT != static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO) || this->gdo0_pin_ == nullptr ||
|
||||
!this->gdo0_pin_->digital_read()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read state
|
||||
this->read_(Register::RXBYTES);
|
||||
uint8_t rx_bytes = this->state_.NUM_RXBYTES;
|
||||
bool overflow = this->state_.RXFIFO_OVERFLOW;
|
||||
if (overflow || rx_bytes == 0) {
|
||||
ESP_LOGW(TAG, "RX FIFO overflow, flushing");
|
||||
this->enter_idle_();
|
||||
this->strobe_(Command::FRX);
|
||||
this->strobe_(Command::RX);
|
||||
this->wait_for_state_(State::RX);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read packet
|
||||
uint8_t payload_length;
|
||||
if (this->state_.LENGTH_CONFIG == static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_VARIABLE)) {
|
||||
this->read_(Register::FIFO, &payload_length, 1);
|
||||
} else {
|
||||
payload_length = this->state_.PKTLEN;
|
||||
}
|
||||
if (payload_length == 0 || payload_length > 64) {
|
||||
ESP_LOGW(TAG, "Invalid payload length: %u", payload_length);
|
||||
this->enter_idle_();
|
||||
this->strobe_(Command::FRX);
|
||||
this->strobe_(Command::RX);
|
||||
this->wait_for_state_(State::RX);
|
||||
return;
|
||||
}
|
||||
this->packet_.resize(payload_length);
|
||||
this->read_(Register::FIFO, this->packet_.data(), payload_length);
|
||||
|
||||
// Read status and trigger
|
||||
uint8_t status[2];
|
||||
this->read_(Register::FIFO, status, 2);
|
||||
int8_t rssi_raw = static_cast<int8_t>(status[0]);
|
||||
float rssi = (rssi_raw * RSSI_STEP) - RSSI_OFFSET;
|
||||
bool crc_ok = (status[1] & STATUS_CRC_OK_MASK) != 0;
|
||||
uint8_t lqi = status[1] & STATUS_LQI_MASK;
|
||||
if (this->state_.CRC_EN == 0 || crc_ok) {
|
||||
this->packet_trigger_->trigger(this->packet_, rssi, lqi);
|
||||
}
|
||||
|
||||
// Return to rx
|
||||
this->enter_idle_();
|
||||
this->strobe_(Command::FRX);
|
||||
this->strobe_(Command::RX);
|
||||
this->wait_for_state_(State::RX);
|
||||
}
|
||||
|
||||
void CC1101Component::dump_config() {
|
||||
@@ -177,9 +243,12 @@ void CC1101Component::dump_config() {
|
||||
}
|
||||
|
||||
void CC1101Component::begin_tx() {
|
||||
// Ensure Packet Format is 3 (Async Serial), use GDO0 as input during TX
|
||||
// Ensure Packet Format is 3 (Async Serial)
|
||||
this->write_(Register::PKTCTRL0, 0x32);
|
||||
ESP_LOGV(TAG, "Beginning TX sequence");
|
||||
if (this->gdo0_pin_ != nullptr) {
|
||||
this->gdo0_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||
}
|
||||
this->strobe_(Command::TX);
|
||||
if (!this->wait_for_state_(State::TX, 50)) {
|
||||
ESP_LOGW(TAG, "Timed out waiting for TX state!");
|
||||
@@ -188,6 +257,9 @@ void CC1101Component::begin_tx() {
|
||||
|
||||
void CC1101Component::begin_rx() {
|
||||
ESP_LOGV(TAG, "Beginning RX sequence");
|
||||
if (this->gdo0_pin_ != nullptr) {
|
||||
this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||
}
|
||||
this->strobe_(Command::RX);
|
||||
}
|
||||
|
||||
@@ -201,20 +273,6 @@ void CC1101Component::set_idle() {
|
||||
this->enter_idle_();
|
||||
}
|
||||
|
||||
void CC1101Component::set_gdo0_config(uint8_t value) {
|
||||
this->state_.GDO0_CFG = value;
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::IOCFG0);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_gdo2_config(uint8_t value) {
|
||||
this->state_.GDO2_CFG = value;
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::IOCFG2);
|
||||
}
|
||||
}
|
||||
|
||||
bool CC1101Component::wait_for_state_(State target_state, uint32_t timeout_ms) {
|
||||
uint32_t start = millis();
|
||||
while (millis() - start < timeout_ms) {
|
||||
@@ -282,6 +340,33 @@ void CC1101Component::read_(Register reg, uint8_t *buffer, size_t length) {
|
||||
this->disable();
|
||||
}
|
||||
|
||||
CC1101Error CC1101Component::transmit_packet(const std::vector<uint8_t> &packet) {
|
||||
if (this->state_.PKT_FORMAT != static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO)) {
|
||||
return CC1101Error::PARAMS;
|
||||
}
|
||||
|
||||
// Write packet
|
||||
this->enter_idle_();
|
||||
this->strobe_(Command::FTX);
|
||||
if (this->state_.LENGTH_CONFIG == static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_VARIABLE)) {
|
||||
this->write_(Register::FIFO, static_cast<uint8_t>(packet.size()));
|
||||
}
|
||||
this->write_(Register::FIFO, packet.data(), packet.size());
|
||||
this->strobe_(Command::TX);
|
||||
if (!this->wait_for_state_(State::IDLE, 1000)) {
|
||||
ESP_LOGW(TAG, "TX timeout");
|
||||
this->enter_idle_();
|
||||
this->strobe_(Command::RX);
|
||||
this->wait_for_state_(State::RX);
|
||||
return CC1101Error::TIMEOUT;
|
||||
}
|
||||
|
||||
// Return to rx
|
||||
this->strobe_(Command::RX);
|
||||
this->wait_for_state_(State::RX);
|
||||
return CC1101Error::NONE;
|
||||
}
|
||||
|
||||
// Setters
|
||||
void CC1101Component::set_output_power(float value) {
|
||||
this->output_power_requested_ = value;
|
||||
@@ -428,6 +513,7 @@ void CC1101Component::set_modulation_type(Modulation value) {
|
||||
this->state_.PA_POWER = value == Modulation::MODULATION_ASK_OOK ? 1 : 0;
|
||||
if (this->initialized_) {
|
||||
this->enter_idle_();
|
||||
this->set_output_power(this->output_power_requested_);
|
||||
this->write_(Register::MDMCFG2);
|
||||
this->write_(Register::FREND0);
|
||||
this->strobe_(Command::RX);
|
||||
@@ -462,13 +548,6 @@ void CC1101Component::set_sync0(uint8_t value) {
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_pktlen(uint8_t value) {
|
||||
this->state_.PKTLEN = value;
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::PKTLEN);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_magn_target(MagnTarget value) {
|
||||
this->state_.MAGN_TARGET = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
@@ -546,4 +625,50 @@ void CC1101Component::set_hyst_level(HystLevel value) {
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_packet_mode(bool value) {
|
||||
this->state_.PKT_FORMAT =
|
||||
static_cast<uint8_t>(value ? PacketFormat::PACKET_FORMAT_FIFO : PacketFormat::PACKET_FORMAT_ASYNC_SERIAL);
|
||||
if (value) {
|
||||
// Configure GDO0 for FIFO status (asserts on RX FIFO threshold or end of packet)
|
||||
this->state_.GDO0_CFG = 0x01;
|
||||
// Set max RX FIFO threshold to ensure we only trigger on end-of-packet
|
||||
this->state_.FIFO_THR = 15;
|
||||
} else {
|
||||
// Configure GDO0 for serial data (async serial mode)
|
||||
this->state_.GDO0_CFG = 0x0D;
|
||||
}
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::PKTCTRL0);
|
||||
this->write_(Register::IOCFG0);
|
||||
this->write_(Register::FIFOTHR);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_packet_length(uint8_t value) {
|
||||
if (value == 0) {
|
||||
this->state_.LENGTH_CONFIG = static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_VARIABLE);
|
||||
} else {
|
||||
this->state_.LENGTH_CONFIG = static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_FIXED);
|
||||
this->state_.PKTLEN = value;
|
||||
}
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::PKTCTRL0);
|
||||
this->write_(Register::PKTLEN);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_crc_enable(bool value) {
|
||||
this->state_.CRC_EN = value ? 1 : 0;
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::PKTCTRL0);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_whitening(bool value) {
|
||||
this->state_.WHITE_DATA = value ? 1 : 0;
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::PKTCTRL0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::cc1101
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
#include "esphome/components/spi/spi.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "cc1101defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::cc1101 {
|
||||
|
||||
enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW };
|
||||
|
||||
class CC1101Component : public Component,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
|
||||
@@ -15,6 +18,7 @@ class CC1101Component : public Component,
|
||||
CC1101Component();
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
|
||||
// Actions
|
||||
@@ -24,8 +28,7 @@ class CC1101Component : public Component,
|
||||
void set_idle();
|
||||
|
||||
// GDO Pin Configuration
|
||||
void set_gdo0_config(uint8_t value);
|
||||
void set_gdo2_config(uint8_t value);
|
||||
void set_gdo0_pin(InternalGPIOPin *pin) { this->gdo0_pin_ = pin; }
|
||||
|
||||
// Configuration Setters
|
||||
void set_output_power(float value);
|
||||
@@ -48,7 +51,6 @@ class CC1101Component : public Component,
|
||||
void set_num_preamble(uint8_t value);
|
||||
void set_sync1(uint8_t value);
|
||||
void set_sync0(uint8_t value);
|
||||
void set_pktlen(uint8_t value);
|
||||
|
||||
// AGC settings
|
||||
void set_magn_target(MagnTarget value);
|
||||
@@ -63,6 +65,16 @@ class CC1101Component : public Component,
|
||||
void set_wait_time(WaitTime value);
|
||||
void set_hyst_level(HystLevel value);
|
||||
|
||||
// Packet mode settings
|
||||
void set_packet_mode(bool value);
|
||||
void set_packet_length(uint8_t value);
|
||||
void set_crc_enable(bool value);
|
||||
void set_whitening(bool value);
|
||||
|
||||
// Packet mode operations
|
||||
CC1101Error transmit_packet(const std::vector<uint8_t> &packet);
|
||||
Trigger<std::vector<uint8_t>, float, uint8_t> *get_packet_trigger() const { return this->packet_trigger_; }
|
||||
|
||||
protected:
|
||||
uint16_t chip_id_{0};
|
||||
bool initialized_{false};
|
||||
@@ -73,6 +85,13 @@ class CC1101Component : public Component,
|
||||
|
||||
CC1101State state_;
|
||||
|
||||
// GDO pin for packet reception
|
||||
InternalGPIOPin *gdo0_pin_{nullptr};
|
||||
|
||||
// Packet handling
|
||||
Trigger<std::vector<uint8_t>, float, uint8_t> *packet_trigger_{new Trigger<std::vector<uint8_t>, float, uint8_t>()};
|
||||
std::vector<uint8_t> packet_;
|
||||
|
||||
// Low-level Helpers
|
||||
uint8_t strobe_(Command cmd);
|
||||
void write_(Register reg);
|
||||
@@ -107,4 +126,28 @@ template<typename... Ts> class SetIdleAction : public Action<Ts...>, public Pare
|
||||
void play(const Ts &...x) override { this->parent_->set_idle(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SendPacketAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) { this->data_func_ = func; }
|
||||
void set_data_static(const uint8_t *data, size_t len) {
|
||||
this->data_static_ = data;
|
||||
this->data_static_len_ = len;
|
||||
}
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
if (this->data_func_) {
|
||||
auto data = this->data_func_(x...);
|
||||
this->parent_->transmit_packet(data);
|
||||
} else if (this->data_static_ != nullptr) {
|
||||
std::vector<uint8_t> data(this->data_static_, this->data_static_ + this->data_static_len_);
|
||||
this->parent_->transmit_packet(data);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
|
||||
const uint8_t *data_static_{nullptr};
|
||||
size_t data_static_len_{0};
|
||||
};
|
||||
|
||||
} // namespace esphome::cc1101
|
||||
|
||||
@@ -6,6 +6,12 @@ namespace esphome::cc1101 {
|
||||
|
||||
static constexpr float XTAL_FREQUENCY = 26000000;
|
||||
|
||||
static constexpr float RSSI_OFFSET = 74.0f;
|
||||
static constexpr float RSSI_STEP = 0.5f;
|
||||
|
||||
static constexpr uint8_t STATUS_CRC_OK_MASK = 0x80;
|
||||
static constexpr uint8_t STATUS_LQI_MASK = 0x7F;
|
||||
|
||||
static constexpr uint8_t BUS_BURST = 0x40;
|
||||
static constexpr uint8_t BUS_READ = 0x80;
|
||||
static constexpr uint8_t BUS_WRITE = 0x00;
|
||||
@@ -134,6 +140,10 @@ enum class SyncMode : uint8_t {
|
||||
SYNC_MODE_15_16,
|
||||
SYNC_MODE_16_16,
|
||||
SYNC_MODE_30_32,
|
||||
SYNC_MODE_NONE_CS,
|
||||
SYNC_MODE_15_16_CS,
|
||||
SYNC_MODE_16_16_CS,
|
||||
SYNC_MODE_30_32_CS,
|
||||
};
|
||||
|
||||
enum class Modulation : uint8_t {
|
||||
@@ -218,6 +228,19 @@ enum class HystLevel : uint8_t {
|
||||
HYST_LEVEL_HIGH,
|
||||
};
|
||||
|
||||
enum class PacketFormat : uint8_t {
|
||||
PACKET_FORMAT_FIFO,
|
||||
PACKET_FORMAT_SYNC_SERIAL,
|
||||
PACKET_FORMAT_RANDOM_TX,
|
||||
PACKET_FORMAT_ASYNC_SERIAL,
|
||||
};
|
||||
|
||||
enum class LengthConfig : uint8_t {
|
||||
LENGTH_CONFIG_FIXED,
|
||||
LENGTH_CONFIG_VARIABLE,
|
||||
LENGTH_CONFIG_INFINITE,
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) CC1101State {
|
||||
// Byte array accessors for bulk SPI transfers
|
||||
uint8_t *regs() { return reinterpret_cast<uint8_t *>(this); }
|
||||
|
||||
@@ -117,9 +117,7 @@ CONF_MIN_HUMIDITY = "min_humidity"
|
||||
CONF_MAX_HUMIDITY = "max_humidity"
|
||||
CONF_TARGET_HUMIDITY = "target_humidity"
|
||||
|
||||
visual_temperature = cv.float_with_unit(
|
||||
"visual_temperature", "(°C|° C|°|C|°K|° K|K|°F|° F|F)?"
|
||||
)
|
||||
visual_temperature = cv.float_with_unit("visual_temperature", "(°|(° ?)?[CKF])?")
|
||||
|
||||
|
||||
VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Schema(
|
||||
|
||||
@@ -7,9 +7,11 @@ BYTE_ORDER_LITTLE = "little_endian"
|
||||
BYTE_ORDER_BIG = "big_endian"
|
||||
|
||||
CONF_COLOR_DEPTH = "color_depth"
|
||||
CONF_CRC_ENABLE = "crc_enable"
|
||||
CONF_DRAW_ROUNDING = "draw_rounding"
|
||||
CONF_ENABLED = "enabled"
|
||||
CONF_IGNORE_NOT_FOUND = "ignore_not_found"
|
||||
CONF_ON_PACKET = "on_packet"
|
||||
CONF_ON_RECEIVE = "on_receive"
|
||||
CONF_ON_STATE_CHANGE = "on_state_change"
|
||||
CONF_REQUEST_HEADERS = "request_headers"
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace copy {
|
||||
static const char *const TAG = "copy.select";
|
||||
|
||||
void CopySelect::setup() {
|
||||
source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(index); });
|
||||
source_->add_on_state_callback([this](size_t index) { this->publish_state(index); });
|
||||
|
||||
traits.set_options(source_->traits.get_options());
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from esphome import automation, pins
|
||||
from esphome import automation, core, pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32, time
|
||||
from esphome.components.esp32 import (
|
||||
@@ -23,16 +23,20 @@ from esphome.const import (
|
||||
CONF_MINUTE,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_PIN,
|
||||
CONF_PINS,
|
||||
CONF_RUN_DURATION,
|
||||
CONF_SECOND,
|
||||
CONF_SLEEP_DURATION,
|
||||
CONF_TIME_ID,
|
||||
CONF_WAKEUP_PIN,
|
||||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from esphome.types import ConfigType
|
||||
|
||||
WAKEUP_PINS = {
|
||||
VARIANT_ESP32: [
|
||||
@@ -113,7 +117,7 @@ WAKEUP_PINS = {
|
||||
}
|
||||
|
||||
|
||||
def validate_pin_number(value):
|
||||
def validate_pin_number_esp32(value: ConfigType) -> ConfigType:
|
||||
valid_pins = WAKEUP_PINS.get(get_esp32_variant(), WAKEUP_PINS[VARIANT_ESP32])
|
||||
if value[CONF_NUMBER] not in valid_pins:
|
||||
raise cv.Invalid(
|
||||
@@ -122,6 +126,51 @@ def validate_pin_number(value):
|
||||
return value
|
||||
|
||||
|
||||
def validate_pin_number(value: ConfigType) -> ConfigType:
|
||||
if not CORE.is_esp32:
|
||||
return value
|
||||
return validate_pin_number_esp32(value)
|
||||
|
||||
|
||||
def validate_wakeup_pin(
|
||||
value: ConfigType | list[ConfigType],
|
||||
) -> list[ConfigType]:
|
||||
if not isinstance(value, list):
|
||||
processed_pins: list[ConfigType] = [{CONF_PIN: value}]
|
||||
else:
|
||||
processed_pins = list(value)
|
||||
|
||||
for i, pin_config in enumerate(processed_pins):
|
||||
# now validate each item
|
||||
validated_pin = WAKEUP_PIN_SCHEMA(pin_config)
|
||||
validate_pin_number(validated_pin[CONF_PIN])
|
||||
processed_pins[i] = validated_pin
|
||||
|
||||
return processed_pins
|
||||
|
||||
|
||||
def validate_config(config: ConfigType) -> ConfigType:
|
||||
# right now only BK72XX supports the list format for wakeup pins
|
||||
if CORE.is_bk72xx:
|
||||
if CONF_WAKEUP_PIN_MODE in config:
|
||||
wakeup_pins = config.get(CONF_WAKEUP_PIN, [])
|
||||
if len(wakeup_pins) > 1:
|
||||
raise cv.Invalid(
|
||||
"You need to remove the global wakeup_pin_mode and define it per pin"
|
||||
)
|
||||
if wakeup_pins:
|
||||
wakeup_pins[0][CONF_WAKEUP_PIN_MODE] = config.pop(CONF_WAKEUP_PIN_MODE)
|
||||
elif (
|
||||
isinstance(config.get(CONF_WAKEUP_PIN), list)
|
||||
and len(config[CONF_WAKEUP_PIN]) > 1
|
||||
):
|
||||
raise cv.Invalid(
|
||||
"Your platform does not support providing multiple entries in wakeup_pin"
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def _validate_ex1_wakeup_mode(value):
|
||||
if value == "ALL_LOW":
|
||||
esp32.only_on_variant(supported=[VARIANT_ESP32], msg_prefix="ALL_LOW")(value)
|
||||
@@ -141,6 +190,15 @@ def _validate_ex1_wakeup_mode(value):
|
||||
return value
|
||||
|
||||
|
||||
def _validate_sleep_duration(value: core.TimePeriod) -> core.TimePeriod:
|
||||
if not CORE.is_bk72xx:
|
||||
return value
|
||||
max_duration = core.TimePeriod(hours=36)
|
||||
if value > max_duration:
|
||||
raise cv.Invalid("sleep duration cannot be more than 36 hours on BK72XX")
|
||||
return value
|
||||
|
||||
|
||||
deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep")
|
||||
DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component)
|
||||
EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", automation.Action)
|
||||
@@ -186,6 +244,13 @@ WAKEUP_CAUSES_SCHEMA = cv.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
WAKEUP_PIN_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_WAKEUP_PIN_MODE): cv.enum(WAKEUP_PIN_MODES, upper=True),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
@@ -194,14 +259,15 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.All(cv.only_on_esp32, WAKEUP_CAUSES_SCHEMA),
|
||||
cv.positive_time_period_milliseconds,
|
||||
),
|
||||
cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_WAKEUP_PIN): cv.All(
|
||||
cv.only_on_esp32,
|
||||
pins.internal_gpio_input_pin_schema,
|
||||
validate_pin_number,
|
||||
cv.Optional(CONF_SLEEP_DURATION): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
_validate_sleep_duration,
|
||||
),
|
||||
cv.Optional(CONF_WAKEUP_PIN): validate_wakeup_pin,
|
||||
cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(
|
||||
cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True
|
||||
cv.only_on([PLATFORM_ESP32, PLATFORM_BK72XX]),
|
||||
cv.enum(WAKEUP_PIN_MODES),
|
||||
upper=True,
|
||||
),
|
||||
cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All(
|
||||
cv.only_on_esp32,
|
||||
@@ -212,7 +278,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_PINS): cv.ensure_list(
|
||||
pins.internal_gpio_input_pin_schema, validate_pin_number
|
||||
pins.internal_gpio_input_pin_schema,
|
||||
validate_pin_number_esp32,
|
||||
),
|
||||
cv.Required(CONF_MODE): cv.All(
|
||||
cv.enum(EXT1_WAKEUP_MODES, upper=True),
|
||||
@@ -238,7 +305,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]),
|
||||
cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX]),
|
||||
validate_config,
|
||||
)
|
||||
|
||||
|
||||
@@ -249,8 +317,21 @@ async def to_code(config):
|
||||
if CONF_SLEEP_DURATION in config:
|
||||
cg.add(var.set_sleep_duration(config[CONF_SLEEP_DURATION]))
|
||||
if CONF_WAKEUP_PIN in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_WAKEUP_PIN])
|
||||
cg.add(var.set_wakeup_pin(pin))
|
||||
pins_as_list = config.get(CONF_WAKEUP_PIN, [])
|
||||
if CORE.is_bk72xx:
|
||||
cg.add(var.init_wakeup_pins_(len(pins_as_list)))
|
||||
for item in pins_as_list:
|
||||
cg.add(
|
||||
var.add_wakeup_pin(
|
||||
await cg.gpio_pin_expression(item[CONF_PIN]),
|
||||
item.get(
|
||||
CONF_WAKEUP_PIN_MODE, WakeupPinMode.WAKEUP_PIN_MODE_IGNORE
|
||||
),
|
||||
)
|
||||
)
|
||||
else:
|
||||
pin = await cg.gpio_pin_expression(pins_as_list[0][CONF_PIN])
|
||||
cg.add(var.set_wakeup_pin(pin))
|
||||
if CONF_WAKEUP_PIN_MODE in config:
|
||||
cg.add(var.set_wakeup_pin_mode(config[CONF_WAKEUP_PIN_MODE]))
|
||||
if CONF_RUN_DURATION in config:
|
||||
@@ -305,7 +386,10 @@ DEEP_SLEEP_ENTER_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Exclusive(CONF_SLEEP_DURATION, "time"): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
_validate_sleep_duration,
|
||||
)
|
||||
),
|
||||
# Only on ESP32 due to how long the RTC on ESP8266 can stay asleep
|
||||
cv.Exclusive(CONF_UNTIL, "time"): cv.All(
|
||||
@@ -363,5 +447,6 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
"deep_sleep_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO},
|
||||
"deep_sleep_bk72xx.cpp": {PlatformFramework.BK72XX_ARDUINO},
|
||||
}
|
||||
)
|
||||
|
||||
64
esphome/components/deep_sleep/deep_sleep_bk72xx.cpp
Normal file
64
esphome/components/deep_sleep/deep_sleep_bk72xx.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifdef USE_BK72XX
|
||||
|
||||
#include "deep_sleep_component.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::deep_sleep {
|
||||
|
||||
static const char *const TAG = "deep_sleep.bk72xx";
|
||||
|
||||
optional<uint32_t> DeepSleepComponent::get_run_duration_() const { return this->run_duration_; }
|
||||
|
||||
void DeepSleepComponent::dump_config_platform_() {
|
||||
for (const WakeUpPinItem &item : this->wakeup_pins_) {
|
||||
LOG_PIN(" Wakeup Pin: ", item.wakeup_pin);
|
||||
}
|
||||
}
|
||||
|
||||
bool DeepSleepComponent::pin_prevents_sleep_(WakeUpPinItem &pinItem) const {
|
||||
return (pinItem.wakeup_pin_mode == WAKEUP_PIN_MODE_KEEP_AWAKE && pinItem.wakeup_pin != nullptr &&
|
||||
!this->sleep_duration_.has_value() && (pinItem.wakeup_level == get_real_pin_state_(*pinItem.wakeup_pin)));
|
||||
}
|
||||
|
||||
bool DeepSleepComponent::prepare_to_sleep_() {
|
||||
if (wakeup_pins_.size() > 0) {
|
||||
for (WakeUpPinItem &item : this->wakeup_pins_) {
|
||||
if (pin_prevents_sleep_(item)) {
|
||||
// Defer deep sleep until inactive
|
||||
if (!this->next_enter_deep_sleep_) {
|
||||
this->status_set_warning();
|
||||
ESP_LOGV(TAG, "Waiting for pin to switch state to enter deep sleep...");
|
||||
}
|
||||
this->next_enter_deep_sleep_ = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeepSleepComponent::deep_sleep_() {
|
||||
for (WakeUpPinItem &item : this->wakeup_pins_) {
|
||||
if (item.wakeup_pin_mode == WAKEUP_PIN_MODE_INVERT_WAKEUP) {
|
||||
if (item.wakeup_level == get_real_pin_state_(*item.wakeup_pin)) {
|
||||
item.wakeup_level = !item.wakeup_level;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Wake-up on P%u %s (%d)", item.wakeup_pin->get_pin(), item.wakeup_level ? "HIGH" : "LOW",
|
||||
static_cast<int32_t>(item.wakeup_pin_mode));
|
||||
}
|
||||
|
||||
if (this->sleep_duration_.has_value())
|
||||
lt_deep_sleep_config_timer((*this->sleep_duration_ / 1000) & 0xFFFFFFFF);
|
||||
|
||||
for (WakeUpPinItem &item : this->wakeup_pins_) {
|
||||
lt_deep_sleep_config_gpio(1 << item.wakeup_pin->get_pin(), item.wakeup_level);
|
||||
lt_deep_sleep_keep_floating_gpio(1 << item.wakeup_pin->get_pin(), true);
|
||||
}
|
||||
|
||||
lt_deep_sleep_enter();
|
||||
}
|
||||
|
||||
} // namespace esphome::deep_sleep
|
||||
|
||||
#endif // USE_BK72XX
|
||||
@@ -19,7 +19,7 @@
|
||||
namespace esphome {
|
||||
namespace deep_sleep {
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#if defined(USE_ESP32) || defined(USE_BK72XX)
|
||||
|
||||
/** The values of this enum define what should be done if deep sleep is set up with a wakeup pin on the ESP32
|
||||
* and the scenario occurs that the wakeup pin is already in the wakeup state.
|
||||
@@ -33,7 +33,17 @@ enum WakeupPinMode {
|
||||
*/
|
||||
WAKEUP_PIN_MODE_INVERT_WAKEUP,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(USE_BK72XX)
|
||||
struct WakeUpPinItem {
|
||||
InternalGPIOPin *wakeup_pin;
|
||||
WakeupPinMode wakeup_pin_mode;
|
||||
bool wakeup_level;
|
||||
};
|
||||
#endif // USE_BK72XX
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
struct Ext1Wakeup {
|
||||
uint64_t mask;
|
||||
@@ -75,6 +85,13 @@ class DeepSleepComponent : public Component {
|
||||
void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode);
|
||||
#endif // USE_ESP32
|
||||
|
||||
#if defined(USE_BK72XX)
|
||||
void init_wakeup_pins_(size_t capacity) { this->wakeup_pins_.init(capacity); }
|
||||
void add_wakeup_pin(InternalGPIOPin *wakeup_pin, WakeupPinMode wakeup_pin_mode) {
|
||||
this->wakeup_pins_.emplace_back(WakeUpPinItem{wakeup_pin, wakeup_pin_mode, !wakeup_pin->is_inverted()});
|
||||
}
|
||||
#endif // USE_BK72XX
|
||||
|
||||
#if defined(USE_ESP32)
|
||||
#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
void set_ext1_wakeup(Ext1Wakeup ext1_wakeup);
|
||||
@@ -114,7 +131,17 @@ class DeepSleepComponent : public Component {
|
||||
bool prepare_to_sleep_();
|
||||
void deep_sleep_();
|
||||
|
||||
#ifdef USE_BK72XX
|
||||
bool pin_prevents_sleep_(WakeUpPinItem &pinItem) const;
|
||||
bool get_real_pin_state_(InternalGPIOPin &pin) const { return (pin.digital_read() ^ pin.is_inverted()); }
|
||||
#endif // USE_BK72XX
|
||||
|
||||
optional<uint64_t> sleep_duration_;
|
||||
|
||||
#ifdef USE_BK72XX
|
||||
FixedVector<WakeUpPinItem> wakeup_pins_;
|
||||
#endif // USE_BK72XX
|
||||
|
||||
#ifdef USE_ESP32
|
||||
InternalGPIOPin *wakeup_pin_;
|
||||
WakeupPinMode wakeup_pin_mode_{WAKEUP_PIN_MODE_IGNORE};
|
||||
@@ -124,8 +151,10 @@ class DeepSleepComponent : public Component {
|
||||
#endif
|
||||
|
||||
optional<bool> touch_wakeup_;
|
||||
|
||||
optional<WakeupCauseToRunDuration> wakeup_cause_to_run_duration_;
|
||||
#endif // USE_ESP32
|
||||
|
||||
optional<uint32_t> run_duration_;
|
||||
bool next_enter_deep_sleep_{false};
|
||||
bool prevent_{false};
|
||||
|
||||
@@ -4,6 +4,7 @@ import itertools
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
from esphome import yaml_util
|
||||
import esphome.codegen as cg
|
||||
@@ -12,6 +13,7 @@ from esphome.const import (
|
||||
CONF_ADVANCED,
|
||||
CONF_BOARD,
|
||||
CONF_COMPONENTS,
|
||||
CONF_DISABLED,
|
||||
CONF_ESPHOME,
|
||||
CONF_FRAMEWORK,
|
||||
CONF_IGNORE_EFUSE_CUSTOM_MAC,
|
||||
@@ -23,6 +25,7 @@ from esphome.const import (
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
CONF_REF,
|
||||
CONF_REFRESH,
|
||||
CONF_SAFE_MODE,
|
||||
CONF_SOURCE,
|
||||
CONF_TYPE,
|
||||
CONF_VARIANT,
|
||||
@@ -80,6 +83,7 @@ CONF_ASSERTION_LEVEL = "assertion_level"
|
||||
CONF_COMPILER_OPTIMIZATION = "compiler_optimization"
|
||||
CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES = "enable_idf_experimental_features"
|
||||
CONF_ENABLE_LWIP_ASSERT = "enable_lwip_assert"
|
||||
CONF_ENABLE_OTA_ROLLBACK = "enable_ota_rollback"
|
||||
CONF_EXECUTE_FROM_PSRAM = "execute_from_psram"
|
||||
CONF_RELEASE = "release"
|
||||
|
||||
@@ -117,8 +121,8 @@ ARDUINO_ALLOWED_VARIANTS = [
|
||||
]
|
||||
|
||||
|
||||
def get_cpu_frequencies(*frequencies):
|
||||
return [str(x) + "MHZ" for x in frequencies]
|
||||
def get_cpu_frequencies(*frequencies: int) -> list[str]:
|
||||
return [f"{frequency}MHZ" for frequency in frequencies]
|
||||
|
||||
|
||||
CPU_FREQUENCIES = {
|
||||
@@ -135,7 +139,7 @@ CPU_FREQUENCIES = {
|
||||
}
|
||||
|
||||
# Make sure not missed here if a new variant added.
|
||||
assert all(v in CPU_FREQUENCIES for v in VARIANTS)
|
||||
assert all(variant in CPU_FREQUENCIES for variant in VARIANTS)
|
||||
|
||||
FULL_CPU_FREQUENCIES = set(itertools.chain.from_iterable(CPU_FREQUENCIES.values()))
|
||||
|
||||
@@ -249,10 +253,10 @@ def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType):
|
||||
def add_idf_component(
|
||||
*,
|
||||
name: str,
|
||||
repo: str = None,
|
||||
ref: str = None,
|
||||
path: str = None,
|
||||
refresh: TimePeriod = None,
|
||||
repo: str | None = None,
|
||||
ref: str | None = None,
|
||||
path: str | None = None,
|
||||
refresh: TimePeriod | None = None,
|
||||
components: list[str] | None = None,
|
||||
submodules: list[str] | None = None,
|
||||
):
|
||||
@@ -333,7 +337,7 @@ def _format_framework_espidf_version(ver: cv.Version, release: str) -> str:
|
||||
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.{ext}"
|
||||
|
||||
|
||||
def _is_framework_url(source: str) -> str:
|
||||
def _is_framework_url(source: str) -> bool:
|
||||
# platformio accepts many URL schemes for framework repositories and archives including http, https, git, file, and symlink
|
||||
import urllib.parse
|
||||
|
||||
@@ -570,6 +574,13 @@ def final_validate(config):
|
||||
path=[CONF_FLASH_SIZE],
|
||||
)
|
||||
)
|
||||
if advanced[CONF_ENABLE_OTA_ROLLBACK]:
|
||||
safe_mode_config = full_config.get(CONF_SAFE_MODE)
|
||||
if safe_mode_config is None or safe_mode_config.get(CONF_DISABLED, False):
|
||||
_LOGGER.warning(
|
||||
"OTA rollback requires safe_mode, disabling rollback support"
|
||||
)
|
||||
advanced[CONF_ENABLE_OTA_ROLLBACK] = False
|
||||
if errs:
|
||||
raise cv.MultipleInvalid(errs)
|
||||
|
||||
@@ -595,9 +606,6 @@ CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size"
|
||||
KEY_VFS_SELECT_REQUIRED = "vfs_select_required"
|
||||
KEY_VFS_DIR_REQUIRED = "vfs_dir_required"
|
||||
|
||||
# Ring buffer IRAM requirement tracking
|
||||
KEY_RINGBUF_IN_IRAM = "ringbuf_in_iram"
|
||||
|
||||
|
||||
def require_vfs_select() -> None:
|
||||
"""Mark that VFS select support is required by a component.
|
||||
@@ -617,23 +625,15 @@ def require_vfs_dir() -> None:
|
||||
CORE.data[KEY_VFS_DIR_REQUIRED] = True
|
||||
|
||||
|
||||
def enable_ringbuf_in_iram() -> None:
|
||||
"""Keep ring buffer functions in IRAM instead of moving them to flash.
|
||||
|
||||
Call this from components that use esphome/core/ring_buffer.cpp and need
|
||||
the ring buffer functions to remain in IRAM for performance reasons
|
||||
(e.g., voice assistants, audio components).
|
||||
This prevents CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH from being enabled.
|
||||
"""
|
||||
CORE.data[KEY_RINGBUF_IN_IRAM] = True
|
||||
|
||||
|
||||
def _parse_idf_component(value: str) -> ConfigType:
|
||||
"""Parse IDF component shorthand syntax like 'owner/component^version'"""
|
||||
if "^" not in value:
|
||||
raise cv.Invalid(f"Invalid IDF component shorthand '{value}'")
|
||||
name, ref = value.split("^", 1)
|
||||
return {CONF_NAME: name, CONF_REF: ref}
|
||||
# Match operator followed by version-like string (digit or *)
|
||||
if match := re.search(r"(~=|>=|<=|==|!=|>|<|\^|~)(\d|\*)", value):
|
||||
return {CONF_NAME: value[: match.start()], CONF_REF: value[match.start() :]}
|
||||
raise cv.Invalid(
|
||||
f"Invalid IDF component shorthand '{value}'. "
|
||||
f"Expected format: 'owner/component<op>version' where <op> is one of: ^, ~, ~=, ==, !=, >=, >, <=, <"
|
||||
)
|
||||
|
||||
|
||||
def _validate_idf_component(config: ConfigType) -> ConfigType:
|
||||
@@ -701,6 +701,7 @@ FRAMEWORK_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range(
|
||||
min=8192, max=32768
|
||||
),
|
||||
cv.Optional(CONF_ENABLE_OTA_ROLLBACK, default=True): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list(
|
||||
@@ -981,29 +982,14 @@ async def to_code(config):
|
||||
cg.add_platformio_option("framework", "arduino, espidf")
|
||||
cg.add_build_flag("-DUSE_ARDUINO")
|
||||
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
|
||||
cg.add_platformio_option(
|
||||
"board_build.embed_txtfiles",
|
||||
[
|
||||
"managed_components/espressif__esp_insights/server_certs/https_server.crt",
|
||||
"managed_components/espressif__esp_rainmaker/server_certs/rmaker_mqtt_server.crt",
|
||||
"managed_components/espressif__esp_rainmaker/server_certs/rmaker_claim_service_server.crt",
|
||||
"managed_components/espressif__esp_rainmaker/server_certs/rmaker_ota_server.crt",
|
||||
],
|
||||
)
|
||||
cg.add_define(
|
||||
"USE_ARDUINO_VERSION_CODE",
|
||||
cg.RawExpression(
|
||||
f"VERSION_CODE({framework_ver.major}, {framework_ver.minor}, {framework_ver.patch})"
|
||||
),
|
||||
)
|
||||
add_idf_sdkconfig_option(
|
||||
"CONFIG_ARDUINO_LOOP_STACK_SIZE",
|
||||
conf[CONF_ADVANCED][CONF_LOOP_TASK_STACK_SIZE],
|
||||
)
|
||||
add_idf_sdkconfig_option("CONFIG_AUTOSTART_ARDUINO", True)
|
||||
add_idf_sdkconfig_option("CONFIG_MBEDTLS_PSK_MODES", True)
|
||||
add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True)
|
||||
add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True)
|
||||
|
||||
# ESP32-S2 Arduino: Disable USB Serial on boot to avoid TinyUSB dependency
|
||||
if get_esp32_variant() == VARIANT_ESP32S2:
|
||||
@@ -1041,18 +1027,14 @@ async def to_code(config):
|
||||
add_idf_sdkconfig_option("CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH", True)
|
||||
|
||||
# Place ring buffer functions into flash instead of IRAM by default
|
||||
# This saves IRAM but may impact performance for audio/voice components.
|
||||
# Components that need ring buffer in IRAM call enable_ringbuf_in_iram().
|
||||
# Users can also set ringbuf_in_iram: true to force IRAM placement.
|
||||
# In ESP-IDF 6.0 flash placement becomes the default.
|
||||
if conf[CONF_ADVANCED][CONF_RINGBUF_IN_IRAM] or CORE.data.get(
|
||||
KEY_RINGBUF_IN_IRAM, False
|
||||
):
|
||||
# User config or component requires ring buffer in IRAM for performance
|
||||
# This saves IRAM. In ESP-IDF 6.0 flash placement becomes the default.
|
||||
# Users can set ringbuf_in_iram: true as an escape hatch if they encounter issues.
|
||||
if conf[CONF_ADVANCED][CONF_RINGBUF_IN_IRAM]:
|
||||
# User requests ring buffer in IRAM
|
||||
# IDF 6.0+: will need CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=n
|
||||
add_idf_sdkconfig_option("CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH", False)
|
||||
else:
|
||||
# No component needs it - place in flash to save IRAM
|
||||
# Place in flash to save IRAM (default)
|
||||
add_idf_sdkconfig_option("CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH", True)
|
||||
|
||||
# Setup watchdog
|
||||
@@ -1178,6 +1160,11 @@ async def to_code(config):
|
||||
"CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH", True
|
||||
)
|
||||
|
||||
# Enable OTA rollback support
|
||||
if advanced[CONF_ENABLE_OTA_ROLLBACK]:
|
||||
add_idf_sdkconfig_option("CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE", True)
|
||||
cg.add_define("USE_OTA_ROLLBACK")
|
||||
|
||||
cg.add_define("ESPHOME_LOOP_TASK_STACK_SIZE", advanced[CONF_LOOP_TASK_STACK_SIZE])
|
||||
|
||||
cg.add_define(
|
||||
@@ -1207,7 +1194,7 @@ APP_PARTITION_SIZES = {
|
||||
}
|
||||
|
||||
|
||||
def get_arduino_partition_csv(flash_size):
|
||||
def get_arduino_partition_csv(flash_size: str):
|
||||
app_partition_size = APP_PARTITION_SIZES[flash_size]
|
||||
eeprom_partition_size = 0x1000 # 4 KB
|
||||
spiffs_partition_size = 0xF000 # 60 KB
|
||||
@@ -1227,7 +1214,7 @@ spiffs, data, spiffs, 0x{spiffs_partition_start:X}, 0x{spiffs_partition_size:
|
||||
"""
|
||||
|
||||
|
||||
def get_idf_partition_csv(flash_size):
|
||||
def get_idf_partition_csv(flash_size: str):
|
||||
app_partition_size = APP_PARTITION_SIZES[flash_size]
|
||||
|
||||
return f"""\
|
||||
|
||||
@@ -4,25 +4,20 @@
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "preferences.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_clk_tree.h>
|
||||
#include <esp_cpu.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_task_wdt.h>
|
||||
#include <esp_timer.h>
|
||||
#include <soc/rtc.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include <hal/cpu_hal.h>
|
||||
void setup(); // NOLINT(readability-redundant-declaration)
|
||||
void loop(); // NOLINT(readability-redundant-declaration)
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#include <Esp.h>
|
||||
#else
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
#include <esp_clk_tree.h>
|
||||
#endif
|
||||
void setup();
|
||||
void loop();
|
||||
#endif
|
||||
// Weak stub for initArduino - overridden when the Arduino component is present
|
||||
extern "C" __attribute__((weak)) void initArduino() {}
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@@ -41,29 +36,13 @@ void arch_restart() {
|
||||
|
||||
void arch_init() {
|
||||
// Enable the task watchdog only on the loop task (from which we're currently running)
|
||||
#if defined(USE_ESP_IDF)
|
||||
esp_task_wdt_add(nullptr);
|
||||
// Idle task watchdog is disabled on ESP-IDF
|
||||
#elif defined(USE_ARDUINO)
|
||||
enableLoopWDT();
|
||||
// Disable idle task watchdog on the core we're using (Arduino pins the task to a core)
|
||||
#if defined(CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0) && CONFIG_ARDUINO_RUNNING_CORE == 0
|
||||
disableCore0WDT();
|
||||
#endif
|
||||
#if defined(CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1) && CONFIG_ARDUINO_RUNNING_CORE == 1
|
||||
disableCore1WDT();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// If the bootloader was compiled with CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE the current
|
||||
// partition will get rolled back unless it is marked as valid.
|
||||
esp_ota_img_states_t state;
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (esp_ota_get_state_partition(running, &state) == ESP_OK) {
|
||||
if (state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
}
|
||||
}
|
||||
// Handle OTA rollback: mark partition valid immediately unless USE_OTA_ROLLBACK is enabled,
|
||||
// in which case safe_mode will mark it valid after confirming successful boot.
|
||||
#ifndef USE_OTA_ROLLBACK
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
#endif
|
||||
}
|
||||
void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); }
|
||||
|
||||
@@ -71,21 +50,10 @@ uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||
uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); }
|
||||
uint32_t arch_get_cpu_freq_hz() {
|
||||
uint32_t freq = 0;
|
||||
#ifdef USE_ESP_IDF
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_CPU, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq);
|
||||
#else
|
||||
rtc_cpu_freq_config_t config;
|
||||
rtc_clk_cpu_freq_get_config(&config);
|
||||
freq = config.freq_mhz * 1000000U;
|
||||
#endif
|
||||
#elif defined(USE_ARDUINO)
|
||||
freq = ESP.getCpuFreqMHz() * 1000000;
|
||||
#endif
|
||||
return freq;
|
||||
}
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
TaskHandle_t loop_task_handle = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
void loop_task(void *pv_params) {
|
||||
@@ -96,6 +64,7 @@ void loop_task(void *pv_params) {
|
||||
}
|
||||
|
||||
extern "C" void app_main() {
|
||||
initArduino();
|
||||
esp32::setup_preferences();
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
xTaskCreate(loop_task, "loopTask", ESPHOME_LOOP_TASK_STACK_SIZE, nullptr, 1, &loop_task_handle);
|
||||
@@ -103,11 +72,6 @@ extern "C" void app_main() {
|
||||
xTaskCreatePinnedToCore(loop_task, "loopTask", ESPHOME_LOOP_TASK_STACK_SIZE, nullptr, 1, &loop_task_handle, 1);
|
||||
#endif
|
||||
}
|
||||
#endif // USE_ESP_IDF
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
extern "C" void init() { esp32::setup_preferences(); }
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
|
||||
break;
|
||||
}
|
||||
gpio_set_intr_type(this->get_pin_num(), idf_type);
|
||||
gpio_intr_enable(this->get_pin_num());
|
||||
if (!isr_service_installed) {
|
||||
auto res = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3);
|
||||
if (res != ESP_OK) {
|
||||
@@ -94,7 +95,6 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
|
||||
isr_service_installed = true;
|
||||
}
|
||||
gpio_isr_handler_add(this->get_pin_num(), func, arg);
|
||||
gpio_intr_enable(this->get_pin_num());
|
||||
}
|
||||
|
||||
std::string ESP32InternalGPIOPin::dump_summary() const {
|
||||
|
||||
@@ -5,6 +5,7 @@ import json # noqa: E402
|
||||
import os # noqa: E402
|
||||
import pathlib # noqa: E402
|
||||
import shutil # noqa: E402
|
||||
from glob import glob # noqa: E402
|
||||
|
||||
|
||||
def merge_factory_bin(source, target, env):
|
||||
@@ -126,3 +127,14 @@ def esp32_copy_ota_bin(source, target, env):
|
||||
# Run merge first, then ota copy second
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", merge_factory_bin) # noqa: F821
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_copy_ota_bin) # noqa: F821
|
||||
|
||||
# Find server certificates in managed components and generate .S files.
|
||||
# Workaround for PlatformIO not processing target_add_binary_data() from managed component CMakeLists.
|
||||
project_dir = env.subst("$PROJECT_DIR")
|
||||
managed_components = os.path.join(project_dir, "managed_components")
|
||||
if os.path.isdir(managed_components):
|
||||
for cert_file in glob(os.path.join(managed_components, "**/server_certs/*.crt"), recursive=True):
|
||||
try:
|
||||
env.FileToAsm(cert_file, FILE_TYPE="TEXT")
|
||||
except Exception as e:
|
||||
print(f"Error processing {os.path.basename(cert_file)}: {e}")
|
||||
|
||||
@@ -4,26 +4,28 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include <nvs_flash.h>
|
||||
#include <cstring>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace esp32 {
|
||||
|
||||
static const char *const TAG = "esp32.preferences";
|
||||
|
||||
// Buffer size for converting uint32_t to string: max "4294967295" (10 chars) + null terminator + 1 padding
|
||||
static constexpr size_t KEY_BUFFER_SIZE = 12;
|
||||
|
||||
struct NVSData {
|
||||
std::string key;
|
||||
uint32_t key;
|
||||
std::unique_ptr<uint8_t[]> data;
|
||||
size_t len;
|
||||
|
||||
void set_data(const uint8_t *src, size_t size) {
|
||||
data = std::make_unique<uint8_t[]>(size);
|
||||
memcpy(data.get(), src, size);
|
||||
len = size;
|
||||
this->data = std::make_unique<uint8_t[]>(size);
|
||||
memcpy(this->data.get(), src, size);
|
||||
this->len = size;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,27 +33,27 @@ static std::vector<NVSData> s_pending_save; // NOLINT(cppcoreguidelines-avoid-n
|
||||
|
||||
class ESP32PreferenceBackend : public ESPPreferenceBackend {
|
||||
public:
|
||||
std::string key;
|
||||
uint32_t key;
|
||||
uint32_t nvs_handle;
|
||||
bool save(const uint8_t *data, size_t len) override {
|
||||
// try find in pending saves and update that
|
||||
for (auto &obj : s_pending_save) {
|
||||
if (obj.key == key) {
|
||||
if (obj.key == this->key) {
|
||||
obj.set_data(data, len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
NVSData save{};
|
||||
save.key = key;
|
||||
save.key = this->key;
|
||||
save.set_data(data, len);
|
||||
s_pending_save.emplace_back(std::move(save));
|
||||
ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %zu", key.c_str(), len);
|
||||
ESP_LOGVV(TAG, "s_pending_save: key: %" PRIu32 ", len: %zu", this->key, len);
|
||||
return true;
|
||||
}
|
||||
bool load(uint8_t *data, size_t len) override {
|
||||
// try find in pending saves and load from that
|
||||
for (auto &obj : s_pending_save) {
|
||||
if (obj.key == key) {
|
||||
if (obj.key == this->key) {
|
||||
if (obj.len != len) {
|
||||
// size mismatch
|
||||
return false;
|
||||
@@ -61,22 +63,24 @@ class ESP32PreferenceBackend : public ESPPreferenceBackend {
|
||||
}
|
||||
}
|
||||
|
||||
char key_str[KEY_BUFFER_SIZE];
|
||||
snprintf(key_str, sizeof(key_str), "%" PRIu32, this->key);
|
||||
size_t actual_len;
|
||||
esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(), nullptr, &actual_len);
|
||||
esp_err_t err = nvs_get_blob(this->nvs_handle, key_str, nullptr, &actual_len);
|
||||
if (err != 0) {
|
||||
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key.c_str(), esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key_str, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
if (actual_len != len) {
|
||||
ESP_LOGVV(TAG, "NVS length does not match (%zu!=%zu)", actual_len, len);
|
||||
return false;
|
||||
}
|
||||
err = nvs_get_blob(nvs_handle, key.c_str(), data, &len);
|
||||
err = nvs_get_blob(this->nvs_handle, key_str, data, &len);
|
||||
if (err != 0) {
|
||||
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key_str, esp_err_to_name(err));
|
||||
return false;
|
||||
} else {
|
||||
ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %zu", key.c_str(), len);
|
||||
ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %zu", key_str, len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -103,14 +107,12 @@ class ESP32Preferences : public ESPPreferences {
|
||||
}
|
||||
}
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
|
||||
return make_preference(length, type);
|
||||
return this->make_preference(length, type);
|
||||
}
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
|
||||
auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
pref->nvs_handle = nvs_handle;
|
||||
|
||||
uint32_t keyval = type;
|
||||
pref->key = str_sprintf("%" PRIu32, keyval);
|
||||
pref->nvs_handle = this->nvs_handle;
|
||||
pref->key = type;
|
||||
|
||||
return ESPPreferenceObject(pref);
|
||||
}
|
||||
@@ -123,17 +125,19 @@ class ESP32Preferences : public ESPPreferences {
|
||||
// goal try write all pending saves even if one fails
|
||||
int cached = 0, written = 0, failed = 0;
|
||||
esp_err_t last_err = ESP_OK;
|
||||
std::string last_key{};
|
||||
uint32_t last_key = 0;
|
||||
|
||||
// go through vector from back to front (makes erase easier/more efficient)
|
||||
for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) {
|
||||
const auto &save = s_pending_save[i];
|
||||
ESP_LOGVV(TAG, "Checking if NVS data %s has changed", save.key.c_str());
|
||||
if (is_changed(nvs_handle, save)) {
|
||||
esp_err_t err = nvs_set_blob(nvs_handle, save.key.c_str(), save.data.get(), save.len);
|
||||
ESP_LOGV(TAG, "sync: key: %s, len: %zu", save.key.c_str(), save.len);
|
||||
ESP_LOGVV(TAG, "Checking if NVS data %" PRIu32 " has changed", save.key);
|
||||
if (this->is_changed(this->nvs_handle, save)) {
|
||||
char key_str[KEY_BUFFER_SIZE];
|
||||
snprintf(key_str, sizeof(key_str), "%" PRIu32, save.key);
|
||||
esp_err_t err = nvs_set_blob(this->nvs_handle, key_str, save.data.get(), save.len);
|
||||
ESP_LOGV(TAG, "sync: key: %s, len: %zu", key_str, save.len);
|
||||
if (err != 0) {
|
||||
ESP_LOGV(TAG, "nvs_set_blob('%s', len=%zu) failed: %s", save.key.c_str(), save.len, esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "nvs_set_blob('%s', len=%zu) failed: %s", key_str, save.len, esp_err_to_name(err));
|
||||
failed++;
|
||||
last_err = err;
|
||||
last_key = save.key;
|
||||
@@ -141,7 +145,7 @@ class ESP32Preferences : public ESPPreferences {
|
||||
}
|
||||
written++;
|
||||
} else {
|
||||
ESP_LOGV(TAG, "NVS data not changed skipping %s len=%zu", save.key.c_str(), save.len);
|
||||
ESP_LOGV(TAG, "NVS data not changed skipping %" PRIu32 " len=%zu", save.key, save.len);
|
||||
cached++;
|
||||
}
|
||||
s_pending_save.erase(s_pending_save.begin() + i);
|
||||
@@ -149,12 +153,12 @@ class ESP32Preferences : public ESPPreferences {
|
||||
ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
|
||||
failed);
|
||||
if (failed > 0) {
|
||||
ESP_LOGE(TAG, "Writing %d items failed. Last error=%s for key=%s", failed, esp_err_to_name(last_err),
|
||||
last_key.c_str());
|
||||
ESP_LOGE(TAG, "Writing %d items failed. Last error=%s for key=%" PRIu32, failed, esp_err_to_name(last_err),
|
||||
last_key);
|
||||
}
|
||||
|
||||
// note: commit on esp-idf currently is a no-op, nvs_set_blob always writes
|
||||
esp_err_t err = nvs_commit(nvs_handle);
|
||||
esp_err_t err = nvs_commit(this->nvs_handle);
|
||||
if (err != 0) {
|
||||
ESP_LOGV(TAG, "nvs_commit() failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
@@ -163,10 +167,13 @@ class ESP32Preferences : public ESPPreferences {
|
||||
return failed == 0;
|
||||
}
|
||||
bool is_changed(const uint32_t nvs_handle, const NVSData &to_save) {
|
||||
char key_str[KEY_BUFFER_SIZE];
|
||||
snprintf(key_str, sizeof(key_str), "%" PRIu32, to_save.key);
|
||||
|
||||
size_t actual_len;
|
||||
esp_err_t err = nvs_get_blob(nvs_handle, to_save.key.c_str(), nullptr, &actual_len);
|
||||
esp_err_t err = nvs_get_blob(nvs_handle, key_str, nullptr, &actual_len);
|
||||
if (err != 0) {
|
||||
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", to_save.key.c_str(), esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key_str, esp_err_to_name(err));
|
||||
return true;
|
||||
}
|
||||
// Check size first before allocating memory
|
||||
@@ -174,9 +181,9 @@ class ESP32Preferences : public ESPPreferences {
|
||||
return true;
|
||||
}
|
||||
auto stored_data = std::make_unique<uint8_t[]>(actual_len);
|
||||
err = nvs_get_blob(nvs_handle, to_save.key.c_str(), stored_data.get(), &actual_len);
|
||||
err = nvs_get_blob(nvs_handle, key_str, stored_data.get(), &actual_len);
|
||||
if (err != 0) {
|
||||
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key.c_str(), esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key_str, esp_err_to_name(err));
|
||||
return true;
|
||||
}
|
||||
return memcmp(to_save.data.get(), stored_data.get(), to_save.len) != 0;
|
||||
|
||||
@@ -96,10 +96,6 @@ void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->advertising_set_manufacturer_data(std::span<const uint8_t>(data));
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_set_manufacturer_data(std::span<const uint8_t> data) {
|
||||
this->advertising_init_();
|
||||
this->advertising_->set_manufacturer_data(data);
|
||||
this->advertising_start();
|
||||
|
||||
@@ -118,7 +118,6 @@ class ESP32BLE : public Component {
|
||||
void advertising_start();
|
||||
void advertising_set_service_data(const std::vector<uint8_t> &data);
|
||||
void advertising_set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void advertising_set_manufacturer_data(std::span<const uint8_t> data);
|
||||
void advertising_set_appearance(uint16_t appearance) { this->appearance_ = appearance; }
|
||||
void advertising_set_service_data_and_name(std::span<const uint8_t> data, bool include_name);
|
||||
void advertising_add_service_uuid(ESPBTUUID uuid);
|
||||
|
||||
@@ -59,10 +59,6 @@ void BLEAdvertising::set_service_data(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->set_manufacturer_data(std::span<const uint8_t>(data));
|
||||
}
|
||||
|
||||
void BLEAdvertising::set_manufacturer_data(std::span<const uint8_t> data) {
|
||||
delete[] this->advertising_data_.p_manufacturer_data;
|
||||
this->advertising_data_.p_manufacturer_data = nullptr;
|
||||
this->advertising_data_.manufacturer_len = data.size();
|
||||
|
||||
@@ -37,7 +37,6 @@ class BLEAdvertising {
|
||||
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
|
||||
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
|
||||
void set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void set_manufacturer_data(std::span<const uint8_t> data);
|
||||
void set_appearance(uint16_t appearance) { this->advertising_data_.appearance = appearance; }
|
||||
void set_service_data(const std::vector<uint8_t> &data);
|
||||
void set_service_data(std::span<const uint8_t> data);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "esp32_ble_beacon.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
|
||||
@@ -15,10 +15,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_characteristic_on_w
|
||||
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
|
||||
new Trigger<std::vector<uint8_t>, uint16_t>();
|
||||
characteristic->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
|
||||
// Convert span to vector for trigger - copy is necessary because:
|
||||
// 1. Trigger stores the data for use in automation actions that execute later
|
||||
// 2. The span is only valid during this callback (points to temporary BLE stack data)
|
||||
// 3. User lambdas in automations need persistent data they can access asynchronously
|
||||
// Convert span to vector for trigger
|
||||
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
|
||||
});
|
||||
return on_write_trigger;
|
||||
@@ -30,10 +27,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_descriptor_on_write
|
||||
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
|
||||
new Trigger<std::vector<uint8_t>, uint16_t>();
|
||||
descriptor->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
|
||||
// Convert span to vector for trigger - copy is necessary because:
|
||||
// 1. Trigger stores the data for use in automation actions that execute later
|
||||
// 2. The span is only valid during this callback (points to temporary BLE stack data)
|
||||
// 3. User lambdas in automations need persistent data they can access asynchronously
|
||||
// Convert span to vector for trigger
|
||||
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
|
||||
});
|
||||
return on_write_trigger;
|
||||
|
||||
@@ -5,7 +5,7 @@ import logging
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble, ota
|
||||
from esphome.components import esp32_ble
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
from esphome.components.esp32_ble import (
|
||||
IDF_MAX_CONNECTIONS,
|
||||
@@ -328,7 +328,7 @@ async def to_code(config):
|
||||
# Note: CONFIG_BT_ACL_CONNECTIONS and CONFIG_BTDM_CTRL_BLE_MAX_CONN are now
|
||||
# configured in esp32_ble component based on max_connections setting
|
||||
|
||||
ota.request_ota_state_listeners() # To be notified when an OTA update starts
|
||||
cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts
|
||||
cg.add_define("USE_ESP32_BLE_CLIENT")
|
||||
|
||||
CORE.add_job(_add_ble_features)
|
||||
|
||||
@@ -71,23 +71,20 @@ void ESP32BLETracker::setup() {
|
||||
|
||||
global_esp32_ble_tracker = this;
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
ota::get_global_ota_callback()->add_global_state_listener(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
void ESP32BLETracker::on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
|
||||
if (state == ota::OTA_STARTED) {
|
||||
this->stop_scan();
|
||||
#ifdef USE_OTA
|
||||
ota::get_global_ota_callback()->add_on_state_callback(
|
||||
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
|
||||
if (state == ota::OTA_STARTED) {
|
||||
this->stop_scan();
|
||||
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
|
||||
for (auto *client : this->clients_) {
|
||||
client->disconnect();
|
||||
}
|
||||
for (auto *client : this->clients_) {
|
||||
client->disconnect();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ESP32BLETracker::loop() {
|
||||
if (!this->parent_->is_active()) {
|
||||
|
||||
@@ -22,10 +22,6 @@
|
||||
#include "esphome/components/esp32_ble/ble_uuid.h"
|
||||
#include "esphome/components/esp32_ble/ble_scan_result.h"
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::esp32_ble_tracker {
|
||||
|
||||
using namespace esp32_ble;
|
||||
@@ -245,9 +241,6 @@ class ESP32BLETracker : public Component,
|
||||
public GAPScanEventHandler,
|
||||
public GATTcEventHandler,
|
||||
public BLEStatusEventHandler,
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
public ota::OTAGlobalStateListener,
|
||||
#endif
|
||||
public Parented<ESP32BLE> {
|
||||
public:
|
||||
void set_scan_duration(uint32_t scan_duration) { scan_duration_ = scan_duration; }
|
||||
@@ -281,10 +274,6 @@ class ESP32BLETracker : public Component,
|
||||
void gap_scan_event_handler(const BLEScanResult &scan_result) override;
|
||||
void ble_before_disabled_event_handler() override;
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
void on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) override;
|
||||
#endif
|
||||
|
||||
/// Add a listener for scanner state changes
|
||||
void add_scanner_state_listener(BLEScannerStateListener *listener) {
|
||||
this->scanner_state_listeners_.push_back(listener);
|
||||
|
||||
@@ -3,7 +3,7 @@ import logging
|
||||
from esphome import automation, pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
from esphome.components.esp32 import add_idf_component
|
||||
from esphome.components.esp32 import add_idf_component, add_idf_sdkconfig_option
|
||||
from esphome.components.psram import DOMAIN as psram_domain
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
@@ -186,7 +186,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
{
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
|
||||
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
|
||||
cv.frequency, cv.Range(min=8e6, max=20e6)
|
||||
cv.frequency, cv.float_range(min=8e6, max=20e6)
|
||||
),
|
||||
}
|
||||
),
|
||||
@@ -352,6 +352,8 @@ async def to_code(config):
|
||||
cg.add_define("USE_CAMERA")
|
||||
|
||||
add_idf_component(name="espressif/esp32-camera", ref="2.1.1")
|
||||
add_idf_sdkconfig_option("CONFIG_SCCB_HARDWARE_I2C_DRIVER_NEW", True)
|
||||
add_idf_sdkconfig_option("CONFIG_SCCB_HARDWARE_I2C_DRIVER_LEGACY", False)
|
||||
|
||||
for conf in config.get(CONF_ON_STREAM_START, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
|
||||
@@ -16,7 +16,7 @@ def valid_pwm_pin(value):
|
||||
esp8266_pwm_ns = cg.esphome_ns.namespace("esp8266_pwm")
|
||||
ESP8266PWM = esp8266_pwm_ns.class_("ESP8266PWM", output.FloatOutput, cg.Component)
|
||||
SetFrequencyAction = esp8266_pwm_ns.class_("SetFrequencyAction", automation.Action)
|
||||
validate_frequency = cv.All(cv.frequency, cv.Range(min=1.0e-6))
|
||||
validate_frequency = cv.All(cv.frequency, cv.float_range(min=1.0e-6))
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
|
||||
@@ -41,6 +41,10 @@ static constexpr size_t SHA256_HEX_SIZE = 64; // SHA256 hash as hex string (32
|
||||
#endif // USE_OTA_PASSWORD
|
||||
|
||||
void ESPHomeOTAComponent::setup() {
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
ota::register_ota_platform(this);
|
||||
#endif
|
||||
|
||||
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
|
||||
if (this->server_ == nullptr) {
|
||||
this->log_socket_error_(LOG_STR("creation"));
|
||||
@@ -293,8 +297,8 @@ void ESPHomeOTAComponent::handle_data_() {
|
||||
// accidentally trigger the update process.
|
||||
this->log_start_(LOG_STR("update"));
|
||||
this->status_set_warning();
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_STARTED, 0.0f, 0);
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0);
|
||||
#endif
|
||||
|
||||
// This will block for a few seconds as it locks flash
|
||||
@@ -353,8 +357,8 @@ void ESPHomeOTAComponent::handle_data_() {
|
||||
last_progress = now;
|
||||
float percentage = (total * 100.0f) / ota_size;
|
||||
ESP_LOGD(TAG, "Progress: %0.1f%%", percentage);
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_IN_PROGRESS, percentage, 0);
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0);
|
||||
#endif
|
||||
// feed watchdog and give other tasks a chance to run
|
||||
this->yield_and_feed_watchdog_();
|
||||
@@ -383,8 +387,8 @@ void ESPHomeOTAComponent::handle_data_() {
|
||||
delay(10);
|
||||
ESP_LOGI(TAG, "Update complete");
|
||||
this->status_clear_warning();
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_COMPLETED, 100.0f, 0);
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, 0);
|
||||
#endif
|
||||
delay(100); // NOLINT
|
||||
App.safe_reboot();
|
||||
@@ -398,8 +402,8 @@ error:
|
||||
}
|
||||
|
||||
this->status_momentary_error("err", 5000);
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
this->state_callback_.call(ota::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
|
||||
|
||||
#ifdef USE_OTA_PASSWORD
|
||||
std::string password_;
|
||||
std::unique_ptr<uint8_t[]> auth_buf_;
|
||||
#endif // USE_OTA_PASSWORD
|
||||
|
||||
std::unique_ptr<socket::Socket> server_;
|
||||
@@ -93,7 +94,6 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
|
||||
uint8_t handshake_buf_pos_{0};
|
||||
uint8_t ota_features_{0};
|
||||
#ifdef USE_OTA_PASSWORD
|
||||
std::unique_ptr<uint8_t[]> auth_buf_;
|
||||
uint8_t auth_buf_pos_{0};
|
||||
uint8_t auth_type_{0}; // Store auth type to know which hasher to use
|
||||
#endif // USE_OTA_PASSWORD
|
||||
|
||||
@@ -13,7 +13,7 @@ static const char *const TAG = "espnow.transport";
|
||||
bool ESPNowTransport::should_send() { return this->parent_ != nullptr && !this->parent_->is_failed(); }
|
||||
|
||||
void ESPNowTransport::setup() {
|
||||
packet_transport::PacketTransport::setup();
|
||||
PacketTransport::setup();
|
||||
|
||||
if (this->parent_ == nullptr) {
|
||||
ESP_LOGE(TAG, "ESPNow component not set");
|
||||
@@ -26,15 +26,10 @@ void ESPNowTransport::setup() {
|
||||
this->peer_address_[2], this->peer_address_[3], this->peer_address_[4], this->peer_address_[5]);
|
||||
|
||||
// Register received handler
|
||||
this->parent_->register_received_handler(static_cast<ESPNowReceivedPacketHandler *>(this));
|
||||
this->parent_->register_received_handler(this);
|
||||
|
||||
// Register broadcasted handler
|
||||
this->parent_->register_broadcasted_handler(static_cast<ESPNowBroadcastedHandler *>(this));
|
||||
}
|
||||
|
||||
void ESPNowTransport::update() {
|
||||
packet_transport::PacketTransport::update();
|
||||
this->updated_ = true;
|
||||
this->parent_->register_broadcasted_handler(this);
|
||||
}
|
||||
|
||||
void ESPNowTransport::send_packet(const std::vector<uint8_t> &buf) const {
|
||||
|
||||
@@ -18,7 +18,6 @@ class ESPNowTransport : public packet_transport::PacketTransport,
|
||||
public ESPNowBroadcastedHandler {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||
|
||||
void set_peer_address(peer_address_t address) {
|
||||
|
||||
@@ -434,10 +434,13 @@ def _final_validate_rmii_pins(config: ConfigType) -> None:
|
||||
|
||||
# Check all used pins against RMII reserved pins
|
||||
for pin_list in pins.PIN_SCHEMA_REGISTRY.pins_used.values():
|
||||
for pin_path, _, pin_config in pin_list:
|
||||
for pin_path, pin_device, pin_config in pin_list:
|
||||
pin_num = pin_config.get(CONF_NUMBER)
|
||||
if pin_num not in rmii_pins:
|
||||
continue
|
||||
# Skip if pin is not directly on ESP, but at some expander (device set to something else than 'None')
|
||||
if pin_device is not None:
|
||||
continue
|
||||
# Found a conflict - show helpful error message
|
||||
pin_function = rmii_pins[pin_num]
|
||||
component_path = ".".join(str(p) for p in pin_path)
|
||||
|
||||
@@ -50,7 +50,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.GenerateID(): cv.declare_id(FactoryResetComponent),
|
||||
cv.Optional(CONF_MAX_DELAY, default="10s"): cv.All(
|
||||
cv.positive_time_period_seconds,
|
||||
cv.Range(min=cv.TimePeriod(milliseconds=1000)),
|
||||
cv.Range(
|
||||
min=cv.TimePeriod(seconds=1), max=cv.TimePeriod(seconds=65535)
|
||||
),
|
||||
),
|
||||
cv.Optional(CONF_RESETS_REQUIRED): cv.positive_not_null_int,
|
||||
cv.Optional(CONF_ON_INCREMENT): validate_automation(
|
||||
@@ -82,7 +84,7 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
reset_count,
|
||||
config[CONF_MAX_DELAY].total_milliseconds,
|
||||
config[CONF_MAX_DELAY].total_seconds,
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
for conf in config.get(CONF_ON_INCREMENT, []):
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#if !defined(USE_RP2040) && !defined(USE_HOST)
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
namespace esphome::factory_reset {
|
||||
|
||||
static const char *const TAG = "factory_reset";
|
||||
static const uint32_t POWER_CYCLES_KEY = 0xFA5C0DE;
|
||||
@@ -33,10 +32,10 @@ void FactoryResetComponent::dump_config() {
|
||||
this->flash_.load(&count);
|
||||
ESP_LOGCONFIG(TAG, "Factory Reset by Reset:");
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Max interval between resets %" PRIu32 " seconds\n"
|
||||
" Max interval between resets: %u seconds\n"
|
||||
" Current count: %u\n"
|
||||
" Factory reset after %u resets",
|
||||
this->max_interval_ / 1000, count, this->required_count_);
|
||||
this->max_interval_, count, this->required_count_);
|
||||
}
|
||||
|
||||
void FactoryResetComponent::save_(uint8_t count) {
|
||||
@@ -61,8 +60,8 @@ void FactoryResetComponent::setup() {
|
||||
}
|
||||
this->save_(count);
|
||||
ESP_LOGD(TAG, "Power on reset detected, incremented count to %u", count);
|
||||
this->set_timeout(this->max_interval_, [this]() {
|
||||
ESP_LOGD(TAG, "No reset in the last %" PRIu32 " seconds, resetting count", this->max_interval_ / 1000);
|
||||
this->set_timeout(static_cast<uint32_t>(this->max_interval_) * 1000, [this]() {
|
||||
ESP_LOGD(TAG, "No reset in the last %u seconds, resetting count", this->max_interval_);
|
||||
this->save_(0); // reset count
|
||||
});
|
||||
} else {
|
||||
@@ -70,7 +69,6 @@ void FactoryResetComponent::setup() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace factory_reset
|
||||
} // namespace esphome
|
||||
} // namespace esphome::factory_reset
|
||||
|
||||
#endif // !defined(USE_RP2040) && !defined(USE_HOST)
|
||||
|
||||
@@ -9,12 +9,11 @@
|
||||
#include <esp_system.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
namespace esphome::factory_reset {
|
||||
class FactoryResetComponent : public Component {
|
||||
public:
|
||||
FactoryResetComponent(uint8_t required_count, uint32_t max_interval)
|
||||
: required_count_(required_count), max_interval_(max_interval) {}
|
||||
FactoryResetComponent(uint8_t required_count, uint16_t max_interval)
|
||||
: max_interval_(max_interval), required_count_(required_count) {}
|
||||
|
||||
void dump_config() override;
|
||||
void setup() override;
|
||||
@@ -26,9 +25,9 @@ class FactoryResetComponent : public Component {
|
||||
~FactoryResetComponent() = default;
|
||||
void save_(uint8_t count);
|
||||
ESPPreferenceObject flash_{}; // saves the number of fast power cycles
|
||||
uint8_t required_count_; // The number of boot attempts before fast boot is enabled
|
||||
uint32_t max_interval_; // max interval between power cycles
|
||||
CallbackManager<void(uint8_t, uint8_t)> increment_callback_{};
|
||||
uint16_t max_interval_; // max interval between power cycles in seconds
|
||||
uint8_t required_count_; // The number of boot attempts before fast boot is enabled
|
||||
};
|
||||
|
||||
class FastBootTrigger : public Trigger<uint8_t, uint8_t> {
|
||||
@@ -37,7 +36,6 @@ class FastBootTrigger : public Trigger<uint8_t, uint8_t> {
|
||||
parent->add_increment_callback([this](uint8_t current, uint8_t target) { this->trigger(current, target); });
|
||||
}
|
||||
};
|
||||
} // namespace factory_reset
|
||||
} // namespace esphome
|
||||
} // namespace esphome::factory_reset
|
||||
|
||||
#endif // !defined(USE_RP2040) && !defined(USE_HOST)
|
||||
|
||||
@@ -65,7 +65,7 @@ void FanCall::validate_() {
|
||||
auto traits = this->parent_.get_traits();
|
||||
|
||||
if (this->speed_.has_value()) {
|
||||
this->speed_ = clamp(*this->speed_, 1, static_cast<int>(traits.supported_speed_count()));
|
||||
this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count());
|
||||
|
||||
// https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes
|
||||
// "Manually setting a speed must disable any set preset mode"
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace fan {
|
||||
class FanTraits {
|
||||
public:
|
||||
FanTraits() = default;
|
||||
FanTraits(bool oscillation, bool speed, bool direction, uint8_t speed_count)
|
||||
FanTraits(bool oscillation, bool speed, bool direction, int speed_count)
|
||||
: oscillation_(oscillation), speed_(speed), direction_(direction), speed_count_(speed_count) {}
|
||||
|
||||
/// Return if this fan supports oscillation.
|
||||
@@ -23,9 +23,9 @@ class FanTraits {
|
||||
/// Set whether this fan supports speed levels.
|
||||
void set_speed(bool speed) { this->speed_ = speed; }
|
||||
/// Return how many speed levels the fan has
|
||||
uint8_t supported_speed_count() const { return this->speed_count_; }
|
||||
int supported_speed_count() const { return this->speed_count_; }
|
||||
/// Set how many speed levels this fan has.
|
||||
void set_supported_speed_count(uint8_t speed_count) { this->speed_count_ = speed_count; }
|
||||
void set_supported_speed_count(int speed_count) { this->speed_count_ = speed_count; }
|
||||
/// Return if this fan supports changing direction
|
||||
bool supports_direction() const { return this->direction_; }
|
||||
/// Set whether this fan supports changing direction
|
||||
@@ -61,7 +61,7 @@ class FanTraits {
|
||||
bool oscillation_{false};
|
||||
bool speed_{false};
|
||||
bool direction_{false};
|
||||
uint8_t speed_count_{};
|
||||
int speed_count_{};
|
||||
std::vector<const char *> preset_modes_{};
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ CONFIG_SCHEMA = (
|
||||
cv.Optional(CONF_DECAY_MODE, default="SLOW"): cv.enum(
|
||||
DECAY_MODE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1, max=255),
|
||||
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
||||
cv.Optional(CONF_ENABLE_PIN): cv.use_id(output.FloatOutput),
|
||||
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ enum DecayMode {
|
||||
|
||||
class HBridgeFan : public Component, public fan::Fan {
|
||||
public:
|
||||
HBridgeFan(uint8_t speed_count, DecayMode decay_mode) : speed_count_(speed_count), decay_mode_(decay_mode) {}
|
||||
HBridgeFan(int speed_count, DecayMode decay_mode) : speed_count_(speed_count), decay_mode_(decay_mode) {}
|
||||
|
||||
void set_pin_a(output::FloatOutput *pin_a) { pin_a_ = pin_a; }
|
||||
void set_pin_b(output::FloatOutput *pin_b) { pin_b_ = pin_b; }
|
||||
@@ -33,7 +33,7 @@ class HBridgeFan : public Component, public fan::Fan {
|
||||
output::FloatOutput *pin_b_;
|
||||
output::FloatOutput *enable_{nullptr};
|
||||
output::BinaryOutput *oscillating_{nullptr};
|
||||
uint8_t speed_count_{};
|
||||
int speed_count_{};
|
||||
DecayMode decay_mode_{DECAY_MODE_SLOW};
|
||||
fan::FanTraits traits_;
|
||||
std::vector<const char *> preset_modes_{};
|
||||
|
||||
6
esphome/components/hmac_sha256/__init__.py
Normal file
6
esphome/components/hmac_sha256/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import esphome.config_validation as cv
|
||||
|
||||
AUTO_LOAD = ["sha256"]
|
||||
CODEOWNERS = ["@dwmw2"]
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({})
|
||||
102
esphome/components/hmac_sha256/hmac_sha256.cpp
Normal file
102
esphome/components/hmac_sha256/hmac_sha256.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "hmac_sha256.h"
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_HOST)
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::hmac_sha256 {
|
||||
|
||||
constexpr size_t SHA256_DIGEST_SIZE = 32;
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
|
||||
HmacSHA256::~HmacSHA256() { mbedtls_md_free(&this->ctx_); }
|
||||
|
||||
void HmacSHA256::init(const uint8_t *key, size_t len) {
|
||||
mbedtls_md_init(&this->ctx_);
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
mbedtls_md_setup(&this->ctx_, md_info, 1); // 1 = HMAC mode
|
||||
mbedtls_md_hmac_starts(&this->ctx_, key, len);
|
||||
}
|
||||
|
||||
void HmacSHA256::add(const uint8_t *data, size_t len) { mbedtls_md_hmac_update(&this->ctx_, data, len); }
|
||||
|
||||
void HmacSHA256::calculate() { mbedtls_md_hmac_finish(&this->ctx_, this->digest_); }
|
||||
|
||||
void HmacSHA256::get_bytes(uint8_t *output) { memcpy(output, this->digest_, SHA256_DIGEST_SIZE); }
|
||||
|
||||
void HmacSHA256::get_hex(char *output) {
|
||||
for (size_t i = 0; i < SHA256_DIGEST_SIZE; i++) {
|
||||
sprintf(output + (i * 2), "%02x", this->digest_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool HmacSHA256::equals_bytes(const uint8_t *expected) {
|
||||
return memcmp(this->digest_, expected, SHA256_DIGEST_SIZE) == 0;
|
||||
}
|
||||
|
||||
bool HmacSHA256::equals_hex(const char *expected) {
|
||||
char hex_output[SHA256_DIGEST_SIZE * 2 + 1];
|
||||
this->get_hex(hex_output);
|
||||
hex_output[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||
return strncmp(hex_output, expected, SHA256_DIGEST_SIZE * 2) == 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
HmacSHA256::~HmacSHA256() = default;
|
||||
|
||||
// HMAC block size for SHA256 (RFC 2104)
|
||||
constexpr size_t HMAC_BLOCK_SIZE = 64;
|
||||
|
||||
void HmacSHA256::init(const uint8_t *key, size_t len) {
|
||||
uint8_t ipad[HMAC_BLOCK_SIZE], opad[HMAC_BLOCK_SIZE];
|
||||
|
||||
memset(ipad, 0, sizeof(ipad));
|
||||
if (len > HMAC_BLOCK_SIZE) {
|
||||
sha256::SHA256 keysha256;
|
||||
keysha256.init();
|
||||
keysha256.add(key, len);
|
||||
keysha256.calculate();
|
||||
keysha256.get_bytes(ipad);
|
||||
} else {
|
||||
memcpy(ipad, key, len);
|
||||
}
|
||||
memcpy(opad, ipad, sizeof(opad));
|
||||
|
||||
for (size_t i = 0; i < HMAC_BLOCK_SIZE; i++) {
|
||||
ipad[i] ^= 0x36;
|
||||
opad[i] ^= 0x5c;
|
||||
}
|
||||
|
||||
this->ihash_.init();
|
||||
this->ihash_.add(ipad, sizeof(ipad));
|
||||
|
||||
this->ohash_.init();
|
||||
this->ohash_.add(opad, sizeof(opad));
|
||||
}
|
||||
|
||||
void HmacSHA256::add(const uint8_t *data, size_t len) { this->ihash_.add(data, len); }
|
||||
|
||||
void HmacSHA256::calculate() {
|
||||
uint8_t ibytes[32];
|
||||
|
||||
this->ihash_.calculate();
|
||||
this->ihash_.get_bytes(ibytes);
|
||||
|
||||
this->ohash_.add(ibytes, sizeof(ibytes));
|
||||
this->ohash_.calculate();
|
||||
}
|
||||
|
||||
void HmacSHA256::get_bytes(uint8_t *output) { this->ohash_.get_bytes(output); }
|
||||
|
||||
void HmacSHA256::get_hex(char *output) { this->ohash_.get_hex(output); }
|
||||
|
||||
bool HmacSHA256::equals_bytes(const uint8_t *expected) { return this->ohash_.equals_bytes(expected); }
|
||||
|
||||
bool HmacSHA256::equals_hex(const char *expected) { return this->ohash_.equals_hex(expected); }
|
||||
|
||||
#endif // USE_ESP32 || USE_LIBRETINY
|
||||
|
||||
} // namespace esphome::hmac_sha256
|
||||
#endif
|
||||
59
esphome/components/hmac_sha256/hmac_sha256.h
Normal file
59
esphome/components/hmac_sha256/hmac_sha256.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_HOST)
|
||||
|
||||
#include <string>
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
#include "mbedtls/md.h"
|
||||
#else
|
||||
#include "esphome/components/sha256/sha256.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::hmac_sha256 {
|
||||
|
||||
class HmacSHA256 {
|
||||
public:
|
||||
HmacSHA256() = default;
|
||||
~HmacSHA256();
|
||||
|
||||
/// Initialize a new HMAC-SHA256 digest computation.
|
||||
void init(const uint8_t *key, size_t len);
|
||||
void init(const char *key, size_t len) { this->init((const uint8_t *) key, len); }
|
||||
void init(const std::string &key) { this->init(key.c_str(), key.length()); }
|
||||
|
||||
/// Add bytes of data for the digest.
|
||||
void add(const uint8_t *data, size_t len);
|
||||
void add(const char *data, size_t len) { this->add((const uint8_t *) data, len); }
|
||||
|
||||
/// Compute the digest, based on the provided data.
|
||||
void calculate();
|
||||
|
||||
/// Retrieve the HMAC-SHA256 digest as bytes.
|
||||
/// The output must be able to hold 32 bytes or more.
|
||||
void get_bytes(uint8_t *output);
|
||||
|
||||
/// Retrieve the HMAC-SHA256 digest as hex characters.
|
||||
/// The output must be able to hold 64 bytes or more.
|
||||
void get_hex(char *output);
|
||||
|
||||
/// Compare the digest against a provided byte-encoded digest (32 bytes).
|
||||
bool equals_bytes(const uint8_t *expected);
|
||||
|
||||
/// Compare the digest against a provided hex-encoded digest (64 bytes).
|
||||
bool equals_hex(const char *expected);
|
||||
|
||||
protected:
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
static constexpr size_t SHA256_DIGEST_SIZE = 32;
|
||||
mbedtls_md_context_t ctx_{};
|
||||
uint8_t digest_[SHA256_DIGEST_SIZE]{};
|
||||
#else
|
||||
sha256::SHA256 ihash_;
|
||||
sha256::SHA256 ohash_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome::hmac_sha256
|
||||
#endif
|
||||
@@ -69,9 +69,6 @@ def validate_url(value):
|
||||
def validate_ssl_verification(config):
|
||||
error_message = ""
|
||||
|
||||
if CORE.is_esp32 and not CORE.using_esp_idf and config[CONF_VERIFY_SSL]:
|
||||
error_message = "ESPHome supports certificate verification only via ESP-IDF"
|
||||
|
||||
if CORE.is_rp2040 and config[CONF_VERIFY_SSL]:
|
||||
error_message = "ESPHome does not support certificate verification on RP2040"
|
||||
|
||||
@@ -93,9 +90,9 @@ def validate_ssl_verification(config):
|
||||
def _declare_request_class(value):
|
||||
if CORE.is_host:
|
||||
return cv.declare_id(HttpRequestHost)(value)
|
||||
if CORE.using_esp_idf:
|
||||
if CORE.is_esp32:
|
||||
return cv.declare_id(HttpRequestIDF)(value)
|
||||
if CORE.is_esp8266 or CORE.is_esp32 or CORE.is_rp2040:
|
||||
if CORE.is_esp8266 or CORE.is_rp2040:
|
||||
return cv.declare_id(HttpRequestArduino)(value)
|
||||
return NotImplementedError
|
||||
|
||||
@@ -121,11 +118,11 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.positive_not_null_time_period,
|
||||
cv.positive_time_period_milliseconds,
|
||||
),
|
||||
cv.SplitDefault(CONF_BUFFER_SIZE_RX, esp32_idf=512): cv.All(
|
||||
cv.uint16_t, cv.only_with_esp_idf
|
||||
cv.SplitDefault(CONF_BUFFER_SIZE_RX, esp32=512): cv.All(
|
||||
cv.uint16_t, cv.only_on_esp32
|
||||
),
|
||||
cv.SplitDefault(CONF_BUFFER_SIZE_TX, esp32_idf=512): cv.All(
|
||||
cv.uint16_t, cv.only_with_esp_idf
|
||||
cv.SplitDefault(CONF_BUFFER_SIZE_TX, esp32=512): cv.All(
|
||||
cv.uint16_t, cv.only_on_esp32
|
||||
),
|
||||
cv.Optional(CONF_CA_CERTIFICATE_PATH): cv.All(
|
||||
cv.file_,
|
||||
@@ -158,25 +155,20 @@ async def to_code(config):
|
||||
cg.add(var.set_watchdog_timeout(timeout_ms))
|
||||
|
||||
if CORE.is_esp32:
|
||||
if CORE.using_esp_idf:
|
||||
cg.add(var.set_buffer_size_rx(config[CONF_BUFFER_SIZE_RX]))
|
||||
cg.add(var.set_buffer_size_tx(config[CONF_BUFFER_SIZE_TX]))
|
||||
cg.add(var.set_buffer_size_rx(config[CONF_BUFFER_SIZE_RX]))
|
||||
cg.add(var.set_buffer_size_tx(config[CONF_BUFFER_SIZE_TX]))
|
||||
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE",
|
||||
config.get(CONF_VERIFY_SSL),
|
||||
)
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_TLS_INSECURE",
|
||||
not config.get(CONF_VERIFY_SSL),
|
||||
)
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY",
|
||||
not config.get(CONF_VERIFY_SSL),
|
||||
)
|
||||
else:
|
||||
cg.add_library("NetworkClientSecure", None)
|
||||
cg.add_library("HTTPClient", None)
|
||||
if config.get(CONF_VERIFY_SSL):
|
||||
esp32.add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True)
|
||||
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_TLS_INSECURE",
|
||||
not config.get(CONF_VERIFY_SSL),
|
||||
)
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY",
|
||||
not config.get(CONF_VERIFY_SSL),
|
||||
)
|
||||
if CORE.is_esp8266:
|
||||
cg.add_library("ESP8266HTTPClient", None)
|
||||
if CORE.is_rp2040 and CORE.using_arduino:
|
||||
@@ -327,13 +319,15 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"http_request_host.cpp": {PlatformFramework.HOST_NATIVE},
|
||||
"http_request_arduino.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP8266_ARDUINO,
|
||||
PlatformFramework.RP2040_ARDUINO,
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
},
|
||||
"http_request_idf.cpp": {PlatformFramework.ESP32_IDF},
|
||||
"http_request_idf.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace esphome::http_request {
|
||||
|
||||
static const char *const TAG = "http_request";
|
||||
|
||||
@@ -42,5 +41,4 @@ std::string HttpContainer::get_response_header(const std::string &header_name) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
} // namespace esphome::http_request
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace esphome::http_request {
|
||||
|
||||
struct Header {
|
||||
std::string name;
|
||||
@@ -255,6 +254,9 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
|
||||
size_t read_index = 0;
|
||||
while (container->get_bytes_read() < max_length) {
|
||||
int read = container->read(buf + read_index, std::min<size_t>(max_length - read_index, 512));
|
||||
if (read <= 0) {
|
||||
break;
|
||||
}
|
||||
App.feed_wdt();
|
||||
yield();
|
||||
read_index += read;
|
||||
@@ -302,5 +304,4 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
|
||||
size_t max_response_buffer_size_{SIZE_MAX};
|
||||
};
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
} // namespace esphome::http_request
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "http_request_arduino.h"
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/components/watchdog/watchdog.h"
|
||||
@@ -9,8 +9,7 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace esphome::http_request {
|
||||
|
||||
static const char *const TAG = "http_request.arduino";
|
||||
|
||||
@@ -75,8 +74,6 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &ur
|
||||
container->client_.setInsecure();
|
||||
}
|
||||
bool status = container->client_.begin(url.c_str());
|
||||
#elif defined(USE_ESP32)
|
||||
bool status = container->client_.begin(url.c_str());
|
||||
#endif
|
||||
|
||||
App.feed_wdt();
|
||||
@@ -90,9 +87,6 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &ur
|
||||
|
||||
container->client_.setReuse(true);
|
||||
container->client_.setTimeout(this->timeout_);
|
||||
#if defined(USE_ESP32)
|
||||
container->client_.setConnectTimeout(this->timeout_);
|
||||
#endif
|
||||
|
||||
if (this->useragent_ != nullptr) {
|
||||
container->client_.setUserAgent(this->useragent_);
|
||||
@@ -177,7 +171,6 @@ void HttpContainerArduino::end() {
|
||||
this->client_.end();
|
||||
}
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
} // namespace esphome::http_request
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
#endif // USE_ARDUINO && !USE_ESP32
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
#include "http_request.h"
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_RP2040)
|
||||
#if defined(USE_RP2040)
|
||||
#include <HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
#endif
|
||||
@@ -15,8 +15,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace esphome::http_request {
|
||||
|
||||
class HttpRequestArduino;
|
||||
class HttpContainerArduino : public HttpContainer {
|
||||
@@ -36,7 +35,6 @@ class HttpRequestArduino : public HttpRequestComponent {
|
||||
const std::set<std::string> &collect_headers) override;
|
||||
};
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
} // namespace esphome::http_request
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
#endif // USE_ARDUINO && !USE_ESP32
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace esphome::http_request {
|
||||
|
||||
static const char *const TAG = "http_request.host";
|
||||
|
||||
@@ -139,7 +138,6 @@ void HttpContainerHost::end() {
|
||||
this->bytes_read_ = 0;
|
||||
}
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
} // namespace esphome::http_request
|
||||
|
||||
#endif // USE_HOST
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#ifdef USE_HOST
|
||||
#include "http_request.h"
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
namespace esphome::http_request {
|
||||
|
||||
class HttpRequestHost;
|
||||
class HttpContainerHost : public HttpContainer {
|
||||
@@ -27,7 +27,6 @@ class HttpRequestHost : public HttpRequestComponent {
|
||||
const char *ca_path_{};
|
||||
};
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
} // namespace esphome::http_request
|
||||
|
||||
#endif // USE_HOST
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "http_request_idf.h"
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/components/watchdog/watchdog.h"
|
||||
@@ -14,8 +14,7 @@
|
||||
|
||||
#include "esp_task_wdt.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace esphome::http_request {
|
||||
|
||||
static const char *const TAG = "http_request.idf";
|
||||
|
||||
@@ -245,7 +244,6 @@ void HttpContainerIDF::feed_wdt() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
} // namespace esphome::http_request
|
||||
|
||||
#endif // USE_ESP_IDF
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
|
||||
#include "http_request.h"
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_event.h>
|
||||
#include <esp_http_client.h>
|
||||
#include <esp_netif.h>
|
||||
#include <esp_tls.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace esphome::http_request {
|
||||
|
||||
class HttpContainerIDF : public HttpContainer {
|
||||
public:
|
||||
@@ -48,7 +47,6 @@ class HttpRequestIDF : public HttpRequestComponent {
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
|
||||
};
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
} // namespace esphome::http_request
|
||||
|
||||
#endif // USE_ESP_IDF
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -16,7 +16,11 @@ namespace http_request {
|
||||
|
||||
static const char *const TAG = "http_request.ota";
|
||||
|
||||
void OtaHttpRequestComponent::setup() {}
|
||||
void OtaHttpRequestComponent::setup() {
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
ota::register_ota_platform(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OtaHttpRequestComponent::dump_config() { ESP_LOGCONFIG(TAG, "Over-The-Air updates via HTTP request"); };
|
||||
|
||||
@@ -44,24 +48,24 @@ void OtaHttpRequestComponent::flash() {
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting update");
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_STARTED, 0.0f, 0);
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0);
|
||||
#endif
|
||||
|
||||
auto ota_status = this->do_ota_();
|
||||
|
||||
switch (ota_status) {
|
||||
case ota::OTA_RESPONSE_OK:
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_COMPLETED, 100.0f, ota_status);
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, ota_status);
|
||||
#endif
|
||||
delay(10);
|
||||
App.safe_reboot();
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_ERROR, 0.0f, ota_status);
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
this->state_callback_.call(ota::OTA_ERROR, 0.0f, ota_status);
|
||||
#endif
|
||||
this->md5_computed_.clear(); // will be reset at next attempt
|
||||
this->md5_expected_.clear(); // will be reset at next attempt
|
||||
@@ -128,11 +132,18 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
App.feed_wdt();
|
||||
yield();
|
||||
|
||||
if (bufsize < 0) {
|
||||
ESP_LOGE(TAG, "Stream closed");
|
||||
this->cleanup_(std::move(backend), container);
|
||||
return OTA_CONNECTION_ERROR;
|
||||
} else if (bufsize > 0 && bufsize <= OtaHttpRequestComponent::HTTP_RECV_BUFFER) {
|
||||
// Exit loop if no data available (stream closed or end of data)
|
||||
if (bufsize <= 0) {
|
||||
if (bufsize < 0) {
|
||||
ESP_LOGE(TAG, "Stream closed with error");
|
||||
this->cleanup_(std::move(backend), container);
|
||||
return OTA_CONNECTION_ERROR;
|
||||
}
|
||||
// bufsize == 0: no more data available, exit loop
|
||||
break;
|
||||
}
|
||||
|
||||
if (bufsize <= OtaHttpRequestComponent::HTTP_RECV_BUFFER) {
|
||||
// add read bytes to MD5
|
||||
md5_receive.add(buf, bufsize);
|
||||
|
||||
@@ -154,8 +165,8 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
last_progress = now;
|
||||
float percentage = container->get_bytes_read() * 100.0f / container->content_length;
|
||||
ESP_LOGD(TAG, "Progress: %0.1f%%", percentage);
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_IN_PROGRESS, percentage, 0);
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0);
|
||||
#endif
|
||||
}
|
||||
} // while
|
||||
@@ -243,6 +254,9 @@ bool OtaHttpRequestComponent::http_get_md5_() {
|
||||
int read_len = 0;
|
||||
while (container->get_bytes_read() < MD5_SIZE) {
|
||||
read_len = container->read((uint8_t *) this->md5_expected_.data(), MD5_SIZE);
|
||||
if (read_len <= 0) {
|
||||
break;
|
||||
}
|
||||
App.feed_wdt();
|
||||
yield();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import ota, update
|
||||
from esphome.components import update
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_SOURCE
|
||||
|
||||
@@ -38,6 +38,6 @@ async def to_code(config):
|
||||
|
||||
cg.add(var.set_source_url(config[CONF_SOURCE]))
|
||||
|
||||
ota.request_ota_state_listeners()
|
||||
cg.add_define("USE_OTA_STATE_CALLBACK")
|
||||
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -20,19 +20,19 @@ static const char *const TAG = "http_request.update";
|
||||
|
||||
static const size_t MAX_READ_SIZE = 256;
|
||||
|
||||
void HttpRequestUpdate::setup() { this->ota_parent_->add_state_listener(this); }
|
||||
|
||||
void HttpRequestUpdate::on_ota_state(ota::OTAState state, float progress, uint8_t error) {
|
||||
if (state == ota::OTAState::OTA_IN_PROGRESS) {
|
||||
this->state_ = update::UPDATE_STATE_INSTALLING;
|
||||
this->update_info_.has_progress = true;
|
||||
this->update_info_.progress = progress;
|
||||
this->publish_state();
|
||||
} else if (state == ota::OTAState::OTA_ABORT || state == ota::OTAState::OTA_ERROR) {
|
||||
this->state_ = update::UPDATE_STATE_AVAILABLE;
|
||||
this->status_set_error(LOG_STR("Failed to install firmware"));
|
||||
this->publish_state();
|
||||
}
|
||||
void HttpRequestUpdate::setup() {
|
||||
this->ota_parent_->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t err) {
|
||||
if (state == ota::OTAState::OTA_IN_PROGRESS) {
|
||||
this->state_ = update::UPDATE_STATE_INSTALLING;
|
||||
this->update_info_.has_progress = true;
|
||||
this->update_info_.progress = progress;
|
||||
this->publish_state();
|
||||
} else if (state == ota::OTAState::OTA_ABORT || state == ota::OTAState::OTA_ERROR) {
|
||||
this->state_ = update::UPDATE_STATE_AVAILABLE;
|
||||
this->status_set_error(LOG_STR("Failed to install firmware"));
|
||||
this->publish_state();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::update() {
|
||||
@@ -76,6 +76,11 @@ void HttpRequestUpdate::update_task(void *params) {
|
||||
|
||||
yield();
|
||||
|
||||
if (read_bytes <= 0) {
|
||||
// Network error or connection closed - break to avoid infinite loop
|
||||
break;
|
||||
}
|
||||
|
||||
read_index += read_bytes;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
class HttpRequestUpdate final : public update::UpdateEntity, public PollingComponent, public ota::OTAStateListener {
|
||||
class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
@@ -29,8 +29,6 @@ class HttpRequestUpdate final : public update::UpdateEntity, public PollingCompo
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||
|
||||
void on_ota_state(ota::OTAState state, float progress, uint8_t error) override;
|
||||
|
||||
protected:
|
||||
HttpRequestComponent *request_parent_;
|
||||
OtaHttpRequestComponent *ota_parent_;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Any
|
||||
|
||||
from esphome import pins
|
||||
from esphome import automation, pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import display
|
||||
from esphome.components.esp32 import add_idf_component
|
||||
@@ -17,6 +17,8 @@ from esphome.const import (
|
||||
CONF_OE_PIN,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -135,6 +137,7 @@ CLOCK_SPEEDS = {
|
||||
HUB75Display = hub75_ns.class_("HUB75Display", cg.PollingComponent, display.Display)
|
||||
Hub75Config = cg.global_ns.struct("Hub75Config")
|
||||
Hub75Pins = cg.global_ns.struct("Hub75Pins")
|
||||
SetBrightnessAction = hub75_ns.class_("SetBrightnessAction", automation.Action)
|
||||
|
||||
|
||||
def _merge_board_pins(config: ConfigType) -> ConfigType:
|
||||
@@ -576,3 +579,27 @@ async def to_code(config: ConfigType) -> None:
|
||||
config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"hub75.set_brightness",
|
||||
SetBrightnessAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HUB75Display),
|
||||
cv.Required(CONF_BRIGHTNESS): cv.templatable(cv.int_range(min=0, max=255)),
|
||||
},
|
||||
key=CONF_BRIGHTNESS,
|
||||
),
|
||||
)
|
||||
async def hub75_set_brightness_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
template_ = await cg.templatable(config[CONF_BRIGHTNESS], args, cg.uint8)
|
||||
cg.add(var.set_brightness(template_))
|
||||
return var
|
||||
|
||||
@@ -179,7 +179,7 @@ void HOT HUB75Display::draw_pixels_at(int x_start, int y_start, int w, int h, co
|
||||
}
|
||||
}
|
||||
|
||||
void HUB75Display::set_brightness(int brightness) {
|
||||
void HUB75Display::set_brightness(uint8_t brightness) {
|
||||
this->brightness_ = brightness;
|
||||
this->enabled_ = (brightness > 0);
|
||||
if (this->driver_ != nullptr) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -34,7 +35,7 @@ class HUB75Display : public display::Display {
|
||||
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override;
|
||||
|
||||
// Brightness control (runtime mutable)
|
||||
void set_brightness(int brightness);
|
||||
void set_brightness(uint8_t brightness);
|
||||
|
||||
protected:
|
||||
// Display internal methods
|
||||
@@ -46,10 +47,17 @@ class HUB75Display : public display::Display {
|
||||
Hub75Config config_; // Immutable configuration
|
||||
|
||||
// Runtime state (mutable)
|
||||
int brightness_{128};
|
||||
uint8_t brightness_{128};
|
||||
bool enabled_{false};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetBrightnessAction : public Action<Ts...>, public Parented<HUB75Display> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, brightness)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->set_brightness(this->brightness_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::hub75
|
||||
|
||||
#endif
|
||||
|
||||
@@ -121,7 +121,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
nrf52="100kHz",
|
||||
): cv.All(
|
||||
cv.frequency,
|
||||
cv.Range(min=0, min_included=False),
|
||||
cv.float_range(min=0, min_included=False),
|
||||
),
|
||||
cv.Optional(CONF_TIMEOUT): cv.All(
|
||||
cv.only_with_framework(["arduino", "esp-idf"]),
|
||||
|
||||
@@ -116,7 +116,7 @@ void IDFI2CBus::dump_config() {
|
||||
if (s.second) {
|
||||
ESP_LOGCONFIG(TAG, "Found device at address 0x%02X", s.first);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import (
|
||||
add_idf_sdkconfig_option,
|
||||
enable_ringbuf_in_iram,
|
||||
get_esp32_variant,
|
||||
)
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32C5,
|
||||
@@ -15,6 +10,8 @@ from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32P4,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
add_idf_sdkconfig_option,
|
||||
get_esp32_variant,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE
|
||||
@@ -280,9 +277,6 @@ async def to_code(config):
|
||||
# Helps avoid callbacks being skipped due to processor load
|
||||
add_idf_sdkconfig_option("CONFIG_I2S_ISR_IRAM_SAFE", True)
|
||||
|
||||
# Keep ring buffer functions in IRAM for audio performance
|
||||
enable_ringbuf_in_iram()
|
||||
|
||||
cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
|
||||
if CONF_I2S_BCLK_PIN in config:
|
||||
cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN]))
|
||||
|
||||
@@ -45,10 +45,12 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(LEDCOutput),
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_FREQUENCY, default="1kHz"): cv.frequency,
|
||||
cv.Optional(CONF_FREQUENCY, default="1kHz"): cv.All(
|
||||
cv.frequency, cv.float_range(min=0, min_included=False)
|
||||
),
|
||||
cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15),
|
||||
cv.Optional(CONF_PHASE_ANGLE): cv.All(
|
||||
cv.only_with_esp_idf, cv.angle, cv.float_range(min=0.0, max=360.0)
|
||||
cv.angle, cv.float_range(min=0.0, max=360.0)
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
@@ -4,24 +4,27 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include <flashdb.h>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace esphome {
|
||||
namespace libretiny {
|
||||
|
||||
static const char *const TAG = "lt.preferences";
|
||||
|
||||
// Buffer size for converting uint32_t to string: max "4294967295" (10 chars) + null terminator + 1 padding
|
||||
static constexpr size_t KEY_BUFFER_SIZE = 12;
|
||||
|
||||
struct NVSData {
|
||||
std::string key;
|
||||
uint32_t key;
|
||||
std::unique_ptr<uint8_t[]> data;
|
||||
size_t len;
|
||||
|
||||
void set_data(const uint8_t *src, size_t size) {
|
||||
data = std::make_unique<uint8_t[]>(size);
|
||||
memcpy(data.get(), src, size);
|
||||
len = size;
|
||||
this->data = std::make_unique<uint8_t[]>(size);
|
||||
memcpy(this->data.get(), src, size);
|
||||
this->len = size;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,30 +32,30 @@ static std::vector<NVSData> s_pending_save; // NOLINT(cppcoreguidelines-avoid-n
|
||||
|
||||
class LibreTinyPreferenceBackend : public ESPPreferenceBackend {
|
||||
public:
|
||||
std::string key;
|
||||
uint32_t key;
|
||||
fdb_kvdb_t db;
|
||||
fdb_blob_t blob;
|
||||
|
||||
bool save(const uint8_t *data, size_t len) override {
|
||||
// try find in pending saves and update that
|
||||
for (auto &obj : s_pending_save) {
|
||||
if (obj.key == key) {
|
||||
if (obj.key == this->key) {
|
||||
obj.set_data(data, len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
NVSData save{};
|
||||
save.key = key;
|
||||
save.key = this->key;
|
||||
save.set_data(data, len);
|
||||
s_pending_save.emplace_back(std::move(save));
|
||||
ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %zu", key.c_str(), len);
|
||||
ESP_LOGVV(TAG, "s_pending_save: key: %" PRIu32 ", len: %zu", this->key, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load(uint8_t *data, size_t len) override {
|
||||
// try find in pending saves and load from that
|
||||
for (auto &obj : s_pending_save) {
|
||||
if (obj.key == key) {
|
||||
if (obj.key == this->key) {
|
||||
if (obj.len != len) {
|
||||
// size mismatch
|
||||
return false;
|
||||
@@ -62,13 +65,15 @@ class LibreTinyPreferenceBackend : public ESPPreferenceBackend {
|
||||
}
|
||||
}
|
||||
|
||||
fdb_blob_make(blob, data, len);
|
||||
size_t actual_len = fdb_kv_get_blob(db, key.c_str(), blob);
|
||||
char key_str[KEY_BUFFER_SIZE];
|
||||
snprintf(key_str, sizeof(key_str), "%" PRIu32, this->key);
|
||||
fdb_blob_make(this->blob, data, len);
|
||||
size_t actual_len = fdb_kv_get_blob(this->db, key_str, this->blob);
|
||||
if (actual_len != len) {
|
||||
ESP_LOGVV(TAG, "NVS length does not match (%zu!=%zu)", actual_len, len);
|
||||
return false;
|
||||
} else {
|
||||
ESP_LOGVV(TAG, "fdb_kv_get_blob: key: %s, len: %zu", key.c_str(), len);
|
||||
ESP_LOGVV(TAG, "fdb_kv_get_blob: key: %s, len: %zu", key_str, len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -90,16 +95,14 @@ class LibreTinyPreferences : public ESPPreferences {
|
||||
}
|
||||
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
|
||||
return make_preference(length, type);
|
||||
return this->make_preference(length, type);
|
||||
}
|
||||
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
|
||||
auto *pref = new LibreTinyPreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
pref->db = &db;
|
||||
pref->blob = &blob;
|
||||
|
||||
uint32_t keyval = type;
|
||||
pref->key = str_sprintf("%u", keyval);
|
||||
pref->db = &this->db;
|
||||
pref->blob = &this->blob;
|
||||
pref->key = type;
|
||||
|
||||
return ESPPreferenceObject(pref);
|
||||
}
|
||||
@@ -112,18 +115,20 @@ class LibreTinyPreferences : public ESPPreferences {
|
||||
// goal try write all pending saves even if one fails
|
||||
int cached = 0, written = 0, failed = 0;
|
||||
fdb_err_t last_err = FDB_NO_ERR;
|
||||
std::string last_key{};
|
||||
uint32_t last_key = 0;
|
||||
|
||||
// go through vector from back to front (makes erase easier/more efficient)
|
||||
for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) {
|
||||
const auto &save = s_pending_save[i];
|
||||
ESP_LOGVV(TAG, "Checking if FDB data %s has changed", save.key.c_str());
|
||||
if (is_changed(&db, save)) {
|
||||
ESP_LOGV(TAG, "sync: key: %s, len: %zu", save.key.c_str(), save.len);
|
||||
fdb_blob_make(&blob, save.data.get(), save.len);
|
||||
fdb_err_t err = fdb_kv_set_blob(&db, save.key.c_str(), &blob);
|
||||
ESP_LOGVV(TAG, "Checking if FDB data %" PRIu32 " has changed", save.key);
|
||||
if (this->is_changed(&this->db, save)) {
|
||||
char key_str[KEY_BUFFER_SIZE];
|
||||
snprintf(key_str, sizeof(key_str), "%" PRIu32, save.key);
|
||||
ESP_LOGV(TAG, "sync: key: %s, len: %zu", key_str, save.len);
|
||||
fdb_blob_make(&this->blob, save.data.get(), save.len);
|
||||
fdb_err_t err = fdb_kv_set_blob(&this->db, key_str, &this->blob);
|
||||
if (err != FDB_NO_ERR) {
|
||||
ESP_LOGV(TAG, "fdb_kv_set_blob('%s', len=%zu) failed: %d", save.key.c_str(), save.len, err);
|
||||
ESP_LOGV(TAG, "fdb_kv_set_blob('%s', len=%zu) failed: %d", key_str, save.len, err);
|
||||
failed++;
|
||||
last_err = err;
|
||||
last_key = save.key;
|
||||
@@ -131,7 +136,7 @@ class LibreTinyPreferences : public ESPPreferences {
|
||||
}
|
||||
written++;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "FDB data not changed; skipping %s len=%zu", save.key.c_str(), save.len);
|
||||
ESP_LOGD(TAG, "FDB data not changed; skipping %" PRIu32 " len=%zu", save.key, save.len);
|
||||
cached++;
|
||||
}
|
||||
s_pending_save.erase(s_pending_save.begin() + i);
|
||||
@@ -139,17 +144,20 @@ class LibreTinyPreferences : public ESPPreferences {
|
||||
ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
|
||||
failed);
|
||||
if (failed > 0) {
|
||||
ESP_LOGE(TAG, "Writing %d items failed. Last error=%d for key=%s", failed, last_err, last_key.c_str());
|
||||
ESP_LOGE(TAG, "Writing %d items failed. Last error=%d for key=%" PRIu32, failed, last_err, last_key);
|
||||
}
|
||||
|
||||
return failed == 0;
|
||||
}
|
||||
|
||||
bool is_changed(const fdb_kvdb_t db, const NVSData &to_save) {
|
||||
char key_str[KEY_BUFFER_SIZE];
|
||||
snprintf(key_str, sizeof(key_str), "%" PRIu32, to_save.key);
|
||||
|
||||
struct fdb_kv kv;
|
||||
fdb_kv_t kvp = fdb_kv_get_obj(db, to_save.key.c_str(), &kv);
|
||||
fdb_kv_t kvp = fdb_kv_get_obj(db, key_str, &kv);
|
||||
if (kvp == nullptr) {
|
||||
ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", to_save.key.c_str());
|
||||
ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", key_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -160,10 +168,10 @@ class LibreTinyPreferences : public ESPPreferences {
|
||||
|
||||
// Allocate buffer on heap to avoid stack allocation for large data
|
||||
auto stored_data = std::make_unique<uint8_t[]>(kv.value_len);
|
||||
fdb_blob_make(&blob, stored_data.get(), kv.value_len);
|
||||
size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &blob);
|
||||
fdb_blob_make(&this->blob, stored_data.get(), kv.value_len);
|
||||
size_t actual_len = fdb_kv_get_blob(db, key_str, &this->blob);
|
||||
if (actual_len != kv.value_len) {
|
||||
ESP_LOGV(TAG, "fdb_kv_get_blob('%s') len mismatch: %u != %u", to_save.key.c_str(), actual_len, kv.value_len);
|
||||
ESP_LOGV(TAG, "fdb_kv_get_blob('%s') len mismatch: %u != %u", key_str, actual_len, kv.value_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(LibreTinyPWM),
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_FREQUENCY, default="1kHz"): cv.frequency,
|
||||
cv.Optional(CONF_FREQUENCY, default="1kHz"): cv.All(
|
||||
cv.frequency, cv.float_range(min=0, min_included=False)
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
@@ -241,9 +241,12 @@ CONFIG_SCHEMA = cv.All(
|
||||
CONF_HARDWARE_UART,
|
||||
esp8266=UART0,
|
||||
esp32=UART0,
|
||||
esp32_c2=UART0,
|
||||
esp32_c3=USB_SERIAL_JTAG,
|
||||
esp32_c5=USB_SERIAL_JTAG,
|
||||
esp32_c6=USB_SERIAL_JTAG,
|
||||
esp32_c61=USB_SERIAL_JTAG,
|
||||
esp32_h2=USB_SERIAL_JTAG,
|
||||
esp32_p4=USB_SERIAL_JTAG,
|
||||
esp32_s2=USB_CDC,
|
||||
esp32_s3=USB_SERIAL_JTAG,
|
||||
|
||||
@@ -56,7 +56,7 @@ void MCP23016::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
this->update_reg_(pin, false, iodir);
|
||||
}
|
||||
}
|
||||
float MCP23016::get_setup_priority() const { return setup_priority::IO; }
|
||||
float MCP23016::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
bool MCP23016::read_reg_(uint8_t reg, uint8_t *value) {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.core import CORE
|
||||
from esphome.helpers import IS_MACOS
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_MD5")
|
||||
|
||||
# Add OpenSSL library for host platform
|
||||
if CORE.is_host:
|
||||
if IS_MACOS:
|
||||
# macOS needs special handling for Homebrew OpenSSL
|
||||
cg.add_build_flag("-I/opt/homebrew/opt/openssl/include")
|
||||
cg.add_build_flag("-L/opt/homebrew/opt/openssl/lib")
|
||||
cg.add_build_flag("-lcrypto")
|
||||
|
||||
@@ -39,6 +39,44 @@ void MD5Digest::add(const uint8_t *data, size_t len) { br_md5_update(&this->ctx_
|
||||
void MD5Digest::calculate() { br_md5_out(&this->ctx_, this->digest_); }
|
||||
#endif // USE_RP2040
|
||||
|
||||
#ifdef USE_HOST
|
||||
MD5Digest::~MD5Digest() {
|
||||
if (this->ctx_) {
|
||||
EVP_MD_CTX_free(this->ctx_);
|
||||
}
|
||||
}
|
||||
|
||||
void MD5Digest::init() {
|
||||
if (this->ctx_) {
|
||||
EVP_MD_CTX_free(this->ctx_);
|
||||
}
|
||||
this->ctx_ = EVP_MD_CTX_new();
|
||||
EVP_DigestInit_ex(this->ctx_, EVP_md5(), nullptr);
|
||||
this->calculated_ = false;
|
||||
memset(this->digest_, 0, 16);
|
||||
}
|
||||
|
||||
void MD5Digest::add(const uint8_t *data, size_t len) {
|
||||
if (!this->ctx_) {
|
||||
this->init();
|
||||
}
|
||||
EVP_DigestUpdate(this->ctx_, data, len);
|
||||
}
|
||||
|
||||
void MD5Digest::calculate() {
|
||||
if (!this->ctx_) {
|
||||
this->init();
|
||||
}
|
||||
if (!this->calculated_) {
|
||||
unsigned int len = 16;
|
||||
EVP_DigestFinal_ex(this->ctx_, this->digest_, &len);
|
||||
this->calculated_ = true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
MD5Digest::~MD5Digest() = default;
|
||||
#endif // USE_HOST
|
||||
|
||||
} // namespace md5
|
||||
} // namespace esphome
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#include "esphome/core/hash_base.h"
|
||||
|
||||
#ifdef USE_HOST
|
||||
#include <openssl/evp.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include "esp_rom_md5.h"
|
||||
#define MD5_CTX_TYPE md5_context_t
|
||||
@@ -31,7 +35,7 @@ namespace md5 {
|
||||
class MD5Digest : public HashBase {
|
||||
public:
|
||||
MD5Digest() = default;
|
||||
~MD5Digest() override = default;
|
||||
~MD5Digest() override;
|
||||
|
||||
/// Initialize a new MD5 digest computation.
|
||||
void init() override;
|
||||
@@ -47,7 +51,12 @@ class MD5Digest : public HashBase {
|
||||
size_t get_size() const override { return 16; }
|
||||
|
||||
protected:
|
||||
#ifdef USE_HOST
|
||||
EVP_MD_CTX *ctx_{nullptr};
|
||||
bool calculated_{false};
|
||||
#else
|
||||
MD5_CTX_TYPE ctx_{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace md5
|
||||
|
||||
@@ -7,7 +7,7 @@ from urllib.parse import urljoin
|
||||
from esphome import automation, external_files, git
|
||||
from esphome.automation import register_action, register_condition
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32, microphone, ota, socket
|
||||
from esphome.components import esp32, microphone, socket
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_FILE,
|
||||
@@ -448,14 +448,11 @@ async def to_code(config):
|
||||
# The inference task queues detection events that need immediate processing
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
# Keep ring buffer functions in IRAM for audio performance
|
||||
esp32.enable_ringbuf_in_iram()
|
||||
|
||||
mic_source = await microphone.microphone_source_to_code(config[CONF_MICROPHONE])
|
||||
cg.add(var.set_microphone_source(mic_source))
|
||||
|
||||
cg.add_define("USE_MICRO_WAKE_WORD")
|
||||
ota.request_ota_state_listeners()
|
||||
cg.add_define("USE_OTA_STATE_CALLBACK")
|
||||
|
||||
esp32.add_idf_component(name="espressif/esp-tflite-micro", ref="1.3.3~1")
|
||||
|
||||
|
||||
@@ -119,21 +119,18 @@ void MicroWakeWord::setup() {
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
ota::get_global_ota_callback()->add_global_state_listener(this);
|
||||
#ifdef USE_OTA
|
||||
ota::get_global_ota_callback()->add_on_state_callback(
|
||||
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
|
||||
if (state == ota::OTA_STARTED) {
|
||||
this->suspend_task_();
|
||||
} else if (state == ota::OTA_ERROR) {
|
||||
this->resume_task_();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
void MicroWakeWord::on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
|
||||
if (state == ota::OTA_STARTED) {
|
||||
this->suspend_task_();
|
||||
} else if (state == ota::OTA_ERROR) {
|
||||
this->resume_task_();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void MicroWakeWord::inference_task(void *params) {
|
||||
MicroWakeWord *this_mww = (MicroWakeWord *) params;
|
||||
|
||||
|
||||
@@ -9,13 +9,8 @@
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#endif
|
||||
|
||||
#include <freertos/event_groups.h>
|
||||
|
||||
#include <frontend.h>
|
||||
@@ -31,22 +26,13 @@ enum State {
|
||||
STOPPED,
|
||||
};
|
||||
|
||||
class MicroWakeWord : public Component
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
,
|
||||
public ota::OTAGlobalStateListener
|
||||
#endif
|
||||
{
|
||||
class MicroWakeWord : public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
float get_setup_priority() const override;
|
||||
void dump_config() override;
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
void on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) override;
|
||||
#endif
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
|
||||
@@ -233,11 +233,11 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_PASSWORD, default=""): cv.string,
|
||||
cv.Optional(CONF_CLEAN_SESSION, default=False): cv.boolean,
|
||||
cv.Optional(CONF_CLIENT_ID): cv.string,
|
||||
cv.SplitDefault(CONF_IDF_SEND_ASYNC, esp32_idf=False): cv.All(
|
||||
cv.boolean, cv.only_with_esp_idf
|
||||
cv.SplitDefault(CONF_IDF_SEND_ASYNC, esp32=False): cv.All(
|
||||
cv.boolean, cv.only_on_esp32
|
||||
),
|
||||
cv.Optional(CONF_CERTIFICATE_AUTHORITY): cv.All(
|
||||
cv.string, cv.only_with_esp_idf
|
||||
cv.string, cv.only_on_esp32
|
||||
),
|
||||
cv.Inclusive(CONF_CLIENT_CERTIFICATE, "cert-key-pair"): cv.All(
|
||||
cv.string, cv.only_on_esp32
|
||||
@@ -245,8 +245,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Inclusive(CONF_CLIENT_CERTIFICATE_KEY, "cert-key-pair"): cv.All(
|
||||
cv.string, cv.only_on_esp32
|
||||
),
|
||||
cv.SplitDefault(CONF_SKIP_CERT_CN_CHECK, esp32_idf=False): cv.All(
|
||||
cv.boolean, cv.only_with_esp_idf
|
||||
cv.SplitDefault(CONF_SKIP_CERT_CN_CHECK, esp32=False): cv.All(
|
||||
cv.boolean, cv.only_on_esp32
|
||||
),
|
||||
cv.Optional(CONF_DISCOVERY, default=True): cv.Any(
|
||||
cv.boolean, cv.one_of("CLEAN", upper=True)
|
||||
|
||||
@@ -21,8 +21,7 @@ void MQTTSelectComponent::setup() {
|
||||
call.set_option(state);
|
||||
call.perform();
|
||||
});
|
||||
this->select_->add_on_state_callback(
|
||||
[this](const std::string &state, size_t index) { this->publish_state(this->select_->option_at(index)); });
|
||||
this->select_->add_on_state_callback([this](size_t index) { this->publish_state(this->select_->option_at(index)); });
|
||||
}
|
||||
|
||||
void MQTTSelectComponent::dump_config() {
|
||||
|
||||
@@ -13,14 +13,16 @@ CONF_SEND_TO_NEXTION = "send_to_nextion"
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"nextion_upload_arduino.cpp": {
|
||||
"nextion_upload_esp32.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
"nextion_upload_arduino.cpp": {
|
||||
PlatformFramework.ESP8266_ARDUINO,
|
||||
PlatformFramework.RP2040_ARDUINO,
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
},
|
||||
"nextion_upload_idf.cpp": {PlatformFramework.ESP32_IDF},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -154,14 +154,11 @@ async def to_code(config):
|
||||
cg.add_define("USE_NEXTION_TFT_UPLOAD")
|
||||
cg.add(var.set_tft_url(config[CONF_TFT_URL]))
|
||||
if CORE.is_esp32:
|
||||
if CORE.using_arduino:
|
||||
cg.add_library("NetworkClientSecure", None)
|
||||
cg.add_library("HTTPClient", None)
|
||||
esp32.add_idf_sdkconfig_option("CONFIG_ESP_TLS_INSECURE", True)
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY", True
|
||||
)
|
||||
elif CORE.is_esp8266 and CORE.using_arduino:
|
||||
elif CORE.is_esp8266:
|
||||
cg.add_library("ESP8266HTTPClient", None)
|
||||
|
||||
if CONF_TOUCH_SLEEP_TIMEOUT in config:
|
||||
|
||||
@@ -13,17 +13,12 @@
|
||||
#include "esphome/components/display/display_color_utils.h"
|
||||
|
||||
#ifdef USE_NEXTION_TFT_UPLOAD
|
||||
#ifdef USE_ARDUINO
|
||||
#ifdef USE_ESP32
|
||||
#include <HTTPClient.h>
|
||||
#endif // USE_ESP32
|
||||
#ifdef USE_ESP8266
|
||||
#include <esp_http_client.h>
|
||||
#elif defined(USE_ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#endif // USE_ESP8266
|
||||
#elif defined(USE_ESP_IDF)
|
||||
#include <esp_http_client.h>
|
||||
#endif // ARDUINO vs USE_ESP_IDF
|
||||
#endif // USE_ESP32 vs USE_ESP8266
|
||||
#endif // USE_NEXTION_TFT_UPLOAD
|
||||
|
||||
namespace esphome {
|
||||
@@ -1078,7 +1073,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||
|
||||
#ifdef USE_NEXTION_TFT_UPLOAD
|
||||
/**
|
||||
* Set the tft file URL. https seems problematic with Arduino..
|
||||
* Set the tft file URL.
|
||||
*/
|
||||
void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; }
|
||||
|
||||
@@ -1422,16 +1417,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||
uint32_t original_baud_rate_ = 0;
|
||||
bool upload_first_chunk_sent_ = false;
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
/**
|
||||
* will request chunk_size chunks from the web server
|
||||
* and send each to the nextion
|
||||
* @param HTTPClient http_client HTTP client handler.
|
||||
* @param int range_start Position of next byte to transfer.
|
||||
* @return position of last byte transferred, -1 for failure.
|
||||
*/
|
||||
int upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start);
|
||||
#elif defined(USE_ESP_IDF)
|
||||
#ifdef USE_ESP32
|
||||
/**
|
||||
* will request 4096 bytes chunks from the web server
|
||||
* and send each to Nextion
|
||||
@@ -1440,7 +1426,16 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||
* @return position of last byte transferred, -1 for failure.
|
||||
*/
|
||||
int upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &range_start);
|
||||
#endif // USE_ARDUINO vs USE_ESP_IDF
|
||||
#elif defined(USE_ARDUINO)
|
||||
/**
|
||||
* will request chunk_size chunks from the web server
|
||||
* and send each to the nextion
|
||||
* @param HTTPClient http_client HTTP client handler.
|
||||
* @param int range_start Position of next byte to transfer.
|
||||
* @return position of last byte transferred, -1 for failure.
|
||||
*/
|
||||
int upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start);
|
||||
#endif // USE_ESP32 vs USE_ARDUINO
|
||||
|
||||
/**
|
||||
* Ends the upload process, restart Nextion and, if successful,
|
||||
@@ -1450,12 +1445,6 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||
*/
|
||||
bool upload_end_(bool successful);
|
||||
|
||||
/**
|
||||
* Returns the ESP Free Heap memory. This is framework independent.
|
||||
* @return Free Heap in bytes.
|
||||
*/
|
||||
uint32_t get_free_heap_();
|
||||
|
||||
#endif // USE_NEXTION_TFT_UPLOAD
|
||||
|
||||
bool check_connect_();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "nextion.h"
|
||||
|
||||
#ifdef USE_NEXTION_TFT_UPLOAD
|
||||
#ifdef USE_ARDUINO
|
||||
#ifndef USE_ESP32
|
||||
|
||||
#include <cinttypes>
|
||||
#include "esphome/components/network/util.h"
|
||||
@@ -10,10 +10,6 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_heap_caps.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
static const char *const TAG = "nextion.upload.arduino";
|
||||
@@ -21,23 +17,17 @@ static const char *const TAG = "nextion.upload.arduino";
|
||||
// Followed guide
|
||||
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
|
||||
|
||||
inline uint32_t Nextion::get_free_heap_() {
|
||||
#if defined(USE_ESP32)
|
||||
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
#elif defined(USE_ESP8266)
|
||||
return EspClass::getFreeHeap();
|
||||
#endif // USE_ESP32 vs USE_ESP8266
|
||||
}
|
||||
|
||||
int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||
uint32_t range_size = this->tft_size_ - range_start;
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_());
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, EspClass::getFreeHeap());
|
||||
uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1;
|
||||
ESP_LOGD(TAG, "Range start: %" PRIu32, range_start);
|
||||
if (range_size <= 0 or range_end <= range_start) {
|
||||
ESP_LOGD(TAG, "Range end: %" PRIu32, range_end);
|
||||
ESP_LOGD(TAG, "Range size: %" PRIu32, range_size);
|
||||
ESP_LOGE(TAG, "Invalid range");
|
||||
ESP_LOGD(TAG,
|
||||
"Range end: %" PRIu32 "\n"
|
||||
"Range size: %" PRIu32,
|
||||
range_end, range_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -95,14 +85,8 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||
this->recv_ret_string_(recv_string, upload_first_chunk_sent_ ? 500 : 5000, true);
|
||||
this->content_length_ -= read_len;
|
||||
const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_;
|
||||
#if defined(USE_ESP32) && defined(USE_PSRAM)
|
||||
ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 "+%" PRIu32 ")", upload_percentage,
|
||||
this->content_length_, static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)),
|
||||
static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)));
|
||||
#else
|
||||
ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 ")", upload_percentage, this->content_length_,
|
||||
this->get_free_heap_());
|
||||
#endif
|
||||
EspClass::getFreeHeap());
|
||||
upload_first_chunk_sent_ = true;
|
||||
if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request
|
||||
ESP_LOGD(TAG, "Recv: [%s]",
|
||||
@@ -148,9 +132,11 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||
}
|
||||
|
||||
bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
ESP_LOGD(TAG, "TFT upload requested");
|
||||
ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse));
|
||||
ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str());
|
||||
ESP_LOGD(TAG,
|
||||
"TFT upload requested\n"
|
||||
"Exit reparse: %s\n"
|
||||
"URL: %s",
|
||||
YESNO(exit_reparse), this->tft_url_.c_str());
|
||||
|
||||
if (this->connection_state_.is_updating_) {
|
||||
ESP_LOGW(TAG, "Upload in progress");
|
||||
@@ -180,15 +166,14 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate);
|
||||
|
||||
// Define the configuration for the HTTP client
|
||||
ESP_LOGV(TAG, "Init HTTP client");
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_());
|
||||
ESP_LOGV(TAG,
|
||||
"Init HTTP client\n"
|
||||
"Heap: %" PRIu32,
|
||||
EspClass::getFreeHeap());
|
||||
HTTPClient http_client;
|
||||
http_client.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along
|
||||
|
||||
bool begin_status = false;
|
||||
#ifdef USE_ESP32
|
||||
begin_status = http_client.begin(this->tft_url_.c_str());
|
||||
#endif
|
||||
#ifdef USE_ESP8266
|
||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
|
||||
http_client.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
@@ -256,22 +241,24 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
this->send_command_("sleep=0");
|
||||
this->send_command_("dim=100");
|
||||
delay(250); // NOLINT
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_());
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, EspClass::getFreeHeap());
|
||||
|
||||
App.feed_wdt();
|
||||
char command[128];
|
||||
// Tells the Nextion the content length of the tft file and baud rate it will be sent at
|
||||
// Once the Nextion accepts the command it will wait until the file is successfully uploaded
|
||||
// If it fails for any reason a power cycle of the display will be needed
|
||||
sprintf(command, "whmi-wris %d,%d,1", this->content_length_, baud_rate);
|
||||
snprintf(command, sizeof(command), "whmi-wris %" PRIu32 ",%" PRIu32 ",1", this->content_length_, baud_rate);
|
||||
|
||||
// Clear serial receive buffer
|
||||
ESP_LOGV(TAG, "Clear RX buffer");
|
||||
this->reset_(false);
|
||||
delay(250); // NOLINT
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_());
|
||||
|
||||
ESP_LOGV(TAG, "Upload cmd: %s", command);
|
||||
ESP_LOGV(TAG,
|
||||
"Heap: %" PRIu32 "\n"
|
||||
"Upload cmd: %s",
|
||||
EspClass::getFreeHeap(), command);
|
||||
this->send_command_(command);
|
||||
|
||||
if (baud_rate != this->original_baud_rate_) {
|
||||
@@ -290,7 +277,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
ESP_LOGD(TAG, "Upload resp: [%s] %zu B",
|
||||
format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str(),
|
||||
response.length());
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_());
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, EspClass::getFreeHeap());
|
||||
|
||||
if (response.find(0x05) != std::string::npos) {
|
||||
ESP_LOGV(TAG, "Upload prep done");
|
||||
@@ -302,10 +289,12 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
return this->upload_end_(false);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Upload TFT:");
|
||||
ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str());
|
||||
ESP_LOGD(TAG, " Size: %d bytes", this->content_length_);
|
||||
ESP_LOGD(TAG, " Heap: %" PRIu32, this->get_free_heap_());
|
||||
ESP_LOGD(TAG,
|
||||
"Upload TFT:\n"
|
||||
" URL: %s\n"
|
||||
" Size: %d bytes\n"
|
||||
" Heap: %" PRIu32,
|
||||
this->tft_url_.c_str(), this->content_length_, EspClass::getFreeHeap());
|
||||
|
||||
// Proceed with the content download as before
|
||||
|
||||
@@ -322,7 +311,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
return this->upload_end_(false);
|
||||
}
|
||||
App.feed_wdt();
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32 " left: %" PRIu32, this->get_free_heap_(), this->content_length_);
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32 " left: %" PRIu32, EspClass::getFreeHeap(), this->content_length_);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Upload complete");
|
||||
@@ -356,5 +345,5 @@ WiFiClient *Nextion::get_wifi_client_() {
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
#endif // NOT USE_ESP32
|
||||
#endif // USE_NEXTION_TFT_UPLOAD
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "nextion.h"
|
||||
|
||||
#ifdef USE_NEXTION_TFT_UPLOAD
|
||||
#ifdef USE_ESP_IDF
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_http_client.h>
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
static const char *const TAG = "nextion.upload.idf";
|
||||
static const char *const TAG = "nextion.upload.esp32";
|
||||
|
||||
// Followed guide
|
||||
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
|
||||
@@ -25,8 +25,10 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r
|
||||
uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1;
|
||||
ESP_LOGD(TAG, "Range start: %" PRIu32, range_start);
|
||||
if (range_size <= 0 or range_end <= range_start) {
|
||||
ESP_LOGD(TAG, "Range end: %" PRIu32, range_end);
|
||||
ESP_LOGD(TAG, "Range size: %" PRIu32, range_size);
|
||||
ESP_LOGD(TAG,
|
||||
"Range end: %" PRIu32 "\n"
|
||||
"Range size: %" PRIu32,
|
||||
range_end, range_size);
|
||||
ESP_LOGE(TAG, "Invalid range");
|
||||
return -1;
|
||||
}
|
||||
@@ -151,9 +153,11 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r
|
||||
}
|
||||
|
||||
bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
ESP_LOGD(TAG, "TFT upload requested");
|
||||
ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse));
|
||||
ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str());
|
||||
ESP_LOGD(TAG,
|
||||
"TFT upload requested\n"
|
||||
"Exit reparse: %s\n"
|
||||
"URL: %s",
|
||||
YESNO(exit_reparse), this->tft_url_.c_str());
|
||||
|
||||
if (this->connection_state_.is_updating_) {
|
||||
ESP_LOGW(TAG, "Upload in progress");
|
||||
@@ -183,8 +187,10 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate);
|
||||
|
||||
// Define the configuration for the HTTP client
|
||||
ESP_LOGV(TAG, "Init HTTP client");
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size());
|
||||
ESP_LOGV(TAG,
|
||||
"Init HTTP client\n"
|
||||
"Heap: %" PRIu32,
|
||||
esp_get_free_heap_size());
|
||||
esp_http_client_config_t config = {
|
||||
.url = this->tft_url_.c_str(),
|
||||
.cert_pem = nullptr,
|
||||
@@ -208,8 +214,10 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
}
|
||||
|
||||
// Perform the HTTP request
|
||||
ESP_LOGV(TAG, "Check connection");
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size());
|
||||
ESP_LOGV(TAG,
|
||||
"Check connection\n"
|
||||
"Heap: %" PRIu32,
|
||||
esp_get_free_heap_size());
|
||||
err = esp_http_client_perform(http_client);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "HTTP failed: %s", esp_err_to_name(err));
|
||||
@@ -218,8 +226,10 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
}
|
||||
|
||||
// Check the HTTP Status Code
|
||||
ESP_LOGV(TAG, "Check status");
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size());
|
||||
ESP_LOGV(TAG,
|
||||
"Check status\n"
|
||||
"Heap: %" PRIu32,
|
||||
esp_get_free_heap_size());
|
||||
int status_code = esp_http_client_get_status_code(http_client);
|
||||
if (status_code != 200 && status_code != 206) {
|
||||
return this->upload_end_(false);
|
||||
@@ -255,7 +265,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
// Tells the Nextion the content length of the tft file and baud rate it will be sent at
|
||||
// Once the Nextion accepts the command it will wait until the file is successfully uploaded
|
||||
// If it fails for any reason a power cycle of the display will be needed
|
||||
sprintf(command, "whmi-wris %" PRIu32 ",%" PRIu32 ",1", this->content_length_, baud_rate);
|
||||
snprintf(command, sizeof(command), "whmi-wris %" PRIu32 ",%" PRIu32 ",1", this->content_length_, baud_rate);
|
||||
|
||||
// Clear serial receive buffer
|
||||
ESP_LOGV(TAG, "Clear RX buffer");
|
||||
@@ -300,10 +310,12 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
return this->upload_end_(false);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Uploading TFT:");
|
||||
ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str());
|
||||
ESP_LOGD(TAG, " Size: %" PRIu32 " bytes", this->content_length_);
|
||||
ESP_LOGD(TAG, " Heap: %" PRIu32, esp_get_free_heap_size());
|
||||
ESP_LOGD(TAG,
|
||||
"Uploading TFT:\n"
|
||||
" URL: %s\n"
|
||||
" Size: %" PRIu32 " bytes\n"
|
||||
" Heap: %" PRIu32,
|
||||
this->tft_url_.c_str(), this->content_length_, esp_get_free_heap_size());
|
||||
|
||||
// Proceed with the content download as before
|
||||
|
||||
@@ -324,9 +336,8 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32 " left: %" PRIu32, esp_get_free_heap_size(), this->content_length_);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "TFT upload complete");
|
||||
|
||||
ESP_LOGD(TAG, "Close HTTP");
|
||||
ESP_LOGD(TAG, "TFT upload complete\n"
|
||||
"Close HTTP");
|
||||
esp_http_client_close(http_client);
|
||||
esp_http_client_cleanup(http_client);
|
||||
ESP_LOGV(TAG, "Connection closed");
|
||||
@@ -336,5 +347,5 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP_IDF
|
||||
#endif // USE_ESP32
|
||||
#endif // USE_NEXTION_TFT_UPLOAD
|
||||
@@ -91,7 +91,7 @@ def set_sdkconfig_options(config):
|
||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT", True)
|
||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT_MAX_SERVICES", 5)
|
||||
|
||||
# TODO: Add suport for synchronized sleepy end devices (SSED)
|
||||
# TODO: Add support for synchronized sleepy end devices (SSED)
|
||||
add_idf_sdkconfig_option(f"CONFIG_OPENTHREAD_{config.get(CONF_DEVICE_TYPE)}", True)
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ OpenThreadSrpComponent = openthread_ns.class_("OpenThreadSrpComponent", cg.Compo
|
||||
_CONNECTION_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_PAN_ID): cv.hex_int,
|
||||
cv.Optional(CONF_CHANNEL): cv.int_,
|
||||
cv.Optional(CONF_CHANNEL): cv.int_range(min=11, max=26),
|
||||
cv.Optional(CONF_NETWORK_KEY): cv.hex_int,
|
||||
cv.Optional(CONF_EXT_PAN_ID): cv.hex_int,
|
||||
cv.Optional(CONF_NETWORK_NAME): cv.string_strict,
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
static const char *const TAG = "openthread";
|
||||
|
||||
namespace esphome {
|
||||
namespace openthread {
|
||||
namespace esphome::openthread {
|
||||
|
||||
OpenThreadComponent *global_openthread_component = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -275,7 +274,5 @@ const char *OpenThreadComponent::get_use_address() const { return this->use_addr
|
||||
|
||||
void OpenThreadComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; }
|
||||
|
||||
} // namespace openthread
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::openthread
|
||||
#endif
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace openthread {
|
||||
namespace esphome::openthread {
|
||||
|
||||
class InstanceLock;
|
||||
|
||||
@@ -91,6 +90,5 @@ class InstanceLock {
|
||||
InstanceLock() {}
|
||||
};
|
||||
|
||||
} // namespace openthread
|
||||
} // namespace esphome
|
||||
} // namespace esphome::openthread
|
||||
#endif
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
|
||||
static const char *const TAG = "openthread";
|
||||
|
||||
namespace esphome {
|
||||
namespace openthread {
|
||||
namespace esphome::openthread {
|
||||
|
||||
void OpenThreadComponent::setup() {
|
||||
// Used eventfds:
|
||||
@@ -209,6 +208,5 @@ otInstance *InstanceLock::get_instance() { return esp_openthread_get_instance();
|
||||
|
||||
InstanceLock::~InstanceLock() { esp_openthread_lock_release(); }
|
||||
|
||||
} // namespace openthread
|
||||
} // namespace esphome
|
||||
} // namespace esphome::openthread
|
||||
#endif
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#ifdef USE_OPENTHREAD
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace openthread_info {
|
||||
namespace esphome::openthread_info {
|
||||
|
||||
static const char *const TAG = "openthread_info";
|
||||
|
||||
@@ -19,6 +18,5 @@ void NetworkKeyOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Network Key"
|
||||
void PanIdOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "PAN ID", this); }
|
||||
void ExtPanIdOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Extended PAN ID", this); }
|
||||
|
||||
} // namespace openthread_info
|
||||
} // namespace esphome
|
||||
} // namespace esphome::openthread_info
|
||||
#endif
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#ifdef USE_OPENTHREAD
|
||||
|
||||
namespace esphome {
|
||||
namespace openthread_info {
|
||||
namespace esphome::openthread_info {
|
||||
|
||||
using esphome::openthread::InstanceLock;
|
||||
|
||||
@@ -213,6 +212,5 @@ class ExtPanIdOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor:
|
||||
std::array<uint8_t, 8> last_extpanid_{};
|
||||
};
|
||||
|
||||
} // namespace openthread_info
|
||||
} // namespace esphome
|
||||
} // namespace esphome::openthread_info
|
||||
#endif
|
||||
|
||||
@@ -13,8 +13,6 @@ from esphome.const import (
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
|
||||
OTA_STATE_LISTENER_KEY = "ota_state_listener"
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
AUTO_LOAD = ["md5", "safe_mode"]
|
||||
|
||||
@@ -88,7 +86,6 @@ BASE_OTA_SCHEMA = cv.Schema(
|
||||
@coroutine_with_priority(CoroPriority.OTA_UPDATES)
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_OTA")
|
||||
CORE.add_job(final_step)
|
||||
|
||||
if CORE.is_rp2040 and CORE.using_arduino:
|
||||
cg.add_library("Updater", None)
|
||||
@@ -122,24 +119,7 @@ async def ota_to_code(var, config):
|
||||
await automation.build_automation(trigger, [(cg.uint8, "x")], conf)
|
||||
use_state_callback = True
|
||||
if use_state_callback:
|
||||
request_ota_state_listeners()
|
||||
|
||||
|
||||
def request_ota_state_listeners() -> None:
|
||||
"""Request that OTA state listeners be compiled in.
|
||||
|
||||
Components that need to be notified about OTA state changes (start, progress,
|
||||
complete, error) should call this function during their code generation.
|
||||
This enables the add_state_listener() API on OTAComponent.
|
||||
"""
|
||||
CORE.data[OTA_STATE_LISTENER_KEY] = True
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.FINAL)
|
||||
async def final_step():
|
||||
"""Final code generation step to configure optional OTA features."""
|
||||
if CORE.data.get(OTA_STATE_LISTENER_KEY, False):
|
||||
cg.add_define("USE_OTA_STATE_LISTENER")
|
||||
cg.add_define("USE_OTA_STATE_CALLBACK")
|
||||
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
#include "ota_backend.h"
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
@@ -7,64 +7,70 @@
|
||||
namespace esphome {
|
||||
namespace ota {
|
||||
|
||||
class OTAStateChangeTrigger final : public Trigger<OTAState>, public OTAStateListener {
|
||||
class OTAStateChangeTrigger : public Trigger<OTAState> {
|
||||
public:
|
||||
explicit OTAStateChangeTrigger(OTAComponent *parent) : parent_(parent) { parent->add_state_listener(this); }
|
||||
|
||||
void on_ota_state(OTAState state, float progress, uint8_t error) override {
|
||||
if (!this->parent_->is_failed()) {
|
||||
this->trigger(state);
|
||||
}
|
||||
explicit OTAStateChangeTrigger(OTAComponent *parent) {
|
||||
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||
if (!parent->is_failed()) {
|
||||
trigger(state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
OTAComponent *parent_;
|
||||
};
|
||||
|
||||
template<OTAState State> class OTAStateTrigger final : public Trigger<>, public OTAStateListener {
|
||||
class OTAStartTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit OTAStateTrigger(OTAComponent *parent) : parent_(parent) { parent->add_state_listener(this); }
|
||||
|
||||
void on_ota_state(OTAState state, float progress, uint8_t error) override {
|
||||
if (state == State && !this->parent_->is_failed()) {
|
||||
this->trigger();
|
||||
}
|
||||
explicit OTAStartTrigger(OTAComponent *parent) {
|
||||
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||
if (state == OTA_STARTED && !parent->is_failed()) {
|
||||
trigger();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
OTAComponent *parent_;
|
||||
};
|
||||
|
||||
using OTAStartTrigger = OTAStateTrigger<OTA_STARTED>;
|
||||
using OTAEndTrigger = OTAStateTrigger<OTA_COMPLETED>;
|
||||
using OTAAbortTrigger = OTAStateTrigger<OTA_ABORT>;
|
||||
|
||||
class OTAProgressTrigger final : public Trigger<float>, public OTAStateListener {
|
||||
class OTAProgressTrigger : public Trigger<float> {
|
||||
public:
|
||||
explicit OTAProgressTrigger(OTAComponent *parent) : parent_(parent) { parent->add_state_listener(this); }
|
||||
|
||||
void on_ota_state(OTAState state, float progress, uint8_t error) override {
|
||||
if (state == OTA_IN_PROGRESS && !this->parent_->is_failed()) {
|
||||
this->trigger(progress);
|
||||
}
|
||||
explicit OTAProgressTrigger(OTAComponent *parent) {
|
||||
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||
if (state == OTA_IN_PROGRESS && !parent->is_failed()) {
|
||||
trigger(progress);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
OTAComponent *parent_;
|
||||
};
|
||||
|
||||
class OTAErrorTrigger final : public Trigger<uint8_t>, public OTAStateListener {
|
||||
class OTAEndTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit OTAErrorTrigger(OTAComponent *parent) : parent_(parent) { parent->add_state_listener(this); }
|
||||
|
||||
void on_ota_state(OTAState state, float progress, uint8_t error) override {
|
||||
if (state == OTA_ERROR && !this->parent_->is_failed()) {
|
||||
this->trigger(error);
|
||||
}
|
||||
explicit OTAEndTrigger(OTAComponent *parent) {
|
||||
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||
if (state == OTA_COMPLETED && !parent->is_failed()) {
|
||||
trigger();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
OTAComponent *parent_;
|
||||
class OTAAbortTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit OTAAbortTrigger(OTAComponent *parent) {
|
||||
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||
if (state == OTA_ABORT && !parent->is_failed()) {
|
||||
trigger();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class OTAErrorTrigger : public Trigger<uint8_t> {
|
||||
public:
|
||||
explicit OTAErrorTrigger(OTAComponent *parent) {
|
||||
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||
if (state == OTA_ERROR && !parent->is_failed()) {
|
||||
trigger(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ota
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace esphome {
|
||||
namespace ota {
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
#ifdef USE_OTA_STATE_CALLBACK
|
||||
OTAGlobalCallback *global_ota_callback{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
OTAGlobalCallback *get_global_ota_callback() {
|
||||
@@ -13,12 +13,7 @@ OTAGlobalCallback *get_global_ota_callback() {
|
||||
return global_ota_callback;
|
||||
}
|
||||
|
||||
void OTAComponent::notify_state_(OTAState state, float progress, uint8_t error) {
|
||||
for (auto *listener : this->state_listeners_) {
|
||||
listener->on_ota_state(state, progress, error);
|
||||
}
|
||||
get_global_ota_callback()->notify_ota_state(state, progress, error, this);
|
||||
}
|
||||
void register_ota_platform(OTAComponent *ota_caller) { get_global_ota_callback()->register_ota(ota_caller); }
|
||||
#endif
|
||||
|
||||
} // namespace ota
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user